服务调用有2种方式:rest、rpc,springcloud使用RestTemplate实现rest调用,还可以使用feign进行声明式的远程http服务调用,所谓声明式就是通过服务接口来调用,和rpc相似。

 

使用feign进行服务调用

feign是一种服务调用方式,自然是在消费者中使用的
 
1、创建时勾选Spring Cloud Routing -> OpenFeign,或者手动添加依赖

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

 

2、引导类上加 @EnableFeignClients

//如果不指定basePackages,默认会扫描所有的包
@EnableFeignClients(basePackages = "com.chy.mall.feignclient")

在引导类上加@EnableXxx的,在启动时会扫描相关注解。

 

3、新建包feignclient用来存放feign服务调用的接口

@FeignClient("order-service")  //指定要调用的服务名称。feign也内置了ribbon,会自动实现负载均衡
@RequestMapping("/order")
public interface OrderServiceFeignClient {

    @GetMapping("/list/{user_id}")  //指定地址
    List<Order> findOrdersByUserId(@PathVariable("user_id") Integer userId); 

}

可以使用springmvc的注解绑定参数。

@FeignClient(name=“order-service”),name的别名是value,也可以写成@FeignClient(value=“order-service”),注解特性可以缺省value。

 

4、service

@Service
public class UserService {

  @Autowired
    private OrderServiceFeignClient orderServiceFeignClient;  //注入要使用的Feign接口

    public List<Order> findOrdersById(Integer userId){
        //通过Feign接口进行服务调用
        return orderServiceFeignClient.findOrdersByUserId(userId);
    }

}

 

feign的常用配置项

以feign的日志配置为例,一般的日志直接在logging.level中配置即可,feign的日志配置方式有些不同,就算把日志级别设置为debug,feign默认也不会输出任何日志。
 

feign有自己的日志级别

  • NONE:默认值,不打印任何日志
  • BASIC: 只打印请求方法、url、响应状态码、执行时间
  • HEADERS:在BASIC的基础上,会打印请求、响应的header
  • FULL:打印请求、响应的header、body、元数据,可以看到提供者返回的数据。

生产环境一般用BASIC,输出日志少,性能好;开发环境一般用FULL,方便调试。
 

代码配置方式

yml

logging:
  level:
    root: info
    #feign打印日志的前提是feign接口的日志级别是debug,因为feign输出的日志是debug级别
    com.chy.mall.feignclient: debug

 
配置类 config.FeignClientConfig

import feign.Logger;
import org.springframework.context.annotation.Bean;

/**
 * 定义feign接口的日志级别
 * 不要加@Configuration
 */
public class FeignClientConfig {

    @Bean
    public Logger.Level level(){
        return Logger.Level.FULL;
    }

}

 
引导类

//指定feign的配置类
@EnableFeignClients(basePackages = "com.chy.mall.feignclient", defaultConfiguration = FeignClientConfig.class)

//上面是全局配置,给所有的feign接口指定,也可以分别给feign接口指定
//@FeignClient("order-service", configuration = FeignClientConfig.class)

步骤也繁琐,不推荐。

 

属性配置方式(推荐)

logging:
  level:
    root: info
    #将feign接口的日志级别设置为debug,因为feign输出的日志是debug级别
    com.chy.mall.feignclient: debug
    
feign:
  client:
    config:
      #全局配置,指定所有feign服务调用的日志级别
      default:
        loggerLevel: full
       #也可以分别配置各个服务调用的
#      order-server:
#        loggerLevel: full
#      msg-server:
#        loggerLevel: full

无需其它配置。
 

除了loggerLevel,feign的其它常用配置项如下

  • connectTimeout: 10000  连接超时时间
  • readTimeout: 10000  读取超时时间

 

feign的继承特性

提供者提供接口,消费者中有要调用的接口声明,提供者修改了接口的映射地址、形参表、返回值类型,比如返回类型是实体类,现在需求变了,这个实体类要增删一些成员变量,提供者修改接口后,消费者也要同步修改,接口维护起来很麻烦。

feign的继承特性:将要服务之间要调用的接口、涉及的实体类提出来,单独写成一个模块,打成jar包在提供者、消费者中引入,然后提供者的controller实现接口,消费者的feign接口继承接口(无需再进行声明)。更改接口时,要稍微好维护一些。

官方不推荐使用feign的继承特性,因为会使服务耦合在一起,但实际使用的公司也不少。

 

传递多个参数

restful适合参数少的情况,如果参数较多,可以使用以下几种方式

  • feign接口、提供者的接口都使用 @RequestParam + 多个参数
  • feign接口、提供者的接口都使用 @RequestParam + Map<String,Object>
  • 提供者的接口用实体类声明参数,feign接口用 @SpringQueryMap + 实体类

第一二种都支持feign的继承特性,第三种不支持。

不建议使用Map接收参数,需要自己转换参数的数据类型,麻烦、易出错。

如果把@SpringQueryMap换成@RequestParam,可以调用服务,但传过去的实体的成员变量都是null。

 

脱离ribbon使用feign

ribbon是从自己的注册中心获取节点列表,如果是通过第三方的网关调用他们的服务,可以用HttpClient发起请求,也可以用feign发起请求,此时feign是脱离ribbon使用的。

//url指定请求地址,value指定服务名
@FeignClient(value = "xxx-server",url = "http://www.xxx.com/xxx")

服务名是必需的,如果不指定服务名,启动不了应用。在指定了url的情况下,服务名可以随便写一个。

 

feign与RestTemplate的对比

feign 伪装、假装,feign只是伪rpc调用,实际使用的还是RestTemplate,使用的协议还是http。

  • feign性能要低于直接使用RestTemplate。feign是将RestTemplate包装为feign接口,生成动态代理,通过代理进行操作,有额外的开销,拉低了性能。
  • feign让代码可读性更好、易于维护。服务调用接口声明都聚集在一起,方便查看、修改;接口改动时只需修改feignclient包下的feign接口,无需找到所有的服务调用处,维护方便。

一般都是使用feign。

 

feign性能优化之配置连接池

feign默认使用urlconnection发起请求,不使用连接池;feign也支持用httpclient发起请求,可以使用feign-httpclient、okhttp等jar包提供的httpclient连接池。
 

httpclient

<dependency>
    <groupId>io.github.openfeign</groupId>
    <artifactId>feign-httpclient</artifactId>
</dependency>
feign:
  #配置httpclient连接池
  httpclient:
  	#默认true
    enabled: true
    #feign连接池的最大连接数
    max-connections: 200
    #feign连接池单个url的最大连接数
    max-connections-per-route: 50

这些数值都是默认值,根据压测结果进行修改

 

okhttp

feign:
  okhttp:
  	#默认false
    enabled: true
  #配置httpclient连接池
  httpclient:
    #feign连接池的最大连接数
    max-connections: 200
    #feign连接池单个url的最大连接数
    max-connections-per-route: 50

当然,也可以使用其它提供了httpclient连接池的jar包,但需要指定jar包的<version>。