# spring-starter-invoker-rpc
**Repository Path**: liju023/spring-cloud-invoker-parent
## Basic Information
- **Project Name**: spring-starter-invoker-rpc
- **Description**: 简单轻量零侵代码的rpc框架,支持http、websocket、rabbitmq,nacos、consul、zookeeper、redis模式,将数据包序列化反后调用service响应。单体工程可以通过IP直连实现相互访问,微服务使用loadbalancer模块调用、rabbitmq使用队列方式实现rpc。可全局统一服务访问、也可以客户端指定服务访问多模式支持。
- **Primary Language**: Java
- **License**: AFL-3.0
- **Default Branch**: jdk-17-release
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 14
- **Forks**: 4
- **Created**: 2019-05-15
- **Last Updated**: 2025-09-10
## Categories & Tags
**Categories**: rpc
**Tags**: None
## README
#### 介绍
spring boot > 3.0.0 + 版本、 jdk 17 +
一个简单轻量的rpc框架,可以不使用任何注册中心或使用注册中心两种方式。
几年前在一次项目开发中偶然突发奇想为啥不用service与service之间通信呢岂不是更加舒适,于是基于这种模式在接下来的时间经过不断的摸索和推论最终以spring invoker为切入点实现了service与service
之间像使用本地上下文这样直接IOC后调用。然而在后来的开发中又遇多种业务的限制、演变出多种模式的rpc如。在某些特定场景下也可以使用webSocket、redis、rabbitmq单向网络实现rpc。后期逐步会在加入netty模式主要想达到协议的转发
以及rpc可视化的控制台目前已再运量中。
目前invoker rpc序列化支持jdk、json两种方式,并基于spring cloud loadbalancer模块,实现nacos、zookeeper、eureka、consul等注册中心模式。同时也支持项目中没有注册中心场景下的项目,
该invoker rpc类同于openfeign等一个远程调用工具。
直接使用@EnableRemoteDiscoveryClient自动发现或注册客户端与服务端,其次在service类的接口中定义(@RemoteClient、@RemoteRedisClient、@RemoteWebSocketClient, @RemoteCloudClient、@RemoteAmqpClient)注解
即可,老项目也易改造、能做到零侵入项目快速满足项目需求。
#### 支持模式
1)支持eureka注册中心
2)支持nacos注册中心
3)支持consul注册中心
4)支持zookeeper注册中心
5)支持IP直连方式
6)支持redis直连、集群方式
7)支持rabbitmq直连、集群方式
8)支持webSocket直连方式
#### 更新说明
1、 3.0.2 开启spring boot 3.0 + 版本、jdk 17 支持
### application.yml 配置
```
spring:
invoker:
#默认使用json方式序列化
serializable: json、jdk
#request 配置
request:
#请求客户端
client: httpClient || okHttp
#最大链接数
maxConnections: 100
#最大空闲链接,单位毫秒
timeToLive: 1000 * 30
#请求超时
connectionRequestTimeout: 15000
#连接超时
connectTimeout: 20000
#读取超时
readTimeout: 15000
#全局客户端配置
global-client:
#回调
fallback: com.xxx.TestFallback
#回调工厂
fallbackFactory: com.xxx.FallbackFactory
#客户端rpc请求账号
client-user: invoker-user
#客户端rpc请求密码
client-password: invoker-password
#全局服务端配置
global-server:
#服务端验证账号
server-user: invoker-user
#服务端验证密码
server-password: invoker-password
webSocket:
#webSocket模式下,握手地址
endpoint: /websocket-invoker
#socket账号
server-user-name: invoker-username
#socket密码
server-password: 123456
#hystrix配置
hystrix:
#启动熔断降级处理,默认true
enabled: true
command:
#断路器启用
circuitBreakerEnabled: true
#断路器错误阈值百分比
circuitBreakerErrorThresholdPercentage: 90
#断路器强制关闭
circuitBreakerForceClosed: false
#断路器强行打开
circuitBreakerForceOpen: true
#断路器请求容量阈值
circuitBreakerRequestVolumeThreshold: 100
#断路器睡眠窗口(毫秒)
circuitBreakerSleepWindowInMilliseconds: 1000
#执行隔离信号量最大并发请求
executionIsolationSemaphoreMaxConcurrentRequests: 1000
private HystrixCommandProperties.ExecutionIsolationStrategy executionIsolationStrategy = null;
#超时时执行隔离线程中断
executionIsolationThreadInterruptOnTimeout: true
#执行隔离线程中断未来取消
executionIsolationThreadInterruptOnFutureCancel: false
#执行时间(毫秒)
executionTimeoutInMilliseconds: 3000
#执行超时启用
executionTimeoutEnabled: true
#回退隔离信号量最大并发请求
fallbackIsolationSemaphoreMaxConcurrentRequests: 100
#回调启用
fallbackEnabled: true
#度量健康快照间隔(毫秒)
metricsHealthSnapshotIntervalInMilliseconds: 3000
#度量滚动百分比桶大小
metricsRollingPercentileBucketSize: 100
#度量滚动百分比已启用
metricsRollingPercentileEnabled: true
#度量滚动百分比窗口(单位:毫秒)
metricsRollingPercentileWindowInMilliseconds: 100
#度量滚动百分比窗口桶
metricsRollingPercentileWindowBuckets: 1
#度量滚动统计窗口(单位:毫秒)
metricsRollingStatisticalWindowInMilliseconds: 3000
#度量滚动统计窗口Buckets
metricsRollingStatisticalWindowBuckets: 1
#请求缓存启动
requestCacheEnabled: true
#请求日志启动
requestLogEnabled: true
thread:
coreSize = 10;
maximumSize = 10;
keepAliveTimeMinutes = 1;
maxQueueSize = -1;
queueSizeRejectionThreshold = 5;
allowMaximumSizeToDivergeFromCoreSize = false;
rollingStatisticalWindowInMilliseconds = 10000;
rollingStatisticalWindowBuckets = 10;
```
### maven
| 依赖 | 版本 | 描述 |
|-----------------------------------------|-------|--------------------|
| spring-boot-invoker-annotations | 3.0.4 | invoker注解包 |
| spring-boot-invoker-core | 3.0.4 | invoker核心包 |
| spring-boot-invoker-hystrix | 3.0.4 | invoker熔断 |
| spring-boot-starter-invoker | 3.0.4 | spring boot使用 |
| spring-boot-starter-redis-invoker | 3.0.4 | spring redis使用 |
| spring-boot-starter-amqp-invoker | 3.0.4 | spring rabbitmq使用 |
| spring-boot-starter-websocket-invoker | 3.0.4 | spring websocket使用 |
| spring-cloud-starter-invoker | 3.0.4 | spring微服务使用 |
| spring-invoker-dependencies | 3.0.4 | 版本依赖 |
1. pom.xml 添加如下代码
```
com.i360day
spring-invoker-dependencies
${project.version}
pom
import
```
### 使用说明
#### 1、requestTemplate 拦截器
```java
public interface InvokerRequestInterceptor {
/**
* 对requestTemplate进行设置
*
* @param requestTemplate
*/
void apply(RequestTemplate requestTemplate);
}
```
#### 2、目标代理类可重写
```java
public interface Targeter {
MethodInterceptor target(Map dispatchMap, FallbackFactory fallbackFactory, TargetProxy target);
}
```
#### 3、解码和加码器
框架使用了jdk、json两种模式进行数据的加解码;原理是把rpc的参数进行有序、序列化后设置到RemoteInvocation类中,再使用jdk把RemoteInvocation数据包序列化传入请求中,服务端接收到后再对RemoteInvocation解码操作。
```java
public interface Decoder {
/**
* 解析response响应结果
* @param response
* @param type
* @return RemoteInvocationResult
* @throws IOException
*/
RemoteInvocationResult decode(Response response, Type type) throws IOException, ClassNotFoundException;
}
public interface Encoder {
/**
* 对RemoteInvocationResult对象编码
* @param outputStream
* @param responseType
* @param result
* @throws IOException
*/
void encoder(OutputStream outputStream, Type responseType, RemoteInvocationResult result) throws IOException;
}
```
#### 4、客户端接口定义
1、rpc接口可支持类泛型,方法泛型、其中返回泛型需要结合@RemoteResponseBody进行使用,@RemoteRequestParam可对参数指定序列化方式
2、rpc接口可继承一个有@RemoteModule标记的类,其目的将公共属性单独配置(如:address、clientUser、clientPassword)
3、rpc接口上的@RemoteClient标记,可自定义decoder,encoder加解码、fallbackFactory降级处理、clientProxyClass客户端代理类、serverProxyClass服务端代理类、group分组、version版本
4、rpc接口方法上可使用@RemoteDisable标记未禁止调用
5、如服务端设置了账号密码,客户端应对其设置@RemoteModule(clientUser="invoker-user", clientPassword="invoker-password")
```
spring:
invoker:
#默认使用json方式序列化
serializable: json、jdk
#request 配置
request:
#请求客户端
client: httpClient || okHttp
#最大链接数
maxConnections: 100
#最大空闲链接,单位毫秒
timeToLive: 1000 * 30
#请求超时
connectionRequestTimeout: 15000
#连接超时
connectTimeout: 20000
#读取超时
readTimeout: 15000
#全局客户端配置
global-client:
#回调
fallback: com.xxx.TestFallback
#回调工厂
fallbackFactory: com.xxx.FallbackFactory
#客户端rpc请求账号
client-user: invoker-user
#客户端rpc请求密码
client-password: invoker-password
```
```java
@RemoteClient(decoder = SpringDecode.class, encoder = SpringEncoder.class, fallbackFactory = SpringInvokerSampleServiceHystrix.class)
public interface SpringInvokerSampleServiceFacade extends SpringInvokerSampleServiceModule {
/***
* 方法禁止调用定义
* @param testVo
* @return
*/
@RemoteDisable
String testQuery(TestVo testVo);
/***
* 多泛型的定义
* @param testVo
* @param params
* @param testVoSet
* @param clazz
* @param testVoMap
* @param httpStatus
* @return
* @param
*/
List list(List>> testVo, Map>> params, Set testVoSet, Class clazz, Map testVoMap, @RemoteRequestParam(serialize = HttpStatus.class) HttpStatusCode httpStatus);
/**
* 异步调用
* @param strs
* @param testVos
* @param number
* @param numbers
*/
@Async
void testArrays(String[] strs, TestVo[] testVos, int number, int[] numbers);
/**
* 返回空值或任意对象
* @param obj
* @return
*/
Object testNull(Object obj);
/**
* 无参使用方式
* @return
*/
TestVo testVoid();
/**
* 对数组的序列化
* @param bytes1
* @param bytes2
* @return
*/
TestVo testByte(byte[] bytes1, Byte[] bytes2);
/**
* 多模式定义
* @param clazz 定义有泛型的class
* @param testVo 定义类
* @param set 定义有泛型的set
* @param a 定义有泛型>的list
* @param list 定义返回泛型的list
* @param param 定义返回泛型和>泛型的map
* @param httpStatusCode 定义枚举指定@RemoteRequestParam序列化
* @param httpStatus 定义有泛型的枚举
* @return 结合@RemoteResponseBody返回指定泛型
*/
@RemoteResponseBody(deserialize = TestVo.class)
T testSerialize(Class extends HttpInvokerEvent> clazz, TestVo testVo, Set extends Object> set, List>[] a, List list, Map param, @RemoteRequestParam(serialize = HttpStatus.class) HttpStatusCode httpStatusCode, Enum extends HttpStatus> httpStatus);
}
```
#### 5、服务端接口定义
1、当接口已在上下文中,会使用上下文的service。不会在对接口进行代理
2、可定义请求时必须携带账号密码进行请求,并content-type必须是application/x-rpc-http-invoker
3、可自定义RemoteInvocationExecutor执行器类,自定义处理请求
```
spring:
invoker:
#默认使用json方式序列化
serializable: json、jdk
#全局服务端配置
global-server:
#服务端验证账号
server-user: invoker-user
#服务端验证密码
server-password: invoker-password
```
```java
public interface RemoteInvocationExecutor {
/**
* 执行器
* @param invocation
* @param targetObject
* @param targetInterface
* @return
* @throws NoSuchMethodException
* @throws IllegalAccessException
* @throws InvocationTargetException
*/
Object invoke(RemoteInvocation invocation, Object targetObject, Class> targetInterface) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException;
}
```
#### 1、使用main方法调用远程rpc
```java
public class RemoteMainTest{
/**
* http客户端 请求
*/
public void rabbitmqTest(){
AmqpInvokerSampleServiceFacade facade = InvokerBuilder.create()
//接口class
.interfaceClass(SpringInvokerSampleServiceFacade.class)
//降级处理
.fallbackFactory(SpringInvokerSampleServiceHystrix.class)
//
.interceptor((Function) (o) -> {
return new HttpInvokerClientMethodInterceptor(
o.getTargetProxy(),
new HttpComponentsInvokerRequestExecutor(new AmqpInvokerRequest(new InvokerProperties())),
new ObjectMapperRemoteInvocationFactory(),
new SpringDecode(() -> new HttpMessageConverters(new HttpInvokerHttpMessageConverter(new ObjectMapper())))
);
})
.build();
String result = facade.testQuery(testVo);
}
/**
* mq客户端 请求
*/
public void rabbitmqTest(){
AmqpInvokerSampleServiceFacade facade = InvokerBuilder.create()
.interfaceClass(AmqpInvokerSampleServiceFacade.class)
// .fallbackFactory(SpringInvokerSampleServiceHystrix.class)
.interceptor((Function) (o) -> {
return new HttpInvokerClientMethodInterceptor(
o.getTargetProxy(),
new AmqpInvokerRequestExecutor(new AmqpInvokerRequest(new InvokerProperties())),
new ObjectMapperRemoteInvocationFactory(),
new SpringDecode(() -> new HttpMessageConverters(new HttpInvokerHttpMessageConverter(new ObjectMapper())))
);
})
.build();
String result = facade.testQuery(testVo);
}
}
```
#### 1、spring-boot-starter-invoker 方式
##### 启动类添加@EnableRemoteDiscoveryClient注解
```java
@SpringBootApplication
@EnableRemoteDiscoveryClient
public class TestBootstrap {
public static void main(String[] args) {
ConfigurableApplicationContext run = SpringApplication.run(TestBootstrap.class);
}
}
```
##### 2、 提供者 service
service类需要实现一个接口
而这个接口如果在消费端就会自动注册成一个远程客户端,如果在本工程就会被代理成HttpRequestHandler
```java
//不会对当前service做任何处理,唯一需要实现一个接口
@Service
public class TestService implements TestServiceFacade {
public String test(){
return "ok";
}
}
//可指定编码和解码器:decoder = SpringDecode.class, encoder = SpringEncoder.class
//当使用了RemoteModule模块,可忽略(address="http://127.0.0.1:9092/invoker-service"),接口调用失败后使用fallbackFactory方式降级处理
@RemoteClient(address="http://127.0.0.1:9092/invoker-service", fallbackFactory = TestServiceHystrix.class)
public interface TestServiceFacade extends TestModule{
String test();
}
//可使用模块类来区分配置或者使用配置文件, 注:address是完整的地址、IP + 端口 + /项目名称, 没有则忽略(/invoker-service)
@RemoteModule(address = "${spring.invoker.xxx.address:http://127.0.0.1:9092/invoker-service}")
public interface TestModule{
}
```
##### 3、消费者 controller
```java
@RestController
@RequestMapping("/test")
public class TestController{
@Autowired
private TestServiceFacade testServiceFacade;
@RequestMapping("/query")
public Object query(){
//调用后以rpc方式进行请求
return testServiceFacade.test();
}
}
```
#### 2、spring-cloud-starter-invoker 方式
##### 启动类添加@EnableRemoteDiscoveryClient注解
```java
@SpringBootApplication
@EnableRemoteDiscoveryClient
public class TestBootstrap {
public static void main(String[] args) {
ConfigurableApplicationContext run = SpringApplication.run(TestBootstrap.class);
}
}
```
##### 2、 提供者 service
service类需要实现一个接口
而这个接口如果在消费端就会自动注册成一个远程客户端,如果在本工程就会被代理成HttpRequestHandler
###### 2.1 默认会支持IP直连方式
###### 2.2 使用注册(nacos、zookeeper、eureka、consul)中心调用
```java
//不会对当前service做任何处理,唯一需要实现一个接口
@Service
public class CloudTestService implements CloudTestServiceFacade {
public String test(){
return "ok";
}
}
//当使用了RemoteModule模块,可忽略(name="invoker-service"),接口调用失败后使用fallbackFactory方式降级处理
@RemoteCloudClient(address="https://${spring.application.name:invoker-service}/invoker-server", fallbackFactory = CloudTestServiceHystrix.class)
public interface CloudTestServiceFacade extends TestModule{
String test();
}
//可使用模块类来区分配置或者使用配置文件, 注:address是完整的地址、IP + 端口 + /项目名称, 没有则忽略(/invoker-service)
@RemoteModule(address = "https://${spring.application.name:invoker-service}/invoker-server")
public interface CloudTestModule{
}
```
##### 3、 消费者 controller
```java
@RestController
@RequestMapping("/test")
public class TestController{
@Autowired
private TestServiceFacade testServiceFacade;
@Autowired
private CloudTestServiceFacade cloudTestServiceFacade;
//http IP直连调用
@RequestMapping("/query1")
public Object query(){
return testServiceFacade.test();
}
//nacos、zookeeper、eureka、consul注册中心调用
@RequestMapping("/query2")
public Object query(){
return cloudTestServiceFacade.test();
}
}
```
#### 3、spring-boot-starter-redis-invoker 方式
##### 启动类添加@EnableRemoteDiscoveryClient注解
```java
@SpringBootApplication
@EnableRemoteDiscoveryClient
public class TestBootstrap {
public static void main(String[] args) {
ConfigurableApplicationContext run = SpringApplication.run(TestBootstrap.class);
}
}
```
##### 2、 提供者 service
service类需要实现一个接口
而这个接口如果在消费端就会自动注册成一个远程客户端,如果在本工程就会被代理成HttpRequestHandler
###### 2.1 默认会支持IP直连方式
###### 2.2 使用redis方式访问
```java
//不会对当前service做任何处理,唯一需要实现一个接口
@Service
public class RedisTestService implements RedisTestServiceFacade {
public String test(){
return "ok";
}
}
//当使用了RemoteModule模块,可忽略(address="redis://127.0.0.1:6379/invoker-service"),接口调用失败后使用fallbackFactory方式降级处理
@RemoteRedisClient(address="redis://127.0.0.1:6379/invoker-service", fallbackFactory = TestServiceHystrix.class)
public interface RedisTestServiceFacade extends RedisTestModule{
String test();
}
//可使用模块类来区分配置或者使用配置文件, 注:address是完整的地址、IP + 端口 + /项目名称, 没有则忽略(/invoker-service)
@RemoteModule(address="redis://127.0.0.1:6379/invoker-service")
public interface RedisTestModule{
}
```
##### 3、 消费者 controller
```java
@RestController
@RequestMapping("/test")
public class TestController{
@Autowired
private TestServiceFacade testServiceFacade;
@Autowired
private RedisTestServiceFacade redisTestServiceFacade;
//使用http方式调用接口
@RequestMapping("/query1")
public Object query(){
return testServiceFacade.test();
}
//使用redis方式调用接口
@RequestMapping("/query2")
public Object query(){
return redisTestServiceFacade.test();
}
}
```
#### 4、spring-boot-starter-amqp-invoker 方式
##### 启动类添加@EnableRemoteDiscoveryClient注解
```java
@SpringBootApplication
@EnableRemoteDiscoveryClient
public class TestBootstrap {
public static void main(String[] args) {
ConfigurableApplicationContext run = SpringApplication.run(TestBootstrap.class);
}
}
```
##### 2、 提供者 service
service类需要实现一个接口
而这个接口如果在消费端就会自动注册成一个远程客户端,如果在本工程就会被代理成HttpRequestHandler
###### 2.1 默认会支持IP直连方式
###### 2.2 使用rabbitmq方式访问
```java
//不会对当前service做任何处理,唯一需要实现一个接口
@Service
public class AmqpTestService implements AmqpTestServiceFacade {
public String test(){
return "ok";
}
}
//1、当是集群方式则以英文逗号分割(amqp://127.0.0.1:5672,amqp://127.0.0.1:5672)
//2、如果是多个地址,则在列表中随机抽取访问
//当使用了RemoteModule模块,可忽略(address="amqp://127.0.0.1:5672"),接口调用失败后使用fallbackFactory方式降级处理
@RemoteAmqpClient(address="amqp://127.0.0.1:5672", fallbackFactory = AmqpServiceHystrix.class)
public interface AmqpTestServiceFacade extends AmqpTestModule{
String test();
}
//可使用模块类来区分配置或者使用配置文件, 注:如果没有项目名(contextPath)则为空或不填写
@RemoteModule(address="amqp://127.0.0.1:5672")
public interface AmqpTestModule{
}
```
##### 3、 消费者 controller
```java
@RestController
@RequestMapping("/test")
public class TestController{
@Autowired
private TestServiceFacade testServiceFacade;
@Autowired
private AmqpTestServiceFacade amqpTestServiceFacade;
//使用http方式调用接口
@RequestMapping("/query1")
public Object query(){
return testServiceFacade.test();
}
//使用rabbitmq方式调用接口
@RequestMapping("/query2")
public Object query(){
return amqpTestServiceFacade.test();
}
}
```
#### 5、spring-boot-starter-websocket-invoker 方式
##### 启动类添加@EnableRemoteDiscoveryClient注解
```java
@SpringBootApplication
@EnableRemoteDiscoveryClient
public class TestBootstrap {
public static void main(String[] args) {
ConfigurableApplicationContext run = SpringApplication.run(TestBootstrap.class);
}
}
```
##### 2、 提供者 service
service类需要实现一个接口
而这个接口如果在消费端就会自动注册成一个远程客户端,如果在本工程就会被代理成HttpRequestHandler
###### 2.1 默认会支持IP直连方式
###### 2.2 使用webSocket方式访问
```java
//不会对当前service做任何处理,唯一需要实现一个接口
@Service
public class WebSocketTestService implements WebSocketTestServiceFacade {
public String test(){
return "ok";
}
}
//当使用了RemoteModule模块,可忽略(address="ws://127.0.0.1:9092/invoker-service"),接口调用失败后使用fallbackFactory方式降级处理
@RemoteWebSocketClient(address="ws://127.0.0.1:9092/invoker-service", fallbackFactory = AmqpServiceHystrix.class)
public interface WebSocketTestServiceFacade extends AmqpTestModule{
String test();
}
//可使用模块类来区分配置或者使用配置文件, 注:如果没有项目名(contextPath)则为空或不填写
@RemoteModule(address="ws://127.0.0.1:9092/invoker-service")
public interface WebSocketTestModule{
}
```
##### 3、 消费者 controller
```java
@RestController
@RequestMapping("/test")
public class TestController{
@Autowired
private TestServiceFacade testServiceFacade;
@Autowired
private WebSocketTestServiceFacade webSocketTestServiceFacade;
//使用http方式调用接口
@RequestMapping("/query1")
public Object query(){
return testServiceFacade.test();
}
//使用webSocket方式调用接口
@RequestMapping("/query2")
public Object query(){
return webSocketTestServiceFacade.test();
}
}
```
#### 6、security oauth2 服务调用验证
```java
//当开启了security后,可使用RemoteIgnoreService忽略该service无权限访问
@RemoteIgnoreService
public class TestService{
//...
}
//加入请求头
@Component
public class RequestInterceptor implements HttpInvokerRequestInterceptor {
@Override
public void apply(RequestTemplate requestTemplate) {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if ((authentication instanceof AbstractAuthenticationToken)) {
AbstractAuthenticationToken aat = (AbstractAuthenticationToken)authentication;
if ((aat.getDetails() instanceof OAuth2AuthenticationDetails)) {
OAuth2AuthenticationDetails details = (OAuth2AuthenticationDetails)aat.getDetails();
requestTemplate.addHeaders("Authorization", new String(String.format("%s %s", new Object[] { details.getTokenType(), details.getTokenValue() })));
}
}
}
}
```
#### 参与贡献