当前位置: 首页>編程日記>正文

灰度发布--Spring Cloud Gray

灰度发布--Spring Cloud Gray

特性

  • 支持灰度调用
  • 通过feign,restTemplate(通过注解@LoadBalanced 基于ribbon实现负载均衡)调用,支持灰度追踪
  • 支持自动注册为灰度服务,默认不自动注册
  • 优先走灰度服务,其次走正常服务
  • 支持修改服务状态,以此实现破窗能力
  • 通过破窗能力,实现蓝绿发布
  • 其它待补充

介绍

设计思想见
Spring Cloud Gray - 微服务灰度中间件

结构划分

  • spring-cloud-gray-client
    定义了一套灰度路由决策模型,灰度信息追踪模型,以及和spring-cloud-gray-server的基本通信功能。

  • spring-cloud-gray-client-netflix
    在spring-cloud-gray-client的基础上集成了微服务注册中心eureka,扩展ribbon的负载均衡规则,提供了对zuul,feign,RestTemplate的灰度路由能力,并且无缝支持hystrix线程池隔离。

  • spring-cloud-gray-server 管控端
    负责灰度决策、灰度追踪等信息的管理以及持久化。

  • spring-cloud-gray-webui 管控端
    提供操作界面。

GrayServer 部署、配置、使用

部署操作界面web-ui

部署后端服务

  • 添加pom依赖
<dependency><groupId>cn.springcloud.gray</groupId><artifactId>spring-cloud-starter-gray-server</artifactId><version>A.1.1.2</version>
</dependency>
  • application.yaml
    只需修改spring.datasource、eureka-default-Zone即可
server:port: 20202
spring:main:allow-bean-definition-overriding: trueapplication:name: gray-Server#通用数据源配置datasource:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://116.196.83.187:65525/gray_server?charset=utf8mb4&useSSL=falseusername: rootpassword: Zhulong123# Hikari 数据源专用配置hikari:maximum-pool-size: 20minimum-idle: 5# JPA 相关配置jpa:open-in-view: falsedatabase-platform: org.hibernate.dialect.MySQL5InnoDBDialectshow-sql: truegenerate-ddl: truehibernate:ddl-auto: update
eureka:client:register-with-eureka: truefetch-registry: trueserviceUrl:defaultZone: http://localhost:20001/eureka/registry-fetch-interval-seconds: 5gray:server:discovery:evictionEnabled: trueevictionIntervalTimerInMs: 60000instance:#正常的实例状态,默认为STARTING, UP  normalInstanceStatus: STARTING,UPeviction:enabled: trueevictionIntervalTimerInMs: 86400000evictionInstanceStatus: DOWN,UNKNOWNlastUpdateDateExpireDays: 1
  • 创建Application类(注解 @EnableGrayServer)
  • 初始化信息
--- 添加一个用户名为admin,密码是abc123的用户
insert into `user` ( `user_id`, `account`, `name`, `password`, `roles`, `status`, `create_time`, `operator`, `operate_time`) values ( 'admin', 'admin', 'Admin', 'e7a57e51394e91cba19deca3337bfab0', 'admin', '1', now(), 'admin', now());
--- 添加默认的namespace
INSERT INTO `gray_server`.`namespace`(`code`, `create_time`, `creator`, `del_flag`, `name`) VALUES ('default', '2021-07-19 09:55:19', 'admin', b'0', 'default');
--- 为namespace配置权限
INSERT INTO `gray_server`.`default_namespace`(`user_id`, `ns_code`) VALUES ('admin', 'default');
--- 为用户设置资源权限
INSERT INTO `gray_server`.`user_resource_authority`(`id`, `authority_flag`, `del_flag`, `operate_time`, `operator`, `resource`, `resource_id`, `user_id`) VALUES (1, 9, b'0', '2021-07-19 09:55:19', 'admin', 'namespace', 'default', 'admin');

服务端配置

用户管理

在这里插入图片描述

配置namespace

可以通过初始化信息录入,默认为default;
在这里插入图片描述

配置策略

在这里插入图片描述

  • 定义灰度策略
  • 为灰度策略设置决策(track开头的一般为灰度追踪),可以同时设置多个决策

注意:决策是灰度中进行比对的最小项。它定义一种规则,对请求进行比对,返回 true/false。当请求调用时,灰度调用端可以根据灰度实例的灰度决策,进行对比,以判断灰度实例是否可以受理该请求。多个决策是"与"的关系。

配置灰度服务

定义服务owner

在这里插入图片描述
权限控制是以服务为对象的,拥有服务的权限,就可以操作服务的所有灰度信息。
(service_owner、user_service_authority)

在服务的权限控制中,分为两种角色,owner和管理者,owner拥有最大的权限,管理者除了不能删除owner的权限,其它权限同owner一样。(注意:必须先有owner,service列表才能查询出来;然后再由owner分配管理者)

维护服务列表

在这里插入图片描述

  1. 配置服务灰度,关联策略,可以关联多个;
    只要任一策略满足要求即可;
    在这里插入图片描述
  2. 配置多版本灰度

在这里插入图片描述
version应当与eureka.instance.metadata-map中的version相对应,如

eureka:instance:metadata-map:version: v3instanceId: s
  1. 维护灰度实例
  • 点击实例,在实例列表中维护;
  • 选择实例,点击策略,维护实例与策略间的关联关系;

注意:
灰度状态是用来控制实例是被灰度,当灰度状态打开时,只有匹配该实例的任意灰度策略的请求,才会被转到到该实例上。
灰度实例支持自动注册到服务端,默认不自动注册;
一个实例可以有多个灰度策略,策略与策略之间是"或"的关系。就是说,一个请求只要 满足实例的任意一个灰度策略,这个请求被路由到该实例上。

  1. 维护灰度追踪
    灰度追踪分为两部分,这里配置的是透传项,对比部分在灰度策略

设置灰度追踪的目的是为了将用户请求的最初的信息透传到服务链,比如version参数能够从网关一直透传到后面的服务中。
弹出添加面板,输入追踪类型(Name)和追踪字段(Infos)。
Name: HttpParameter Infos: version
Infos 可以追踪多个字段,多个字段用逗号(,)分隔

注意
在服务灰度、灰度实例修改决策时是全局的,会影响到该决策的所有服务、实例;
对灰度实例、服务灰度、灰度策略的修改,请通过服务端去处理,而不要直接通过数据库去修改。因为客户端会将数据缓存到内存中(concurrenthashmap及caffine中),在服务端修改后会发送事件,而客户端会通过定时任务访问服务端的事件日志(gray_event_log)来更新本地缓存。

客户端配置、使用

eureka

<dependency><artifactId>spring-cloud-gray-utils</artifactId><groupId>cn.springcloud.gray</groupId><version>${project.version}</version>
</dependency>

gateway

<dependency><groupId>cn.springcloud.gray</groupId><artifactId>spring-cloud-starter-gray-client</artifactId><version>D.0.0.2</version><exclusions><exclusion><artifactId>spring-cloud-gray-plugin-webmvc</artifactId><groupId>cn.springcloud.gray</groupId></exclusion>
<!--                <exclusion>-->
<!--                    <groupId>cn.springcloud.gray</groupId>-->
<!--                    <artifactId>spring-cloud-gray-plugin-eureka</artifactId>-->
<!--                </exclusion>--></exclusions>
</dependency><dependency><groupId>cn.springcloud.gray</groupId><artifactId>spring-cloud-gray-plugin-gateway</artifactId><version>D.0.0.2</version>
</dependency>

client

<dependency><groupId>cn.springcloud.gray</groupId><artifactId>spring-cloud-starter-gray-client</artifactId><version>D.0.0.2</version>
</dependency>
<dependency><groupId>cn.springcloud.gray</groupId><artifactId>spring-cloud-gray-plugin-feign</artifactId><version>D.0.0.2</version>
</dependency>

注意:灰度追踪配置,同时支持在gray-server及在client的yaml中配置,优先以gray-server配置为准

表结构

在这里插入图片描述

流程浅析

服务选择过程

  1. 配置eureka自定义metadata
eureka:instance:metadata-map:version: v3#      zone: gray3instanceId: s
#    initial-status: starting
  1. 获取eureka.metadata
org.springframework.cloud.netflix.ribbon.eureka.EurekaServerIntrospector#getMetadata
  1. 将所有实例划分为灰度服务、正常服务
    3.1. 先做服务级别的拆分
    1)、只有在服务级别配置服务灰度或多版本灰度,才会走服务级别的拆分
    2)、如果配置了服务灰度,则实例需要满足灰度策略(filterServiceGrayPolicies判断),才会是有效的,可以被继续筛选
    3)、如果配置了服务灰度,但没有配置多版本灰度,则grayServer为空,2)筛选后的列表都会是正常实例;
    4)、如果配置了多版本灰度,则eureka.instance.metadata-map中的version与多版本灰度的version一致,才能被列为灰度实例;
    5)、根据多版本灰度中关联的灰度策略来判断,实例是否是有效的,可以被继续筛选;
    6)、如果灰度策略没有配置灰度决策,那么任一灰度都可以判断通过;
cn.springcloud.gray.choose.ServiceGrayServerSorter#distinguishServerSpecList
@Override
protected ServerListResult<ServerSpec<SERVER>> distinguishServerSpecList(String serviceId, List<ServerSpec<SERVER>> serverSpecs) {List<ServerSpec<SERVER>> serverSpecList = serverSpecs;GrayManager grayManager = getGrayManager();GrayService grayService = grayManager.getGrayService(serviceId);if (Objects.nonNull(grayService) && !grayService.getRoutePolicies().isEmpty()) {serverSpecList = filterServiceGrayPolicies(grayService.getRoutePolicies().getDatas(), serverSpecs);}Collection<String> multiVersions = getMultiVersions(grayService);if (CollectionUtils.isEmpty(multiVersions)) {return new ServerListResult<>(serviceId, Collections.EMPTY_LIST, serverSpecList);}List<ServerSpec<SERVER>> grayServerSpecs = new ArrayList<>(serverSpecList.size());List<ServerSpec<SERVER>> normalServerSpecs = new ArrayList<>(serverSpecList.size());serverSpecList.forEach(serverSpec -> {if (StringUtils.isNotEmpty(serverSpec.getVersion()) && multiVersions.contains(serverSpec.getVersion())) {grayServerSpecs.add(serverSpec);} else {normalServerSpecs.add(serverSpec);}});return new ServerListResult<>(serviceId, grayServerSpecs, normalServerSpecs);
}

策略

一个实例可以有多个灰度策略,策略与策略之间是"或"的关系。就是说,一个请求只要 满足实例的任意一个灰度策略,这个请求被路由到该实例上。
cn.springcloud.gray.choose.AbstractPolicyPredicate#testPolicies

@Override
public boolean testPolicies(Collection<Policy> policies, DecisionInputArgs decisionInputArgs) {if (Objects.isNull(policies) || policies.size() < 1) {return false;}for (Policy policy : policies) {if (policy.predicateDecisions(decisionInputArgs)) {return true;}}return false;
}

决策

决策是灰度中进行比对的最小项。它定义一种规则,对请求进行比对,返回 true/false。当请求调用时,灰度调用端可以根据灰度实例的灰度决策,进行对比,以判断灰度实例是否可以受理该请求。多个决策是"与"的关系。

public boolean predicateDecisions(DecisionInputArgs args) {for (GrayDecision decision : decisions) {if (!decision.test(args) ) {return false;}}return true;
}
5.根据多版本灰度中关联的灰度策略来判断
cn.springcloud.gray.choose.ServiceGrayServerSorter#filterServerSpecAccordingToRoutePolicy(java.lang.String, java.util.List<cn.springcloud.gray.servernode.ServerSpec<SERVER>>)cn.springcloud.gray.choose.ServiceGrayServerSorter#filterServerSpecAccordingToRoutePolicy(cn.springcloud.gray.model.GrayService, cn.springcloud.gray.choose.PolicyPredicate, java.util.List<cn.springcloud.gray.servernode.ServerSpec<SERVER>>)//断言service 多版本灰度策略,并过滤不匹配的server返回。
private List<ServerSpec<SERVER>> filterServerSpecAccordingToRoutePolicy(GrayService grayService, PolicyPredicate policyPredicate, List<ServerSpec<SERVER>> serverSpecs) {Map<String, DataSet<String>> multiVersionRoutePoliciesMap = grayService.getMultiVersionRotePolicies();Map<String, List<Policy>> multiVersionPolicies = new HashMap<>();return serverSpecs.stream().filter(serverSpec -> {String version = serverSpec.getVersion();List<Policy> policies = multiVersionPolicies.get(version);if (Objects.isNull(policies)) {policies = Collections.EMPTY_LIST;DataSet<String> routePolicies = multiVersionRoutePoliciesMap.get(version);if (Objects.nonNull(routePolicies)) {policies = policyDecisionManager.getPolicies(routePolicies.getDatas());}multiVersionPolicies.put(version, policies);}return !CollectionUtils.isEmpty(policies) && policyPredicate.testPolicies(policies, createDecisionInputArgs(serverSpec));}).collect(Collectors.toList());
}

3.2. 再做实例级别的拆分
1)、先根据配置区分灰度实例、正常实例
2)、再判断灰度实例是否符合策略(filterServerSpecAccordingToRoutePolicy)

cn.springcloud.gray.choose.InstanceGrayServerSorter#distinguishServerSpecList
  1. 在客户端根据服务列表选择服务实例去调用
    chooseServer::轮询(无论是正常服务,灰度服务,或是所有服务)
cn.springcloud.gray.choose.DefaultServerChooser#chooseServer
@Override
public Object chooseServer(List<Object> servers, ListChooser<Object> chooser) {if (!graySwitcher.state()) {log.debug("灰度未开启,从servers列表挑选");return chooser.choose(ChooseGroup.ALL, servers);}String serviceId = serverIdExtractor.getServiceId(servers);List<Object> serverList = serverListProcessor.process(serviceId, servers);if (!grayManager.hasServiceGray(serviceId)) {log.debug("{} 服务没有相关灰度策略, 从serverList列表进行灰度策选, serverList.size={}", serviceId, serverList.size());return chooseInstanceServer(serverList, chooser);}List<ServerSpec<Object>> serverSpecs = serverExplainer.apply(servers);//区分灰度实例和正常实例,并比对灰度实例的灰度决策ServerListResult<ServerSpec<Object>> serviceServerSpecListResult =serviceGrayServerSorter.distinguishAndMatchGrayServerSpecList(serverSpecs);if (Objects.isNull(serviceServerSpecListResult)) {log.debug("区分 {} 服务灰度列表和正常列表失败, 从serverList列表进行灰度策选, serverList.size={}", serviceId, serverList.size());return chooseInstanceServer(serverList, chooser);}return chooseServiceSpecServer(serviceServerSpecListResult, chooser);
}如果灰度服务不满足要求,则走正常服务
protected Object chooseServiceSpecServer(ServerListResult<ServerSpec<Object>> serviceServerSpecListResult, ListChooser<Object> chooser) {Object server = null;if (GrayClientHolder.getGraySwitcher().isEanbleGrayRouting()) {server = chooseInstanceSpecServer(serviceServerSpecListResult.getGrayServers(), chooser);log.debug("从{}服务的灰度实例列表中挑选到 {}", serviceServerSpecListResult.getServiceId(), server);}if (Objects.isNull(server)) {server = chooseInstanceSpecServer(serviceServerSpecListResult.getNormalServers(), chooser);log.debug("从{}服务的正常实例列表中挑选到 {}", serviceServerSpecListResult.getServiceId(), server);}return server;
}protected Object chooseInstanceServer(List<Object> servers, ListChooser<Object> chooser) {ServerListResult<Object> serverListResult = instanceGrayServerSorter.distinguishAndMatchGrayServerList(servers);if (serverListResult == null) {return chooser.choose(ChooseGroup.ALL, servers);}if (GrayClientHolder.getGraySwitcher().isEanbleGrayRouting()&& CollectionUtils.isNotEmpty(serverListResult.getGrayServers())) {Object server = chooser.choose(ChooseGroup.GRAY, serverListResult.getGrayServers());if (server != null) {return server;}}return chooser.choose(ChooseGroup.NORMAL, serverListResult.getNormalServers());
}

chooseServer::轮询(无论是正常服务,灰度服务,或是所有服务)
并不直接使用RoundRobinRule
而是基于ClientConfigEnabledRoundRobinRule的抽象类PredicateBasedRule;在choose方法中,通过AbstractServerPredicate的chooseRoundRobinAfterFiltering函数来选择具体的服务实例。(支持轮询或随机)
cn.springcloud.gray.client.netflix.ribbon.GrayChooserRule#choose

public Server choose(Object key) {try {return serverChooser.chooseServer(getLoadBalancer().getAllServers(), (group, servers) -> {Optional<Server> server = getPredicate().chooseRoundRobinAfterFiltering(servers, key);if (server.isPresent()) {return server.get();} else {return null;}});} catch (Exception e) {log.warn("gray choose server occur exception:{}, execute super method.", e.getMessage(), e);return super.choose(key);}
}

灰度追踪

首先,服务拆分是相同的
其次,灰度追踪采用的策略是哪种呢??
举个例子,以httpHeader为例,满足策略的不是httpHeaderPolicy,而是httpTrackHeadPolicy。

通过网关请求service-b,service-b通过feign访问service-a
localhost:20401/ser-b/api/test/feignGet
即service-b配置灰度追踪,service-a 灰度实例上配置灰度策略为httpTrackHeader

HttpTrackHeaderGrayDecisionFactory : 
[HttpTrackHeaderGrayDecision] serviceId:service-a, uri:http://service-a/api/test/get 没有获取到灰度追踪信息, testReslut:false

在这里插入图片描述
在这里插入图片描述

事件监听

gray:enabled: trueserver:url: http://gray-Serverloadbalanced: trueretryable: trueretryNumberOfRetries: 3

cn.springcloud.gray.refresh.GrayInformationRefresher#publishRefreshedEvent
启动完成后,会发布jvm级别的一个事件(cn.springcloud.gray.refresh.GrayRefreshedEvent),让服务准备去监听跨服务的事件cn.springcloud.gray.client.plugin.event.longpolling.GrayRefreshedSortMarkListener#onApplicationEvent(GrayRefreshedEvent event)启动cn.springcloud.gray.client.plugin.event.longpolling.LongPollingWorker#LongPollingWorkerexecutor = Executors.newScheduledThreadPool(1,new DefaultThreadFactory("gray.client.event.longPolling"));executorService = Executors.newScheduledThreadPool(Runtime.getRuntime().availableProcessors(),new DefaultThreadFactory("gray.client.event.retrieveResult.process"));启动监听cn.springcloud.gray.client.plugin.event.longpolling.LongPollingWorker#listenEvents监听gray-server配置的事件(即日志)
cn.springcloud.gray.client.plugin.event.longpolling.GrayEventRemoteClient#listeningNewestStatus


https://www.fengoutiyan.com/post/16257.html

相关文章:

  • docker灰度发布
  • zuul灰度发布功能实现
  • 灰度发布
  • k8s灰度发布方案
  • 灰度发布是什么意思
  • 灰度发布的标准
  • nacos 灰度发布
  • 项目灰度发布
  • 鏡像模式如何設置在哪,圖片鏡像操作
  • 什么軟件可以把圖片鏡像翻轉,C#圖片處理 解決左右鏡像相反(旋轉圖片)
  • 手機照片鏡像翻轉,C#圖像鏡像
  • 視頻鏡像翻轉軟件,python圖片鏡像翻轉_python中鏡像實現方法
  • 什么軟件可以把圖片鏡像翻轉,利用PS實現圖片的鏡像處理
  • 照片鏡像翻轉app,java實現圖片鏡像翻轉
  • 什么軟件可以把圖片鏡像翻轉,python圖片鏡像翻轉_python圖像處理之鏡像實現方法
  • matlab下載,matlab如何鏡像處理圖片,matlab實現圖像鏡像
  • 圖片鏡像翻轉,MATLAB:鏡像圖片
  • 鏡像翻轉圖片的軟件,圖像處理:實現圖片鏡像(基于python)
  • canvas可畫,JavaScript - canvas - 鏡像圖片
  • 圖片鏡像翻轉,UGUI優化:使用鏡像圖片
  • Codeforces,CodeForces 1253C
  • MySQL下載安裝,Mysql ERROR: 1253 解決方法
  • 勝利大逃亡英雄逃亡方案,HDU - 1253 勝利大逃亡 BFS
  • 大一c語言期末考試試題及答案匯總,電大計算機C語言1253,1253《C語言程序設計》電大期末精彩試題及其問題詳解
  • lu求解線性方程組,P1253 [yLOI2018] 扶蘇的問題 (線段樹)
  • c語言程序設計基礎題庫,1253號C語言程序設計試題,2016年1月試卷號1253C語言程序設計A.pdf
  • 信奧賽一本通官網,【信奧賽一本通】1253:抓住那頭牛(詳細代碼)
  • c語言程序設計1253,1253c語言程序設計a(2010年1月)
  • 勝利大逃亡英雄逃亡方案,BFS——1253 勝利大逃亡
  • 直流電壓測量模塊,IM1253B交直流電能計量模塊(艾銳達光電)
  • c語言程序設計第三版課后答案,【渝粵題庫】國家開放大學2021春1253C語言程序設計答案
  • 18轉換為二進制,1253. 將數字轉換為16進制
  • light-emitting diode,LightOJ-1253 Misere Nim
  • masterroyale魔改版,1253 Dungeon Master
  • codeformer官網中文版,codeforces.1253 B
  • c語言程序設計考研真題及答案,2020C語言程序設計1253,1253計算機科學與技術專業C語言程序設計A科目2020年09月國家開 放大學(中央廣播電視大學)
  • c語言程序設計基礎題庫,1253本科2016c語言程序設計試題,1253電大《C語言程序設計A》試題和答案200901
  • 肇事逃逸車輛無法聯系到車主怎么辦,1253尋找肇事司機