赞
踩
为了解决越来越频繁的服务更新上线,版本回退,快速迭代;服务灰度发很好地解决该问题;
该系列文章主要介绍基于springcloud实现服务灰度发布,下图大致灰度发布系统各个组成部分及路由过程;
不解释
处理请求灰度打标:根据请求信息,如ip,用户id等信息,如果该请求符合灰度策略的用户,那么给该请求添加一个标识为灰度用户的请求头信息;
给服务实例打标签,服务灰度策略配置;
给服务打标是怎么样实现? 据于eureka服务实及eureka客户端实现,灰度的服务标签信息主要保存于服务实例的metadata里;通过eureka的/eureka/apps/appId/instanceId/metadata?接口设置;然后eureka客户端通过定时拉取服务实例信息,可得到服务实例里的metadata信息;
据网关处理请求头标签结果,改写路由规则;
ribbon据请求头标签信息,如何寻找对应服务?由上面可知,eureka 客户端在拉取服务信息列表时,可得服务实例里的metadata里的标签,根据该标签信息,ribbon可判断那个服务实例是正常实例,那些实例是灰度例实;由此再根据请求头灰度标签信息决定路由到那个服务实例
通过分析ribbon实例源码,发现ribbon为每个服务都会创建相应的负载均衡器及负载策略,而这些信息都在RibbonClientConfiguration去被始化,那么改写该配置类则可以修改其负载策略了;下面为改写负载策略的代码
RibbonClientGrayConfig
public class RibbonClientGrayConfig extends RibbonClientConfiguration { //具体某个服务名称 @Value("${ribbon.client.name}") private String name = "client"; //当前应用名称 @Value("${spring.application.name}") private String appName; @Autowired private PropertiesFactory propertiesFactory; @Bean @Primary @ConditionalOnMissingBean @Override public IRule ribbonRule(IClientConfig config) { if (this.propertiesFactory.isSet(IRule.class, name)) { return this.propertiesFactory.get(IRule.class, config, name); } //GrayRule自定路由策略,取代原来的策略 GrayRule rule = new GrayRule(appName,name); rule.setEurekaUrls(eurekaUrls); rule.initWithNiwsConfig(config); return rule; } //注册一个feign拦截器,处理通过feign远程调用灰度标签传递 @Bean GrayFeignInterceptor grayFeiginInterceptor() { return new GrayFeignInterceptor(); } }
GrayRule
public class GrayRule extends PredicateBasedRule { final static Logger logger = LoggerFactory.getLogger(GrayRule.class); private AbstractServerPredicate predicate = new GrayPredicate(); private RoundRobinRule roundRobinRule; private String appName; private String clientName; private String eurekaUrls; public void setEurekaUrls(String eurekaUrls) { this.eurekaUrls = eurekaUrls; } public GrayRule() { } public GrayRule(String appName, String clientName) { this.appName = appName; this.clientName = clientName; } @Override public void initWithNiwsConfig(IClientConfig clientConfig) { super.initWithNiwsConfig(clientConfig); roundRobinRule = new RoundRobinRule(); } @Override public void setLoadBalancer(ILoadBalancer lb) { super.setLoadBalancer(lb); roundRobinRule.setLoadBalancer(lb); } @Override public AbstractServerPredicate getPredicate() { return predicate; } @Override public Server choose(Object key) { boolean gray = GrayUtils.isGray(); String routeStatus = gray ? "灰度服务" : "正常服务"; logger.debug("[{}]将路由到[{}]的{}", appName, clientName, routeStatus); Server server = super.choose(gray ? ServerStatus.GRAY : ServerStatus.NORMAL); if (gray && server == null) { logger.debug("[{}]没有灰度服务实例,回退路由到正常服务", clientName); server = super.choose(ServerStatus.NORMAL); } if (server == null) { logger.error("[{}]没有{}实例,请检查服务环境当前注册中心:{}", clientName, routeStatus, eurekaUrls); } else { logger.debug("当前选择的服务[{}]:{}", clientName, server.getHostPort()); GrayUtils.setTestServer(server); } return server; } }
灰度服务判断处理
public class GrayPredicate extends AbstractServerPredicate { @Override public boolean apply(PredicateKey input) { Server server = input.getServer(); ServerInstanceStatus routTo = (ServerInstanceStatus) input.getLoadBalancerKey(); if (server instanceof DiscoveryEnabledServer) { DiscoveryEnabledServer enabledServer = (DiscoveryEnabledServer) server; InstanceInfo instanceInfo = enabledServer.getInstanceInfo(); //从metadata得到服务是否为灰度服务 String serverStatus = instanceInfo.getMetadata().get(Constant.INSTANCE_STATUS); switch (routTo) { case GRAY: return ServerInstanceStatus.GRAY.getValue().equals(serverStatus); case NORMAL: if (ServerInstanceStatus.GRAY.getValue().equals(serverStatu
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。