在上一篇文章,利用nacos实现了服务的注册和发现,利用RestTemplate实现了服务的远程调用。但是远程调用的代码太复杂了。
而且这种调用方式,与原本的本地方法调用差异太大,编程时的体验也不统一,一会儿远程调用,一会儿本地调用。
因此,我们必须想办法改变远程调用的开发模式,让远程调用像本地方法调用一样简单。而这就要用到OpenFeign组件了。
其实远程调用的关键点就在于四个:
所以,OpenFeign就利用SpringMVC的相关注解来声明上述4个参数,然后基于动态代理帮我们生成远程调用的代码,而无需我们手动再编写,非常方便。
1.快速入门
1.1 导入依赖
1 2 3 4 5 6 7 8 9 10
| <!--openFeign--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> <!--负载均衡器--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-loadbalancer</artifactId> </dependency>
|
1.2 启用OpenFeign
在对应的服务启动类上添加注解,启动OpenFeign功能:
1 2 3 4 5 6 7 8
| @MapperScan("com.hmall.item.mapper") @EnableFeignClients @SpringBootApplication public class ItemApplication { public static void main(String[] args) { SpringApplication.run(ItemApplication.class, args); } }
|
1.3 编写OpenFeign客户端(接口)
只需要为需要调用的服务(提供者)编写接口,也就是把对应被调用服务需要被调用的controller层的接口写成第三方接口(OpenFeign客户端),然后通过@FeignClient(“item-service”)声明被调用的是哪个服务。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| package com.hmall.cart.client;
import com.hmall.cart.domain.dto.ItemDTO; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam;
import java.util.List;
@FeignClient("item-service") public interface ItemClient {
@GetMapping("/items") List<ItemDTO> queryItemByIds(@RequestParam("ids") Collection<Long> ids); }
|
这里只需要声明接口,无需实现方法。接口中的几个关键信息:
@FeignClient("item-service")
:声明服务名称
@GetMapping
:声明请求方式
@GetMapping("/items")
:声明请求路径
@RequestParam("ids") Collection<Long> ids
:声明请求参数
List<ItemDTO>
:返回值类型
有了上述信息,OpenFeign就可以利用动态代理帮我们实现这个方法,并且向http://item-service/items
发送一个GET
请求,携带ids为请求参数,并自动将返回值处理为List<ItemDTO>
。
我们只需要直接调用这个方法,即可实现远程调用了
1.4 使用FeignClient
通过注入上面声明接口,来调用其中的方法:
这时候openfeign就提我们服务拉取,负载均衡,发送http请求,通过调用此接口中的方法就会找到对应声明的服务中controller对应的接口,完成服务调用。
1.5 连接池
Feign底层发起http请求,依赖于其它的框架。其底层支持的http客户端实现包括:
- HttpURLConnection:默认实现,不支持连接池
- Apache HttpClient :支持连接池
- OKHttp:支持连接池
因此我们通常会使用带有连接池的客户端来代替默认的HttpURLConnection。比如,我们使用OK Http.
导入依赖然后在配置文件中开启即可:
1 2 3 4 5
| <!--OK http 的依赖 --> <dependency> <groupId>io.github.openfeign</groupId> <artifactId>feign-okhttp</artifactId> </dependency>
|
1 2 3
| feign: okhttp: enabled: true
|
2.抽取公共模块
为了避免代码重复编写,最好是将feign接口抽取到一个公共模块。
比如新建一个模块hm-api:
将需要被调用的服务的接口以及需要用上的实体类,还有openfeign的配置都写在这个模块里面。
导入依赖:
1 2 3 4 5 6 7 8 9 10 11 12
| <dependencies> <!--open feign--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> <!-- load balancer--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-loadbalancer</artifactId> </dependency> </dependencies>
|
最后只需在使用远程调用的服务加上这个模块的坐标即可:
1 2 3 4 5
| <dependency> <groupId>com.heima</groupId> <artifactId>hm-api</artifactId> <version>1.0.0</version> </dependency>
|
然后需要在启动类上声明这个包的接口的位置:
1 2 3 4 5 6 7 8 9 10 11
| @MapperScan("com.hmall.item.mapper")
@EnableFeignClients(basePackages = "com.hmall.api.client",defaultConfiguration = DefaultFeignConfig.class) @SpringBootApplication public class ItemApplication { public static void main(String[] args) { SpringApplication.run(ItemApplication.class, args); } }
|
1 2 3 4 5 6
| @FeignClient(value = "cart-service",configuration = DefaultFeignConfig.class) public interface CartClient { @DeleteMapping("/carts") void deleteCartItemByIds(@RequestParam("ids") Collection<Long> ids); }
|
DefaultFeignConfig:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| ** * @Author wzy * @Date 2023/12/17 16:20 * @description: feign服务调用配置 */ public class DefaultFeignConfig {
@Bean public Logger.Level feignLoggerLevel(){ return Logger.Level.FULL; }
@Bean public RequestInterceptor userInfoRequestInterceptor(){ return new RequestInterceptor() { @Override public void apply(RequestTemplate requestTemplate) { Long userId = UserContext.getUser(); if(userId!=null){ requestTemplate.header("user-info", userId.toString()); } } }; }
@Bean public ItemClientFallback itemClientFallback(){ return new ItemClientFallback(); }
}
|