当前位置:网站首页>Implementation of gray-scale publishing scheme for microservice architecture based on gateway and Nacos

Implementation of gray-scale publishing scheme for microservice architecture based on gateway and Nacos

2022-06-11 14:47:00 Xiao bichao

One 、 Grayscale Publishing

Grayscale Publishing ( Also known as the Canary release ) Between black and white , A publishing method that can smooth the transition . On it can be carried out A/B testing, Let some users continue to use product features A, Some users start to use product features B, If the user is right B There is no objection , Then gradually expand the scope , Move all users to B Up here . Gray level release can guarantee the stability of the whole system , It can be found in the initial grayscale 、 Adjustment issues , In order to ensure its impact .

The period from the beginning to the end of grayscale Publishing , It's called grayscale . Grayscale publishing can get users' feedback as early as possible , Perfect product function , Improve product quality , Let users participate in product testing , Enhance interaction with users , Reduce the range of users affected by product upgrades .

Based on GateWay and Nacos Realize the gray-scale publishing scheme of micro service architecture , First, the production services and gray environment services are registered to Nacos in , But the version is different , For example, the production environment version is 1.0 , The grayscale environment version is 2.0 , After the request passes through the gateway , Determine whether the carried user is a grayscale user , If so, forward the request to 2.0 In the service of , Otherwise forward to 1.0 In the service of .

Two 、 Start implementing

First set up two web Service simulation production and gray environment , Register to nacos in , Pay attention to the service here ID Should agree :

Production environment configuration :

spring:
  application:
    name: web
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
        metadata:
          version: 1.0 #  Specified version number 

Grayscale environment configuration :

spring:
  application:
    name: web
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
        metadata:
          version: 2.0 #  Specified version number 

After starting two services , Can be in nacos See details in :

 Insert picture description here
 Insert picture description here
In order to simulate the difference between the two services , Create the same interface , Different returns :

@RestController
public class TestController {

    @GetMapping("/getTest")
    public String getTest(){
        return " The current in the - Production environment !";
    }
}
@RestController
public class TestController {

    @GetMapping("/getTest")
    public String getTest(){
        return " The current in the - Gray scale environment !";
    }
}

Let's start building GateWay gateway , You also need to register to nacos in , But what's different from before is , Here we want to implement a load balancer , Determine which version of service to use in the load balancer , Here's to demonstrate the effect , stay nacos Create a new profile , Configure the grayscale user in this configuration file , In the project, we should start from db or noSQL Intermediate acquisition .

 Insert picture description here

Data ID: env-config.yaml
Group: DEFAULT_GROUP

env:
  gray:
    version: 2.0
    users: abc,ii,ss,kk,bb,pp
  pro:
    version: 1.0

Add another GateWay Routing configuration :
 Insert picture description here

Data ID:gateway.yaml
Group: DEFAULT_GROUP

spring:
  cloud:
    gateway:
      httpclient:
        connect-timeout: 2000
        response-timeout: 10s
      routes:
        - id: web
          uri: lb://web/
          order: 0
          predicates:
            - Path=/web/**
          filters:
            - StripPrefix=1 #  Remove the prefix from the request address 

Here we build gateway Gateway service , Sign up to nacos in , And load the configuration file created above :

spring:
  application:
    name: gateway
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
      config:
        server-addr: 127.0.0.1:8848
        file-extension: yaml
        refresh-enabled: true
        extension-configs[0]:
          data-id: env-config.yaml
          group: DEFAULT_GROUP
          refresh: true

After starting , Check whether you have registered to nacos It's in :
 Insert picture description here

Test whether load forwarding can be performed :

 Insert picture description here
 Insert picture description here

The load effect has been achieved , But it has not achieved the desired effect , So let's start with gateway The gateway is modified .

First, let's create a new one EnvProperties To receive env-config.yaml Configuration in , Be sure to add @RefreshScope annotation , Only in this way can the corresponding service be notified after the configuration is modified :

@Data
@Configuration
@RefreshScope
public class EnvProperties {
    @Value("${env.pro.version}")
    private String proVersion;

    @Value("${env.gray.users}")
    private List<String> grayUsers;

    @Value("${env.gray.version}")
    private String grayVersion;
}

Creating a ThreadLocal , Store the current version information , Let's write it down here , Then we will know what the function is :

public class GrayscaleThreadLocalEnvironment {
    private static ThreadLocal<String> threadLocal = new ThreadLocal<String>();
    
    public static void setCurrentEnvironment(String currentEnvironmentVsersion) {
        threadLocal.set(currentEnvironmentVsersion);
    }
    
    public static String getCurrentEnvironment() {
        return threadLocal.get();
    }
}

Create below filter Intercept request , Then get the user information , Here is the default user ID stay header in ,key by userId, After getting it, judge whether it is in Grayscale user list , If it exists, put the current ThreadLocal( It is stated above ThreadLocal ) The version number of the grayscale stored in ,, Otherwise, it is the production version number :

@Component
@RefreshScope
public class GrayscaleGlobalFilter implements GlobalFilter, Ordered {

    @Autowired
    EnvProperties envProperties;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();
        HttpHeaders header = response.getHeaders();
        header.add("Content-Type", "application/json; charset=UTF-8");

        List<String> list = request.getHeaders().get("userId");
        if (Objects.isNull(list) || list.isEmpty()) {
            return resultErrorMsg(response,"  The lack of userId!");
        }
        String userId = list.get(0);
        if (StringUtils.isBlank(userId)) {
            return resultErrorMsg(response,"  The lack of userId!");
        }
        if (envProperties.getGrayUsers().contains(userId)) {
            // Specify grayscale version 
            GrayscaleThreadLocalEnvironment.setCurrentEnvironment(envProperties.getGrayVersion());
        } else {
            // Specify production version 
            GrayscaleThreadLocalEnvironment.setCurrentEnvironment(envProperties.getProVersion());
        }
        return chain.filter(exchange.mutate().request(request).build());
    }

    public int getOrder() {
        return -1;
    }

    private Mono<Void> resultErrorMsg(ServerHttpResponse response, String msg) {
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("code", "403");
        jsonObject.put("message", msg);
        DataBuffer buffer = response.bufferFactory().wrap(jsonObject.toJSONString().getBytes());
        return response.writeWith(Mono.just(buffer));
    }
}

The above filter has identified whether the current request belongs to grayscale or production , Now we need to rewrite Ribbon Load Balancer , It's rewritten here RoundRobinRule , stay choose In the method , Based on the current ThreadLocal Version in , A service with an equivalent version in a convenience service , As a forwarding service , To prevent service acquisition failure , The retry policy has been added here , retry 10 Time or failure , I.e. abort retry :

@Component
@Slf4j
public class EnvRoundRobinRule extends RoundRobinRule {

    private AtomicInteger nextServerCyclicCounter;

    public EnvRoundRobinRule() {
        nextServerCyclicCounter = new AtomicInteger(0);
    }

    public Server choose(ILoadBalancer lb, Object key) {
        if (lb == null) {
            log.warn("no load balancer");
            return null;
        }
        Server server = null;
        int count = 0;
        //  If you fail , retry  10  Time 
        while (Objects.isNull(server) && count++ < 10) {
            List<Server> reachableServers = lb.getReachableServers();
            List<Server> allServers = lb.getAllServers();
            int upCount = reachableServers.size();
            int serverCount = allServers.size();

            if ((upCount == 0) || (serverCount == 0)) {
                log.warn("No up servers available from load balancer: " + lb);
                return null;
            }
            List<NacosServer> filterServers = new ArrayList<>();
            String currentEnvironmentVersion = GrayscaleThreadLocalEnvironment.getCurrentEnvironment();

            for (Server serverInfo : reachableServers) {
                NacosServer nacosServer = (NacosServer) serverInfo;
                String version = nacosServer.getMetadata().get("version");
                if (version.equals(currentEnvironmentVersion)) {
                    filterServers.add(nacosServer);
                }
            }

            int filterServerCount = filterServers.size();
            int nextServerIndex = incrementAndGetModulo(filterServerCount);
            server = filterServers.get(nextServerIndex);

            if (server == null) {
                Thread.yield();
                continue;
            }
            if (server.isAlive() && (server.isReadyToServe())) {
                return (server);
            }
            server = null;
        }

        if (count >= 10) {
            log.warn("No available alive servers after 10 tries from load balancer: "
                    + lb);
        }
        return server;
    }

    private int incrementAndGetModulo(int modulo) {
        for (; ; ) {
            int current = nextServerCyclicCounter.get();
            int next = (current + 1) % modulo;
            if (nextServerCyclicCounter.compareAndSet(current, next))
                return next;
        }
    }
}

Here we are The process is almost over , The following is header add userId by abc, Then visit more times , You can see that they have been forwarded to Gray scale environment :
 Insert picture description here
The following is header add userId by 110, Then visit more times , You can see that they have been forwarded to Production environment :
 Insert picture description here

原网站

版权声明
本文为[Xiao bichao]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/162/202206111430227966.html