1.nacos 服务注册与发现
2.本地负载均衡器算法
3.gateway 网关
4.ThreadLocal
1.什么是灰度发布?
2.什么是灰度策略?
3.灰度发布落地方案有哪些
4.灰度发布架构设计原理 nginx+lua?
5.如何基于GateWay+Nacos构建灰度环境
6.GateWay负载均衡路由算法原理------改写
7.如何重写本地负载均衡器,走灰度环境
8.为何不基于nginx+lua实现?而使用GateWay
9.代码落地实战:构建微服务灰度发布环境
接口地址----服务治理框架
灰度发布(又名金丝雀发布)是指在黑与白之间,能够平滑过渡的一种发布方式。在其上可以进行A/B testing,即让一部分用户继续用产品特性A,一部分用户开始用产品特性B,如果用户对B没有什么反对意见,那么逐步扩大范围,把所有用户都迁移到B上面来。灰度发布可以保证整体系统的稳定,在初始灰度的时候就可以发现、调整问题,以保证其影响度。
灰度期:灰度发布开始到结束期间的这一段时间,称为灰度期。
灰度发布能及早获得用户的意见反馈,完善产品功能,提升产品质量,让用户参与产品测试,加强与用户互动,降低产品升级所影响的用户范围。
整合服务注册中心
1.nginx+lua+nacos 实现(需要懂lua脚本);—
2.gateway+nacos+重写本地负载均衡器 (java实现)—java程序员推荐 性能没有直接
通过nginx实现效率高;
1.服务注册时,需要指定注册到nacos元数据 自定义版本号码:
例如:正式环境 version =1.0 灰度环境 version=2.0
2.配置文件中需要整合
server:
port: 7053
spring:
application:
name: mayikt-member #服务名称 在 注册中心展示服务名称 --
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848 # nacos服务注册中心Server端 地址
metadata:
version: 2.0 # 指定 元数据版本号码
构建微服务网关
访问http://127.0.0.1:81/mayikt-member/member 路由转发到
会员服务。
配置文件内容:
spring:
application:
###服务的名称
name: mayikt-gateway
cloud:
nacos:
discovery:
###nacos注册地址
server-addr: 127.0.0.1:8848
config:
server-addr: 127.0.0.1:8848
file-extension: yml
gateway:
routes:
- id: mayikt-member
uri: lb://mayikt-member
filters:
- StripPrefix=1
predicates:
- Path=/mayikt-member/**
maven依赖:
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
<version>2.0.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<version>0.2.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
<version>0.2.2.RELEASE</version>
</dependency>
</dependencies>
import com.mayikt.gateway.manage.GrayscaleThreadLocalEnvironment;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.util.List;
/**
* @author 余胜军
* @ClassName GrayscaleGateway
* @qq 644064779
* @addres www.mayikt.com
* 微信:yushengjun644
*/
@Component
@RefreshScope
public class GrayscaleGlobalFilter implements GlobalFilter, Ordered {
@Value("${mayikt.gateway.grayscaleUserConfig}")
private String[] grayscaleUserConfig;
@Value("${mayikt.gateway.grayscale.version}")
private String grayscaleVersion;
@Value("${mayikt.gateway.formal.version}")
private String formalVersion;
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 1.获取到ServerHttpRequest
ServerHttpRequest request = exchange.getRequest();
ServerHttpResponse response = exchange.getResponse();
// 2.判断是否是灰度用户 根据参数判断
List<String> mayiktParameGrayscales = request.getHeaders().get("mayiktParameGrayscale");
if (mayiktParameGrayscales != null && mayiktParameGrayscales.size() > 0) {
// 3.如果是灰度用户
grayscale(mayiktParameGrayscales);
} else {
// 设置当前环境为正式环境
GrayscaleThreadLocalEnvironment.setCurrentEnvironment(formalVersion);
}
return chain.filter(exchange.mutate().request(request).build());
}
/**
* 灰度流程
*/
private void grayscale(List<String> mayiktParameGrayscales) {
String mayiktParameGrayscale = mayiktParameGrayscales.get(0);
if (StringUtils.isEmpty(mayiktParameGrayscale)) {
return;
}
for (String userConfig :
grayscaleUserConfig) {
if (userConfig.equals(mayiktParameGrayscale)) {
// 设置当前用户灰度的环境
GrayscaleThreadLocalEnvironment.setCurrentEnvironment(grayscaleVersion);
return;
}
}
// 设置当前环境为正式环境
GrayscaleThreadLocalEnvironment.setCurrentEnvironment(formalVersion);
}
public int getOrder() {
return -1;
}
}
/**
* @author 余胜军
* @ClassName Grayscale
* @qq 644064779
* @addres www.mayikt.com
* 微信:yushengjun644
*/
public class GrayscaleThreadLocalEnvironment {
private static ThreadLocal<String> threadLocal = new ThreadLocal<String>();
/**
* 设置当前线程对应的版本
*
* @param currentEnvironmentVsersion
*/
public static void setCurrentEnvironment(String currentEnvironmentVsersion) {
threadLocal.set(currentEnvironmentVsersion);
}
/**
* 获取当前环境配置
*
* @return
*/
public static String getCurrentEnvironment() {
return threadLocal.get();
}
}
package com.mayikt.gateway.config;
import com.mayikt.gateway.manage.GrayscaleThreadLocalEnvironment;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.RoundRobinRule;
import com.netflix.loadbalancer.Server;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.alibaba.nacos.ribbon.NacosServer;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
/**
* @author 余胜军
* @ClassName MayiktRoundRobinRule
* @qq 644064779
* @addres www.mayikt.com
* 微信:yushengjun644
*/
@Component
@Slf4j
public class MayiktRoundRobinRule extends RoundRobinRule {
private AtomicInteger nextServerCyclicCounter;
private static final boolean AVAILABLE_ONLY_SERVERS = true;
private static final boolean ALL_SERVERS = false;
public MayiktRoundRobinRule() {
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;
while (server == null && 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 :
allServers) {
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) {
/* Transient. */
Thread.yield();
continue;
}
if (server.isAlive() && (server.isReadyToServe())) {
return (server);
}
// Next.
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;
}
}
}
http://127.0.0.1:81/mayikt-member/member 请求设定参数 mayiktParameGrayscale=mayikt
测走灰度用户。
正常流程:
1.先走过滤器---- 判断该用户是否 是为灰度用户
是如果为灰度用户的话 设定当前线程版本号码=2.0
----- 最优先加载的
2.本地路由负载均衡算法— 获取当前线程
对应的版本号码=2.0 在从nacos上获取2.0 版本接口地址 本地实现
负载均衡。