板凳--------第60章 SOCKET:服务器设计

60.1 迭代型和并发型服务器 1016

1.迭代型: 服务器每次只处理一个客户端,只有当完全处理完一个客户端的请求后才会去处理下一个客户端。只适用于快速处理客户端请求的场景,因为每个客户端都必须等待,直到前面所有的客户端都处理完了服务器才能继续服务下一个客户端。
2.并发型: 服务器能够同时处理多个客户端的请求。适用于每个请求都需要大量处理时间,或是当客户端和服务器在进行扩展对话中需要来回传递信息的场景。本章重点

60.2 迭代型UDP echo服务器 1016

#include <syslog.h>
#include "id_echo.h"
#include "become_daemon.h"
int
main(int argc, char *argv[])
{
    int sfd;
    ssize_t numRead;
    socklen_t len;
    struct sockaddr_storage claddr;
    char buf[BUF_SIZE];
    char addrStr[IS_ADDR_STR_LEN];

    if (becomeDaemon(0) == -1)
        errExit("becomeDaemon");

    sfd = inetBind(SERVICE, SOCK_DGRAM, NULL);
    if (sfd == -1) {
        syslog(LOG_ERR, "Could not create server socket (%s)", strerror(errno));
        exit(EXIT_FAILURE);
    }

    /* Receive datagrams and return copies to senders */

    for (;;) {
        len = sizeof(struct sockaddr_storage);
        numRead = recvfrom(sfd, buf, BUF_SIZE, 0,
                           (struct sockaddr *) &claddr, &len);
        if (numRead == -1)
            errExit("recvfrom");

        if (sendto(sfd, buf, numRead, 0, (struct sockaddr *) &claddr, len)
                        != numRead)
            syslog(LOG_WARNING, "Error echoing response to %s (%s)",
                    inetAddressStr((struct sockaddr *) &claddr, len,
                                   addrStr, IS_ADDR_STR_LEN),
                    strerror(errno));
    }
}
#include "id_echo.h"

int
main(int argc, char *argv[])
{
    int sfd, j;
    size_t len;
    ssize_t numRead;
    char buf[BUF_SIZE];

    if (argc < 2 || strcmp(argv[1], "--help") == 0)
        usageErr("%s host msg...\n", argv[0]);

    /* Construct server address from first command-line argument */

    sfd = inetConnect(argv[1], SERVICE, SOCK_DGRAM);
    if (sfd == -1)
        fatal("Could not connect to server socket");

    /* Send remaining command-line arguments to server as separate datagrams */

    for (j = 2; j < argc; j++) {
        len = strlen(argv[j]);
        if (write(sfd, argv[j], len) != len)
            fatal("partial/failed write");

        numRead = read(sfd, buf, BUF_SIZE);
        if (numRead == -1)
            errExit("read");

        printf("[%ld bytes] %.*s\n", (long) numRead, (int) numRead, buf);
    }

    exit(EXIT_SUCCESS);
}

(base) wannian07@wannian07-PC:~/Desktop/std/linux_prog_interface$ gcc id_echo_cl.c -o id_echo_cl error_functions.c become_daemon.c inet_sockets.c
(base) wannian07@wannian07-PC:~/Desktop/std/linux_prog_interface$ gcc id_echo_cl.c -o id_echo_cl error_functions.c become_daemon.c inet_sockets.c

在这里插入图片描述

60.3 并发型TCP echo服务器 1019

客户端发送无限量数据给服务器,适合并发型,使多个客户端能够同时得到服务
1.服务器调用becomeDaemon()成为一个守护进程
2.为使程序更小,使用Internet域套接字函数库
3.服务器为每个客户端传教一个子进程,确保不会出现僵尸进程。通过信号SIGCHLD安装信号处理例程来实现
4.服务器主体部分由for()循环组成,接受客户端的连接,通过fork()传教子进程。子进程调用handleRequest()处理客户端,父进程在for()循环下接受下一个客户端连接。
5.每次调用fork()后,监听套接字和连接套接字都在子进程中得到复制。
在这里插入图片描述

6.每个子进程在处理完一个客户端后终止。
客户端:

#include "inet_sockets.h"
#include "tlpi_hdr.h"

#define BUF_SIZE 100

int
main(int argc, char *argv[])
{
    int sfd;
    ssize_t numRead;
    char buf[BUF_SIZE];

    if (argc != 2 || strcmp(argv[1], "--help") == 0)
        usageErr("%s host\n", argv[0]);

    sfd = inetConnect(argv[1], "echo", SOCK_STREAM);
    if (sfd == -1)
        errExit("inetConnect");

    switch (fork()) {
    case -1:
        errExit("fork");

    case 0:             /* Child: read server's response, echo on stdout */
        for (;;) {
            numRead = read(sfd, buf, BUF_SIZE);
            if (numRead <= 0)                   /* Exit on EOF or error */
                break;
            printf("%.*s", (int) numRead, buf);
        }
        exit(EXIT_SUCCESS);

    default:            /* Parent: write contents of stdin to socket */
        for (;;) {
            numRead = read(STDIN_FILENO, buf, BUF_SIZE);
            if (numRead <= 0)                   /* Exit loop on EOF or error */
                break;
            if (write(sfd, buf, numRead) != numRead)
                fatal("write() failed");
        }

        /* Close writing channel, so server sees EOF */

        if (shutdown(sfd, SHUT_WR) == -1)
            errExit("shutdown");
        exit(EXIT_SUCCESS);
    }
}

服务器:

#include <signal.h>
#include <syslog.h>
#include <sys/wait.h>
#include "become_daemon.h"
#include "inet_sockets.h"       /* Declarations of inet*() socket functions */
#include "tlpi_hdr.h"

#define SERVICE "echo"          /* Name of TCP service */
#define BUF_SIZE 4096

static void             /* SIGCHLD handler to reap dead child processes */
grimReaper(int sig)
{
    int savedErrno;             /* Save 'errno' in case changed here */

    savedErrno = errno;
    while (waitpid(-1, NULL, WNOHANG) > 0)
        continue;
    errno = savedErrno;
}

/* Handle a client request: copy socket input back to socket */

static void
handleRequest(int cfd)
{
    char buf[BUF_SIZE];
    ssize_t numRead;

    while ((numRead = read(cfd, buf, BUF_SIZE)) > 0) {
        if (write(cfd, buf, numRead) != numRead) {
            syslog(LOG_ERR, "write() failed: %s", strerror(errno));
            exit(EXIT_FAILURE);
        }
    }

    if (numRead == -1) {
        syslog(LOG_ERR, "Error from read(): %s", strerror(errno));
        exit(EXIT_FAILURE);
    }
}

int
main(int argc, char *argv[])
{
    int lfd, cfd;               /* Listening and connected sockets */
    struct sigaction sa;

    if (becomeDaemon(0) == -1)
        errExit("becomeDaemon");

    /* Establish SIGCHLD handler to reap terminated child processes */

    sigemptyset(&sa.sa_mask);
    sa.sa_flags = SA_RESTART;
    sa.sa_handler = grimReaper;
    if (sigaction(SIGCHLD, &sa, NULL) == -1) {
        syslog(LOG_ERR, "Error from sigaction(): %s", strerror(errno));
        exit(EXIT_FAILURE);
    }

    lfd = inetListen(SERVICE, 10, NULL);
    if (lfd == -1) {
        syslog(LOG_ERR, "Could not create server socket (%s)", strerror(errno));
        exit(EXIT_FAILURE);
    }

    for (;;) {
        cfd = accept(lfd, NULL, NULL);  /* Wait for connection */
        if (cfd == -1) {
            syslog(LOG_ERR, "Failure in accept(): %s", strerror(errno));
            exit(EXIT_FAILURE);
        }

        /* Handle each client request in a new child process */

        switch (fork()) {
        case -1:
            syslog(LOG_ERR, "Can't create child (%s)", strerror(errno));
            close(cfd);                 /* Give up on this client */
            break;                      /* May be temporary; try next client */

        case 0:                         /* Child */
            close(lfd);                 /* Unneeded copy of listening socket */
            handleRequest(cfd);
            _exit(EXIT_SUCCESS);

        default:                        /* Parent */
            close(cfd);                 /* Unneeded copy of connected socket */
            break;                      /* Loop to accept next connection */
        }
    }
}

(base) wannian07@wannian07-PC:~/Desktop/std/linux_prog_interface$ gcc is_echo_cl.c -o is_echo_cl error_functions.c become_daemon.c inet_sockets.c
(base) wannian07@wannian07-PC:~/Desktop/std/linux_prog_interface$ gcc is_echo_sv.c -o is_echo_sv error_functions.c become_daemon.c inet_sockets.c

60.4 并发型服务器的其他设计方案 1021

https://www.bilibili.com/read/cv28123863/
TCP多进程并发服务器(进程池版本)&TCP多进程并发服务器(动态创建进程版本)
在这里插入图片描述

TCP多线程并发服务器(进程池版本)&TCP多线程并发服务器(动态创建进程版本)

在这里插入图片描述

https://www.bilibili.com/read/cv28123863/

在服务器上预先创建进程或线程
1.服务器程序在启动阶段(即在任何客户端请求到来之前)就立刻预先创建好一定数量的子进程(或线程),而不是针对每个客户端来创建一个新的子进程(或线程)。这些子进程构成了一种服务池(server pool)
2.服务池中的每个子进程一次只处理一个客户端。在处理完客户端请求后,子进程并不会终止,而是获取下一个待处理的客户端继续处理,如此类推。
3.服务器应用中仔细地管理子进程。服务池应该足够大,以确保能充分响应客户端的请求。 a. 服务器父进程必须对未占用的子进程加以监视,服务器处于负载高峰期时增加服务池的大小,这样就总会有足够多的子进程存在,从而可以立刻服务于新的客户端请求。
b. 负载下降了,那么应该相应地降低服务池的大小,因为过多的空余进程会降低系统的整体性能。
c. 服务池中的子进程必须遵循某些协议,使得它们能以独占的方式选择一个客户端连接。
d. 服务池中的每个子进程在监听描述符的 accept()调用上阻塞就足够了。
e. 服务器父进程在创建任何子进程之前先创建监听套接字, 然后每个子进程在 fork()调用中继承该套接字的文件描述符。当一个新的客户端连接到来时,只有其中一个子进程能完成 accept()调用。
f. 由于 accept()在一些老式的实现中并不是一个原子化的系统调用,因此可能需要通过一些互斥技术(例如文件锁)来支持,以确保每次只有一个子进程可以执行 accept()调用
在单个进程中处理多个客户端
设计让单个服务器进程来处理多个客户端, 必须采用一种能允许单个进程同时监视多个文件描述符上 I/O 事件的 I/O 模型(I/O 多路复用、信号驱动 I/O 或者 epoll)。
在设计单进程服务器时,服务器进程必须做一些通常由内核来处理的调度任务。在每个客户端一个服务器进程地解决方案中,依靠内核来确保每个服务器进程能公平地访问到服务器主机的资源。用单个服务器进程来处理多个客户端时,服务器进程必须自行确保一个或多个客户端不会霸占服务器,从而使其他的客户端处于饥饿状态。
采用服务器集群
使用多个服务器系统—服务器集群(server farm)。构建服务器集群最简单的一种方法是 DNS 轮转负载共享(DNS round-robin load sharing)(或负载分发, load distribution),一个地区的域名权威服务器将同一个域名映射到多个 IP地址上(即,多台服务器共享同一个域名)。后续对 DNS 服务器的域名解析请求将以循环轮转的方式以不同的顺序返回这些 IP 地址。
优势: DNS 循环轮转的是成本低,而且容易实施。
问题1、是远端 DNS 服务器上所执行的缓存操作, 这意味着今后位于某个特定主机(或一组主机)上的客户端发出的请求会绕过循环轮转 DNS 服务器,并总是由同一个服务器来负责处理。
问题2、循环轮转 DNS 并没有任何内建的用来确保达到良好负载均衡(不同的客户端在服务器上产生的负载不同)或者是确保高可用性的机制(如果其中一台服务器宕机或者运行的服务器 程序崩溃了怎么办?)。
服务器亲和性(server affinity),确保来自同一个客户端的请求序列能够全部定向到同一台服务器上,这样由服务器维护的任何有关客户端状态的信息都能保持准确。
服务器负载均衡(server load balancing)由一台负载均衡服务器将客户端请求路由到服务器集群中的其中一个成员上。(为了确保高可用性,可能还会有一台备用的服务器。一旦负载均衡主服务器崩溃,备用服务器就立刻接管主服务器的任务。)这消除了由远端 DNS 缓存所引起的问题,因为服务器集群只对外提供了一个单独的 IP 地址(也就是负载均衡服务器的 IP 地址)。负载均衡服务器结合一些算法来衡量或计算服务器负载(可能是根据服务器集群的成员所提供的量值),并智能化地将负载分发到集群中的各个成员之上。负载均衡服务器也会自动检测集群中失效的成员(如果需要,还会自动检测新增加的服务器成员)。最后,负载均衡服务器可能还会提供对服务器亲和力的支持。
https://www.jb51.net/article/122310.htm
服务器负载均衡的基本功能和实现原理

60.5 inetd( Internet 超级服务器)守护进程

/etc/services , 列出了系统中服务项目,大部分服务器进程通常只是等待着偶尔发送过来的连接请求或数据报, 等待。服务器进程会占用内核进程表中的槽位,会占用一些内存和交换空间,对系统产生负载。
守护进程 inetd 用来消除运行大量非常用服务器进程的需要。好处:
1.与其为每个服务运行一个单独的守护进程,现在只用一个进程—inetd 守护进程—就可以监视一组指定的套接字端口,并按照需要启动其他的服务。因此可降低系统上运行的进程数量。
2.inetd 简化了启动其他服务的编程工作。因为由 inetd 执行的一些步骤通常在所有的网络服务启动时都会用到。 由于 inetd 监管着一系列的服务,可按照需要启动其他的服务,因此 inetd 有时候也被称为 Internet 超级服务器。
inetd 守护进程所做的操作
inetd 守护进程通常在系统启动时运行。 inetd 执行如下步骤:
1.对于在配置文件/etc/inetd.conf 中指定的每项服务, inetd 都会创建一个恰当类型的套接字(即流式套接字或数据报套接字),然后绑定到指定的端口号上。此外,每个 TCP套接字都会通过 listen()调用允许客户端发来连接。
2.通过 select()调用, inetd 对前一步中创建的所有套接字进行监视,看是否有数据报或请求连接发送过来
3.select()调用进入阻塞态,直到一个 UDP 套接字上有数据报可读或者 TCP 套接字上收到了连接请求。在 TCP 连接中, inetd 在进入下一个步骤之前会先为连接执行 accept()调用。
4.要启动这个套接字上指定的服务, inted 调用 fork()创建一个新的进程, 然后通过 exec()启动服务器程序。在执行 exec()前,子进程执行如下的步骤。
(a)除了用于 UDP 数据报和接受 TCP 连接的文件描述符外,将其他所有从父进程继 承而来的文件描述符都关闭。
(b)在文件描述符 0、 1 和 2 上复制套接字文件描 述符,并关闭套接字文件描述符本身(因为已经不需要它了)。完成这一步之后, 启动的服务器进程就能通过这三个标准的文件描述符同套接字通信了。
(c)为启动的服务器进程设定用户和组 ID,设定的值可在 /etc/inetd.conf 中的相应条目找到。
5.第 3 步中,如果在 TCP 套接字上接受了一个连接, inetd 就关闭这个连接套接字。
6.inetd 服务跳转回第 2 步继续执行。
/etc/inetd.conf 文件
inetd 守护进程的操作由一个配置文件来控制,通常是/etc/inetd.conf。该文件中的每一行都描述了一种由 inetd 处理的服务
echo 服务: /etc/inetd.conf 文件中的每一行都由以下字段组成,由空格来将它们分隔开。
1.服务名称(service name):指定了一项服务的名称,这项服务可在/etc/services 文件中找到。结合协议字段(protocol),就可以通过查找/etc/services 文件以确定 inetd 应该为这项服务监视哪一个端口号。
2.套接字类型(Socket type):该字段指定了这项服务所用的套接字类型—流式 套接字(stream)还是数据报套接字(dgram)。
3.协议( protocol):该字段指定了这个套接字所使用的协议。这个字段可以包含文件 /etc/protocols 中所列出的任何 Internet 协议,所有的服务都会指定 tcp或 udp。
4. 标记(flags):该字段的内容要么是 wait,要么是 nowait。这个字段指明了由 inetd 启 动的服务器(暂时的)是否会接管用于该服务的套接字。如果启动的服务器需要管理 这个套接字,那么该字段被指定为 wait。这将导致 inetd 把这个套接字从它所监视的文件描述符集合中移除,直到这个服务器 程序退出为止。
5.登录名( login name):该字段由/etc/passwd 中的用户名部分组成,还可以在其后 紧跟一个句号以及一个/etc/group 中的组名称。这些名称确定了运行的服务器程 序的用户 ID 和组 ID。
6.服务器程序(server program):该字段指定了被执行的服务器程序的路径名。
7.服务器程序参数(server program arguments):该字段指定了一个或多个参数,参数之间由空格符分隔。当执行服务器程序时,这些参数就作为程序的参数列表。在被执行的服务器程序中, 第一个参数对应于 argv[0], 通常和服务器程序名称的基础部分相同。 下一个参数对应于 argv[1],以此类推。
由 inetd 调用的流式套接字(TCP)服务器通常都被设计为只处理一个单独的客户端连接,处理完后就终止, 把监听其他连接的任务留给了 inetd。 修改了/etc/inetd.conf 文件后,需要发送一个 SIGHUP 信号给 inetd,请求它重新读取配置文件
inetd 可以简化服务器程序的编程工作,并发型(通常是 TCP)服务器。这是因为 inetd 已经帮它所调用的服务器程序完成了以下步骤。
1.执行所有和套接字相关的初始化工作,调用 socket()、 bind()以及 listen()(针对 TCP服务器)。
2.对于一个 TCP 服务,为新到来的连接执行 accept()操作。
3.创建一个新的进程来处理到来的 UDP 数据报或者是 TCP 连接。自动将调用的服务器进程设置为守护进程。 inetd 通过 fork()处理所有与进程创建相关的细节,通过 SIGCHLD 信号处理例程清除所有退出的子进程。
4.将代表 UDP 套接字或 TCP 连接套接字的文件描述符复制到标准文件描述符 0、 1 和2 上,并关闭所有其他的文件描述符(因为它们并不会在调用的服务器进程中用到)。
5.执行服务器程序。
在/etc/inetd.conf 文件中创建如下的条目,使得 inetd 可以调用该服务器程序

#include <syslog.h>
#include "tlpi_hdr.h"
#define BUF_SIZE 4096
int
main(int argc, char *argv[])
{
    char buf[BUF_SIZE];
    ssize_t numRead;
    while ((numRead = read(STDIN_FILENO, buf, BUF_SIZE)) > 0) {
        if (write(STDOUT_FILENO, buf, numRead) != numRead) {
            syslog(LOG_ERR, "write() failed: %s", strerror(errno));
            exit(EXIT_FAILURE);
        }
    }
    if (numRead == -1) {
        syslog(LOG_ERR, "Error from read(): %s", strerror(errno));
        exit(EXIT_FAILURE);
    }
    exit(EXIT_SUCCESS);
} 

60.6 总结

https://www.cnblogs.com/wangbin2188/tag/Linux/

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/728598.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

10年265倍!动态展示全球第一股英伟达10年股价走势

英伟达在过去十年的股价走势展示了其在市场上的强劲表现和显著增长。自1999年上市以来&#xff0c;英伟达的股价经历了多次显著的涨幅&#xff0c;并在2024年达到了历史新高。 从2023年6月的数据来看&#xff0c;英伟达的股价为386.54美元/股&#xff0c;市值为9548亿美元。然…

redis以后台的方式启动

文章目录 1、查看redis安装的目录2、Redis以后台的方式启动3、通过客户端连接redis4、连接后&#xff0c;测试与redis的连通性 1、查看redis安装的目录 [rootlocalhost ~]# cd /usr/local/redis/ [rootlocalhost redis]# ll 总用量 112 drwxr-xr-x. 2 root root 150 12月 6…

Excel中插入的图片在不同电脑上消失的问题及解决方法

在使用Excel时插入图片&#xff0c;然后在不同电脑上打开却发现图片消失并被替换为链接地址&#xff0c;这个问题通常出现于文件中的图片路径没有正确保存或者电脑上缺少相关的图片文件。下面让我们来详细解释这个问题以及可能的解决方法。 ### 问题原因分析1. **相对路径问题…

数据结构—排序、查找、图论和字符串算法之Java实例

一&#xff1a;引言 在编程的海洋中&#xff0c;算法是程序员的灵魂之光。它们不仅指引着代码的前进方向&#xff0c;更能解决难题&#xff0c;提升效率。虽然各式各样的算法琳琅满目&#xff0c;但其中有一些却是每位程序员必定会遇到且应当深刻掌握的。本文将带您走进这些至…

从零开始学WEB前端——HTML理论讲解

有同学可能就会问&#xff1a;为什么我的创建的记事本文件名字叫“新建文本文档”而不是“新建文本文档.txt”呢&#xff1f; 这是因为.txt是后缀名&#xff0c;表示的是打开方式&#xff0c;就比如.docx后缀的都是默认用word打开&#xff0c;.xlsx的都是默认用excel打开。 常…

Linux ls-al命令实现,tree命令实现,不带缓存的文件IO(open,read,write)

shell命令 ls -al 实现 #include <43func.h> void error_check(int ret, const char *msg) {if (ret -1) {perror(msg);exit(EXIT_FAILURE);} }char get_file_type(mode_t mode) {if (S_ISREG(mode)) return -;//检查给定的文件模式&#xff08;通常是从 stat 或 lst…

【vite】define 全局常量定义

&#x1f9ed; define 说明 类型&#xff1a; Record<string, any> 定义全局常量替换方式。其中每项在开发环境下会被定义在全局&#xff0c;而在构建时被静态替换。 Vite 使用 esbuild define 来进行替换&#xff0c;因此值的表达式必须是一个包含 JSON 可序列化值&a…

WebHttpServletRequestResponse(完整知识点汇总)

额外知识点 Web核心 Web 全球广域网&#xff0c;也成为万维网&#xff08;www&#xff09;&#xff0c;可通过浏览器访问的网站 JavaWeb 使用Java技术来解决相关Web互联网领域的技术栈 JavaWeb技术栈 B/S架构&#xff1a;Browser/Server&#xff0c;即浏览器/服务器 架构模式…

海康威视-下载的录像视频浏览器播放问题

目录 1、播放异常比对 2、视频编码检查 2.1、正常视频解析 2.2、海康视频解析 2.3、比对工具 3、转码 3.1、maven依赖 3.2、实现代码 4、验证 在前面的文章&#xff08;海康威视-按时间下载录像文件_海康威视 sdk 下载录像 大小0-CSDN博客&#xff09;中&#xff0c;通…

.NET+Python量化【1】——环境部署和个人资金账户信息查询

前言&#xff1a;量化资料很少&#xff0c;.NET更少。那我就来开个先河吧~ 以下是使用QMT进行量化开发的环境部署和基础信息获取有关操作。 1、首先自己申请券商的QMT权限&#xff0c;此步骤省略。 2、登陆QMT&#xff0c;选择极简模式&#xff0c;或者独立交易模式之类的。会进…

C语言 | Leetcode C语言题解之第171题Excel表列序号

题目&#xff1a; 题解&#xff1a; int titleToNumber(char* columnTitle) {int number 0;long multiple 1;for (int i strlen(columnTitle) - 1; i > 0; i--) {int k columnTitle[i] - A 1;number k * multiple;multiple * 26;}return number; }

【Mybatis-plus】查询及更新为null或空字符串

前言 查询为 null 或者 空字符串时&#xff0c;可以使用 or() 关键字。 提示&#xff1a;以下是本篇文章正文内容&#xff0c;下面案例可供参考 查询 使用 LambdaQueryWrapper 查询 parentCode 为 null 或者 空字符串 的数据。 LambdaQueryWrapper<CompanyEntity> qu…

go 1.22 增强 http.ServerMux 路由能力

之前 server func main() {http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {fmt.Println("Received request:", r.URL.Path)fmt.Fprintf(w, "Hello, client! You requested: %s\n", r.URL.Path)})log.Println("Serv…

Gone——golang依赖注入框架介绍

文章目录 Gone是什么特性小试牛刀概念与启动流程人话版本鬼话版本代码版本 关于Logo Gone是什么 首先&#xff0c;Gone是Golang的一个轻量级的依赖注入框架&#xff0c;目前依赖注入的装配流程是通过反射来实现的&#xff1b;虽然golang的反射一直被人诟病太慢&#xff0c;但是…

RK3568平台(音频篇)音频ALSA框架

一.ALSA框架简介 ALSA表示先进linux声音架构&#xff08;Advanced Linux Sound Archiecture&#xff09;&#xff0c;它由一系列的内核驱动、应用程序编程接口&#xff08;API&#xff09;以及支持linux下声音的应用程序组成、 ALSA项目发起的原有是linux下的声卡驱动&#x…

【论文笔记】LoRA LOW-RANK ADAPTATION OF LARGE LANGUAGE MODELS

题目&#xff1a;LoRA: LOW-RANK ADAPTATION OF LARGE LANGUAGE MODELS 来源: ICLR 2022 模型名称: LoRA 论文链接: https://arxiv.org/abs/2106.09685 项目链接: https://github.com/microsoft/LoRA 文章目录 摘要引言问题定义现有方法的问题方法将 LORA 应用于 Transformer 实…

双写一致性

双写一致性 当修改了数据库的数据也要同时更新缓存的数据&#xff0c;缓存和数据库的数据要保持一致。 注意这里是对数据库进行写操作而不是读操作&#xff0c;通常我们有两种方式完成这个写操作&#xff0c;分别是&#xff1a;先删除缓存再修改数据库 和 先修改数据库再删除…

并发锁机制

JDK1.6 synchronized &#xff08;底层是由C实现的&#xff09;&#xff1a; synchronized: 互斥锁&#xff0c;悲观 锁&#xff0c;同步锁&#xff0c;重量级锁&#xff08;耗性能&#xff09;&#xff0c;多线程使用重量级锁很容易发生线程阻塞&#xff0c;因为涉及到多个线程…

elementUI的el-table自定义表头

<el-table-column label"昨日仪表里程(KM)" align"left" min-width"190" :render-header"(h, obj) > renderHeader(h, obj, 参数)" > <template slot-scope"scope"> <span>{{ scope.row.firstStartMil…

最新Springboot小程序医院核酸检测服务系统

采用技术 最新Springboot小程序医院核酸检测服务系统的设计与实现~ 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;SpringBootMyBatis 工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 页面展示效果 管理员页面 医护人员管理 普通管理员管理 接种进…