【微服务】七. http客户端Feign

7.1 基于Feign远程调用

RestTimeplate方式调用存在的问题

先来看以前利用RestTemplate发起远程调用的代码:

String  url = "http://userservice/user"+order.getUserId();
User user = restTemplate.getForObject(url,User.class);

存在下面的问题:

  • 代码可读性差,编程体验不统一
  • 参数复杂URL难以维护

Feign的介绍

Feign是一个声明式的http客户端,官方地址:https://github.com/OpenFeign/feign
其作用就是帮助我们优雅的实现http请求的发送,解决上面的问题。

定义和使用Feign客户端
使用Feign的步骤如下:

  • 引入依赖
<dependency>
	<groupId>org.springframework.cloudgroupId>
    <artifactId>spring-cloud-starter-openfeignartifactId>
dependency>
  • 在order-service的启动类添加注解开启Feign的功能:
@EnableFeignClients  -- 开启feign注解
@MapperScan("cn.itcast.order.mapper")
@SpringBootApplication
 public class OrderApplication{
     public static void main(String[] args){
         SpringApplication.run(OrderApplication.class,args);
     }
 }
  • 编写Feign客户端
@FeignClient("userservice")
public interface UserClient{
    @GetMapping("/user/{id}")
    User findById(@PathVariable("id") Long id);
}

主要是基于SpringMVC的注解来声明远程调用的信息,比如:

  • 服务名称:userservice
  • 请求方式:GET
  • 请求路径:/user/{id}
  • 请求参数:Long id
  • 返回值类型:User
  1. 用Feign客户端代替RestTemplate
@Autowired
private UserClient userClient;

public Order queryOrderById(Long orderId){
	// 1. 查询订单
	Order order = orderMapper.findById(orderId);
	// 2. 利用Feign发起http请求,查询用户
	User user = userClient.findById(order.getUserId());
	// 3. 封装User到Order
	order.setUser(user);
	// 4. 返回
	return order; 
}

实际操作:
【微服务】七. http客户端Feign_第1张图片
feign集成了ribbon,已经实现了负载均衡
【微服务】七. http客户端Feign_第2张图片

总结

Feign的使用步骤:

  1. 引入依赖
  2. 添加@EnableFeignClients注解
  3. 编写FeignClient接口
  4. 使用FeignClient中定义的方法代替RestTemplate

7.2 自定义feign配置

Feign运行自定义配置来覆盖默认配置,可以修改的配置如下:

类型 作用 说明
feign.Logger.Level 修改日志级别 包含四种不同的级别:NONE、BASIC、HEADERS、FULL(一般情况下用basic或none,调试错误下用full)
feign.codec.Decoder 响应结果的解析器 http远程调用的结果做解析,例如解析json字符串为java对象
feign.codec.Encoder 请求参数编码 将请求参数编码,便于通过http请求发送
feign.Contract 支持的注解格式 默认是SpringMVC的注解
feign.Retryer 失败重试机制 请求失败的重试机制,默认是没有,不过会使用Ribbon的重试

一般我们需要配置的就是日志级别。
配置Feign日志有两种方式:
方式一:配置文件方式
全局生效:

feign:
	client:
		config:
			default:  # 这里用default就是全局配置,如果是写服务名称,则是针对某个微服务的配置
				loggerLeverl: FULL # 日志级别

局部生效:

feign:
	client:
		config:
			userservice: # 这里用default就是全局配置,如果是写服务名称,则是针对某个微服务的配置
				loggerLevel: FULL # 日志级别

方式二:java代码配置,需要先声明一个Bean

public class FeignClientConfiguration{
	@Bean
	public Logger.Level feignLogLevel(){
		return Logger.Level.BASIC;  // 引入的包: feign.Logger
	}
}

而后,如果是全局配置,则把它放到@EnableFeignClients这个注解中:(加到启动类上)

@EnableFeignClients(defaultConfiguration = FeignClientConfiguration.class);

如果是局部配置,则把它放到@FeignClient这个注解中:(加到具体的client接口上)

@FeignClient(value = "userservice",configuration = FeignClientConfiguration.class);

总结

Feign的日志配置:

  1. 方式一是配置文件,feign.client.config.xxx,loggerLevel
  • 如果xxx是default则代表全局
  • 如果xxx是服务名称,例如userservice则代表某服务
  1. 方式二是java代码配置Logger.Level这个Bean
  • 如果是在@EnableFeignClients注解声明则代表全局
  • 如果在@EnableClient注解中声明则代表某服务

7.3 Feign的性能优化

7.3.1 Feign底层的客户端实现:

  • URLConnection:默认实现,不支持连接池
  • Apache HttpClient:支持连接池
  • OKHttp:支持连接池
    因为优化Feign的性能主要包括:
  • 使用连接池代替默认的URLConnection
  • 日志级别,最好用basic或none(默认none)

7.3.2 Feign的性能优化–连接池配置

Feign添加HttpClient的支持
引入依赖:


<dependency>
   <groupId>io.github.openfeigngroupId>
   <artifactId>feign-httpclientartifactId>
dependency>

配置连接池:

feign:
	client:
		config:
			default: # default 全局的配置
				loggerLevel: BASIC # 日志级别,BASIC就是基本请求和响应
				
	httpclient:
		enbaled: true # 开启feign对HttpClient的支持
		max-connections: 200 # 最大的连接数
		max-connections-per-route: 50 # 每个路径的最大连接数

真实业务中要对程序进行压测,来具体确定max-connections、max-connections-per-route的值

总结:

Feign的优化:

  1. 日志级别尽量使用basic
  2. 使用HttpClient和OKHttp代替URLConnection
    引入feign-httpClient依赖
    配置文件开启httpClient功能,设置连接池参数

7.4 Feign最佳实践分析

方式一(继承):

给消费者的FeignClient和提供者的controller定义统一的父接口作为标准。
【微服务】七. http客户端Feign_第3张图片
一般我们不推荐客户端和服务端之间共享一个interface,因为会造成紧耦合,而且对spring mvc不起作用

  • 服务紧耦合
  • 父接口参数列表中的映射不会被继承

方式二(抽取):

将FeignClient抽取为独立模块,并且把接口有关的POJO,默认的Feign配置都放到这个模块中,提供给所有的消费者使用
【微服务】七. http客户端Feign_第4张图片
降低了耦合度,但是如果order-service只用feign-api中的两三个接口,需要把feign-api的所有都加载出来。

总结

Feign的最佳实践

  1. 让controller和FeignClient继承同一接口
  2. 将FeignClient、POJO、Feign的默认配置都定义到一个项目中,供所有消费者使用

7.5 实践:抽取FeignClient

抽取FeignClient

实现最佳实践方式二的步骤如下:

1.首先创建一个module,命名为feign-api,然后引入feign的starter依赖

2.将order-service中编写的UserClient、User、DefaultFeignConfiguration都复制到feign-api项目中

3.在order-service中引入feign-api的依赖

4.修改order-service中的所有与上述三个组件有关的import部分,改成导入feign-api中的包

5.重启测试

当定义的FeignClient不在SpringBootApplication的扫描包范围时,这些FeignClient无法使用。有两种方式解决:

方式一:指定FeignClient所在包(将所有的都拿过来)

@EnableFeignClients(basePackages="cn.itcast.feign.clients")

方式二:指定FeignClient字节码(只拿UserClient)

@EnableFeignClients(clients = {UserClient.clss})

总结

不同包的FeignClient的导入有两种方式:

1.在@EnableFeignClients注解中添加basePackages,指定FeignClient所在的包

2.在@EnableFeignClients注解中添加clients,指定具体FeignClient的字节码

你可能感兴趣的:(微服务,微服务)