一、实现目标
module[user-service]和modul[order-service]由RestTemplate通信改成用OpenFeign通信
二、Spring Cloud OpenFeign简介
Spring Cloud OpenFeign是一种声明式、模板化的HTTP客户端。在Spring Cloud中使用OpenFeign,可以做到使用HTTP请求访问远程服务,就像调用本地方法一样的,开发者完全感知不到这是在调用远程方法,更感知不到在访问HTTP请求。
三、项目[ac-mall-cloud]接入OpenFeign
3.1 父级工程配置
在父级工程 [ac-mall-cloud] pom.xml中引入OpenFeign依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>${alibaba.cloud.version}</version>
</dependency>
父级工程 [ac-mall-cloud] pom.xml全配置如下
<?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">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>ac-mall-cloud</artifactId>
<version>1.0-SNAPSHOT</version>
<modules>
<module>user-service</module>
<module>product-service</module>
<module>order-service</module>
</modules>
<packaging>pom</packaging>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Greenwich.SR1</spring-cloud.version>
<mysql.version>8.0.17</mysql.version>
<mybatis.plus.version>3.2.0</mybatis.plus.version>
<druid.version>1.1.10</druid.version>
<boot.version>2.2.4.RELEASE</boot.version>
<alibaba.cloud.version>2.1.0.RELEASE</alibaba.cloud.version>
<lombok.version>1.18.10</lombok.version>
</properties>
<!-- 管理子类所有的jar包的版本,这样的目的是方便去统一升级和维护 -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>${boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${alibaba.cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<version>${alibaba.cloud.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>${alibaba.cloud.version}</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis.plus.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>${druid.version}</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
<scope>provided</scope>
</dependency>
</dependencies>
</dependencyManagement>
<!-- 所有的子工程都会自动加入下面的依赖 -->
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.10</version>
<scope>provided</scope>
</dependency>
</dependencies>
<!-- SpringBoot 工程编译打包的插件,放在父pom中就直接给所有子工程继承 -->
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
3.2 module [user-service] 配置
1、新建一个constant包放常量类,并新建ModulePrePath类
public class ModulePrePath {
public static final String API = "api";
}
2、新建一个api包,放供其他微服务调用接口的类,并新建UserApi类
/**
* @author Alan Chen
* @description 用户API
* @date 2021/3/31
*/
@RestController
@RequestMapping(ModulePrePath.API+"/users")
public class UserApi {
@Autowired
IUserService userService;
@GetMapping("/{userId}")
public User getUser(@PathVariable String userId){
return userService.getUser(userId);
}
}
module [user-service] 效果如下
3、测试UserApi接口
重启module [user-service] ,在浏览器中输入访问地址http://127.0.0.1:8010/api/users/1
,效果如下
4、api包和controller包说明
controller包里放的是供本项目前端客户端(如APP)访问的接口;api包里的接口,是专门提供给其他微服务访问的。
3.3 module [order-service] 配置
1、在module [order-service]pom.xml中引入OpenFeign依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
module [order-service]pom.xml全配置如下
<?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>ac-mall-cloud</artifactId>
<groupId>org.example</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>org.ac</groupId>
<artifactId>order-service</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
</dependencies>
</project>
2、主启动类加上注解@EnableFeignClients注解
@EnableFeignClients
申明该项目是Feign客户端,扫描对应的feign client
@EnableFeignClients
@SpringBootApplication
public class OrderApplication {
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class);
}
@Bean
@LoadBalanced // Ribbon负载均衡注解
RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
3、新建一个constant包放常量类,并新建ModulePrePath类
public class ModulePrePath {
public static final String API = "api";
}
4、新建一个feign包放feign接口,并新建UserServiceClient接口
@FeignClient("user-service")
public interface UserServiceClient {
/**
* 获取用户信息
* @param userId
* @return
*/
@GetMapping(ModulePrePath.API+"/users/{userId}")
UserDto getUser(@PathVariable("userId") String userId);
}
@FeignClient("user-service")
配置的是module[user-service]的服务名
@GetMapping(ModulePrePath.API+"/users/{userId}")
路径和module[user-service]中UserApi获取用户信息接口的路径是一致的。
5、将RestTemplate通信改成用OpenFeign通信
修改OrderServiceImpl类的makeOrder方法
// 1、根据用户ID调用用户服务接口数据,查询用户的名字
//UserDto userDto = restTemplate.getForObject(USER_SERVICE_URL,UserDto.class,userId);
//换成OpenFeign
UserDto userDto = userServiceClient.getUser(userId);
OrderServiceImpl全部代码如下
@Service
public class OrderServiceImpl implements IOrderService {
@Autowired
OrderDao orderDao;
@Autowired
RestTemplate restTemplate;
@Autowired
UserServiceClient userServiceClient;order-service
//final static String USER_SERVICE_URL="http://127.0.0.1:8010/users/{userId}";
final static String USER_SERVICE_URL="http://user-service/users/{userId}"; //用服务名来替换IP
public Order makeOrder(String productId, String userId) {
/**
* RestTemplate是java创造出来的,在java能够访问到网络资源的包是java.net.URLConnenction/Socket
* RestTemplate是对URLConnenction的封装
* apache--HttpClient 也是对URLConnenction/HttpURLConnenction的封装
* oKHttp 也封装了URLConnenction
* netty/rpc/grpc/thirt/tomcat
*/
// 1、根据用户ID调用用户服务接口数据,查询用户的名字
//UserDto userDto = restTemplate.getForObject(USER_SERVICE_URL,UserDto.class,userId);
//换成OpenFeign
UserDto userDto = userServiceClient.getUser(userId);
String userName=userDto.getUserName();
// 2、生成订单
Order order = new Order();
order.setId(UUID.randomUUID().toString());
order.setCreateTime(new Date());
order.setPriceFen(1600L);
order.setUserId(userId);
order.setUserName(userName);
order.setProductId(productId);
order.setOrderNo(UUID.randomUUID().toString());
// 3、保存数据库
orderDao.insert(order);
return order;
}
}
6、测试下单接口
我们在浏览器中输入访问地址http://127.0.0.1:8020/orders/1/1
,效果如下
四、Ribbon、Feign、LoadBalancer和OpenFeign的关系
4.1 Ribbon
- Ribbon 是 Netflix开源的基于HTTP和TCP等协议负载均衡组件
- Ribbon 可以用来做客户端负载均衡,调用注册中心的服务
- Ribbon的使用需要代码里手动调用目标服务
- RestTemplate集成 Ribbon,可以让RestTemplate通过服务名来访问微服务接口(否则只能通过IP访问)
4.2 Feign
- Feign是Spring Cloud组件中的一个轻量级RESTful的HTTP服务客户端
- Feign内置了Ribbon,用来做客户端负载均衡,去调用服务注册中心的服务。
- Feign的使用方式是:使用Feign的注解定义接口,调用这个接口,就可以调用服务注册中心的服务
- Feign本身不支持Spring MVC的注解,它有一套自己的注解。
4.3 OpenFeign
- OpenFeign是Spring Cloud 在Feign的基础上支持了Spring MVC的注解,如
@RequesMapping
等等。 - OpenFeign的
@FeignClient
可以解析SpringMVC的@RequestMapping
注解下的接口,并通过动态代理的方式产生实现类,实现类中做负载均衡并调用其他服务。
4.4 LoadBalancer
Ribbon目前已经停止维护,新版SpringCloud(2021.x.x)用LoadBalancer替代了Ribbon。Spring Cloud全家桶在Spring Cloud Commons项目中,添加了Spring cloud Loadbalancer作为新的负载均衡器,并且做了兼容
Nacos 2021版本已经没有自带ribbon的整合,所以无法通过修改Ribbon负载均衡的模式来实现nacos提供的负载均衡模式,需要引入另一个支持的jar包loadbalancer。
4.5 Ribbon/LoadBalancer载均衡策略
Ribbon有多种负载均衡策略
- 随机 RandomRule
- 轮询 RoundRobinRule
- 重试 RetryRule
- 最低并发 BestAvailableRule
- 可用过滤 AvailabilityFilteringRule
- 响应时间加权重 ResponseTimeWeightedRule
- 区域权重 ZoneAvoidanceRule
LoadBalancer貌似只提供了两种负载均衡器,不指定的时候默认用的是轮询
- RandomLoadBalancer 随机
- RoundRobinLoadBalancer 轮询