nginx&http 第二章 ngx 事件event配置等初始化
event事件模块,配置分为两层:ngx_events_module 事件模块 和 ngx_event_core_module 事件核心模块。
ngx_events_module:模块类型NGX_CORE_MODULE,所以此模块在最外层核心模块解析“events”命令的时候会回调ngx_events_block函数。
ngx_event_core_module:模块类型NGX_EVENT_MODULE,所以此模块在ngx_events_block函数被回调后,才能解析配置信息
最外层的events模块,类型NGX_CORE_MODULE,属于核心模块,核心模块在最开始配置文件初始化的时候,就会调用指令的命令集。所以在核心模块启动的时候就会调用events的模块配置解析指令函数:ngx_events_block
static ngx_command_t ngx_events_commands[] = {
{ ngx_string("events"),
NGX_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS,
ngx_events_block,
0,
0,
NULL },
ngx_null_command
};
static ngx_core_module_t ngx_events_module_ctx = {
ngx_string("events"),
NULL,
ngx_event_init_conf
};
/*
可以看到,ngx_events_module_ctx实现的接口只是定义了模块名字而已,ngx_core_module_t接口中定义的create_onf方法没有实现(NULL空指针即为不实现),
为什么呢?这是因为ngx_events_module模块并不会解析配置项的参数,只是在出现events配置项后会调用各事件模块去解析eventso()块内的配置项,
自然就不需要实现create_conf方法来创建存储配置项参数的结构体.
*/
//一旦在nginx.conf配置文件中找到ngx_events_module感兴趣的“events{},ngx_events_module模块就开始工作了
//除了对events配置项的解析外,该模块没有做其他任何事情
ngx_module_t ngx_events_module = {
NGX_MODULE_V1,
&ngx_events_module_ctx, /* module context */
ngx_events_commands, /* module directives */
NGX_CORE_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
ngx_event_core_module 事件核心模块
static ngx_str_t event_core_name = ngx_string("event_core");
//相关配置见ngx_event_core_commands ngx_http_core_commands ngx_stream_commands ngx_http_core_commands ngx_core_commands ngx_mail_commands
static ngx_command_t ngx_event_core_commands[] = {
//每个worker进程可以同时处理的最大连接数
//连接池的大小,也就是每个worker进程中支持的TCP最大连接数,它与下面的connections配置项的意义是重复的,可参照9.3.3节理解连接池的概念
{ ngx_string("worker_connections"),
NGX_EVENT_CONF|NGX_CONF_TAKE1,
ngx_event_connections,
0,
0,
NULL },
//设置事件模型。 use [kqueue | rtsig | epoll | dev/poll | select | poll | eventport] linux系统中只支持select poll epoll三种
//freebsd里的kqueue,LINUX中没有
//确定选择哪一个事件模块作为事件驱动机制
{ ngx_string("use"),
NGX_EVENT_CONF|NGX_CONF_TAKE1,
ngx_event_use,
0,
0,
NULL },
//当事件模块通知有TCP连接时,尽可能在本次调度中对所有的客户端TCP连接请求都建立连接
//对应于事件定义的available字段。对于epoll事件驱动模式来说,意味着在接收到一个新连接事件时,调用accept以尽可能多地接收连接
{ ngx_string("multi_accept"),
NGX_EVENT_CONF|NGX_CONF_FLAG,
ngx_conf_set_flag_slot,
0,
offsetof(ngx_event_conf_t, multi_accept),
NULL },
//accept_mutex on|off是否打开accept进程锁,是为了实现worker进程接收连接的负载均衡、打开后让多个worker进程轮流的序列号的接收TCP连接
//默认是打开的,如果关闭的话TCP连接会更快,但worker间的连接不会那么均匀。
{ ngx_string("accept_mutex"),
NGX_EVENT_CONF|NGX_CONF_FLAG,
ngx_conf_set_flag_slot,
0,
offsetof(ngx_event_conf_t, accept_mutex),
NULL },
//accept_mutex_delay time,如果设置为accpt_mutex on,则worker同一时刻只有一个进程能个获取accept锁,这个accept锁不是阻塞的,如果娶不到会
//立即返回,然后等待time时间重新获取。
//启用accept_mutex负载均衡锁后,延迟accept_mutex_delay毫秒后再试图处理新连接事件
{ ngx_string("accept_mutex_delay"),
NGX_EVENT_CONF|NGX_CONF_TAKE1,
ngx_conf_set_msec_slot,
0,
offsetof(ngx_event_conf_t, accept_mutex_delay),
NULL },
//debug_connection 1.2.2.2则在收到该IP地址请求的时候,使用debug级别打印。其他的还是沿用error_log中的设置
//需要对来自指定IP的TCP连接打印debug级别的调斌日志
{ ngx_string("debug_connection"),
NGX_EVENT_CONF|NGX_CONF_TAKE1,
ngx_event_debug_connection,
0,
0,
NULL },
ngx_null_command
};
//ngx_event_core_module模块则仅实现了create_conf方法和init_conf方法,这是因为它并不真正负责TCP网络事件的驱动,
//所以不会实现ngx_event_actions_t中的方法
ngx_event_module_t ngx_event_core_module_ctx = {
&event_core_name,
ngx_event_core_create_conf, /* create configuration */
ngx_event_core_init_conf, /* init configuration */
{ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }
};
/*
Nginx定义了一系列(目前为9个)运行在不同操作系统、不同内核版本上的事件IO复用模块,包括:ngx_epoll_module、ngx_kqueue_module、
ngx_poll_module、ngx_select_module、ngx_devpoll_module、ngx_eventport_module、ngx_aio_module、ngx_rtsig_module
。在ngx_event_core_module模块的初始化过程中,将会从以上9个模块中选取1个作为Nginx进程的事件驱动模块。
*/
ngx_module_t ngx_event_core_module = {
NGX_MODULE_V1,
&ngx_event_core_module_ctx, /* module context */
ngx_event_core_commands, /* module directives */
NGX_EVENT_MODULE, /* module type */
NULL, /* init master */
ngx_event_module_init, /* init module */ //解析完配置文件后执行
ngx_event_process_init, /* init process */ //在创建子进程的里面执行 ngx_worker_process_init
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
/用于存储从ngx_event_core_commands配置命令中解析出的各种参数
typedef struct {
ngx_uint_t connections; //连接池的大小
//通过"use"选择IO复用方式 select epoll等,然后通过解析赋值 见ngx_event_use
//默认赋值见ngx_event_core_init_conf,ngx_event_core_module后的第一个NGX_EVENT_MODULE也就是ngx_epoll_module默认作为第一个event模块
ngx_uint_t use; //选用的事件模块在所有事件模块中的序号,也就是ctx_index成员 赋值见ngx_event_use /*
事件的available标志位对应着multi_accept配置项。当available为l时,告诉Nginx -次性尽量多地建立新连接,它的实现原理也就在这里
*/ //默认0
ngx_flag_t multi_accept; //标志位,如果为1,则表示在接收到一个新连接事件时,一次性建立尽可能多的连接 /*
如果ccf->worker_processes > 1 && ecf->accept_mutex,则在创建进程后,调用ngx_event_process_init把accept添加到epoll事件驱动中,
否则在ngx_process_events_and_timers->ngx_trylock_accept_mutex中把accept添加到epoll事件驱动中
*/
ngx_flag_t accept_mutex;//标志位,为1时表示启用负载均衡锁 /*
负载均衡锁会使有些worker进程在拿不到锁时延迟建立新连接,accept_mutex_delay就是这段延迟时间的长度。关于它如何影响负载均衡的内容
*/ //默认500ms,也就是0.5s
ngx_msec_t accept_mutex_delay; //单位ms 如果没获取到mutex锁,则延迟这么多毫秒重新获取 u_char *name;//所选用事件模块的名字,它与use成员是匹配的 epoll select /*
在-with-debug编译模式下,可以仅针对某些客户端建立的连接输出调试级别的日志,而debug_connection数组用于保存这些客户端的地址信息
*/
#if (NGX_DEBUG)
ngx_array_t debug_connection;
#endif
} ngx_event_conf_t;

转载来自:https://blog.csdn.net/initphp/article/details/52434261
1. ngx_event_module_init 模块初始化函数
/*
ngx_event_module_init方法其实很简单,它主要初始化了一些变量,尤其是ngx_http_stub_status_module统计模块使用的一些原子性的统计变量
*/
static ngx_int_t
ngx_event_module_init(ngx_cycle_t *cycle)
{
void ***cf;
u_char *shared;
size_t size, cl;
ngx_shm_t shm;
ngx_time_t *tp;
ngx_core_conf_t *ccf;
ngx_event_conf_t *ecf;
/* 获取配置信息 */
cf = ngx_get_conf(cycle->conf_ctx, ngx_events_module);
ecf = (*cf)[ngx_event_core_module.ctx_index]; if (!ngx_test_config && ngx_process <= NGX_PROCESS_MASTER) {
ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0,
"using the \"%s\" event method", ecf->name);
} ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module); ngx_timer_resolution = ccf->timer_resolution; #if !(NGX_WIN32)
{
ngx_int_t limit;
struct rlimit rlmt; if (getrlimit(RLIMIT_NOFILE, &rlmt) == -1) { // 每个进程能打开的最多文件数。
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
"getrlimit(RLIMIT_NOFILE) failed, ignored"); } else {
if (ecf->connections > (ngx_uint_t) rlmt.rlim_cur
&& (ccf->rlimit_nofile == NGX_CONF_UNSET
|| ecf->connections > (ngx_uint_t) ccf->rlimit_nofile))
{
limit = (ccf->rlimit_nofile == NGX_CONF_UNSET) ?
(ngx_int_t) rlmt.rlim_cur : ccf->rlimit_nofile; ngx_log_error(NGX_LOG_WARN, cycle->log, 0,
"%ui worker_connections exceed "
"open file resource limit: %i",
ecf->connections, limit);
}
}
}
#endif /* !(NGX_WIN32) */ if (ccf->master == 0) {
return NGX_OK;
} if (ngx_accept_mutex_ptr) {
return NGX_OK;
} /* cl should be equal to or greater than cache line size */ /*
计算出需要使用的共享内存的大小。为什么每个统计成员需要使用128字节呢?这似乎太大了,看上去,每个ngx_atomic_t原子变量最多需要8字
节而已。其实是因为Nginx充分考虑了CPU的二级缓存。在目前许多CPU架构下缓存行的大小都是128字节,而下面需要统计的变量都是访问非常频
繁的成员,同时它们占用的内存又非常少,所以采用了每个成员都使用128字节存放的形式,这样速度更快
*/
cl = 128; size = cl /* ngx_accept_mutex */
+ cl /* ngx_connection_counter */
+ cl; /* ngx_temp_number */ #if (NGX_STAT_STUB) size += cl /* ngx_stat_accepted */
+ cl /* ngx_stat_handled */
+ cl /* ngx_stat_requests */
+ cl /* ngx_stat_active */
+ cl /* ngx_stat_reading */
+ cl /* ngx_stat_writing */
+ cl; /* ngx_stat_waiting */ #endif shm.size = size;
shm.name.len = sizeof("nginx_shared_zone") - 1;
shm.name.data = (u_char *) "nginx_shared_zone";
shm.log = cycle->log; //开辟一块共享内存,共享内存的大小为shm.size
if (ngx_shm_alloc(&shm) != NGX_OK) {
return NGX_ERROR;
} //共享内存的首地址就在shm.addr成员中
shared = shm.addr; //原子变量类型的accept锁使用了128字节的共享内存
ngx_accept_mutex_ptr = (ngx_atomic_t *) shared; /*
ngx_accept_mutex就是负载均衡锁,spin值为-1则是告诉Nginx这把锁不可以使进程进入睡眠状态
*/
ngx_accept_mutex.spin = (ngx_uint_t) -1; if (ngx_shmtx_create(&ngx_accept_mutex, (ngx_shmtx_sh_t *) shared,
cycle->lock_file.data)
!= NGX_OK)
{
return NGX_ERROR;
} //原予变量类型的ngx_connection counter将统计所有建立过的连接数(包括主动发起的连接)
ngx_connection_counter = (ngx_atomic_t *) (shared + 1 * cl); (void) ngx_atomic_cmp_set(ngx_connection_counter, 0, 1); ngx_log_debug2(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
"counter: %p, %d",
ngx_connection_counter, *ngx_connection_counter); ngx_temp_number = (ngx_atomic_t *) (shared + 2 * cl); tp = ngx_timeofday(); ngx_random_number = (tp->msec << 16) + ngx_pid; #if (NGX_STAT_STUB)
//依次初始化需要统计的6个原子变量,也就是使用共享内存作为原子变量
ngx_stat_accepted = (ngx_atomic_t *) (shared + 3 * cl);
ngx_stat_handled = (ngx_atomic_t *) (shared + 4 * cl);
ngx_stat_requests = (ngx_atomic_t *) (shared + 5 * cl);
ngx_stat_active = (ngx_atomic_t *) (shared + 6 * cl);
ngx_stat_reading = (ngx_atomic_t *) (shared + 7 * cl);
ngx_stat_writing = (ngx_atomic_t *) (shared + 8 * cl);
ngx_stat_waiting = (ngx_atomic_t *) (shared + 9 * cl); #endif return NGX_OK;
}
模块配置的初始化
1、ngx_events_module 模块配置初始化
ngx_init_cycle方法中的模块初始化。ngx_events_module类型为NGX_CORE_MODULE,所以在ngx_init_cycle中就会进行核心模块的初始化。
但是ngx_events_module中的create_conf方法为NULL,所以不会调用创建配置的方法。
2. ngx_conf_parse 解析顶层“event”的配置
ngx_init_cycle方法中会调用ngx_conf_parse方法,并且解析的nginx.conf配置文件。此次调用只解析最顶层的配置信息“events”,而不会解析{}块中的内容
/* 解析命令行中的配置参数;例如:nginx -t -c /nginx/conf/nginx.conf×/
if (ngx_conf_param(&conf) != NGX_CONF_OK) { //这时候的conf指向的是cycle
environ = senv;
ngx_destroy_cycle_pools(&conf);
return NULL;
}
if (ngx_conf_parse(&conf, &cycle->conf_file) != NGX_CONF_OK) {//这时候的conf指向的是cycle
environ = senv;
ngx_destroy_cycle_pools(&conf);
return NULL;
}
3. ngx_events_block 解析events块block中的内容
ngx_events_block方法为ngx_events_commands命令集的回调函数。在最顶层解析nginx.conf文件的时候,会进行核心模块的命令集遍历。 会遍历模块命令集的cmd->set方法
ngx_events_block中主要创建ngx_event_core_module事件的核心模块以及配置信息。
/**
* 模块解析
* 事件模块配置如下:
* events {
worker_connections 1024;
}
光使用核心配置的方式,只能解析到 events 这一层。
如果需要继续往{}中的内容解析,就得重新调用ngx_conf_parse进行解析
*/ static char *
ngx_events_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
char *rv;
void ***ctx;
ngx_uint_t i;
ngx_conf_t pcf;
ngx_event_module_t *m; if (*(void **) conf) {
return "is duplicate";
} /* count the number of the event modules and set up their indices */ ngx_event_max_module = 0;
for (i = 0; ngx_modules[i]; i++) {
if (ngx_modules[i]->type != NGX_EVENT_MODULE) {
continue;
} ngx_modules[i]->ctx_index = ngx_event_max_module++;
} ctx = ngx_pcalloc(cf->pool, sizeof(void *));
if (ctx == NULL) {
return NGX_CONF_ERROR;
} *ctx = ngx_pcalloc(cf->pool, ngx_event_max_module * sizeof(void *));
if (*ctx == NULL) {
return NGX_CONF_ERROR;
} //conf为ngx_conf_handler中的conf = confp[ngx_modules[i]->ctx_index];也就是conf指向的是ngx_cycle_s->conf_ctx[],
//所以对conf赋值就是对ngx_cycle_s中的conf_ctx赋值,最终就是ngx_cycle_s中的conf_ctx[ngx_events_module=>index]指向了ctx
*(void **) conf = ctx;
/* 模块初始化,如果是NGX_EVENT_MODULE,则调用模块的create_conf方法 */ for (i = 0; ngx_modules[i]; i++) {
if (ngx_modules[i]->type != NGX_EVENT_MODULE) {
continue;
} m = ngx_modules[i]->ctx; if (m->create_conf) {
(*ctx)[ngx_modules[i]->ctx_index] = m->create_conf(cf->cycle);
if ((*ctx)[ngx_modules[i]->ctx_index] == NULL) {
return NGX_CONF_ERROR;
}
}
} //零时保存之前的cf,在下面解析完event{}配置后,在恢复
pcf = *cf;
cf->ctx = ctx;
cf->module_type = NGX_EVENT_MODULE;
cf->cmd_type = NGX_EVENT_CONF;
/* 调用配置解析,这次解析的是 块中的内容,非文件内容 */ rv = ngx_conf_parse(cf, NULL);//这时候cf里面的上下文ctx为NGX_EVENT_MODULE模块create_conf的用于存储event{}的空间 *cf = pcf; //解析完event{}配置后,恢复 if (rv != NGX_CONF_OK) {
return rv;
} /* 初始化 模块的init_conf 方法*/
for (i = 0; ngx_modules[i]; i++) {
if (ngx_modules[i]->type != NGX_EVENT_MODULE) {
continue;
} m = ngx_modules[i]->ctx; if (m->init_conf) {
rv = m->init_conf(cf->cycle, (*ctx)[ngx_modules[i]->ctx_index]);
if (rv != NGX_CONF_OK) {
return rv;
}
}
} return NGX_CONF_OK;
}
4. ngx_event_core_create_conf和ngx_event_core_init_conf
ngx_event_core_create_conf:主要是创建event事件核心模块
ngx_event_core_init_conf:初始化event事件核心模块
/**
* 创建Event的核心配置文件
*/
static void *
ngx_event_core_create_conf(ngx_cycle_t *cycle) {
ngx_event_conf_t *ecf; /* 分配配置文件内容 */
ecf = ngx_palloc(cycle->pool, sizeof(ngx_event_conf_t));
if (ecf == NULL) {
return NULL;
} /* 设置默认值 */
ecf->connections = NGX_CONF_UNSET_UINT;
ecf->use = NGX_CONF_UNSET_UINT;
ecf->multi_accept = NGX_CONF_UNSET;
ecf->accept_mutex = NGX_CONF_UNSET;
ecf->accept_mutex_delay = NGX_CONF_UNSET_MSEC;
ecf->name = (void *) NGX_CONF_UNSET; #if (NGX_DEBUG) if (ngx_array_init(&ecf->debug_connection, cycle->pool, 4,
sizeof(ngx_cidr_t)) == NGX_ERROR)
{
return NULL;
} #endif return ecf;
} /**
* 初始化Event的核心配置文件
*/
static char *
ngx_event_core_init_conf(ngx_cycle_t *cycle, void *conf) {
ngx_event_conf_t *ecf = conf;///用于存储从ngx_event_core_commands配置命令中解析出的各种参数 #if (NGX_HAVE_EPOLL) && !(NGX_TEST_BUILD_EPOLL)
int fd;
#endif
ngx_int_t i;
ngx_module_t *module;
ngx_event_module_t *event_module; module = NULL; #if (NGX_HAVE_EPOLL) && !(NGX_TEST_BUILD_EPOLL) fd = epoll_create(100); if (fd != -1) {
(void) close(fd);
module = &ngx_epoll_module; } else if (ngx_errno != NGX_ENOSYS) {
module = &ngx_epoll_module;
} #endif #if (NGX_HAVE_DEVPOLL) && !(NGX_TEST_BUILD_DEVPOLL) module = &ngx_devpoll_module; #endif #if (NGX_HAVE_KQUEUE) module = &ngx_kqueue_module; #endif #if (NGX_HAVE_SELECT) if (module == NULL) {
module = &ngx_select_module;
} #endif /**
* 查询使用的事件模型:epoll、kqueue等
* 因为在模块初始化的时候,epoll\kqueue等event的模型模块都会被初始化
* 但是每个服务器只能选择一种相应的事件模型,所以选择一个适合自己的模块
//ngx_event_core_module后的第一个NGX_EVENT_MODULE也就是ngx_epoll_module默认作为第一个event模块
*/
if (module == NULL) {
for (i = 0; cycle->modules[i]; i++) { if (cycle->modules[i]->type != NGX_EVENT_MODULE) {
continue;
} event_module = cycle->modules[i]->ctx;
//不能为ngx_event_core_module
if (ngx_strcmp(event_module->name->data, event_core_name.data)
== 0) {
continue;
} module = cycle->modules[i];
break;
}
} if (module == NULL) {
ngx_log_error(NGX_LOG_EMERG, cycle->log, 0, "no events module found");
return NGX_CONF_ERROR ;
} ngx_conf_init_uint_value(ecf->connections, DEFAULT_CONNECTIONS);
cycle->connection_n = ecf->connections; /**
* 存储使用的事件模型模块索引 例如:epoll、kqueue
* nginx.conf中存储的是:use epoll;
* 这里会找到cycle->modules的具体模块的索引值,存储最终的索引值
*/
ngx_conf_init_uint_value(ecf->use, module->ctx_index); event_module = module->ctx;
ngx_conf_init_ptr_value(ecf->name, event_module->name->data);//所选用事件模块的名字,它与use成员是匹配的 epoll select
ngx_conf_init_value(ecf->multi_accept, 0);
ngx_conf_init_value(ecf->accept_mutex, 1);
ngx_conf_init_msec_value(ecf->accept_mutex_delay, 500); return NGX_CONF_OK;
}
nginx&http 第二章 ngx 事件event配置等初始化的更多相关文章
- nginx&http 第二章 ngx 事件event处理 数据结构
ngx_event.c :这个文件主要放置Nginx事件event模块的核心代码. 包含:进程事件分发器(ngx_process_events_and_timers).事件模块的模块和配置.模块初始化 ...
- nginx&http 第二章 ngx 事件event初始化 ngx_event_process_init
|----------(ngx_worker_process_cycle->ngx_worker_process_init) |--------->for(;;) {ngx_process ...
- nginx&http 第三章 ngx 事件event epoll 处理
1. epoll模块命令集 ngx_epoll_commands epoll模块上下文 ngx_epoll_module_ctx epoll模块配置 ngx_epoll_module static ...
- nginx&http 第三章 ngx 事件event accept epoll /init
tcp 三次握手成功后,listen fd 可读,在process_event_timer 中调用rev->handler(rev)处理: 其回调函数为: ngx_event_accept / ...
- nginx&http 第二章 ngx启动多进程
Nginx服务器使用 master/worker 多进程模式. 主进程(Master process)启动后,会接收和处理外部信号: 主进程启动后通过fork() 函数产生一个或多个子进程(work ...
- Jenkins入门系列之——02第二章 Jenkins安装与配置
2014-12-08:已不再担任SCM和CI的职位,Jenkins的文章如无必要不会再维护. 写的我想吐血,累死了. 网页看着不爽的,自己去下载PDF.有问题请留言! Jenkins入门系列之——03 ...
- nginx&http 第三章 ngx 事件http 初始化1
在 http 配置块中,我们配置了 http 连接相关的信息,HTTP 框架也正是从这里启动的 在 nginx 初始化的过程中,执行了 ngx_init_cycle 函数,其中进行了配置文件解析,调用 ...
- flask 第二章 endpoint重名 Flask路由 初始化配置 Falsk Config 蓝图+目录结构
今日内容 1.路由的分发,以下两种方式效果一样,但是都能指向同一个函数 from flask import Flask app=Flask(__name__) #第一种方式 @app.route('/ ...
- 学习笔记-[Maven实战]-第二章:Maven安装和配置
在windows上安装Maven 1.检查JDK安装:在CMD下运行以下命令来检查JAVA安装情况: 命令: C:\Documents and Settings\Administrator>ec ...
随机推荐
- SpringBoot+单机redis
spring boot-redis集成 看教程来的,看起来很简单,但是集成后发现启动失败? WARN 2556 --- [ restartedMain] ationConfigEmbeddedWebA ...
- logstash-安装
1.下载 cd /usr/local/src wget https://mirrors.huaweicloud.com/logstash/7.8.0/logstash-7.8.0.tar.gz ...
- intellij idea:设置java方法注释模板(intellij idea 2019.2)
一,打开方法注释模板的编辑窗口 菜单file->打开settings editor栏目下->打开Live Templates 说明:刘宏缔的架构森林是一个专注架构的博客,地址:http ...
- Ubuntu20.4安装
官网下载镜像 https://releases.ubuntu.com/20.04/ubuntu-20.04-live-server-amd64.iso 挂载开装 选语言 选键盘 网络设置DHCP到地址 ...
- Pytest学习(三) - setup和teardown的使用
一.前言 从文章标题可以看出,就是初始化和释放的操作,根据我的java习惯来学习pytest,个人感觉没差太多,理解上也不是很难. 哦,对了,差点跑题了,这个框架是基于Python语言的,在学习的时候 ...
- Asp.Net Core如何根据数据库自动生成实体类
通过引用Nuget包添加实体类 运行 Install-Package Microsoft.EntityFrameworkCore.SqlServer 运行 Install-Package Micros ...
- [开源] .Net ORM FreeSql 1.10.0 稳步向行
写在开头 FreeSql 是 .NET 开源生态下的 ORM 轮子,转眼快两年了,说真的开源不容易(只有经历过才明白).今天带点干货和湿货给大家,先说下湿货. 认识我的人,知道 CSRedisCore ...
- Codeforces Round #677 (Div. 3) D/1433D Districts Connection
https://codeforces.com/contest/1433/problem/D 找两个不同权值的节点A.B连起来,所有与A不同权值的连到A上,相同的连到B上. #include<io ...
- 全球首个优秀的华人.net微服务框架 作者:百大僧
话不多说,直接上地址 https://gitee.com/shoubashou/NetCoreMicroService,目标斩获10000star, 通往牛逼的路上,风景差得让人只说脏话. 是全球首个 ...
- 设备屏幕与Size Class对应