Reference: http://xiaorui.cc/2016/10/16/nginx%E5%8A%A8%E6%80%81%E9%85%8D%E7%BD%AE%E5%8F%8A%E6%9C%8D%E5%8A%A1%E5%8F%91%E7%8E%B0%E9%82%A3%E4%BA%9B%E4%BA%8B/

次的准备闲聊关于nginx服务发现的话题,  按照我以往写文章的性子,估计会迁移一些主题.  毕竟单纯聊nginx和动态服务发现没啥意思,因我以前的文章有大量的涉及到。

该文章写的有些乱,欢迎来喷 ! 另外文章后续不断更新中,请到原文地址查看更新.    http://xiaorui.cc/?p=3855

nginx upstream相关

我这里就不再精细的讲述nginx的源码, 毕竟我自己也只是粗略看了点nginx的底层代码.  nginx的动态配置肯定是绕不过upstream模块的运作了,他主要的驱动是xxx_pass,比如最常用的proxy_pass, uwsgi_pass。upstream相关配置结构的建立,并不一定是非要配置upstream这个指令才会去做,有时proxy_pass直接就是跟一个ip地址或域名, 他其实隐式的调用upstream 。

那么一个具体的request过程如何跟upstream配置怎么关联起来的?

首先是通过peer.init函数指针初始化,然后根据round robbin算法选择ngx_http_upstream_init_round_robin_peer,每种算法锁出发的函数不一样,这是废话。 从配置上看,凡是请求到location /xxx的request请求,他们都关联到同一个upstream配置,但是既然大家公用一个结构,那么需不需要互斥呢?如果大家都要修改其中的某个成员…

实际上,nginx中一个请求不会中多个进程中同时处理,一个request生老病死都在一个worker内。  每个nginx worker都会监听自己相关的epoll fd事件。 当一个worker争取到了nginx listen fd accept的锁,那么他就可以接收新的request请求,这个请求的创建出的fd会压到他本进程的事件里。 我想说的是,他不会逃到worker里, 因为也没这个必要这么实现,跨多进程传递socket本身是个很蛋疼的实现.

nginx relaod相关

那么nginx upstrem阐述完了,我们再聊聊nginx -s reload.

当你增删改upstream配置的时候,如何让nginx不停服务的重载配置? nginx -s reload.  reload的实现相对好理解,-s的作用是向master进程发送信号。 其实是SIGHUP信号的封装,也就是说我们可以直接通过kill向nginx的pid发送SIGHUP信号来完成reload操作。 
master通过ngx_signal_handler处理信号,见到reload就重置ngx_reconfigure = 1.

 
1
2
3
4
casengx_signal_value(NGX_RECONFIGURE_SIGNAL):  
    ngx_reconfigure = 1;  
    action = ", reconfiguring";  
break;  

这样当ngx_master_process_cycle的for循环中检测到ngx_reconfigure ==1,就开始做重加载配置的操作,通过ngx_start_worker_processes开启新进程,而之前的进程则通过ngx_signal_worker_processes来发送信号进行“优雅”的关闭.  所谓优雅的关闭, 就是让当前真正处理请求的进程等到处理完之后再退出,当然进程不能再次参与accept锁的争夺了.  这样新的请求会拿到listen accept锁,可建立reqeust连接.

如何保证已经建连的连接 ?

如果是短连接,那么在请求完之后,worker会主动发送close(),后面就把fd事件给踢掉。

那么长连接会怎么办?  这个时候长连接是有两种的, 前者是是client 跟 nginx proxy的连接,后者是proxy跟后端服务的连接,比如nginx –> tornado 就属于后者.

nginx proxy主要就是给client提供服务,在对外请求完毕的情况下,右面的连接可有可无了,  当然我们还是操作proxy给后端服务发送close()关闭连接 。 如果发生异常进程退出,后端经过keepalive timeout后进行回收资源.

前者的连接就有点意思了,  当你的请求是http1.1长连接时候,不管是单次请求或者是stream这种流式请求,都会在请求完之后退出。 那么对于客户端有什么影响?  客户端会收到fin的tcp关闭请求,配合nginx关闭连接之后. 当你再次访问nginx的时候,才会重建一个连接…

什么是nginx的动态配置?

到此为止, nginx reload 和 upstream 都过了一遍.  那么动态配置又是什么? 我们什么时候需要动态配置?

我认为nginx的动态配置可以有这么几种方法.

一种是dns动态解析 ,一种是模板渲染, 一种服务发现动态配置重载.

碰巧,这几种配置我都有在线上用过,我不会单纯的说谁更好,谁更差,因为每个动态配置方法都有他的应用场景.  比如dns动态解析非常适合依赖域名来辨识主机的集群服务,多用在类aws、阿里云这样的云服务.   模板动态配置方法更适合那种大型的运维平台管理体系.    服务发现配置适合那种docker化的服务, 或者类似的微服务框架体系.

第一种, dns动态解析

我们通常在配置nginx upstream的时候都会使用ip地址. 对于那种依赖域名的集群服务,非常适合这样的配置.  因为你修改了一组域名的配置,你如果nginx域名来解析, 就不再重新配置了。 这个dns服务器肯定不能是公网. 内网必须配置一个高性能的dns服务,比如dnsmasq, bind. 另外我们需要注意dns缓存的问题, nginx默认会根据dns server返回的ttl来设置记录的失效时间, 推荐配置成5s .  如果ttl太小,dns也会成为一个瓶颈点.

dns的缺点也明显,那就是ip级别,很多时候一台服务器我们会开N个port,  动态的域名只能跟踪ip的级别, 这样显得很不灵活。  由于nginx dns动态解析多适用于云主机环境,云主机一般是一两个核,所以这是可以接受的.

如果你使用skydns的化,也可以实现我们后面讲述的动态服务发现.

我们可以简单的配置dns模式.:

 
1
2
3
4
5
resolver 10.0.0.1;
set $blog http://service-123.xiaorui.cc;
location / {
    proxy_pass $blog;
}

也可以配置更加的丰富.   我这边用的是tengine。

 
1
2
3
4
5
6
7
8
9
10
upstream backend {
    dynamic_resolve fallback=stale fail_timeout=30s;
 
    server a.com;
    server b.com;
}
 
server {
    proxy_pass http://backend;
}

nginx plus版的配置.  相对来说,nginx plus的功能更加丰富点,但是需要收费,我在free期间测试过该功能,通过.

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#xiaorui.cc
 
resolver 10.0.0.1;
 
upstream xiaorui.cc {
    zone upstream_dynamic 64k;
    server backend2.example.com:8080 fail_timeout=5s slow_start=30s;
    server backend3.example.com      resolve;
    server backend4.example.com      service=http resolve;
}
 
server {
    location / {
        proxy_pass http://xiaorui.cc;
        health_check;
    }
}

另外可以使用nginx的dns加强版插件. https://github.com/GUI/nginx-upstream-dynamic-servers

第二种,动态模板解析

我在上面有说过动态模板解析的场景一般是那种运维平台系统, 我曾经在全网集群调度平台,智能dns管理系统中使用过这种方法, 他的优点在于系统的集中式管理,本身来说这种管理方式只是把手动的操作变成自动化而已,但这种配置方式相当的稳定,只是每次渲染模板后都需要nginx -s reload.

另外他的开发成本还是不低的,小公司hold不住的,一般各大公司的运维平台多使用这种方式.

简单说下他的实现,比如python语言,我们可以使用jinja2、mako的模板引擎,动态的根据你提供变量渲染配置.

第三种,服务发现模式.

服务发现是这次的重点,博客里有聊过etcd、zookeeper、consul 这三种服务发现的使用及坑.   那么跟nginx是怎么实现的动态加载配置? 他跟nginx -s reload不一样, reload会重新开一组进程来接收请求,让老进程完事后再退出.    切记,如果你线上流量较大,不要频繁的reload,   因为nginx新老配置交替的时候会掉15%左右的qps .

其实并不一定要按照reload的路子走,我们可以针对nginx进行改造。 让他可以主动去拉取最新的服务发现里的主机.  在github中的不少nginx服务发现的实现,他们不会新开进程,而是在在master进程中注册一个定时器,该事件会触发watch,当发现version的版本号发生变更时,就知道主机列表发生了变化,进而重载配置.   那么如何让worker进程也使用新的配置?  这时候可能不会使用共享变量,因为读写都要加锁,及其影响速度.  所以每个worker都会缓存一份location upstream的配置,每个worker也会有个定时器,他的事件共享内存的upstream有无变化…

优点是什么?  服务发现模式特别适合微服务,更片面的说 docker服务化.    因为docker可以更加弹性的扩展缩减集群的计算架构.    当docker容器起来后,直接给consul注册一个槽位,间隔性的发送健康心跳.  我们不需要管nginx的配置,nginx会自己动态的重载最新的配置.     当我们把docker容器干掉了,由于consul有配置过期自动清理记录,很大程度保证consul服务发现列表的 “纯洁性” .

一个nginx consul的场景图:

下图是etcd的服务发现流程.   服务的提供者会注册到服务注册中心上,请求者在请求之前会访问一次注册中心,从里面取出主机记录才会进一步访问.

nginx动态配置及服务发现那些事的更多相关文章

  1. spring-boot 2.5.4,nacos 作为配置、服务发现中心,Cloud Native Buildpacks 打包镜像,GitLab CI/CD

    spring-boot 2.5.4,nacos 作为配置.服务发现中心,Cloud Native Buildpacks 打包镜像,GitLab CI/CD 本文主要介绍 Java 通过 Cloud N ...

  2. Consul-template的简单应用:配置中心,服务发现与健康监测

    简介 Consul-template是Consul的一个方扩展工具,通过监听Consul中的数据可以动态修改一些配置文件,大家比较热衷于应用在Nginx,HAProxy上动态配置健康状态下的客户端反向 ...

  3. 使用docker+consul+nginx集成分布式的服务发现与注册架构

    一.环境说明: 1.一台虚拟机,该系统已经装好了docker: ip 192.168.10.224 虚拟网卡,与主机互通 操作系统rhel6 内核 2.6.32  64位 docker版本 1.7.1 ...

  4. Chris Richardson微服务翻译:微服务架构中的服务发现

    Chris Richardson 微服务系列翻译全7篇链接: 微服务介绍 构建微服务之使用API网关 构建微服务之微服务架构的进程通讯 微服务架构中的服务发现(本文) 微服务之事件驱动的数据管理 微服 ...

  5. Presto服务发现(Discovery Service)

    Presto 集群配置不管是coordinator还是worker配置项中都有一项discovery.uri,这个是一个比较核心的东西,简单来说就是服务发现的地址. coordinator和worke ...

  6. zookeeper服务发现实战及原理--spring-cloud-zookeeper源码分析

    1.为什么要服务发现? 服务实例的网络位置都是动态分配的.由于扩展.失败和升级,服务实例会经常动态改变,因此,客户端代码需要使用更加复杂的服务发现机制. 2.常见的服务发现开源组件 etcd—用于共享 ...

  7. 微服务实战(四):服务发现的可行方案以及实践案例 - DockOne.io

    原文:微服务实战(四):服务发现的可行方案以及实践案例 - DockOne.io 这是关于使用微服务架构创建应用系列的第四篇文章.第一篇介绍了微服务架构的模式,讨论了使用微服务架构的优缺点.第二和第三 ...

  8. etcd:用于服务发现的键值存储系统

    etcd是一个高可用的键值存储系统,主要用于共享配置和服务发现.etcd是由CoreOS开发并维护的,灵感来自于 ZooKeeper 和 Doozer,它使用Go语言编写,并通过Raft一致性算法处理 ...

  9. 开源服务发现项目Zookeeper,Doozer,Etcd

    这篇文章是Jason Wilder对于常见的服务项目发现Zookeeper.Doozer,Etcd所写的一篇博客,其原文地址例如以下:Open-Source Service Discovery. 服务 ...

随机推荐

  1. 自由是有代价的:聊聊这几年尝试的道路 要想生活好,别看哲学书和思想书。简单看看可以,看多了问题就大了。还是要去研究研究些具体的问题。别jb坐在屋子里,嘴里念着海子的诗,脑袋里想康德想的事情,兜里屁都没有,幻想自己是大国总理,去想影帝是怎么炼成的。

    自由是有代价的:聊聊这几年尝试的道路 现在不愿意写过多的技术文章了,一点是现在做的技术比较偏,写出来看的人也不多,二来是家庭事务比较繁多,没以前那么有时间写了.最近,园子里多了一些写经历的文章,我也将 ...

  2. magento 得到树形结构的分类列表

    <?php ?> <?php   class Lehui_AllCategoryList_Block_List extends Mage_Core_Block_Template { ...

  3. JavaScript三种方式改变标签css

    原文地址:https://www.cnblogs.com/xiangru0921/p/6514225.html <body> <div id="div">这 ...

  4. OCR OneNote

    文章地址:https://www.cnblogs.com/Charltsing/p/OneNoteOCR.html 前段时间有人问我能不能通过OneNote扫描图片,并返回文本.经过几天的测试,以及对 ...

  5. Elasticsearch与RDS比较

    Elasticsearch是一个分布式,实时,全文搜索引擎.所有操作都是通过RESTful接口实现,其底层实现是基于Lucene全文搜索引擎.数据以JSON文档的格式存储索引,不需要预先规定范式. 和 ...

  6. Canvas文本操作

    Canvas的画图环境提供三个方法如:绘制填充文本:fillText();绘制描边文本:strokeText();绘制文本并返回一个对象:measure();measure()方法返回的对象中包括一个 ...

  7. Linux Crontab内环境变量与Shell环境变量的关系及解决问题的办法

    为了定时监控Linux系统CPU.内存.负载的使用情况,写了个Shell脚本,当达到一定值得时候,发送邮件通知.需要用到Crontab的定时任务去执行这个脚本,但是发现通过命令(./test.sh)执 ...

  8. 【转载并整理】JAVA解析或生成xml的四种方法

    参考文章 1:http://blog.csdn.net/clemontine/article/details/53011362 2:http://www.jb51.net/article/98456. ...

  9. java appium客户端 6.1.0android长按及滑动变更

    今天使用appium 6.1.0 java客户端,发现长按longpress(element),无法使用.如下代码在5.0.4 版本是可以正常运行的, WebElement noteDelete = ...

  10. Vue 动态组件、动画、插件

    1 动态组件 ①简单来说: 就是几个组件放在一个挂载点下,然后根据父组件的某个变量来决定显示哪个,或者都不显示. ②动态切换: 在挂载点使用component标签,然后使用v-bind:is=”组件名 ...