文章目录
- 一、前提条件和背景
- 1. 前提
- 2. 背景
- 二、Feign模块
- 1. 依赖引入
- 2. `application.yaml`配置
- 3. 扩展支持MultipartFile
- 4. 将media-api注册到feign
- 三、Media模块
- 四、Content模块
- 1. 引入依赖
- 2. 启用FeignClient
- 3. 测试
- 五、需要澄清的几点
一、前提条件和背景
1. 前提
已经部署好Nacos,本文以192.168.101.65:8848
为例。
2. 背景
有两个微服务media
和content
,都已经注册到Nacos
。
后者通过引用Feign
实现远程调用前者。
两个微服务都被分为3个子模块:api、service、model
,对应三层架构。
请根据自身情况出发阅读本文。
二、Feign模块
1. 依赖引入
首先需要Feign
依赖和扩展。
<!-- 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
类才引入的,非必需功能。
<!-- 测试 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>6.1.2</version>
<scope>compile</scope>
</dependency>
其次需要涉及到的微服务的数据模型,根据个人情况而定。
如果只想要它们的数据模型,而不引入不必要的依赖,可以使用通配符*全部过滤掉。
<!-- 数据模型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
配置
填入以下内容,大抵为超时熔断处理。(可选),甚至可以留空。
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)
是一个工具方法,和配置无关。
@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;
}
}
}
4. 将media-api注册到feign
新建一个类,如下。
@FeignClient(value)
要和服务名称对上,即media
模块spring.application.name=media-api
。
@FeignClient(path)
要和服务前缀路径对上,即media
模块server.servlet.context-path=/media
。
然后MediaClient
中的方法定义尽量和media
模块对应的controller
函数保持一致。
@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模块
被调用方media
模块无需做什么修改。
四、Content模块
测试在content-api
上操作。
1. 引入依赖
content模块需要引入刚才feign模块的依赖。
<dependency>
<!-- 根据自身情况引入 -->
</dependency>
2. 启用FeignClient
在启动类上加上@EnableFeignClients
注解。
3. 测试
新建测试类,如下
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
中特定依赖的所有传递性依赖。