背景:使用ribbon完成 服务->服务 的灰度发布
思路:不同的用户根据ribbon的rule规则匹配到不同的服务
服务调用者api-passenger
pom.xml配置
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.2.7.RELEASE</version> <relativePath/> </parent> <groupId>com.dandan</groupId> <artifactId>api-passenger</artifactId> <version>0.0.1-SNAPSHOT</version> <name>api-passenger</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> <spring-cloud.version>Hoxton.SR4</spring-cloud.version> </properties> <dependencies> <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> <!-- 实现通过 metadata 进行灰度路由 --> <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>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
application.yml跟上文一致
启动类ApiPassengerApplication
package com.dandan.apipassenger; import com.dandan.apipassenger.gray.GrayRibbonConfiguration; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.cloud.netflix.ribbon.RibbonClient; import org.springframework.context.annotation.Bean; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.client.RestTemplate; @SpringBootApplication public class ApiPassengerApplication { public static void main(String[] args) { SpringApplication.run(ApiPassengerApplication.class, args); } @LoadBalanced @Bean public RestTemplate restTemplate(){ return new RestTemplate(); } }
controller请求类
package com.dandan.apipassenger.controller; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; @RestController @RequestMapping("/test") public class TestCallServiceSmsController { @Autowired private RestTemplate restTemplate; @GetMapping("/call") public String testCall(){ return restTemplate.getForObject("http://service-sms/test/sms-test",String.class); } }
AOP拦截器类 RequestAspect
package com.dandan.apipassenger.gray; import io.jmnarloch.spring.cloud.ribbon.support.RibbonFilterContextHolder; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; import org.springframework.stereotype.Component; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import javax.servlet.http.HttpServletRequest; /** * ribbon的灰度发布简单方式 */ @Aspect @Component public class RequestAspect_2 { @Pointcut("execution(* com.dandan.apipassenger.controller..*Controller*.*(..))") private void anyMehtod(){ } @Before(value = "anyMehtod()") public void before(JoinPoint joinPoint){ HttpServletRequest request = ((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest(); String version = request.getHeader("version"); //灰度规则匹配的地方,查db,redis... if(version.trim().equals("v1")) { RibbonFilterContextHolder.getCurrentContext().add("version", "v1"); } else if(version.trim().equals("v2")) { RibbonFilterContextHolder.getCurrentContext().add("version", "v2"); } } }
service-sms和cloud-eureka配置跟 使用网关zuul完成灰度发布 一致
配置完成后启动
cloud-eureka
api-passenger
service-sms(开启两个服务)
访问
GET localhost:8080/test/call
header传参:{version:v1}
对应找到service-sms:8091
GET localhost:8080/test/call
header传参:{version:v2}
对应找到service-sms:8092