最近在整合项目的RPC调用,对Feign有了更多对一些理解
Feign它是一个声明式WebService客户端.,它支持多种注解,Feign自带注解以及JAX-RS标准的注解.Feign也支持可拔插式的编码器和解码器.即我们可以自定义编码器、解码器、错误处理器;Spring Cloud是对Feign进行了封装,使其支持了Spring MVC标准注解和HttpMessageConverters.
使用
我们可以通过JAX-RS标准的注解直接使用Feign.
- Feign配置
@Slf4j
public class CommonErrorDecoder implements ErrorDecoder {
@Override
public final Exception decode(String methodKey, Response response) {
FeignException exception = errorStatus(methodKey, response);
log.error("REST服务异常:URL ={}, CODE ={}, MSG ={}",
response.request().url(),
response.status(),
exception.getMessage());
return postDecode(methodKey, response, exception);
}
protected Exception postDecode(String methodKey, Response response, Exception exception) {
return exception;
}
}
@Configuration
public class FeignConfig {
/**
* 设置 OkHttpClient
*/
@Bean
public OkHttpClient okHttpClient() {
return new OkHttpClient(new okhttp3.OkHttpClient().newBuilder()
.addInterceptor(new HttpLoggingInterceptor()
.setLevel(HttpLoggingInterceptor.Level.BODY))
.build());
}
@Bean
public Gson gson() {
return GsonFactory.create(Collections.emptyList());
}
@Bean
public GsonEncoder gsonEncoder(Gson gson) {
return new GsonEncoder(gson);
}
@Bean
public GsonDecoder gsonDecoder() {
return new GsonDecoder();
}
@Bean
public JAXRSContract jaxrsContract() {
return new JAXRSContract();
}
/**
* 统一错误处理,RPC 调用失败发送报警信息
*/
@Bean
public CommonErrorDecoder commonErrorDecoder() {
return new CommonErrorDecoder();
}
/**
* 统一错误重试处理,RPC 调用失败客户端自动重试
*/
@Bean
public RetryErrorDecoder retryErrorDecoder() {
return new RetryErrorDecoder();
}
/**
* 设置重试策略 重试只对IOException起作用,其他情况重试需自定义errorDecoder
*/
@Bean
public Retryer retryer() {
return new Retryer.Default(2000, SECONDS.toMillis(10), 5);
}
/**
* 设置超时时间
*/
@Bean
public Request.Options options() {
return new Request.Options(10000, 10000);
}
@Bean("feignForTestApi")
public Feign feignForOpenApi(OkHttpClient okHttpClient, GsonDecoder gsonDecoder, Gson gson,
CommonErrorDecoder commonErrorDecoder, JAXRSContract jaxrsContract, Retryer retryer,
Request.Options options) {
return new Feign.Builder()
.client(okHttpClient)
.decoder(new OpenApiGsonDecoder(gsonDecoder, gson))
.contract(jaxrsContract)
.requestInterceptor(template -> {
// 把当前参数转存到map
final Map<String, String> params = new HashMap<>();
template.queries().forEach((key, values) -> {
params.put(key, values.iterator().next());
});
// 清空参数
template.queries(Collections.emptyMap());
// 添加新的参数
params.put("param", key);
params.forEach(template::query);
})
.errorDecoder(commonErrorDecoder)
.retryer(retryer)
.options(options)
.build();
}
}
@Configuration
public class ApiConfig {
@Value("${rest.testApi.url}")
private String testApiHost;
@Bean
public TestApi initTestApi(@Qualifier("feignForTestApi") Feign feign) {
return feign.newInstance(new Target.HardCodedTarget<>(TestApi.class, testApiHost));
}
}
- 配置好Feign后配置远程接口
public interface TestApi {
@POST
@Path("/v1/getInfo")
List<Info> getInfo();
}
也可以使用Spring Cloud 封装好的Feign.
@FeignClient(name = "test-service", url = "${rest.testApi.url}")
public interface testApi {
@RequestMapping(value = {"/v2/getInfo"}, method = {RequestMethod.GET}, consumes = {MediaType.APPLICATION_FORM_URLENCODED_VALUE})
GetEntityIdResponse getId(@RequestParam("size") Integer size, @RequestParam("cityId") Integer cityId);
}
相关配置大同小异,完美封装了Fegin插拔式的好处,可通过yml文件配置自定义编码器、解码器、错误解码器、重试等
接下来理解Feign配合Ribbon、Hystrix实现负载均衡、熔断