前言
1、http调用
2、整合ribbon
3、整合hystrix
4、interceptor
5、使用线程池对http调用
6、http装换https
Feign可以在同一个注册中心实现服务与服务之间的调用,当然也可以在不同的注册中心实现接口调用,整合Ribbon做负载均衡,整合hystrix做容错处理,通过Interceptor支持,可以传递信息
谈谈feign在项目中如何使用
第一步: 添加依赖
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-okhttp</artifactId>
</dependency>
第二步: 开启注解扫描
@EnableFeignClients({
"com.xx.cloud.feign"
})
第三步:写代码
@FeignClient(name = "${xx.xx.serviceId}")
public interface FeignDeviceClinet {
@RequestMapping(value = "${xx.xx.register}", method = RequestMethod.POST)
public ObjectRestResponse<Boolean> postRegister(ClifeDeviceRegisterBean entiry);
分析: 上述可以实现http调用但是当服务出现问题,导致调用失败,我们应该对他实现容错处理(hystrix)
Hystrix
第一步: 引入依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
第二步:写application.yml
feign:
eureka:
enabled: true
hystrix:
enabled: true
第三步:写代码
@FeignClient(name = "${xx.xx.serviceId}", fallbackFactory = FeignDeviceClinetFailBackFactory.class)
public interface FeignDeviceClinet {
@RequestMapping(value = "${xx.xx.register}", method = RequestMethod.POST)
public ObjectRestResponse<Boolean> postRegister(ClifeDeviceRegisterBean entiry);
配置服务容错 官方提供了几种容错的现实,下面我们重点介绍下面这种实现,他可以捕获到异常、还可以接口做处理。
public class FeignDeviceClinetFailBackFactory implements FallbackFactory<FeignDeviceClinet>{
private final static Logger logger = LoggerFactory.getLogger(FeignDeviceClinetFailBackFactory.class);
@Override
public FeignDeviceClinet create(Throwable e) {
if(e!=null && e.getStackTrace()!=null && e.getMessage()!=null)
{
logger.error(String.format("错误信息: %s",e.getMessage()));
}
return new FeignDeviceClinetFailBack() {
@Override
public ObjectRestResponse<Boolean> postRegister(ClifeDeviceRegisterBean entiry) {
ObjectRestResponse<Boolean> result = new ObjectRestResponse<>().data(false);
result.setStatus(404);
return result;
}
}
}
}
如果服务注册到注册中心上,客户端如何做负载均衡,因此ribbon从此诞生。注册中心可以eureka、nacos etc.
Ribbon
第一步:整合依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
</dependency>
第二步:application.yml
ribbon:
eureka:
enabled: true
ReadTimeout: 60000
ConnectTimeout: 60000
MaxAutoRetries: 0
MaxAutoRetriesNextServer: 1
OkToRetryOnAllOperations: false
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
Interceptor
在分布式环境下面,服务于服务之间传递参数、或者在请求头添加参数。下面介绍feign拦截器。
第一步:添加配置configuration
@FeignClient(name = "${xx.xx.serviceId}", fallbackFactory = FeignDeviceClinetFailBackFactory.class,
configuration = FeignDeviceInterceptor.class)
public interface FeignDeviceClinet {
@RequestMapping(value = "${xx.xx.register}", method = RequestMethod.POST)
public ObjectRestResponse<Boolean> postRegister(ClifeDeviceRegisterBean entiry);
第二步:写代码
@Slf4j
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class FeignDeviceInterceptor implements RequestInterceptor {
private final BeanFactory beanFactory;
public Date getUtTC() {
Calendar calendars = Calendar.getInstance();
int offset = calendars.get(Calendar.ZONE_OFFSET);
calendars.add(Calendar.MILLISECOND, -offset);
Date time = calendars.getTime();
return time;
}
private Map<String, Object> commonHeaders() {
Map<String, Object> params = new ConcurrentHashMap<String, Object>();
params.put("appId", ParamContant.systemId);
params.put("appVersion", ParamContant.appVersion);
params.put("clientId", UUID.randomUUID().toString().replace("-", ""));
params.put("sequenceId", UUID.randomUUID().toString().replace("-", ""));
long time = getUtTC().getTime();
params.put("timestamp", time);
params.put("Content-Type", ParamContant.contentType);
params.put("timezone", 8);
params.put("language", ParamContant.language);
HaierService bean = beanFactory.getBean(HaierService.class);
params.put("accessToken", bean.getToken());
return params;
}
public Map<String, Object> requestHeaders() {
// 第一步: 封装公共头
Map<String, Object> commonHeaders = commonHeaders();
// 第二步: 封装key
log.info("appId = {}, appKey = {}, timestamp = {}, code = {}, httpUrl = {}", ParamContant.appId,
ParamContant.systemKey,
commonHeaders.get("timestamp"),
DataMap.getValue("code"),
ParamContant.httpUrl
);
JSONObject vo = new JSONObject();
vo.put("code", DataMap.getValue("code").trim());
String sign = CommonUtils.getSign(ParamContant.systemId,
ParamContant.appKey,
String.valueOf(commonHeaders.get("timestamp")),
vo.toJSONString(),
ParamContant.httpUrl);
// 第三步: 存入sign
commonHeaders.put("sign", sign);
// 第四步: 发送Http请求
return commonHeaders;
}
@Override
public void apply(RequestTemplate requestTemplate) {
Map<String, Object> objectMap = requestHeaders();
log.info("interceptor send Haier Cloud params = {}", JSONObject.toJSONString(objectMap));
objectMap.forEach((key, value) -> {
if (value instanceof Integer || value instanceof Long) {
requestTemplate.header(key, String.valueOf(value));
} else {
requestTemplate.header(key, (String) value);
}
});
}
线程池
feign底层实现没有使用线程池,那么接下来我们引入线程池 (httpclient, okHttp etc)。
第一步:添加依赖
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.7</version>
</dependency>
第二步:写配置文件
feign:
eureka:
enabled: true
httpclient:
enabled: true
hystrix:
enabled: true
okhttp:
enabled: true
compression:
request:
enabled: true
mime-types: text/xml,application/xml,application/json
min-request-size: 2048
response:
enabled: true
Https
在实际项目中可能需要服务支持https调用,下面介绍对https的支持
@Configuration
public class FeignConfig {
/**
* 将feign http 转换为https调用
* @param cachingFactory
* @param clientFactory
* @return
* @throws NoSuchAlgorithmException
* @throws KeyManagementException
*/
@Bean
@ConditionalOnMissingBean
public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory,
SpringClientFactory clientFactory) throws NoSuchAlgorithmException, KeyManagementException {
//添加https支持
SSLContext ctx = SSLContext.getInstance("SSL");
X509TrustManager tm = new X509TrustManager() {
@Override
public void checkClientTrusted(X509Certificate[] chain,
String authType) throws CertificateException {
}
@Override
public void checkServerTrusted(X509Certificate[] chain,
String authType) throws CertificateException {
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return null;
}
};
ctx.init(null, new TrustManager[]{tm}, null);
return new LoadBalancerFeignClient(new Client.Default(ctx.getSocketFactory(),
new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
// TODO Auto-generated method stub
return true;
}
}) ,
cachingFactory, clientFactory);
}
}
推荐配置
feign:
eureka:
enabled: true
httpclient:
enabled: true
hystrix:
enabled: true
okhttp:
enabled: true
compression:
request:
enabled: true
mime-types: text/xml,application/xml,application/json
min-request-size: 2048
response:
enabled: true
ribbon:
eureka:
enabled: true
ReadTimeout: 60000
ConnectTimeout: 60000
MaxAutoRetries: 0
MaxAutoRetriesNextServer: 1
OkToRetryOnAllOperations: false
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
hystrix:
threadpool:
default:
coreSize: 2 ##并发执行的最大线程数,默认10
maxQueueSize: 10 ##BlockingQueue的最大队列数
queueSizeRejectionThreshold: 5 ##即使maxQueueSize没有达到,达到queueSizeRejectionThreshold该值后,请求也会被拒绝
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 15000