nginx平滑升级
1.平滑升级操作
1.1 备份安装目录下的nginx
cd /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/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…...
[教你传话,表白,写信]
第一步 关注飞鸽传话助手 第二部 点击链接进入 第三步 点击发送,输入内容 第四步 就可以收到了...
Python|GIF 解析与构建(5):手搓截屏和帧率控制
目录 Python|GIF 解析与构建(5):手搓截屏和帧率控制 一、引言 二、技术实现:手搓截屏模块 2.1 核心原理 2.2 代码解析:ScreenshotData类 2.2.1 截图函数:capture_screen 三、技术实现&…...
7.4.分块查找
一.分块查找的算法思想: 1.实例: 以上述图片的顺序表为例, 该顺序表的数据元素从整体来看是乱序的,但如果把这些数据元素分成一块一块的小区间, 第一个区间[0,1]索引上的数据元素都是小于等于10的, 第二…...
【力扣数据库知识手册笔记】索引
索引 索引的优缺点 优点1. 通过创建唯一性索引,可以保证数据库表中每一行数据的唯一性。2. 可以加快数据的检索速度(创建索引的主要原因)。3. 可以加速表和表之间的连接,实现数据的参考完整性。4. 可以在查询过程中,…...
通过Wrangler CLI在worker中创建数据库和表
官方使用文档:Getting started Cloudflare D1 docs 创建数据库 在命令行中执行完成之后,会在本地和远程创建数据库: npx wranglerlatest d1 create prod-d1-tutorial 在cf中就可以看到数据库: 现在,您的Cloudfla…...
Module Federation 和 Native Federation 的比较
前言 Module Federation 是 Webpack 5 引入的微前端架构方案,允许不同独立构建的应用在运行时动态共享模块。 Native Federation 是 Angular 官方基于 Module Federation 理念实现的专为 Angular 优化的微前端方案。 概念解析 Module Federation (模块联邦) Modul…...
关于 WASM:1. WASM 基础原理
一、WASM 简介 1.1 WebAssembly 是什么? WebAssembly(WASM) 是一种能在现代浏览器中高效运行的二进制指令格式,它不是传统的编程语言,而是一种 低级字节码格式,可由高级语言(如 C、C、Rust&am…...
Element Plus 表单(el-form)中关于正整数输入的校验规则
目录 1 单个正整数输入1.1 模板1.2 校验规则 2 两个正整数输入(联动)2.1 模板2.2 校验规则2.3 CSS 1 单个正整数输入 1.1 模板 <el-formref"formRef":model"formData":rules"formRules"label-width"150px"…...
优选算法第十二讲:队列 + 宽搜 优先级队列
优选算法第十二讲:队列 宽搜 && 优先级队列 1.N叉树的层序遍历2.二叉树的锯齿型层序遍历3.二叉树最大宽度4.在每个树行中找最大值5.优先级队列 -- 最后一块石头的重量6.数据流中的第K大元素7.前K个高频单词8.数据流的中位数 1.N叉树的层序遍历 2.二叉树的锯…...
【7色560页】职场可视化逻辑图高级数据分析PPT模版
7种色调职场工作汇报PPT,橙蓝、黑红、红蓝、蓝橙灰、浅蓝、浅绿、深蓝七种色调模版 【7色560页】职场可视化逻辑图高级数据分析PPT模版:职场可视化逻辑图分析PPT模版https://pan.quark.cn/s/78aeabbd92d1...
Selenium常用函数介绍
目录 一,元素定位 1.1 cssSeector 1.2 xpath 二,操作测试对象 三,窗口 3.1 案例 3.2 窗口切换 3.3 窗口大小 3.4 屏幕截图 3.5 关闭窗口 四,弹窗 五,等待 六,导航 七,文件上传 …...
