痛点
在联调和测试阶段,我们都需要环境,包括前段和后端。如何使用环境?可以使用一个环境,比如dev后者dit。但这样相互影响太严重,我一段代码发布一下,其他人改代码再发布一下,如果修改的有问题可能会影响其他人的联调和测试。
解决办法
金丝雀部署。有一下四个问题需要解决,一个一个来,慢慢更新
- 前端使用nginx
- 后端Nacos+FeignClient使用分组实现
- 后端网关
- 后端mq组件
具体实现
一、前端使用nginx
见:https://blog.csdn.net/o544033135/article/details/128339764
如果想动态,需要使用Kong或者Openrest
二、Nacos+FeignClient
方法一,使用分组实现
服务启动时指定分组,此时,同组的服务才能进行交互。
spring:
application:
name: demo
cloud:
nacos:
discovery:
server-addr: localhost:8848
group: group001
但是这里有个问题,如果同组没有服务就调用失败了。如果想在同组没有服务的情况下仍然可以发起调用,需要自定义规则。
方法二,自定义路由
使用自定义IRule,修复负载算法,从所有服务端中选择同一个canary环境的服务进行调用,如果没有,则选择稳定环境,代码如下。需要增加这个配置【canary.enable=true】自定义规则才会生效
`package com.org.demo.config;
import com.alibaba.cloud.nacos.ribbon.NacosServerIntrospector;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.Server;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.netflix.ribbon.ServerIntrospector;
import org.springframework.stereotype.Component;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.stream.Collectors;
/**
-
灰度规则
-
@author dzh
-
@date 2023/1/4 8:26
*/
@Component
@ConditionalOnProperty(name = “canary.enable”, havingValue = “true”)
public class CanaryRule implements IRule {
public static final String CANARY_KEY = “canary”;
ILoadBalancer loadBalancer;
LinkedHashSet serverList = new LinkedHashSet<>();
int index = 0;
@Value(“${spring.cloud.nacos.discovery.metadata.canary}”)
String canary;
ServerIntrospector serverIntrospector = new NacosServerIntrospector();@Override
public Server choose(Object key) {
filterServer();
if (serverList.size() == 0) {
return null;
}
return serverList.stream().skip(index++ % serverList.size()).findFirst().orElse(null);
}void filterServer() {
if (serverList.size() > 1) { return; } boolean hasCanary = loadBalancer.getReachableServers().stream() .anyMatch(server -> StringUtils.equals(serverIntrospector.getMetadata(server).get(CANARY_KEY), canary)); // 默认获取没有指定虚拟环境的服务 String[] canaryCompare = new String[]{""}; if (hasCanary) { // 如果有同名指定的虚拟环境,则选择 canaryCompare[0] = canary; } List<Server> serverList = loadBalancer.getReachableServers().stream() .filter(server -> StringUtils.equals(serverIntrospector.getMetadata(server).get(CANARY_KEY), canaryCompare[0])) .collect(Collectors.toList()); // 选择结果为同名灰度环境或者非灰度环境的服务 this.serverList.addAll(serverList);
}
@Override
public void setLoadBalancer(ILoadBalancer lb) {
loadBalancer = lb;
}@Override
public ILoadBalancer getLoadBalancer() {
return loadBalancer;
}
}
`
带来问题
需要更多的资源。因为每个人都需要启动实例。其实可以在本地启动服务节省资源,启动服务也方便。
后端网关
- 服务注册。启动Canary环境实例时,需要使用新的应用名称。比如应用叫payment,虚拟环境叫xxo,注册到nacos上的时候,应用会变成payment-xxo,如图
实现逻辑:配置环境变量或者启动参数,在application.yml配置中获取,上代码
spring:
cloud:
nacos:
discovery:
service: ${spring.application.name}${CANARY:}
server-addr: server.local.cn:8848
- 路由配置
有了Canary服务以后,网关可以配置对应的路由,让指定的请求分发到制定的Canary环境。代码如下,意思是,如果Cokkie中的Canary的值是xxo的话,将请求发到payment-xxo这台实例。
routes:
- id: payment-xxo
uri: lb://payment-xxo
predicates:
- Path=/payment/**
- Cookie=Canary, xxo
存在问题
- 每个虚拟环境都有自己的路由配置,后续可能虚拟环境不用了,这个路由配置就是垃圾数据。我肯可以启动定时任务进行清理,前提是这些配置在数据库中。
- 不够智能。我们需要手动配置路由,不方便。可以结合devops,如果有Canary环境实例上线就注册路由。路由一定时间有效,可以进行续期
四、后端mq组件
待更新