# spring-starter-invoker-rpc
**Repository Path**: houlinhai/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**: 0
- **Forks**: 4
- **Created**: 2025-07-18
- **Last Updated**: 2025-07-18
## Categories & Tags
**Categories**: Uncategorized
**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.3 | invoker注解包 |
| spring-boot-invoker-core | 3.0.3 | invoker核心包 |
| spring-boot-invoker-hystrix | 3.0.3 | invoker熔断 |
| spring-boot-starter-invoker | 3.0.3 | spring boot使用 |
| spring-boot-starter-redis-invoker | 3.0.3 | spring redis使用 |
| spring-boot-starter-amqp-invoker | 3.0.3 | spring rabbitmq使用 |
| spring-boot-starter-websocket-invoker | 3.0.3 | spring websocket使用 |
| spring-cloud-starter-invoker | 3.0.3 | spring微服务使用 |
| spring-invoker-dependencies | 3.0.3 | 版本依赖 |
1. pom.xml 添加如下代码
```
com.i360day
spring-invoker-dependencies
${project.version}
pom
import
```
### 使用说明
#### 1、spring-boot-starter-invoker 方式
##### 1、 启动类添加@EnableRemoteDiscoveryClient注解
注意的是@EnableRemoteDiscoveryClient的扫描路径要多于@SpringBootApplication,因为invoker在扫描时spring还未把上下文加载,这个
时候就会出现接口会被重复注册两次,一次是接口的暴露一次是接口代理
```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 方式
##### 1、 启动类添加@EnableRemoteDiscoveryClient注解
注意的是@EnableRemoteDiscoveryClient的扫描路径要多于@SpringBootApplication,因为invoker在扫描时spring还未把上下文加载,这个
时候就会出现接口会被重复注册两次,一次是接口的暴露一次是接口代理
```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直连方式
```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{
}
```
###### 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 方式
##### 1、 启动类添加@EnableRemoteDiscoveryClient注解
注意的是@EnableRemoteDiscoveryClient的扫描路径要多于@SpringBootApplication,因为invoker在扫描时spring还未把上下文加载,这个
时候就会出现接口会被重复注册两次,一次是接口的暴露一次是接口代理
```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直连方式
```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{
}
```
###### 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 方式
##### 1、 启动类添加@EnableRemoteDiscoveryClient注解
注意的是@EnableRemoteDiscoveryClient的扫描路径要多于@SpringBootApplication,因为invoker在扫描时spring还未把上下文加载,这个
时候就会出现接口会被重复注册两次,一次是接口的暴露一次是接口代理
```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直连方式
```java
//不会对当前service做任何处理,唯一需要实现一个接口
@Service
public class TestService implements TestServiceFacade {
public String test(){
return "ok";
}
}
//1、如果是多个地址,则在列表中随机抽取访问
//可指定编码和解码器: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{
}
```
###### 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 方式
##### 1、 启动类添加@EnableRemoteDiscoveryClient注解
注意的是@EnableRemoteDiscoveryClient的扫描路径要多于@SpringBootApplication,因为invoker在扫描时spring还未把上下文加载,这个
时候就会出现接口会被重复注册两次,一次是接口的暴露一次是接口代理
```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直连方式
```java
//不会对当前service做任何处理,唯一需要实现一个接口
@Service
public class TestService implements TestServiceFacade {
public String test(){
return "ok";
}
}
//1、如果是多个地址,则在列表中随机抽取访问
//可指定编码和解码器: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{
}
```
###### 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() })));
}
}
}
}
```
#### 参与贡献