目录
- 前言
- SpringCloud Feign远程服务调用
-
- 一.远程调用逻辑图
- 二.两个服务的yml配置和访问路径
- 三.使用RestTemplate远程调用
- 四.构建Feign
- 五.自定义Feign配置
- 六.Feign配置日志
- 七.Feign调优
- 八.抽离Feign
前言
微服务分解成多个不同的服务,那么多个服务之间怎么调用呢?
Spring Cloud最新面试题
Spring Cloud Nacos详解之注册中心
Spring Cloud Nacos详解之配置中心
Spring Cloud Nacos详解之集群配置
Spring Cloud Eureka详解
Spring Cloud Ribbon详解
Spring Cloud Gateway详解
Spring Cloud Hystrix详解
SpringCloud Feign远程服务调用
一.远程调用逻辑图
现在有两个服务,订单服务(服务消费者)和用户服务(服务提供者),分别对应不同的数据库。
注意: 因为远程调用都是服务消费者调用服务提供者,所以配置和业务编写,都在服务消费者里面。
二.两个服务的yml配置和访问路径
用两个不同的数据库,模拟部署在两台服务器的数据库
订单yml配置 访问路径:@GetMapping("order/{orderId}")
server:
port: 8082
spring:
datasource:
url: jdbc:mysql://localhost:3306/cloud_order?useSSL=false
username: root
password: root
driver-class-name: com.mysql.jdbc.Driver
application:
name: orderservice //订单服务的名称
用户yml配置 访问路径:@GetMapping("user/{id}")
server:
port: 8081
spring:
datasource:
url: jdbc:mysql://localhost:3306/cloud_user?useSSL=false
username: root
password: root
driver-class-name: com.mysql.jdbc.Driver
application:
name: userservice //用户服务的名称
三.使用RestTemplate远程调用
1.注入RestTemplate
/**
* 因为启动类本身也是一个配置了,所以我们在启动类进行注入,你自己自定义配置类注入也行
* 创建RestTemplate并注入Spring容器
*/
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
2.编写远程调用
@Autowired
private RestTemplate restTemplate;
public Order queryOrderById(Long orderId) {
// 根据订单id查询订单
Order order = orderMapper.findById(orderId);
// 利用RestTemplate发起http请求,根据用户id查询用户
// url路径 http://服务名称(上面配置了)/请求路径/参数
String url = "http://localhost:8081/user/" + order.getUserId();
// 发送http请求,实现远程调用,现在是get请求类型
User user = restTemplate.getForObject(url, User.class);
// 封装user到Order
order.setUser(user);
// 返回值
return order;
}
3.RestTemplate的缺点
- 参数复杂URL难以维护。
- 不符合正常接口调用的格式。
四.构建Feign
1.引入依赖
<!--feign客户端依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
2.在启动类使用注解开启Feign功能
@SpringBootApplication
@EnableFeignClients
public class OrderApplication {
}
3.编写远程调用
//服务名称
@FeignClient(value = "userservice")
public interface UserClient {
@GetMapping("/user/{id}")
User findById(@PathVariable("id") Long id);
}
4.调用接口
@Autowired
private UserClient userClient;
public Order queryOrderById(Long orderId) {
// 根据订单id查询订单
Order order = orderMapper.findById(orderId);
// 用Feign远程调用
User user = userClient.findById(order.getUserId());
// 封装user到Order
order.setUser(user);
// 返回
return order;
}
5.Feign还集成了Ribbon,所以我们不用考虑负载均衡问题
五.自定义Feign配置
类型 | 作用 | 说明 |
---|---|---|
feign.Logger.Level | 修改日志级别 | 四种不同的级别:NONE(没有任何日志)、BASIC(发起请求的开始结束时间)、HEADERS(会记录请求头请求体)、FULL(请求和响应信息) |
feign.codec.Decoder | 响应结果的解析器 | http远程调用的结果做解析,例如解析json字符串为java对象 |
feign.codec.Encoder | 请求参数编码 | 将请求参数编码,便于通过http请求发送 |
feign. Contract | 支持的注解格式 | 默认是SpringMVC的注解 |
feign. Retryer | 失败重试机制 | 请求失败的重试机制,默认是没有,不过会使用Ribbon的重试 |
一般我们自定义配置的是日志
六.Feign配置日志
1.配置文件配置日志
//全局配置
feign:
client:
config:
default://default全局配置,远程调用的服务的接口也会打印。
loggerLevel:FULL //日志级别
//局部配置
feign:
client:
config:
orderservice://只打印服务名为orderservice的日志。
loggerLevel:FULL //日志级别
2.代码方式配置日志
//第一步:注入对象
public class DefaultFeignConfiguration {
@Bean
public Logger.Level logLevel(){
//日志级别
return Logger.Level.BASIC;
}
}
//第二步:注解配置
//全局配置
@EnableFeignClients(defaultConfiguration = DefaultFeignConfiguration.class)
//局部配置
@FeignClient(value = "userservice",confiquration = FeignClientConfiguration.class)
七.Feign调优
Feign底层客户端实现:
- URLConnection:默认实现,不支持连接池。
- Apache HttpClient: 支持连接池。
- OKHttp:支持连接池。
1.使用连接池替代默认的URL Connection(使用HttpClient支持)
①pom文件引入依赖
<!--引入HttpClient依赖-->
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-httpclient</artifactId>
</dependency>
②yml文件进行配置
feign:
httpclient:
enabled: true # 支持HttpClient的开关
max-connections: 100 # 最大连接数
max-connections-per-route: 25 # 单个路径的最大连接数
2.日志级别最好是basic或none
八.抽离Feign
如果A、B、C服务都要调用D服务,那我们要在A、B、C里面都使用Feign调用D吗,如果写就造成了很多代码冗余,所以我们要把Feign抽离出来放在一个公共的服务里面。我们新建一个Feign-api服务,然后谁用谁就在pom文件引入一下。
注意: 需要把调用的接口(加上@FeignClient(vaule=服务名)
注解)和实体类写在公共的服务里面。
Feign-api结构目录
<!--谁用谁引入,引入feign的统一api-->
<dependency>
<groupId>cn.xinxin.demo</groupId>
<artifactId>feign-api</artifactId>
<version>1.0</version>
</dependency>
但是这样做会导致SpringBootApplication在扫描包时找不到定义FeignClient对象,那么怎么解决呢?
解决
方式一:指定FeignClient所在包
@EnableFeignClients(basePackages = "cn.xinxin.feign.clients")
方式二:指定FeignClient字节码
EnableFeignClients(clients = {UserClient.class})
Feign的pom文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>cloud-demo</artifactId>
<groupId>cn.xinxin.demo</groupId>
<version>1.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>feign-api</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<!--feign客户端依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
</dependencies>
</project>
示例: 订单服务远程调用用户服务