nginx平滑升级
1.平滑升级操作
1.1 备份安装目录下的nginx
cd /usr/local/nginx/sbin
mv nginx nginx.bak
1.2 复制objs目录下的nginx到当前sbin目录下
cp /opt/software/nginx/nginx-1.20.2/objs/nginx /usr/local/nginx/sbin/
1.3 发送信号user2给nginx老版本对应的进程
kill -user2 'more /usr/local/logs/nginx.pid'
1.4 发送信号quit给nginx老版本的进程
kill -quit 'more /usr/local/nginx/logs/nginx.pid.oldbin'
2. 源码解析
平滑升级中,在处理user2信号时,会将ngx_change_binary标识置为1,master进程在处理循环中,如果识别到ngx_change_binary为1,执行ngx_exec_new_binary,将监听套接字放到环境变量中,同时会重命令pid的文件名,便于下次终止老版本的进程,fork新进程,新进程执行新版本的逻辑。
2.1 如何处理信号
依托于ngx_signal_t数据结构
2.1.1 ngx_signal_t
typedef struct {int signo;char *signame;char *name;void (*handler)(int signo, siginfo_t *siginfo, void *ucontext);
} ngx_signal_t;
全局变量signals是ngx_signal_t的数组,记录了需要处理的信号,信号名以及对应的信号处理函数
2.1.2 singals
ngx_signal_t signals[] = {{ ngx_signal_value(NGX_RECONFIGURE_SIGNAL),"SIG" ngx_value(NGX_RECONFIGURE_SIGNAL),"reload",ngx_signal_handler },{ ngx_signal_value(NGX_REOPEN_SIGNAL),"SIG" ngx_value(NGX_REOPEN_SIGNAL),"reopen",ngx_signal_handler },{ ngx_signal_value(NGX_NOACCEPT_SIGNAL),"SIG" ngx_value(NGX_NOACCEPT_SIGNAL),"",ngx_signal_handler },{ ngx_signal_value(NGX_TERMINATE_SIGNAL),"SIG" ngx_value(NGX_TERMINATE_SIGNAL),"stop",ngx_signal_handler },{ ngx_signal_value(NGX_SHUTDOWN_SIGNAL),"SIG" ngx_value(NGX_SHUTDOWN_SIGNAL),"quit",ngx_signal_handler },{ ngx_signal_value(NGX_CHANGEBIN_SIGNAL),"SIG" ngx_value(NGX_CHANGEBIN_SIGNAL),"",ngx_signal_handler },{ SIGALRM, "SIGALRM", "", ngx_signal_handler },{ SIGINT, "SIGINT", "", ngx_signal_handler },{ SIGIO, "SIGIO", "", ngx_signal_handler },{ SIGCHLD, "SIGCHLD", "", ngx_signal_handler },{ SIGSYS, "SIGSYS, SIG_IGN", "", NULL },{ SIGPIPE, "SIGPIPE, SIG_IGN", "", NULL },{ 0, NULL, "", NULL }
};
ngx_init_signals是在nginx启动时向内核注册信号处理
2.1.3 ngx_init_signals
ngx_int_t
ngx_init_signals(ngx_log_t *log)
{ngx_signal_t *sig;struct sigaction sa;for (sig = signals; sig->signo != 0; sig++) {ngx_memzero(&sa, sizeof(struct sigaction));if (sig->handler) {sa.sa_sigaction = sig->handler;sa.sa_flags = SA_SIGINFO;} else {sa.sa_handler = SIG_IGN;}sigemptyset(&sa.sa_mask);if (sigaction(sig->signo, &sa, NULL) == -1) {
#if (NGX_VALGRIND)ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,"sigaction(%s) failed, ignored", sig->signame);
#elsengx_log_error(NGX_LOG_EMERG, log, ngx_errno,"sigaction(%s) failed", sig->signame);return NGX_ERROR;
#endif}}return NGX_OK;
}
在处理USR2信号时,会将ngx_change_binary标识设置为1
2.1.4 ngx_signal_handler
static void
ngx_signal_handler(int signo, siginfo_t *siginfo, void *ucontext)
{char *action;ngx_int_t ignore;ngx_err_t err;ngx_signal_t *sig;ignore = 0;err = ngx_errno;for (sig = signals; sig->signo != 0; sig++) {if (sig->signo == signo) {break;}}ngx_time_sigsafe_update();action = "";switch (ngx_process) {case NGX_PROCESS_MASTER:case NGX_PROCESS_SINGLE:switch (signo) {case ngx_signal_value(NGX_SHUTDOWN_SIGNAL):ngx_quit = 1;action = ", shutting down";break;case ngx_signal_value(NGX_TERMINATE_SIGNAL):case SIGINT:ngx_terminate = 1;action = ", exiting";break;case ngx_signal_value(NGX_NOACCEPT_SIGNAL):if (ngx_daemonized) {ngx_noaccept = 1;action = ", stop accepting connections";}break;case ngx_signal_value(NGX_RECONFIGURE_SIGNAL):ngx_reconfigure = 1;action = ", reconfiguring";break;case ngx_signal_value(NGX_REOPEN_SIGNAL):ngx_reopen = 1;action = ", reopening logs";break;case ngx_signal_value(NGX_CHANGEBIN_SIGNAL):if (ngx_getppid() == ngx_parent || ngx_new_binary > 0) {/** Ignore the signal in the new binary if its parent is* not changed, i.e. the old binary's process is still* running. Or ignore the signal in the old binary's* process if the new binary's process is already running.*/action = ", ignoring";ignore = 1;break;}ngx_change_binary = 1;action = ", changing binary";break;case SIGALRM:ngx_sigalrm = 1;break;case SIGIO:ngx_sigio = 1;break;case SIGCHLD:ngx_reap = 1;break;}break;case NGX_PROCESS_WORKER:case NGX_PROCESS_HELPER:switch (signo) {case ngx_signal_value(NGX_NOACCEPT_SIGNAL):if (!ngx_daemonized) {break;}ngx_debug_quit = 1;/* fall through */case ngx_signal_value(NGX_SHUTDOWN_SIGNAL):ngx_quit = 1;action = ", shutting down";break;case ngx_signal_value(NGX_TERMINATE_SIGNAL):case SIGINT:ngx_terminate = 1;action = ", exiting";break;case ngx_signal_value(NGX_REOPEN_SIGNAL):ngx_reopen = 1;action = ", reopening logs";break;case ngx_signal_value(NGX_RECONFIGURE_SIGNAL):case ngx_signal_value(NGX_CHANGEBIN_SIGNAL):case SIGIO:action = ", ignoring";break;}break;}if (siginfo && siginfo->si_pid) {ngx_log_error(NGX_LOG_NOTICE, ngx_cycle->log, 0,"signal %d (%s) received from %P%s",signo, sig->signame, siginfo->si_pid, action);} else {ngx_log_error(NGX_LOG_NOTICE, ngx_cycle->log, 0,"signal %d (%s) received%s",signo, sig->signame, action);}if (ignore) {ngx_log_error(NGX_LOG_CRIT, ngx_cycle->log, 0,"the changing binary signal is ignored: ""you should shutdown or terminate ""before either old or new binary's process");}if (signo == SIGCHLD) {ngx_process_get_status();}ngx_set_errno(err);
}

2.2 master进程处理ngx_change_binary标识
master进程处理循环中会检测ngx_change_binary标识,如果置为1,会调用ngx_exec_new_binary,fork新进程,新进行执行execv来调用新的二进制文件
void
ngx_master_process_cycle(ngx_cycle_t *cycle)
{char *title;u_char *p;size_t size;ngx_int_t i;ngx_uint_t sigio;sigset_t set;struct itimerval itv;ngx_uint_t live;ngx_msec_t delay;ngx_core_conf_t *ccf;sigemptyset(&set);sigaddset(&set, SIGCHLD);sigaddset(&set, SIGALRM);sigaddset(&set, SIGIO);sigaddset(&set, SIGINT);sigaddset(&set, ngx_signal_value(NGX_RECONFIGURE_SIGNAL));sigaddset(&set, ngx_signal_value(NGX_REOPEN_SIGNAL));sigaddset(&set, ngx_signal_value(NGX_NOACCEPT_SIGNAL));sigaddset(&set, ngx_signal_value(NGX_TERMINATE_SIGNAL));sigaddset(&set, ngx_signal_value(NGX_SHUTDOWN_SIGNAL));sigaddset(&set, ngx_signal_value(NGX_CHANGEBIN_SIGNAL));if (sigprocmask(SIG_BLOCK, &set, NULL) == -1) {ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,"sigprocmask() failed");}sigemptyset(&set);size = sizeof(master_process);for (i = 0; i < ngx_argc; i++) {size += ngx_strlen(ngx_argv[i]) + 1;}title = ngx_pnalloc(cycle->pool, size);if (title == NULL) {/* fatal */exit(2);}p = ngx_cpymem(title, master_process, sizeof(master_process) - 1);for (i = 0; i < ngx_argc; i++) {*p++ = ' ';p = ngx_cpystrn(p, (u_char *) ngx_argv[i], size);}ngx_setproctitle(title);ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);ngx_start_worker_processes(cycle, ccf->worker_processes,NGX_PROCESS_RESPAWN);ngx_start_cache_manager_processes(cycle, 0);ngx_new_binary = 0;delay = 0;sigio = 0;live = 1;for ( ;; ) {if (delay) {if (ngx_sigalrm) {sigio = 0;delay *= 2;ngx_sigalrm = 0;}ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,"termination cycle: %M", delay);itv.it_interval.tv_sec = 0;itv.it_interval.tv_usec = 0;itv.it_value.tv_sec = delay / 1000;itv.it_value.tv_usec = (delay % 1000 ) * 1000;if (setitimer(ITIMER_REAL, &itv, NULL) == -1) {ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,"setitimer() failed");}}ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "sigsuspend");sigsuspend(&set);ngx_time_update();ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,"wake up, sigio %i", sigio);if (ngx_reap) {ngx_reap = 0;ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "reap children");live = ngx_reap_children(cycle);}if (!live && (ngx_terminate || ngx_quit)) {ngx_master_process_exit(cycle);}if (ngx_terminate) {if (delay == 0) {delay = 50;}if (sigio) {sigio--;continue;}sigio = ccf->worker_processes + 2 /* cache processes */;if (delay > 1000) {ngx_signal_worker_processes(cycle, SIGKILL);} else {ngx_signal_worker_processes(cycle,ngx_signal_value(NGX_TERMINATE_SIGNAL));}continue;}if (ngx_quit) {ngx_signal_worker_processes(cycle,ngx_signal_value(NGX_SHUTDOWN_SIGNAL));ngx_close_listening_sockets(cycle);continue;}if (ngx_reconfigure) {ngx_reconfigure = 0;if (ngx_new_binary) {ngx_start_worker_processes(cycle, ccf->worker_processes,NGX_PROCESS_RESPAWN);ngx_start_cache_manager_processes(cycle, 0);ngx_noaccepting = 0;continue;}ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "reconfiguring");cycle = ngx_init_cycle(cycle);if (cycle == NULL) {cycle = (ngx_cycle_t *) ngx_cycle;continue;}ngx_cycle = cycle;ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx,ngx_core_module);ngx_start_worker_processes(cycle, ccf->worker_processes,NGX_PROCESS_JUST_RESPAWN);ngx_start_cache_manager_processes(cycle, 1);/* allow new processes to start */ngx_msleep(100);live = 1;ngx_signal_worker_processes(cycle,ngx_signal_value(NGX_SHUTDOWN_SIGNAL));}if (ngx_restart) {ngx_restart = 0;ngx_start_worker_processes(cycle, ccf->worker_processes,NGX_PROCESS_RESPAWN);ngx_start_cache_manager_processes(cycle, 0);live = 1;}if (ngx_reopen) {ngx_reopen = 0;ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "reopening logs");ngx_reopen_files(cycle, ccf->user);ngx_signal_worker_processes(cycle,ngx_signal_value(NGX_REOPEN_SIGNAL));}if (ngx_change_binary) {ngx_change_binary = 0;ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "changing binary");ngx_new_binary = ngx_exec_new_binary(cycle, ngx_argv);}if (ngx_noaccept) {ngx_noaccept = 0;ngx_noaccepting = 1;ngx_signal_worker_processes(cycle,ngx_signal_value(NGX_SHUTDOWN_SIGNAL));}}
}

2.2.1 ngx_exec_new_binary
将cycle中的listening监听套接字放到环境变量中
将pid文件生命为oldpid
创建新进程,执行新的二进制文件
ngx_pid_t
ngx_exec_new_binary(ngx_cycle_t *cycle, char *const *argv)
{char **env, *var;u_char *p;ngx_uint_t i, n;ngx_pid_t pid;ngx_exec_ctx_t ctx;ngx_core_conf_t *ccf;ngx_listening_t *ls;ngx_memzero(&ctx, sizeof(ngx_exec_ctx_t));ctx.path = argv[0];ctx.name = "new binary process";ctx.argv = argv;n = 2;env = ngx_set_environment(cycle, &n);if (env == NULL) {return NGX_INVALID_PID;}var = ngx_alloc(sizeof(NGINX_VAR)+ cycle->listening.nelts * (NGX_INT32_LEN + 1) + 2,cycle->log);if (var == NULL) {ngx_free(env);return NGX_INVALID_PID;}p = ngx_cpymem(var, NGINX_VAR "=", sizeof(NGINX_VAR));ls = cycle->listening.elts;for (i = 0; i < cycle->listening.nelts; i++) {p = ngx_sprintf(p, "%ud;", ls[i].fd);}*p = '\0';env[n++] = var;#if (NGX_SETPROCTITLE_USES_ENV)/* allocate the spare 300 bytes for the new binary process title */env[n++] = "SPARE=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX""XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX""XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX""XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX""XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";#endifenv[n] = NULL;#if (NGX_DEBUG){char **e;for (e = env; *e; e++) {ngx_log_debug1(NGX_LOG_DEBUG_CORE, cycle->log, 0, "env: %s", *e);}}
#endifctx.envp = (char *const *) env;ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);if (ngx_rename_file(ccf->pid.data, ccf->oldpid.data) == NGX_FILE_ERROR) {ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,ngx_rename_file_n " %s to %s failed ""before executing new binary process \"%s\"",ccf->pid.data, ccf->oldpid.data, argv[0]);ngx_free(env);ngx_free(var);return NGX_INVALID_PID;}pid = ngx_execute(cycle, &ctx);if (pid == NGX_INVALID_PID) {if (ngx_rename_file(ccf->oldpid.data, ccf->pid.data)== NGX_FILE_ERROR){ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,ngx_rename_file_n " %s back to %s failed after ""an attempt to execute new binary process \"%s\"",ccf->oldpid.data, ccf->pid.data, argv[0]);}}ngx_free(env);ngx_free(var);return pid;
}ngx_pid_t
ngx_execute(ngx_cycle_t *cycle, ngx_exec_ctx_t *ctx)
{return ngx_spawn_process(cycle, ngx_execute_proc, ctx, ctx->name,NGX_PROCESS_DETACHED);
}static void
ngx_execute_proc(ngx_cycle_t *cycle, void *data)
{ngx_exec_ctx_t *ctx = data;if (execve(ctx->path, ctx->argv, ctx->envp) == -1) {ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,"execve() failed while executing %s \"%s\"",ctx->name, ctx->path);}exit(1);
}
2.3 旧版本的master退出
此时,nginx会有两个进程,一个是新版本的,一个是旧版本的,需要将旧版本的结束,其在处理quit信号时,主进程会向work进程发送quit信号,现时主进程也会关闭监听套接字
if (ngx_quit) {ngx_signal_worker_processes(cycle,ngx_signal_value(NGX_SHUTDOWN_SIGNAL));ngx_close_listening_sockets(cycle);continue;
}
2.3.1 ngx_signal_worker_processes
向工作进程发送quit消息
static void
ngx_signal_worker_processes(ngx_cycle_t *cycle, int signo)
{ngx_int_t i;ngx_err_t err;ngx_channel_t ch;ngx_memzero(&ch, sizeof(ngx_channel_t));#if (NGX_BROKEN_SCM_RIGHTS)ch.command = 0;#elseswitch (signo) {case ngx_signal_value(NGX_SHUTDOWN_SIGNAL):ch.command = NGX_CMD_QUIT;break;case ngx_signal_value(NGX_TERMINATE_SIGNAL):ch.command = NGX_CMD_TERMINATE;break;case ngx_signal_value(NGX_REOPEN_SIGNAL):ch.command = NGX_CMD_REOPEN;break;default:ch.command = 0;}#endifch.fd = -1;for (i = 0; i < ngx_last_process; i++) {ngx_log_debug7(NGX_LOG_DEBUG_EVENT, cycle->log, 0,"child: %i %P e:%d t:%d d:%d r:%d j:%d",i,ngx_processes[i].pid,ngx_processes[i].exiting,ngx_processes[i].exited,ngx_processes[i].detached,ngx_processes[i].respawn,ngx_processes[i].just_spawn);if (ngx_processes[i].detached || ngx_processes[i].pid == -1) {continue;}if (ngx_processes[i].just_spawn) {ngx_processes[i].just_spawn = 0;continue;}if (ngx_processes[i].exiting&& signo == ngx_signal_value(NGX_SHUTDOWN_SIGNAL)){continue;}if (ch.command) {if (ngx_write_channel(ngx_processes[i].channel[0],&ch, sizeof(ngx_channel_t), cycle->log)== NGX_OK){if (signo != ngx_signal_value(NGX_REOPEN_SIGNAL)) {ngx_processes[i].exiting = 1;}continue;}}ngx_log_debug2(NGX_LOG_DEBUG_CORE, cycle->log, 0,"kill (%P, %d)", ngx_processes[i].pid, signo);if (kill(ngx_processes[i].pid, signo) == -1) {err = ngx_errno;ngx_log_error(NGX_LOG_ALERT, cycle->log, err,"kill(%P, %d) failed", ngx_processes[i].pid, signo);if (err == NGX_ESRCH) {ngx_processes[i].exited = 1;ngx_processes[i].exiting = 0;ngx_reap = 1;}continue;}if (signo != ngx_signal_value(NGX_REOPEN_SIGNAL)) {ngx_processes[i].exiting = 1;}}
}
2.3.2 ngx_close_listening_sockets
关闭监听套接字
void
ngx_close_listening_sockets(ngx_cycle_t *cycle)
{ngx_uint_t i;ngx_listening_t *ls;ngx_connection_t *c;if (ngx_event_flags & NGX_USE_IOCP_EVENT) {return;}ngx_accept_mutex_held = 0;ngx_use_accept_mutex = 0;ls = cycle->listening.elts;for (i = 0; i < cycle->listening.nelts; i++) {c = ls[i].connection;if (c) {if (c->read->active) {if (ngx_event_flags & NGX_USE_EPOLL_EVENT) {/** it seems that Linux-2.6.x OpenVZ sends events* for closed shared listening sockets unless* the events was explicitly deleted*/ngx_del_event(c->read, NGX_READ_EVENT, 0);} else {ngx_del_event(c->read, NGX_READ_EVENT, NGX_CLOSE_EVENT);}}ngx_free_connection(c);c->fd = (ngx_socket_t) -1;}ngx_log_debug2(NGX_LOG_DEBUG_CORE, cycle->log, 0,"close listening %V #%d ", &ls[i].addr_text, ls[i].fd);if (ngx_close_socket(ls[i].fd) == -1) {ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_socket_errno,ngx_close_socket_n " %V failed", &ls[i].addr_text);}#if (NGX_HAVE_UNIX_DOMAIN)if (ls[i].sockaddr->sa_family == AF_UNIX&& ngx_process <= NGX_PROCESS_MASTER&& ngx_new_binary == 0&& (!ls[i].inherited || ngx_getppid() != ngx_parent)){u_char *name = ls[i].addr_text.data + sizeof("unix:") - 1;if (ngx_delete_file(name) == NGX_FILE_ERROR) {ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_socket_errno,ngx_delete_file_n " %s failed", name);}}#endifls[i].fd = (ngx_socket_t) -1;}cycle->listening.nelts = 0;
}
相关文章:

nginx平滑升级
1.平滑升级操作1.1 备份安装目录下的nginxcd /usr/local/nginx/sbin mv nginx nginx.bak1.2 复制objs目录下的nginx到当前sbin目录下cp /opt/software/nginx/nginx-1.20.2/objs/nginx /usr/local/nginx/sbin/1.3 发送信号user2给nginx老版本对应的进程kill -user2 more /usr/lo…...

高可用的“异地多活”架构设计
前言 后台服务可以划分为两类,有状态和无状态。高可用对于无状态的应用来说是比较简单的,无状态的应用,只需要通过 F5 或者任何代理的方式就可以很好的解决。后文描述的主要是针对有状态的服务进行分析。 服务端进行状态维护主要是通过磁盘…...

【面试题】Map和Set
1. Map和Object的区别 形式不同 // Object var obj {key1: hello,key2: 100,key3: {x: 100} } // Map var m new Map([[key1, hello],[key2, 100],[key3, {x: 100}] ])API不同 // Map的API m.set(name, 小明) // 新增 m.delete(key2) // 删除 m.has(key3) // …...

Spring之事务底层源码解析
Spring之事务底层源码解析 1、EnableTransactionManagement工作原理 开启 Spring 事务本质上就是增加了一个 Advisor,当我们使用 EnableTransactionManagement 注解来开启 Spring 事务时,该注解代理的功能就是向 Spring 容器中添加了两个 Bean…...
【华为OD机试真题 Python】创建二叉树
前言:本专栏将持续更新华为OD机试题目,并进行详细的分析与解答,包含完整的代码实现,希望可以帮助到正在努力的你。关于OD机试流程、面经、面试指导等,如有任何疑问,欢迎联系我,wechat:steven_moda;email:nansun0903@163.com;备注:CSDN。 题目描述 请按下列描达构建…...

RuoYi-Vue-Plus搭建(若依)
项目简介 1.RuoYi-Vue-Plus 是重写 RuoYi-Vue 针对 分布式集群 场景全方位升级(不兼容原框架)2.环境安装参考:https://blog.csdn.net/tongxin_tongmeng/article/details/128167926 JDK 11、MySQL 8、Redis 6.X、Maven 3.8.X、Nodejs > 12、Npm 8.X3.IDEA环境配置…...
uboot和linux内核移植流程简述
一、移植uboot流程 1、从半导体芯片厂下载对应的demo,然后编译测试demo版的uboot 开发板基本都是参考半导体厂商的 dmeo 板,而半导体厂商会在他们自己的开发板上移植好 uboot、linux kernel 和 rootfs 等,最终制作好 BSP包提供给用户。我们可…...

【CS224W】(task2)传统图机器学习和特征工程
note 和CS224W课程对应,将图的基本表示写在task1笔记中了;传统图特征工程:将节点、边、图转为d维emb,将emb送入ML模型训练Traditional ML Pipeline Hand-crafted feature ML model Hand-crafted features for graph data Node-l…...

【算法基础】并查集⭐⭐⭐⭐⭐【思路巧,代码短,面试常考】
并查集,在一些有N个元素的集合应用问题中,我们通常是在开始时让每个元素构成一个单元素的集合,然后按一定顺序将属于同一组的元素所在的集合合并,其间要反复查找一个元素在哪个集合中。这一类问题近几年来反复出现在信息学的国际国内赛题中。其特点是看似并不复杂,但数据量…...

人工智能轨道交通行业周刊-第34期(2023.2.13-2.19)
本期关键词:智慧地铁、枕簧检测选配机器人、智慧工地、接触网检修、工业缺陷检测 1 整理涉及公众号名单 1.1 行业类 RT轨道交通人民铁道世界轨道交通资讯网铁路信号技术交流北京铁路轨道交通网上榜铁路视点ITS World轨道交通联盟VSTR铁路与城市轨道交通RailMetro…...

Retrofit 网络框架源码解析(二)
目录一、Okhttp请求二、Retrofit 请求retrofit是如何封装请求的三、Retrofit的构建过程四、Retrofit构建IxxxService对象的过程(Retrofit.create())4.1 动态代理4.2 ServiceMethod4.3 okHttpCall4.4 callAdapter五、Retrofit网络请求操作一、Okhttp请求 …...

SQL Server 2008新特性——更改跟踪
在大型的数据库应用中,经常会遇到部分数据的脱机和多个数据库的合并问题。比如现在有一个全省范围使用的应用程序,每个市都部署了单独的相同的应用程序服务器和数据库服务器,每个月需要将全省所有市的数据全部汇总起来用于出全省的报表&#…...

四六级真题长难句分析与应用
一、基本结构的长难句 基本结构的长难句主要考点:断开和简化 什么是长难句? 其实就是多件事连在了一块,这时候句子就变长、变难了 分析步骤: 第一件事就是要把长难句给断开,把多件事断开成一件一件的事情࿰…...
华为OD机试 - 玩牌高手(Python) | 机试题算法+思路 【2023】
最近更新的博客 华为OD机试 - 寻找路径 | 备考思路,刷题要点,答疑 【新解法】 华为OD机试 - 五键键盘 | 备考思路,刷题要点,答疑 【新解法】 华为OD机试 - IPv4 地址转换成整数 | 备考思路,刷题要点,答疑 【新解法】 华为OD机试 - 对称美学 | 备考思路,刷题要点,答疑 …...

【论文阅读】 Few-shot object detection via Feature Reweighting
Few-shot object detection的开山之作之一 ~~ 特征学习器使用来自具有足够样本的基本类的训练数据来 提取 可推广以检测新对象类的meta features。The reweighting module将新类别中的一些support examples转换为全局向量,该全局向量indicates meta features对于检…...
现代卷积神经网络经典架构图
卷积神经网络(LeNet) LeNet 的简化版深层卷积神经网络(AlexNet) 从LeNet(左)到AlexNet(右)改进: dropOut层 - 不改变期望但是改变方差ReLU层 - 减缓梯度消失MaxPooling数…...

有关eclipse的使用tips
一、alt/键 会产生单词提示,可以提高编程速度。例如不需要辛辛苦苦的打出:System.out.println();整句,只需要在eclipse中输入syso,然后按住ALT/就会出来System.out.println();在alt键/不管用的情况下,可使用以下方法来…...
Mybatis(4)之CRUD
首先是 增 ,我们要在数据库中增加一个数据 先来看看我们之前的插入语句 <insert id"insertRole">insert into try(id,name,age) values(3,nuonuo,20)</insert> 请注意,我们这里的 insert 是固定的,但在实际的业务场…...
OSG三维渲染引擎编程学习之五十七:“第五章:OSG场景渲染” 之 “5.15 光照”
目录 第五章 OSG场景渲染 5.15 光照 5.15.1 osg::Light光 5.15.2 osg::LightSource光源 第五章 OSG场景渲染 OSG存在场景树和渲染树,“场景数”的构建在第三章“OSG场景组织”已详细阐明,本章开始深入探讨“渲染树”。 渲染树一棵以状态集(StateSet)和渲染叶(RenderLe…...

[教你传话,表白,写信]
第一步 关注飞鸽传话助手 第二部 点击链接进入 第三步 点击发送,输入内容 第四步 就可以收到了...
Golang 面试经典题:map 的 key 可以是什么类型?哪些不可以?
Golang 面试经典题:map 的 key 可以是什么类型?哪些不可以? 在 Golang 的面试中,map 类型的使用是一个常见的考点,其中对 key 类型的合法性 是一道常被提及的基础却很容易被忽视的问题。本文将带你深入理解 Golang 中…...

Python:操作 Excel 折叠
💖亲爱的技术爱好者们,热烈欢迎来到 Kant2048 的博客!我是 Thomas Kant,很开心能在CSDN上与你们相遇~💖 本博客的精华专栏: 【自动化测试】 【测试经验】 【人工智能】 【Python】 Python 操作 Excel 系列 读取单元格数据按行写入设置行高和列宽自动调整行高和列宽水平…...

相机Camera日志实例分析之二:相机Camx【专业模式开启直方图拍照】单帧流程日志详解
【关注我,后续持续新增专题博文,谢谢!!!】 上一篇我们讲了: 这一篇我们开始讲: 目录 一、场景操作步骤 二、日志基础关键字分级如下 三、场景日志如下: 一、场景操作步骤 操作步…...

3.3.1_1 检错编码(奇偶校验码)
从这节课开始,我们会探讨数据链路层的差错控制功能,差错控制功能的主要目标是要发现并且解决一个帧内部的位错误,我们需要使用特殊的编码技术去发现帧内部的位错误,当我们发现位错误之后,通常来说有两种解决方案。第一…...

8k长序列建模,蛋白质语言模型Prot42仅利用目标蛋白序列即可生成高亲和力结合剂
蛋白质结合剂(如抗体、抑制肽)在疾病诊断、成像分析及靶向药物递送等关键场景中发挥着不可替代的作用。传统上,高特异性蛋白质结合剂的开发高度依赖噬菌体展示、定向进化等实验技术,但这类方法普遍面临资源消耗巨大、研发周期冗长…...
Golang dig框架与GraphQL的完美结合
将 Go 的 Dig 依赖注入框架与 GraphQL 结合使用,可以显著提升应用程序的可维护性、可测试性以及灵活性。 Dig 是一个强大的依赖注入容器,能够帮助开发者更好地管理复杂的依赖关系,而 GraphQL 则是一种用于 API 的查询语言,能够提…...
AI编程--插件对比分析:CodeRider、GitHub Copilot及其他
AI编程插件对比分析:CodeRider、GitHub Copilot及其他 随着人工智能技术的快速发展,AI编程插件已成为提升开发者生产力的重要工具。CodeRider和GitHub Copilot作为市场上的领先者,分别以其独特的特性和生态系统吸引了大量开发者。本文将从功…...

c#开发AI模型对话
AI模型 前面已经介绍了一般AI模型本地部署,直接调用现成的模型数据。这里主要讲述讲接口集成到我们自己的程序中使用方式。 微软提供了ML.NET来开发和使用AI模型,但是目前国内可能使用不多,至少实践例子很少看见。开发训练模型就不介绍了&am…...
docker 部署发现spring.profiles.active 问题
报错: org.springframework.boot.context.config.InvalidConfigDataPropertyException: Property spring.profiles.active imported from location class path resource [application-test.yml] is invalid in a profile specific resource [origin: class path re…...
【前端异常】JavaScript错误处理:分析 Uncaught (in promise) error
在前端开发中,JavaScript 异常是不可避免的。随着现代前端应用越来越多地使用异步操作(如 Promise、async/await 等),开发者常常会遇到 Uncaught (in promise) error 错误。这个错误是由于未正确处理 Promise 的拒绝(r…...