缘由

今天阅读 深入理解nginx 的第五章,前半部分讲解了upstream的问题,并且指导完成了一个例子。

upstream

先来回答upstream拿来做什么:客户端请求nginx,nginx将请求转发给第三方服务器(也称上游服务器),第三方服务器返回响应,nginx将响应转发给客户端。这时,nginx作为了一个代理服务器。一般,我们只是把第三方服务器的内容原封不动的返回给客户端。这样做的目的主要是减轻上游服务器端的压力。

例子

背景描述

我们要利用nginx的upstream完成这样一个功能,当然我们访问“http://localhost/test?word”的时候,nginx会把word这个单词(可以是任意单词),传递归google,那么google就用这个word完成搜索,然后将搜索后的页面返回给nginx,nginx再返回给我们。这个例子基本就说清楚了为什么在国内用反向代码就可以访问。

思路总览

那么下面,我按照我的思路来为大家理一下,当然我的思路肯定是比不上书上清晰,代码写的也没有书上工整。首先看我们使用到了那些结构体和方法。下面一幅截图看清楚了,我使用的所有的结构体和方法。

作为一个单独的模块

由于我们写的是一个独立的模块,所以肯定要按照nginx的要求完成ngx_module_t 这个结构体:
这是对于所有模块都需要有的ngx_module_t,书中87页有描述:

ngx_module_t  ngx_http_mytest_module =
{NGX_MODULE_V1,&ngx_http_mytest_module_ctx,           /* module context */ngx_http_mytest_commands,              /* module directives */NGX_HTTP_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_http_mytest_commands如下,:

static ngx_command_t  ngx_http_mytest_commands[] =
{{ngx_string("mytest"),NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_HTTP_LMT_CONF | NGX_CONF_NOARGS,ngx_http_mytest,NGX_HTTP_LOC_CONF_OFFSET,0,NULL},ngx_null_command
};

其中表明当我们遇见了mytest配置项目的时候,将会调用ngx_http_mytest,如下:

static char *
ngx_http_mytest(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{ngx_http_core_loc_conf_t  *clcf;//首先找到mytest配置项所属的配置块,clcf貌似是location块内的数据
//结构,其实不然,它可以是main、srv或者loc级别配置项,也就是说在每个
//http{}和server{}内也都有一个ngx_http_core_loc_conf_t结构体clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);//http框架在处理用户请求进行到NGX_HTTP_CONTENT_PHASE阶段时,如果
//请求的主机域名、URI与mytest配置项所在的配置块相匹配,就将调用我们
//实现的ngx_http_mytest_handler方法处理这个请求clcf->handler = ngx_http_mytest_handler;return NGX_CONF_OK;
}

其中又调用了 ngx_http_mytest_handler:

static ngx_int_t ngx_http_mytest_handler(ngx_http_request_t *r){ngx_http_mytest_ctx_t *myctx = ngx_http_get_module_ctx(r,ngx_http_mytest_module);if(myctx == NULL){myctx = ngx_palloc(r->pool,sizeof(ngx_http_mytest_ctx_t));if(myctx == NULL){return NGX_ERROR;}ngx_http_set_ctx(r,myctx,ngx_http_mytest_module);}if(ngx_http_upstream_create(r) != NGX_OK){ngx_log_error(NGX_LOG_DEBUG,r->connection->log,0,"ngx_http_upstream_create failed");return NGX_ERROR;}//得到配置结构体ngx_http_mytest_conf_t *mycf = (ngx_http_mytest_conf_t *)ngx_http_get_module_loc_conf(r,ngx_http_mytest_module);ngx_http_upstream_t *u = r->upstream;u->conf = &mycf->upstream;u->buffering = mycf->upstream.buffering;//resolved 用于存放上游服务器的地址u->resolved = (ngx_http_upstream_resolved_t *) ngx_palloc(r->pool,sizeof(ngx_http_upstream_resolved_t));if(u->resolved == NULL){ngx_log_error(NGX_LOG_DEBUG,r->connection->log,0,"ngx_pcalloc resolved error:%s",strerror(errno));return NGX_ERROR;}//这里上游服务器就是www.google.comstatic struct sockaddr_in backendSockAddr;struct hostent *pHost = gethostbyname((char*) "www.google.com");if(pHost == NULL){ngx_log_error(NGX_LOG_DEBUG,r->connection->log,0,"gethostbynameerror:%s",strerror(errno));return NGX_ERROR;}//访问上游服务器的端口backendSockAddr.sin_family = AF_INET;backendSockAddr.sin_port = htons((in_port_t)80);char* pDmsIP = inet_ntoa(*(struct in_addr*) (pHost->h_addr_list[0]));backendSockAddr.sin_addr.s_addr = inet_addr(pDmsIP);myctx->backendServer.data = (u_char * )pDmsIP;myctx->backendServer.len = strlen(pDmsIP);//将地址设置到resolved成员中u->resolved->sockaddr = (struct sockaddr *)&backendSockAddr;u->resolved->socklen = sizeof(struct sockaddr_in );u->resolved->naddrs = 1;//设置三个必须实现的回调方法u->create_request = mytest_upstream_create_request;u->process_header = mytest_process_status_line;u->finalize_request = mytest_upstream_finalize_request;r->main->count++;ngx_http_upstream_init(r);return NGX_DONE;
}

ngx_http_mytest_handler完成了很大一部分工作,除了指定了我们的上游服务器是www.google.com,还设定了好了作为代理服务器的必须实现的回调方法和初始化代理功能的upstream模块。

  • //设置三个必须实现的回调方法
  • u->create_request = mytest_upstream_create_request;
  • u->process_header = mytest_process_status_line;
  • u->finalize_request = mytest_upstream_finalize_request;
  • //初始化
  • ngx_http_upstream_init(r);

三个upstream回调方法

/*** zy:* 三个反向代理必须实现的方法之一* create_request:用于创建发送给上游服务器的HTTP请求,* upstream将会回调它*/
static ngx_int_t mytest_upstream_create_request(ngx_http_request_t *r){//模仿发送给google的请求static ngx_str_t backendQueryLine=ngx_string("GET /search?q=%V HTTP/1.1\r\nHost:www.google.com\r\nConnection:close\r\n\r\n");ngx_int_t queryLinelen = backendQueryLine.len + r->args.len-2;ngx_buf_t *b = ngx_create_temp_buf(r->pool,queryLinelen);if(b == NULL){return NGX_ERROR;}b->last = b->pos+queryLinelen;ngx_snprintf(b->pos,queryLinelen,(char *)backendQueryLine.data,&r->args);//发送给上游服务器的请求必须要用ngx_chain_t的链表结构r->upstream->request_bufs=ngx_alloc_chain_link(r->pool);if(r->upstream->request_bufs == NULL){return NGX_ERROR;}r->upstream->request_bufs->buf = b;r->upstream->request_bufs->next = NULL;r->upstream->request_sent = 0;r->upstream->header_sent = 0;r->header_hash = 1;return NGX_OK;
}static ngx_int_t mytest_upstream_process_header(ngx_http_request_t *r){ngx_int_t rc;ngx_table_elt_t *h;ngx_http_upstream_header_t *hh;ngx_http_upstream_main_conf_t *umcf;//需要处理HTTP头部的方法而已umcf = ngx_http_get_module_main_conf(r, ngx_http_upstream_module);for( ; ; ){rc = ngx_http_parse_header_line(r,&r->upstream->buffer,1);if(rc == NGX_OK){h = ngx_list_push(&r->upstream->headers_in.headers);if(h == NULL){return NGX_ERROR;}h->hash = r->header_hash;h->key.len = r->header_name_end - r->header_name_start;h->value.len = r->header_end - r->header_start;h->key.data = ngx_pnalloc(r->pool,h->key.len + 1 + h->value.len + 1 + h->key.len);if(h->key.data == NULL){return NGX_ERROR;}//下面是因为在一个连续的buf存储三个数据,所以才需要改变指针的指向h->value.data = h->key.data + h->key.len +1;h->lowcase_key = h->key.data + h->key.len + 1 + h->value.len + 1;ngx_memcpy(h->key.data,r->header_name_start,h->key.len);h->key.data[h->key.len] = '\0';ngx_memcpy(h->value.data,r->header_start,h->value.len);h->key.data[h->value.len] = '\0';if(h->key.len == r->lowcase_index){ngx_memcpy(h->lowcase_key,r->lowcase_header,h->key.len);}else{ngx_strlow(h->lowcase_key,h->key.data,h->key.len);}//upstream 模块会对一些HTTP做特殊处理hh = ngx_hash_find(&umcf->headers_in_hash,h->hash,h->lowcase_key,h->key.len);if(hh && hh->handler(r,h,hh->offset)!=NGX_OK){return NGX_ERROR;}continue;}//NGX_HTTP_PARSE_HEADER_DONE表示头部已经解析完毕,接着是解析包体if(rc == NGX_HTTP_PARSE_HEADER_DONE){//需要根据http协议来添加server和data字段,这是从上游服务器返回过来但是没有的if(r->upstream->headers_in.server == NULL){h = ngx_list_push(&r->upstream->headers_in.headers);if(h == NULL){return NGX_ERROR;}h->hash = ngx_hash(ngx_hash(ngx_hash(ngx_hash(ngx_hash('s', 'e'), 'r'), 'v'), 'e'), 'r');ngx_str_set( &h->key,"Server");ngx_str_null( &h->value);h->lowcase_key = (u_char *)"server";}if(r->upstream->headers_in.date == NULL){h = ngx_list_push(&r->upstream->headers_in.headers);if(h == NULL){return NGX_ERROR;}h->hash = ngx_hash(ngx_hash(ngx_hash('d','a'),'t'),'e');ngx_str_set(&h->key,"Date");ngx_str_null(&h->value);h->lowcase_key = (u_char *)"date";}return NGX_OK; //返回时表示解析成功,向下游转发包体}//没有接受到完成的头部,那么会让upstream接着接受新的字符if(rc == NGX_AGAIN){return NGX_AGAIN;}//不属于上面三类都是不合法的ngx_log_error(NGX_LOG_ERR,r->connection->log,0,"upstream sent invalid header");return NGX_HTTP_UPSTREAM_INVALID_HEADER;}
}
/*** zy:用process_header方法解析包头* 就是上游服务器返回给我们的* 写了两个,一个用于解析响应行,一个用于解析响应头*/static ngx_int_t mytest_process_status_line(ngx_http_request_t *r){size_t len;ngx_int_t rc;ngx_http_upstream_t *u;ngx_http_mytest_ctx_t *ctx = ngx_http_get_module_ctx(r,ngx_http_mytest_module);if(ctx == NULL){return NGX_ERROR;}u = r->upstream;//调用ngx的框架代码解析rc = ngx_http_parse_status_line(r, &u->buffer, &ctx->status);//没有得到足够的相应头if(rc == NGX_AGAIN){return rc;}//得到错误的响应头if(rc == NGX_ERROR) {//ngx_log_error(NGX_LOG_ERR,r->connection->log,0,"upstream sent no valid HTTP/1.0 header");// ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,"upstream sent no valid HTTP/1.0 header");r->http_version = NGX_HTTP_VERSION_9;u->state->status = NGX_HTTP_OK;return NGX_OK;}//对发往下游的响应头部作统一的处理if(u->state){u->state->status = ctx->status.code;}u->headers_in.status_n = ctx->status.code;len = ctx->status.end - ctx->status.start;u->headers_in.status_line.len = len;u->headers_in.status_line.data = ngx_palloc(r->pool,len);if(   u->headers_in.status_line.data == NULL){return NGX_ERROR;}ngx_memcpy(u->headers_in.status_line.data, ctx->status.start , len);//设置解析头部的回调方法u->process_header = mytest_upstream_process_header;return mytest_upstream_process_header(r);
}
/*** zy:没什么需要特别完成的。*/
static void mytest_upstream_finalize_request(ngx_http_request_t *r,ngx_int_t rc){ngx_log_error(NGX_LOG_DEBUG,r->connection->log,0,"mytest_upstream_finalize_request");
}

从配置文件来看

从解析配置文件开始,那么,我们在配置文件中写了这么一段

location /test{
     mytest;
}

如下图所示:

这段配置文件由于属于location这个块配置项,当解析到此的时候会调用所有的模块的ngx_http_module_t的结构体中与location相关的两个回调方法。(具体调用顺序在P88)

如下所示,本例写了这两个回调方法:

static ngx_http_module_t  ngx_http_mytest_module_ctx =
{NULL,                              /* preconfiguration */NULL,                         /* postconfiguration */NULL,                              /* create main configuration */NULL,                              /* init main configuration */NULL,                              /* create server configuration */NULL,                              /* merge server configuration */ngx_http_mytest_create_loc_conf,                /* create location configuration */ngx_http_mytest_merge_loc_conf                   /* merge location configuration */
};

两个函数的含义如下:

/*** P174* 这个方法是一个回调方法,我们需要在ngx_http_mytest_module_ctx中注册* 具体什么时候调用请看87页、117页* 简单说来,* 这里只是先创建好数据结构,分配好结构体的内存,* 具体赋值需要等到ngx_commmand_t遇见想对应的配置项** 书中为了简单起见,不就在配置项里面填写相应的值了,而是直接在这里进行赋值*/
static void *ngx_http_mytest_create_loc_conf(ngx_conf_t *cf){ngx_http_mytest_conf_t *mycf;mycf = (ngx_http_mytest_conf_t *) ngx_pcalloc(cf->pool,sizeof(ngx_http_mytest_conf_t));if( mycf == NULL){return NULL;}mycf->upstream.connect_timeout = 60000;mycf->upstream.send_timeout = 60000;mycf->upstream.read_timeout = 60000;mycf->upstream.store_access = 0600;mycf->upstream.buffering = 0;   //使用固定大小的内存作为缓冲区来转发上游响应包体mycf->upstream.bufs.num = 8;mycf->upstream.bufs.size = ngx_pagesize;mycf->upstream.buffer_size = ngx_pagesize;mycf->upstream.busy_buffers_size = 2 * ngx_pagesize;mycf->upstream.temp_file_write_size = 2 * ngx_pagesize;mycf->upstream.max_temp_file_size = 1024 * 1024 * 1024;//下面两个必须初始化,但是会在函数内赋值mycf->upstream.hide_headers = NGX_CONF_UNSET_PTR;mycf->upstream.pass_headers = NGX_CONF_UNSET_PTR;return mycf;
}/*** P175* zy:主要是为了给hide_headers初始化*/
static char *ngx_http_mytest_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child){ngx_http_mytest_conf_t *prev = (ngx_http_mytest_conf_t *) parent;ngx_http_mytest_conf_t *conf = (ngx_http_mytest_conf_t *) child;ngx_hash_init_t hash;hash.max_size = 100;hash.bucket_size = 1024;hash.name = "proxy_headers_hash";if(ngx_http_upstream_hide_headers_hash(cf,&conf->upstream,&prev->upstream,ngx_http_proxy_hide_headers,// zy:好像这里只是需要传一个ngx的字符串过去就可以了&hash)!=NGX_OK){return NGX_CONF_ERROR;}return NGX_CONF_OK;
}

没说到的结构体而又需要的

/*** zy:* 这里必须使用到配置文件结构体,* 所谓配置文件结构体,就是指在配置文件中填写的值,* 然后读取到这个结构体来,再做进一步的操作。* 书中说必须要在配置文件的结构体补充一个ngx_http_up3stream_conf_t变量,* 具体的赋值请看函数:**/typedef struct{ngx_http_upstream_conf_t upstream;}ngx_http_mytest_conf_t;/*书上也没讲为什么,但是就是需要*/
static ngx_str_t  ngx_http_proxy_hide_headers[] =
{ngx_string("Date"),ngx_string("Server"),ngx_string("X-Pad"),ngx_string("X-Accel-Expires"),ngx_string("X-Accel-Redirect"),ngx_string("X-Accel-Limit-Rate"),ngx_string("X-Accel-Buffering"),ngx_string("X-Accel-Charset"),ngx_null_string
};/*** zy:必须要用一个上下文来保持现在的状态,* 因为会反复调用process_header方法用于解析上游服务器返回过来的状态** 但是上游服务器又不能一次性发送完,nginx也不是一次就接受完,* 所以会调用多次,那么上次解析到哪里了?就保存这个状态** 使用ngx原有的ngx_http_status_t即可*/
typedef struct
{ngx_http_status_t           status;ngx_str_t                   backendServer;
} ngx_http_mytest_ctx_t;

其他问题总结

无法获得nginx的core文件的问题

由于这次我是自己将书上的代码敲了一遍,所以有些代码自己写了,导致发生了段错误,刚开始没有头绪非常难调整,因为我设定的了一个master线程和一个work线程,当发生段错误的时候,work线程直接退出了,而master线程此时又会再起一个新的work线程,一直产生不了core文件,应该是配置文件我还不是特别熟悉。所以,不太清楚哪里没有设定对 。从log文件来,已经清楚的标明了:core dump。
最后的解决办法就是:将配置文件中写成:

worker_processes  0;
master_process off;

这样,就没有work线程,那么只产生一个线程,这个线程又是当master又当work,当我执行我的操作,然后崩溃了,然后唯一的这个线程就会退出,退出之后,在相应的目录就会产生core文件了。

除此之外,还需要手动设定一下产生core文件的大小。默认是0,那么使用
ulimit -a可以查看,第一个就是core文件的大小。
使用ulimit -c 1024,可以修改为1024.具体权限问题,请根据实际情况来看,而且我发现每个终端的数值还不一样。

为什么gdb调试core文件的时候是问号

如下:
#0  0xb76c7530 in ?? ()
#1  0x000007b3 in ?? ()

的符号
简单的说就来,没有加载symbols文件,使用(gdb) file (文件)命令,就可以了。
我的具体使用情况如下所示:

asd@asd-desktop:~/Documents/nginx-1.2.9/objs$ sudo gdb --core=core
GNU gdb (Ubuntu/Linaro 7.4-2012.04-0ubuntu2.1) 7.4-2012.04
Copyright (C) 2012 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "i686-linux-gnu".
For bug reporting instructions, please see:
<http://bugs.launchpad.net/gdb-linaro/>.
[New LWP 12637]
Core was generated by `./nginx'.
Program terminated with signal 11, Segmentation fault.
#0  0x08062f8d in ?? ()
(gdb) file nginx
Reading symbols from /home/asd/Documents/nginx-1.2.9/objs/nginx...done.
(gdb) bt
#0  0x08062f8d in ngx_resolve_name_done (ctx=0x39) at src/core/ngx_resolver.c:355
#1  0x0809c295 in ngx_http_upstream_finalize_request (r=0x9f28b58, u=0x9f2f6a4, rc=0)at src/http/ngx_http_upstream.c:3002
#2  0x0809b16c in ngx_http_upstream_process_non_buffered_request (r=0x9f28b58, do_write=1)at src/http/ngx_http_upstream.c:2458
#3  0x0809af61 in ngx_http_upstream_process_non_buffered_downstream (r=0x9f28b58)at src/http/ngx_http_upstream.c:2389
#4  0x0809a703 in ngx_http_upstream_send_response (r=0x9f28b58, u=0x9f2f6a4)at src/http/ngx_http_upstream.c:2162
#5  0x080996e3 in ngx_http_upstream_process_header (r=0x9f28b58, u=0x9f2f6a4)at src/http/ngx_http_upstream.c:1658
#6  0x080983ba in ngx_http_upstream_handler (ev=0x9f53340) at src/http/ngx_http_upstream.c:949
#7  0x08076bab in ngx_epoll_process_events (cycle=0x9f29568, timer=59597, flags=1)at src/event/modules/ngx_epoll_module.c:683
#8  0x08069307 in ngx_process_events_and_timers (cycle=0x9f29568) at src/event/ngx_event.c:247
#9  0x08073f24 in ngx_single_process_cycle (cycle=0x9f29568) at src/os/unix/ngx_process_cycle.c:315
#10 0x0804a417 in main (argc=1, argv=0xbfc51384) at src/core/nginx.c:409
(gdb) q
asd@asd-desktop:~/Documents/nginx-1.2.9/objs$ 
  • 参考: 转 GDB 调试 core文件

nginx:代理服务器(涉及upstream)的例子相关推荐

  1. 【Linux网络编程】Nginx -- 模块开发(upstream / subrequest)

    [Linux网络编程]Nginx -- 模块开发(upstream / subrequest) [1]upstream VS subrequest 简介 upstream 为访问上游服务器,它把Ngi ...

  2. 搭建nginx代理服务器

    搭建nginx代理服务器 原理: 1.在proxy-1的nginx.conf主配置文件后添加 2.修改proxy-1的子配置文件 3.修改存储服务器的一些配置 4.启动服务测试: 原理: 通过代理服务 ...

  3. nginx的rewrite 参数和例子

    nginx的rewrite 参数和例子 正则表达式匹配,其中: * ~ 为区分大小写匹配 * ~* 为不区分大小写匹配 * !~和!~*分别为区分大小写不匹配及不区分大小写不匹配 文件及目录匹配,其中 ...

  4. nginx 报错 upstream timed out (110: Connection timed out)解决方案

    nginx 报错 upstream timed out (110: Connection timed out)解决方案 参考文章: (1)nginx 报错 upstream timed out (11 ...

  5. Nginx代理服务器缓存清理模块purge

    如果开启了Nginx的代理服务器缓存,缓存失效的时间是通过proxy_cache_valid定义的多长时间失效,以及上游服务发来的响应一些头部,比如cache-control来定义缓存什么时候失效.但 ...

  6. 入门学习Nginx代理服务器?就看这篇Nginx进阶学习最佳配置实践指南

    前置基础知识学习 1.Nginx基础安装与配置详细 https://blog.weiyigeek.top/2019/9-1-121.html 2.Nginx进阶学习之最佳配置实践指南 https:// ...

  7. nginx响应超时upstream timed out (110: Connection timed out) while reading response header from upstream

    问题描述 解决方法 提高nginx网络吞吐量buffers优化指令说明 nginx代理超时配置 nginx缓存区大小设置 问题描述 后台server服务响应时间正常,但是请求没有打到服务器,在ngin ...

  8. [temp]Nginx 错误502 upstream sent too big header while reading response header from upstream

    Nginx 502 Bad Gateway的含义是请求的PHP-CGI已经执行,但是由于某种原因(一般是读取资源的问题)没有执行完毕而导致PHP-CGI进程终止. Nginx 504 Gateway ...

  9. Nginx 错误502 upstream sent too big header while reading response header from upstream

    Nginx 502 Bad Gateway的含义是请求的PHP-CGI已经执行,但是由于某种原因(一般是读取资源的问题)没有执行完毕而导致PHP-CGI进程终止. Nginx 504 Gateway ...

最新文章

  1. 【 FPGA 】常数( localparam )和参数( parameter )
  2. 二.MongoDB特点
  3. python综合程序设计-Python程序设计实验五:综合运用三种基本结构进行程序设计...
  4. BIO bi_sector submit_bio make_request_fn
  5. 字符输入流_Reader类FileReader类介绍
  6. MySQL 错误 1366:1366 Incorrect integer value
  7. AVR 矩阵键盘程序源代码2(有连续按键功能)(原创)
  8. I00012 打印三位数的水仙花数及其个数
  9. 韩开发新技术 用纸代替硅制造电路芯片
  10. Win 2003安装过后的一些配置技巧
  11. node使用ffmpeg拼接音频
  12. 从零开始学PCR技术(三):PCR引物设计
  13. BSW系列:CFAR算法解析
  14. UMD算法讲义——Lecture 2:算法设计:稳定婚姻问题
  15. 【PhotoShop】用图片自带的alpha通道抠图
  16. java getvalueat_Java swing jdbc:设置背景颜色,获取素材方法,表格,图片等的切换【诗书画唱】...
  17. 强大的dex反编译器
  18. 看电影用这个小程序,爆米花钱肯定给你省出来!
  19. android查ip地址,Android 查看IP地址
  20. C# 四舍五入、进一法、舍位(取整,舍去小数,向负无穷舍入)函数

热门文章

  1. 2021年中国地质灾害发生数量、伤亡人员及避免情况分析[图]
  2. 【转】手机充电器原理
  3. 利用python进行图像视觉基础练习
  4. 《Drools7.0.0.Final规则引擎教程》第4章 global全局变量
  5. LVGL lv_cont 容器(8)
  6. git报错-The file will have its original
  7. MySQL作为空间数据库
  8. python语言要英语基础吗_学编程需要英语基础吗?
  9. python setup.py bdist_wheel生成wheel文件,示例:安装webrtcvad
  10. 关于tensorflow的报错NodeDef mentions attr ‘xxx‘ not in Op的解决方案和产生原因