> For the complete documentation index, see [llms.txt](https://maxwell.gitbook.io/way-to-architect/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://maxwell.gitbook.io/way-to-architect/xi-tong-she-ji/fu-zai-jun-heng/nginx-dong-tai-pei-zhi.md).

# Nginx动态配置

## Nginx七层负载均衡动态配置

在使用upstream实现负载均衡时，假如upstream列表有变更，那么就需要到服务器进行手动修改。有没有这样一种方式：后端服务上线时，将其自动注册到upstream列表中；后端服务下线时，自动从upstream列表中摘除？答案就是使用配置中心。

这里以Consul为例。

Consul是一款开源的分布式服务注册与发现系统，通过HTTP API可以使得服务注册、发现实现起来非常简单，它支持如下特性：

* **服务注册**：服务实现者可以通过HTTP API或DNS方式，将服务注册到Consul。
* **服务发现**：服务消费者可以通过HTTP API或DNS方式，从Consul获取服务的IP和PORT。
* **故障检测**：支持如TCP、HTTP等方式的健康检查机制，从而当服务有故障时自动摘除。
* **K/V存储**：使用K/V存储实现动态配置中心，其使用HTTP长轮询实现变更触发和配置更改。
* **多数据中心**：支持多数据中心，可以按照数据中心注册和发现服务，即支持只消费本地机房服务，使用多数据中心集群还可以避免单数据中心的单点故障。
* **Raft算法**：Consul使用Raft算法实现集群数据一致性。

通过Consul可以管理服务注册与发现，接下来需要有一个与Nginx部署在同一台机器的Agent来实现Nginx配置更改和Nginx重启功能。我们有Confd或者Consul-template两个选择，而Consul-template是Consul官方提供的，我们就选择它了。其使用HTTP长轮询实现变更触发和配置更改（使用Consul的watch命令实现）。也就是说，我们使用Consul-template实现配置模板，然后拉取Consul配置渲染模板来生成Nginx实际配置。

除Consul外，还有一个选择是etcd3，其使用了gRPC和protobuf可以说是一个亮点。不过，etcd3目前没有提供多数据中心、故障检测、Web管理界面。

### 使用Consul + Consul-template实现

整体架构图如下：

![](/files/-LFrMvKlnPbM3HDWRjgm)

1．首先，`upstream`服务启动，我们通过管理后台向`Consul`注册服务。

2．在`Nginx`机器上部署并启动`Consul-template Agent`，其通过长轮询监听服务变更。

3．`Consul-template`监听到变更后，动态修改`upstream`列表。

4．`Consul-template`修改完`upstream`列表后，调用重启`Nginx`脚本重启`Nginx`。

首先我们要启动Consul-Server。

./consul agent -server -bootstrap-expect 1-data-dir /tmp/consul  -bind 0.0.0.0-client 0.0.0.0

此处需要使用data-dir指定Agent状态存储位置，

bind指定集群通信的地址，

client指定客户端通信的地址（如Consul-template与Consul通信）。

在启动时还可以使用-ui-dir ./ui/指定Consul Web UI目录，实现通过Web UI管理Consul，

然后访问如[http://127.0.0.1:8500即可看到控制界面。](https://maxwell.gitbook.io/way-to-architect/xi-tong-she-ji/fu-zai-jun-heng/http:/127.0.0.1:8500即可看到控制界面。)

![](http://www.10tiao.com/img.do?url=http%3A//mmbiz.qpic.cn/mmbiz_png/G38RbhwficlcMyKCicVMJssAEwcDwJ0VZvPsczTI4IPuEKfBN3WA17XFp0FXoOOpj1GydRicc1lUX4H7A9zQBQxMQ/0%3Fwx_fmt%3Dpng)

使用如下HTTP API注册服务。

```bash
curl -X PUT http://127.0.0.1:8500/v1/catalog/register -d'{"Datacenter": "dc1", "Node":"tomcat", "Address":"192.168.1.1","Service": {"Id" :"192.168.1.1:8080", "Service": "item_jd_tomcat","tags": ["dev"], "Port": 8080}}'
curl -X PUT http://127.0.0.1:8500/v1/catalog/register -d'{"Datacenter": "dc1", "Node":"tomcat", "Address":"192.168.1.2","Service": {"Id" :"192.168.1.1:8090", "Service": "item_jd_tomcat","tags": ["dev"], "Port": 8090}}'
```

上面指令中，Datacenter指定数据中心，Address指定服务IP，Service.Id指定服务唯一标识，Service.Service指定服务分组，Service.tags指定服务标签（如测试环境、预发环境等），Service.Port指定服务端口。

通过如下HTTP API摘除服务。

```bash
curl -X PUThttp://127.0.0.1:8500/v1/catalog/deregister -d '{"Datacenter":"dc1", "Node": "tomcat", "ServiceID" :"192.168.1.1:8080"}'
```

通过如下HTTP API发现服务。

```bash
curl http://127.0.0.1:8500/v1/catalog/service/item_jd_tomcat
```

接下来我们需要在Consul-template机器上添加一份配置模板`item.jd.tomcat.ctmpl`。

```lua
upstream item_jd_tomcat {
    #占位server，必须有一个server，否则无法启动
    server 127.0.0.1:1111; 
    {{range service "dev.item_jd_tomcat@dc1"}}
       server {{.Address}}:{{.Port}} weight=1;
    {{end}}
}
```

service 指定格式为：标签.服务@数据中心，然后通过循环输出Address和Port，从而生成Nginx upstream配置。

接下来启动Consul-template：

```bash
./consul-template -consul 127.0.0.1:8500 -template./item.jd.tomcat.ctmpl:/usr/servers/nginx/conf/domains/item.jd.tomcat:"./restart.sh"
```

使用consul指定Consul服务器客户端通信地址，template格式是“配置模板:目标配置文件:脚本”，即通过配置模板更新目标配置文件，然后调用脚本重启Nginx。

直接通过`Nginx include`指令将`/usr/servers/nginx/conf/domains/item.jd.tomcat`包含到`nginx.conf`配置文件即可，`restart.sh`脚本代码如下所示。

```bash
#!/bin/bash
ps -fe | grep nginx | grep -v grep
if [ $? -ne 0 ]
then
  sudo /usr/servers/nginx/sbin/nginx
  echo "nginx start"
else
  sudo /usr/servers/nginx/sbin/nginx -s reload
  echo "nginx reload"
fi
```

即如果Nginx没有启动，则启动，否则重启。

Java真实服务中进行服务注册与摘除

项目中添加`Consul Java Client`：

```markup
<dependency>
    <groupId>com.orbitz.consul</groupId>
    <artifactId>consul-client</artifactId>
    <version>0.12.8</version>
</dependency>
```

代码中进行服务注册与摘除：

```java
public static void main(String[] args) {
    //启动嵌入容器（如Tomcat）
    SpringApplication.run(Bootstrap.class, args);
    //服务注册
    Consul consul = Consul.builder().withHostAndPort(HostAndPort.fromString ("192.168.61.129:8500")).build();
    final AgentClient agentClient = consul.agentClient();
    String service = "item_jd_tomcat";
    String address = "192.168.61.1";
    String tag = "dev";
    int port= 9080;
    final String serviceId = address + ":" + port;
    ImmutableRegistration.Builder builder = ImmutableRegistration.builder();
    builder.id(serviceId).name(service).address(address).port(port).addTags(tag);
    agentClient.register(builder.build());
    //JVM停止时摘除服务
    Runtime.getRuntime().addShutdownHook(new Thread() {
       @Override
        publicvoid run() {
           agentClient.deregister(serviceId);
        }
    });
}
```

到此我们就实现了动态`upstream`负载均衡，`upstream`服务启动后自动注册到`Nginx`，`upstream`服务停止时，自动从`Nginx`上摘除。

通过`Consul`+ `Consul-template`方式，每次发现配置变更都需要`reload nginx`，而reload是有一定损耗的。而且，如果你需要长连接支持的话，那么当`reload nginx`时长连接所在worker进程会进行优雅退出，并当该worker进程上的所有连接都释放时，进程才真正退出（表现为worker进程处于worker process is shutting down）。因此，如果能做到不reload就能动态更改upstream，那么就完美了。对于社区版Nginx目前有三个选择：Tengine的Dyups模块、微博的Upsync和使用OpenResty的balancer\_by\_lua。微博使用Upsync+Consul实现动态负载均衡，而又拍云使用其开源的slardar（Consul + balancer\_by\_lua）实现动态负载均衡。

### 使用**Consul+OpenResty实现**

todo

## 内容来源

[使用Nginx实现HTTP动态负载均衡—《亿级流量网站架构核心技术》<br>](http://www.10tiao.com/html/164/201703/2652898370/1.html)

### <br>


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://maxwell.gitbook.io/way-to-architect/xi-tong-she-ji/fu-zai-jun-heng/nginx-dong-tai-pei-zhi.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
