RPC简述
RPC 全称是 Remote Procedure Call ,即远程过程调用,其对应的是我们的本地调用。RPC 的目的是:让我们调用远程方法像调用本地方法一样。
我们使用RPC远程框架的话只需要关心客户端和服务端即可,不需要去关注RPC调用过程,那一块会由框架来为我们实现。
框架实现过程:
1、Client远程调用的话,会根据对象进行远程通信并进行编码,然后传输给服务端,服务端收到后进行解码后通过反射进行本地调用。
2、服务器调用本地方法后查询到结果后,按照第一步的流程返回给客户端。
Spring Cloud Feign
1)什么是Feign
Feign是Netflix开发的声明式、模板化的HTTP客户端,Feign可帮助我们更加便捷、优雅地调用HTTP API。
Feign可以做到使用 HTTP 请求远程服务时就像调用本地方法一样的体验,开发者完全感知不到这是远程方法,更感知不到这是个 HTTP 请求。不需要通过常规的 Http Client 构造请求再解析返回数据。
它解决了让开发者调用远程接口就跟调用本地方法一样,无需关注与远程的交互细节,更无需关注分布式环境开发。但实际上,Feign 底层也是基于HTTP协议进行请求访问的,只是框架帮我们都封装好了。
2)Feign和Open Feign的区别
Spring Cloud openfeign对Feign进行了增强,使其支持Spring MVC注解并可以携带很多参数,另外还整合了Ribbon和Eureka,让open Feign支持负载均衡。
3)Feign架构设计
4)Spring Cloud Alibaba 整合Feign
1.引入Open Feign依赖
1 2 3 4 5
| <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency>
|
2.订单服务controller
订单服务controller
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| package com.javaxing.xingmallorder.contrller;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController;
@RequestMapping("/order") @RestController public class OrderContrller { @GetMapping("/queryOrder") public String queryOrder(@RequestParam("orderId") Integer orderId){ System.out.println("成功请求到该接口"); return "Query Success"+orderId; } }
|
3.用户服务端编写feign接口
1 2 3 4 5 6 7 8 9 10 11
| package com.javaxing.springbootnacos.api;
import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam;
@FeignClient(value = "xingmall-order",path = "/order") public interface OrderFeignService { @GetMapping("/queryOrder") public String queryOrder(@RequestParam("orderId") Integer orderId); }
|
4.在服务端启动上类添加注解
1 2 3 4 5 6 7 8
| @SpringBootApplication @EnableFeignClients public class MallUserApplication {
public static void main(String[] args) { SpringApplication.run(MallUserApplication.class, args); } }
|
5.用户服务controller(调用方)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| package com.javaxing.springbootnacos.contrller;
import com.javaxing.springbootnacos.api.OrderFeignService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController;
@RestController public class UserContrller { @Autowired private OrderFeignService orderFeignService;
@RequestMapping(value = "/testRibbon") public String testRibbon() { String result = orderFeignService.queryOrder(10010); return result; } }
|
6.调用测试
Spring Cloud Feign扩展
Feign 提供了很多的扩展机制,可以让我们灵活的使用,如日志配置、拦截器扩展等。
1)日志配置
我们可以通过日志扩展,让每次的Feign请求信息都打印出来,便于我们在开发和生产环境的过程中更好的去定位BUG。
- NONE【性能最佳,适用于生产】:不记录任何日志(默认值)。
- BASIC【适用于生产环境追踪问题】:仅记录请求方法、URL、响应状态代码以及执行时间。
- HEADERS:记录BASIC级别的基础上,记录请求和响应的header。
- FULL【比较适用于开发及测试环境定位问题】:记录请求和响应的header、body和元数据。
本次实验源码地址:https://files.javaxing.com/nacos/NacosServer.zip
1.1 代码全局配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| package com.javaxing.springbootnacos.config;
import feign.Logger; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration;
@Configuration public class FeignConfig {
@Bean public Logger.Level feignLoggerLevel() { return Logger.Level.FULL; } }
|
application.yaml
1 2 3
| logging: level: com.javaxing.springbootnacos.api.OrderFeignService: debug
|
不论是全局还是局部,在yml配置文件中配置 Client 的日志级别才能正常输出日志
1.2 yaml配置方式
局部配置有2种,1种是在feignClass文件里面配置,1种是在application.yaml文件里面配置。
1.2.1 class文件配置
feignConfig文件配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| package com.javaxing.springbootnacos.config;
import feign.Logger; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration;
public class FeignConfig {
@Bean public Logger.Level feignLoggerLevel() { return Logger.Level.FULL; } }
|
如果需要局部配置,这里的@Configuration 就不可以加,如果加了就会变成全局日志
Feign接口
1 2 3 4 5
| @FeignClient(value = "xingmall-order",path = "/order",configuration = FeignConfig.class) public interface OrderFeignService { @GetMapping("/queryOrder") public String queryOrder(@RequestParam("orderId") Integer orderId); }
|
在feignClient注解之上增加feignConfig.class的路径,设置这个feign接口使用的配置
application.yaml 开放日志
1 2 3
| logging: level: com.javaxing.springbootnacos.api.OrderFeignService: debug
|
不论是全局还是局部,在yml配置文件中配置 Client 的日志级别才能正常输出日志
1.2.2 application.yaml配置
1 2 3 4 5 6 7 8
| logging: level: com.javaxing.springbootnacos.api.OrderFeignService: debug feign: client: config: xingmall-order: loggerLevel: FULL
|
1、不论是全局还是局部,在yml配置文件中配置 Client 的日志级别才能正常输出日志
2、feign.client.config 配置对应的微服务的日志输出,模式为FULL 全部输出
Feign 接口
1 2 3 4 5 6 7 8 9 10 11
| package com.javaxing.springbootnacos.api;
import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam;
@FeignClient(value = "xingmall-order",path = "/order") public interface OrderFeignService { @GetMapping("/queryOrder") public String queryOrder(@RequestParam("orderId") Integer orderId); }
|
1.3 日志配置测试
2)通过拦截器实现参数传递
通常情况下,我们的接口都是有做权限校验的,而我们在通过open feign去调用微服务时,可以通过拦截器来实现参数传递。
2.1 Feign是如何实现拦截的
每次 feign 发起http请求调用之前,会去执行拦截器中的逻辑。
1 2 3 4 5 6 7 8 9 10
|
package feign;
public interface RequestInterceptor { void apply(RequestTemplate var1); }
|
2.2 Feign调用时配置Basic 认证
1 2 3 4 5 6 7
| @Configuration public class FeignConfig { @Bean public BasicAuthRequestInterceptor basicAuthRequestInterceptor() { return new BasicAuthRequestInterceptor("admin", "123456"); } }
|
官方源码实现
2.3 自定义拦截器实现认证逻辑
2.3.1 全局拦截器
自定义Interceptor拦截器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| package com.javaxing.springbootnacos.Interceptor;
import feign.RequestInterceptor; import feign.RequestTemplate;
import java.util.UUID;
public class FeignAuthRequestInterceptor implements RequestInterceptor { @Override public void apply(RequestTemplate template) { String access_token = UUID.randomUUID().toString(); template.header("Authorization",access_token); template.header("AppVersion","1"); } }
|
FeignConfig配置文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| package com.javaxing.springbootnacos.config;
import com.javaxing.springbootnacos.Interceptor.FeignAuthRequestInterceptor; import feign.Logger; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration;
@Configuration public class FeignConfig { @Bean public Logger.Level feignLoggerLevel() { return Logger.Level.FULL; }
@Bean public FeignAuthRequestInterceptor feignAuthRequestInterceptor(){ return new FeignAuthRequestInterceptor(); } }
|
验证拦截实验
我们在user上去通过open feign调用order上的服务,可以看到user微服务打印的日志中,携带了header头。
2.3.2 yaml拦截(微服务拦截)
有时候我们不需要全局拦截,我们只针对某个微服务的feign调用进行自定义拦截,就可以使用 局部拦截。
application.yaml
1 2 3 4 5 6
| feign: client: config: xingmall-order: requestInterceptors[0]: com.javaxing.springbootnacos.Interceptor.FeignAuthRequestInterceptor
|
Interceptor自定义拦截器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| package com.javaxing.springbootnacos.Interceptor;
import feign.RequestInterceptor; import feign.RequestTemplate;
import java.util.UUID;
public class FeignAuthRequestInterceptor implements RequestInterceptor { @Override public void apply(RequestTemplate template) { String access_token = UUID.randomUUID().toString(); template.header("Authorization",access_token); template.header("AppVersion","1"); } }
|
验证结果
3)超时时间配置
通过 Options模块可以配置Feign调用时的连接超时时间和读取超时时间。
配置的方式有2种:1、全局配置 2、针对单独微服务配置
2.1 代码全局配置方式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| package com.javaxing.springbootnacos.config;
import feign.Logger; import feign.Request; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration;
@Configuration public class FeignConfig { @Bean public Logger.Level feignLoggerLevel() { return Logger.Level.FULL; } @Bean public Request.Options options() { return new Request.Options(5000, 10000); } }
|
第一个参数:连接的超时时间(ms),默认值是 2s
第二个参数:请求处理的超时时间(ms),默认值是 5s
2.2 yaml配置
1 2 3 4 5 6 7 8
| feign: client: config: xingmall-order: connectTimeout: 5000 readTimeout: 10000
|
局部配置的话,需要指定要用到的微服务名称和超时时间
2.3 验证方案
订单服务controller
订单服务模拟休眠20秒,这样用户服务来调用订单服务时,肯定会出现 超时的情况。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| package com.javaxing.xingmallorder.contrller;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController;
@RequestMapping("/order") @RestController public class OrderContrller { @GetMapping("/queryOrder") public String queryOrder(@RequestParam("orderId") Integer orderId) throws InterruptedException { Thread.sleep(20000); System.out.println("成功请求到该接口"); return "Query Success"+orderId; } }
|
用户服务controller
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| package com.javaxing.springbootnacos.contrller;
import com.javaxing.springbootnacos.api.OrderFeignService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController;
@RestController public class UserContrller { @Autowired private OrderFeignService orderFeignService;
@RequestMapping(value = "/testRibbon") public String testRibbon() { String result = orderFeignService.queryOrder(10010); return result; } }
|
因为我们设定的超时时间为10秒,而我们让订单服务休眠20秒,用户服务在调用的过程中肯定会超时的。
在生产环境中,如果是重要的数据请求,如订单下单,如果请求超时了,一定要做一个降级熔断处理。
4)优化 - Feign请求组件
Feign 中默认使用 JDK 原生的 URLConnection 发送 HTTP 请求,无法复用连接池等机制,为此我们可以替换成其他的组件。如,Apache HttpClient,OkHttp。
4.1 Apache HttpClient
4.1.1 引入依赖
1 2 3 4 5 6 7 8 9 10 11
| <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.5.7</version> </dependency> <dependency> <groupId>io.github.openfeign</groupId> <artifactId>feign-httpclient</artifactId> <version>10.1.0</version> </dependency>
|
4.1.2 启用Apache 组件
1 2 3 4
| feign: httpclient: enabled: true
|
使用 Apache HttpClient 可以忽略,默认开启
4.2 OKHttp
4.2.1 引入依赖
1 2 3 4
| <dependency> <groupId>io.github.openfeign</groupId> <artifactId>feign-okhttp</artifactId> </dependency>
|
4.2.2 启用OKHttp组件
1 2 3 4 5
| feign: httpclient: enabled: false okhttp: enabled: true
|
在配置文件里面设置:禁用httpClient,启用okHttp
5)优化 - GZIP压缩Feign请求
开启GZIP压缩后可以有效节约网络资源,提升接口性能。
开启方法
1 2 3 4 5 6 7 8 9 10 11
| feign: compression: request: enabled: true mime-types: text/xml,application/xml,application/json min-request-size: 2048 response: enabled: true
|
注意:只有在feign的Http Client不是OKHttp的情况下才会进行GZIP压缩,如果使用OKHttp的话,GZIP是不会生效的。
OKHttp无法使用的原因
@ConditionalOnMissingBean(type = {“okhttp3.OkHttpClient”}) 表示Spring BeanFactory初始化时,匹配到这个bean条件时,不启用GZIP,换言之,你用的OKHttp 就无法使用GZIP压缩
6)优化 - 编码器解码器配置
Feign 中提供了自定义的编码解码器设置,同时也提供了多种编码器的实现,比如 Gson、Jaxb、Jackson。我们可以用不同的编码解码器来处理数据的传输。如果你想传输 XML 格式的数据,可以自定义 XML 编码解码器来实现获取使用官方提供的 Jaxb。
源码扩展点:Encoder & Decoder
1 2 3 4 5 6
| public interface Encoder { void encode(Object object, Type bodyType, RequestTemplate template) throws EncodeException; } public interface Decoder { Object decode(Response response, Type type) throws IOException, DecodeException, FeignException; }
|
6.1 代码全局配置方式
配置编码解码器只需要在 Feign 的配置类中注册 Decoder 和 Encoder 这两个类即可:
1 2 3 4 5 6 7 8
| @Bean public Decoder decoder() { return new JacksonDecoder(); } @Bean public Encoder encoder() { return new JacksonEncoder(); }
|
6.2 yaml配置
1 2 3 4 5 6 7
| feign: client: config: xingmall-order: encoder: feign.jackson.JacksonEncoder decoder: feign.jackson.JacksonDecoder
|
Spring Cloud 整合Dubbo
本次实验源码地址:https://files.javaxing.com/nacos/NacosServer.zip
1)项目环境介绍
本次整合的实验项目,一共有2个模块,消费者和生产者,而消费者和生产者 各自有一个api和biz模块,api模块存放的是对外开放的方法和实体类,biz则是具体的实现方法。
本次实验,消费者和生产者都要引入dubbo依赖,而生产者则在api模块开放service接口,消费者引入 生产者的api模块,并通过dubbo 调用这些service接口,完成RPC远程调用。
我们在最顶层,有一个父类工程,其中依赖里面已经引入了springboot,spring cloud alibaba的依赖:
NacosServer工程的pom.xml
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 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98
| <?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>
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.3.12.RELEASE</version> <relativePath/> </parent>
<groupId>com.javaxing</groupId> <artifactId>NacosServer</artifactId> <version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<properties> <spring-cloud.version>Hoxton.SR12</spring-cloud.version> <spring-cloud-alibaba.version>2.2.8.RELEASE</spring-cloud-alibaba.version> </properties>
<dependencies> <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> <scope>test</scope> </dependency>
<dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> <version>2.1.0.RELEASE</version> </dependency>
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency>
<dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.5.7</version> </dependency> <dependency> <groupId>io.github.openfeign</groupId> <artifactId>feign-httpclient</artifactId> <version>10.1.0</version> </dependency>
</dependencies>
<dependencyManagement> <dependencies>
<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>${spring-cloud-alibaba.version}</version> <type>pom</type> <scope>import</scope> </dependency>
</dependencies> </dependencyManagement>
<modules> <module>xingmall-user</module> <module>xingmall-order</module> <module>xingmall-order-duboo</module> <module>xingmall-feign-dubbo</module> </modules>
</project>
|
xingmall-order-duboo pom.xml
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
| <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/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>com.javaxing</groupId> <artifactId>NacosServer</artifactId> <version>1.0-SNAPSHOT</version> </parent> <artifactId>xingmall-order-duboo</artifactId> <groupId>com.javaxing</groupId> <version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<dependencies> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-dubbo</artifactId> <version>2.2.7.RELEASE</version> </dependency>
</dependencies>
<modules>
<module>xingmall-order-duboo-producer</module> <module>xingmall-order-duboo-consumer</module>
</modules>
</project>
|
注意:因为spring cloud alibaba 2.2.8这个版本没有整合dubbo,所以需要指定dubbo的版本,如果是springCloudAlibaba其他版本的话,具体看官网的版本对应:https://github.com/alibaba/spring-cloud-alibaba/wiki/%E7%89%88%E6%9C%AC%E8%AF%B4%E6%98%8E
2)producer
工程模块截图
2.1 producer 父工程 xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| <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/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <artifactId>xingmall-order-duboo</artifactId> <groupId>com.javaxing</groupId> <version>1.0-SNAPSHOT</version> </parent> <artifactId>xingmall-order-duboo-producer</artifactId> <name>Archetype - xingmall-order-duboo-producer</name>
<modules> <module>xingmall-order-duboo-producer-api</module> <module>xingmall-order-duboo-producer-biz</module> </modules>
<packaging>pom</packaging> </project>
|
2.2 producer api模块
pom.xml
1 2 3 4 5 6 7 8 9 10 11 12 13
| <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/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>com.javaxing</groupId> <artifactId>xingmall-order-duboo-producer</artifactId> <version>1.0-SNAPSHOT</version> </parent> <artifactId>xingmall-order-duboo-producer-api</artifactId> <packaging>war</packaging> <name>xingmall-order-duboo-producer-api Maven Webapp</name> <url>http://maven.apache.org</url> </project>
|
service接口
1 2 3 4 5 6
| package com.javaxing.xingmallorderdubooproducer.service;
public interface OrderService { public String queryOrderList(); public String queryOrderById(Integer id); }
|
2.3 producer biz模块
pom.xml
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
| <?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 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>com.javaxing</groupId> <artifactId>xingmall-order-duboo-producer</artifactId> <version>1.0-SNAPSHOT</version> </parent>
<groupId>com.javaxing</groupId> <artifactId>xingmall-order-duboo-producer-biz</artifactId> <version>0.0.1-SNAPSHOT</version> <name>xingmall-order-duboo-producer</name> <description>xingmall-order-duboo-producer</description> <properties> <java.version>1.8</java.version> </properties>
<dependencies> <dependency> <groupId>com.javaxing</groupId> <artifactId>xingmall-order-duboo-producer-api</artifactId> <version>1.0-SNAPSHOT</version> </dependency> </dependencies>
<build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
|
serviceImpl实现类
服务实现类上配置@DubboService暴露服务
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| package com.javaxing.xingmallorderdubooproducer.service; import org.apache.dubbo.config.annotation.DubboService;
@DubboService public class OrderServiceImpl implements OrderService{
public String queryOrderList(){ return "query success"; }
public String queryOrderById(Integer id){ return "query success by id"; } }
|
启动类
1 2 3 4 5 6 7 8 9 10 11 12
| package com.javaxing.xingmallorderdubooproducer;
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication public class XingmallOrderDubooProducerApplication {
public static void main(String[] args) { SpringApplication.run(XingmallOrderDubooProducerApplication.class, args); } }
|
application.yaml
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
| server: port: 8080
spring: main: allow-bean-definition-overriding: true application: name: xingmall-producer cloud: nacos: discovery: server-addr: 10.211.55.12:8848 namespace: 37731914-6c28-4265-a090-7a9391ed843d cluster-name: shanghai
dubbo: scan: base-packages: com.javaxing.xingmallorderdubooproducer.service protocol: name: dubbo port: -1
|
3)consumer
工程模块截图
2.1 consumer 父工程xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| <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/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>com.javaxing</groupId> <artifactId>xingmall-order-duboo</artifactId> <version>1.0-SNAPSHOT</version> </parent>
<groupId>com.javaxing</groupId> <artifactId>xingmall-order-duboo-consumer</artifactId> <version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<modules> <module>xingmall-order-duboo-consumer-biz</module> <module>xingmall-order-duboo-consumer-api</module> </modules> </project>
|
2.2 consumer api模块
pom.xml
1 2 3 4 5 6 7 8 9 10 11 12
| <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/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>com.javaxing</groupId> <artifactId>xingmall-order-duboo-consumer</artifactId> <version>1.0-SNAPSHOT</version> </parent> <artifactId>xingmall-order-duboo-consumer-api</artifactId> <name>Archetype - xingmall-order-duboo-consumer-api</name> <url>http://maven.apache.org</url> </project>
|
2.3 consumer biz模块
pom.xml
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
| <?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 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>com.javaxing</groupId> <artifactId>xingmall-order-duboo-consumer</artifactId> <version>1.0-SNAPSHOT</version> </parent>
<groupId>com.javaxing</groupId> <artifactId>xingmall-order-duboo-consumer-biz</artifactId> <version>0.0.1-SNAPSHOT</version> <name>xingmall-order-duboo-consumer</name> <description>xingmall-order-duboo-consumer</description> <properties> <java.version>1.8</java.version> </properties>
<dependencies>
<dependency> <groupId>com.javaxing</groupId> <artifactId>xingmall-order-duboo-producer-api</artifactId> <version>1.0-SNAPSHOT</version> </dependency> </dependencies>
<build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
|
controoler
服务消费方通过@DubboReference引入服务
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
| package com.javaxing.xingmallorderdubooconsumer.controller;
import com.javaxing.xingmallorderdubooproducer.service.OrderService; import org.apache.dubbo.config.annotation.DubboReference; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController;
@RestController public class QueryOrderContrller {
@DubboReference private OrderService orderService;
@GetMapping("/queryOrderById") public String queryOrderById(Integer id){ String result = orderService.queryOrderById(id); System.out.println("成功执行根据订单id查询订单详情:"+result); return result; }
@GetMapping("/queryOrderList") public String queryOrderList(){ String result = orderService.queryOrderList(); System.out.println("成功执行查询订单列表:"+result); return result; } }
|
application.yaml
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
| server: port: 8081
spring: main: allow-bean-definition-overriding: true application: name: xingmall-consumer cloud: nacos: discovery: server-addr: 10.211.55.12:8848 namespace: 37731914-6c28-4265-a090-7a9391ed843d cluster-name: shanghai
dubbo: cloud: subscribed-services: xingmall-producer protocol: name: dubbo port: -1
|
如果不写allow-bean-definition-overriding:true,就会出现下面的错误:
4)远程调用测试
我们通过接口进行远程调用,访问consumer接口,通过consumer去调用producer service接口。
我们找到DubboInvoker的doInvoke源码,可以看到 springboot在启动时,会去调用该方法,这个方法是dubbo的入口点。
从Open Feign迁移到Dubbo
在生产环境中,如果一开始的时候,我们在微服务之间的调用模块之间使用的是open feign,随着功能和微服务的扩展,性能逐渐跟不上的事情,就需要把open feign方案迁移到dubbo方案,但是直接迁移的话时间成本和人工成本太高。
为此我们可以通过以下的方案,在不调整 Feign 接口以及 RestTemplate URL 的前提下,实现无缝迁移。
实现原理
- 通过@DubboTransported 注解,让服务消费端的 Spring Cloud Open Feign 接口以及 @LoadBalanced RestTemplate Bean 底层走 Dubbo 调用(可切换 Dubbo 支持的协议)
- 而服务提供方则只需在原有 @RestController 类上追加 Dubbo @Servce 注解(需要抽取接口)即可
1)搭建基础feign环境
项目工程图
我们创建2个模块,一个生产者 一个消费者。消费者在controller上通过feign远程调用生产者的controller。
2)producer
2.1 producer父工程xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| <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/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>com.javaxing</groupId> <artifactId>xingmall-feign-dubbo</artifactId> <version>1.0-SNAPSHOT</version> </parent> <artifactId>xingmall-feign-dubbo-producer</artifactId> <name>Archetype - xingmall-feign-dubbo-producer</name> <url>http://maven.apache.org</url>
<packaging>pom</packaging>
<modules> <module>xingmall-feign-dubbo-producer-api</module> <module>xingmall-feign-dubbo-producer-biz</module> </modules> </project>
|
2.2 producer api模块
xml
1 2 3 4 5 6 7 8 9 10 11 12
| <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/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>com.javaxing</groupId> <version>1.0-SNAPSHOT</version> <artifactId>xingmall-feign-dubbo-producer</artifactId> </parent> <artifactId>xingmall-feign-dubbo-producer-api</artifactId> <name>Archetype - xingmall-feign-dubbo-producer-api</name> <url>http://maven.apache.org</url> </project>
|
service
1 2 3
| public interface OrderService { public String queryOrderById(Integer id); }
|
2.3 producer biz模块
xml
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
| <?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 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>com.javaxing</groupId> <version>1.0-SNAPSHOT</version> <artifactId>xingmall-feign-dubbo-producer</artifactId> </parent>
<groupId>com.javaxing</groupId> <artifactId>xingmall-feign-dubbo-producer-biz</artifactId> <version>0.0.1-SNAPSHOT</version> <name>xingmall-feign-dubbo-producer-biz</name> <description>xingmall-feign-dubbo-producer-biz</description> <properties> <java.version>1.8</java.version> </properties>
<build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build>
</project>
|
serviceImpl
1 2 3 4 5 6 7 8 9 10 11
| package com.javaxing.xingmallfeigndubboproducerbiz.service;
import org.apache.dubbo.config.annotation.DubboService;
@Service public class OrderServiceImpl implements OrderService{ public String queryOrderById(Integer id){ System.out.println("调用producer方法成功"); return "query success id:"+id; } }
|
controller
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| package com.javaxing.xingmallfeigndubboproducerbiz.controller;
import com.javaxing.xingmallfeigndubboproducerbiz.service.OrderService; import org.apache.dubbo.config.annotation.DubboService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController;
@RestController @RequestMapping("/order") public class producerController {
@Autowired private OrderService orderService;
@GetMapping("/queryOrderById") public String queryOrderById(@RequestParam("id") Integer id){ return orderService.queryOrderById(id); } }
|
application.yaml
1 2 3 4 5 6 7 8 9 10 11
| server: port: 8080 spring: application: name: xingmall-feign-dubbo-producer cloud: nacos: discovery: server-addr: 10.211.55.12:8848 namespace: 37731914-6c28-4265-a090-7a9391ed843d cluster-name: shanghai
|
3)consumer
2.1 consumer 父工程xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| <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/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>com.javaxing</groupId> <artifactId>xingmall-feign-dubbo</artifactId> <version>1.0-SNAPSHOT</version> </parent>
<groupId>com.javaxing</groupId> <version>1.0-SNAPSHOT</version> <artifactId>xingmall-feign-dubbo-consumer</artifactId> <packaging>pom</packaging> <name>Archetype - xingmall-feign-dubbo-consumer</name> <url>http://maven.apache.org</url> <modules> <module>xingmall-feign-dubbo-consumer-api</module> <module>xingmall-feign-dubbo-consumer-biz</module> </modules> </project>
|
2.2 consumer api模块
xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| <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/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>com.javaxing</groupId> <artifactId>xingmall-feign-dubbo-consumer</artifactId> <version>1.0-SNAPSHOT</version> </parent> <artifactId>xingmall-feign-dubbo-consumer-api</artifactId> <name>Archetype - xingmall-feign-dubbo-consumer-api</name> <url>http://maven.apache.org</url>
</project>
|
feign接口
1 2 3 4 5 6 7 8 9 10 11 12 13
| package com.javaxing.xingmallfeigndubboconsumerbiz.feign;
import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam;
@FeignClient(value = "xingmall-feign-dubbo-producer",path = "/order") public interface OrderFeign {
@GetMapping("/queryOrderById") public String queryOrderById(@RequestParam("id") Integer id); }
|
2.3 consumer biz模块
xml
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
| <?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 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>com.javaxing</groupId> <version>1.0-SNAPSHOT</version> <artifactId>xingmall-feign-dubbo-consumer</artifactId> </parent>
<groupId>com.javaxing</groupId> <artifactId>xingmall-feign-dubbo-consumer-biz</artifactId> <version>0.0.1-SNAPSHOT</version> <name>xingmall-feign-dubbo-consumer-biz</name> <description>xingmall-feign-dubbo-consumer-biz</description> <properties> <java.version>1.8</java.version> </properties>
<dependencies> <dependency> <groupId>com.javaxing</groupId> <artifactId>xingmall-feign-dubbo-consumer-api</artifactId> <version>1.0-SNAPSHOT</version> </dependency> </dependencies>
<build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build>
</project>
|
controller
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| package com.javaxing.xingmallfeigndubboconsumerbiz.controller;
import com.javaxing.xingmallfeigndubboconsumerbiz.feign.OrderFeign; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController;
@RestController public class TestController {
@Autowired private OrderFeign orderFeign;
@GetMapping("/testFeignToDubbo") public String testFeignToDubbo(Integer id){ return orderFeign.queryOrderById(id); } }
|
启动类
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| package com.javaxing.xingmallfeigndubboconsumerbiz;
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication @EnableFeignClients public class XingmallFeignDubboConsumerBizApplication {
public static void main(String[] args) { SpringApplication.run(XingmallFeignDubboConsumerBizApplication.class, args); } }
|
application.yaml
1 2 3 4 5 6 7 8 9 10 11
| server: port: 8081 spring: application: name: xingmall-feign-dubbo-consumer cloud: nacos: discovery: server-addr: 10.211.55.12:8848 namespace: 37731914-6c28-4265-a090-7a9391ed843d cluster-name: shanghai
|
4)feign调用测试
调用之前,我们先看一下nacos注册中心的情况
调用测试
producer控制台
5)feign迁移到dubbo
5.1 producer添加dubbo参数
添加yaml参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| server: port: 8080 spring: main: allow-bean-definition-overriding: true application: name: xingmall-feign-dubbo-producer cloud: nacos: discovery: server-addr: 10.211.55.12:8848 namespace: 37731914-6c28-4265-a090-7a9391ed843d cluster-name: shanghai
dubbo: scan: base-packages: com.javaxing.xingmallfeigndubboproducerbiz.service.OrderService protocol: name: dubbo port: -1
|
service 增加dubbo注解
1 2 3 4 5 6 7 8 9 10 11
| package com.javaxing.xingmallfeigndubboproducerbiz.service;
import org.apache.dubbo.config.annotation.DubboService;
@DubboService public class OrderServiceImpl implements OrderService{ public String queryOrderById(Integer id){ System.out.println("调用producer方法成功"); return "query success id:"+id; } }
|
5.2 consumer feign添加dubbo注解
添加@DubboTransported注解
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| package com.javaxing.xingmallfeigndubboconsumerbiz.feign;
import com.alibaba.cloud.dubbo.annotation.DubboTransported; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam;
@FeignClient(value = "xingmall-feign-dubbo-producer",path = "/order") @DubboTransported(protocol = "dubbo") public interface OrderFeign {
@GetMapping("/queryOrderById") public String queryOrderById(@RequestParam("id") Integer id); }
|
添加yaml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| server: port: 8081 spring: main: allow-bean-definition-overriding: true application: name: xingmall-feign-dubbo-consumer cloud: nacos: discovery: server-addr: 10.211.55.12:8848 namespace: 37731914-6c28-4265-a090-7a9391ed843d cluster-name: shanghai
dubbo: cloud: subscribed-services: xingmall-feign-dubbo-producer protocol: name: dubbo port: -1
|
增加xml依赖,引入producer api
1 2 3 4 5
| <dependency> <groupId>com.javaxing</groupId> <artifactId>xingmall-feign-dubbo-producer-api</artifactId> <version>1.0-SNAPSHOT</version> </dependency>
|
controller
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| package com.javaxing.xingmallfeigndubboconsumerbiz.controller;
import com.javaxing.xingmallfeigndubboconsumerbiz.feign.OrderFeign; import com.javaxing.xingmallfeigndubboproducerbiz.service.OrderService; import org.apache.dubbo.config.annotation.DubboReference; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController;
@RestController public class TestController {
@DubboReference private OrderService orderService;
@GetMapping("/testFeignToDubbo") public String testFeignToDubbo(Integer id){ return orderService.queryOrderById(id); } }
|
增加@DubboReference并且通过orderSerivce进行调用(producer api service)。