服务调用有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>。