编辑
2020-06-16
服务端技术
00
请注意,本文编写于 1203 天前,最后修改于 113 天前,其中某些信息可能已经过时。

目录

一、Feign基本使用
二、项目多模块改造

本篇文章主要是记录了Feign的使用方式,并且重点讲述了使用Maven构建多模块项目,从而更好地适应微服务架构的软件开发模式。在服务调用的场景中,我们经常调用基于HTTP协议的服务,Feign封装了Http调用流程,更适合面向接口化的变成习惯。Feign底层使用了Ribbon作为负载均衡的客户端,而有关Ribbon的负载均衡的实现请见《RestTemplate与负载均衡器》

一、Feign基本使用

1、引入依赖

xml
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency>

2、启动类注解

java
@EnableFeignClients

3、声明伪RPC调用接口(因为本质还是HTTP)

java
@FeignClient(name = "SHOP-CLIENT") public interface ShopClient { @GetMapping("/shop/show") String getShop(); }

4、注入接口对象并使用

java
@RestController @RequestMapping("/order") public class OrderController { @Autowired private ShopClient shopClient; @GetMapping public String getOrder(){ return shopClient.getShop(); } }

使用Feign可以让调用者无感知这是一个远程调用,获得与本地方法一致的体验

二、项目多模块改造

其实多模块改造主要就是为了复用,毕竟让调用者去声明Client端是不合理的,Client应该由服务端声明,也就是我自己知道自己提供了哪些服务,别人来使用这些服务即可,而不是别人看我的源代码才知道我提供哪些接口。另外像接口输入参数,输出参数也都是需要为调用者提供的。

商品服务有两个功能:查看所有商品、新增商品,商品在数据库中的定义如下:

java
@Data @AllArgsConstructor public class ShopInfo { private String shopId; private String shopName; private Integer shopStock; }

分别是商品ID、商品名称、商品库存等字段,我们需要给外界展示的是ID和名称,库存多少是没有必要展示费消费者的。同样的对于商城的管理系统来说,如果要添加新的商品,那么主键ID是没有必要让用户手动传入的,所以对于这两种情况分别就是此服务模块的输入参数和输出参数,分别定义对应的JavaBean:

java
@Data public class ShopInfoInput { private String shopName; private Integer shopStock; } @Data @AllArgsConstructor public class ShopInfoOutput { private String shopId; private String shopName; }

并且将这两个类放到shop-common模块中,作为商品服务的通用模块。接下来是逻辑的编写,应该放到shop-serivce模块中,shop-service和我们平时的SpringBoot工程无区别,实现的都是主体业务逻辑:

java
@RestController @RequestMapping("/shop") public class ShopController { private static final List<ShopInfo> *list* = Arrays.*asList*( new ShopInfo(UUID.*randomUUID*().toString(), "ThinkPad X1", 10), new ShopInfo(UUID.*randomUUID*().toString(), "MacBook Air", 5), new ShopInfo(UUID.*randomUUID*().toString(), "MacBook Pro", 20) ); private static final CopyOnWriteArrayList<ShopInfo> *collect* = new CopyOnWriteArrayList<>(*list*); @GetMapping("show") public List<ShopInfoOutput> getAllShop(){ return *collect*.stream() .map(x -> new ShopInfoOutput(x.getShopId(), x.getShopName())) .collect(Collectors.*toList*()); } @PostMapping("create") public List<ShopInfoOutput> addOneShop(@RequestBody ShopInfoInput shopInfoInput){ *collect*.add(new ShopInfo(UUID.*randomUUID*().toString(), shopInfoInput.getShopName(), shopInfoInput.getShopStock())); return *collect*.stream() .map(x -> new ShopInfoOutput(x.getShopId(), x.getShopName())) .collect(Collectors.*toList*()); } } @Data @AllArgsConstructor class ShopInfo { private String shopId; private String shopName; private Integer shopStock; }

同时配置好Eureka,在application.yml中:

yml
server: port: 8080 eureka: client: service-url: defaultZone: http://localhost:8761/eureka/ spring: application: name: shop-client

最后需要把服务提供暴露给外界使用,所以直接使用Feign来完成shop-client模块的编写:

java
@FeignClient(name = "SHOP-CLIENT") public interface ShopClient { @GetMapping("/shop/show") List<ShopInfoInput> getAllShop(); @PostMapping("/shop/create") List<ShopInfoOutput> addOneShop(@RequestBody ShopInfoInput shopInfoInput); }

通过上面的代码我们不难发现,项目被分成了三个模块,分别是shop-common、shop-service和shop-client,他们之间的依赖关系如下图所示:

mark

所以,接下来介绍一下1个大工程的pom文件和3个小模块的pom文件,首先是父工程的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 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <!-- Spring Boot版本 --> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.3.0.RELEASE</version> <relativePath/> </parent> <groupId>xpu.edu</groupId> <artifactId>shop_server</artifactId> <version>0.0.1-SNAPSHOT</version> <name>shop_server</name> <description>Demo project for Spring Boot</description> <!-- 共用的一些配置 --> <properties> <java.version>1.8</java.version> <spring-cloud.version>Hoxton.SR4</spring-cloud.version> <shop-common-version>0.0.1-SNAPSHOT</shop-common-version> </properties> <!-- 包含的子模块 --> <modules> <module>shop_client</module> <module>shop_common</module> <module>shop_service</module> </modules> <!-- 打包方式必须是pom --> <packaging>pom</packaging> <!-- 依赖管理 --> <dependencyManagement> <dependencies> <!-- 公用模块 --> <dependency> <groupId>xpu.edu</groupId> <artifactId>shop_common</artifactId> <version>${shop-common-version}</version> </dependency> <!-- SpringCloud --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> </project>

父工程的还是基于SpringBoot,另外包含了一些公用配置,包含SpringCloud的版本等信息,还包含了公用模块的依赖。接下来看看shop-service模块:

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 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>xpu.edu</groupId> <artifactId>shop_server</artifactId> <version>0.0.1-SNAPSHOT</version> </parent> <artifactId>shop_service</artifactId> <version>0.0.1-SNAPSHOT</version> <name>shop_service</name> <dependencies> <dependency> <groupId>xpu.edu</groupId> <artifactId>shop_common</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <dependency> <groupId>com.google.code.gson</groupId> <artifactId>gson</artifactId> </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> <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>

这个模块和我们之前写的工程一致,各种必要的依赖(不要忘记公用模块shop-common),编译插件、SpringBoot插件。最后需要看的是shop-client模块,因为这个模块相当于是整个系统的使用手册:

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 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>xpu.edu</groupId> <artifactId>shop_server</artifactId> <version>0.0.1-SNAPSHOT</version> </parent> <artifactId>shop_client</artifactId> <version>0.0.1-SNAPSHOT</version> <name>shop_client</name> <packaging>jar</packaging> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-openfeign-core</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> </dependency> <dependency> <groupId>xpu.edu</groupId> <artifactId>shop_common</artifactId> </dependency> </dependencies> </project>

因为这个模块用到了@FeignClient(name = "SHOP-CLIENT")、@GetMapping("/shop/show")、@PostMapping("/shop/create")等注解,所以需要引入spring-web、spring-cloud-openfeign-core等依赖,同样的公用模块需要引入,所以加上了shop-common这个模块的依赖。最后是shop-common的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 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>xpu.edu</groupId> <artifactId>shop_server</artifactId> <version>0.0.1-SNAPSHOT</version> </parent> <artifactId>shop_common</artifactId> <name>shop_common</name> <packaging>jar</packaging> <dependencies> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> </dependencies> </project>

其实就是引入了一个lombok,没啥其他的东西。

那么别的模块如何使用shop-server这个工程提供的服务呢?下面是一个order-server即订单服务。它也是一个多模块的项目,分为order-common、order-client、order-service。只是为了测试所以,order-common与order-client模块都是空的,我们直接使用order-service模块测试一下即可:

1、引入依赖

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 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>edu.xpu</groupId> <artifactId>order_server</artifactId> <version>0.0.1-SNAPSHOT</version> </parent> <artifactId>order_service</artifactId> <version>0.0.1-SNAPSHOT</version> <name>order_service</name> <description>Demo project for Spring Boot</description> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> <dependency> <groupId>xpu.edu</groupId> <artifactId>shop_client</artifactId> <version>${shop_client.version}</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.67</version> </dependency> <dependency> <groupId>edu.xpu</groupId> <artifactId>order_common</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </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> <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>

2、添加注解,其实就是为了把FeignClient给添加到IOC容器中

java
@SpringBootApplication @EnableDiscoveryClient @EnableFeignClients(basePackages = "xpu.edu.shop_client") public class OrderServiceApplication { public static void main(String[] args) { SpringApplication.run(OrderServiceApplication.class, args); } }

3、使用其他(shop-client)模块的FeignClient

java
@RestController @RequestMapping("/") public class TestShopClient { @Autowired private ShopClient shopClient; @GetMapping public String test(){ ShopInfoInput infoInput = new ShopInfoInput(); infoInput.setShopName("iPad Pro"); infoInput.setShopStock(100); shopClient.addOneShop(infoInput); return JSON.toJSONString(shopClient.getAllShop()); } }

mark

完整的代码请见:

https://github.com/zouchanglin/practic_code/tree/master/maven_test

本文作者:Tim

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!