如果这个项目让你有所收获,记得 Star 关注哦,这对我是非常不错的鼓励与支持。
源码地址(后端):https://gitee.com/csps/mingyue
源码地址(前端):https://gitee.com/csps/mingyue-ui
文档地址:https://gitee.com/csps/mingyue/wikis
远程调用为分布式系统提供了一种方便、高效和可靠的通信方式,使得不同部分的组件能够协同工作,并实现代码的模块化、解耦和重用。它是构建可扩展、灵活和可维护的分布式系统的重要工具和技术。
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 结合使用可以享受到 OpenFeign 声明式 API 定义的便利性,并利用 OkHttp3 提供的强大的 HTTP 功能和性能优势。这样的组合可以简化代码、提高性能,并为开发人员提供更好的可扩展性和配置灵活性。
<dependencies>
<dependency>
<groupId>com.csp.mingyuegroupId>
<artifactId>mingyue-common-coreartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-openfeignartifactId>
dependency>
<dependency>
<groupId>io.github.openfeigngroupId>
<artifactId>feign-okhttpartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-loadbalancerartifactId>
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.
*
* {@link #value()} is an alias for (and mutually exclusive with) this attribute.
*
* 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.
*
* 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 @Configuration
for all feign clients. Can contain override
* @Bean
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 {};
}
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
<dependency>
<groupId>com.csp.mingyuegroupId>
<artifactId>mingyue-common-feignartifactId>
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);
}
<dependency>
<groupId>com.csp.mingyuegroupId>
<artifactId>mingyue-system-apiartifactId>
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"
}
默认的HttpURLConnection
是 JDK 自带的,并不支持连接池,如果要实现连接池的机制,还需要自己来管理连接对象。OkHttp3 自带连接池管理,提升吞吐量。
<dependency>
<groupId>io.github.openfeigngroupId>
<artifactId>feign-okhttpartifactId>
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
服务。下一节,网关增加接口拦截,引入白名单列表。