在上一个案列中Springcloud-注册中心
使用的交互是利用RestTemplate发起远程调用的代码,Feign是Springcloud整合的声明式组件,
Feign
Feign和RestTemplate都是用于在Java中实现RESTful API调用的工具,但它们之间有一些区别和优缺点。
区别
- Feign是一个声明式HTTP客户端,它使HTTP API请求变得更简单和更优雅。它基于注解,在创建动态代理时解析接口方法,并将其映射到HTTP请求。也就是说,您只需要编写Java接口并添加注解,就可以轻松地使用Feign处理HTTP请求。另一方面,RestTemplate是一个基于HTTP协议的同步请求客户端,需要显式地编写代码来发送HTTP请求和处理响应。
- Feign使用Netflix公司的Ribbon客户端负载平衡器来分发HTTP请求,而RestTemplate没有默认的负载平衡器。
- Feign还支持异步请求,通过使用Hystrix来实现异步执行HTTP请求,并提供了服务降级、断路器等重要的功能,而RestTemplate则不支持异步请求,但可以通过Spring Cloud Circuit Breaker来实现类似的功能。
- Feign还提供了可插拔的编码器和解码器,以将Java对象转换为HTTP请求、响应和错误,而RestTemplate使用消息转换器来完成类似的编解码工作。
优点
Feign具有更加优雅简洁的方式来定义和声明请求,也更加易于使用和理解。
Feign的异步请求和服务降级、断路器等功能可以使应用程序更加健壮和可靠,同时可以提高可用性和可伸缩性。
RestTemplate作为JDK中标准库的一部分,在Spring Boot中也有深入的集成,如果你不想增加额外的依赖,RestTemplate是一个很好的选择,同时它也可以胜任大多数的HTTP请求场景。
不同的项目在不同的情况下可能会选择使用不同的工具来完成对RESTful API的调用,开发人员需要根据具体情况进行选择。
步骤
- 引入依赖
<!--feign客户端依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
2.启动类添加注解开关@EnableFeignClients()
3. 为需要做调用的远程服务建立一个接口,其中建立方法,方法的返回值和mapping对应被远程调用服务中的返回值和mapping
//value表示服务名
@FeignClient(value = "userservice",configuration = Defaultconfig.class)//服务名
public interface UserClient {
@GetMapping("/user/{id}")//服务的地址
User findByif(@PathVariable("id") Long id);
}
路由对应被调用的服务接口路由
4. 此时controller调用变成
public Order queryOrderById(Long orderId) {
// 1.查询订单
Order order = orderMapper.findById(orderId);
// 2.用Feign远程调用
User user = userClient.findByif(order.getUserId());
order.setUser(user);
// 4.返回
return order;
}
此时就可以发起交互了,只需要服务名,feign就解析服务名得到ip地址发起交互
在默认情况下,Feign 会使用 Ribbon 进行负载均衡,而在 Ribbon 的配置文件中,会指定服务的负载均衡规则。如果没有指定负载均衡规则,则使用默认的轮询规则。因此,即使没有配置目标服务的 IP 地址,Feign 也可以通过 Ribbon 进行负载均衡,并向目标服务发起 HTTP 请求。
总之,Feign 的功能依赖于 Spring Cloud 的服务注册与发现模块以及 Ribbon 负载均衡模块。在默认情况下,Feign 可以通过这些模块获取目标服务的 IP 地址列表,并使用 Ribbon 进行负载均衡,而不需要显式指定 IP 地址。
2.2.自定义配置
Feign可以支持很多的自定义配置,如下表所示:
类型 | 作用 | 说明 |
---|---|---|
feign.Logger.Level | 修改日志级别 | 包含四种不同的级别:NONE、BASIC、HEADERS、FULL |
feign.codec.Decoder | 响应结果的解析器 | http远程调用的结果做解析,例如解析json字符串为java对象 |
feign.codec.Encoder | 请求参数编码 | 将请求参数编码,便于通过http请求发送 |
feign. Contract | 支持的注解格式 | 默认是SpringMVC的注解 |
feign. Retryer | 失败重试机制 | 请求失败的重试机制,默认是没有,不过会使用Ribbon的重试 |
一般情况下,默认值就能满足我们使用,如果要自定义时,只需要创建自定义的@Bean覆盖默认Bean即可。
下面以日志为例来演示如何自定义配置。
2.2.1.配置文件方式
基于配置文件修改feign的日志级别可以针对单个服务:
feign:
client:
config:
userservice: # 针对某个微服务的配置
loggerLevel: FULL # 日志级别
也可以针对所有服务(default):
feign:
client:
config:
default: # 这里用default就是全局配置,如果是写服务名称,则是针对某个微服务的配置
loggerLevel: FULL # 日志级别
basic的日志信息:向哪个服务发起请求,响应如何
而日志的级别分为四种:
- NONE:不记录任何日志信息,这是默认值。
- BASIC:仅记录请求的方法,URL以及响应状态码和执行时间
- HEADERS:在BASIC的基础上,额外记录了请求和响应的头信息
- FULL:记录所有请求和响应的明细,包括头信息、请求体、元数据。
2.2.2.Java代码方式
也可以基于Java代码来修改日志级别,先声明一个类,然后声明一个Logger.Level的对象:
第一步设置在任意类作为配置类,以bean方式注入配置
//feign 配置类的第二种方式
public class Defaultconfig {
@Bean
public Logger.Level getLogLevel(){
return Logger.Level.BASIC;
}
}
2.拥有配置以后
对服务进行配置
在feign客户端注解添加
@FeignClient(value = "userservice",configuration = Defaultconfig.class)//服务名
全局添加配置
@EnableFeignClients(defaultConfiguration = Defaultconfig.class)
2.3.Feign使用优化
Feign底层发起http请求,依赖于其它的框架。其底层客户端实现包括:
•URLConnection:默认实现,不支持连接池
•Apache HttpClient :支持连接池
•OKHttp:支持连接池
因此提高Feign的性能主要手段就是使用连接池代替默认的URLConnection。
这里我们用Apache的HttpClient来演示。
1)引入依赖
在order-service(服务消费者)的pom文件中引入Apache的HttpClient依赖:
<!--httpClient的依赖 -->
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-httpclient</artifactId>
</dependency>
2)配置连接池
在order-service的application.yml中添加配置:
feign:
client:
config:
default: # default全局的配置
loggerLevel: BASIC # 日志级别,BASIC就是基本的请求和响应信息
httpclient:
enabled: true # 开启feign对HttpClient的支持
max-connections: 200 # 最大的连接数
max-connections-per-route: 50 # 每个路径的最大连接数
接下来,在FeignClientFactoryBean中的loadBalance方法中打断点:
Debug方式启动order-service服务,可以看到这里的client,底层就是Apache HttpClient:
总结,Feign的优化:
1.日志级别尽量用basic
2.使用HttpClient或OKHttp代替URLConnection
① 引入feign-httpClient依赖
② 配置文件开启httpClient功能,设置连接池参数
最佳使用
在实际开发中,我们可以通过抽取Feign相关的代码模块,将其封装成单独的模块,以便在其他项目中进行复用。这样可以提高代码的可复用性和维护性,并且方便团队协作。
以下是在实际项目中抽取Feign模块的一些步骤和示例:
创建一个独立的Maven项目
在Maven项目中,我们可以将所有Feign相关的代码和配置文件都放在一个独立的模块中。我们可以在项目中添加以下三个主要的依赖:
spring-cloud-starter-openfeign:包含了Spring Cloud对Feign的支持,以及必要的依赖项。
spring-boot-starter-web:用于支持Web项目。
spring-boot-starter-test:用于支持单元测试。
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
</dependencies>
定义Feign接口
在项目中定义Feign接口,以及对应的请求URL和请求参数。示例如下:
(实在分布式中,可以不用url)
@FeignClient(name = "example-service", url = "http://localhost:8080")
public interface ExampleServiceFeignClient {
@GetMapping("/example")
String getExample();
}
定义Feign配置类
在项目中定义Feign的配置类,其中可以设置连接超时时间、读取超时时间、重试次数等参数。示例如下:
@Configuration
public class FeignConfig {
@Bean
public Request.Options options() {
return new Request.Options(5000, 10000);
}
@Bean
public Retryer retryer() {
return new Retryer.Default();
}
}
在应用程序中使用Feign
我们可以在其他应用程序中使用我们创建的Feign模块。在其他应用程序中添加以下依赖项:
<dependency>
<groupId>com.example</groupId>
<artifactId>example-feign</artifactId>
<version>1.0.0</version>
</dependency>
然后,我们可以像下面这样使用Feign:
@RestController
public class ExampleController {
private final ExampleServiceFeignClient exampleServiceFeignClient;
public ExampleController(ExampleServiceFeignClient exampleServiceFeignClient) {
this.exampleServiceFeignClient = exampleServiceFeignClient;
}
@GetMapping("/example")
public String getExample() {
return exampleServiceFeignClient.getExample();
}
}
通过以上步骤,我们可以将Feign相关代码模块独立出来,方便在其他项目中进行复用。
实体类转换
在使用Feign进行远程服务调用时,通常情况下会定义一个接口来描述远程服务,并使用@FeignClient注解来声明该接口是一个Feign客户端。该接口中定义的方法描述了远程服务的请求方式、请求参数和返回值类型等信息。
在封装Feign代码模块时,可以针对不同的远程服务定义不同的Feign客户端接口,每个接口中定义的返回值类型可能不同。在将Feign客户端集成到其他项目中时,我们可能需要将远程服务返回的实体类转换为本地项目中的实体类,以方便数据的处理。这时,可以使用Java中的对象映射库来进行实体类之间的转换。
常用的对象映射库包括Dozer、MapStruct、ModelMapper等。这些库可以将一个实体类映射为另一个实体类,可以支持属性之间的复杂转换。以MapStruct为例,可以定义一个名为“Mapper”的接口,用于定义实体类之间的映射关系,如下所示:
@Mapper
public interface UserMapper {
UserDTO toUserDTO(User user);
}
在这个示例中,UserMapper接口使用@Mapper注解标识,并定义了一个方法toUserDTO,用于将User实体类映射为UserDTO实体类。在其他项目中使用Feign客户端调用远程服务时,可以先将返回的实体类通过UserMapper进行转换,然后再进行处理。
@Autowired
private UserMapper userMapper;
public void getUser(Long userId) {
User user = userFeignClient.getUser(userId);
UserDTO userDTO = userMapper.toUserDTO(user);
// 处理UserDTO对象
}
通过使用对象映射库,我们可以方便地将远程服务返回的实体类转换为本地项目中的实体类,减少系统之间的耦合,增加代码可读性和可维护性。