发布时间:2026-07-21阅读(1)
在前面的内容中,我们分析了基于RestTemplate实现http远程通信的方法。并且集成了Ribbon实现了请求的负载均衡功能。但是在使用RestTemplate中,仍然可以发现它对于开发者来说并不透明。
我们仍然需要去通过RestTemplate中的方法来发起远程通信请求,这些流程理论上来说应该都需要对开发者屏蔽,因为开发者只需要关心业务逻辑的实现即可。
其核心目的是让开发者像调用本地方法一样调用远程方法。
所以在Spring Cloud中集成了Netflix的Feign组件。
1.1 什么是FeignFeign是一个声明式的伪RPC(Feign 的英文表意为“假装,伪装,变形”)的REST客户端,它用了基于接口的注解方式,可以以Java接口注解的方式调用Http请求,从而将请求模版化。
Feign被广泛应用在Spring Cloud 的解决方案中,是学习基于Spring Cloud 微服务架构不可或缺的重要组件。
1.2 什么是OpenFeignFeign后来交给了OpenFeign组织维护变成了OpenFeign。
OpenFeign是一种声明式、模板化的HTTP客户端。在Spring Cloud中使用OpenFeign,可以做到使用HTTP请求访问远程服务,就像调用本地方法一样的,开发者完全感知不到这是在调用远程方法,更感知不到在访问HTTP请求。
OpenFeign做的事情:
英文原义:Remote Procedure Call Protocol
中文释义:(RFC-1831)远程调用协议 ,最初由RFC-1050定义。
RPC协议假定某些传输协议的存在,如TCP或UDP,为通信程序之间携带信息数据。在OSI网络通信模型中,RPC跨越了传输层传输层)和应用层。RPC使得开发包括网络分布式多程序在内的应用程序更加容易。
RPC采用客户机/服务器模式。请求程序就是一个客户机,而服务提供程序就是一个服务器。首先,调用进程发送一个有进程参数的调用信息到服务进程,然后等待应答信息。在服务器端,进程保持睡眠状态直到调用信息的到达为止。当一个调用信息到达,服务器获得进程参数,计算结果,发送答复信息,然后等待下一个调用信息,最后,客户端调用过程接收答复信息,获得进程结果,然后调用执行继续进行。
目前,有多种RPC模式和执行。最初由Sun公司提出。IETF ONC宪章重新修订了Sun版本,使得ONC RPC协议成为IETF标准协议。现在使用最普遍的模式和执行是开放式软件基础的分布式计算环境(DCE)。
远程过程调用(RPC)信息协议由两个不同结构组成:调用信息和答复信息。
上面描述了这么一大段,我相信有些同学还是不太明白RPC是个什么玩意儿;这里对它做一个简单的定义:实际上RPC就是一个规范,这个规范让远程接口调用变的跟本地接口调用一样简单,不需要你再去关注远程接口的ip、端口、url了,这些事情底层RPC框架都帮你完成了,你只需要拿到要调用的接口的代理对象,然后直接调用该代理对象的方法就完事了。简不简单?E不Easy?
我们通过RPC协议进行微服务调用变的简单了,那RPC到底要实现什么呢?如果在面试过程中面试官说让你实现一个RPC框架,你该怎么实现呢?
OK,以上就是RPC的一个设计思路,具体怎么实现怎么落地,我们接下来参考参考OpenFeign的底层源码。
第2章 OpenFeign使用2.1 服务端1.构建一个父工程作为服务端


2.在父工程目录下添加api子工程(Maven类型)
3.在父工程目录下添加service子工程(通过springboot脚手架添加)

勾选springweb和OpenFeign依赖
4. 配置pom
<!-- 父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"> <modelVersion>4.0.0</modelVersion> <groupId>com.example</groupId> <artifactId>openfeign-order-service</artifactId> <packaging>pom</packaging> <version>1.0-SNAPSHOT</version> <modules> <module>order-api</module> <module>order-service</module> </modules> <name>openfeign-order-service</name> <!-- FIXME change it to the projects website --> <url>http://www.example.com</url> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> <spring-cloud.version>Hoxton.SR9</spring-cloud.version> <spring-boot.version>2.3.2.RELEASE</spring-boot.version> </properties> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.11</version> <scope>test</scope> </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>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>${spring-boot.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement></project><!-- order-service 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 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>com.example</groupId> <artifactId>openfeign-order-service</artifactId> <version>1.0-SNAPSHOT</version> </parent> <groupId>com.example</groupId> <artifactId>order-service</artifactId> <version>0.0.1-SNAPSHOT</version> <name>order-service</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> </properties> <dependencies> <dependency> <artifactId>order-api</artifactId> <groupId>com.example</groupId> <version>1.0-SNAPSHOT</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> </dependency> </dependencies></project><!-- order-api 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>openfeign-order-service</artifactId> <groupId>com.example</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>order-api</artifactId> <name>order-api</name> <!-- FIXME change it to the projects website --> <url>http://www.example.com</url> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> </dependencies> <build> <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) --> <plugins> <!-- clean lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#clean_Lifecycle --> <plugin> <artifactId>maven-clean-plugin</artifactId> <version>3.1.0</version> </plugin> <!-- default lifecycle, jar packaging: see https://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_jar_packaging --> <plugin> <artifactId>maven-resources-plugin</artifactId> <version>3.0.2</version> </plugin> <plugin> <artifactId>maven-compiler-plugin</artifactId> <version>3.8.0</version> </plugin> <plugin> <artifactId>maven-surefire-plugin</artifactId> <version>2.22.1</version> </plugin> <plugin> <artifactId>maven-jar-plugin</artifactId> <version>3.0.2</version> </plugin> <plugin> <artifactId>maven-install-plugin</artifactId> <version>2.5.2</version> </plugin> <plugin> <artifactId>maven-deploy-plugin</artifactId> <version>2.8.2</version> </plugin> <!-- site lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#site_Lifecycle --> <plugin> <artifactId>maven-site-plugin</artifactId> <version>3.7.1</version> </plugin> <plugin> <artifactId>maven-project-info-reports-plugin</artifactId> <version>3.0.0</version> </plugin> </plugins> </pluginManagement> </build></project>
5.api项目中创建要远程调用的接口
public interface OrderService { @GetMapping("/orders") String orders(); @PostMapping("/order") String insert(@RequestBody OrderDto dto);}
6.创建实体类OrderDto
public class OrderDto { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getCount() { return count; } public void setCount(int count) { this.count = count; } private int count;}
7.api项目中构建OpenFeignclient
@FeignClient("order-service")public interface OrderServiceClient extends OrderService {}
8.在service项目中实现具体接口
@RestControllerpublic class OrderServiceImpl implements OrderService { @Value("${server.port}") private int port; @Override public String orders() { return "获取" port "端口上的Orders成功"; } @Override public String insert(OrderDto dto) { return port "端口上:" dto.getName() "插入" dto.getCount() "个成功"; }}
1.创建工程

2.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 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.example</groupId> <artifactId>openfeign-user-service</artifactId> <version>0.0.1-SNAPSHOT</version> <name>openfeign-user-service</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <spring-boot.version>2.3.2.RELEASE</spring-boot.version> </properties> <dependencies> <!--<dependency> <groupId>io.github.openfeign</groupId> <artifactId>feign-okhttp</artifactId> </dependency>--> <dependency> <groupId>com.example</groupId> <artifactId>order-api</artifactId> <version>1.0-SNAPSHOT</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency><!--可以支持OKHTTP--> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> <version>2.2.6.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-ribbon</artifactId> <version>2.2.6.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> <exclusions> <exclusion> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> </exclusion> </exclusions> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>${spring-boot.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>1.8</source> <target>1.8</target> <encoding>UTF-8</encoding> </configuration> </plugin> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build></project>
3.配置服务端地址,因为没用到Eureka,所以,服务端地址还是配置在本地
order-service.ribbon.listOfServers=\ localhost:8080,localhost:8081
4.创建controller
@RestControllerpublic class TestController { @Autowired OrderServiceClient orderServiceClient; @GetMapping("/test") public String test(){ return orderServiceClient.orders(); } @PostMapping("/test") public String insert(@RequestBody OrderDto orderDto){ return orderServiceClient.insert(orderDto); }}
5.启动类
@EnableFeignClients(basePackages = "com.example.clients")//这个路径要跟api中带@FeignClient注解的接口的路径一样@SpringBootApplicationpublic class OpenfeignUserServiceApplication { public static void main(String[] args) { ConfigurableApplicationContext context = SpringApplication.run(OpenfeignUserServiceApplication.class, args); System.out.println(context.getBean(HelloService.class)); }}
通过引入OpenFeign这个组件后,不难发现对于开发人员来说,已经完全实现了网络通信的透明化。并且OpenFeign的整个配置过程也非常简单。
作为一个http请求通信的代理框架来说,其实并没有太多值得研究的东西。不过OpenFeign里面有几个特性还是值得聊一聊。
Gzip是一种流行的文件压缩算法,现在的应用十分广泛,尤其是在Linux平台。当应用Gzip压缩到一个纯文本文件时,效果是非常明显的,大约可以减少70%以上的文件大小,当然压缩效率取决于文件中的内容形式。
在网络通信中,客户端向服务端请求一个数据时,压缩过的内容和未压缩过的内容在传输效率上,压缩后的内容传输效率一定是更高的。
HTTP 协议支持GZIP 压缩机制,也称协议压缩。 HTTP GZIP压缩是由WEB服务器和浏览器共同遵守的协议,也就是说WEB服务器和浏览器都必须遵守。目前主流的服务器和浏览器都支持GZIP压缩技术。包括 Chrome、IE、FireFox、Opera 等;服务器有 tomcat、Apache 和 IIS 等。
OpenFeign开启压缩的配置如下
#feign 请求与响应的压缩feign.compression.request.enabled=truefeign.compression.response.enabled=truefeign.compression.request.mime-types=text/xml,application/xml,application/jsonfeign.compression.request.min-request-size=2048 # 2048个字符
我们可以针对指定的Feign接口开启日志,可以通过以下配置来开启Feign的日志配置。
在application.properties中指定日志级别:
logging.level.com.example.clients.OrderServiceClient=DEBUG
开启日志后,在没有任何其他配置信息的情况下,打印信息如下,比较简单。
2022-06-20 15:06:00.162 DEBUG 21008 --- [nio-8088-exec-1] com.example.clients.OrderServiceClient : [OrderServiceClient#orders] <--- HTTP/1.1 200 (393ms)2022-06-20 15:06:00.162 DEBUG 21008 --- [nio-8088-exec-1] com.example.clients.OrderServiceClient : [OrderServiceClient#orders] connection: keep-alive2022-06-20 15:06:00.162 DEBUG 21008 --- [nio-8088-exec-1] com.example.clients.OrderServiceClient : [OrderServiceClient#orders] content-length: 342022-06-20 15:06:00.162 DEBUG 21008 --- [nio-8088-exec-1] com.example.clients.OrderServiceClient : [OrderServiceClient#orders] content-type: text/plain;charset=UTF-82022-06-20 15:06:00.162 DEBUG 21008 --- [nio-8088-exec-1] com.example.clients.OrderServiceClient : [OrderServiceClient#orders] date: Mon, 20 Jun 2022 07:06:00 GMT2022-06-20 15:06:00.162 DEBUG 21008 --- [nio-8088-exec-1] com.example.clients.OrderServiceClient : [OrderServiceClient#orders] keep-alive: timeout=602022-06-20 15:06:00.163 DEBUG 21008 --- [nio-8088-exec-1] com.example.clients.OrderServiceClient : [OrderServiceClient#orders] 2022-06-20 15:06:00.164 DEBUG 21008 --- [nio-8088-exec-1] com.example.clients.OrderServiceClient : [OrderServiceClient#orders] 获取8080端口上的Orders成功2022-06-20 15:06:00.164 DEBUG 21008 --- [nio-8088-exec-1] com.example.clients.OrderServiceClient : [OrderServiceClient#orders] <--- END HTTP (34-byte body)
在user-service模块下,添加FeignClientLogConfig类
@Configurationpublic class FeignClientConfig { @Bean Logger.Level feignLogger(){ return Logger.Level.FULL; }}
它的作用是告诉Feign打印日志的类型
修改好之后,重启user-service模块。
然后访问user-service模块中的接口,即可看到日志信息.
3.3 通信协议OpenFeign默认采用的是sun.net.www.protocol.http包下的HttpURLConnection来实现网络通信的。
OpenFeign除了支持HttpURLConnection以外,还支持OKhttp,OkHttp是一个处理网络请求的开源项目,由移动支付Square公司贡献。它是一个高性能的HTTP通信框架。
SPDY(读作“SPeeDY”)是Google开发的基于TCP的会话层 [1] 协议,用以最小化网络延迟,提升网络速度,优化用户的网络使用体验。SPDY并不是一种用于替代HTTP的协议,而是对HTTP协议的增强。新协议的功能包括数据流的多路复用、请求优先级以及HTTP报头压缩。谷歌表示,引入SPDY协议后,在实验室测试中页面加载速度比原先快64%。
1.在user-service模块中,添加okhttp的jar依赖
<dependency> <groupId>io.github.openfeign</groupId> <artifactId>feign-okhttp</artifactId></dependency>
2.修改application.properties文件
feign.httpclient.enabled=falsefeign.okhttp.enabled=true
3.验证配置是否生效,可以在OkHttpFeignLoadBalancedConfiguration中debug,可以看到如果开启了okhttp,则会通过OkHttpClient进行初始化。
@Configuration(proxyBeanMethods = false)@ConditionalOnClass(OkHttpClient.class)@ConditionalOnProperty("feign.okhttp.enabled")@Import(OkHttpFeignConfiguration.class)class OkHttpFeignLoadBalancedConfiguration {@Bean@ConditionalOnMissingBean(Client.class)public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory,SpringClientFactory clientFactory, okhttp3.OkHttpClient okHttpClient) {OkHttpClient delegate = new OkHttpClient(okHttpClient);return new LoadBalancerFeignClient(delegate, cachingFactory, clientFactory);}}
4.并且在请求时,会在OkHttpClient.java类中的execute()方法加断点,看到请求的执行是通过okhttp来完成的。
下文预告上一篇:发酵型杨梅酒制作方法
下一篇:如何注册qq号 怎样注册qq号
Copyright © 2024 有趣生活 All Rights Reserve吉ICP备19000289号-5 TXT地图HTML地图XML地图