当前位置: 首页 > news >正文

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…...

高可用的“异地多活”架构设计

前言 后台服务可以划分为两类&#xff0c;有状态和无状态。高可用对于无状态的应用来说是比较简单的&#xff0c;无状态的应用&#xff0c;只需要通过 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&#xff0c;当我们使用 EnableTransactionManagement 注解来开启 Spring 事务时&#xff0c;该注解代理的功能就是向 Spring 容器中添加了两个 Bean&#xf…...

【华为OD机试真题 Python】创建二叉树

前言:本专栏将持续更新华为OD机试题目,并进行详细的分析与解答,包含完整的代码实现,希望可以帮助到正在努力的你。关于OD机试流程、面经、面试指导等,如有任何疑问,欢迎联系我,wechat:steven_moda;email:nansun0903@163.com;备注:CSDN。 题目描述 请按下列描达构建…...

RuoYi-Vue-Plus搭建(若依)

项目简介 1.RuoYi-Vue-Plus 是重写 RuoYi-Vue 针对 分布式集群 场景全方位升级(不兼容原框架)2.环境安装参考&#xff1a;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&#xff0c;然后编译测试demo版的uboot 开发板基本都是参考半导体厂商的 dmeo 板&#xff0c;而半导体厂商会在他们自己的开发板上移植好 uboot、linux kernel 和 rootfs 等&#xff0c;最终制作好 BSP包提供给用户。我们可…...

【CS224W】(task2)传统图机器学习和特征工程

note 和CS224W课程对应&#xff0c;将图的基本表示写在task1笔记中了&#xff1b;传统图特征工程&#xff1a;将节点、边、图转为d维emb&#xff0c;将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)

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

Retrofit 网络框架源码解析(二)

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

SQL Server 2008新特性——更改跟踪

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

四六级真题长难句分析与应用

一、基本结构的长难句 基本结构的长难句主要考点&#xff1a;断开和简化 什么是长难句&#xff1f; 其实就是多件事连在了一块&#xff0c;这时候句子就变长、变难了 分析步骤&#xff1a; 第一件事就是要把长难句给断开&#xff0c;把多件事断开成一件一件的事情&#xff0…...

华为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转换为全局向量&#xff0c;该全局向量indicates meta features对于检…...

现代卷积神经网络经典架构图

卷积神经网络&#xff08;LeNet&#xff09; LeNet 的简化版深层卷积神经网络&#xff08;AlexNet&#xff09; 从LeNet&#xff08;左&#xff09;到AlexNet&#xff08;右&#xff09;改进&#xff1a; dropOut层 - 不改变期望但是改变方差ReLU层 - 减缓梯度消失MaxPooling数…...

有关eclipse的使用tips

一、alt/键 会产生单词提示&#xff0c;可以提高编程速度。例如不需要辛辛苦苦的打出&#xff1a;System.out.println();整句&#xff0c;只需要在eclipse中输入syso&#xff0c;然后按住ALT/就会出来System.out.println();在alt键/不管用的情况下&#xff0c;可使用以下方法来…...

Mybatis(4)之CRUD

首先是 增 &#xff0c;我们要在数据库中增加一个数据 先来看看我们之前的插入语句 <insert id"insertRole">insert into try(id,name,age) values(3,nuonuo,20)</insert> 请注意&#xff0c;我们这里的 insert 是固定的&#xff0c;但在实际的业务场…...

OSG三维渲染引擎编程学习之五十七:“第五章:OSG场景渲染” 之 “5.15 光照”

目录 第五章 OSG场景渲染 5.15 光照 5.15.1 osg::Light光 5.15.2 osg::LightSource光源 第五章 OSG场景渲染 OSG存在场景树和渲染树,“场景数”的构建在第三章“OSG场景组织”已详细阐明,本章开始深入探讨“渲染树”。 渲染树一棵以状态集(StateSet)和渲染叶(RenderLe…...

[教你传话,表白,写信]

第一步 关注飞鸽传话助手 第二部 点击链接进入 第三步 点击发送,输入内容 第四步 就可以收到了...

物联网在智慧农业中的应用

随看现代科技的不断发展&#xff0c;近年来我国农业的进步是显而易见的。从八九十年代农业生产以人力为主&#xff0c;到之后的机械渐渐代替人力&#xff0c;再到如今物联网技术在农业领域的应用&#xff0c;多种前沿技术应用于农业物联网&#xff0c;对智慧农业生产的各个环节…...

【RabbitMQ】Windows 安装 RabbitMQ

文章目录工具下载Eralng 安装与配置RabbitMQ 安装工具下载 RabbitMQ 3.7.4版本 网盘链接&#xff1a;https://pan.baidu.com/s/1pO6Q8fUbiMrtclpq2KqVVQ?pwdgf29 提取码&#xff1a;gf29 Eralng 网盘链接&#xff1a;https://pan.baidu.com/s/1irf8fgK77k8T9QzsIRwa7g?pwd9…...

MQTT8-MQTT在智能汽车公司的实际应用

一、引言 智能汽车的发展概况 智能汽车作为一种新兴的汽车类型,它的发展历程可以追溯到20世纪90年代。近年来,随着人工智能、物联网、自动驾驶等技术的发展,智能汽车迅速崛起,已经成为汽车行业的一股重要趋势。 智能汽车通过安装传感器、通讯设备和计算设备等,实现了车…...

在elasticsearch8.3中安装elasticsearch-analysis-ik中文分词插件

title: 在elasticsearch8.3中安装elasticsearch-analysis-ik中文分词插件 date: 2022-08-28 00:00:00 tags: ElasticSearchelasticsearch-analysis-ik中文分词插件 categories:ElasticSearch 安装 手动下载 在官方发布页面下载安装包 elasticsearch-analysis-ik-[版本].zip&…...

初识K8s

概览 k8s 概念和架构从零搭建K8s 集群k8s 核心概念搭建集群监控平台搭建高可用k8s集群集群环境 实际部署项目 k8s 概念和架构 1、K8S概述和特性 概述&#xff1a; k8s是谷歌在2014年开源的容器化集群管理系统使用k8s进行容器化应用部署使用k8s利于应用扩展k8s目标实施让部…...

搭建企业级docker仓库—Harbor

一、简介 docker 官方提供的私有仓库 registry&#xff0c;用起来虽然简单 &#xff0c;但在管理的功能上存在不足。 Harbor是一个用于存储和分发Docker镜像的企业级Registry服务器&#xff0c;harbor使用的是官方的docker registry(v2命名是distribution)服务去完成。harbor在…...

【Linux】shell中运算符(整数、字符串)

文章目录1. 整数1.1、算数运算符1.1.1 加减乘除运算1.1.2 号关系运算1.1.2.1 (赋值)、(等于)、!(不等于)的使用1.1.2.2 >、>、<、<的使用1.2 $((运算式)) 双括号形式 、 $[运算式] 语法 进行运算1.3 -eq关系运算符1.4 、、-eq的区别2 字符串2.1 字符串运算3 逻辑运…...

【从零单排Golang】第八话:通过cache缓存模块示范interface该怎么用

和许多面向对象的编程语言一样&#xff0c;Golang也存在interface接口这样的概念。interface相当于是一个中间层&#xff0c;下游只需要关心interface实现了什么行为&#xff0c;利用这些行为做些业务级别事情&#xff0c;而上游则负责实现interface&#xff0c;把这些行为具象…...

解析从Linux零拷贝深入了解Linux-I/O(上)

本文将从文件传输场景以及零拷贝技术深究 Linux I/O 的发展过程、优化手段以及实际应用。前言 存储器是计算机的核心部件之一&#xff0c;在完全理想的状态下&#xff0c;存储器应该要同时具备以下三种特性&#xff1a; 速度足够快&#xff1a;存储器的存取速度应当快于 CPU …...

JavaScript系列之公有、私有和静态属性和方法

文章の目录一、公有属性、公有方法1、定义2、理解3、ES54、ES6二、私有属性、私有方法1、定义2、理解3、ES54、ES6三、静态属性、静态方法1、定义2、理解3、ES54、ES6写在最后一、公有属性、公有方法 1、定义 指的是所属这个类的所有对象都可以访问的属性&#xff0c;叫做公有…...

wordpress静态cdn/关键词搜索排名软件

0: 由于天朝的特殊&#xff0c;在国内很不好编译&#xff08;主要是依赖库下载不了&#xff09;。 所以记录下编译过程 需要的工具: debian 或者其他linux其他版本。 make,git,golang(最好1.11版本以上) 编译过程 export GOPATH/data/tidb mkdir -p /data/tidb/src/githu…...

电子商务网站建设的过程/电子商务与网络营销教案

MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code&#xff0c;并且改名为MyBatis 。2013年11月迁移到Github。一、理解什么是MyBatis&#xff1f;MyBatis 是支持普通 SQL 查询&#xff0c;存储过程和高级映射的优秀持…...

重庆网站建设设计公司/线上推广平台

计算广告学是一门由信息科学、统计学、计算机科学以及微观经济学等学科交叉融合的新兴分支学科。前MediaV首席科学家、前Yahoo&#xff01;高级科学家刘鹏开设计算广告学&#xff08;Computational Advertising&#xff09;公开课。课程地址&#xff1a; http://study.163.com…...

好看的网站界面设计/关键词优化搜索排名

导读在上篇中&#xff0c;我们已经创建了一个 shell 主循环、切分了命令输入&#xff0c;以及通过 fork 和 exec 执行命令。在这部分&#xff0c;我们将会解决剩下的问题。首先&#xff0c;cd test_dir2 命令无法修改我们的当前目录。其次&#xff0c;我们仍无法优雅地从 shell…...

怎样用mysql做网站/网络整合营销方案

抽象类前面我们讲通过继承&#xff0c;多态的方式&#xff0c;构建出扩展性良好的java程序&#xff0c;但是还不够。因为它们不能够进一步约束程序员编写程序的方式&#xff0c;而抽象类就能通过某种方式让使用它的程序员们按照某种约束进行编码。抽象类是什么&#xff1f;定义…...

asp.net 网站后台管理系统制作/今日热点新闻视频

结合前两篇文章&#xff1a; 小试Flex框架Fabrication Flex多国语言示例 加上Fabrication自身支持的元标签&#xff0c;可简化一些代码&#xff0c;但简化后也付出了一定的代码&#xff0c;那就是变量需要声明为public&#xff0c;而之前虽然繁琐&#xff0c;但却可以将其声明为…...