写在最前
如果这个项目让你有所收获,记得 Star 关注哦,这对我是非常不错的鼓励与支持。
源码地址(后端):https://gitee.com/csps/mingyue
源码地址(前端):https://gitee.com/csps/mingyue-ui
文档地址:https://gitee.com/csps/mingyue/wikisapplication-common.yml
远程调用
远程调用为分布式系统提供了一种方便、高效和可靠的通信方式,使得不同部分的组件能够协同工作,并实现代码的模块化、解耦和重用。它是构建可扩展、灵活和可维护的分布式系统的重要工具和技术。
为什么要使用远程调用?
- 分布式系统:在分布式系统中,不同的组件或服务可能分布在不同的机器或进程中。使用远程调用可以使这些组件能够通过网络相互通信,进行数据交换和协作,实现系统的功能和业务逻辑。
- 模块化和解耦:远程调用使得系统的各个模块能够独立开发和部署,通过定义清晰的接口和协议,模块之间可以解耦并独立演化。这样,系统的不同部分可以独立进行修改、升级或替换,而不会对其他部分产生影响。
- 代码重用性:通过远程调用,可以将某些功能或服务封装为可复用的模块,供多个应用程序或系统共享和调用。这样可以避免重复开发相同的功能,提高代码的重用性和开发效率。
- 扩展性和弹性:远程调用可以实现系统的弹性扩展和水平扩展。当系统负载增加时,可以通过部署更多的服务实例,并使用负载均衡器进行流量分发,以应对更高的并发请求。
- 服务治理和监控:使用远程调用框架可以集成服务治理和监控功能,例如服务注册与发现、负载均衡、熔断器、日志记录等。这些功能可以提供更好的可用性、性能和可维护性,同时也可以帮助开发人员进行系统的监控和故障排查。
技术选型
OpenFeign 与 Dobbo 3 都是优秀的微服务架构下的远程调用框架,各有千秋。
维度 | OpenFeign | Dobbo 3 |
---|---|---|
类型 | 基于 Java 的声明式 Web 服务客户端 | RPC(远程过程调用)框架 |
协议支持 | 支持 RESTful API,通常基于 HTTP 协议进行通信 | 支持多种远程通信协议,不仅包括 Dubbo 自有的二进制协议,还支持 HTTP、gRPC等,这使得 Dubbo 3 在不同场景下更加灵活 |
编程模型 | 声明式的 API 定义和调用方式,通过 Java 接口和注解来描述服务接口和方法,使用起来更加简洁和直观 | 声明式的 API,但更多情况下使用 XML 配置或注解配置,同时支持传统的编程式调用方式。 |
服务治理 | 集成了 Netflix 的负载均衡器 Ribbon,但相较 Dubbo 3在服务治理方面较为简单。 | 提供了更多功能,如负载均衡、服务注册与发现、熔断器等,适用于大规模分布式系统的构建。 |
生态和社区 | 2019年 Netflix 公司宣布 Feign 组件正式进入停更维护状态,于是 Spring 官方便推出了一个名为 OpenFeign 的组件作为 Feign 的替代方案。 | 2011年开源,2012年发布2.5.3版本后停止更新,2017年阿里重启 dubbo 项目,现在由 Apache 软件基金会进行维护和发展,具有稳定的社区支持。 |
OpenFeign + OkHttp3
本架构将采用 OpenFeign + OkHttp3 作为远程调用工具。
- 声明式 API 定义:OpenFeign 提供了声明式的 API 定义方式,使用接口和注解来描述服务接口和方法。这使得代码更加简洁、可读性更强,并且开发人员可以更专注于业务逻辑而不是底层的 HTTP 请求细节。
- 强大的 HTTP 功能:OkHttp3 是一个功能强大的 HTTP 客户端库,提供了丰富的功能和特性,如连接池管理、请求和响应拦截器、请求重试、缓存等。通过将 OpenFeign 与 OkHttp3 结合使用,可以利用 OkHttp3 的这些功能来处理HTTP 请求和响应,提高性能和可靠性。
- 高性能和可扩展性:OkHttp3 被广泛认为是一个高性能的 HTTP 客户端,具有优秀的性能和效率。它支持并发请求、连接复用和异步操作等,能够满足大规模和高并发的需求。同时,OkHttp3 也具备很好的可扩展性,可以通过自定义拦截器和插件来扩展和定制其功能。
- 网络层配置:OkHttp3 提供了丰富的网络层配置选项,如连接超时、读写超时、代理设置等。通过使用 OkHttp3 作为OpenFeign 的底层 HTTP 客户端,可以灵活地配置和管理网络层的行为,以满足特定的需求。
- 生态和社区支持:OpenFeign 和 OkHttp3 都是广受欢迎的开源项目,拥有活跃的社区和稳定的维护。这意味着开发人员可以从社区中获得支持、文档和更新的功能。
综上所述,将 OpenFeign 与 OkHttp3 结合使用可以享受到 OpenFeign 声明式 API 定义的便利性,并利用 OkHttp3 提供的强大的 HTTP 功能和性能优势。这样的组合可以简化代码、提高性能,并为开发人员提供更好的可扩展性和配置灵活性。
添加 mingyue-common-feign
引入依赖
<dependencies>
<dependency>
<groupId>com.csp.mingyue</groupId>
<artifactId>mingyue-common-core</artifactId>
</dependency>
<!-- SpringCloud Openfeign -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!-- okhttp 扩展 -->
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-okhttp</artifactId>
</dependency>
<!-- LB 扩展 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
</dependencies>
自定义注解
覆盖 @EnableFeignClients 注解,默认 basePackages,客户端直接使用 @EnableMingYueFeignClients 注解即可,无须再指定 basePackages。
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.cloud.openfeign.FeignClientsConfiguration;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 开启 Feign Client
*
* @author Strive
* @date 2023/7/4
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@EnableFeignClients
public @interface EnableMingYueFeignClients {
/**
* Alias for the {@link #basePackages()} attribute. Allows for more concise annotation
* declarations e.g.: {@code @ComponentScan("org.my.pkg")} instead of
* {@code @ComponentScan(basePackages="org.my.pkg")}.
* @return the array of 'basePackages'.
*/
String[] value() default {};
/**
* Base packages to scan for annotated components.
* <p>
* {@link #value()} is an alias for (and mutually exclusive with) this attribute.
* <p>
* Use {@link #basePackageClasses()} for a type-safe alternative to String-based
* package names.
* @return the array of 'basePackages'.
*/
String[] basePackages() default { "com.csp.mingyue" };
/**
* Type-safe alternative to {@link #basePackages()} for specifying the packages to
* scan for annotated components. The package of each class specified will be scanned.
* <p>
* Consider creating a special no-op marker class or interface in each package that
* serves no purpose other than being referenced by this attribute.
* @return the array of 'basePackageClasses'.
*/
Class<?>[] basePackageClasses() default {};
/**
* A custom <code>@Configuration</code> for all feign clients. Can contain override
* <code>@Bean</code> definition for the pieces that make up the client, for instance
* {@link feign.codec.Decoder}, {@link feign.codec.Encoder}, {@link feign.Contract}.
*
* @see FeignClientsConfiguration for the defaults
*/
Class<?>[] defaultConfiguration() default {};
/**
* List of classes annotated with @FeignClient. If not empty, disables classpath
* scanning.
* @return
*/
Class<?>[] clients() default {};
}
Nacos 开启配置
application-common.yml
# feign 配置
feign:
# 启用 okhttp 作为网络请求框架
okhttp:
enabled: true
# 关闭 httpclient 作为网络请求框架
httpclient:
enabled: false
client:
config:
# 将调用的微服务名称改成 default 就配置成全局的了
default:
# 相当于 Request.Optionsn 连接超时时间
connectTimeout: 10000
# 相当于 Request.Options 读取超时时间
readTimeout: 10000
compression:
request:
# 配置请求 GZIP 压缩
enabled: true
response:
# 配置响应 GZIP 压缩
enabled: true
修改 mingyue-system-api
引入依赖
<dependency>
<groupId>com.csp.mingyue</groupId>
<artifactId>mingyue-common-feign</artifactId>
</dependency>
远程调用用户服务
import com.csp.mingyue.common.core.constant.ServiceNameConstants;
import com.csp.mingyue.common.core.vo.R;
import com.csp.mingyue.system.api.entity.SysUser;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
/**
* 远程调用用户服务
*
* @author Strive
* @date 2023/7/3 09:48
*/
@FeignClient(contextId = "remoteUserService", value = ServiceNameConstants.SYSTEM_SERVICE)
public interface RemoteUserService {
/**
* 通过用户名查询用户、角色信息
* @param username 用户名
* @return R
*/
@GetMapping(value = "/sysUser/getSysUserInfoByUsername")
R<SysUser> getSysUserInfoByUsername(@RequestParam(value = "username") String username);
}
mingyue-auth 远程调用 mingyue-system
引入依赖
<!-- 系统服务 API -->
<dependency>
<groupId>com.csp.mingyue</groupId>
<artifactId>mingyue-system-api</artifactId>
</dependency>
修改登录
public SaTokenInfo login(PasswordLoginDto dto) {
R<SysUser> userInfoResp = remoteUserService.getSysUserInfoByUsername(dto.getUsername());
if (Objects.isNull(userInfoResp) || Objects.isNull(userInfoResp.getData())) {
return null;
}
SysUser userInfo = userInfoResp.getData();
if (dto.getUsername().equals(userInfo.getUsername()) && dto.getPassword().equals(userInfo.getPassword())) {
// 第1步,先登录上
StpUtil.login(10001);
// 第2步,获取 Token 相关参数
SaTokenInfo tokenInfo = StpUtil.getTokenInfo();
return tokenInfo;
}
return null;
}
启动测试
curl -X 'POST' \
'http://mingyue-gateway:9100/auth/login' \
-H 'accept: */*' \
-H 'Content-Type: application/json' \
-d '{
"username": "mingyue",
"password": "123456"
}'
返回示例
{
"code": 200,
"msg": "登录成功",
"data": "2GcAFW7UZ0XJjDe5H76CBAtj7zc7bm8S"
}
开启或关闭 OkHttp3
默认的HttpURLConnection
是 JDK 自带的,并不支持连接池,如果要实现连接池的机制,还需要自己来管理连接对象。OkHttp3 自带连接池管理,提升吞吐量。
核心依赖
<!-- okhttp 扩展 -->
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-okhttp</artifactId>
</dependency>
使用方式
Nacos 配置 application-common.yml
# feign 配置
feign:
# 启用 okhttp 作为网络请求框架
okhttp:
# true 开启 / false 关闭
enabled: true
验证是否开启
查看 Client 实现,实际发送请求是由 Feign 中的
Client
接口实现类去处理的,默认使用的是 Defalut 类,该类使用的是HttpURLConnection
。
默认实现,关闭 okhttp 后,对此方法断点查看是否进入
@Override
public Response execute(Request request, Options options) throws IOException {
HttpURLConnection connection = convertAndSend(request, options);
return convertResponse(connection, request);
}
打开 OkHttpClient 类,查看 OkHttp3 源码,找到 execute 方法。开启 okhttp 后,对此方法断点查看是否进入
@Override
public feign.Response execute(feign.Request input, feign.Request.Options options)
throws IOException {
okhttp3.OkHttpClient requestScoped = getClient(options);
Request request = toOkHttpRequest(input);
Response response = requestScoped.newCall(request).execute();
return toFeignResponse(response, input).toBuilder().request(input).build();
}
小结
至此,mingyue-auth
已经可以通过远程调用获取 mingyue-system
服务。下一节,网关增加接口拦截,引入白名单列表。