springCloud的Feign在项目中的使用总结——https请求、结合hystrix、httpclient配置等
目录
一、前言
Feign目前在网上能找的的有两种使用方式,一种是基于springcloud,使用@[email protected]。一种是github上的开源项目,使用@RequestLine注解。
此次示例使用的是第一种方式,但是并没有使用springcloud的服务中心eureka。主要记录了使用feign发送请求参数的传递,访问https地址、httpclient替换feign底层的urlConection,集成hystrix进行超时回调。
二、具体使用
在具体使用之前需要进行相关jar的依赖添加:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
<!-- 使用Apache HttpClient替换Feign原生httpclient -->
<dependency>
<!--不是netfix包下了,应该是独立出来开源了-->
<groupId>io.github.openfeign</groupId>
<artifactId>feign-httpclient</artifactId>
<!-- <version>9.5.1</version> 这里可以不指定version spring-boot-parent中已经有版本定义-->
</dependency>
feign-httpclient用于httpclient替换feign底层http请求。
spring-cloud-starter-netflix-ribbon在进行https地址调用的时候会用到。相关的application.yml配置在具体demo按需要添加。
1. feign发送post请求参数的传递
@FeignClient(name="feignService",url="${dlc.amap.ip}")
public interface FeignHttpsService {
@RequestMapping(value="${dlc.amap.address}={sign}",method = RequestMethod.POST,consumes = MediaType.APPLICATION_JSON_UTF8_VALUE)
String sendPost(@RequestBody String str, @PathVariable String sign);
@RequestMapping(value="helloget",method = RequestMethod.GET)
String sendGet();
}
定义一个接口,接口中有发送get和post两种请求的方法,此接口不需要写实现方法,在项目中使用@autowired注解引入进行使用即可。
@RestController
public class FeignController {
@Autowired
private FeignHttpsService feignHttpService;
@Autowired
private FeignService feignService;
@RequestMapping("test")
public String test(){
String string = "{\n" +
" \"account\":\"leads_catalyst_intg\",\n" +
" \"busiextStr\":\"eyJwYXJhbXMiOnsiVmFyXzE1IjoieHN2IiwiVmFyXzEyIjoid2NwIiwiVmFyXzEzIjoic21uIiwiVmFyXzE0IjoiYXRxIn0sInF1cyI6ImF1dG8tc3RkLW1sLWNvbSMxMDEwMDAifQ==\",\n" +
" \"mobile\":\"PbrUyeLK9SlJu0S+JyzPnk44L6BKIAOkG56FaE0vuBIkBPwWjPFQBPsjPOmDbezuhqspD7CJR6KeJUUUbZddi5goTRm+iDHJyE9jKhGoXQOIpY7v8bv0/1lXaW8EipqHQ8WLo0lRMtRg+6rdynDOULIwnv0ieQWjM9babHfG9QE=\"\n" +
"}";
String sign = "dc5dfed260441a5ca69ecb6cbaecba9b";
String sendPost = feignHttpService.sendPost(string,sign);
System.out.println("https的Post请求:"+sendPost);
return sendPost;
}
@RequestMapping("get")
public String test2(){
String sendPost = feignService.sendGet();
return sendPost;
}
}
在FeignHttpsService接口中可以发现并没有写什么具体的url,全部都是使用${}的方式引入,这样做的好处是可以统一管理url,我们一般是通过ip+实例名进行访问url,在application.yml文件中对url与实例名进行配置,如果后面有需要改动url的情况,只需要改动配置文件即可,不要改动代码。
dlc:
amap:
ip: https://127.0.0.1:6543/
address: api/test?sign
在FeignHttpsService接口中还可以看到参数的传递也是使用{}进行替换,需要注意的是@RequestBody注解是要通过post传递的参数内容, @PathVariable表示是要在url后面进行拼接的参数。
通过日志打印可以清楚的看到最终访问的url:
2019-01-28 17:58:13.489 DEBUG 2868 --- [-feignService-1] c.example.demo.client.FeignHttpsService : [FeignHttpsService#sendPost] ---> POST https://127.0.0.1:6543/api/test?sign=dc5dfed260441a5ca69ecb6cbaecba9b HTTP/1.1
2019-01-28 17:58:13.490 DEBUG 2868 --- [-feignService-1] c.example.demo.client.FeignHttpsService : [FeignHttpsService#sendPost] Content-Type: application/json;charset=UTF-8
2. 访问https地址
@Configuration
public class FeignConfig {
@Bean
@ConditionalOnMissingBean
public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory,
SpringClientFactory clientFactory) throws NoSuchAlgorithmException, KeyManagementException {
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);
}
}
需要在@Configuration注解修饰的类中进行证书认证忽略的操作,这样就可以访问https开头的url。
3. httpclient替换feign底层的urlConection
只需在application.yml中新增配置项,其他不需要改变,就可以实现httpclient替换urlconnection。
feign:
httpclient:
enabled: true
根据springboot的自动配置原理,如果能够找到httpclient的相关class,就会进行自动配置。在最开始的时候我们已经引入了httpClient的依赖,所以增加配置项即可启用,通过查看源码会发现feign给我们默认配置了一套连接池。
默认的连接池配置:
这里有个问题没解决的就是通过@bean新建一个httpclient的连接池,但是在spring启动的时候没有生效。使用actutor查看发现使用的仍然是最初的feign默认的连接池。按理自定义的bean会将spring创建的bean给覆盖,这里却没有。
@Configuration
public class FeignConfig {
@Bean
public HttpClient httpClient(){
System.err.println("init feign httpclient configuration 1111" );
// 生成默认请求配置
RequestConfig.Builder requestConfigBuilder = RequestConfig.custom();
// 超时时间
requestConfigBuilder.setSocketTimeout(5 * 1000);
// 连接时间
requestConfigBuilder.setConnectTimeout(5 * 1000);
RequestConfig defaultRequestConfig = requestConfigBuilder.build();
// 连接池配置
// 长连接保持30秒
final PoolingHttpClientConnectionManager pollingConnectionManager = new PoolingHttpClientConnectionManager(30, TimeUnit.MILLISECONDS);
// 总连接数
pollingConnectionManager.setMaxTotal(5000);
// 同路由的并发数
pollingConnectionManager.setDefaultMaxPerRoute(100);
// httpclient 配置
HttpClientBuilder httpClientBuilder = HttpClientBuilder.create();
// 保持长连接配置,需要在头添加Keep-Alive
httpClientBuilder.setKeepAliveStrategy(new DefaultConnectionKeepAliveStrategy());
httpClientBuilder.setConnectionManager(pollingConnectionManager);
httpClientBuilder.setDefaultRequestConfig(defaultRequestConfig);
HttpClient client = httpClientBuilder.build();
// 启动定时器,定时回收过期的连接
Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
// System.out.println("=====closeIdleConnections===");
pollingConnectionManager.closeExpiredConnections();
pollingConnectionManager.closeIdleConnections(5, TimeUnit.SECONDS);
}
}, 10 * 1000, 5 * 1000);
System.out.println("===== Apache httpclient 初始化连接池===");
return client;
}
}
4. 集成hystrix进行超时回调
@FeignClient(name="feignService",url="${dlc.amap.ip}",fallback=FeignFallback.class)
public interface FeignHttpsService {
@RequestMapping(value="${dlc.amap.address}={sign}",method = RequestMethod.POST,consumes = MediaType.APPLICATION_JSON_UTF8_VALUE)
String sendPost(@RequestBody String str, @PathVariable String sign);
@RequestMapping(value="helloget",method = RequestMethod.GET)
String sendGet();
}
@Component
class FeignFallback implements FeignHttpsService {
@Override
public String sendPost(String str, String sign) {
// TODO Auto-generated method stub
return "sendPost的回调方法";
}
@Override
public String sendGet() {
// TODO Auto-generated method stub
return "sendGet的回调方法";
}
}
@FeignClient中指定fallback的回调方法,此方法需要用@Component修饰,并实现@FeignClient修饰的接口。实现方法后需要重写接口,通过重写可以实现在回调方法中进行出现错误时候的日志记录。
application.yml新增超时时间配置,hystrix默认的超时时间是1s,超过这个时间就会调用fallback的方法。
hystrix:
command:
"FeignHttpsService#sendPost(String,String)":
execution:
isolation:
thread:
timeoutInMilliseconds: 6000
注意:如果不使用指定超时时间的接口及方法 “FeignHttpsService#sendPost(String,String)”,而是使用默认的default的话,就是所有的fallback的超时时间都是6s。指定哪个方法需要进行超时回调的时候需要使用类名+方法名+方法所需参数类型
的方式指定。
三、总结
以上就是使用springcloud中的feign进行http请求控制的一些使用方法,但是由于目前没找到自定义连接池的方式,所以在使用的过程中需要考虑这一点,还有一个feign是github上开源出来脱离于springcloud的使用,这种是依托于命令式编程。可以进行自定义连接池配置。具体使用还没有进行尝试。
四、其他
1. 为什么有了hystrix控制最大连接数,还需要自定义连接池配置?
因为hystrix是在连接池的上一层的,假若连接池最大连接数是100,但是hystrix最大连接数是200,这样是无法触发hystrix熔断机制的,所以在进行hystrix配置的时候,也要考虑连接池配置如何自定义。