灰度发布是指在系统迭代的时候一种平滑过度上线发布方式。灰度发布是在原有的系统的基础上面,额外增加一个新版本,这个新版本包含新上线的需要验证的功能,通过负载均衡引入部分流量到新版本的应用上,如果在这个过程中没有出现问题,便可以平滑地把线上的应用一步步替换成新的版本,这样就完成了一次灰度发布。通过灰度发布的方式可以在用户无感的情况下完成系统发版升级。
这里使用一个开源的实现:ribbon-discovery-filter-spring-cloud-starter
在Eureka中有两种metadata:
eureka.instance.metadata-map.=
来配置,内部就是一个map来保存的。可以配置在远程的服务,也可以随着服务的注册保存到Eureka注册表中。基于Eureka的元数据实现完成灰度发布的原理是:通过获取Eureka的元数据信息,根据元数据信息的识别,最后在路由规则进行负载均衡。
依赖:
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-actuatorartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
<exclusions>
<exclusion>
<artifactId>spring-boot-starter-tomcatartifactId>
<groupId>org.springframework.bootgroupId>
exclusion>
exclusions>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-undertowartifactId>
dependency>
<dependency>
<groupId>io.undertowgroupId>
<artifactId>undertow-servletartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
dependency>
dependencies>
<repositories>
<repository>
<id>spring-milestonesid>
<name>Spring Milestonesname>
<url>https://repo.spring.io/libs-milestoneurl>
<snapshots>
<enabled>falseenabled>
snapshots>
repository>
repositories>
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
plugin>
plugins>
build>
配置文件:这里通过配置文件配置三份配置,-1随机端口,node1和node2是v1版本,node3是v2版本。
server:
port: 8877
spring:
profiles: node1
application:
name: usercenter
eureka:
client:
serviceUrl:
defaultZone: http://${eureka.host:127.0.0.1}:${eureka.port:8671}/eureka/
instance:
prefer-ip-address: true
metadata-map:
version: release
---
server:
port: 8899
spring:
profiles: node2
application:
name: usercenter
eureka:
client:
serviceUrl:
defaultZone: http://${eureka.host:127.0.0.1}:${eureka.port:8671}/eureka/
instance:
prefer-ip-address: true
metadata-map:
version: release
---
server:
port: 8866
spring:
profiles: node3
application:
name: usercenter
eureka:
client:
serviceUrl:
defaultZone: http://${eureka.host:127.0.0.1}:${eureka.port:8671}/eureka/
instance:
prefer-ip-address: true
metadata-map:
version: gray
Controller:
@RestController
@RequestMapping("user")
public class UsercenterController {
@Value("${spring.profiles}")
private String profile;
@Value("${server.port}")
private Integer port;
@GetMapping("list")
public String list() {
return "active " + profile + " port " + port;
}
}
启动类:
@SpringBootApplication
@EnableDiscoveryClient
public class UsercenterApplication {
public static void main(String[] args) {
SpringApplication.run(UsercenterApplication.class, args);
}
}
配置文件:
server:
port: 88
spring:
application:
name: zuul-server
eureka:
client:
serviceUrl:
defaultZone: http://${eureka.host:127.0.0.1}:${eureka.port:8671}/eureka/
instance:
prefer-ip-address: true
logging:
level:
org.springframework.cloud.netflix: debug
zuul:
routes:
service-a:
path: /usercenter/**
serviceId: usercenter
依赖:
<dependency>
<groupId>io.jmnarlochgroupId>
<artifactId>ribbon-discovery-filter-spring-cloud-starterartifactId>
<version>2.1.0version>
dependency>
编写灰度发布过滤器:当携带请求gray_switch
并且值为open
时就会去请求灰度版本的应用。否则就是请求release
版本的应用。
现实中我们可以通过使用Nginx,然后将部分流量添加请求头,这样就可以切一部分的流量去做灰度了。
@Component
public class GrayFilter extends ZuulFilter {
@Override
public String filterType() {
return FilterConstants.PRE_TYPE;
}
@Override
public int filterOrder() {
return FilterConstants.PRE_DECORATION_FILTER_ORDER - 1;
}
@Override
public boolean shouldFilter() {
RequestContext ctx = RequestContext.getCurrentContext();
return !ctx.containsKey(FORWARD_TO_KEY) && !ctx.containsKey(SERVICE_ID_KEY);
}
@Override
public Object run() throws ZuulException {
HttpServletRequest request = RequestContext.getCurrentContext().getRequest();
String mark = request.getHeader("gray_switch");
if (StringUtils.isNotBlank(mark) && "open".equals(mark)) {
RibbonFilterContextHolder.getCurrentContext().add("version", "gray");
} else {
RibbonFilterContextHolder.getCurrentContext().add("version", "release");
}
return null;
}
}
分别启动Eureka、Zuul和UsercenterApplication。
在启动UsercenterApplication的使用指定active profile
:
例如通过IDEA启动时修改配置Active profiles
启动给三个分别设置 node1、node2、node3。
在UsercenterApplication所在的pomx文件下,mvn运行
mvn spring-boot:run -Dspring-boot.run.profiles=node1
mvn spring-boot:run -Dspring-boot.run.profiles=node2
mvn spring-boot:run -Dspring-boot.run.profiles=node3
访问接口时
http://localhost:88/usercenter/user/list
可以关注我的微信公众号,有更多的技术干货文章