一、前提条件和背景 1. 前提 已经部署好Nacos,本文以192.168.101.65:8848
为例。
2. 背景 有两个微服务media
和content
,都已经注册到Nacos
。 后者通过引用Feign
实现远程调用前者。 两个微服务都被分为3个子模块:api、service、model
,对应三层架构。
请根据自身情况出发阅读本文。
二、Feign模块 1. 依赖引入 首先需要Feign
依赖和扩展。
language-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 <!-- openfeign --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-loadbalancer</artifactId> <version>4.1.0</version> </dependency> <dependency> <groupId>io.github.openfeign</groupId> <artifactId>feign-httpclient</artifactId> </dependency> <!--feign支持Multipart格式传参--> <dependency> <groupId>io.github.openfeign.form</groupId> <artifactId>feign-form</artifactId> <version>3.8.0</version> </dependency> <dependency> <groupId>io.github.openfeign.form</groupId> <artifactId>feign-form-spring</artifactId> <version>3.8.0</version> </dependency>
需要测试
依赖(可选),为了MockMultipartFile
类才引入的,非必需功能。
language-xml 1 2 3 4 5 6 7 <!-- 测试 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>6.1.2</version> <scope>compile</scope> </dependency>
其次需要涉及到的微服务的数据模型,根据个人情况而定。
如果只想要它们的数据模型,而不引入不必要的依赖,可以使用通配符*全部过滤掉。
language-xml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 <!-- 数据模型pojo --> <dependency> <groupId>com.xuecheng</groupId> <artifactId>xuecheng-plus-media-model</artifactId> <version>0.0.1-SNAPSHOT</version> <exclusions> <exclusion> <groupId>*</groupId> <artifactId>*</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>com.xuecheng</groupId> <artifactId>xuecheng-plus-content-model</artifactId> <version>0.0.1-SNAPSHOT</version> <exclusions> <exclusion> <groupId>*</groupId> <artifactId>*</artifactId> </exclusion> </exclusions> </dependency>
2. application.yaml
配置 填入以下内容,大抵为超时熔断处理。(可选),甚至可以留空。
language-yml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 feign: hystrix: enabled: true circuitbreaker: enabled: true hystrix: command: default: execution: isolation: thread: timeoutInMilliseconds: 30000 ribbon: ConnectTimeout: 60000 ReadTimeout: 60000 MaxAutoRetries: 0 MaxAutoRetriesNextServer: 1
3. 扩展支持MultipartFile 新建一个配置类,如下, 主要是Encoder feignEncoder()
使得Feign
支持MultipartFile
类型传输。MultipartFile getMultipartFile(File file)
是一个工具方法,和配置无关。
language-java 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 @Configuration public class MultipartSupportConfig { @Autowired private ObjectFactory<HttpMessageConverters> messageConverters; @Bean @Primary//注入相同类型的bean时优先使用 @Scope("prototype") public Encoder feignEncoder() { return new SpringFormEncoder(new SpringEncoder(messageConverters)); } //将file转为Multipart public static MultipartFile getMultipartFile(File file) { try { byte[] content = Files.readAllBytes(file.toPath()); MultipartFile multipartFile = new MockMultipartFile(file.getName(), file.getName(), Files.probeContentType(file.toPath()), content); return multipartFile; } catch (IOException e) { e.printStackTrace(); XueChengPlusException.cast("File->MultipartFile转化失败"); return null; } } }
新建一个类,如下。@FeignClient(value)
要和服务名称对上,即media
模块spring.application.name=media-api
。@FeignClient(path)
要和服务前缀路径对上,即media
模块server.servlet.context-path=/media
。 然后MediaClient
中的方法定义尽量和media
模块对应的controller
函数保持一致。
language-java 1 2 3 4 5 6 7 8 9 10 11 @FeignClient(value = "media-api", path = "/media") public interface MediaClient { @RequestMapping(value = "/upload/coursefile", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) public UploadFileResultDto upload( @RequestPart("filedata") MultipartFile file, @RequestParam(value = "objectName", required = false) String objectName ); }
被调用方media
模块无需做什么修改。
四、Content模块 测试在content-api
上操作。
1. 引入依赖 content模块需要引入刚才feign模块的依赖。
language-xml 1 2 3 <dependency> <!-- 根据自身情况引入 --> </dependency>
2. 启用FeignClient 在启动类上加上@EnableFeignClients
注解。
3. 测试 新建测试类,如下
language-java 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.xuecheng.content.service.jobhandler; import com.xuecheng.feign.client.MediaClient; import com.xuecheng.media.model.dto.UploadFileResultDto; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.mock.web.MockMultipartFile; import org.springframework.web.multipart.MultipartFile; @SpringBootTest class CoursePublishTaskTest { @Autowired MediaClient mediaClient; @Test void generateCourseHtml() { MultipartFile file = new MockMultipartFile( "filedata", "filename.txt", "text/plain", "Some dataset...".getBytes() ); UploadFileResultDto upload = mediaClient.upload(file, "/static-test/t-1"); System.out.println(upload); } }
启动Media
模块,启动测试方法, 具体的Debug和检验,可以通过Media
模块对应的controller
函数打印日志,检查是否通过MediaClient
被触发。
五、需要澄清的几点
Feign模块不需要注册到Nacos且不需要服务发现 : 正确。feign-client
模块只是一个包含Feign客户端接口的库,它自身并不是一个独立的微服务。因此,它不需要注册到Nacos,也不需要服务发现功能。这个模块只是被其他微服务模块(如content
模块)作为依赖引入。这样做的主要目的是为了代码的重用和解耦,允许任何微服务通过引入这个依赖来调用其他服务。
只有调用者(如content
模块)需要使用@EnableFeignClients
注解,被调用者(如media
模块)不需要 : 正确。@EnableFeignClients
注解是用来启用Feign客户端的,它告诉Spring Cloud这个服务将会使用Feign来进行远程服务调用。因此,只有需要使用Feign客户端的服务(在这个例子中是content
模块)需要添加这个注解。而被调用的服务(如media
模块),只需作为普通的Spring Boot应用运行,提供REST API即可,无需使用@EnableFeignClients
。
如何在服务间共享数据模型(如DTOs)而不引入不必要的依赖。 解决这个问题的一种方法是创建一个共享的库或模块,这个库包含所有服务共享的数据模型。另一种使用依赖剥离,使用通配符(*)可以排除pom.xml
中特定依赖的所有传递性依赖。