超简单灰度发布
基于springboot+zuul实现灰度发布
1.网关引入依赖
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>io.jmnarloch</groupId>
<artifactId>ribbon-discovery-filter-spring-cloud-starter</artifactId>
<version>2.1.0</version>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR8</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
redis是用来存储规则的,当然也可以用其他的存储中间件
2.网关配置
server.port=9999
spring.application.name=zuul
# 配置redis
spring.redis.port=6379
spring.redis.host=localhost
spring.redis.password=
eureka.client.serviceUrl.defaultZone=http://eureka:7000/eureka
3.网关的redis配置,redis的配置方式有很多种,自行选择就行了
@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport {
@Bean
RedisTemplate<String, Object> redisTemplate(
RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory);
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer =
new Jackson2JsonRedisSerializer(Object.class);
// 设置值(value)的序列化采用Jackson2JsonRedisSerializer。
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
// 设置键(key)的序列化采用StringRedisSerializer。
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
}
4.实现灰度发布逻辑,继承ZuulFilter,注意要加@Component
@Component
public class GrayFilter extends ZuulFilter {
@Autowired
private RedisTemplate redisTemplate;
@Override
public String filterType() {
return FilterConstants.ROUTE_TYPE;
}
@Override
public int filterOrder() {
return 0;
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public Object run() {
RequestContext requestContext = RequestContext.getCurrentContext();
HttpServletRequest request = requestContext.getRequest();
// 获取请求头的userId
String userId = request.getHeader("userId");
if(StringUtils.isBlank(userId)){
return null;
}
// 从缓存中获取规则
ValueOperations<String, Object> operations = redisTemplate.opsForValue();
Object version = operations.get(userId);
// 如果请求投没有加userId,则走ribbon的负载
if(version == null || StringUtils.isBlank(String.valueOf(version))){
RibbonFilterContextHolder.getCurrentContext().remove("version");
return null;
}
// 2.0表示新服务
if("2.0".equals(version.toString())){
RibbonFilterContextHolder.getCurrentContext().add("version", "2.0");
}else if("1.0".equals(version.toString())){
// 1.0表示老服务
RibbonFilterContextHolder.getCurrentContext().add("version", "1.0");
}
return null;
}
}
5.服务端的依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
6.服务端的配置
老服务的配置
server.port=9100
spring.application.name=service
eureka.client.serviceUrl.defaultZone=http://eureka:7000/eureka
eureka.client.register-with-eureka=true
eureka.client.fetch-registry=true
# 这个就是需要配置的信息,我这里1.0表示老服务
eureka.instance.metadata-map.version=1.0
新服务的配置
server.port=9101
spring.application.name=service
eureka.client.serviceUrl.defaultZone=http://eureka:7000/eureka
eureka.client.register-with-eureka=true
eureka.client.fetch-registry=true
# 这里2.0表示新服务
eureka.instance.metadata-map.version=2.0
7.准备规则
在redis中添加规则
set zhangsan "2.0"
set wangwu "1.0"
zhangsan 只能访问新服务
wangwu 只能访问老服务
8.启动redis,注册中心,新老服务,网关
9.测试
访问:http://localhost:9999/service/gray/test ,这是通过网关访问服务,请求头中添加userId:wangwu
userId设置成lisi多请求几次会发现网关通过ribbon负载访问服务