网站首页 > 精选教程 正文
在之前提到的OpenResty/Nginx的负载均衡当中,当服务器启动之后,upstream中的上游服务器就是固定死的了,做不到动态的变更。这里面说到的变更,其实更多指的是增加机器。因为当上游服务器不可用时,upstream会自动将服务器摘除,但是当新增服务器时,upstream就做不到了。传统的负载均衡办法,就是能是修改配置,然后重启服务。下面介绍一下动态负载均衡的方式,一种是通过动态重启服务;另外一种是通过代码的方式动态拉取服务器列表。
Consul
Consul是一个分布式服务注册与发现系统。这里面使用Consul来管理上游服务器,当服务器启动时将其注册到注册中心去,当服务关闭时从注册中心列表中剔除。这里面需要注意一点的是:当上游服务器关闭时,Consul本身不会自动从列表中剔除,而是需要在服务器关闭前主动向Consul发起删除服务。
Consul有以下特性:
- 服务注册:服务提供者可以通过HTTP或DNS的方式,将服务注册到Consul中去。
- 服务发现:服务消费者可以通过HTTP或DNS的方式,获取服务的IP和PORT。
- 故障检测:支持如TCP/HTTP等方式的健康检查机制,当服务出现故障时摘除服务。
- K/V存储:使用key-value实现配置中心,使用HTTP长轮询实现变更配置。
- 多数据中心:支持多数据中心,可以按照数据中心注册和发现服务。可以支持只消费本地机房的服务,多机房可以做到异地容灾。
- Raft算法:Consul的一致性算法。
通过Consul可以获取到upstream中的上游服务器列表,下面要做的事情就是生成upstream中的模板了。这里就需要用到Consul-templete,它可以使用HTTP长轮询实现变更触发和配置更改。从而可以根据Consul服务器列表动态生成配置文件,然后去重新启动OpenResty/Nginx即可。
Consul+Consul-templete 方式
Consul+Consul-templete 就如上面所说的,是一种监听服务器列表变更,然后动态生成upstream模板,重启服务器。
Consul-Server
笔者使用的是MAC,下面所进行的操作都是基于MAC系统的。首先需要安装Consul如下:
brew install consul
安装完成之后,可以通过如下命令启动Consul服务:
consul agent -dev
启动完成之后,可以通过如下地址:localhost:8500/ui。访问Consul的Web界面:
- 注册服务
可以使用HTTP的方式向Consul注册一个服务:
curl -X PUT http://localhost:8500/v1/agent/service/register -d ' { "ID": "moguhu_server1", "Name": "moguhu_server", "Tags": ["dev"], "Address": "127.0.0.1", "Port": 8081 } '
- ID:代表要注册的服务的唯一标识
- Name:表示一组服务的名称
- Tags:服务标签,可以用于区分开发、测试环境
- Address:服务的地址
- Port:服务的端口
Consul-template
Consul-template的作用是生成upstream配置模板,安装命令如下:
brew install consul-template
然后在nginx.conf同级目录下创建moguhu_server.ctmpl
upstream moguhu_server { {{range service "dev.moguhu_server@dc1"}} server {{.Address}}:{{.Port}}; {{end}} }
重启OpenResty脚本如下:reboot.sh
ps -ef | grep nginx | grep -v grep if [ $? -ne 0 ] then sudo ~/software/openresty/nginx/sbin/nginx -p ~/hugege/code-sublime/01-zhtest -c ~/hugege/code-sublime/01-zhtest/config/nginx.conf echo "OpenResty start..." else sudo ~/software/openresty/nginx/sbin/nginx -p ~/hugege/code-sublime/01-zhtest -c ~/hugege/code-sublime/01-zhtest/config/nginx.conf -s reload echo "Openresty restart..." fi
然后nginx.conf配置如下:
include /Users/xuefeihu/hugege/code-sublime/01-zhtest/config/moguhu_server.conf; ... server { listen 80; server_name localhost; charset utf-8; location / { proxy_pass http://moguhu_server; } }
上游服务器
上游服务器upstream中使用的是Spring Boot实现的,其核心代码如下所示:
@Configuration public class ConsulConfiguration { @Value("${server.port}") private int port; @Value("${spring.application.name}") private String serviceId; @PostConstruct public void init() { // 参数完全对应HTTP API ImmutableRegistration.Builder builder = ImmutableRegistration.builder() .id(serviceId) .name("moguhu_server") .address("127.0.0.1") .port(port) .addTags("dev"); // 向Consul注册服务 Consul consul = Consul.builder().withHostAndPort(HostAndPort.fromString("127.0.0.1:8500")).build(); final AgentClient agentClient = consul.agentClient(); agentClient.register(builder.build()); //注册shutdown hook,停掉应用时从Consul摘除服务 Runtime.getRuntime().addShutdownHook(new Thread(() -> agentClient.deregister(serviceId))); } }
笔者在实验时,Consul版本的问题,造成在JVM停止时,没有执行删除服务的操作。因此附上下面的pom依赖
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.17</version> </dependency> <dependency> <groupId>com.orbitz.consul</groupId> <artifactId>consul-client</artifactId> <version>1.2.1</version> </dependency> <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>25.1-jre</version> </dependency> <dependency> <groupId>com.squareup.okhttp3</groupId> <artifactId>okhttp</artifactId> <version>3.10.0</version> </dependency> </dependencies>
测试验证
1、启动Consul
consul agent -dev
2、启动Consul-template
consul-template -consul-addr 127.0.0.1:8500 -template \ "/Users/xuefeihu/hugege/code-sublime/01-zhtest/config/moguhu_server.ctmpl:\ /Users/xuefeihu/hugege/code-sublime/01-zhtest/config/moguhu_server.conf:\ sh /Users/xuefeihu/hugege/code-sublime/01-zhtest/config/reboot.sh"
3、启动2台upstream服务器
然后你会发现在nginx.conf的同级目录下生成了moguhu_server.conf文件,内容如下:
upstream real_server { server 127.0.0.1:8081; server 127.0.0.1:8082; }
当手动停掉一台服务器时,配置又会变更为如下:
upstream real_server { server 127.0.0.1:8081; }
此时reboot.sh脚本会自动触发执行,如下所示:
Consul+Lua 方式
上面的方式实现动态负载均衡在配置较多的时候会有一些问题,比如配置较多时,OpenResty重启的速度就会变慢。所以通过Lua脚本的方式可以规避掉重启这一步骤。
使用Lua实现时,与上面的组件相比Consul-templete就不需要了。通过Consul的http://127.0.0.1:8500/v1/catalog/service/moguhu_server接口就可以获取到服务的列表,如下所示:
[ { "ID": "5d452e6b-71e0-67ae-5169-c7e6342da53b", "Node": "Jacks-MacBook-Air.local", "Address": "127.0.0.1", "Datacenter": "dc1", "TaggedAddresses": { "lan": "127.0.0.1", "wan": "127.0.0.1" }, "NodeMeta": { "consul-network-segment": "" }, "ServiceID": "csi", "ServiceName": "moguhu_server", "ServiceTags": [ "dev" ], "ServiceAddress": "127.0.0.1", "ServiceMeta": {}, "ServicePort": 8081, "ServiceEnableTagOverride": false, "CreateIndex": 10, "ModifyIndex": 10 }, { "ID": "5d452e6b-71e0-67ae-5169-c7e6342da53b", "Node": "Jacks-MacBook-Air.local", "Address": "127.0.0.1", "Datacenter": "dc1", "TaggedAddresses": { "lan": "127.0.0.1", "wan": "127.0.0.1" }, "NodeMeta": { "consul-network-segment": "" }, "ServiceID": "oc", "ServiceName": "moguhu_server", "ServiceTags": [ "dev" ], "ServiceAddress": "127.0.0.1", "ServiceMeta": {}, "ServicePort": 8082, "ServiceEnableTagOverride": false, "CreateIndex": 12, "ModifyIndex": 12 } ]
这一方式当中主要就是OpenResty里面的相关配置。
OpenResty 配置
upstreams.lua
local http = require "socket.http" local ltn12 = require "ltn12" local cjson = require "cjson" local _M = {} _M._VERSION="0.1" function _M:update_upstreams() local resp = {} http.request{ url = "http://127.0.0.1:8500/v1/catalog/service/moguhu_server", sink = ltn12.sink.table(resp) } local resp = cjson.decode(resp) local upstreams = {} for i, v in ipairs(resp) do upstreams[i+1] = {ip=v.Address, port=v.ServicePort} end ngx.shared.upstream_list:set("moguhu_server", cjson.encode(upstreams)) end function _M:get_upstreams() local upstreams_str = ngx.shared.upstream_list:get("moguhu_server") return upstreams_str end return _M
nginx.conf
lua_shared_dict upstream_list 10m; # 第一次初始化 init_by_lua_block { local upstreams = require "upstreams"; upstreams.update_upstreams(); } # 定时拉取配置 init_worker_by_lua_block { local upstreams = require "upstreams"; local handle = nil; handle = function () --TODO:控制每次只有一个worker执行 upstreams.update_upstreams(); ngx.timer.at(5, handle); end ngx.timer.at(5, handle); } ... upstream moguhu_server { server 0.0.0.1; #占位server balancer_by_lua_block { local balancer = require "ngx.balancer"; local upstreams = require "upstreams"; local tmp_upstreams = upstreams.get_upstreams(); local ip_port = tmp_upstreams[math.random(1, table.getn(tmp_upstreams))]; ngx.log(ngx.ERR, "current :=============", math.random(1, table.getn(tmp_upstreams))); balancer.set_current_peer(ip_port.ip, ip_port.port); } } server { listen 80; server_name localhost; charset utf-8; location / { proxy_pass http://moguhu_server; } }
上面通过balancer_by_lua_block去动态的设置了,upstream的服务器列表。然后启动OpenResty就可以了。
参考:《亿级流量网站架构核心技术》
- 上一篇: 什么是 Dockerfile? 什么是质数
- 下一篇: Linux 三剑客 linux三剑客命令详解
猜你喜欢
- 2024-10-20 linux日常脚本 linux常用脚本语言
- 2024-10-20 Linux 三剑客 linux三剑客命令详解
- 2024-10-20 Dockerfile 命令详解 dockerfile from命令
- 2024-10-20 什么是 Dockerfile? 什么是质数
- 2024-10-20 Docker实战九之Docker Dockerfile
- 2024-10-20 Centos7迁移不同的PHP项目工作笔记
- 2024-10-20 Docker之容器命令 docker容器命令行不存在能否使用宿主命令
- 2024-10-20 Docker容器服务编排实践 docker 容器编排
- 2024-10-20 详解Linux Shell脚本编写技巧,附实例说明
- 2024-10-20 从实例来学习Linux shell脚本,附实例分享
你 发表评论:
欢迎- 最近发表
- 标签列表
-
- nginx反向代理 (57)
- nginx日志 (56)
- nginx限制ip访问 (62)
- mac安装nginx (55)
- java和mysql (59)
- java中final (62)
- win10安装java (72)
- java启动参数 (64)
- java链表反转 (64)
- 字符串反转java (72)
- java逻辑运算符 (59)
- java 请求url (65)
- java信号量 (57)
- java定义枚举 (59)
- java字符串压缩 (56)
- java中的反射 (59)
- java 三维数组 (55)
- java插入排序 (68)
- java线程的状态 (62)
- java异步调用 (55)
- java中的异常处理 (62)
- java锁机制 (54)
- java静态内部类 (55)
- java怎么添加图片 (60)
- java 权限框架 (55)
本文暂时没有评论,来添加一个吧(●'◡'●)