前言
SpringCloud微服务框架中,我们实现了服务之间的低耦合性,将多个服务的单机系统拆分成多个独立的微服务。
在微服务中,我们将单机架构的服务拆分成多个单独的服务模块,但不可避免的,微服务之间并不是完全独立,隔离开来的,有时服务之间也需要互相进行调用,但我们如何调用呢?
远程调用的概念
以订单服务+商品服务为例,单机架构的商城服务将订单查询功能与商品信息查询功能放在一个项目中,共用一个数据库和配置文件,同时,订单服务想通过商品id查询商品信息时,可以直接调用商品服务的service层,虽然操作简单,但是两个服务间的耦合性太高,代码之间的联系千丝万缕,假若我们的某个服务出错,就会导致整个商城服务崩溃,因此我们需要将这个大服务拆分成小的服务,当订单服务需要调用商品服务时,通过远程调用,根据商品服务提供的地址查询信息。
传统远程调用缺点
- 人工运维成本高,效率低
- 稳定性差,代码修改频繁
- 代码耦合性高,无法做到真正的解耦
还是以订单服务调用商品服务为例,传统的远程调用是直接在订单服务中将商品服务的服务名称和地址信息固定写死,这个前提就是我们得需要知道商品服务的信息才能进行调用,除了需要知道商品服务的地址信息之外,如果商品服务将其服务名称和地址信息修改了,那我们订单服务调用就会报错,需要重新修改代码信息,并重新打包部署,大大增加了我们的运维成本,同时,若是这时有用户需要该服务,就需要等待服务重新上线,严重影响了用户的体验。
OpenFeign简单介绍
OpenFeign是alibaba旗下的一款SpringCloud组件,基于负载均衡和服务注册的基础上,OpenFeign可以从实时Naocs配置中心上获取被调用服务注册的信息,例如服务名称,服务地址等,通过这些信息,可以取消我们的代码写死,在服务信息修改的时候,Nacos配置中心也会实时更新服务信息,从而让调用方动态调用服务。
因此,实现OpenFeign之前还需要实现配置中心功能(例如consul,nacos)和负载均衡(例如LoadBalanced)。
基于订单服务实现远程调用(快速实现)
注:案例代码可在gitee仓库获取,链接:https://gitee.com/xiaozhang-aixue-programming/example-git
- 添加依赖
SpringBoot集成了OpenFeign的实现,我们只需要在项目中添加OpenFeign的依赖即可使用。
XML
<!--Openfeign的依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
注:OpenFeign只需添加在服务调用方,也就是orderService的依赖中即可
- 修改配置
orderService作为调用方,虽然openFeign不需要添加额外配置,但是需要调用方实现服务发现(此处以注册在nacos上为例)和负载均衡,被调用方需要实现服务注册
-
orderService:
#服务端口号
server:
port: 8080spring:
#mysql数据库配置
datasource:
url: jdbc:mysql://127.0.0.1:3306/cloud_order?characterEncoding=utf8&useSSL=false
username: root
password: 070609
driver-class-name: com.mysql.cj.jdbc.Driver服务名,对应Nacos的DataId
application: name: order-service cloud: nacos: # 全局nacos地址,【只写这里一处】,config和discovery共用 server-addr: 121.199.35.34:10020 # 服务注册发现 discovery: # Nacos负载均衡 loadbalancer: nacos: enabled: truemybatis:
configuration: # 配置打印 MyBatis 执行的 SQL
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
map-underscore-to-camel-case: true #自动驼峰转换 -
productService(配置项参考上文)
server:
port: 9090
spring:
datasource:
url: jdbc:mysql://127.0.0.1:3306/cloud_product?characterEncoding=utf8&useSSL=false
username: root
password: 070609
driver-class-name: com.mysql.cj.jdbc.Driver
application:
name: product-service
cloud:
nacos:
discovery:
server-addr: 121.199.35.34:10020
loadbalancer:
nacos:
enabled: true
mybatis:
configuration: # 配置打印 MyBatis 执行的 SQL
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
map-underscore-to-camel-case: true #自动驼峰转换 -
添加@EnableFeignClients,启动feign服务
在服务调用方-orderService的启动类上添加注解@EnableFeignClients,启动OpenFeign
@SpringBootApplication
@EnableFeignClients
public class OrderDemoApplication {
public static void main(String[] args) {
ApplicationContext context = SpringApplication.run(OrderDemoApplication.class, args);
}
}
- 创建接口
创建api调用接口,基于Spring MVC来声明feign客户端的信息
@FeignClient(value = "product-service", path = "/product")
public interface ProductApi {
@RequestMapping("/{productId}")
ProductInfo getProductById(@PathVariable("productId") Integer productId);
}
@FeignClient(value = "product-service", path = "/product")中,value声明的是接口提供方的服务名称,和productService注册在nacos上的服务名称对应,path声明是productService的接口的路径,根据这个可映射到productService的接口上
- 修改远程调用代码
将原本使用restTemple的代码修改成根据接口映射调用
java
@Service
public class orderService {
@Autowired
orderMapper orderMapper;
// @Autowired
// RestTemplate restTemplate;
//注入productApi
@Autowired
ProductApi productApi;
public orderInfo getOrderInfo(Integer id){
orderInfo orderInfo = orderMapper.selectOrderInfoById(id);
// String url="http://product-service/product/"+orderInfo.getProductId();
// productInfo productInfo = restTemplate.getForObject(url, productInfo.class);
productInfo productInfo = productApi.getProductInfoById(orderInfo.getProductId());
orderInfo.setProductInfo(productInfo);
return orderInfo;
}
}
- 测试
启动服务,使用浏览器或是postman访问u接口,获取productInfo信息

总结
- 使用OpenFeign可以从配置中心上获取服务的信息,动态获取服务的地址和其他信息,从而实现两个服务之间的调用;
- 使用OpenFeign服务必须注册在配置中心上,否则无法拉取服务信息,也不能进行地址的映射;
- 服务调用方必须在启动类上添加@EnableFeignClients注解,启动Feign功能,同时也需要绑定服务提供方的信息;