define
Grayscale publishing is published in a smooth transition way, by switching the route weight between the old version and the new version on the line, gradually switching from the old version to the new version; For example, in order to launch new functions, only a small number of service nodes should be updated first, and a small number of users should experience the new version through routing weights. If there are no problems, all service nodes should be updated. In this way, the impact is minimized and the system stability is ensured.
Gray released
A system often has an access layer such as Nginx (Openresty), a gateway layer such as Zuul, and a service layer such as various RPC frameworks. There are routing functions in these layers, that is to say, these layers can do grayscale; The access layer can use Nginx + Lua to achieve gray level, the gateway layer zuul can combine ribbon to achieve gray level, RPC framework such as Dubbo itself provides routing function can directly do gray level processing; Let’s see how to do that;
Access layer gray
At the access layer, we use Openresty with more powerful functions, and then use Lua for routing and forwarding. The relevant routing policy can be configured in distributed cache Redis, and of course, it can also be persisted in database.
- To prepare
Prepare one Openresty, two tomcat web servers (port 8081,8082), and redis. In order to facilitate simulation in redis configuration whitelist, if in the whitelist, go 8082, not 8081;
- Openresty configuration
The luA script that supports lua and related routing needs to be configured in Openresty. Nginx.conf is configured as follows:
http { ... lua_package_path "/lualib/? .lua;;" ; Lua module lua_package_cpath "/lualib/? .so;;" ; Upstream tomcat1 {server 127.0.0.1:8081; } upstream tomcat2 {server 127.0.0.1:8082; } server { listen 80; server_name localhost; location / { content_by_lua_file lua/gray.lua; } location @tomcat1 { proxy_pass http://tomcat1; } location @tomcat2 { proxy_pass http://tomcat2; }}... }Copy the code
Configure all requests to go through the gray. Lua script in the lua directory, as shown below:
local redis = require "resty.redis"; local redis_obj = redis:new(); redis_obj:set_timeout(2000); Local OK,err = redis_obj:connect("127.0.0.1", 6379); if not ok then ngx.say("failed to connect redis ",err); return; Local_ip = ngx.var.remote_addr; Local whitelist = redis_obj:get("whitelist"); If string.find(whitelist,local_ip) == nil then ngx.exec("@tomcat1"); else ngx.exec("@tomcat2"); end local ok,err = redis_obj:close();Copy the code
The built-in function module of Openresty can directly connect to Redis, and then extract the whitelist from Redis to see whether the current request IP is in the whitelist, and then perform simple routing functions. Can dynamically modify redis inside the whitelist, real-time update.
Localhost :0>set whitelist 127.0.0.1" OK" localhost:0>get whitelist "127.0.0.1"Copy the code
- Start the test
Start Tomcat1, Tomcat2, and Openresty respectively at http://localhost to dynamically modify the whitelist in Redis, and then visit to see the results verified.
Gateway layer gray scale
Zuul takes the gateway layer as an example. Zuul needs to modify the ribbon’s load policy based on Eureka’s metadata.
- To prepare
Two ports are prepared for the test service respectively: 8765,8766. The application. Yml configuration is as follows:
server:
port: 8765
eureka:
instance:
metadata-map:
route: 1
Copy the code
Also prepare the request address /hiGray, return value route1;
server:
port: 8766
eureka:
instance:
metadata-map:
route: 2
Copy the code
Also prepare request address /hiGray, return value route2; Used to distinguish whether to go gray server; Then you need to introduce a plug-in on the Zuul side:
<dependency> <groupId>io.jmnarloch</groupId> <artifactId>ribbon-discovery-filter-spring-cloud-starter</artifactId> The < version > 2.1.0 < / version > < / dependency >Copy the code
You need to prepare a filter of pre type as follows:
@Configuration public class GrayFilter extends ZuulFilter { @Override public Object run() { RequestContext ctx = RequestContext.getCurrentContext(); HttpServletRequest request = ctx.getRequest(); String ip = request.getRemoteAddr(); //ipv6 local address, Namely 127.0.0.1 if (" 0:0:0:0:0:0:1-0 ". The equals (IP)) {RibbonFilterContextHolder. GetCurrentContext (). The add (" route ", "1"); } else { RibbonFilterContextHolder.getCurrentContext() .add("route", "2"); } return null; }... }Copy the code
The whitelist address is ipv6:0:0:0:0:0:0:0:0:1. If it is a whitelist address, it is routed to port 8765. If it is a whitelist address, port 8766 is used.
- test
Start eureka-Server, two Eureka-Clients, and Zuul gateway respectively, and access the gateway address. Test using 127.0.0.1 and local IP access respectively;
Service layer gray
The server uses the RPC framework Dubbo as an example. Dubbo itself provides various routing rules, including conditional routing and script routing. The script routing rules support all scripts of the JDK script engine, such as: Javascript, JRuby, Groovy, etc., using the default javascript as an example;
- To prepare
Registries for ZooKeeper, two providers can specify ports 20881 and 20882 locally, respectively, as consumers, and the routing scripts highlighted below:
function gray_rule(invokers, context) {
var tag = context.getAttachment("tag");
var result = new java.util.ArrayList(invokers.size());
if(tag == "gray"){
for (i = 0; i < invokers.size(); i ++) {
if (invokers.get(i).getUrl().getPort()==20881) {
result.add(invokers.get(i));
}
}
} else {
for (i = 0; i < invokers.size(); i ++) {
if (invokers.get(i).getUrl().getPort()==20882) {
result.add(invokers.get(i));
}
}
}
return result;
} (invokers,context)
Copy the code
Dubbo runs the Invocation with three arguments: Invokers, Invocation and rpcContext.getContext (); By setting the tag in the RpcContext on the consumer side:
RpcContext.getContext().setAttachment("tag", "gray");
Copy the code
In this way, we can determine in the script that only the consumer with gray tag goes to the server with port 20881, and the rest go to the server with port 20882. The above script needs to be registered with ZooKeeper. The manual registration code is as follows. You can also use dubbo-admin to set the routing script:
URL registryUrl = URL. The valueOf (" zookeeper: / / 127.0.0.1:2181 "); ZookeeperRegistryFactory zookeeperRegistryFactory = new ZookeeperRegistryFactory(); zookeeperRegistryFactory.setZookeeperTransporter(new CuratorZookeeperTransporter()); Registry zookeeperRegistry = (ZookeeperRegistry) zookeeperRegistryFactory.createRegistry(registryUrl); URL routerURL = URL. The valueOf (" script: / / 0.0.0.0 / com. DubboApi. DemoService? category=routers&dynamic=false"); routerURL = routerURL.addParameter("rule", URL.encode("(.. JavaScript..) ")); zookeeperRegistry.register(routerURL); / / registerCopy the code
For details, see the official document old Routing Rules
- test
Start ZooKeeper, then start two producers, start the consumer by modifying the tag and observe the route;
conclusion
In this paper, from the access layer, gateway layer, service layer of these three layers briefly introduced by routing rules to achieve grayscale publishing; Each layer of typical middleware to introduce how to achieve a simple grayscale release; In general, it uses the routing function of middleware to dynamically load some external customized routing strategy scripts, so as to achieve the purpose of grayscale publishing.
The code address
Dubbo
Spring-Cloud
Thank you for attention
You can pay attention to the wechat public account “Roll back the code”, read the first time, the article continues to update; Focus on Java source code, architecture, algorithms, and interviews