springboot+cloud 学习(二)应用间通信Feign(伪RPC,实则HTTP)
在微服务中,使用什么协议来构建服务体系,一直是个热门话题。 争论的焦点集中在两个候选技术: RPC or Restful
Restful架构是基于Http应用层协议的产物,RPC架构是基于TCP传输层协议的产物。
网络七层模型
在说RPC和HTTP的区别之前,了解一下七层网络结构模型(虽然实际应用中基本上都是五层),它可以分为以下几层: (从上到下)
- 第一层:应用层。定义了用于在网络中进行通信和传输数据的接口;
- 第二层:表示层。定义不同的系统中数据的传输格式,编码和解码规范等;
- 第三层:会话层。管理用户的会话,控制用户间逻辑连接的建立和中断;
- 第四层:传输层。管理着网络中的端到端的数据传输;
- 第五层:网络层。定义网络设备间如何传输数据;
- 第六层:链路层。将上面的网络层的数据包封装成数据帧,便于物理层传输;
- 第七层:物理层。这一层主要就是传输这些二进制数据。
实际应用过程中,五层协议结构里面是没有表示层和会话层的。应该说它们和应用层合并了。我们应该将重点放在应用层和传输层这两个层面。因为HTTP是应用层协议,而TCP是传输层协议。
RPC
RPC 即远程过程调用(Remote Procedure Call Protocol,简称RPC),像调用本地服务(方法)一样调用服务器的服务(方法)。通常的实现有 XML-RPC , JSON-RPC , 通信方式基本相同, 所不同的只是传输数据的格式.
RPC框架的主要目标就是让远程服务调用更简单、透明。RPC框架负责屏蔽底层的传输方式(TCP或者UDP)、序列化方式(XML/JSON/二进制)和通信细节。开发人员在使用的时候只需要了解谁在什么位置提供了什么样的远程服务接口即可,并不需要关心底层通信细节和调用过程。
Restful
REST即表述性状态传递(Representational State Transfer,简称REST),是一种软件架构风格。REST通过HTTP协议定义的通用动词方法(GET、PUT、DELETE、POST) ,以URI对网络资源进行唯一标识,响应端根据请求端的不同需求,通过无状态通信,对其请求的资源进行表述。满足REST约束条件和原则的架构,就被称为是RESTful架构.
区别
使用RPC远程服务调用方式与传统http接口直接调用方式的差别在于:
1. 从使用方面看,Http接口只关注服务提供方(服务端),对于客户端怎么调用,调用方式怎样并不关心,通常情况下,客户端使用Http方式进行调用时,只要将内容进行传输即可,这样客户端在使用时,需要更关注网络方面的传输,比较不适用与业务方面的开发;而RPC服务则需要客户端接口与服务端保持一致,服务端提供一个方法,客户端通过接口直接发起调用,业务开发人员仅需要关注业务方法的调用即可,不再关注网络传输的细节,在开发上更为高效。
2. 从性能角度看,使用Http时,Http本身提供了丰富的状态功能与扩展功能,但也正由于Http提供的功能过多,导致在网络传输时,需要携带的信息更多,从性能角度上讲,较为低效。而RPC服务网络传输上仅传输与业务内容相关的数据,传输数据更小,性能更高。
3. 从运维角度看,使用Http接口时,常常使用一个前端代理,来进行Http转发代理请求的操作,需要进行扩容时,则需要去修改代理服务器的配置,较为繁琐,也容易出错。而使用RPC方式的微服务,则只要增加一个服务节点即可,注册中心可自动感知到节点的变化,通知调用客户端进行负载的动态控制,更为智能,省去运维的操作。
Feign
Feign 是一个声明web服务客户端,这便得编写web服务客户端更容易,使用Feign 创建一个接口并对它进行注解,它具有可插拔的注解支持包括Feign注解与JAX-RS注解,Feign还支持可插拔的编码器与解码器,Spring Cloud 增加了对 Spring MVC的注解,Spring Web 默认使用了HttpMessageConverters, Spring Cloud 集成 Ribbon 和 Eureka 提供的负载均衡的HTTP客户端 Feign.
maven 多模块下的Feign使用
目前我的项目中有2个模块device和equip,每个模块由于业务的需要拆分成4部分(parent、client、common、server)
parent为父项目,基本没什么代码,会定义一些依赖作统一管理,其pom.xml如下
<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.skyworth.tvmanage</groupId> <artifactId>management-equip</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>pom</packaging> <name>equip</name> <description>equip servers</description> <modules> <module>management-equip-client</module> <module>management-equip-common</module> <module>management-equip-server</module> </modules> <!-- 必须要引入 springboot parent ,帮我们实现了很多jar包的依赖管理 --> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.2.RELEASE</version> <relativePath /> <!-- lookup parent from repository --> </parent> <!-- 版本集中配置 --> <properties> <spring.cloud.version>Finchley.RELEASE</spring.cloud.version> <management-common.version>0.0.1-SNAPSHOT</management-common.version> </properties> <!-- cloud --> <dependencyManagement> <dependencies> <!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-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.skyworth.tvmanage</groupId> <artifactId>management-equip-common</artifactId> <version>${management-common.version}</version> </dependency> </dependencies> </dependencyManagement> </project>
server部分为业务模块,common为一些公共调用的类,client部分定义向外暴露的接口,client部分pom.xml如下
<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>com.skyworth.tvmanage</groupId> <artifactId>management-equip</artifactId> <version>0.0.1-SNAPSHOT</version> </parent> <!-- 向外暴露的接口 client --> <artifactId>management-equip-client</artifactId> <name>equip-client</name> <description>equip servers</description> <!-- 版本集中配置 --> <properties> </properties> <dependencies> <dependency> <groupId>com.skyworth.tvmanage</groupId> <artifactId>management-equip-common</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> </dependencies> </project>
然后,再来看client接口的定义 ManagementEquipClient.class, @FeignClient里的名称为Eureka里注册的需要调用的服务名称
@FeignClient("MANAGEMENT-EQUIP") public interface ManagementEquipClient { @PostMapping("/tvmanage/equip/addEquip") public void addEquip(@RequestBody Equip equip); @RequestMapping(value="/tvmanage/equip/checkEquipExists", method=RequestMethod.GET) Integer checkEquipExists(@RequestParam String core,@RequestParam String type,@RequestParam String country); @RequestMapping(value="/tvmanage/equip/getDefaultScheme", method=RequestMethod.GET) Integer getDefaultScheme(@RequestParam String core,@RequestParam String type,@RequestParam String country); }
由于我这里,feign是device调用equip,基本equip部分配置就差不多了,再来看看devcie部分,在device应用主类中通过@EnableFeignClients注解开启Feign功能。
这里需要注意的是@EnableFeignClients的扫包问题,就是***的地址要正确
@SpringBootApplication @EnableDiscoveryClient @EnableFeignClients(basePackages="****") public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
然后在device部分的controller中,声明equip服务,就可以正常调用了