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给我们默认配置了一套连接池。

springCloud的Feign在项目中的使用总结——https请求、结合hystrix、httpclient配置等

默认的连接池配置:
springCloud的Feign在项目中的使用总结——https请求、结合hystrix、httpclient配置等

这里有个问题没解决的就是通过@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配置的时候,也要考虑连接池配置如何自定义。