概述
通过配置进行服务切换, 如果入参和出参一致, 那么代码几乎不用改动; 示例使用用户服务为例, 通过代理类进行切换qq或微信的用户服务
CODE
代理类UserServiceProxy( 入参出参通用 )
public interface UserServiceProxy {
List<User> listUser(String name);
}
代理继承类
这里的入参出参尽量保持一致, 当然不一样也可以通过implements来特定实现; 下面的示例为了有所区分, url的地址不同
public interface QQ extends UserServiceProxy{
@GetMapping("/openapi/v2/user/list")
List<User> listUser(@RequestParam String name);
}
public interface Wechat extends UserServiceProxy{
@GetMapping("/openapi/v1/user/listActive")
List<User> listUser(@RequestParam String name);
}
代理配置类ProxyConfiguration
因为@Condition
类的注解和@FeinClient
无法结合使用(会导致注入失败) , 所以需要自行管理FeignClient的bean
- 高版本的Feign(3.x+)可以使用FeignClientBuilder的customize方法, 可以指定加载feign的配置信息; 不需要自己做服务的负载均衡, taget中已经做了对应的实现
- 低版本的需要使用其他的方式实现, 低版本的没有customize, 同时FeignClientFactoryBean是包内可见的
高版本(3.x+)
@Component
public class ProxyConfiguration {
@Value("${user.service.qq.enable:false}")
boolean enableQqService;
@Autowired
ObjectFactory<HttpMessageConverters> messageConverters;
@Bean
public UserServiceProxy userProxy1(ApplicationContext context) {
UserServiceProxy.QQ qq = new FeignClientBuilder(context)
.forType(UserServiceProxy.QQ.class, "qqUser")
.customize((ct) -> ct.contract(new SpringMvcContract())
.encoder(new SpringEncoder(messageConverters)).build()).build();
UserServiceProxy.Wechat wechat = new FeignClientBuilder(context)
.forType(UserServiceProxy.Wechat.class, "wechatUser")
.customize((ct) -> ct.contract(new SpringMvcContract()).build()).build();
return enableQqService ? qq : wechat;
}
}
低版本(2.x+), 自定义Target
@Component
public class ProxyConfiguration {
@Value("${user.service.qq.enable:false}")
boolean enableQqService;
@Autowired
ObjectFactory<HttpMessageConverters> messageConverters;
@Bean
public UserServiceProxy userProxy2(@Autowired(required = false) ServiceInstanceChooser chooser) {
CustomTarget<UserServiceProxy.QQ> qq = new CustomTarget<>(UserServiceProxy.QQ.class, "qq", "/qq_open/user", chooser);
CustomTarget<UserServiceProxy.Wechat> wechat = new CustomTarget<>(UserServiceProxy.Wechat.class, "wechat", "/wechat_open/user", chooser);
return enableQqService ?
new Feign.Builder().contract(new SpringMvcContract()).encoder(new SpringEncoder(messageConverters)).target(qq) :
new Feign.Builder().contract(new SpringMvcContract()).encoder(new SpringEncoder(messageConverters)).target(wechat);
}
static class CustomTarget<T> extends Target.HardCodedTarget<T> {
private final ServiceInstanceChooser chooser;
public CustomTarget(Class<T> type, String name, String url, ServiceInstanceChooser chooser) {
super(type, name, url);
this.chooser = chooser;
}
@Override
public String url() {
//使用chooser做负载均衡
return chooser.choose(super.url()).getUri() + super.url();
}
}
}
POM
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.3</version>
<relativePath/>
</parent>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>2020.0.3</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-openfeign</artifactId>
</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>
运行
通过@Autowire UserServiceProxy
进行方法调用, 能正确返回数据即切换成功