├── .gitignore
├── cluster
├── pom.xml
└── src
│ └── main
│ └── java
│ └── cn
│ └── thinkinjava
│ ├── Cluster.java
│ ├── ClusterInovker.java
│ ├── LoadBalance.java
│ ├── cluster
│ └── AbstractCluster.java
│ ├── failover
│ └── Failover.java
│ └── loadBlancer
│ └── AbstractLoadBalance.java
├── common
└── pom.xml
├── config
└── pom.xml
├── core
├── pom.xml
└── src
│ ├── main
│ ├── java
│ │ └── thinkinjava
│ │ │ ├── LifeCycle.java
│ │ │ ├── api
│ │ │ ├── client
│ │ │ │ ├── LuConsumer.java
│ │ │ │ └── LuConsumerConfig.java
│ │ │ └── server
│ │ │ │ ├── LuProvider.java
│ │ │ │ ├── LuProviderConfig.java
│ │ │ │ └── ProviderManager.java
│ │ │ ├── breaker
│ │ │ ├── CircuitBreaker.java
│ │ │ ├── CircuitBreakerEvent.java
│ │ │ ├── CircuitBreakerFilter.java
│ │ │ ├── CircuitBreakerStatFilter.java
│ │ │ ├── CircuitRenewEvent.java
│ │ │ ├── CircuitState.java
│ │ │ ├── CircuitStateManager.java
│ │ │ ├── Metrics.java
│ │ │ ├── TimeWindow.java
│ │ │ ├── WindowsManager.java
│ │ │ └── package-info.java
│ │ │ ├── conf
│ │ │ ├── Callback.java
│ │ │ ├── Config.java
│ │ │ ├── ConfigBuilder.java
│ │ │ ├── Constant.java
│ │ │ ├── DefaultConfig.java
│ │ │ ├── DefaultDynamicProperties.java
│ │ │ ├── DynamicProperties.java
│ │ │ ├── DynamicProperty.java
│ │ │ ├── Plugins.java
│ │ │ ├── Property.java
│ │ │ ├── ServerConfig.java
│ │ │ ├── ServerConstants.java
│ │ │ └── package-info.java
│ │ │ ├── core
│ │ │ ├── BaseServiceConfig.java
│ │ │ ├── ConsumerInvoker.java
│ │ │ ├── Invoker.java
│ │ │ ├── ProviderInvoker.java
│ │ │ └── ServiceConfig.java
│ │ │ ├── event
│ │ │ ├── Event.java
│ │ │ ├── EventBus.java
│ │ │ ├── Subscriber.java
│ │ │ └── package-info.java
│ │ │ ├── exception
│ │ │ ├── CircuitBreakerException.java
│ │ │ ├── FlowBufferAddException.java
│ │ │ ├── LimitException.java
│ │ │ ├── LuTimeoutException.java
│ │ │ └── UserOperationException.java
│ │ │ ├── filter
│ │ │ ├── ExampleFilter.java
│ │ │ ├── ExampleFilter2.java
│ │ │ ├── Filter.java
│ │ │ ├── FilterChain.java
│ │ │ ├── FilterWrapper.java
│ │ │ ├── LoadFilters.java
│ │ │ └── package-info.java
│ │ │ ├── flow
│ │ │ └── limit
│ │ │ │ ├── FlowBufferManager.java
│ │ │ │ ├── LimitEvent.java
│ │ │ │ ├── LimitFilter.java
│ │ │ │ ├── SemaphoreManager.java
│ │ │ │ └── package-info.java
│ │ │ ├── proxy
│ │ │ ├── ConsumerInterceptor.java
│ │ │ ├── ProviderInterceptor.java
│ │ │ ├── ProxyFactory.java
│ │ │ └── package-info.java
│ │ │ ├── rpc
│ │ │ ├── Request.java
│ │ │ ├── Response.java
│ │ │ └── remoting
│ │ │ │ ├── LuClient.java
│ │ │ │ ├── LuServer.java
│ │ │ │ ├── LuUserProcessor.java
│ │ │ │ └── Url.java
│ │ │ ├── util
│ │ │ ├── CloseUtil.java
│ │ │ ├── SimpleDateFormatUtil.java
│ │ │ └── currence
│ │ │ │ ├── ChannelType.java
│ │ │ │ ├── FlowBuffer.java
│ │ │ │ ├── LuRejectedExecutionHandler.java
│ │ │ │ ├── LuThreadFactory.java
│ │ │ │ ├── LuThreadPool.java
│ │ │ │ ├── LuThreadPoolFactory.java
│ │ │ │ └── Semaphore.java
│ │ │ └── vmZK
│ │ │ ├── LocalFileRegister.java
│ │ │ └── ServiceRegisterDisCover.java
│ └── resources
│ │ ├── application.properties
│ │ └── log4j.xml
│ └── test
│ └── java
│ └── thinkinjava
│ ├── proxy
│ └── InterceptorTest.java
│ ├── rpc
│ └── remoting
│ │ ├── LuServerTest.java
│ │ ├── MonaLuClientTest.java
│ │ ├── RpcTest.java
│ │ └── testService
│ │ ├── Demo.java
│ │ └── DemoImpl.java
│ └── vmZK
│ └── LocalFileRegisterTest.java
├── example
├── pom.xml
└── src
│ └── main
│ └── java
│ └── cn.thinkinjava
│ ├── ClientTest.java
│ ├── Demo.java
│ ├── DemoImpl.java
│ └── ServerTest.java
├── extension
└── pom.xml
├── filter
└── pom.xml
├── lu-netty-remoting
└── pom.xml
├── pom.xml
├── proxy
└── pom.xml
├── raft-register
└── pom.xml
├── readme.md
├── remoting
└── pom.xml
├── serializer
└── pom.xml
└── serviceMesh
└── pom.xml
/.gitignore:
--------------------------------------------------------------------------------
1 | /target/
2 | !.mvn/wrapper/maven-wrapper.jar
3 |
4 | ### STS ###
5 | .apt_generated
6 | .classpath
7 | .factorypath
8 | .project
9 | .settings
10 | .springBeans
11 | .sts4-cache
12 |
13 | ### IntelliJ IDEA ###
14 | .idea
15 | *.iws
16 | *.iml
17 | *.ipr
18 | .mvn
19 |
20 | ### NetBeans ###
21 | /nbproject/private/
22 | /build/
23 | /nbbuild/
24 | /dist/
25 | /nbdist/
26 | /.nb-gradle/
27 |
28 | */target
29 | *.cmd
30 | mvnw
31 | cluster/src/main/resources/
32 | cluster/src/test/
33 | common/src/
34 | config/src/
35 | extension/src/
36 | filter/src/
37 | lu-netty-remoting/src/
38 | proxy/src/
39 | raft-register/src/
40 | remoting/src/
41 | serializer/src/
42 | serviceMesh/src/
43 |
--------------------------------------------------------------------------------
/cluster/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | Lu-Rpc-parent
7 | cn.thinkinjava
8 | 0.0.1-SNAPSHOT
9 |
10 | 4.0.0
11 |
12 | cluster
13 | 用于包装多个 invoker,可添加负载均衡,路由等 cluster 类型功能
14 |
15 |
16 |
17 | cn.thinkinjava
18 | core
19 | 0.0.1-SNAPSHOT
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/cluster/src/main/java/cn/thinkinjava/Cluster.java:
--------------------------------------------------------------------------------
1 | package cn.thinkinjava;
2 |
3 | import thinkinjava.core.Invoker;
4 |
5 | /**
6 | *
7 | * @author 莫那·鲁道
8 | * @date 2018/10/19-下午11:03
9 | */
10 | public interface Cluster {
11 |
12 | void add(Invoker invoker);
13 |
14 | Invoker getWrapperInvoker();
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/cluster/src/main/java/cn/thinkinjava/ClusterInovker.java:
--------------------------------------------------------------------------------
1 | package cn.thinkinjava;
2 |
3 | import thinkinjava.core.Invoker;
4 | import thinkinjava.core.ServiceConfig;
5 | import thinkinjava.rpc.Request;
6 | import thinkinjava.rpc.Response;
7 |
8 | /**
9 | *
10 | * @author 莫那·鲁道
11 | * @date 2018/10/19-下午11:06
12 | */
13 | public abstract class ClusterInovker implements Invoker {
14 |
15 | public Response invoke(Request req) {
16 | return null;
17 | }
18 |
19 | public ServiceConfig getConfig() {
20 | return null;
21 | }
22 |
23 | protected abstract Response doInvoke(Request req);
24 | }
25 |
--------------------------------------------------------------------------------
/cluster/src/main/java/cn/thinkinjava/LoadBalance.java:
--------------------------------------------------------------------------------
1 | package cn.thinkinjava;
2 |
3 | import java.util.List;
4 |
5 | import thinkinjava.core.Invoker;
6 |
7 | /**
8 | *
9 | *
10 | * @author 莫那·鲁道
11 | * @date 2018/10/19-下午10:56
12 | */
13 | public interface LoadBalance {
14 |
15 | Invoker select(List invokers);
16 |
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/cluster/src/main/java/cn/thinkinjava/cluster/AbstractCluster.java:
--------------------------------------------------------------------------------
1 | package cn.thinkinjava.cluster;
2 |
3 | import cn.thinkinjava.Cluster;
4 | import thinkinjava.core.Invoker;
5 |
6 | /**
7 | *
8 | * @author 莫那·鲁道
9 | * @date 2018/10/19-下午11:05
10 | */
11 | public abstract class AbstractCluster implements Cluster {
12 |
13 |
14 | public void add(Invoker invoker) {
15 |
16 | }
17 |
18 | public Invoker getWrapperInvoker() {
19 | return null;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/cluster/src/main/java/cn/thinkinjava/failover/Failover.java:
--------------------------------------------------------------------------------
1 | package cn.thinkinjava.failover;
2 |
3 | /**
4 | *
5 | * @author 莫那·鲁道
6 | * @date 2018-12-15-11:53
7 | */
8 | public interface Failover {
9 |
10 |
11 |
12 | }
13 |
--------------------------------------------------------------------------------
/cluster/src/main/java/cn/thinkinjava/loadBlancer/AbstractLoadBalance.java:
--------------------------------------------------------------------------------
1 | package cn.thinkinjava.loadBlancer;
2 |
3 | import java.util.List;
4 |
5 | import cn.thinkinjava.LoadBalance;
6 | import thinkinjava.core.Invoker;
7 |
8 | /**
9 | *
10 | *
11 | * @author 莫那·鲁道
12 | * @date 2018/10/19-下午11:00
13 | */
14 | public abstract class AbstractLoadBalance implements LoadBalance {
15 |
16 | protected List invokers;
17 |
18 | public AbstractLoadBalance(List invokers) {
19 | this.invokers = invokers;
20 | }
21 |
22 | public Invoker select(List invokers) {
23 | return doSelect(invokers);
24 | }
25 |
26 | protected abstract Invoker doSelect(List invokers);
27 | }
28 |
--------------------------------------------------------------------------------
/common/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | Lu-Rpc-parent
7 | cn.thinkinjava
8 | 0.0.1-SNAPSHOT
9 |
10 | 4.0.0
11 |
12 | common
13 | 一些公用的组件
14 |
15 |
16 |
--------------------------------------------------------------------------------
/config/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | Lu-Rpc-parent
7 | cn.thinkinjava
8 | 0.0.1-SNAPSHOT
9 |
10 | 4.0.0
11 |
12 | config
13 | RPC 框架的各种配置
14 |
15 |
16 |
--------------------------------------------------------------------------------
/core/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | Lu-Rpc-parent
7 | cn.thinkinjava
8 | 0.0.1-SNAPSHOT
9 |
10 | 4.0.0
11 |
12 | core
13 | 核心模块, 尽量不要在此模块添加功能. 核心层应当只包括 invoker,
14 |
15 |
16 |
17 | org.apache.maven.plugins
18 | maven-compiler-plugin
19 |
20 | 8
21 | 8
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/core/src/main/java/thinkinjava/LifeCycle.java:
--------------------------------------------------------------------------------
1 | package thinkinjava;
2 |
3 | public interface LifeCycle {
4 |
5 | /** 初始化资源 */
6 | void init() throws Throwable;
7 |
8 | /** 关闭资源 */
9 | void destroy() throws Throwable;
10 |
11 | }
12 |
--------------------------------------------------------------------------------
/core/src/main/java/thinkinjava/api/client/LuConsumer.java:
--------------------------------------------------------------------------------
1 | package thinkinjava.api.client;
2 |
3 | import thinkinjava.core.ConsumerInvoker;
4 | import thinkinjava.proxy.ConsumerInterceptor;
5 | import thinkinjava.proxy.ProxyFactory;
6 |
7 | /**
8 | * 服务消费者 API
9 | *
10 | * @author 莫那·鲁道
11 | * @date 2018/10/14-下午10:13
12 | */
13 | public class LuConsumer {
14 |
15 | private LuConsumerConfig config;
16 |
17 | private T proxy;
18 |
19 | public LuConsumer(LuConsumerConfig config) {
20 | this.config = config;
21 | }
22 |
23 | /**
24 | * 从注册中心引用一个服务. 即接口类型.
25 | */
26 | @SuppressWarnings("unchecked")
27 | public T ref() {
28 |
29 | if (proxy != null) {
30 | return proxy;
31 | }
32 | proxy = (T) ProxyFactory.getProxy(config.getService(), new ConsumerInterceptor(new ConsumerInvoker(config)));
33 | return proxy;
34 | }
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/core/src/main/java/thinkinjava/api/client/LuConsumerConfig.java:
--------------------------------------------------------------------------------
1 | package thinkinjava.api.client;
2 |
3 | import org.slf4j.Logger;
4 | import org.slf4j.LoggerFactory;
5 |
6 | import thinkinjava.core.BaseServiceConfig;
7 | import thinkinjava.exception.UserOperationException;
8 | import thinkinjava.rpc.remoting.Url;
9 |
10 | /**
11 | *
12 | *
13 | * @author 莫那·鲁道
14 | * @date 2018/10/14-下午10:14
15 | */
16 | public class LuConsumerConfig extends BaseServiceConfig {
17 |
18 | private static final Logger LOGGER = LoggerFactory.getLogger("LuConsumerConfig");
19 |
20 |
21 | @Override
22 | public Url getServiceUrl() {
23 | try {
24 | return serviceRegisterDisCover.getServiceUrl(getServiceName());
25 |
26 | } catch (UserOperationException e) {
27 | LOGGER.error(e.getMessage(), e);
28 | System.exit(1);
29 | }
30 | return null;
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/core/src/main/java/thinkinjava/api/server/LuProvider.java:
--------------------------------------------------------------------------------
1 | package thinkinjava.api.server;
2 |
3 | import thinkinjava.rpc.remoting.LuServer;
4 |
5 | /**
6 | * 服务提供者 API
7 | *
8 | * @author 莫那·鲁道
9 | * @date 2018/10/14-下午10:18
10 | */
11 | @SuppressWarnings("unchecked")
12 | public class LuProvider {
13 |
14 | private LuProviderConfig config;
15 |
16 | public LuProvider(LuProviderConfig config) {
17 | this.config = config;
18 | }
19 |
20 | /**
21 | * 发布到注册中心,同时启动 RPC 服务器. 端口 8081
22 | */
23 | public void export() {
24 | // 注册到一个地方. 代表发布出去. 例如 zk
25 | config.registerService(config.getService(), config.getServiceUrl());
26 |
27 | ProviderManager.putProvider(config.getServiceName(), this);
28 |
29 | LuServer.create(8081).start();
30 | }
31 |
32 | public LuProviderConfig getConfig() {
33 | return config;
34 | }
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/core/src/main/java/thinkinjava/api/server/LuProviderConfig.java:
--------------------------------------------------------------------------------
1 | package thinkinjava.api.server;
2 |
3 | import thinkinjava.core.BaseServiceConfig;
4 | import thinkinjava.core.ProviderInvoker;
5 | import thinkinjava.proxy.ProviderInterceptor;
6 | import thinkinjava.proxy.ProxyFactory;
7 | import thinkinjava.rpc.remoting.Url;
8 |
9 | /**
10 | *
11 | *
12 | * @author 莫那·鲁道
13 | * @date 2018/10/14-下午10:31
14 | */
15 | public class LuProviderConfig extends BaseServiceConfig {
16 |
17 | private Object ref;
18 |
19 | private Object refProxy;
20 |
21 | public Object getRef() {
22 | return ref;
23 | }
24 |
25 |
26 | public void setRef(Class ref) {
27 | try {
28 | this.ref = ref.newInstance();
29 | this.refProxy = ProxyFactory.getProxy(getService(), new ProviderInterceptor(new ProviderInvoker(this.ref,this)));
30 | } catch (InstantiationException e) {
31 | e.printStackTrace();
32 | } catch (IllegalAccessException e) {
33 | e.printStackTrace();
34 | }
35 | }
36 |
37 | public void setRef(Object ref) {
38 | this.ref = ref;
39 | this.refProxy = ProxyFactory.getProxy(getService(), new ProviderInterceptor(new ProviderInvoker(this.ref, this)));
40 | }
41 |
42 | public Object getRefProxy() {
43 | return refProxy;
44 | }
45 |
46 | public void setRefProxy(Object refProxy) {
47 | this.refProxy = refProxy;
48 | }
49 |
50 | public Url getServiceUrl() {
51 | return serviceRegisterDisCover.getServiceUrl(getServiceName());
52 | }
53 |
54 | public void setServiceUrl(Url url) {
55 | serviceRegisterDisCover.registerService(getServiceName(), url);
56 | }
57 |
58 | void registerService(Class serviceClass, Url url) {
59 | serviceRegisterDisCover.registerService(serviceClass.getName(), url);
60 | }
61 |
62 | void registerService(Class serviceClass) {
63 | registerService(serviceClass, new Url());
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/core/src/main/java/thinkinjava/api/server/ProviderManager.java:
--------------------------------------------------------------------------------
1 | package thinkinjava.api.server;
2 |
3 | import java.util.Map;
4 | import java.util.concurrent.ConcurrentHashMap;
5 |
6 | /**
7 | *
8 | *
9 | * @author 莫那·鲁道
10 | * @date 2018/10/16-下午7:12
11 | */
12 | public class ProviderManager {
13 |
14 | private static Map cache = new ConcurrentHashMap();
15 |
16 | public static LuProvider getProvider(String name) {
17 | return cache.get(name);
18 | }
19 |
20 | public static void putProvider(String name, LuProvider luProvider) {
21 | cache.put(name, luProvider);
22 | }
23 |
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/core/src/main/java/thinkinjava/breaker/CircuitBreaker.java:
--------------------------------------------------------------------------------
1 | package thinkinjava.breaker;
2 |
3 | import java.util.Date;
4 |
5 | import thinkinjava.conf.DynamicProperties;
6 | import thinkinjava.conf.DynamicProperty;
7 | import thinkinjava.conf.Plugins;
8 | import thinkinjava.event.EventBus;
9 |
10 | import static thinkinjava.breaker.CircuitBreaker.State.CLOSE;
11 | import static thinkinjava.breaker.CircuitBreaker.State.HALF_OPEN;
12 | import static thinkinjava.breaker.CircuitBreaker.State.OPEN;
13 | import static thinkinjava.conf.Constant.DEFAULT_EXCEPTION_RATIO;
14 | import static thinkinjava.conf.Constant.DEFAULT_LESS_REQUEST;
15 | import static thinkinjava.conf.Constant.DEFAULT_SLEEP_TIME_SECONDS;
16 | import static thinkinjava.conf.Constant.EXCEPTION_RATIO;
17 | import static thinkinjava.conf.Constant.LESS_REQUEST;
18 | import static thinkinjava.conf.Constant.SLEEP_TIME_SECONDS;
19 |
20 | /**
21 | *
22 | * 熔断器.
23 | *
24 | * 4 种状态变化:
25 | * 1. CLOSE to OPEN
26 | * 2. OPEN to HALF_OPEN
27 | * 3. HALF_OPEN to OPEN
28 | * 4. HALF_OPEN to CLOSE
29 | *
30 | */
31 | public class CircuitBreaker {
32 |
33 | public static CircuitBreaker getInstance() {
34 | return CircuitBreakerInner.INSTANCE;
35 | }
36 |
37 | private volatile int sleepTime;
38 | private volatile int less;
39 | private volatile int expRatio;
40 |
41 | private DynamicProperty sleepTime_;
42 | private DynamicProperty less_;
43 | private DynamicProperty expRatio_;
44 |
45 | DynamicProperties properties = Plugins.getInstance().getDynamicProperties();
46 |
47 | private CircuitBreaker() {
48 | init();
49 | }
50 |
51 | private static class CircuitBreakerInner {
52 |
53 | private static final CircuitBreaker INSTANCE = new CircuitBreaker();
54 | }
55 |
56 | private void init() {
57 | sleepTime_ = properties.getInteger(SLEEP_TIME_SECONDS, DEFAULT_SLEEP_TIME_SECONDS);
58 | sleepTime = sleepTime_.get();
59 | sleepTime_.addCallback(() -> sleepTime = sleepTime_.get());
60 |
61 | less_ = properties.getInteger(LESS_REQUEST, DEFAULT_LESS_REQUEST);
62 | less = less_.get();
63 | less_.addCallback(() -> less = less_.get());
64 |
65 | expRatio_ = properties.getInteger(EXCEPTION_RATIO, DEFAULT_EXCEPTION_RATIO);
66 | expRatio = expRatio_.get();
67 | expRatio_.addCallback(() -> expRatio = expRatio_.get());
68 | }
69 |
70 | /**
71 | * 尝试调用:
72 | * 如果是 CLOSE 状态, 立即调用.
73 | * 如果是 HALF_OPEN 状态, 停止调用.
74 | * 如果是 OPEN 状态, 检查是否超过休眠时间, 如果超过, 尝试修改为半开并调用, 防止并发时过多请求进入.
75 | * @param state
76 | * @return true 表示可以调用, 反之不能调用.
77 | */
78 | public boolean tryInvoke(CircuitState state) {
79 | if (state.getState().get() == CLOSE) {
80 | return true;
81 | }
82 | if (state.getState().get() == HALF_OPEN) {
83 | return false;
84 | }
85 |
86 | if (state.getStartTime() + sleepTime < System.currentTimeMillis()) {
87 | // open to half_open
88 | return state.getState().compareAndSet(OPEN, HALF_OPEN);
89 | }
90 | return true;
91 |
92 |
93 | }
94 |
95 | /**
96 | * 当 HALF_OPEN 时调用成功.
97 | * @param state
98 | */
99 | public void success(CircuitState state) {
100 | // half_open To Close
101 | if (state.getState().compareAndSet(HALF_OPEN, CLOSE)) {
102 | state.setStartTime(-1);
103 | // 发布恢复事件.
104 | publishRenewEvent(state);
105 | }
106 | }
107 |
108 | /**
109 | * HALF_OPEN 调用失败: 修改为 OPEN.
110 | * CLOSE 调用失败, 判断是否超过错误百分比.
111 | * @param state
112 | */
113 | public void fail(CircuitState state) {
114 | if (state.getState().get() == HALF_OPEN) {
115 | tryHalfOpen2open(state);
116 | } else if (state.getState().get() == CLOSE) {
117 | tryClose2open(state);
118 | }
119 | }
120 |
121 | private void tryClose2open(CircuitState state) {
122 | long total = WindowsManager.getTotal(state.getUnique());
123 |
124 | // 最少得 {xxx} 个请求.才触发熔断逻辑
125 | if (total < less) {
126 | return;
127 | }
128 | // 例如大于 {xxx}% ,熔断.
129 | if (WindowsManager.getExceptionRatio(state.getUnique()) >= expRatio) {
130 | if (state.getState().compareAndSet(CLOSE, OPEN)) {
131 | // 更新时间
132 | state.setStartTime(System.currentTimeMillis());
133 |
134 | // 发布熔断事件.
135 | publishBreakerEvent(state);
136 | }
137 | }
138 | }
139 |
140 | private void tryHalfOpen2open(CircuitState state) {
141 | if (state.getState().compareAndSet(HALF_OPEN, OPEN)) {
142 | state.setStartTime(System.currentTimeMillis());
143 |
144 | // 发布熔断事件. 相当于没 sleepTime 发布一次事件.
145 | publishBreakerEvent(state);
146 | }
147 |
148 | }
149 |
150 |
151 | /**
152 | * 发布熔断事件.
153 | */
154 | private void publishBreakerEvent(CircuitState state) {
155 | // 发布熔断事件
156 | CircuitBreakerEvent event = CircuitBreakerEvent.newBuilder().unique(state.getUnique()).startTime(new Date()).build();
157 | EventBus.post(event);
158 | }
159 |
160 | /**
161 | * 发布恢复事件.
162 | */
163 | private void publishRenewEvent(CircuitState state) {
164 | // 发布恢复事件.
165 | CircuitRenewEvent renewEvent = CircuitRenewEvent.newBuilder().unique(state.getUnique()).endTime(new Date()).build();
166 | EventBus.post(renewEvent);
167 | }
168 |
169 |
170 | public enum State {
171 | /** 熔断器打开 */
172 | OPEN,
173 | /** 熔断器半开 */
174 | HALF_OPEN,
175 | /** 熔断器关闭 */
176 | CLOSE
177 | }
178 |
179 | }
180 |
--------------------------------------------------------------------------------
/core/src/main/java/thinkinjava/breaker/CircuitBreakerEvent.java:
--------------------------------------------------------------------------------
1 | package thinkinjava.breaker;
2 |
3 | import java.util.Date;
4 |
5 | import thinkinjava.event.Event;
6 | import thinkinjava.util.SimpleDateFormatUtil;
7 |
8 | /**
9 | *
10 | * 熔断事件.
11 | *
12 | */
13 | public class CircuitBreakerEvent implements Event {
14 |
15 | private String unique;
16 |
17 | private Date startTime;
18 |
19 | private CircuitBreakerEvent(Builder builder) {
20 | setUnique(builder.unique);
21 | setStartTime(builder.startTime);
22 | }
23 |
24 | public static Builder newBuilder() {
25 | return new Builder();
26 | }
27 |
28 | public String getUnique() {
29 | return unique;
30 | }
31 |
32 | public void setUnique(String unique) {
33 | this.unique = unique;
34 | }
35 |
36 | public Date getStartTime() {
37 | return startTime;
38 | }
39 |
40 | public void setStartTime(Date startTime) {
41 | this.startTime = startTime;
42 | }
43 |
44 | @Override
45 | public String toString() {
46 | return "CircuitBreakerEvent{" +
47 | "unique='" + unique + '\'' +
48 | ", startTime=" + SimpleDateFormatUtil.format1(startTime) +
49 | '}';
50 | }
51 |
52 | public static final class Builder {
53 |
54 | private String unique;
55 | private Date startTime;
56 |
57 | private Builder() {
58 | }
59 |
60 | public Builder unique(String val) {
61 | unique = val;
62 | return this;
63 | }
64 |
65 | public Builder startTime(Date val) {
66 | startTime = val;
67 | return this;
68 | }
69 |
70 | public CircuitBreakerEvent build() {
71 | return new CircuitBreakerEvent(this);
72 | }
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/core/src/main/java/thinkinjava/breaker/CircuitBreakerFilter.java:
--------------------------------------------------------------------------------
1 | package thinkinjava.breaker;
2 |
3 |
4 | import org.slf4j.Logger;
5 | import org.slf4j.LoggerFactory;
6 |
7 | import thinkinjava.core.Invoker;
8 | import thinkinjava.exception.CircuitBreakerException;
9 | import thinkinjava.filter.Filter;
10 | import thinkinjava.rpc.Request;
11 | import thinkinjava.rpc.Response;
12 |
13 | /**
14 | * 熔断检查过滤器
15 | */
16 | public class CircuitBreakerFilter implements Filter {
17 |
18 | private static final Logger LOGGER = LoggerFactory.getLogger(CircuitBreakerFilter.class);
19 |
20 |
21 |
22 | private CircuitBreaker circuitBreaker = CircuitBreaker.getInstance();
23 |
24 | public static CircuitBreakerFilter getInstance() {
25 | return CircuitBreakerFilterInner.INSTANCE;
26 | }
27 |
28 |
29 | private static class CircuitBreakerFilterInner {
30 |
31 | private static final CircuitBreakerFilter INSTANCE = new CircuitBreakerFilter();
32 | }
33 |
34 | private CircuitBreakerFilter() {
35 | }
36 |
37 |
38 | @Override
39 | public Response filter(Invoker invoker, Request request) throws Throwable {
40 |
41 | LOGGER.info("CircuitBreakerFilter filtering");
42 |
43 | String unique = request.getUnique();
44 | CircuitState cs = CircuitStateManager.getInstance().getCircuitState(unique);
45 |
46 | // 可能是 CLOSE, 也可能是 HALF_OPEN
47 | if (circuitBreaker.tryInvoke(cs)) {
48 | try {
49 | Response res = invoker.invoke(request);
50 |
51 | // 调用成功, 关闭 HALF_OPEN.
52 | circuitBreaker.success(cs);
53 |
54 | return res;
55 | } catch (Throwable throwable) {
56 | // 处理异常.
57 | circuitBreaker.fail(cs);
58 | throw throwable;
59 | }
60 | }
61 | throw new CircuitBreakerException("CircuitBreaker State : " + cs.getState().get().name() + ", unique : " + unique);
62 |
63 | }
64 |
65 | }
66 |
--------------------------------------------------------------------------------
/core/src/main/java/thinkinjava/breaker/CircuitBreakerStatFilter.java:
--------------------------------------------------------------------------------
1 | package thinkinjava.breaker;
2 |
3 | import org.slf4j.Logger;
4 | import org.slf4j.LoggerFactory;
5 |
6 | import thinkinjava.core.Invoker;
7 | import thinkinjava.filter.Filter;
8 | import thinkinjava.rpc.Request;
9 | import thinkinjava.rpc.Response;
10 |
11 | /**
12 | *
13 | * 熔断数据统计.
14 | */
15 | public class CircuitBreakerStatFilter implements Filter {
16 |
17 | private static final Logger LOGGER = LoggerFactory.getLogger(CircuitBreakerStatFilter.class);
18 |
19 |
20 | private CircuitStateManager circuitStateManager = CircuitStateManager.getInstance();
21 |
22 |
23 | private CircuitBreakerStatFilter() {
24 | }
25 |
26 | public static CircuitBreakerStatFilter getInstance() {
27 | return CircuitBreakerStatFilterLazyHolder.INSTANCE;
28 | }
29 |
30 | private static class CircuitBreakerStatFilterLazyHolder {
31 |
32 | private static final CircuitBreakerStatFilter INSTANCE = new CircuitBreakerStatFilter();
33 | }
34 |
35 | @Override
36 | public Response filter(Invoker invoker, Request request) throws Throwable {
37 |
38 | LOGGER.info("CircuitBreakerStatFilter filtering");
39 |
40 | String unique = request.getUnique();
41 | // 无论成功失败, 都要统计.
42 | circuitStateManager.handlerNormal(unique);
43 |
44 | Response res = null;
45 | try {
46 | res = invoker.invoke(request);
47 | } catch (Throwable throwable) {
48 | circuitStateManager.handlerError(unique);
49 | throw throwable;
50 | }
51 | return res;
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/core/src/main/java/thinkinjava/breaker/CircuitRenewEvent.java:
--------------------------------------------------------------------------------
1 | package thinkinjava.breaker;
2 |
3 | import java.util.Date;
4 |
5 | import thinkinjava.event.Event;
6 | import thinkinjava.util.SimpleDateFormatUtil;
7 |
8 | /**
9 | *
10 | * 电路恢复事件.
11 | */
12 | public class CircuitRenewEvent implements Event {
13 |
14 | private String unique;
15 |
16 | private Date endTime;
17 |
18 |
19 | public CircuitRenewEvent(String unique, Date endTime) {
20 | this.unique = unique;
21 | this.endTime = endTime;
22 | }
23 |
24 | private CircuitRenewEvent(Builder builder) {
25 | setUnique(builder.unique);
26 | setEndTime(builder.endTime);
27 | }
28 |
29 | public static Builder newBuilder() {
30 | return new Builder();
31 | }
32 |
33 | public String getUnique() {
34 | return unique;
35 | }
36 |
37 | public void setUnique(String unique) {
38 | this.unique = unique;
39 | }
40 |
41 | public Date getEndTime() {
42 | return endTime;
43 | }
44 |
45 | public void setEndTime(Date endTime) {
46 | this.endTime = endTime;
47 | }
48 |
49 | @Override
50 | public String toString() {
51 | return "CircuitRenewEvent{" +
52 | "unique='" + unique + '\'' +
53 | ", endTime=" + SimpleDateFormatUtil.format1(endTime) +
54 | '}';
55 | }
56 |
57 | public static final class Builder {
58 |
59 | private String unique;
60 | private Date endTime;
61 |
62 | private Builder() {
63 | }
64 |
65 | public Builder unique(String val) {
66 | unique = val;
67 | return this;
68 | }
69 |
70 | public Builder endTime(Date val) {
71 | endTime = val;
72 | return this;
73 | }
74 |
75 | public CircuitRenewEvent build() {
76 | return new CircuitRenewEvent(this);
77 | }
78 | }
79 | }
80 |
81 |
--------------------------------------------------------------------------------
/core/src/main/java/thinkinjava/breaker/CircuitState.java:
--------------------------------------------------------------------------------
1 | package thinkinjava.breaker;
2 |
3 | import java.io.Serializable;
4 | import java.util.concurrent.atomic.AtomicLong;
5 | import java.util.concurrent.atomic.AtomicReference;
6 |
7 | public class CircuitState implements Serializable {
8 |
9 | private static final long serialVersionUID = 3911162672779172131L;
10 |
11 | private String unique;
12 |
13 | private final AtomicReference state = new AtomicReference<>(CircuitBreaker.State.CLOSE);
14 |
15 | private AtomicLong startTime;
16 |
17 | public CircuitState(String unique) {
18 | this.unique = unique;
19 | }
20 |
21 | private CircuitState(Builder builder) {
22 | setUnique(builder.unique);
23 | startTime = builder.startTime;
24 | }
25 |
26 | public static Builder newBuilder() {
27 | return new Builder();
28 | }
29 |
30 |
31 | public String getUnique() {
32 | return unique;
33 | }
34 |
35 | public void setUnique(String unique) {
36 | this.unique = unique;
37 | }
38 |
39 | public AtomicReference getState() {
40 | return state;
41 | }
42 |
43 | public void setState(CircuitBreaker.State state) {
44 | this.state.set(state);
45 | }
46 |
47 | public long getStartTime() {
48 | return startTime.get();
49 | }
50 |
51 | public void setStartTime(long startTime) {
52 | this.startTime.set(startTime);
53 | }
54 |
55 | @Override
56 | public String toString() {
57 | return "CircuitState{" +
58 | "unique='" + unique + '\'' +
59 | ", state=" + state +
60 | '}';
61 | }
62 |
63 |
64 | public static final class Builder {
65 |
66 | private String unique;
67 | private AtomicLong startTime;
68 |
69 | private Builder() {
70 | }
71 |
72 | public Builder unique(String val) {
73 | unique = val;
74 | return this;
75 | }
76 |
77 | public Builder startTime(AtomicLong val) {
78 | startTime = val;
79 | return this;
80 | }
81 |
82 | public CircuitState build() {
83 | return new CircuitState(this);
84 | }
85 | }
86 | }
87 |
88 |
89 |
--------------------------------------------------------------------------------
/core/src/main/java/thinkinjava/breaker/CircuitStateManager.java:
--------------------------------------------------------------------------------
1 | package thinkinjava.breaker;
2 |
3 | import java.util.concurrent.ConcurrentHashMap;
4 |
5 | /**
6 | *
7 | * 熔断状态管理器.
8 | */
9 | public class CircuitStateManager {
10 |
11 | private static final ConcurrentHashMap CACHE = new ConcurrentHashMap<>();
12 |
13 | public static CircuitStateManager getInstance() {
14 | return CircuitStateManagerInner.INSTANCE;
15 | }
16 |
17 | private CircuitStateManager() {
18 | }
19 |
20 |
21 | private static class CircuitStateManagerInner {
22 |
23 | private static final CircuitStateManager INSTANCE = new CircuitStateManager();
24 | }
25 |
26 | /**
27 | * 获取状态.
28 | */
29 | public CircuitState getCircuitState(String unique) {
30 | CircuitState cs = CACHE.get(unique);
31 | if (cs == null) {
32 | cs = new CircuitState(unique);
33 | CircuitState old = CACHE.putIfAbsent(unique, cs);
34 | if (old != null) {
35 | cs = old;
36 | }
37 | }
38 | return cs;
39 | }
40 |
41 | /**
42 | * 处理错误信息.
43 | */
44 | public void handlerError(String unique) {
45 | WindowsManager.statisticError(unique);
46 | }
47 |
48 | /**
49 | *
50 | * 处理所有信息.
51 | *
52 | * @param unique
53 | */
54 | public void handlerNormal(String unique) {
55 | WindowsManager.statisticNormal(unique);
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/core/src/main/java/thinkinjava/breaker/Metrics.java:
--------------------------------------------------------------------------------
1 | package thinkinjava.breaker;
2 |
3 | import java.util.concurrent.atomic.LongAdder;
4 |
5 |
6 | public class Metrics {
7 |
8 | /** 请求总数量 */
9 | private LongAdder total = new LongAdder();
10 | /** 异常数量 */
11 | private LongAdder exception = new LongAdder();
12 | /** 此桶的计数开始时间 */
13 | private long startTime;
14 |
15 | public Metrics(long startTime) {
16 | this.startTime = startTime;
17 | }
18 |
19 | public void addException() {
20 | exception.add(1L);
21 | }
22 |
23 | public long getException() {
24 | return exception.sum();
25 | }
26 |
27 | public void addTotal() {
28 | total.add(1L);
29 | }
30 |
31 | public long getTotal() {
32 | return this.total.sum();
33 | }
34 |
35 | public long getStartTime() {
36 | return startTime;
37 | }
38 |
39 | public void setStartTime(long startTime) {
40 | this.startTime = startTime;
41 | }
42 |
43 | public Metrics reset(long time) {
44 | total.reset();
45 | exception.reset();
46 | startTime = time;
47 | return this;
48 | }
49 |
50 | public Metrics reset() {
51 | total.reset();
52 | exception.reset();
53 | return this;
54 | }
55 |
56 | @Override
57 | public String toString() {
58 | return "\r\nMetrics{" +
59 | "total=" + total +
60 | ", exception=" + exception +
61 | ", startTime=" + startTime +
62 | '}';
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/core/src/main/java/thinkinjava/breaker/TimeWindow.java:
--------------------------------------------------------------------------------
1 | package thinkinjava.breaker;
2 |
3 | import java.util.ArrayList;
4 | import java.util.List;
5 | import java.util.concurrent.atomic.AtomicReferenceArray;
6 | import java.util.concurrent.locks.ReentrantLock;
7 |
8 | import thinkinjava.conf.DynamicProperties;
9 | import thinkinjava.conf.Plugins;
10 |
11 | import static thinkinjava.conf.Constant.BUCKET_COUNT;
12 | import static thinkinjava.conf.Constant.DEFAULT_BUCKET_COUNT;
13 | import static thinkinjava.conf.Constant.DEFAULT_WINDOWS_LENGTH;
14 | import static thinkinjava.conf.Constant.WINDOWS_LENGTH;
15 |
16 | /**
17 | *
18 | * @author 莫那·鲁道
19 | * @date 2018-12-15-10:29
20 | */
21 | public class TimeWindow {
22 | private static final long serialVersionUID = 4303657461699755715L;
23 |
24 | private transient DynamicProperties properties = Plugins.getInstance().getDynamicProperties();
25 |
26 | /** 窗口长度(单个时间长度) {} 秒 */
27 | private int windowsLength = properties.getInteger(WINDOWS_LENGTH, DEFAULT_WINDOWS_LENGTH).get();
28 | /** 桶的数量(10 个,也就是10秒) */
29 | private int bucketCount = properties.getInteger(BUCKET_COUNT, DEFAULT_BUCKET_COUNT).get();
30 |
31 | private int windowsTime = windowsLength * bucketCount;
32 |
33 | /** 滑动数组 */
34 | private AtomicReferenceArray array = new AtomicReferenceArray<>(bucketCount);
35 |
36 | private final ReentrantLock lock = new ReentrantLock();
37 |
38 | public TimeWindow() {
39 | }
40 |
41 | /**
42 | *
43 | * @param bucketCount 可以认为单位就是秒. 因为单个滑动窗口的时间长度是 1000 毫秒.
44 | */
45 | public TimeWindow(int bucketCount) {
46 | this.windowsLength = DEFAULT_WINDOWS_LENGTH;
47 | this.bucketCount = bucketCount;
48 | this.windowsTime = this.windowsLength * bucketCount;
49 | this.array = new AtomicReferenceArray<>(bucketCount);
50 | }
51 |
52 | /**
53 | *
54 | * 获取当前秒时间的桶.
55 | */
56 | public Metrics getCurrentWindow() {
57 | // 毫秒
58 | long time = System.currentTimeMillis();
59 | // 秒
60 | long s = time / windowsLength;
61 | // 获取桶的下标(循环数组)
62 | int idx = (int) (s % bucketCount);
63 | // 让该时间从零开始计数
64 | time = time - time % windowsLength;
65 |
66 | for (; ; ) {
67 | Metrics old = array.get(idx);
68 | if (old == null) {
69 | old = new Metrics(time);
70 | if (array.compareAndSet(idx, null, old)) {
71 | return old;
72 | } else {
73 | Thread.yield();
74 | }
75 | } else if (time == old.getStartTime()) {
76 | // 就是这个
77 | return old;
78 | } else if (time > old.getStartTime()) {
79 | if (lock.tryLock()) {
80 | try {
81 | // 重置
82 | return old.reset(time);
83 | } finally {
84 | lock.unlock();
85 | }
86 | } else {
87 | Thread.yield();
88 | }
89 | } else if (time < old.getStartTime()) {
90 | // 通常不会发生.
91 | throw new RuntimeException();
92 | }
93 | }
94 | }
95 |
96 | public List values() {
97 | List list = new ArrayList<>();
98 | for (int i = 0; i < array.length(); i++) {
99 | Metrics mb = array.get(i);
100 | // 必须在当前时间的窗口中
101 | if (mb == null || System.currentTimeMillis() - windowsTime > mb.getStartTime()) {
102 | continue;
103 | }
104 | list.add(mb);
105 | }
106 | return list;
107 | }
108 |
109 | public void reset() {
110 | for (int i = 0; i < array.length(); i++) {
111 | Metrics mb = array.get(i);
112 | if (mb != null) {
113 | mb.reset();
114 | }
115 | }
116 | }
117 |
118 | }
119 |
--------------------------------------------------------------------------------
/core/src/main/java/thinkinjava/breaker/WindowsManager.java:
--------------------------------------------------------------------------------
1 | package thinkinjava.breaker;
2 |
3 |
4 | import java.util.List;
5 | import java.util.Map;
6 | import java.util.concurrent.ConcurrentHashMap;
7 |
8 | /**
9 | * 时间窗口生命周期管理器.
10 | */
11 | public class WindowsManager {
12 |
13 | private static final Map map = new ConcurrentHashMap<>();
14 |
15 |
16 | public static WindowsManager getInstance() {
17 | return StatisticManagerInner.INSTANCE;
18 | }
19 |
20 | private WindowsManager() {
21 | }
22 |
23 | private static class StatisticManagerInner {
24 |
25 | private static final WindowsManager INSTANCE = new WindowsManager();
26 | }
27 |
28 | /**
29 | * 处理异常.(对异常数加一)
30 | * @param unique
31 | */
32 | public static void statisticError(String unique) {
33 | TimeWindow sw = getTimeWindow(unique);
34 | Metrics mb = sw.getCurrentWindow();
35 | mb.addException();
36 | }
37 |
38 | /**
39 | * 处理请求(对总数 加一).
40 | * @param unique
41 | */
42 | public static void statisticNormal(String unique) {
43 | TimeWindow sw = getTimeWindow(unique);
44 | Metrics mb = sw.getCurrentWindow();
45 | mb.addTotal();
46 | }
47 |
48 | /**
49 | * 获取异常数.
50 | * @param unique
51 | * @return
52 | */
53 | public static long getexception(String unique) {
54 | TimeWindow sw = getTimeWindow(unique);
55 | List list = sw.values();
56 | long exception = 0;
57 | for (Metrics metricsBucket : list) {
58 | exception += metricsBucket.getException();
59 | }
60 | return exception;
61 | }
62 |
63 |
64 | /**
65 | * 获取总数
66 | * @param unique
67 | * @return
68 | */
69 | public static long getTotal(String unique) {
70 | TimeWindow sw = getTimeWindow(unique);
71 | List list = sw.values();
72 | long total = 0;
73 | for (Metrics metrics : list) {
74 | total += metrics.getTotal();
75 | }
76 | return total;
77 | }
78 |
79 | /**
80 | * 获取该时间窗口的异常比例
81 | */
82 | public static double getExceptionRatio(String unique) {
83 | TimeWindow sw = getTimeWindow(unique);
84 | List list = sw.values();
85 | long total = 0;
86 | long exception = 0;
87 | for (Metrics metricsBucket : list) {
88 | total += metricsBucket.getTotal();
89 | exception += metricsBucket.getException();
90 | }
91 | if (total == 0) {
92 | return 0.0;
93 | }
94 | return (double) exception / (double) total;
95 | }
96 |
97 | private static TimeWindow getTimeWindow(String unique) {
98 | TimeWindow sw = map.get(unique);
99 | if (sw == null) {
100 | sw = new TimeWindow();
101 |
102 | TimeWindow old = map.putIfAbsent(unique, sw);
103 | if (old != null) {
104 | sw = old;
105 | }
106 | }
107 | return sw;
108 | }
109 |
110 | }
111 |
--------------------------------------------------------------------------------
/core/src/main/java/thinkinjava/breaker/package-info.java:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * @author 莫那·鲁道
4 | * @date 2019-01-26-11:16
5 | */
6 | package thinkinjava.breaker;
7 |
8 | /**
9 | * 熔断模块. 类似 hystrix 的熔断设计.
10 | * */
--------------------------------------------------------------------------------
/core/src/main/java/thinkinjava/conf/Callback.java:
--------------------------------------------------------------------------------
1 | package thinkinjava.conf;
2 |
3 | public interface Callback {
4 |
5 | void apply();
6 | }
7 |
--------------------------------------------------------------------------------
/core/src/main/java/thinkinjava/conf/Config.java:
--------------------------------------------------------------------------------
1 | package thinkinjava.conf;
2 |
3 | public interface Config {
4 |
5 | /**
6 | * @param key String
7 | * @param defaultValue 默认值
8 | * @return 覆盖默认值.
9 | */
10 | String getProperty(String key, String defaultValue);
11 |
12 | /** 保持接口语义一致 */
13 | void setProperty(String key, String val);
14 | }
15 |
--------------------------------------------------------------------------------
/core/src/main/java/thinkinjava/conf/ConfigBuilder.java:
--------------------------------------------------------------------------------
1 | package thinkinjava.conf;
2 |
3 | import java.util.Iterator;
4 | import java.util.ServiceLoader;
5 |
6 | import org.slf4j.Logger;
7 | import org.slf4j.LoggerFactory;
8 |
9 | public class ConfigBuilder {
10 |
11 | private static final Logger LOGGER = LoggerFactory.getLogger(ConfigBuilder.class);
12 |
13 | /** 单例全局, 系统依赖此配置对象. */
14 | private Config config;
15 |
16 | private static ConfigBuilder getInstance() {
17 | return ConfigBuilderInner.INSTANCE;
18 | }
19 |
20 | private ConfigBuilder() {
21 | init();
22 | }
23 |
24 | public void init() {
25 | ServiceLoader configs = ServiceLoader.load(Config.class);
26 |
27 | Iterator iterator = configs.iterator();
28 | while (iterator.hasNext()) {
29 | config = iterator.next();
30 | LOGGER.info("SPI Config Load Success , Config Implement Class : [{}]", config.getClass().getName());
31 | break;
32 | }
33 | if (config == null) {
34 | config = new DefaultConfig();
35 | LOGGER.info("No SPI Config, Config Implement Class : [{}]", config.getClass().getName());
36 | }
37 | }
38 |
39 | private static class ConfigBuilderInner {
40 |
41 | private static final ConfigBuilder INSTANCE = new ConfigBuilder();
42 | }
43 |
44 | public static ConfigBuilder create() {
45 | return getInstance();
46 | }
47 |
48 | /**
49 | * 返回的必须是单例.
50 | * @return 单例 config 对象.
51 | */
52 | public Config build() {
53 | return config;
54 | }
55 |
56 |
57 | @Override
58 | public String toString() {
59 | return "ConfigBuilder{" +
60 | "config=" + config +
61 | '}';
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/core/src/main/java/thinkinjava/conf/Constant.java:
--------------------------------------------------------------------------------
1 | package thinkinjava.conf;
2 |
3 | /**
4 | *
5 | * @author 莫那·鲁道
6 | * @date 2018-12-15-10:42
7 | */
8 | public interface Constant {
9 |
10 | /** 异常比例 double */
11 | String EXCEPTION_RATIO = "exception.ratio";
12 | int DEFAULT_EXCEPTION_RATIO = 50;
13 |
14 |
15 | /** 最少请求量 int */
16 | String LESS_REQUEST = "less.request";
17 | int DEFAULT_LESS_REQUEST = 20;
18 |
19 | /** 熔断后休眠时间 int 单位毫秒 */
20 | String SLEEP_TIME_SECONDS = "sleep.time.seconds";
21 | // 5 * 1000;
22 | int DEFAULT_SLEEP_TIME_SECONDS = 5000;
23 |
24 | /** 单个滑动窗口的时间长度, int 单位 : 毫秒; 注意: 必须能被 1000 整除*/
25 | String WINDOWS_LENGTH = "windows.length";
26 | int DEFAULT_WINDOWS_LENGTH = 1000;
27 |
28 | /** 滑动窗口的窗口数量; int; */
29 | String BUCKET_COUNT = "bucket.count";
30 | int DEFAULT_BUCKET_COUNT = 10;
31 |
32 | /** 控制台端口号 */
33 | String ADMIN_PORT = "admin.port";
34 | int DEFAULT_ADMIN_PORT = 8321;
35 |
36 | /** 第三方服务被访问需要统计, 这个是统计的时间间隔 */
37 | String UNIQUE_HINT_COUNT_STATISTICS_INTERVAL = "unique.hint.count.statistics.interval";
38 | /** 5 * 60 (不能超过 int 最大值)*/
39 | int DEFAULT_UNIQUE_HINT_COUNT_STATISTICS_INTERVAL = 10;
40 |
41 | /** 流量缓冲区的队列长度, 超过长度则直接抛出限流异常 */
42 | String FLOW_BUFFER_QUEUE_MAX_SIZE = "flow.buffer.queue.max.size";
43 | int DEFAULT_FLOW_BUFFER_QUEUE_MAX_SIZE = 32;
44 |
45 | /** 流量缓冲区请求的缓冲时间, 单位毫秒 */
46 | String FLOW_BUFFER_WAIT_TIME_OUT = "flow.buffer.wait.time.out";
47 | long DEFAULT_FLOW_BUFFER_WAIT_TIME_OUT = 200;
48 |
49 | }
50 |
--------------------------------------------------------------------------------
/core/src/main/java/thinkinjava/conf/DefaultConfig.java:
--------------------------------------------------------------------------------
1 | package thinkinjava.conf;
2 |
3 | import java.util.concurrent.ConcurrentHashMap;
4 |
5 | public class DefaultConfig implements Config {
6 |
7 | private ConcurrentHashMap concurrentHashMap = new ConcurrentHashMap<>();
8 |
9 | @Override
10 | public String getProperty(String key, String defaultValue) {
11 | String v = concurrentHashMap.get(key);
12 | if (v == null) {
13 | v = defaultValue;
14 | }
15 | return v;
16 | }
17 |
18 | @Override
19 | public void setProperty(String key, String val) {
20 | concurrentHashMap.put(key, val);
21 | }
22 |
23 | @Override
24 | public String toString() {
25 | return "DefaultConfig{" +
26 | "concurrentHashMap=" + concurrentHashMap +
27 | '}';
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/core/src/main/java/thinkinjava/conf/DefaultDynamicProperties.java:
--------------------------------------------------------------------------------
1 | package thinkinjava.conf;
2 |
3 | import java.util.Map;
4 | import java.util.concurrent.ConcurrentHashMap;
5 | import java.util.concurrent.CopyOnWriteArrayList;
6 | import java.util.concurrent.ThreadPoolExecutor;
7 | import java.util.concurrent.TimeUnit;
8 |
9 | import com.alipay.sofa.common.profile.StringUtil;
10 |
11 | import thinkinjava.LifeCycle;
12 | import thinkinjava.util.currence.LuThreadPoolFactory;
13 |
14 |
15 | public class DefaultDynamicProperties implements DynamicProperties, LifeCycle {
16 |
17 | private Map> callbacks = new ConcurrentHashMap<>();
18 |
19 | private Config config = ConfigBuilder.create().build();
20 |
21 | private ThreadPoolExecutor pool = LuThreadPoolFactory.getThreadPool(1, getClass().getName());
22 |
23 | private DefaultDynamicProperties() {
24 | init();
25 | }
26 |
27 | public static DefaultDynamicProperties getInstance() {
28 | return SystemDynamicPropertiesLazyHolder.INSTANCE;
29 | }
30 |
31 | private static class SystemDynamicPropertiesLazyHolder {
32 |
33 | private static final DefaultDynamicProperties INSTANCE = new DefaultDynamicProperties();
34 | }
35 |
36 | @Override
37 | public void init() {
38 | pool.execute(new Runnable() {
39 | @Override
40 | public void run() {
41 | for (; ; ) {
42 | try {
43 | TimeUnit.SECONDS.sleep(1);
44 | for (Map.Entry> e : callbacks.entrySet()) {
45 | for (Callback callback : e.getValue()) {
46 | callback.apply();
47 | }
48 | }
49 | } catch (Exception e) {
50 | // ignore
51 | }
52 | }
53 | }
54 | });
55 | }
56 |
57 | @Override
58 | public void destroy() throws Exception {
59 | pool.shutdown();
60 | }
61 |
62 | @Override
63 | public DynamicProperty getString(String name, String defaultVal) {
64 | return new DynamicProperty() {
65 | @Override
66 | public String getName() {
67 | return name;
68 | }
69 |
70 | @Override
71 | public void addCallback(Callback callback) {
72 | addCallback0(this, callback);
73 | }
74 |
75 | @Override
76 | public String get() {
77 | return config.getProperty(name, defaultVal);
78 | }
79 | };
80 | }
81 |
82 | @Override
83 | public DynamicProperty getInteger(String name, Integer defaultVal) {
84 | return new DynamicProperty() {
85 | @Override
86 | public String getName() {
87 | return name;
88 | }
89 |
90 | @Override
91 | public void addCallback(Callback callback) {
92 | addCallback0(this, callback);
93 | }
94 |
95 | @Override
96 | public Integer get() {
97 | String r = config.getProperty(name, "");
98 |
99 | if (StringUtil.isBlank(r)) {
100 | return defaultVal;
101 | }
102 | return Integer.valueOf(r);
103 | }
104 | };
105 | }
106 |
107 | @Override
108 | public DynamicProperty getLong(String name, Long defaultVal) {
109 | return new DynamicProperty() {
110 | @Override
111 | public String getName() {
112 | return name;
113 | }
114 |
115 | @Override
116 | public void addCallback(Callback callback) {
117 | addCallback0(this, callback);
118 | }
119 |
120 | @Override
121 | public Long get() {
122 | String r = config.getProperty(name, "");
123 |
124 | if (StringUtil.isBlank(r)) {
125 | return defaultVal;
126 | }
127 | return Long.valueOf(r);
128 | }
129 | };
130 | }
131 |
132 |
133 | @Override
134 | public DynamicProperty getBoolean(String name, Boolean defaultVal) {
135 | return new DynamicProperty() {
136 | @Override
137 | public String getName() {
138 | return name;
139 | }
140 |
141 | @Override
142 | public void addCallback(Callback callback) {
143 | addCallback0(this, callback);
144 | }
145 |
146 | @Override
147 | public Boolean get() {
148 | String r = config.getProperty(name, "");
149 |
150 | if (StringUtil.isBlank(r)) {
151 | return defaultVal;
152 | }
153 | return Boolean.valueOf(r);
154 | }
155 | };
156 | }
157 |
158 | private void addCallback0(DynamicProperty dynamicProperty, Callback callback) {
159 | if (callbacks.get(dynamicProperty) == null) {
160 | CopyOnWriteArrayList list = new CopyOnWriteArrayList<>();
161 | list.add(callback);
162 | callbacks.put(dynamicProperty, list);
163 | } else {
164 | callbacks.get(dynamicProperty).add(callback);
165 | }
166 |
167 | }
168 |
169 |
170 | }
171 |
--------------------------------------------------------------------------------
/core/src/main/java/thinkinjava/conf/DynamicProperties.java:
--------------------------------------------------------------------------------
1 | package thinkinjava.conf;
2 |
3 | public interface DynamicProperties {
4 |
5 | DynamicProperty getString(String name, String defaultVal);
6 |
7 | DynamicProperty getInteger(String name, Integer defaultVal);
8 |
9 | DynamicProperty getLong(String name, Long defaultVal);
10 |
11 | DynamicProperty getBoolean(String name, Boolean defaultVal);
12 |
13 |
14 | }
15 |
--------------------------------------------------------------------------------
/core/src/main/java/thinkinjava/conf/DynamicProperty.java:
--------------------------------------------------------------------------------
1 | package thinkinjava.conf;
2 |
3 | public interface DynamicProperty extends Property {
4 |
5 | String getName();
6 |
7 | void addCallback(Callback callback);
8 | }
9 |
--------------------------------------------------------------------------------
/core/src/main/java/thinkinjava/conf/Plugins.java:
--------------------------------------------------------------------------------
1 | package thinkinjava.conf;
2 |
3 | import java.util.ServiceLoader;
4 |
5 |
6 | public class Plugins {
7 |
8 | private final DynamicProperties dynamicProperties;
9 |
10 | private Plugins() {
11 | dynamicProperties = loadDynamicProperties();
12 | }
13 |
14 |
15 | public static Plugins getInstance() {
16 | return PluginsLazyHolder.INSTANCE;
17 | }
18 |
19 | private static class PluginsLazyHolder {
20 |
21 | private static final Plugins INSTANCE = new Plugins();
22 | }
23 |
24 |
25 | public DynamicProperties getDynamicProperties() {
26 | return dynamicProperties;
27 | }
28 |
29 | private DynamicProperties loadDynamicProperties() {
30 | DynamicProperties d = getPluginImpl(DynamicProperties.class, DefaultDynamicProperties.getInstance());
31 |
32 | if (d == null) {
33 | d = findSPI(DynamicProperties.class, getClass().getClassLoader());
34 | }
35 | if (d == null) {
36 | d = DefaultDynamicProperties.getInstance();
37 | }
38 | return d;
39 | }
40 |
41 |
42 | /**
43 | * 先加载配置文件. 再加载 SPI. 最后使用默认(从系统环境变量获取).
44 | *
45 | * @param pluginClass
46 | * @param dynamicProperties
47 | * @param
48 | * @return
49 | */
50 | private static T getPluginImpl(Class pluginClass, DynamicProperties dynamicProperties) {
51 | String name = pluginClass.getSimpleName();
52 |
53 | String propertyName = "lu-rpc.config.plugin." + name + ".impl";
54 |
55 | String impl = dynamicProperties.getString(propertyName, null).get();
56 | if (impl != null) {
57 | try {
58 | Class> cls = Class.forName(impl);
59 | // narrow the scope (cast) to the type we're expecting
60 | cls = cls.asSubclass(pluginClass);
61 | return (T) cls.newInstance();
62 | } catch (ClassCastException e) {
63 | throw new RuntimeException(
64 | name + " implementation is not an instance of " + name + ": " + impl);
65 | } catch (ClassNotFoundException e) {
66 | throw new RuntimeException(name + " implementation class not found: " + impl, e);
67 | } catch (InstantiationException e) {
68 | throw new RuntimeException(name + " implementation not able to be instantiated: " + impl, e);
69 | } catch (IllegalAccessException e) {
70 | throw new RuntimeException(name + " implementation not able to be accessed: " + impl, e);
71 | }
72 | } else {
73 | return null;
74 | }
75 | }
76 |
77 | private static T findSPI(Class spi, ClassLoader classLoader) {
78 | ServiceLoader sl = ServiceLoader.load(spi,
79 | classLoader);
80 | for (T s : sl) {
81 | if (s != null) {
82 | return s;
83 | }
84 | }
85 | return null;
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/core/src/main/java/thinkinjava/conf/Property.java:
--------------------------------------------------------------------------------
1 | package thinkinjava.conf;
2 |
3 | public interface Property {
4 |
5 | T get();
6 |
7 | }
8 |
--------------------------------------------------------------------------------
/core/src/main/java/thinkinjava/conf/ServerConfig.java:
--------------------------------------------------------------------------------
1 | package thinkinjava.conf;
2 |
3 | import java.util.Map;
4 | import java.util.concurrent.ConcurrentHashMap;
5 |
6 | /**
7 | *
8 | *
9 | * @author 莫那·鲁道
10 | * @date 2018/10/16-下午8:29
11 | */
12 | public class ServerConfig {
13 |
14 | private final static Map CONFIG = new ConcurrentHashMap();
15 |
16 | public static Object getProperty(String key) {
17 | Object v = System.getProperty(key);
18 | if (v == null) {
19 | return CONFIG.get(key);
20 | }
21 |
22 | return null;
23 | }
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/core/src/main/java/thinkinjava/conf/ServerConstants.java:
--------------------------------------------------------------------------------
1 | package thinkinjava.conf;
2 |
3 | /**
4 | *
5 | *
6 | * @author 莫那·鲁道
7 | * @date 2018/10/16-下午8:32
8 | */
9 | public interface ServerConstants {
10 |
11 | String IP = "127.0.0.1";
12 | int PORT = 8081;
13 |
14 |
15 | }
16 |
--------------------------------------------------------------------------------
/core/src/main/java/thinkinjava/conf/package-info.java:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | *
4 | * @author 莫那·鲁道
5 | * @date 2019-01-26-11:06
6 | */
7 | package thinkinjava.conf;
8 |
9 | /**
10 | *
11 | * 这是一个动态配置, 配合分布式配置中心(例如 Apollo) 可动态修改配置.
12 | *
13 | * */
--------------------------------------------------------------------------------
/core/src/main/java/thinkinjava/core/BaseServiceConfig.java:
--------------------------------------------------------------------------------
1 | package thinkinjava.core;
2 |
3 | import thinkinjava.vmZK.LocalFileRegister;
4 | import thinkinjava.vmZK.ServiceRegisterDisCover;
5 |
6 | /**
7 | *
8 | *
9 | * @author 莫那·鲁道
10 | * @date 2018/10/15-下午9:41
11 | */
12 | public abstract class BaseServiceConfig implements ServiceConfig {
13 |
14 | protected ServiceRegisterDisCover serviceRegisterDisCover = new LocalFileRegister();
15 |
16 | private Class> service;
17 |
18 | public String getServiceName() {
19 | return service.getName();
20 | }
21 |
22 | public void setService(Class> service) {
23 | this.service = service;
24 | }
25 |
26 | public Class getService() {
27 | return service;
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/core/src/main/java/thinkinjava/core/ConsumerInvoker.java:
--------------------------------------------------------------------------------
1 | package thinkinjava.core;
2 |
3 | import thinkinjava.api.client.LuConsumerConfig;
4 | import thinkinjava.rpc.Request;
5 | import thinkinjava.rpc.Response;
6 | import thinkinjava.rpc.remoting.LuClient;
7 |
8 | /**
9 | *
10 | * @author 莫那·鲁道
11 | * @date 2018/10/14-下午10:28
12 | */
13 | public class ConsumerInvoker implements Invoker {
14 |
15 | private LuClient client;
16 | private LuConsumerConfig luConsumerConfig;
17 |
18 | public ConsumerInvoker(LuConsumerConfig luConsumerConfig) {
19 | this.luConsumerConfig = luConsumerConfig;
20 | this.client = LuClient.create();
21 | }
22 |
23 | public Response invoke(Request req) {
24 | preHandlerRequest(req);
25 | Response res = client.invoke(req);
26 | postHandlerResponse(res);
27 | return res;
28 | }
29 |
30 | public ServiceConfig getConfig() {
31 | return luConsumerConfig;
32 | }
33 |
34 |
35 | protected void preHandlerRequest(Request req) {
36 | }
37 |
38 | protected void postHandlerResponse(Response res) {
39 |
40 | }
41 |
42 | }
43 |
--------------------------------------------------------------------------------
/core/src/main/java/thinkinjava/core/Invoker.java:
--------------------------------------------------------------------------------
1 | package thinkinjava.core;
2 |
3 | import thinkinjava.rpc.Request;
4 | import thinkinjava.rpc.Response;
5 |
6 | /**
7 | * @author 莫那·鲁道
8 | * @date 2018/10/15-下午3:59
9 | */
10 | public interface Invoker {
11 |
12 | /**
13 | * Remote Calls
14 | * @param req 请求: 会话域
15 | * @return 结果:包装真正结果.
16 | */
17 | Response invoke(Request req) throws Throwable;
18 |
19 | /**
20 | * 获取该服务的配置
21 | *
22 | * @return 获取该服务的配置
23 | */
24 | ServiceConfig getConfig();
25 | }
26 |
--------------------------------------------------------------------------------
/core/src/main/java/thinkinjava/core/ProviderInvoker.java:
--------------------------------------------------------------------------------
1 | package thinkinjava.core;
2 |
3 | import java.lang.reflect.InvocationTargetException;
4 |
5 | import thinkinjava.api.server.LuProviderConfig;
6 | import thinkinjava.rpc.Request;
7 | import thinkinjava.rpc.Response;
8 |
9 | /**
10 | *
11 | *
12 | * @author 莫那·鲁道
13 | * @date 2018/10/16-下午7:41
14 | */
15 | @SuppressWarnings("unchecked")
16 | public class ProviderInvoker implements Invoker {
17 |
18 | private Object realObj;
19 | private ServiceConfig config;
20 |
21 | public ProviderInvoker(Object realObj, LuProviderConfig config) {
22 | this.realObj = realObj;
23 | this.config = config;
24 | }
25 |
26 | public Response invoke(Request req) {
27 | try {
28 | return new Response(req.getMethod().invoke(realObj, req.getArgs()));
29 | } catch (IllegalAccessException e) {
30 | e.printStackTrace();
31 | } catch (InvocationTargetException e) {
32 | e.printStackTrace();
33 | }
34 | return null;
35 | }
36 |
37 | public ServiceConfig getConfig() {
38 | return config;
39 | }
40 |
41 | }
42 |
--------------------------------------------------------------------------------
/core/src/main/java/thinkinjava/core/ServiceConfig.java:
--------------------------------------------------------------------------------
1 | package thinkinjava.core;
2 |
3 | import thinkinjava.rpc.remoting.Url;
4 |
5 | /**
6 | *
7 | * 服务配置类, 无论是客户端还是服务端, 通常需要接口名称, 设置接口类型, 以及接口 URL.
8 | *
9 | * @author 莫那·鲁道
10 | * @date 2018/10/15-下午4:23
11 | */
12 | public interface ServiceConfig {
13 |
14 | String getServiceName();
15 |
16 | void setService(Class> service);
17 |
18 | Url getServiceUrl();
19 | }
20 |
--------------------------------------------------------------------------------
/core/src/main/java/thinkinjava/event/Event.java:
--------------------------------------------------------------------------------
1 | package thinkinjava.event;
2 |
3 | /**
4 | */
5 | public interface Event {
6 |
7 | }
8 |
--------------------------------------------------------------------------------
/core/src/main/java/thinkinjava/event/EventBus.java:
--------------------------------------------------------------------------------
1 | package thinkinjava.event;
2 |
3 | import java.util.concurrent.ConcurrentHashMap;
4 | import java.util.concurrent.CopyOnWriteArraySet;
5 | import java.util.concurrent.ThreadPoolExecutor;
6 | import java.util.concurrent.atomic.AtomicBoolean;
7 |
8 | import org.slf4j.Logger;
9 | import org.slf4j.LoggerFactory;
10 |
11 | import thinkinjava.util.currence.LuThreadPoolFactory;
12 |
13 |
14 | /**
15 | * 事件总线.
16 | */
17 | public class EventBus {
18 |
19 | private static final Logger LOGGER = LoggerFactory.getLogger(EventBus.class);
20 |
21 | private static final ConcurrentHashMap, CopyOnWriteArraySet> MAP = new ConcurrentHashMap<>();
22 |
23 | private static final ThreadPoolExecutor EXECUTOR = LuThreadPoolFactory.getThreadPool(EventBus.class.getName());
24 |
25 | private static final AtomicBoolean flag = new AtomicBoolean();
26 |
27 | static {
28 | EXECUTOR.prestartAllCoreThreads();
29 | }
30 |
31 | private EventBus() {
32 | }
33 |
34 | public static void destroy() {
35 | EXECUTOR.shutdown();
36 | flag.compareAndSet(false, true);
37 | }
38 |
39 | /**
40 | * 发布事件.
41 | * @param event 事件.
42 | */
43 | public static void post(Event event) {
44 | if (flag.get()) {
45 | LOGGER.warn("EventBus always destroy, can not to be post event !");
46 | return;
47 | }
48 | if (event == null) {
49 | return;
50 | }
51 | CopyOnWriteArraySet set = MAP.get(event.getClass());
52 | if (set == null) {
53 | return;
54 | }
55 | for (Subscriber subscribe : set) {
56 | if (subscribe.isSync()) {
57 | handleEvent(event, subscribe);
58 | } else {
59 | EXECUTOR.execute(new Runnable() {
60 | @Override
61 | public void run() {
62 | handleEvent(event, subscribe);
63 | }
64 | });
65 |
66 | }
67 |
68 | }
69 | }
70 |
71 |
72 | /**
73 | * 订阅事件.
74 | * @param eventClass 事件类型.
75 | * @param subscriber 订阅者.
76 | */
77 | public static void register(Class extends Event> eventClass, Subscriber subscriber) {
78 | CopyOnWriteArraySet set = MAP.get(eventClass);
79 | if (set == null) {
80 | set = new CopyOnWriteArraySet<>();
81 | CopyOnWriteArraySet old = MAP.putIfAbsent(eventClass, set);
82 | if (old != null) {
83 | set = old;
84 | }
85 |
86 | }
87 | set.add(subscriber);
88 |
89 | LOGGER.debug("subscriber : {} register a event: {} ", subscriber, eventClass);
90 | }
91 |
92 | /**
93 | * 取消订阅事件.
94 | *
95 | * @param eventClass 事件类型.
96 | * @param subscriber 订阅者.
97 | */
98 | public static void unRegister(Class extends Event> eventClass, Subscriber subscriber) {
99 | CopyOnWriteArraySet set = MAP.get(eventClass);
100 | if (set == null) {
101 | set = new CopyOnWriteArraySet<>();
102 | CopyOnWriteArraySet old = MAP.putIfAbsent(eventClass, set);
103 | if (old != null) {
104 | set = old;
105 | }
106 |
107 | }
108 | set.remove(subscriber);
109 | LOGGER.debug("subscriber : {} unRegister a event: {} ", subscriber, eventClass);
110 |
111 | }
112 |
113 |
114 | /**
115 | * 处理事件.
116 | * @param event 事件.
117 | * @param subscriber 订阅者.
118 | */
119 | private static void handleEvent(final Event event, final Subscriber subscriber) {
120 | try {
121 | subscriber.onEvent(event);
122 | } catch (Exception e) {
123 | LOGGER.error("subscriber {} handler event {} fail ", subscriber, event, e);
124 | }
125 | }
126 |
127 | }
128 |
--------------------------------------------------------------------------------
/core/src/main/java/thinkinjava/event/Subscriber.java:
--------------------------------------------------------------------------------
1 | package thinkinjava.event;
2 |
3 | /**
4 | *
5 | * 事件订阅者.
6 | */
7 | public interface Subscriber {
8 |
9 | /**
10 | * 发生事件时回调此方法.
11 | * @param event
12 | */
13 | void onEvent(Event event);
14 |
15 | /**
16 | * 同步处理还是异步处理, 注意: 同步处理将会影响框架吞吐量.
17 | * @return
18 | */
19 | default boolean isSync() {
20 | return false;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/core/src/main/java/thinkinjava/event/package-info.java:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * @author 莫那·鲁道
4 | * @date 2019-01-26-11:04
5 | */
6 | package thinkinjava.event;
7 | /**
8 | * 类似 guava 的 EventBus 设计, 通过机制解耦模块之间的耦合关系.
9 | * */
--------------------------------------------------------------------------------
/core/src/main/java/thinkinjava/exception/CircuitBreakerException.java:
--------------------------------------------------------------------------------
1 | package thinkinjava.exception;
2 |
3 | /**
4 | *
5 | * 熔断异常. 通常不能算做熔断统计的数据依据.
6 | */
7 | public class CircuitBreakerException extends RuntimeException {
8 |
9 | public CircuitBreakerException() {
10 | }
11 |
12 | public CircuitBreakerException(String message) {
13 | super(message);
14 | }
15 |
16 | public CircuitBreakerException(String message, Throwable cause) {
17 | super(message, cause);
18 | }
19 |
20 | public CircuitBreakerException(Throwable cause) {
21 | super(cause);
22 | }
23 |
24 | public CircuitBreakerException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
25 | super(message, cause, enableSuppression, writableStackTrace);
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/core/src/main/java/thinkinjava/exception/FlowBufferAddException.java:
--------------------------------------------------------------------------------
1 | package thinkinjava.exception;
2 |
3 | /**
4 | *
5 | * 流量缓冲区溢出.
6 | */
7 | public class FlowBufferAddException extends RuntimeException {
8 |
9 | public FlowBufferAddException() {
10 | }
11 |
12 | public FlowBufferAddException(String message) {
13 | super(message);
14 | }
15 |
16 | public FlowBufferAddException(String message, Throwable cause) {
17 | super(message, cause);
18 | }
19 |
20 | public FlowBufferAddException(Throwable cause) {
21 | super(cause);
22 | }
23 |
24 | public FlowBufferAddException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
25 | super(message, cause, enableSuppression, writableStackTrace);
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/core/src/main/java/thinkinjava/exception/LimitException.java:
--------------------------------------------------------------------------------
1 | package thinkinjava.exception;
2 |
3 | /**
4 | *
5 | * 限流异常. 通常不能算做熔断统计的数据依据.
6 | */
7 | public class LimitException extends RuntimeException {
8 |
9 | public LimitException() {
10 | }
11 |
12 | public LimitException(String message) {
13 | super(message);
14 | }
15 |
16 | public LimitException(String message, Throwable cause) {
17 | super(message, cause);
18 | }
19 |
20 | public LimitException(Throwable cause) {
21 | super(cause);
22 | }
23 |
24 | public LimitException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
25 | super(message, cause, enableSuppression, writableStackTrace);
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/core/src/main/java/thinkinjava/exception/LuTimeoutException.java:
--------------------------------------------------------------------------------
1 | package thinkinjava.exception;
2 |
3 | /**
4 | *
5 | * 超时异常.
6 | */
7 | public class LuTimeoutException extends RuntimeException {
8 |
9 |
10 | public LuTimeoutException() {
11 | }
12 |
13 | public LuTimeoutException(String message) {
14 | super(message);
15 | }
16 |
17 | public LuTimeoutException(String message, Throwable cause) {
18 | super(message, cause);
19 | }
20 |
21 | public LuTimeoutException(Throwable cause) {
22 | super(cause);
23 | }
24 |
25 | public LuTimeoutException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
26 | super(message, cause, enableSuppression, writableStackTrace);
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/core/src/main/java/thinkinjava/exception/UserOperationException.java:
--------------------------------------------------------------------------------
1 | package thinkinjava.exception;
2 |
3 | /**
4 | *
5 | * 用户操作错误异常.
6 | *
7 | * @author 莫那·鲁道
8 | * @date 2019-01-26-12:20
9 | */
10 | public class UserOperationException extends RuntimeException {
11 |
12 | public UserOperationException() {
13 | }
14 |
15 | public UserOperationException(String message) {
16 | super(message);
17 | }
18 |
19 | public UserOperationException(String message, Throwable cause) {
20 | super(message, cause);
21 | }
22 |
23 | public UserOperationException(Throwable cause) {
24 | super(cause);
25 | }
26 |
27 | public UserOperationException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
28 | super(message, cause, enableSuppression, writableStackTrace);
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/core/src/main/java/thinkinjava/filter/ExampleFilter.java:
--------------------------------------------------------------------------------
1 | package thinkinjava.filter;
2 |
3 | import thinkinjava.core.Invoker;
4 | import thinkinjava.rpc.Request;
5 | import thinkinjava.rpc.Response;
6 |
7 | /**
8 | * @author 莫那·鲁道
9 | * @date 2018/10/14-下午10:04
10 | */
11 | public class ExampleFilter implements Filter {
12 |
13 | @Override
14 | public Response filter(Invoker invoker, Request req) throws Throwable {
15 | if (req == null) {
16 | throw new IllegalArgumentException("req not be null");
17 | }
18 | System.out.println("ExampleFilter ");
19 | return invoker.invoke(req);
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/core/src/main/java/thinkinjava/filter/ExampleFilter2.java:
--------------------------------------------------------------------------------
1 | package thinkinjava.filter;
2 |
3 | import thinkinjava.core.Invoker;
4 | import thinkinjava.rpc.Request;
5 | import thinkinjava.rpc.Response;
6 |
7 | /**
8 | * @author 莫那·鲁道
9 | * @date 2018/10/14-下午10:04
10 | */
11 | public class ExampleFilter2 implements Filter {
12 |
13 | @Override
14 | public Response filter(Invoker invoker, Request req) throws Throwable {
15 | if (req == null) {
16 | throw new IllegalArgumentException("req not be null");
17 | }
18 | System.out.println("ExampleFilter 2 ");
19 | return invoker.invoke(req);
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/core/src/main/java/thinkinjava/filter/Filter.java:
--------------------------------------------------------------------------------
1 | package thinkinjava.filter;
2 |
3 | import thinkinjava.core.Invoker;
4 | import thinkinjava.rpc.Request;
5 | import thinkinjava.rpc.Response;
6 |
7 | /**
8 | * @author 莫那·鲁道
9 | * @date 2018/10/14-下午10:02
10 | */
11 | public interface Filter {
12 |
13 | /**
14 | * 过滤器设计
15 | * @param invoker
16 | * @param req
17 | * @return
18 | */
19 | Response filter(Invoker invoker, Request req) throws Throwable;
20 | }
21 |
--------------------------------------------------------------------------------
/core/src/main/java/thinkinjava/filter/FilterChain.java:
--------------------------------------------------------------------------------
1 | package thinkinjava.filter;
2 |
3 | import java.util.List;
4 |
5 | import thinkinjava.core.Invoker;
6 |
7 | /**
8 | *
9 | *
10 | * @author 莫那·鲁道
11 | * @date 2018/10/14-下午10:02
12 | */
13 | public class FilterChain {
14 |
15 | private List filters;
16 |
17 | private Invoker invoker;
18 |
19 |
20 | public FilterChain(List filters, Invoker invoker) {
21 | this.filters = filters;
22 | this.invoker = invoker;
23 | }
24 |
25 | public FilterChain(Invoker invoker) {
26 | this(LoadFilters.create().getFilters(), invoker);
27 | }
28 |
29 | public Invoker buildFilterChain() {
30 | // 最后一个
31 | Invoker last = invoker;
32 |
33 | for (int i = filters.size() - 1; i >= 0; i--) {
34 | last = new FilterWrapper(filters.get(i), last);
35 | }
36 | // 第一个
37 | return last;
38 |
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/core/src/main/java/thinkinjava/filter/FilterWrapper.java:
--------------------------------------------------------------------------------
1 | package thinkinjava.filter;
2 |
3 | import thinkinjava.core.Invoker;
4 | import thinkinjava.core.ServiceConfig;
5 | import thinkinjava.rpc.Request;
6 | import thinkinjava.rpc.Response;
7 |
8 | /**
9 | * @author 莫那·鲁道
10 | * @date 2018/10/16-下午5:01
11 | */
12 | public class FilterWrapper implements Invoker {
13 |
14 | private Filter next;
15 |
16 | private Invoker invoker;
17 |
18 | private ServiceConfig config;
19 |
20 | public FilterWrapper(Filter next, Invoker invoker) {
21 | this.next = next;
22 | this.invoker = invoker;
23 | this.config = invoker.getConfig();
24 | }
25 |
26 |
27 | @Override
28 | public Response invoke(Request args) throws Throwable {
29 | if (next != null) {
30 | return next.filter(invoker, args);
31 | } else {
32 | return invoker.invoke(args);
33 | }
34 | }
35 |
36 | public ServiceConfig getConfig() {
37 | return config;
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/core/src/main/java/thinkinjava/filter/LoadFilters.java:
--------------------------------------------------------------------------------
1 | package thinkinjava.filter;
2 |
3 | import java.util.ArrayList;
4 | import java.util.List;
5 |
6 | import thinkinjava.breaker.CircuitBreakerFilter;
7 | import thinkinjava.breaker.CircuitBreakerStatFilter;
8 | import thinkinjava.flow.limit.LimitFilter;
9 |
10 | /**
11 | * TODO 自动加载过滤器
12 | *
13 | * @author 莫那·鲁道
14 | * @date 2018/10/14-下午10:43
15 | */
16 | public final class LoadFilters {
17 |
18 | private List filterList = new ArrayList();
19 |
20 | private LoadFilters() {
21 | }
22 |
23 | public static LoadFilters create() {
24 |
25 | return new LoadFilters();
26 | }
27 |
28 | public List getFilters() {
29 | this.filterList.add(new ExampleFilter());
30 | this.filterList.add(new ExampleFilter2());
31 | this.filterList.add(CircuitBreakerFilter.getInstance());
32 | this.filterList.add(LimitFilter.getInstance());
33 | this.filterList.add(CircuitBreakerStatFilter.getInstance());
34 | return filterList;
35 | }
36 |
37 | }
38 |
--------------------------------------------------------------------------------
/core/src/main/java/thinkinjava/filter/package-info.java:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * @author 莫那·鲁道
4 | * @date 2019-01-26-11:07
5 | */
6 | package thinkinjava.filter;
7 |
8 | /**
9 | * RPC 框架过滤器, 使用责任链模式, 解耦组件之间的关系. 使用链表设计,可动态修改顺序.
10 | * */
--------------------------------------------------------------------------------
/core/src/main/java/thinkinjava/flow/limit/FlowBufferManager.java:
--------------------------------------------------------------------------------
1 | package thinkinjava.flow.limit;
2 |
3 | import java.util.concurrent.ConcurrentHashMap;
4 |
5 | import thinkinjava.util.currence.FlowBuffer;
6 |
7 | public class FlowBufferManager {
8 |
9 | private ConcurrentHashMap cache = new ConcurrentHashMap<>();
10 |
11 |
12 | public static FlowBufferManager getInstance() {
13 | return FlowBufferManagerInner.INSTANCE;
14 | }
15 |
16 | private FlowBufferManager() {
17 | }
18 |
19 | private static class FlowBufferManagerInner {
20 |
21 | private static final FlowBufferManager INSTANCE = new FlowBufferManager();
22 | }
23 |
24 | /**
25 | * 获取 Buffer
26 | * @param unique ..
27 | * @return PairFlowBuffer
28 | */
29 | public FlowBuffer getBuffer(String unique) {
30 | FlowBuffer buffer = cache.get(unique);
31 | if (buffer == null) {
32 | buffer = FlowBuffer.newBuilder().unique(unique).build();
33 | FlowBuffer old = cache.putIfAbsent(unique, buffer);
34 | if (old != null) {
35 | buffer = old;
36 | }
37 | }
38 | return buffer;
39 | }
40 |
41 | }
42 |
--------------------------------------------------------------------------------
/core/src/main/java/thinkinjava/flow/limit/LimitEvent.java:
--------------------------------------------------------------------------------
1 | package thinkinjava.flow.limit;
2 |
3 | import java.util.Date;
4 |
5 | import thinkinjava.event.Event;
6 |
7 | /**
8 | *
9 | * 限流异常导致的限流事件.
10 | */
11 | public class LimitEvent implements Event {
12 |
13 | /** 限流时间 */
14 | private Date date;
15 |
16 |
17 | private LimitEvent(Builder builder) {
18 | setDate(builder.date);
19 | }
20 |
21 | public static Builder newBuilder() {
22 | return new Builder();
23 | }
24 |
25 | public Date getDate() {
26 | return date;
27 | }
28 |
29 | public void setDate(Date date) {
30 | this.date = date;
31 | }
32 |
33 |
34 |
35 | public static final class Builder {
36 |
37 | private boolean syncRequest;
38 | private Date date;
39 |
40 | private Builder() {
41 | }
42 |
43 |
44 | public Builder syncRequest(boolean val) {
45 | syncRequest = val;
46 | return this;
47 | }
48 |
49 | public Builder date(Date val) {
50 | date = val;
51 | return this;
52 | }
53 |
54 | public LimitEvent build() {
55 | return new LimitEvent(this);
56 | }
57 | }
58 | }
59 |
60 |
--------------------------------------------------------------------------------
/core/src/main/java/thinkinjava/flow/limit/LimitFilter.java:
--------------------------------------------------------------------------------
1 | package thinkinjava.flow.limit;
2 |
3 | import java.util.Date;
4 |
5 | import org.slf4j.Logger;
6 | import org.slf4j.LoggerFactory;
7 |
8 | import thinkinjava.core.Invoker;
9 | import thinkinjava.event.EventBus;
10 | import thinkinjava.exception.FlowBufferAddException;
11 | import thinkinjava.exception.LimitException;
12 | import thinkinjava.exception.LuTimeoutException;
13 | import thinkinjava.filter.Filter;
14 | import thinkinjava.rpc.Request;
15 | import thinkinjava.rpc.Response;
16 | import thinkinjava.util.currence.FlowBuffer;
17 | import thinkinjava.util.currence.Semaphore;
18 |
19 |
20 | /**
21 | * 限流过滤器.
22 | */
23 | public class LimitFilter implements Filter {
24 |
25 | private static final Logger LOGGER = LoggerFactory.getLogger(LimitFilter.class);
26 |
27 |
28 | private static final int RETRY_COUNT = 1;
29 |
30 | private LimitFilter() {
31 | }
32 |
33 | public static LimitFilter getInstance() {
34 | return LimitFilterLazyHolder.INSTANCE;
35 | }
36 |
37 | private static class LimitFilterLazyHolder {
38 |
39 | private static final LimitFilter INSTANCE = new LimitFilter();
40 | }
41 |
42 |
43 | @Override
44 | public Response filter(Invoker invoker, Request request) throws Throwable {
45 |
46 | LOGGER.info("LimitFilter filtering");
47 |
48 | String unique = request.getUnique();
49 |
50 | Semaphore semaphore = SemaphoreManager.getInstance().getSemaphore(unique);
51 |
52 | int count = 0;
53 | loop:
54 | for (; ; ) {
55 |
56 | FlowBuffer flowBuffer = FlowBufferManager.getInstance().getBuffer(unique);
57 | if (semaphore.tryAcquire()) {
58 | try {
59 | // 这里不处理异常, 直接抛出.
60 | return invoker.invoke(request);
61 | } finally {
62 | semaphore.release();
63 | flowBuffer.notifyBuffer();
64 | }
65 | } // 抢信号量失败
66 | else {
67 | try {
68 | // 仅仅等待一次
69 | if (++count <= RETRY_COUNT) {
70 | // 进入队列可能会失败, 也可能会等待超时. 我们都认为是限流异常.
71 | flowBuffer.addBufferAndWait();
72 | // 唤醒后, 再次重新尝试获取信号量.
73 | continue loop;
74 | }
75 | break;
76 | } catch (LuTimeoutException | FlowBufferAddException e) {
77 | // 忽略, 认为是限流异常.
78 | }
79 |
80 | }
81 | }
82 | // 限流事件
83 | EventBus.post(LimitEvent.newBuilder().date(new Date()).build());
84 |
85 | return throwsException();
86 | }
87 |
88 |
89 | private Response throwsException() {
90 | throw new LimitException();
91 | }
92 |
93 | }
94 |
--------------------------------------------------------------------------------
/core/src/main/java/thinkinjava/flow/limit/SemaphoreManager.java:
--------------------------------------------------------------------------------
1 | package thinkinjava.flow.limit;
2 |
3 | import java.util.concurrent.ConcurrentHashMap;
4 |
5 | import thinkinjava.util.currence.Semaphore;
6 |
7 | public class SemaphoreManager{
8 |
9 | private ConcurrentHashMap map = new ConcurrentHashMap<>();
10 |
11 | private SemaphoreManager(){}
12 |
13 | public static SemaphoreManager getInstance() {
14 | return SemaphoreManagerLazyHolder.INSTANCE;
15 | }
16 |
17 | private static class SemaphoreManagerLazyHolder {
18 |
19 | private static final SemaphoreManager INSTANCE = new SemaphoreManager();
20 | }
21 |
22 | public void putSemaphore(String unique, Semaphore semaphore) {
23 | map.put(unique, semaphore);
24 | }
25 |
26 | public Semaphore getSemaphore(String unique) {
27 | Semaphore semaphore = map.get(unique);
28 | if (semaphore == null) {
29 | semaphore = new Semaphore(unique, 10);
30 | Semaphore old = map.putIfAbsent(unique, semaphore);
31 | if (old != null) {
32 | semaphore = old;
33 | }
34 | }
35 | return semaphore;
36 | }
37 |
38 |
39 |
40 |
41 | }
42 |
--------------------------------------------------------------------------------
/core/src/main/java/thinkinjava/flow/limit/package-info.java:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * @author 莫那·鲁道
4 | * @date 2019-01-26-11:08
5 | */
6 | package thinkinjava.flow.limit;
7 | /**
8 | * 限流模块, 利用了 EventBus 和 Filter, 实现限流, 目前设计了缓存区. 即限流后,会被加入到一个限时缓存区.
9 | * */
--------------------------------------------------------------------------------
/core/src/main/java/thinkinjava/proxy/ConsumerInterceptor.java:
--------------------------------------------------------------------------------
1 | package thinkinjava.proxy;
2 |
3 | import java.lang.reflect.InvocationHandler;
4 | import java.lang.reflect.Method;
5 |
6 | import thinkinjava.core.Invoker;
7 | import thinkinjava.filter.FilterChain;
8 | import thinkinjava.rpc.Request;
9 | import thinkinjava.rpc.Response;
10 |
11 | /**
12 | *
13 | * @author 莫那·鲁道
14 | * @date 2018/10/14-下午9:59
15 | */
16 | public class ConsumerInterceptor implements InvocationHandler {
17 |
18 | private Invoker invoker;
19 |
20 | private FilterChain chain;
21 |
22 | public ConsumerInterceptor(Invoker invoker) {
23 | // 过滤器链,链尾是真正的 invoker
24 | this.chain = new FilterChain(invoker);
25 | this.invoker = this.chain.buildFilterChain();
26 | }
27 |
28 | public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
29 |
30 | // 构造请求体
31 | Request req = buildRequest(method, args);
32 |
33 | // 2. 调用过滤器链
34 | Response res = invoker.invoke(req);
35 | return res.getResult();
36 | }
37 |
38 | /**
39 | * 构造请求体
40 | */
41 | private Request buildRequest(Method method, Object[] args) {
42 | Request req = new Request();
43 | req.setServiceName(invoker.getConfig().getServiceName());
44 | req.setArgs(args);
45 | req.setMethodName(method.getName());
46 | req.setIp(invoker.getConfig().getServiceUrl().getIp());
47 | req.setPort(invoker.getConfig().getServiceUrl().getPort());
48 | req.setArgTypes(method.getParameterTypes());
49 |
50 | return req;
51 | }
52 |
53 | }
54 |
--------------------------------------------------------------------------------
/core/src/main/java/thinkinjava/proxy/ProviderInterceptor.java:
--------------------------------------------------------------------------------
1 | package thinkinjava.proxy;
2 |
3 | import java.lang.reflect.InvocationHandler;
4 | import java.lang.reflect.Method;
5 |
6 | import thinkinjava.core.Invoker;
7 | import thinkinjava.filter.FilterChain;
8 | import thinkinjava.rpc.Request;
9 | import thinkinjava.rpc.Response;
10 |
11 | /**
12 | *
13 | * @author 莫那·鲁道
14 | * @date 2018/10/14-下午9:59
15 | */
16 | public class ProviderInterceptor implements InvocationHandler {
17 |
18 | private Invoker invoker;
19 |
20 | private FilterChain chain;
21 |
22 | public ProviderInterceptor(Invoker invoker) {
23 | // 过滤器链,链尾是真正的 invoker
24 | this.chain = new FilterChain(invoker);
25 | this.invoker = this.chain.buildFilterChain();
26 | }
27 |
28 | public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
29 |
30 | // 构造请求体
31 | Request req = buildRequest(method, args);
32 |
33 | // 2. 调用过滤器链
34 | Response res = invoker.invoke(req);
35 | return res.getResult();
36 | }
37 |
38 | /**
39 | * 构造请求体
40 | */
41 | private Request buildRequest(Method method, Object[] args) {
42 | Request req = new Request();
43 | req.setServiceName(invoker.getConfig().getServiceName());
44 | req.setArgs(args);
45 | req.setMethodName(method.getName());
46 | req.setIp(invoker.getConfig().getServiceUrl().getIp());
47 | req.setPort(invoker.getConfig().getServiceUrl().getPort());
48 | req.setArgTypes(method.getParameterTypes());
49 | req.setMethod(method);
50 |
51 | return req;
52 | }
53 |
54 | }
55 |
--------------------------------------------------------------------------------
/core/src/main/java/thinkinjava/proxy/ProxyFactory.java:
--------------------------------------------------------------------------------
1 | package thinkinjava.proxy;
2 |
3 | import java.lang.reflect.InvocationHandler;
4 | import java.lang.reflect.Proxy;
5 |
6 | /**
7 | *
8 | *
9 | * @author 莫那·鲁道
10 | * @date 2018/10/14-下午9:59
11 | */
12 | public class ProxyFactory {
13 |
14 | public static Object getProxy(Class> origin, InvocationHandler h) {
15 | return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class[]{origin},
16 | h);
17 | }
18 |
19 |
20 |
21 |
22 | }
23 |
--------------------------------------------------------------------------------
/core/src/main/java/thinkinjava/proxy/package-info.java:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * @author 莫那·鲁道
4 | * @date 2019-01-26-11:14
5 | */
6 | package thinkinjava.proxy;
7 |
8 | /**
9 | * 动态代理相关, 例如代理类生成工厂, 消费者拦截器, 提供者拦截器.
10 | * */
--------------------------------------------------------------------------------
/core/src/main/java/thinkinjava/rpc/Request.java:
--------------------------------------------------------------------------------
1 | package thinkinjava.rpc;
2 |
3 | import java.io.Serializable;
4 | import java.lang.reflect.Method;
5 |
6 | /**
7 | * @author 莫那·鲁道
8 | * @date 2018/10/14-下午9:39
9 | */
10 | public class Request implements Serializable {
11 |
12 | private String serviceName;
13 |
14 | private String className;
15 |
16 | private String methodName;
17 |
18 | private Method method;
19 |
20 | private Object[] args;
21 |
22 | private Class>[] argTypes;
23 |
24 | private String ip;
25 |
26 | private int port;
27 |
28 | public String getServiceName() {
29 | return serviceName;
30 | }
31 |
32 | public void setServiceName(String serviceName) {
33 | this.serviceName = serviceName;
34 | }
35 |
36 | public String getMethodName() {
37 | return methodName;
38 | }
39 |
40 | public void setMethodName(String methodName) {
41 | this.methodName = methodName;
42 | }
43 |
44 | public String getClassName() {
45 | return className;
46 | }
47 |
48 | public void setClassName(String className) {
49 | this.className = className;
50 | }
51 |
52 | public Object[] getArgs() {
53 | return args;
54 | }
55 |
56 | public void setArgs(Object[] args) {
57 | this.args = args;
58 | }
59 |
60 | public Method getMethod() {
61 | return method;
62 | }
63 |
64 | public void setMethod(Method method) {
65 | this.method = method;
66 | }
67 |
68 | public Class>[] getArgTypes() {
69 | return argTypes;
70 | }
71 |
72 | public void setArgTypes(Class>[] argTypes) {
73 | this.argTypes = argTypes;
74 | }
75 |
76 | public String getIp() {
77 | return ip;
78 | }
79 |
80 | public void setIp(String ip) {
81 | this.ip = ip;
82 | }
83 |
84 | public int getPort() {
85 | return port;
86 | }
87 |
88 | public String getUrl() {
89 | return ip + ":" + port;
90 | }
91 |
92 | public void setUrl(String url) {
93 | if (url != null) {
94 | String[] arr = url.split(":");
95 | ip = arr[0];
96 | port = Integer.valueOf(arr[1]);
97 | }
98 | }
99 |
100 | public void setPort(int port) {
101 | this.port = port;
102 | }
103 |
104 | public String getUnique() {
105 | return ip + ":" + port + "$" + serviceName;
106 | }
107 |
108 |
109 | }
110 |
--------------------------------------------------------------------------------
/core/src/main/java/thinkinjava/rpc/Response.java:
--------------------------------------------------------------------------------
1 | package thinkinjava.rpc;
2 |
3 | import java.io.Serializable;
4 |
5 | /**
6 | *
7 | * @author 莫那·鲁道
8 | * @date 2018/10/14-下午9:39
9 | */
10 | public class Response implements Serializable {
11 |
12 | private Object result;
13 |
14 | public Response(Object result) {
15 | this.result = result;
16 | }
17 |
18 | public Object getResult() {
19 | return result;
20 | }
21 |
22 | public void setResult(Object result) {
23 | this.result = result;
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/core/src/main/java/thinkinjava/rpc/remoting/LuClient.java:
--------------------------------------------------------------------------------
1 | package thinkinjava.rpc.remoting;
2 |
3 | import com.alipay.remoting.exception.RemotingException;
4 | import com.alipay.remoting.rpc.RpcClient;
5 |
6 | import thinkinjava.rpc.Request;
7 | import thinkinjava.rpc.Response;
8 |
9 | /**
10 | * @author 莫那·鲁道
11 | * @date 2018/10/14-下午8:38
12 | */
13 | public class LuClient {
14 |
15 | private final static RpcClient client = new RpcClient();
16 | static {
17 | client.init();
18 | }
19 |
20 | public static LuClient create() {
21 | return new LuClient();
22 | }
23 |
24 | private LuClient() {
25 | }
26 |
27 | public Response invoke(Request req) {
28 | try {
29 |
30 | Object result = client.invokeSync(req.getUrl(), req, 2000);
31 | return new Response(result);
32 |
33 | } catch (RemotingException e) {
34 | e.printStackTrace();
35 | } catch (InterruptedException e) {
36 | e.printStackTrace();
37 | }
38 | throw new RuntimeException("invoke error");
39 | }
40 |
41 |
42 | }
43 |
--------------------------------------------------------------------------------
/core/src/main/java/thinkinjava/rpc/remoting/LuServer.java:
--------------------------------------------------------------------------------
1 | package thinkinjava.rpc.remoting;
2 |
3 | import com.alipay.remoting.rpc.RpcServer;
4 | import com.alipay.remoting.rpc.protocol.UserProcessor;
5 |
6 | /**
7 | *
8 | * @author 莫那·鲁道
9 | * @date 2018/10/14-下午8:49
10 | */
11 | public class LuServer {
12 |
13 | private static RpcServer rpcServer;
14 |
15 | private volatile boolean flag;
16 |
17 | public static LuServer create(int port) {
18 | return new LuServer("127.0.0.1", port, false);
19 | }
20 |
21 | LuServer(String ip, int port, boolean manageConnection) {
22 | rpcServer = new RpcServer(ip, port, manageConnection);
23 | rpcServer.registerUserProcessor(new LuUserProcessor());
24 | }
25 |
26 | public void start() {
27 | if (flag) {
28 | return;
29 | }
30 | rpcServer.start();
31 | flag = true;
32 | }
33 |
34 | public void stop() {
35 | rpcServer.stop();
36 | }
37 |
38 | public RpcServer getServer() {
39 | return rpcServer;
40 | }
41 |
42 | public void registerUserProcessor(UserProcessor userProcessor) {
43 | rpcServer.registerUserProcessor(userProcessor);
44 | }
45 |
46 |
47 | }
48 |
--------------------------------------------------------------------------------
/core/src/main/java/thinkinjava/rpc/remoting/LuUserProcessor.java:
--------------------------------------------------------------------------------
1 | package thinkinjava.rpc.remoting;
2 |
3 | import java.lang.reflect.Method;
4 | import java.util.Map;
5 | import java.util.concurrent.ConcurrentHashMap;
6 |
7 | import com.alipay.remoting.AsyncContext;
8 | import com.alipay.remoting.BizContext;
9 | import com.alipay.remoting.rpc.protocol.AbstractUserProcessor;
10 |
11 | import thinkinjava.api.server.LuProvider;
12 | import thinkinjava.api.server.ProviderManager;
13 | import thinkinjava.rpc.Request;
14 |
15 | /**
16 | *
17 | *
18 | * @author 莫那·鲁道
19 | * @date 2018/10/14-下午8:56
20 | */
21 | @SuppressWarnings("unchecked")
22 | public class LuUserProcessor extends AbstractUserProcessor {
23 |
24 | /** Just for Test */
25 | public static Map methods = new ConcurrentHashMap();
26 | /** Just for Test */
27 | public static Map targets = new ConcurrentHashMap();
28 |
29 |
30 | public LuUserProcessor() {
31 | }
32 |
33 | public void handleRequest(BizContext bizCtx, AsyncContext asyncCtx, Request request) {
34 | // NOOP
35 | }
36 |
37 | public Object handleRequest(BizContext bizCtx, Request request) throws Exception {
38 | LuProvider provider = ProviderManager.getProvider(request.getServiceName());
39 | request.setMethod(provider.getConfig().getService().getDeclaredMethod(request.getMethodName(), request.getArgTypes()));
40 |
41 | return request.getMethod().invoke(provider.getConfig().getRefProxy(), request.getArgs());
42 | }
43 |
44 |
45 | /**
46 | * 用户请求的类名。
47 | * 使用String类型来避免类加载器问题。
48 | */
49 | public String interest() {
50 | return Request.class.getName();
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/core/src/main/java/thinkinjava/rpc/remoting/Url.java:
--------------------------------------------------------------------------------
1 | package thinkinjava.rpc.remoting;
2 |
3 | import java.io.Serializable;
4 |
5 | /**
6 | *
7 | *
8 | * @author 莫那·鲁道
9 | * @date 2018/10/16-下午6:08
10 | */
11 | public class Url implements Serializable {
12 |
13 | public static Url DEFAULT = new Url();
14 |
15 | String ip = "127.0.0.1";
16 |
17 | int port = 8081;
18 |
19 | String password = "helloWorld";
20 |
21 | String token = "LoDao";
22 |
23 | public Url() {
24 | }
25 |
26 | public Url(String ip, int port) {
27 | this.ip = ip;
28 | this.port = port;
29 | }
30 |
31 | public Url(int port) {
32 | this.port = port;
33 | }
34 |
35 | public String getIp() {
36 | return ip;
37 | }
38 |
39 | public void setIp(String ip) {
40 | this.ip = ip;
41 | }
42 |
43 | public int getPort() {
44 | return port;
45 | }
46 |
47 | public void setPort(int port) {
48 | this.port = port;
49 | }
50 |
51 | public String getPassword() {
52 | return password;
53 | }
54 |
55 | public void setPassword(String password) {
56 | this.password = password;
57 | }
58 |
59 | public String getToken() {
60 | return token;
61 | }
62 |
63 | public void setToken(String token) {
64 | this.token = token;
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/core/src/main/java/thinkinjava/util/CloseUtil.java:
--------------------------------------------------------------------------------
1 | package thinkinjava.util;
2 |
3 | import java.io.Closeable;
4 | import java.io.IOException;
5 |
6 | /**
7 | * 简化 Java 里繁琐的 close.
8 | *
9 | * @author 莫那·鲁道
10 | * @date 2018/10/15-上午7:06
11 | */
12 | public class CloseUtil {
13 |
14 | public static void close(Closeable closeable) {
15 | try {
16 | if (closeable != null) {
17 | closeable.close();
18 | }
19 | } catch (IOException e) {
20 | e.printStackTrace();
21 | }
22 | }
23 |
24 | }
25 |
--------------------------------------------------------------------------------
/core/src/main/java/thinkinjava/util/SimpleDateFormatUtil.java:
--------------------------------------------------------------------------------
1 | package thinkinjava.util;
2 |
3 | import java.text.SimpleDateFormat;
4 | import java.util.Date;
5 |
6 | /**
7 | *
8 | * 支持 3 种格式
9 | * 1. yyyy-MM-dd HH:mm:ss:sss
10 | * 2. yyyy-MM-dd HH:mm:ss
11 | * 3. yyyy-MM-dd HH:mm
12 | */
13 | public class SimpleDateFormatUtil {
14 |
15 | /**
16 | * 使用 ThreadLocal 包装 SimpleDateFormat,防止并发问题, 相比较每次 new 节约资源, 加快速度.
17 | */
18 | private final static ThreadLocal simpleDateFormat1 = new ThreadLocal() {
19 | @Override
20 | protected SimpleDateFormat initialValue() {
21 | return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:sss");
22 | }
23 | };
24 |
25 | private final static ThreadLocal simpleDateFormat2 = new ThreadLocal() {
26 | @Override
27 | protected SimpleDateFormat initialValue() {
28 | return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
29 | }
30 | };
31 |
32 | private final static ThreadLocal simpleDateFormat3 = new ThreadLocal() {
33 | @Override
34 | protected SimpleDateFormat initialValue() {
35 | return new SimpleDateFormat("yyyy-MM-dd HH:mm");
36 | }
37 | };
38 |
39 | public static String format1(Date date) {
40 | return simpleDateFormat1.get().format(date);
41 | }
42 |
43 | public static String format2(Date date) {
44 | return simpleDateFormat2.get().format(date);
45 | }
46 |
47 | public static String format3(Date date) {
48 | return simpleDateFormat3.get().format(date);
49 | }
50 |
51 |
52 | }
53 |
--------------------------------------------------------------------------------
/core/src/main/java/thinkinjava/util/currence/ChannelType.java:
--------------------------------------------------------------------------------
1 | package thinkinjava.util.currence;
2 |
3 | /**
4 | * 用于细化流量类型, 即:将一个通道的流量分成 2 种类型, 可动态调控通道大小,达到细化流量分配.
5 | *
6 | * 使用场景: 同一个接口, 有 VIP 用户和普通用户, 不能让普通用户的流量占满流量通道,无论如何也要分出部分流量给 VIP 用户.
7 | */
8 | public enum ChannelType {
9 |
10 | SYNC("sync", 0), ASYNC("async", 1);
11 |
12 | String name;
13 | int code;
14 |
15 | ChannelType(String name, int code) {
16 | this.name = name;
17 | this.code = code;
18 | }
19 |
20 | }
21 |
--------------------------------------------------------------------------------
/core/src/main/java/thinkinjava/util/currence/FlowBuffer.java:
--------------------------------------------------------------------------------
1 | package thinkinjava.util.currence;
2 |
3 | import java.util.concurrent.ConcurrentLinkedQueue;
4 | import java.util.concurrent.TimeUnit;
5 | import java.util.concurrent.atomic.AtomicInteger;
6 | import java.util.concurrent.atomic.AtomicLong;
7 | import java.util.concurrent.atomic.AtomicReference;
8 | import java.util.concurrent.locks.LockSupport;
9 |
10 | import org.slf4j.Logger;
11 | import org.slf4j.LoggerFactory;
12 |
13 | import thinkinjava.breaker.TimeWindow;
14 | import thinkinjava.conf.DynamicProperties;
15 | import thinkinjava.conf.DynamicProperty;
16 | import thinkinjava.conf.Plugins;
17 | import thinkinjava.exception.FlowBufferAddException;
18 | import thinkinjava.exception.LuTimeoutException;
19 |
20 | import static thinkinjava.conf.Constant.DEFAULT_FLOW_BUFFER_QUEUE_MAX_SIZE;
21 | import static thinkinjava.conf.Constant.DEFAULT_FLOW_BUFFER_WAIT_TIME_OUT;
22 | import static thinkinjava.conf.Constant.FLOW_BUFFER_QUEUE_MAX_SIZE;
23 | import static thinkinjava.conf.Constant.FLOW_BUFFER_WAIT_TIME_OUT;
24 | import static thinkinjava.util.currence.FlowBuffer.State.BLOCK;
25 | import static thinkinjava.util.currence.FlowBuffer.State.RELEASE_BY_OTHER;
26 | import static thinkinjava.util.currence.FlowBuffer.State.RELEASE_BY_SELF;
27 |
28 |
29 | /**
30 | *
31 | * 流量缓冲带.
32 | *
33 | * 该组件用于应对突发流量导致的毛刺. 当流量过大,就会将请求贮存在队列中(timeout), 当有空闲资源时, 就会唤醒请求线程.
34 | */
35 | public class FlowBuffer {
36 |
37 | private static final Logger LOGGER = LoggerFactory.getLogger(FlowBuffer.class);
38 |
39 | private String unique;
40 |
41 | /** 通道类型. */
42 | private ChannelType channelType;
43 |
44 | public ConcurrentLinkedQueue queue = new ConcurrentLinkedQueue<>();
45 |
46 | /**
47 | * 队列当前的大小.
48 | */
49 | private AtomicInteger currentSize = new AtomicInteger();
50 |
51 | /**
52 | * 时间窗口内, 进入缓冲区的请求数.
53 | */
54 | private TimeWindow window = new TimeWindow(10);
55 |
56 | /**
57 | * 进入队列的总请求数, 用于调优.
58 | */
59 | private AtomicLong record = new AtomicLong();
60 |
61 | private DynamicProperties properties = Plugins.getInstance().getDynamicProperties();
62 |
63 | private DynamicProperty bufferMaxSize_ = properties.getInteger(FLOW_BUFFER_QUEUE_MAX_SIZE, DEFAULT_FLOW_BUFFER_QUEUE_MAX_SIZE);
64 | private volatile int bufferMaxSize = bufferMaxSize_.get();
65 |
66 | private DynamicProperty waitTime_ = properties.getLong(FLOW_BUFFER_WAIT_TIME_OUT, DEFAULT_FLOW_BUFFER_WAIT_TIME_OUT);
67 | private volatile long waitTime = waitTime_.get();
68 |
69 | private FlowBuffer(Builder builder) {
70 | unique = builder.unique;
71 | channelType = builder.channelType;
72 |
73 | bufferMaxSize_.addCallback(() -> bufferMaxSize = bufferMaxSize_.get());
74 | waitTime_.addCallback(() -> waitTime = waitTime_.get());
75 | }
76 |
77 | public static Builder newBuilder() {
78 | return new Builder();
79 | }
80 |
81 | /**
82 | * 将当前请求加入缓冲区并等待.
83 | */
84 | public void addBufferAndWait() throws LuTimeoutException, FlowBufferAddException {
85 | Node node = new Node(Thread.currentThread(), BLOCK);
86 |
87 | if (currentSize.get() > bufferMaxSize) {
88 | throw new FlowBufferAddException("FlowBuffer Queue Full, Queue Size : " + bufferMaxSize);
89 | }
90 |
91 | boolean result = queue.offer(node);
92 | currentSize.incrementAndGet();
93 | record.incrementAndGet();
94 | window.getCurrentWindow().addTotal();
95 |
96 | if (result) {
97 | LockSupport.parkNanos(Thread.currentThread(), TimeUnit.MILLISECONDS.toNanos(waitTime));
98 | // 自己超时了, 释放一个空间.
99 | if (node.flag.compareAndSet(BLOCK, RELEASE_BY_SELF) && (currentSize.decrementAndGet() != -1)) {
100 | throw new LuTimeoutException("this request on Flow Buffer timeout, timeout time : " + waitTime +
101 | ", unique : " + unique + ", channelType : " + channelType + ", bufferMaxSize : " + bufferMaxSize
102 | + ", currentSize : " + currentSize.get());
103 | }
104 |
105 | } else {
106 | throw new FlowBufferAddException("offer FlowBuffer Queue Fail");
107 | }
108 | }
109 |
110 | /**
111 | * 唤醒缓冲区请求(顺手清理队列里超时的请求).
112 | */
113 | public void notifyBuffer() {
114 | Node node;
115 | // 条件不能变序
116 | while ((node = queue.poll()) != null && node.flag.compareAndSet(BLOCK, RELEASE_BY_OTHER)) {
117 |
118 | currentSize.decrementAndGet();
119 |
120 | LockSupport.unpark(node.currentThread);
121 | break;
122 | }
123 | }
124 |
125 |
126 | public enum State {
127 | /** 阻塞中 */
128 | BLOCK,
129 | /** 被别人唤醒. */
130 | RELEASE_BY_OTHER,
131 | /** 被自己唤醒. */
132 | RELEASE_BY_SELF
133 | }
134 |
135 |
136 | public static class Node {
137 |
138 | volatile Thread currentThread;
139 | final AtomicReference flag;
140 |
141 | public Node(Thread currentThread, State flag) {
142 | this.currentThread = currentThread;
143 | this.flag = new AtomicReference<>(flag);
144 | }
145 | }
146 |
147 | public static final class Builder {
148 |
149 | String unique;
150 | private ChannelType channelType;
151 |
152 | private Builder() {
153 | }
154 |
155 |
156 | public FlowBuffer build() {
157 | return new FlowBuffer(this);
158 | }
159 |
160 | public Builder unique(String val) {
161 | unique = val;
162 | return this;
163 | }
164 |
165 | public Builder channelType(ChannelType val) {
166 | channelType = val;
167 | return this;
168 | }
169 | }
170 |
171 |
172 | }
173 |
--------------------------------------------------------------------------------
/core/src/main/java/thinkinjava/util/currence/LuRejectedExecutionHandler.java:
--------------------------------------------------------------------------------
1 | package thinkinjava.util.currence;
2 |
3 | import java.util.concurrent.RejectedExecutionHandler;
4 | import java.util.concurrent.ThreadPoolExecutor;
5 |
6 | /**
7 | *
8 | * @author 莫那·鲁道
9 | * @date 2018-12-15-10:36
10 | */
11 | public class LuRejectedExecutionHandler implements RejectedExecutionHandler {
12 |
13 | @Override
14 | public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
15 |
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/core/src/main/java/thinkinjava/util/currence/LuThreadFactory.java:
--------------------------------------------------------------------------------
1 | package thinkinjava.util.currence;
2 |
3 | import java.util.concurrent.ThreadFactory;
4 |
5 | /**
6 | *
7 | * @author 莫那·鲁道
8 | * @date 2018-12-15-10:36
9 | */
10 | public class LuThreadFactory implements ThreadFactory {
11 |
12 | private String name;
13 |
14 | public LuThreadFactory(String name) {
15 | this.name = name;
16 | }
17 |
18 | @Override
19 | public Thread newThread(Runnable r) {
20 | return null;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/core/src/main/java/thinkinjava/util/currence/LuThreadPool.java:
--------------------------------------------------------------------------------
1 | package thinkinjava.util.currence;
2 |
3 | import java.util.concurrent.BlockingQueue;
4 | import java.util.concurrent.ThreadPoolExecutor;
5 | import java.util.concurrent.TimeUnit;
6 |
7 | /**
8 | *
9 | * @author 莫那·鲁道
10 | * @date 2018-12-15-10:37
11 | */
12 | public class LuThreadPool extends ThreadPoolExecutor {
13 |
14 | public LuThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,
15 | BlockingQueue workQueue, LuThreadFactory luThreadFactory,
16 | LuRejectedExecutionHandler luRejectedExecutionHandler) {
17 | super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
18 | }
19 |
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/core/src/main/java/thinkinjava/util/currence/LuThreadPoolFactory.java:
--------------------------------------------------------------------------------
1 | package thinkinjava.util.currence;
2 |
3 | import java.util.concurrent.LinkedBlockingQueue;
4 | import java.util.concurrent.ThreadPoolExecutor;
5 | import java.util.concurrent.TimeUnit;
6 |
7 | /**
8 | *
9 | * @author 莫那·鲁道
10 | * @date 2018-12-15-10:33
11 | */
12 | public class LuThreadPoolFactory {
13 |
14 | private static int cpu = Runtime.getRuntime().availableProcessors();
15 | private static long keepAlive = 60 * 1000;
16 | private static TimeUnit unit = TimeUnit.MILLISECONDS;
17 | private static int queueSize = 1024;
18 | private static String name = "default-lu-rpc-threadPool";
19 |
20 | public static ThreadPoolExecutor getThreadPool(int core, String name) {
21 | return new LuThreadPool(
22 | core,
23 | core << 2,
24 | keepAlive,
25 | unit,
26 | new LinkedBlockingQueue(queueSize),
27 | new LuThreadFactory(name),
28 | new LuRejectedExecutionHandler());
29 | }
30 |
31 | public static ThreadPoolExecutor getThreadPool( String name) {
32 | return new LuThreadPool(
33 | cpu,
34 | cpu << 2,
35 | keepAlive,
36 | unit,
37 | new LinkedBlockingQueue(queueSize),
38 | new LuThreadFactory(name),
39 | new LuRejectedExecutionHandler());
40 | }
41 |
42 | }
43 |
--------------------------------------------------------------------------------
/core/src/main/java/thinkinjava/util/currence/Semaphore.java:
--------------------------------------------------------------------------------
1 | package thinkinjava.util.currence;
2 |
3 | import java.util.concurrent.atomic.AtomicInteger;
4 |
5 |
6 | /**
7 | * 信号量(限流器).
8 | */
9 | public class Semaphore implements java.io.Serializable {
10 |
11 | private static final long serialVersionUID = -3109378282317104994L;
12 |
13 | private String unique;
14 |
15 | private AtomicInteger counter = new AtomicInteger(0);
16 |
17 | private AtomicInteger permits;
18 |
19 | public Semaphore(String unique, int permits) {
20 | this.unique = unique;
21 | this.permits = new AtomicInteger(permits);
22 | }
23 |
24 |
25 | /** 获取信号量; 注意: 此处 while 在高并发下, 由于自旋, 可能引起 CPU 飙升 */
26 | public boolean tryAcquire() {
27 | int expect;
28 | while ((expect = counter.get()) < permits.get()) {
29 | return counter.compareAndSet(expect, expect + 1);
30 | }
31 | return false;
32 | }
33 |
34 |
35 | /** 归还信号量 */
36 | public void release() {
37 | if (counter.get() <= 0) {
38 | counter.set(0);
39 | } else {
40 | counter.decrementAndGet();
41 | }
42 | }
43 |
44 | /** 减小信号量阈值 */
45 | public void reducePermits(int reduction) {
46 | if (reduction < 0) {
47 | return;
48 | }
49 | if (permits.get() < reduction) {
50 | permits.set(0);
51 | }
52 | permits.compareAndSet(permits.get(), permits.get() - reduction);
53 | }
54 |
55 | /** 增加信号量阈值 */
56 | public void incrementPermits(int reduction) {
57 | if (reduction < 0) {
58 | return;
59 | }
60 | permits.compareAndSet(permits.get(), permits.get() + reduction);
61 | }
62 |
63 | public String getUnique() {
64 | return unique;
65 | }
66 |
67 | public void setUnique(String unique) {
68 | this.unique = unique;
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/core/src/main/java/thinkinjava/vmZK/LocalFileRegister.java:
--------------------------------------------------------------------------------
1 | package thinkinjava.vmZK;
2 |
3 | import java.io.File;
4 | import java.io.FileInputStream;
5 | import java.io.FileNotFoundException;
6 | import java.io.FileOutputStream;
7 | import java.io.PrintWriter;
8 | import java.util.Arrays;
9 | import java.util.List;
10 | import java.util.Map;
11 | import java.util.concurrent.ConcurrentHashMap;
12 |
13 | import thinkinjava.exception.UserOperationException;
14 | import thinkinjava.rpc.remoting.Url;
15 | import thinkinjava.util.CloseUtil;
16 |
17 | /**
18 | *
19 | * @author 莫那·鲁道
20 | * @date 2018/10/15-上午6:39
21 | */
22 | public class LocalFileRegister implements ServiceRegisterDisCover {
23 |
24 | private static Map cache = new ConcurrentHashMap();
25 |
26 | private static String userHome;
27 | private static String currentDir;
28 | private static String lineSeparator;
29 | private static String configHome;
30 | static {
31 | userHome = System.getProperty("user.home");
32 | configHome = userHome + "/lu/config.conf";
33 | currentDir = System.getProperty("user.dir");
34 | lineSeparator = System.getProperty("line.separator");
35 |
36 | }
37 |
38 | public static LocalFileRegister create() {
39 | return new LocalFileRegister();
40 | }
41 |
42 | public void registerService(String serviceName, Url url) {
43 | File file = new File(userHome + "/lu");
44 | PrintWriter pw = null;
45 | try {
46 | if (!file.exists()) {
47 | file.mkdirs();
48 | }
49 | file = new File(configHome);
50 | pw = new PrintWriter((new FileOutputStream(file, true)));
51 | pw.append(serviceName).append("=").append("localhost").append(":").append(String.valueOf("8081"));
52 | pw.append(lineSeparator);
53 | pw.flush();
54 |
55 | } catch (FileNotFoundException e) {
56 | e.printStackTrace();
57 | } finally {
58 | CloseUtil.close(pw);
59 | }
60 |
61 | }
62 |
63 | public void unregisterService(String serviceName) {
64 | cache.remove(serviceName);
65 |
66 | flushCache();
67 | }
68 |
69 |
70 | public Url getServiceUrl(String serviceName) {
71 |
72 | Url url = cache.get(serviceName);
73 |
74 | if (url != null) {
75 | return url;
76 | }
77 |
78 | readFileAndCache();
79 |
80 | return cache.get(serviceName);
81 | }
82 |
83 | private void readFileAndCache() {
84 | File file = new File(userHome + "/lu");
85 | if (!file.exists()) {
86 | file.mkdirs();
87 | }
88 | file = new File(configHome);
89 | long length = file.length();
90 | if (length <= 0) {
91 | throw new UserOperationException(
92 | "LocalFileRegister Center config is empty, maybe you should start server first! ");
93 | }
94 | byte[] content = new byte[(int) length];
95 | FileInputStream is = null;
96 | try {
97 |
98 | is = new FileInputStream(file);
99 | is.read(content);
100 |
101 | String contentString = new String(content);
102 | String[] arr = contentString.split(lineSeparator);
103 |
104 | List data = Arrays.asList(arr);
105 |
106 | cacheData(data);
107 |
108 | } catch (java.io.IOException e) {
109 | e.printStackTrace();
110 | } finally {
111 | CloseUtil.close(is);
112 | }
113 | }
114 |
115 | private void flushCache() {
116 | File file = new File(configHome);
117 | PrintWriter pw = null;
118 | try {
119 | if (!file.exists()) {
120 | File dir = new File(userHome + "lu");
121 | if (dir.mkdirs()) {
122 | file = new File(configHome);
123 | }
124 | }
125 |
126 | pw = new PrintWriter((new FileOutputStream(file)));
127 |
128 | for (String k : cache.keySet()) {
129 | String serviceName = k;
130 | Url url = cache.get(k);
131 | pw.append(serviceName + "=" + url.getIp() + ":" + url.getPort());
132 | pw.append(lineSeparator);
133 | }
134 |
135 | pw.flush();
136 | } catch (Exception e) {
137 | e.printStackTrace();
138 | } finally {
139 | CloseUtil.close(pw);
140 | }
141 | }
142 |
143 |
144 | private void cacheData(List data) {
145 | for (String item : data) {
146 | String[] arr = item.split("=");
147 | if (arr.length == 2) {
148 | String[] serverUrl = arr[1].split(":");
149 | cache.put(arr[0], new Url(serverUrl[0], Integer.valueOf(serverUrl[1])));
150 | }
151 | }
152 | }
153 |
154 | }
155 |
--------------------------------------------------------------------------------
/core/src/main/java/thinkinjava/vmZK/ServiceRegisterDisCover.java:
--------------------------------------------------------------------------------
1 | package thinkinjava.vmZK;
2 |
3 | import thinkinjava.rpc.remoting.Url;
4 |
5 | /**
6 | * 服务注册发现 ServiceRegisterDisCover
7 | *
8 | * 虚拟的zk
9 | *
10 | * @author 莫那·鲁道
11 | * @date 2018/10/14-下午10:22
12 | */
13 | public interface ServiceRegisterDisCover {
14 |
15 |
16 | void registerService(String serviceName, Url url);
17 |
18 | void unregisterService(String serviceName);
19 |
20 | Url getServiceUrl(String serviceName);
21 |
22 | }
23 |
--------------------------------------------------------------------------------
/core/src/main/resources/application.properties:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stateIs0/Lu-Rpc/fd821ad3fed83cbbfa7877ef9ee2740c1f1b82bc/core/src/main/resources/application.properties
--------------------------------------------------------------------------------
/core/src/main/resources/log4j.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/core/src/test/java/thinkinjava/proxy/InterceptorTest.java:
--------------------------------------------------------------------------------
1 | package thinkinjava.proxy;
2 |
3 | import thinkinjava.api.client.LuConsumer;
4 | import thinkinjava.api.client.LuConsumerConfig;
5 | import thinkinjava.api.server.LuProvider;
6 | import thinkinjava.api.server.LuProviderConfig;
7 | import thinkinjava.rpc.remoting.LuUserProcessor;
8 | import thinkinjava.rpc.remoting.Url;
9 | import thinkinjava.rpc.remoting.testService.Demo;
10 | import thinkinjava.rpc.remoting.testService.DemoImpl;
11 | import thinkinjava.vmZK.LocalFileRegister;
12 |
13 | /**
14 | *
15 | *
16 | * @author 莫那·鲁道
17 | * @date 2018/10/14-下午10:52
18 | */
19 | public class InterceptorTest {
20 |
21 |
22 | public static void main(String[] args) throws NoSuchMethodException {
23 | LuUserProcessor.methods.put("hello", Demo.class.getMethod("hello", String.class));
24 | LuUserProcessor.targets.put(Demo.class.getName(), new DemoImpl());
25 | LuProviderConfig luProviderConfig = new LuProviderConfig();
26 | luProviderConfig.setService(Demo.class);
27 | luProviderConfig.setRef(DemoImpl.class);
28 |
29 | LuProvider luProvider = new LuProvider(luProviderConfig);
30 | luProvider.export();
31 |
32 | LocalFileRegister.create().registerService(Demo.class.getName(), new Url("localhost", 8081));
33 |
34 | LuConsumerConfig luConsumerConfig = new LuConsumerConfig();
35 | luConsumerConfig.setService(Demo.class);
36 |
37 | LuConsumer luConsumer = new LuConsumer(luConsumerConfig);
38 | Demo demo = luConsumer.ref();
39 |
40 | System.out.println(demo.hello("hello"));
41 | }
42 |
43 | }
--------------------------------------------------------------------------------
/core/src/test/java/thinkinjava/rpc/remoting/LuServerTest.java:
--------------------------------------------------------------------------------
1 | package thinkinjava.rpc.remoting;
2 |
3 | import org.junit.After;
4 | import org.junit.Before;
5 | import org.junit.Test;
6 |
7 | import static org.junit.Assert.*;
8 |
9 | /**
10 | * 莫那·鲁道
11 | * @date 2018/10/14-下午8:59
12 | */
13 | public class LuServerTest {
14 |
15 | @Before
16 | public void setUp() throws Exception {
17 |
18 | }
19 |
20 | @After
21 | public void tearDown() throws Exception {
22 | }
23 |
24 | @Test
25 | public void start() {
26 | }
27 |
28 | @Test
29 | public void stop() {
30 | }
31 |
32 | @Test
33 | public void getServer() {
34 | }
35 |
36 | @Test
37 | public void registerUserProcessor() {
38 | }
39 | }
--------------------------------------------------------------------------------
/core/src/test/java/thinkinjava/rpc/remoting/MonaLuClientTest.java:
--------------------------------------------------------------------------------
1 | package thinkinjava.rpc.remoting;
2 |
3 | import org.junit.After;
4 | import org.junit.Before;
5 | import org.junit.Test;
6 |
7 | import static org.junit.Assert.*;
8 |
9 | /**
10 | *
11 | *
12 | * @author 莫那·鲁道
13 | * @date 2018/10/14-下午8:58
14 | */
15 | public class MonaLuClientTest {
16 |
17 | @Before
18 | public void setUp() throws Exception {
19 | }
20 |
21 | @After
22 | public void tearDown() throws Exception {
23 | }
24 |
25 | @Test
26 | public void invoke() {
27 | }
28 | }
--------------------------------------------------------------------------------
/core/src/test/java/thinkinjava/rpc/remoting/RpcTest.java:
--------------------------------------------------------------------------------
1 | package thinkinjava.rpc.remoting;
2 |
3 | import java.lang.reflect.Method;
4 | import java.util.Map;
5 | import java.util.concurrent.ConcurrentHashMap;
6 |
7 | import org.junit.Before;
8 | import org.junit.Test;
9 |
10 | import thinkinjava.rpc.Request;
11 | import thinkinjava.rpc.remoting.testService.Demo;
12 | import thinkinjava.rpc.remoting.testService.DemoImpl;
13 |
14 | public class RpcTest {
15 |
16 | public static Map methods = new ConcurrentHashMap();
17 |
18 | public static Map targets = new ConcurrentHashMap();
19 |
20 |
21 | @Before
22 | public void before() {
23 | try {
24 | LuUserProcessor.methods.put("hello", Demo.class.getMethod("hello", String.class));
25 | LuUserProcessor.targets.put("Demo", new DemoImpl());
26 |
27 | } catch (NoSuchMethodException e) {
28 | e.printStackTrace();
29 | }
30 | }
31 |
32 |
33 | @Test
34 | public void test() {
35 | LuServer server = new LuServer("127.0.0.1", 8081, false);
36 | server.registerUserProcessor(new LuUserProcessor());
37 |
38 | server.start();
39 |
40 | LuClient client = LuClient.create();
41 |
42 | Request req = new Request();
43 |
44 | req.setClassName(Demo.class.getName());
45 | req.setMethodName("hello");
46 | req.setArgs(new Object[]{"client"});
47 | req.setServiceName("Demo");
48 |
49 | Object result = client.invoke(req);
50 | System.out.println(result);
51 |
52 | server.stop();
53 | }
54 |
55 | }
56 |
--------------------------------------------------------------------------------
/core/src/test/java/thinkinjava/rpc/remoting/testService/Demo.java:
--------------------------------------------------------------------------------
1 | package thinkinjava.rpc.remoting.testService;
2 |
3 | /**
4 | *
5 | *
6 | * @author 莫那·鲁道
7 | * @date 2018/10/14-下午9:50
8 | */
9 | public interface Demo {
10 |
11 | String hello(String string);
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/core/src/test/java/thinkinjava/rpc/remoting/testService/DemoImpl.java:
--------------------------------------------------------------------------------
1 | package thinkinjava.rpc.remoting.testService;
2 |
3 | /**
4 | *
5 | *
6 | * @author 莫那·鲁道
7 | * @date 2018/10/14-下午9:50
8 | */
9 | public class DemoImpl implements Demo {
10 |
11 |
12 | public String hello(String string) {
13 | System.out.println("receive client : " + string);
14 | return "server reply --- >" + string;
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/core/src/test/java/thinkinjava/vmZK/LocalFileRegisterTest.java:
--------------------------------------------------------------------------------
1 | package thinkinjava.vmZK;
2 |
3 | import org.junit.After;
4 | import org.junit.Before;
5 | import org.junit.Test;
6 |
7 | import thinkinjava.rpc.remoting.Url;
8 |
9 | /**
10 | *
11 | *
12 | * @author 莫那·鲁道
13 | * @date 2018/10/15-上午7:02
14 | */
15 | public class LocalFileRegisterTest {
16 |
17 | private ServiceRegisterDisCover serviceRegisterDisCover;
18 |
19 | @Before
20 | public void setUp() throws Exception {
21 |
22 | serviceRegisterDisCover = LocalFileRegister.create();
23 | }
24 |
25 | @After
26 | public void tearDown() throws Exception {
27 | }
28 |
29 | @Test
30 | public void registerService() {
31 | serviceRegisterDisCover.registerService("hello", new Url("localhost", 8081));
32 | System.out.println(serviceRegisterDisCover.getServiceUrl("hello"));
33 | serviceRegisterDisCover.unregisterService("hello");
34 |
35 | System.out.println(serviceRegisterDisCover.getServiceUrl("hello"));
36 |
37 | }
38 |
39 | @Test
40 | public void getServerUrl() {
41 | }
42 | }
--------------------------------------------------------------------------------
/example/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | Lu-Rpc-parent
7 | cn.thinkinjava
8 | 0.0.1-SNAPSHOT
9 |
10 | 4.0.0
11 |
12 | example
13 | 使用例子
14 |
15 |
16 |
17 |
18 | cn.thinkinjava
19 | core
20 | 0.0.1-SNAPSHOT
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/example/src/main/java/cn.thinkinjava/ClientTest.java:
--------------------------------------------------------------------------------
1 | package cn.thinkinjava;
2 |
3 | import thinkinjava.api.client.LuConsumer;
4 | import thinkinjava.api.client.LuConsumerConfig;
5 |
6 | /**
7 | *
8 | * @author 莫那·鲁道
9 | * @date 2018/10/19-下午11:25
10 | */
11 | public class ClientTest {
12 | public static void main(String[] args) throws InterruptedException {
13 | LuConsumerConfig luConsumerConfig = new LuConsumerConfig();
14 | luConsumerConfig.setService(Demo.class);
15 | LuConsumer luConsumer = new LuConsumer(luConsumerConfig);
16 | Demo demo = luConsumer.ref();
17 |
18 | for (; ; ) {
19 | System.out.println(demo.hello("hello"));
20 | Thread.sleep(1111);
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/example/src/main/java/cn.thinkinjava/Demo.java:
--------------------------------------------------------------------------------
1 | package cn.thinkinjava;
2 |
3 | /**
4 | *
5 | * @author 莫那·鲁道
6 | * @date 2018/10/19-下午11:26
7 | */
8 | public interface Demo {
9 |
10 | String hello(String string);
11 | }
12 |
--------------------------------------------------------------------------------
/example/src/main/java/cn.thinkinjava/DemoImpl.java:
--------------------------------------------------------------------------------
1 | package cn.thinkinjava;
2 |
3 | /**
4 | *
5 | * @author 莫那·鲁道
6 | * @date 2018/10/19-下午11:26
7 | */
8 | public class DemoImpl implements Demo {
9 |
10 | public String hello(String string) {
11 | System.out.println("receive client : " + string);
12 | return "server reply --- >" + string;
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/example/src/main/java/cn.thinkinjava/ServerTest.java:
--------------------------------------------------------------------------------
1 | package cn.thinkinjava;
2 |
3 | import thinkinjava.api.server.LuProvider;
4 | import thinkinjava.api.server.LuProviderConfig;
5 | import thinkinjava.rpc.remoting.LuUserProcessor;
6 | import thinkinjava.rpc.remoting.Url;
7 |
8 | /**
9 | *
10 | * @author 莫那·鲁道
11 | * @date 2018/10/19-下午11:25
12 | */
13 | public class ServerTest {
14 | public static void main(String[] args) throws NoSuchMethodException {
15 | LuUserProcessor.methods.put("hello", Demo.class.getMethod("hello", String.class));
16 | LuUserProcessor.targets.put("Demo", new DemoImpl());
17 | LuProviderConfig luProviderConfig = new LuProviderConfig();
18 |
19 | luProviderConfig.setService(Demo.class);
20 | luProviderConfig.setRef(DemoImpl.class);
21 | luProviderConfig.setServiceUrl(new Url("127.0.0.1", 8081));
22 |
23 |
24 | LuProvider luProvider = new LuProvider(luProviderConfig);
25 |
26 | luProvider.export();
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/extension/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | Lu-Rpc-parent
7 | cn.thinkinjava
8 | 0.0.1-SNAPSHOT
9 |
10 | 4.0.0
11 |
12 | extension
13 | RPC 框架的扩展功能, 例如链路追踪,分布式配置中心等
14 |
15 |
16 |
--------------------------------------------------------------------------------
/filter/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | Lu-Rpc-parent
7 | cn.thinkinjava
8 | 0.0.1-SNAPSHOT
9 |
10 | 4.0.0
11 |
12 | filter
13 | 过滤器
14 |
15 |
16 |
--------------------------------------------------------------------------------
/lu-netty-remoting/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | Lu-Rpc-parent
7 | cn.thinkinjava
8 | 0.0.1-SNAPSHOT
9 |
10 | 4.0.0
11 |
12 | lu-netty-remoting
13 | 用于自己实现 Netty 远程调用
14 |
15 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 |
6 | cn.thinkinjava
7 | Lu-Rpc-parent
8 | 0.0.1-SNAPSHOT
9 |
10 | core
11 | cluster
12 |
13 | example
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 | pom
25 |
26 | Lu-Rpc
27 | Learning for RPC
28 |
29 |
30 |
31 | UTF-8
32 | UTF-8
33 | 1.8
34 |
35 |
36 |
37 |
38 | junit
39 | junit
40 | 4.12
41 |
42 |
43 |
44 |
45 | com.alipay.sofa
46 | bolt
47 | 1.5.0
48 |
49 |
50 |
51 |
52 | com.alipay.sofa
53 | hessian
54 | 3.3.2
55 |
56 |
57 |
58 | org.slf4j
59 | slf4j-api
60 | 1.7.7
61 |
62 |
63 | ch.qos.logback
64 | logback-core
65 | 1.1.7
66 |
67 |
68 | ch.qos.logback
69 | logback-access
70 | 1.1.7
71 |
72 |
73 | ch.qos.logback
74 | logback-classic
75 | 1.1.7
76 |
77 |
78 |
79 |
80 |
81 | Lu-Rpc
82 |
83 |
84 |
85 |
86 |
--------------------------------------------------------------------------------
/proxy/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | Lu-Rpc-parent
7 | cn.thinkinjava
8 | 0.0.1-SNAPSHOT
9 |
10 | 4.0.0
11 |
12 | proxy
13 | 代理层, 属于扩展功能.
14 |
15 |
16 |
--------------------------------------------------------------------------------
/raft-register/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | Lu-Rpc-parent
7 | cn.thinkinjava
8 | 0.0.1-SNAPSHOT
9 |
10 | 4.0.0
11 |
12 | raft-register
13 | 基于 Raft 协议的注册中心
14 |
15 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | ## 开发者须知
4 | 开发者在修改、扩展框架时,需要阅读开发规范:
5 | 1. [框架设计原则(梁飞)](http://thinkinjava.cn/2018/10/%E6%A1%86%E6%9E%B6%E8%AE%BE%E8%AE%A1%E5%8E%9F%E5%88%99-%E6%A2%81%E9%A3%9E/)
6 | 2. [汇总梁飞博客设计文章](http://thinkinjava.cn/2018/10/汇总梁飞博客设计文章/)
7 | 3. [框架设计上的十点基本常识](http://thinkinjava.cn/2018/10/框架设计上的十点基本常识/)
8 | 4. [Java-并发编程常识-by-梁飞](http://thinkinjava.cn/2018/04/Java-并发编程常识-by-梁飞/)
9 |
10 | 关于修改配置:
11 | 1. [配置设计](http://javatar.iteye.com/blog/949527)
12 |
13 | ## Lu-Rpc
14 |
15 | Lu-Rpc 是个专为学习者准备的 RPC 框架, 初始架构非常简单, 可供初学者扩展和学习.
16 |
17 | Lu 可以认为是中文世界的撸, 即撸 Rpc--- 造个 Rpc 轮子.
18 |
19 | Lu-Rpc 架构图如下:
20 |
21 | 
22 |
23 |
24 |
25 | Lu-Rpc 的领域模型设计借鉴 Dubbo, 服务域没有明显的界限. 核心域就是 Invoker, 非常合适作为核心领域模型的接口.
26 |
27 | 会话域可以是Request,也可以是 Invocation. 这个问题不大.
28 |
29 | ## 快速开始
30 |
31 | 进入 example 模块
32 |
33 | 先启动 ServerTest, 再启动 ClientTest.
34 |
35 |
36 | ## 待开发功能
37 | 0. ~~自研基于 Raft 协议的注册中心~~(已开发结束,地址: https://github.com/stateIs0/lu-raft)
38 | 1. Lu-Service-mesh
39 | 2. 负载均衡
40 | 3. 异常处理
41 | 4. 异步调用,feature 调用。oneway 调用
42 | 5. 连接管理,心跳管理
43 | 6. 服务监控
44 | 7. 服务优雅下线
45 | 8. 无缝支持 SpringBoot
46 | 9. 服务故障转移
47 | 10. 服务链路追踪
48 | 11. 支持分布式配置中心(自研或使用第三方)
49 | 12. 自研网络通信框架。定义自己的 RPC 协议。
50 | 13. 支持零拷贝序列化。
51 | 14. 使用字节码增强,减少反射调用开销。
52 | 15. 增加扩展点机制。
53 | 16. ~~熔断~~(已开发结束)
54 | 17. ~~限流~~(已开发结束)
55 |
56 | 欢迎提交 PR,issue。
57 |
58 | 本人微信:
59 |
60 | 
61 |
--------------------------------------------------------------------------------
/remoting/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | Lu-Rpc-parent
7 | cn.thinkinjava
8 | 0.0.1-SNAPSHOT
9 |
10 | 4.0.0
11 |
12 | remoting
13 | 远程模块, 可添加 netty, SOFABolt 等
14 |
15 |
--------------------------------------------------------------------------------
/serializer/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | Lu-Rpc-parent
7 | cn.thinkinjava
8 | 0.0.1-SNAPSHOT
9 |
10 | 4.0.0
11 |
12 | serializer
13 | 序列化模块
14 |
15 |
16 |
--------------------------------------------------------------------------------
/serviceMesh/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | Lu-Rpc-parent
7 | cn.thinkinjava
8 | 0.0.1-SNAPSHOT
9 |
10 | 4.0.0
11 |
12 | serviceMesh
13 | Lu-Rpc 的 Service Mesh
14 |
15 |
16 |
--------------------------------------------------------------------------------