nginx系列--(三)--http

本文主要介绍http模块accept read流程,!!!请求对应的响应直接在read流程里就会返回给用户,而不需要通过write事件,和redis一样,基本都不通过eventloop write事件来发送响应给客户端,除非一次发送不完,nginx write事件则只会写邮件的时候ssl ocsp的时候才会触发,略,

!!!http模块和stream模块的实现原理基本一样,都是通过handler来执行差异化操作,而主eventloop流程用的都是同一套代码

下拉刷新:间歇性奖励

无线上划:人类在没有标识的情况下很容易过量

#访问localhost:10086/hello返回的是nginx/html/hello/index.html
location /hello {root html; #root表示设置根目录,然后会自动访问hello目录,即要求location 路径对应的目录要一致index  index.html index.htm;
}#访问localhost:10086/hello返回的是nginx/html/index.html
location /hello {alias html;    #alias表示直接用这个目录index  index.html index.htm;
}

!!!笔记:源码流程第一个断点最好不要用入口点,而是要打在执行流程中,比如你要debug http处理请求流程,那么就不要把第一个断点打在请求分发的地方,因为这个dispatch点是可以接受各种不同类型的请求,后续会因为请求会走不同的调用路线,所以我们最好打在具体流程中,即我们要debughttp流程,那么就把断点打在http_handler中,而不是打在调用httphandler的地方,因为调用者肯定是调用handler,而handler是一个指针,不同请求,handler值不同,所以应该直接打在http_handler中,然后有http_handler再网上。可以在handler中打断点,但这样需要step_into往下走才知道在哪里会走到handler,在哪跳转到http_handler,所以直接在http_handler中打断点然后往上回溯要比handler中打断点,然后一行一行step into要简单的多。反正断点的核心就是关键路径上多打几个断点。

!!!注意:nginx是多进程,需要特殊设置,否则因为处理都是在子进程中而导致我们打的断点不起作用

!!!nginx是每个worker都可以同时处理监听套接字以及客户端套接字,nginx并没有分配一个专门的进程来处理监听套接字,而是每个worker在每一轮事件循环开始前都竞争一次accept_mutex,!!!每一轮都竞争accept_mutex肯定会降低效率的,不过如果启用了linux epoll的epollexclusive,就可以不开启accept_mutex,因为此时只会有一个进程的epoll中的listen fd会触发读事件,这是linux操作系统提供的,此时则不用每次都竞争accept_mutex锁,也就是说accept_mutex锁此时会失效,不过nginx肯定会对这个进行一个封装。

nginx可以关闭后台模式;nginx也可以以单进程多线程的方式运行

笔记:nginx对请求的处理流程是一样的,eventloop_for_死循环+epoll_wait+for_所有fd+handler,然后handler是一个指针,也就是说流程是一样的,只是调用的handler不同而已,注意,handler不止一个,他用一个数组来存,并按顺序调用,因为这个handler是注册制的,就是说如何处理tcp/udp发来的数据,完全由注册的handler决定,也就是说如果要是nginx支持自定义的x协议,那么流程如下:1:把handler数组册到指定的handler中;2:handler中的数组要有序,因为他是按顺序调用的,所以我们只要按顺序来注册,那么就可以保证请求按我们指定的流程来处理。

笔记:自己读完以后再去网上找文章查看对应的文章

!!!笔记:默认nginx应用层只支持http,所以conf里最外层的http{xxx}就告诉nginx这个块对应的是http模块。默认的nginx传输层支持TCP和udp,所以conf里最外层的stream{xxx}就告诉nginx这个块对应的是tcp/udp,也就是说我们的处理函数接触的是传输层的原始字节流,我们可以选择转发也可以选择自己解析成应用层协议进一步处理。原始字节流的含义取决于我们怎么解析。nginx的http模块就相当于在原始的stream模块之上添加了http字节流解析模块,这个模块可以把字节流解析成http协议,nginx的http模块把这一切都实现了,就无须我们自己实现了。

!!!笔记:nginx的http模块把http协议的处理分成了11个阶段,而stream模块则分成了7个阶段

!!!笔记:nginx 根据conf中的分块名来调用对应的模块。nginx中http模块和stream模块在系统中的名字分别是http和stream,如果把http模块注册的名字改成http_abc,那么conf中对应的http{xx}就要改成http_abc{xx},否则就会报错找不到对应的模块

!!!笔记(猜测):如果要nginx支持新的应用层协议,那么就只能自己实现新的xx模块,然后配置文件里就能用xx{yy}来定义了,不过这样如果自己实现的话工作量就打了,要自己来解析配置,就很麻烦了

如果要支持

2:http处理流程

直接百度断点ngx_http_handler+搜索epoll_wait打下断点,一共两个断点,就可以抓住主线

2-1:模块启动和listenfd注册到epoll流程

笔记:流程同stream模块一模一样,已经在stream文章的模块启动部分叙述了,这里不再重复,只是简单记录一下

nginx.mainngx_cycle.ngx_init_cyclengx_conf_file.ngx_conf_parse     for ; ;                        ......cmd->set                                   #cmd->set就是各个模块对应的set函数,不同的模块对应不同的函数#比如stream模块就调用ngx_stream_block,http就用ngx_http_block#!!!也就是说nginx用函数指针来实现了一种多态的效果#!!!也就是说可以用同一套代码流程来加载http、stream模块3: http块,这是最顶层的http 块           ngx_http.ngx_http_block                              ngx_conf_file.ngx_conf_parse           ......ngx_http.ngx_http_init_phases        ngx_http.ngx_http_init_phase_handlers  ngx_http.ngx_http_optimize_servers  ngx_http.ngx_http_init_listening   ngx_http.ngx_http_add_listeningls=ngx_create_listening  ls.handler=ngx_http_init_connection      #!!!listenfd对新连接的处理函数设置为ngx_http_init_connection#!!!stream模块中则设置为ngx_stream_init_connection#!!!再次强调,ngx通过函数指针来模拟了多态,#!!!也就是同一套代码可以同时处理httpfd和streamfd#!!!下面的代码不管是http还是stream,都是同一个处理流程:注册到epollfd#!!!因为nginx中http和strema模块的不同在于处理函数的不同,而nginx把不同的handler封装在event_t结构体中#!!!而event_t结构体又包含在connection结构体中,clientfd存在epollfd中的就是connection结构,#!!!也就是说不管是http还是stream,用的都是connection结构,而他们的区别只有字段值的不同#!!!正因为如此,所以可以用同一个epollfd来同时处理httpserver-listenfd、streamserver-listenfd#!!!、httpclientfd、streamclientfd、streamupstreamfd上的读写事件......ngx_process_cycle.ngx_master_process_cycle      ngx_process_cycle.ngx_start_worker_processes  ......unistd.fork                                                          #fork子进程......rev->handler = ngx_event_accept;                           #!!!设置accept事件的处理函数为ngx_event_accept#!!!nginx把fd、handler都封装在ngx_event_t结构体中#!!!然后ngx_event_t结构又封装在connection结构中#!!!connection结构会作为epoll_events的data字段丢到epollfd中#!!!封装使得epoll_eventloop流程代码可以通用#!!!即不论是httpfd事件还是streamfd事件还是listenfd事件ngx_add_event(rev, NGX_READ_EVENT, NGX_EXCLUSIVE_EVENT)    #!!!注册listenfd的read事件到epollfd,并且是exclusive模式#!!!accept_handler为ngx_event_acceptee.data.ptr = (void *) ((uintptr_t) c | ev->instance)    #data字段为c,c是一个connection对象指针epoll_ctl(ep, op, c->fd, &ee)                            #这样我们直接从data取出connection对象#也就是说connection对象保存了一个连接的所有状态#redis也是一样的操作,所以epoll类型的应该都是一样的操作......2:执行eventloop,略,详情见eventloop源码流程

1-2:http listenfd accept流程

ngx_proces_cycle.ngx_worker_process_cycle                          #这里是子进程里面的流程1:!!!初始化eventloop(初始化+注册相关fd到eventloop)......ngx_add_event(rev, NGX_READ_EVENT, NGX_EXCLUSIVE_EVENT)        #!!!注册listenfd的read事件到epollfd,并且是exclusive模式......2:!!!执行eventLoopfor ; ;                                                          #死循环,即eventloop,事件循环ngx_event.ngx_process_events_and_timers                        #处理事件和定时器  ngx_epoll_module.ngx_epoll_process_events                    #到此处时已经是epoll_wait返回的fd了,所以直接处理fd就行了#!!!如果触发了读事件就调用rev->handler(rev=connection->read_events)#!!!如果触发了写事件就调用wev->handler(wev=connection->write_events)wev = c->write;if ((revents & EPOLLOUT) && wev->active) wev->ready = 1;wev->handler                                             #accept只注册了read事件,没有注册write事件,所以revents&EPOLLOUT=0#!!!accept事件对应读事件,所以调用rev->handlerif ((revents & EPOLLIN) && rev->active) :  rev->ready = 1;ngx_event_accept.ngx_event_accept              #不管是http还是stream,listenfd注册流程都是一样的以及accept函数都是一样的#因为所有的不同都封装在handler中,#即conection里的read_events/write_events对象的handler中
----------------在此之上的流程和stream accept一模一样----------------------------------------------------ngx_http_request.ngx_http_init_connection    #!!!即ls->handler,nginx通过函数指针来实现同一套代码同时处理http和stream#笔记:listenfd对应的read events handler为ngx_event_accept#此处的ls->handler是用来处理新建立的连接的#比如stream模块中对应的ls->handler就是与upsteam建立连接#同样的,ngx_http_init_connenction和#ngx_stream_init_connection的逻辑基本是一样的#只不过ngx_http_init_connection少了个连接upstream的操作#笔记:stream_init_connection中设置了clientfd.rev.handler=ngx_stream_session_handler;#但是在cscf->handler即ngx_stream_proxy_handler中#会重新设置为ngx_stream_proxy_downstream_handler;#笔记:clientfd放在session中,然后传递session给这个函数rev = c->read;rev->handler = ngx_http_wait_request_handler; #!!!设置http clientfd的read_events_handler#ngx_http_finalize_request中调用ngx_http_finalize_connection时#会根据是否开启keepalive选项来决定是否把rev->handler设置为ngx_http_keepalive_handler#默认是会的,也就是第一次是走wait_request_handler#但是第二个请求就会走keepalive_handler了c->write->handler = ngx_http_empty_handler;   ngx_event.ngx_handle_read_event               #把clientfd注册到epollfd中#笔记:http模块中accept时就注册到epollfd中了#而stream模块则是等到upstream处理第一次write时才会注册到epollfd中#!!!#define NGX_CLEAR_EVENT EPOLLET 就是说nginx默认是边缘触发ngx_event_accept.ngx_reorder_accept_events    #处理accept公平问题,略

1-3:eventloop过程中,http clientfd对应的读事件处理流程

nginx.mainngx_process_cycle.ngx_master_process_cycle......unistd.fork                                                        #fork子进程......ngx_proces_cycle.ngx_worker_process_cycle                          #这里是子进程里面的流程1:!!!初始化eventloop(初始化+注册相关fd到eventloop)......2:!!!执行eventLoopfor ; ;                                                          #死循环,即eventloop,事件循环ngx_event.ngx_process_events_and_timers                        #处理事件和定时器ngx_epoll_module.ngx_epoll_process_eventsunistd.epoll_wait#本次处理clientfd read eventsngx_http_request.ngx_http_keepalive_handler                #ngx_http_request.ngx_http_wait_request_handler处理新连接,这个处理旧连接#处理流程很简单,就是一个http协议解析器,先解析请求,然后再调用handler处理请求#解析过程很简单:先解析请求行,然后解析header,解析完header后调用handler处理请求#解析的过程中会从conn中取出历史数据,如果不是一个完整行,就标记,等待下一次读取#直到解析完整个请求。ngx_recv.ngx_unix_recv                                   #c->rev,这是个函数指针,实际是ngx_unix_recv#没有数据可读取时就会返回NGX_AGAINsocket2.recv                                           #调用linux系统调用从网络缓冲区读取数据到nginx内存缓冲区,肯定是for循环+非阻塞读取if (n == NGX_AGAIN) :                                    #如果是NGX_AGAIN,则会把读事件重新注册到对应的监听fd中ngx_event.ngx_handle_read_event                        #把读事件重新注册到对应的监听fd中。因为不同os中支持的模型不同,#不同模型的处理不同,所以需要这个函数,比如如果是linxu的epoll, #就是把fd+读事件重新注册到epollfd中,如果已存在就什么也不做return                                                 #这里重新注册读事件后就返回了ngx_connection.ngx_reusable_connection                   #暂不清楚ngx_http_request.ngx_http_process_request_line           #!!!rev->handler(),调用handler函数来处理事件#!!!再次强调,nginx通过函数指针来实现同一套#!!!eventloop代码同时可以处理http和stream请求#这里就开始解析http请求,就是状态机,一边读取一边解析for:                                                   #状态机,直到处理完请求或者没数据可读或者出错if rc == NGX_AGAIN:                                  #不完整的一行,需要继续读取ngx_http_request.ngx_http_read_request_header      #读取rc=ngx_http_request.ngx_http_parse_request_line      #解析请求行,即从字节流中解析出请求行if rc=NGX_OK:              ngx_http_request.ngx_http_process_request_uri      #根据解析出的请求行处理uringx_http_request.ngx_http_set_virtual_server       #ngx_http_request.ngx_http_process_request_headers  #解析headers,和解析请求行一样的流程,边读边解析#即如果rc==NGX_AGAIN就继续读取一行,然后解析#笔记:他不是一个外置函数(ngx_http_keepalive_handler ),#然后在这个外置函数里第一步解析请求行,然后第二步解析header#而是一种尾调用的关系,即是嵌套的,即外置函数调用函数解析请求行,#然后在请求行解析函数的最后一行调用解析headers的函数,#然后在解析headers的函数最后一行又调用handler处理函数#!!!http只需要解析http头部,而data部分则是由handler来处理#因为数据部分是对用户有意义,而无关于http协议,handler就是来处理具体数据的  ngx_http_request.ngx_http_read_request_header    #读取一行ngx_http_request.ngx_http_parse_header_line      #解析一行if rc == NGX_OK:continue:                                      #header有多行,所以NGX_OK表示成功解析一行if rc == NGX_HTTP_PARSE_HEADER_DONE:             #NGX_HTTP_PARSE_HEADER_DONE表示请求头解析完了,就要处理请求了#这里再次说明http解析的过程就是就是一个状态机,就是边读边解析ngx_http_request.ngx_http_process_request_header    #处理全部请求头ngx_http_request.ngx_http_process_request           #处理完请求头后这里处理请求c->read->handler = ngx_http_request_handler;      #nginx_http_request_handler就是#在收完请求头但是还没有收完整个请求之间起作用#!!!再次强调,http协议中读完请求头和请求体就可以处理请求了#!!!不必等到收到body部分,比如getc->write->handler = ngx_http_request_handler;     #ngx_http_request_handler就是根据事件类型来调用对应的event_handlerif (ev->write) {r->write_event_handler(r);} else {r->read_event_handler(r);}  r->read_event_handler = ngx_http_block_reading;   #!!!对于同一个连接上的请求,nginx是顺序处理的,#处理一个请求nginx只会调用一次ngx_http_process_request#而nginx在处理完header后就调用ngx_http_process_request了#如果不重置clientfd的read->handler和write->handler,#那么如果还没处理完,下一个请求来了#比如tcp粘包,缓冲区数据为body+下一个request,#那么就会再次触发clientfd的读取事件,读取到下一个请求#,然后就会再次调用ngx_http_process_reqeust#我猜测(是我猜的):ngx_http_process_reqeust不是无状态的函数#导致对于同一个连接必须处理完当前请求才能继续处理下一个请求#为了避免嵌套,所以nginx选择的解决办法是:#读取到一个完整的请求头即代表开始处理一个请求后#就会重置读取写入事件的handler,来避免再次调用http_process_request,#ngx_http_request_handler只是简单地根据事件类型来#调用对应的event_handler,简单地读取、读取丢弃、或者直接不读取,#就算读取了也不会解析数据,这样也就不会再次调用http_process_reqeust,#所以我们设置对应的read_event_handler=block_reading,#block_reading:如果是边缘触发则本次调用相当于空操作即不读取任何数据#如果是水平触发,就从epoll中移除clientfd,更简单暴力#!!!注意:在当前请求的处理过程中也许会重置read_event_handelr#比如在static_handler中如果不需要请求体(比如get /index.html)#那么就会重置read_event_handler=ngx_http_discard_request_body,#就是读取body,然后丢弃bodyngx_http_core_module.ngx_http_handlerif !r->internal:                                #如果是外部请求,那么就要调用所有handlerr->keepalive=r->headers_in.connection_type    #header中会有keepalive的设置r->phase_handler=0                            #handler是一个数组,phase_handler表示当前handler的下表#phase_handler=0表示从第一个handler开始处理,#因为handler是一个接一个调用的else:r->phase_handler=server_rewrite_index         #如果是内部请求(即内部的请求重定向等)则从server_rewrite_index开始#!!!就是说不必所有请求都走完http处理的所有阶段ngx_http_core_module.ngx_http_core_run_phases   #循环调用phase_handler数组中的所有handler来处理请求while (ph[r->phase_handler].checker):         #checker是对handler的包装,ph是phase_handler数组,#phase_handler就是数组下标#handler有好多,不过都是按顺序来的即一个handler对应一个处理步骤#这个phase_handler数组是在nginx_http_block中填充的,#默认是11个阶段14个handlerrc = ph[r->phase_handler].checker(r, &ph[r->phase_handler])    #handler处理后会自动给phase_handler+1#!!!发送响应给client这个功能是放在handler中的#这里仅仅记录发送静态页面给handler,略去了一大堆其他的handler/** ph表示phase_handler,ph是对handler的一个包装,* checker负责对handler的处理结果进行善后工作比如更新状态* 发送响应给client也算作一个handler,所以在handler中处理,handler是一个函数指针* static_handler专门用来处理静态页面,发送响应的时候也分两步,同时也是尾调用* 第一步执行filter_chain,对数据进行处理,处理完之后再执行output_chain,* output_chain内也要执行一个filter_chain,也是尾调用,* 如果没有设置写延迟,那么最终调用writev来把数据写到发送缓冲区,* 如果设置了就...暂不清楚*/ngx_http_core_module.ngx_http_core_content_phase  #注意:有好几个ngx_http_core_content_phase阶段#其中一个phase阶段的handler就是ngx_http_static_handlerngx_http_static_module.ngx_http_static_handler  #!!!ph->handler,此处是static_handler,负责处理静态页面  #!!!再次强调:nginx通过函数指针来模拟多态#!!!更广阔一点:c框架都是用函数指针来实现同一套代码处理不同的事件#!!!就是说框架的骨架是类型无关的,类型差异都封装在一个变量中#!!!nginx中就是eventloop骨架是描述符类型无关的(httpfd/listenfd/streamfd)#!!!这些fd的处理差异都封装在event_t结构,event_t封装在connection结构中#!!!eventloop骨架与connetion打交道即conn结构封装了不同fd事件处理的差异...deal something and get result metadata...r->read_event_handler = ngx_http_discarded_request_body_handler;                                                                             ngx_http_output_filter                      #响应数据元信息处理好了,#这里就调用output_filter对响应数据进行处理#output_filter是一个链表,同时也是尾调用,#所以如果链表很长,那么嵌套就会很深ngx_http_range_body_filterngx_http_copy_filterngx_output_chain                      #outputchain也是一个filter_chain#负责对要写的数据进行处理ngx_http_trailers_filterngx_http_charset_body_filterngx_http_ssi_body_filterngx_http_postpone_filterngx_http_gzip_body_filterngx_http_chunked_body_filterngx_http_write_filterngx_linux_sendfile_chainif:                    #nginx会根据实际情况来决定是sendfile还是writev,因为效率不同ngx_linux_sendfilesendfile.sendfile  #就是调用linux的sendfile来发送数据else:ngx_writevuio.writev       #就是调用ubuntu的writev来把数据写到发送缓冲区ngx_http_request.ngx_http_finalize_request       #各个http模块在执行完某个操作都需要调用这个函数,#来把请求的引用计数减去1,当引用计数为0时才会真正释放一个请求#有很多if,这里举两个:NGX_DECLINED和r->bufferedif rc == NGX_DECLINED:ngx_http_core_module.ngx_http_core_run_phases #如果此时请求所有阶段还没有处理完,#那么继续执行直到所有阶段都走完if 请求行和请求头收到了但是请求体还没收到&&请求体不可丢弃: #收完请求头后就会设置#read->handler=ngx_http_reqeust_handler#这个函数主要用来处理不能一次性读完写完的情况c->read->handler = ngx_http_request_handler;c->write->handler = ngx_http_request_handler;if r->buffered || c->buffered || r->postponed:  #如果一次数据没有写完,就向epollfd中注册写事件#然后就在下一次事件循环中处理,这一点和redis一样ngx_http_request.ngx_http_set_write_handler r->http_state = NGX_HTTP_WRITING_REQUEST_STATE #所以说nginx处理http请求就是一个状态机#nginx就是通过这个状态来记录当前请求处理到哪一步了#所以即使一个请求的数据还没有全部收到#即使需要中断当前执行以待下次执行#nginx都可以正确处理r->write_event_handler = ngx_http_writer;      #fd可写事件的handler。要么异步io,要么直接写发送缓冲区if wev->delayed || r->aio:                   #如果是延迟或者异步处理ngx_event.ngx_handle_write_eventif lowat>0:                              #lowat:需要低水位发送的字节数ngx_event.ngx_send_lowatif !wev->active && !wev->ready:          #不一定会再次把write_event放到epollfd中ngx_epoll_module.ngx_epoll_add_event   #define ngx_add_event  ngx_event_actions.add#add函数实际是ngx_epoll_add_event函数#注意:它是直接初始化一个结构体来初始化actions的所有字段的epoll_ctl(add,NGX_WRITE_EVENT)       #向epollfd注册写事件else:ngx_http_core_module.ngx_http_output_filter #不需要延迟处理则通过ngx_http_write就是把数据写到发送缓冲区ngx_event.ngx_handle_write_event               #把event事件注册到epollfd中ngx_epoll_module.ngx_epoll_add_event   epoll_ctl(add,NGX_WRITE_EVENT)ngx_http_request.ngx_http_finalize_connection      #!!!这个finalize_request和#!!!finalize_connection有点乱和有些错,但无伤大雅if 请求处理完了&&keepalive==true:c->read->handler=ngx_http_keepalive_handlerelse if 请求行和请求头收到了但是请求体还没收到&&请求体可丢弃ngx_add_timer(c->read,time)                   #http请求收到请求行请求头后就可以开始处理了#此时read->handler=ngx_http_request_handler#ngx_add_timer是确保在指定时间内结束整个请求if rc==NGX_OK:                              #如果某个handler返回NGX_OK就表示请求处理结束,不再调用后面的handlerreturn                            ngx_http_request.ngx_http_run_posted_requests   #请求处理完了,执行请求后的处理

1-3-1:ngx_http_request_handler流程

代码很简单,就直接贴源代码。反正就是对于一个请求,收完请求头之后对应的read->handler就设置成ngx_http_request_handler,一旦整个请求处理完毕,如果keepalive为false,那么该连接就会关闭,下一次新请求到来的时候就要重新走一遍accept、ngx_http_wait_request_handler,如果keepalive为true,那么连接就不会关闭,在ngx_http_finalize_request的时候就会把read->handler设置成ngx_http_keepalive_handler,该连接上的下次请求到来的时候就是走ngx_http_keepalive_handler。至于stream read流程放下了,还有ngx_read/write_event_handler的设置就暂时跳过了,还有ngx_http_finalize_request和ngx_http_finalize_connection的部分的代码流程笔记有些乱以及有些地方是错的,但是大体是对的,暂时先放下了,反正无伤大雅。因为今天还有2分钟就下班了 2024/10/25 17:58

static void
ngx_http_request_handler(ngx_event_t *ev)
{ngx_connection_t    *c;ngx_http_request_t  *r;c = ev->data;r = c->data;ngx_http_set_log_request(c->log, r);ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,"http run request: \"%V?%V\"", &r->uri, &r->args);if (c->close) {r->main->count++;ngx_http_terminate_request(r, 0);ngx_http_run_posted_requests(c);return;}if (ev->delayed && ev->timedout) {ev->delayed = 0;ev->timedout = 0;}if (ev->write) {r->write_event_handler(r);} else {r->read_event_handler(r);        #比如对于某些不需要的请求体,直接丢弃#即此时的read_event_handler为ngx_http_discard_request_body即丢弃请求体并清除定时器}ngx_http_run_posted_requests(c);
}

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

在docker里创建 bridge 网络联通不同容器

1.网络创建: docker network create --subnet192.168.1.0/24 --gateway192.168.1.1 uav_management 2.查看网络: docker network ls 3.给已经创建的容器分配ip: docker network connect --ip 192.168.1.10 uav_management 容器名/容器id 示例&#xf…

影响神经网络速度的因素- FLOPs、MAC、并行度以及计算平台

影响神经网络速度的四个主要因素分别是 FLOPs(浮点操作数)、MAC(内存访问成本)、并行度以及计算平台。这些因素共同作用,直接影响到神经网络的计算速度和资源需求。 1. FLOPs(Floating Point Operations&a…

基于STM32单片机太阳能充电循迹避障小车

本设计基于STM32单片机太阳能充电循迹避障小车,以STM32单片机为微控制器核心,在太阳能板对车载电池充电情况下配合传感器能够实现循迹避障行驶的小车的设计过程。小车采用3.7V锂电池作为电源,太阳能板接入TP4056充电模块,使用MT36…

高效集成金蝶云星空销售出库单的解决方案

审核销售出库单(金蝶->金蝶)集成方案 在企业日常运营中,销售出库单的审核是一个关键环节。为了确保数据的准确性和及时性,我们采用了轻易云数据集成平台,将金蝶云星空系统中的销售出库单数据无缝集成到另一个金蝶云星空系统中。本次案例将…

SpringBoot中使用RESTful风格

文章目录 SpringBoot中使用RESTful风格一、引言二、SpringBoot与RESTful风格1、RESTful风格简介2、SpringBoot中的RESTful注解2.1、代码示例 三、SpringBoot核心配置四、总结 SpringBoot中使用RESTful风格 一、引言 在现代Web开发中,RESTful架构风格因其简洁性和易…

DAY21|二叉树Part08|LeetCode: 669. 修剪二叉搜索树、108.将有序数组转换为二叉搜索树、538.把二叉搜索树转换为累加树

目录 LeetCode: 669. 修剪二叉搜索树 基本思路 C代码 LeetCode: 108.将有序数组转换为二叉搜索树 基本思路 C代码 LeetCode: 538.把二叉搜索树转换为累加树 基本思路 C代码 LeetCode: 669. 修剪二叉搜索树 力扣代码链接 文字讲解:LeetCode: 669. 修剪二叉搜…

ubuntu20.04安装ros与rosdep

目录 前置配置 配置apt清华源 配置ros软件源 添加ros安装源(中科大软件源) 设置秘钥 更新源 ros安装 安装ros 初始化 rosdep 更新 rosdep 设置环境变量 安装 rosinstall 安装验证 启动海龟仿真器 操控海龟仿真器 rosdep安装更新 安装 使用…

高亚科技签约酸动力,助力研发管理数字化升级

近日,中国企业管理软件资深服务商高亚科技与广东酸动力生物科技有限公司(以下简称“酸动力”)正式签署合作协议。借助高亚科技的8Manage PM项目管理软件,酸动力将进一步优化项目过程跟踪与节点监控,提升研发成果的高效…

CSRF与SSRF

csrf(跨站请求伪造)的原理: csrf全称是跨站请求伪造(cross-site request forgery),也被称为one-click attack 或者 session riding scrf攻击利用网站对于用户网页浏览器的信任,劫持用户当前已登录的web应用程序,去执行分用户本意的操作。 利…

享元模式-实现大颗粒度对象缓存机制

详解 享元模式是一种结构型设计模式,其主要目的是通过共享尽可能多的相同部分来有效地支持大量细粒度的对象。它通过将对象的属性分为内在属性(可以共享、不随环境变化的部分)和外在属性(根据场景变化、不能共享的部分&#xff0…

HTML 基础标签——结构化标签<html>、<head>、<body>

文章目录 1. <html> 标签2. <head> 标签3. <body> 标签4. <div> 标签5. <span> 标签小结 在 HTML 文档中&#xff0c;使用特定的结构标签可以有效地组织和管理网页内容。这些标签不仅有助于浏览器正确解析和渲染页面&#xff0c;还能提高网页的可…

新华三H3CNE网络工程师认证—VLAN的配置

VLAN&#xff08;虚拟局域网&#xff09;是一种在逻辑上划分网络的技术&#xff0c;它可以将一个物理网络分割成多个虚拟网络&#xff0c;从而实现不同组的设备之间的隔离。在配置VLAN时&#xff0c;通常涉及到三种端口类型&#xff1a;Access、Trunk和Hybrid。Access端口用于连…

R语言*号标识显著性差异判断组间差异是否具有统计意义

前言 该R代码用于对Iris数据集进行多组比较分析&#xff0c;探讨不同鸢尾花品种在不同测量变量&#xff08;花萼和花瓣长度与宽度&#xff09;上的显著性差异。通过将数据转换为长格式&#xff0c;并利用ANOVA和Tukey检验&#xff0c;代码生成了不同品种间的显著性标记&#x…

手边酒店多商户版V2源码独立部署_博纳软云

新版采用laraveluniapp开发&#xff0c;为更多平台小程序开发提供坚实可靠的底层架构基础。后台UI全部重写&#xff0c;兼容手机端管理。 全新架构、会员卡、钟点房、商城、点餐、商户独立管理

Multi Agents协作机制设计及实践

01 多智能体协作机制的背景概述 在前述博客中&#xff0c;我们利用LangChain、AutoGen等开发框架构建了一个数据多智能体的平台&#xff0c;并使用了LangChain的Multi-Agents框架。然而&#xff0c;在实施过程中&#xff0c;我们发现现有的框架存在一些局限性&#xff0c;这些…

ReactPress—基于React的免费开源博客CMS内容管理系统

ReactPress Github项目地址&#xff1a;https://github.com/fecommunity/reactpress 欢迎提出宝贵的建议&#xff0c;感谢Star。 ![ReactPress](https://i-blog.csdnimg.cn/direct/0720f155edaa4eadba796f4d96d394d7.png#pic_center ReactPress 是使用React开发的开源发布平台&…

如何在一个 Docker 容器中运行多个进程 ?

在容器化的世界里&#xff0c;Docker 彻底改变了开发人员构建、发布和运行应用程序的方式。Docker 容器封装了运行应用程序所需的所有依赖项&#xff0c;使其易于跨不同环境一致地部署。然而&#xff0c;在单个 Docker 容器中管理多个进程可能具有挑战性&#xff0c;这就是 Sup…

【JavaEE初阶 — 多线程】线程安全问题 & synchronized

目录 1. 什么是线程安全问题 (1) 观察线程不安全 (2) 线程安全的概念 2. 造成线程安全的原因 (1)线程调度的随机性 问题描述 解决方案 (2)修改共享数据&#xff06;原子性问题 问题描述 解决方案 3.synchronized 关键字 1. synchronized 的特性 (1) …

产品经理的重要性

一直觉得产品经理很重要&#xff0c;这几年写了好几篇和产品经理相关的思考。2020年写过对产品经理的一些思考的文章&#xff0c;2021年&#xff0c;写了一篇对如何分析项目的思考&#xff0c;2024年写了如何与PM探讨项目。 今天还想再写一篇&#xff0c;主要是最近很有感慨。…

Hunyuan-Large:推动AI技术进步的下一代语言模型

腾讯近期推出了基于Transformer架构的混合专家&#xff08;MoE&#xff09;模型——Hunyuan-Large&#xff08;Hunyuan-MoE-A52B&#xff09;。该模型目前是业界开源的最大MoE模型之一&#xff0c;拥有3890亿总参数和520亿激活参数&#xff0c;展示了极强的计算能力和资源优化优…