├── images ├── Client.png └── Server.png ├── tiny-rpc-springboot-stater ├── src │ └── main │ │ ├── resources │ │ ├── application.yml │ │ ├── META-INF │ │ │ └── spring.factories │ │ └── logback-spring.xml │ │ └── java │ │ └── com.example.autoconfig │ │ ├── MyAutoConfiguration.java │ │ ├── annotation │ │ ├── Provider.java │ │ └── Reference.java │ │ └── processor │ │ ├── ProviderBeanPostProcessor.java │ │ └── ReferenceBeanPostProcessor.java └── pom.xml ├── tiny-rpc-core ├── src │ └── main │ │ ├── resources │ │ └── META-INF │ │ │ └── TINY-RPC │ │ │ └── internal │ │ │ ├── com.example.tinyrpc.registry.Registry │ │ │ ├── com.example.tinyrpc.protocol.Protocol │ │ │ ├── com.example.tinyrpc.common.threadpool.ThreadPool │ │ │ ├── com.example.tinyrpc.proxy.ProxyFactory │ │ │ ├── com.example.tinyrpc.cluster.LoadBalance │ │ │ ├── com.example.tinyrpc.serialization.Serialization │ │ │ └── com.example.tinyrpc.filter.Filter │ │ └── java │ │ └── com │ │ └── example │ │ └── tinyrpc │ │ ├── transport │ │ ├── Client.java │ │ ├── Server.java │ │ ├── Endpoint.java │ │ ├── client │ │ │ ├── ClientHandler.java │ │ │ └── NettyClient.java │ │ ├── server │ │ │ ├── ServerHandler.java │ │ │ └── NettyServer.java │ │ └── AbstractEndpoint.java │ │ ├── common │ │ ├── exception │ │ │ └── BusinessException.java │ │ ├── utils │ │ │ ├── UUIDUtils.java │ │ │ ├── FutureContext.java │ │ │ └── CodecSupport.java │ │ ├── threadpool │ │ │ ├── ThreadPool.java │ │ │ └── impl │ │ │ │ └── DefaultThreadPool.java │ │ ├── extension │ │ │ ├── SPI.java │ │ │ └── ExtensionLoader.java │ │ └── domain │ │ │ ├── Constants.java │ │ │ ├── ResponseBody.java │ │ │ ├── Request.java │ │ │ ├── RpcContext.java │ │ │ ├── Response.java │ │ │ ├── Invocation.java │ │ │ └── URL.java │ │ ├── filter │ │ ├── Filter.java │ │ ├── impl │ │ │ ├── ConsumerContextFilter.java │ │ │ ├── ServerContextFilter.java │ │ │ ├── ActiveLimitFilter.java │ │ │ └── TraceFilter.java │ │ ├── RpcStatus.java │ │ └── Span.java │ │ ├── registry │ │ ├── UpdateAddressCallBack.java │ │ ├── Registry.java │ │ ├── ZkSupport.java │ │ └── impl │ │ │ └── ZkServiceRegistry.java │ │ ├── cluster │ │ ├── LoadBalance.java │ │ └── impl │ │ │ ├── RoundRobinLoadBalancer.java │ │ │ ├── RandomLoadBalancer.java │ │ │ └── LeastActiveLoadBalance.java │ │ ├── serialization │ │ ├── Serialization.java │ │ └── impl │ │ │ ├── FastJsonSerialization.java │ │ │ ├── ProtostuffSerialization.java │ │ │ └── HessianSerialization.java │ │ ├── proxy │ │ ├── ProxyFactory.java │ │ ├── impl │ │ │ ├── JdkProxyFactory.java │ │ │ └── JavassistProxyFactory.java │ │ ├── InvokerInvocationHandler.java │ │ └── JavassistProxy.java │ │ ├── protocol │ │ ├── Invoker.java │ │ ├── impl │ │ │ ├── CallBackInvocationHandler.java │ │ │ ├── RegistryProtocol.java │ │ │ ├── ProtocolFilterWrapper.java │ │ │ ├── RealInvoker.java │ │ │ └── InvokerClientWrapper.java │ │ └── Protocol.java │ │ ├── config │ │ ├── GlobalConfig.java │ │ ├── ServiceConfig.java │ │ └── ReferenceConfig.java │ │ └── codec │ │ ├── Codec.java │ │ ├── Encoder.java │ │ └── Decoder.java └── pom.xml ├── .gitignore ├── pom.xml └── README.md /images/Client.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raylrnd/TINY-RPC/HEAD/images/Client.png -------------------------------------------------------------------------------- /images/Server.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raylrnd/TINY-RPC/HEAD/images/Server.png -------------------------------------------------------------------------------- /tiny-rpc-springboot-stater/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | logging: 2 | file: mylogs.log 3 | path: /TINY-RPC-log -------------------------------------------------------------------------------- /tiny-rpc-core/src/main/resources/META-INF/TINY-RPC/internal/com.example.tinyrpc.registry.Registry: -------------------------------------------------------------------------------- 1 | zookeeper=com.example.tinyrpc.registry.impl.ZkServiceRegistry -------------------------------------------------------------------------------- /tiny-rpc-core/src/main/resources/META-INF/TINY-RPC/internal/com.example.tinyrpc.protocol.Protocol: -------------------------------------------------------------------------------- 1 | TINY-RPC=com.example.tinyrpc.protocol.impl.ProtocolFilterWrapper -------------------------------------------------------------------------------- /tiny-rpc-springboot-stater/src/main/resources/META-INF/spring.factories: -------------------------------------------------------------------------------- 1 | org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.example.autoconfig.MyAutoConfiguration -------------------------------------------------------------------------------- /tiny-rpc-core/src/main/resources/META-INF/TINY-RPC/internal/com.example.tinyrpc.common.threadpool.ThreadPool: -------------------------------------------------------------------------------- 1 | default-pool=com.example.tinyrpc.common.threadpool.impl.DefaultThreadPool -------------------------------------------------------------------------------- /tiny-rpc-core/src/main/resources/META-INF/TINY-RPC/internal/com.example.tinyrpc.proxy.ProxyFactory: -------------------------------------------------------------------------------- 1 | jdk=com.example.tinyrpc.proxy.impl.JdkProxyFactory 2 | javassist=com.example.tinyrpc.proxy.impl.JavassistProxyFactory -------------------------------------------------------------------------------- /tiny-rpc-core/src/main/java/com/example/tinyrpc/transport/Client.java: -------------------------------------------------------------------------------- 1 | package com.example.tinyrpc.transport; 2 | 3 | /** 4 | * @auther zhongshunchao 5 | * @date 2020/5/21 11:25 上午 6 | */ 7 | public interface Client extends Endpoint { 8 | 9 | } 10 | -------------------------------------------------------------------------------- /tiny-rpc-core/src/main/java/com/example/tinyrpc/transport/Server.java: -------------------------------------------------------------------------------- 1 | package com.example.tinyrpc.transport; 2 | 3 | /** 4 | * @auther zhongshunchao 5 | * @date 2020/5/21 11:29 上午 6 | */ 7 | public interface Server extends Endpoint { 8 | 9 | } 10 | -------------------------------------------------------------------------------- /tiny-rpc-core/src/main/resources/META-INF/TINY-RPC/internal/com.example.tinyrpc.cluster.LoadBalance: -------------------------------------------------------------------------------- 1 | random=com.example.tinyrpc.cluster.impl.RandomLoadBalancer 2 | leastactive=com.example.tinyrpc.cluster.impl.LeastActiveLoadBalance 3 | round=com.example.tinyrpc.cluster.impl.RoundRobinLoadBalancer -------------------------------------------------------------------------------- /tiny-rpc-core/src/main/resources/META-INF/TINY-RPC/internal/com.example.tinyrpc.serialization.Serialization: -------------------------------------------------------------------------------- 1 | fastjson=com.example.tinyrpc.serialization.impl.FastJsonSerialization 2 | hessian=com.example.tinyrpc.serialization.impl.HessianSerialization 3 | protostuff=com.example.tinyrpc.serialization.impl.ProtostuffSerialization -------------------------------------------------------------------------------- /tiny-rpc-core/src/main/resources/META-INF/TINY-RPC/internal/com.example.tinyrpc.filter.Filter: -------------------------------------------------------------------------------- 1 | active-limit-filter=com.example.tinyrpc.filter.impl.ActiveLimitFilter 2 | trace-filter=com.example.tinyrpc.filter.impl.TraceFilter 3 | consumer-context-filter=com.example.tinyrpc.filter.impl.ConsumerContextFilter 4 | context-filter=com.example.tinyrpc.filter.impl.ServerContextFilter -------------------------------------------------------------------------------- /tiny-rpc-core/src/main/java/com/example/tinyrpc/common/exception/BusinessException.java: -------------------------------------------------------------------------------- 1 | package com.example.tinyrpc.common.exception; 2 | 3 | /** 4 | * @auther zhongshunchao 5 | * @date 24/06/2020 22:32 6 | * 用于处理业务上的异常 7 | */ 8 | public class BusinessException extends RuntimeException { 9 | 10 | public BusinessException(String message) { 11 | super(message); 12 | } 13 | } 14 | 15 | -------------------------------------------------------------------------------- /tiny-rpc-core/src/main/java/com/example/tinyrpc/filter/Filter.java: -------------------------------------------------------------------------------- 1 | package com.example.tinyrpc.filter; 2 | 3 | import com.example.tinyrpc.common.domain.Invocation; 4 | import com.example.tinyrpc.protocol.Invoker; 5 | 6 | /** 7 | * @auther zhongshunchao 8 | * @date 27/06/2020 20:55 9 | */ 10 | public interface Filter { 11 | Object invoke(Invoker invoker, Invocation invocation) throws Exception; 12 | } 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # ignore .svn metadata files 2 | .svn 3 | # ignore Maven generated target folders 4 | target 5 | # ignore eclipse files 6 | .project 7 | .classpath 8 | .settings 9 | .scala_dependencies 10 | .externalToolBuilders 11 | .factorypath 12 | # ignore IDEA files 13 | *.iml 14 | *.ipr 15 | *.iws 16 | .idea 17 | .DS_Store 18 | atlassian-ide-plugin.xml 19 | maven-ant-tasks.jar 20 | test-output 21 | classes 22 | 23 | -------------------------------------------------------------------------------- /tiny-rpc-core/src/main/java/com/example/tinyrpc/registry/UpdateAddressCallBack.java: -------------------------------------------------------------------------------- 1 | package com.example.tinyrpc.registry; 2 | 3 | import java.util.List; 4 | import java.util.Set; 5 | 6 | /** 7 | * @auther zhongshunchao 8 | * @date 23/06/2020 00:03 9 | */ 10 | @FunctionalInterface 11 | public interface UpdateAddressCallBack { 12 | //更新缓存中的zk地址 13 | void updateAddress(List addUrlList, Set closeUrlSet); 14 | 15 | } 16 | -------------------------------------------------------------------------------- /tiny-rpc-core/src/main/java/com/example/tinyrpc/cluster/LoadBalance.java: -------------------------------------------------------------------------------- 1 | package com.example.tinyrpc.cluster; 2 | 3 | import com.example.tinyrpc.common.extension.SPI; 4 | import com.example.tinyrpc.protocol.Invoker; 5 | 6 | import java.util.List; 7 | 8 | /** 9 | * @auther zhongshunchao 10 | * @date 20/06/2020 21:01 11 | */ 12 | @SPI("random") 13 | public interface LoadBalance { 14 | 15 | Invoker select(List invokers); 16 | 17 | } 18 | -------------------------------------------------------------------------------- /tiny-rpc-core/src/main/java/com/example/tinyrpc/common/utils/UUIDUtils.java: -------------------------------------------------------------------------------- 1 | package com.example.tinyrpc.common.utils; 2 | 3 | import java.util.UUID; 4 | 5 | /** 6 | * @auther zhongshunchao 7 | * @date 04/07/2020 17:47 8 | */ 9 | public class UUIDUtils { 10 | 11 | public static long getUUID() { 12 | UUID uuid = UUID.randomUUID(); 13 | return uuid.getLeastSignificantBits() ^ uuid.getMostSignificantBits(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /tiny-rpc-core/src/main/java/com/example/tinyrpc/serialization/Serialization.java: -------------------------------------------------------------------------------- 1 | package com.example.tinyrpc.serialization; 2 | 3 | import com.example.tinyrpc.common.extension.SPI; 4 | 5 | /** 6 | * @auther zhongshunchao 7 | * @date 17/05/2020 16:40 8 | */ 9 | @SPI("fastjson") 10 | public interface Serialization { 11 | byte[] serialize(T obj) throws Exception; 12 | T deserialize(byte[] data, Class cls) throws Exception; 13 | } 14 | -------------------------------------------------------------------------------- /tiny-rpc-core/src/main/java/com/example/tinyrpc/common/threadpool/ThreadPool.java: -------------------------------------------------------------------------------- 1 | package com.example.tinyrpc.common.threadpool; 2 | 3 | import com.example.tinyrpc.common.domain.URL; 4 | import com.example.tinyrpc.common.extension.SPI; 5 | 6 | import java.util.concurrent.Executor; 7 | 8 | /** 9 | * @auther zhongshunchao 10 | * @date 05/07/2020 15:18 11 | */ 12 | @SPI("default-pool") 13 | public interface ThreadPool { 14 | 15 | Executor getExecutor(URL url); 16 | 17 | } 18 | -------------------------------------------------------------------------------- /tiny-rpc-core/src/main/java/com/example/tinyrpc/proxy/ProxyFactory.java: -------------------------------------------------------------------------------- 1 | package com.example.tinyrpc.proxy; 2 | 3 | import com.example.tinyrpc.common.domain.Invocation; 4 | import com.example.tinyrpc.common.extension.SPI; 5 | import com.example.tinyrpc.protocol.Invoker; 6 | 7 | /** 8 | * @auther zhongshunchao 9 | * @date 19/06/2020 23:54 10 | */ 11 | @SPI("jdk") 12 | public interface ProxyFactory { 13 | 14 | Object getProxy(Invoker invoker, Invocation invocation); 15 | 16 | } 17 | -------------------------------------------------------------------------------- /tiny-rpc-core/src/main/java/com/example/tinyrpc/protocol/Invoker.java: -------------------------------------------------------------------------------- 1 | package com.example.tinyrpc.protocol; 2 | 3 | import com.example.tinyrpc.common.domain.Invocation; 4 | import com.example.tinyrpc.common.domain.URL; 5 | 6 | /** 7 | * @auther zhongshunchao 8 | * @date 13/06/2020 20:52 9 | */ 10 | public interface Invoker { 11 | 12 | URL getUrl(); 13 | 14 | Class getInterface(); 15 | 16 | Object invoke(Invocation invocation) throws Exception; 17 | 18 | void destroy(); 19 | } 20 | -------------------------------------------------------------------------------- /tiny-rpc-core/src/main/java/com/example/tinyrpc/common/extension/SPI.java: -------------------------------------------------------------------------------- 1 | package com.example.tinyrpc.common.extension; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * @auther zhongshunchao 10 | * @date 05/07/2020 10:27 11 | */ 12 | @Retention(RetentionPolicy.RUNTIME) 13 | @Target(ElementType.TYPE) 14 | public @interface SPI { 15 | String value() default ""; 16 | } -------------------------------------------------------------------------------- /tiny-rpc-core/src/main/java/com/example/tinyrpc/common/utils/FutureContext.java: -------------------------------------------------------------------------------- 1 | package com.example.tinyrpc.common.utils; 2 | 3 | import java.util.concurrent.CompletableFuture; 4 | import java.util.concurrent.ConcurrentHashMap; 5 | 6 | /** 7 | * @auther zhongshunchao 8 | * @date 25/05/2020 22:58 9 | */ 10 | public class FutureContext { 11 | //CompletableFuture 为jdk1.8新增的异步计算框架 12 | public static final ConcurrentHashMap> FUTURE_CACHE = new ConcurrentHashMap<>(); 13 | 14 | } 15 | -------------------------------------------------------------------------------- /tiny-rpc-core/src/main/java/com/example/tinyrpc/registry/Registry.java: -------------------------------------------------------------------------------- 1 | package com.example.tinyrpc.registry; 2 | 3 | import com.example.tinyrpc.common.extension.SPI; 4 | import com.example.tinyrpc.common.domain.URL; 5 | 6 | import java.util.Set; 7 | 8 | /** 9 | * @auther zhongshunchao 10 | * @date 30/06/2020 21:13 11 | */ 12 | @SPI("zookeeper") 13 | public interface Registry { 14 | 15 | Set subscribe(String interfaceName, UpdateAddressCallBack updateAddressCallBack); 16 | 17 | void register(URL url); 18 | } 19 | -------------------------------------------------------------------------------- /tiny-rpc-core/src/main/java/com/example/tinyrpc/transport/Endpoint.java: -------------------------------------------------------------------------------- 1 | package com.example.tinyrpc.transport; 2 | 3 | import com.example.tinyrpc.common.domain.Request; 4 | import io.netty.channel.ChannelHandlerContext; 5 | 6 | import java.util.concurrent.Future; 7 | 8 | /** 9 | * @auther zhongshunchao 10 | * @date 07/07/2020 23:03 11 | */ 12 | public interface Endpoint { 13 | 14 | void start(); 15 | 16 | Future send(Request request); 17 | 18 | void received(ChannelHandlerContext ctx, Object msg) ; 19 | 20 | void close(); 21 | } 22 | -------------------------------------------------------------------------------- /tiny-rpc-core/src/main/java/com/example/tinyrpc/protocol/impl/CallBackInvocationHandler.java: -------------------------------------------------------------------------------- 1 | package com.example.tinyrpc.protocol.impl; 2 | 3 | import com.example.tinyrpc.common.domain.URL; 4 | import com.example.tinyrpc.transport.Endpoint; 5 | 6 | import java.lang.reflect.InvocationHandler; 7 | import java.lang.reflect.Method; 8 | 9 | public class CallBackInvocationHandler implements InvocationHandler { 10 | public CallBackInvocationHandler(String callbackMethod, URL url, Endpoint endpoint) { 11 | } 12 | 13 | @Override 14 | public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 15 | return null; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /tiny-rpc-core/src/main/java/com/example/tinyrpc/common/domain/Constants.java: -------------------------------------------------------------------------------- 1 | package com.example.tinyrpc.common.domain; 2 | 3 | /** 4 | * @auther zhongshunchao 5 | * @date 28/06/2020 23:18 6 | */ 7 | public interface Constants { 8 | 9 | String LOCAL_HOST = "127.0.0.1"; 10 | 11 | int CLIENT_SIDE = 1; 12 | 13 | int SERVER_SIDE = 0; 14 | 15 | String SPAN_KEY = "span"; 16 | 17 | String INTERNAL_PATH = "/META-INF/TINY-RPC/internal/"; 18 | 19 | String EXTERNAL_PATH = "META-INF/TINY-RPC"; 20 | 21 | String DEFAULT_SERIALIATION = "protostuff"; 22 | 23 | int HEART_BEAT_TIME_OUT = 20; 24 | 25 | int HEART_BEAT_TIME_OUT_MAX_TIME = 3; 26 | } 27 | -------------------------------------------------------------------------------- /tiny-rpc-core/src/main/java/com/example/tinyrpc/cluster/impl/RoundRobinLoadBalancer.java: -------------------------------------------------------------------------------- 1 | package com.example.tinyrpc.cluster.impl; 2 | 3 | import com.example.tinyrpc.cluster.LoadBalance; 4 | import com.example.tinyrpc.protocol.Invoker; 5 | import java.util.List; 6 | 7 | public class RoundRobinLoadBalancer implements LoadBalance { 8 | 9 | private int index = 0; 10 | 11 | @Override 12 | public Invoker select(List invokers) { 13 | if(invokers.size() == 0) { 14 | return null; 15 | } 16 | Invoker result = invokers.get(index); 17 | index = (index + 1) % invokers.size(); 18 | return result; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /tiny-rpc-core/src/main/java/com/example/tinyrpc/common/domain/ResponseBody.java: -------------------------------------------------------------------------------- 1 | package com.example.tinyrpc.common.domain; 2 | 3 | /** 4 | * @auther zhongshunchao 5 | * @date 06/06/2020 11:04 6 | */ 7 | public class ResponseBody { 8 | 9 | private String errorMsg; 10 | 11 | private Object result; 12 | 13 | public String getErrorMsg() { 14 | return errorMsg; 15 | } 16 | 17 | public void setErrorMsg(String errorMsg) { 18 | this.errorMsg = errorMsg; 19 | } 20 | 21 | public Object getResult() { 22 | return result; 23 | } 24 | 25 | public void setResult(Object result) { 26 | this.result = result; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /tiny-rpc-core/src/main/java/com/example/tinyrpc/cluster/impl/RandomLoadBalancer.java: -------------------------------------------------------------------------------- 1 | package com.example.tinyrpc.cluster.impl; 2 | 3 | import com.example.tinyrpc.cluster.LoadBalance; 4 | import com.example.tinyrpc.protocol.Invoker; 5 | import java.util.List; 6 | import java.util.concurrent.ThreadLocalRandom; 7 | 8 | /** 9 | * @auther zhongshunchao 10 | * @date 21/06/2020 12:39 11 | */ 12 | public class RandomLoadBalancer implements LoadBalance { 13 | 14 | @Override 15 | public Invoker select(List invokers) { 16 | if (invokers.size() == 0) { 17 | return null; 18 | } 19 | return invokers.get(ThreadLocalRandom.current().nextInt(invokers.size())); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /tiny-rpc-core/src/main/java/com/example/tinyrpc/serialization/impl/FastJsonSerialization.java: -------------------------------------------------------------------------------- 1 | package com.example.tinyrpc.serialization.impl; 2 | 3 | import com.example.tinyrpc.serialization.Serialization; 4 | import com.alibaba.fastjson.JSONObject; 5 | 6 | 7 | /** 8 | * @auther zhongshunchao 9 | * @date 31/05/2020 15:33 10 | */ 11 | public class FastJsonSerialization implements Serialization { 12 | 13 | @Override 14 | public byte[] serialize(T obj) { 15 | try { 16 | return JSONObject.toJSONBytes(obj); 17 | } catch (Exception e) { 18 | e.getMessage(); 19 | } 20 | return null; 21 | } 22 | 23 | @Override 24 | public T deserialize(byte[] data, Class cls) { 25 | return JSONObject.parseObject(data, cls); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /tiny-rpc-core/src/main/java/com/example/tinyrpc/common/threadpool/impl/DefaultThreadPool.java: -------------------------------------------------------------------------------- 1 | package com.example.tinyrpc.common.threadpool.impl; 2 | 3 | import com.example.tinyrpc.common.domain.URL; 4 | import com.example.tinyrpc.common.threadpool.ThreadPool; 5 | import org.springframework.scheduling.concurrent.CustomizableThreadFactory; 6 | 7 | import java.util.concurrent.*; 8 | 9 | /** 10 | * @auther zhongshunchao 11 | * @date 05/07/2020 15:23 12 | */ 13 | public class DefaultThreadPool implements ThreadPool { 14 | 15 | @Override 16 | public Executor getExecutor(URL url) { 17 | return new ThreadPoolExecutor(8, 16, 60, TimeUnit.SECONDS, new ArrayBlockingQueue<>(100) 18 | , new CustomizableThreadFactory("default-business-thread-"), new ThreadPoolExecutor.AbortPolicy()); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /tiny-rpc-springboot-stater/src/main/java/com.example.autoconfig/MyAutoConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.example.autoconfig; 2 | 3 | import com.example.autoconfig.processor.ReferenceBeanPostProcessor; 4 | import com.example.autoconfig.processor.ProviderBeanPostProcessor; 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.context.annotation.Configuration; 7 | 8 | /** 9 | * @auther zhongshunchao 10 | * @date 14/06/2020 09:35 11 | */ 12 | @Configuration 13 | public class MyAutoConfiguration { 14 | 15 | @Bean 16 | public ReferenceBeanPostProcessor myReferenceBeanPostProcessor() { 17 | return new ReferenceBeanPostProcessor(); 18 | } 19 | 20 | @Bean 21 | public ProviderBeanPostProcessor myServiceBeanPostProcessor() { 22 | return new ProviderBeanPostProcessor(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /tiny-rpc-springboot-stater/src/main/java/com.example.autoconfig/annotation/Provider.java: -------------------------------------------------------------------------------- 1 | package com.example.autoconfig.annotation; 2 | 3 | import org.springframework.stereotype.Component; 4 | 5 | import java.lang.annotation.ElementType; 6 | import java.lang.annotation.Retention; 7 | import java.lang.annotation.RetentionPolicy; 8 | import java.lang.annotation.Target; 9 | 10 | /** 11 | * @auther zhongshunchao 12 | * @date 2020/5/21 2:55 下午 13 | */ 14 | @Target(ElementType.TYPE) 15 | @Retention(RetentionPolicy.RUNTIME) 16 | @Component 17 | public @interface Provider { 18 | Class interfaceClass() default void.class; 19 | int port() default 8787; 20 | int weight() default 1; 21 | String serializer() default ""; 22 | String proxy() default ""; 23 | String protocol() default ""; 24 | String registry() default ""; 25 | String[] filter() default {}; 26 | } 27 | -------------------------------------------------------------------------------- /tiny-rpc-core/src/main/java/com/example/tinyrpc/proxy/impl/JdkProxyFactory.java: -------------------------------------------------------------------------------- 1 | package com.example.tinyrpc.proxy.impl; 2 | 3 | import com.example.tinyrpc.common.domain.Invocation; 4 | import com.example.tinyrpc.protocol.Invoker; 5 | import com.example.tinyrpc.proxy.InvokerInvocationHandler; 6 | import com.example.tinyrpc.proxy.ProxyFactory; 7 | 8 | import java.lang.reflect.Proxy; 9 | 10 | /** 11 | * @auther zhongshunchao 12 | * @date 2020/5/21 10:27 上午 13 | */ 14 | public class JdkProxyFactory implements ProxyFactory { 15 | 16 | @Override 17 | public Object getProxy(Invoker invoker, Invocation invocation) { 18 | return Proxy.newProxyInstance( 19 | Thread.currentThread().getContextClassLoader(), 20 | new Class[]{invoker.getInterface()}, 21 | new InvokerInvocationHandler(invoker, invocation) 22 | ); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /tiny-rpc-core/src/main/java/com/example/tinyrpc/transport/client/ClientHandler.java: -------------------------------------------------------------------------------- 1 | package com.example.tinyrpc.transport.client; 2 | 3 | import com.example.tinyrpc.transport.Client; 4 | import io.netty.channel.ChannelHandlerContext; 5 | import io.netty.channel.SimpleChannelInboundHandler; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | 9 | /** 10 | * @auther zhongshunchao 11 | * @date 08/05/2020 08:33 12 | */ 13 | public class ClientHandler extends SimpleChannelInboundHandler { 14 | 15 | private static final Logger logger = LoggerFactory.getLogger(ClientHandler.class); 16 | 17 | private Client client; 18 | 19 | public ClientHandler(Client client) { 20 | this.client = client; 21 | } 22 | 23 | @Override 24 | protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception { 25 | client.received(ctx, msg); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /tiny-rpc-core/src/main/java/com/example/tinyrpc/protocol/Protocol.java: -------------------------------------------------------------------------------- 1 | package com.example.tinyrpc.protocol; 2 | 3 | import com.example.tinyrpc.common.domain.Invocation; 4 | import com.example.tinyrpc.common.extension.SPI; 5 | import com.example.tinyrpc.common.domain.URL; 6 | import com.example.tinyrpc.transport.Endpoint; 7 | 8 | /** 9 | * @auther zhongshunchao 10 | * @date 13/06/2020 21:15 11 | * Dubbo中的export()是将invoker转化为Exporter, DubboExporter持有exporterMap对象,该exporterMap可以理解为是当前的invokerList,使用者可以通过export方法得到Exporter,进而得到当前的invokerList集合 12 | * 可以参考Dubbo的服务暴露:https://www.jianshu.com/p/30011f94ec24 13 | */ 14 | @SPI("TINY-RPC") 15 | public interface Protocol { 16 | 17 | /** 18 | * 引用服务 19 | * @param invocation 20 | * @return 返回Filter链中的最后一个Invoker,即RealInvoker 21 | */ 22 | Invoker refer(Invocation invocation); 23 | 24 | /** 25 | * 将接口信息(接口全限定名 -> ip:port)注册到Zookeeper中,然后开启Server 26 | * @param url 27 | */ 28 | Endpoint export(URL url); 29 | } 30 | -------------------------------------------------------------------------------- /tiny-rpc-core/src/main/java/com/example/tinyrpc/config/GlobalConfig.java: -------------------------------------------------------------------------------- 1 | package com.example.tinyrpc.config; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | /** 7 | * @auther zhongshunchao 8 | * @date 05/07/2020 15:31 9 | */ 10 | public class GlobalConfig { 11 | 12 | private static final Logger logger = LoggerFactory.getLogger(GlobalConfig.class); 13 | 14 | private String appName; 15 | 16 | private int threadsNum; 17 | 18 | private GlobalConfig() { 19 | } 20 | 21 | private void loadGlobalConfig() { 22 | 23 | } 24 | 25 | private static volatile GlobalConfig globalConfig; 26 | 27 | public static GlobalConfig getGlobalConfig() { 28 | if (globalConfig == null) { 29 | synchronized (GlobalConfig.class) { 30 | if (globalConfig == null) { 31 | globalConfig = new GlobalConfig(); 32 | } 33 | } 34 | } 35 | return globalConfig; 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /tiny-rpc-core/src/main/java/com/example/tinyrpc/config/ServiceConfig.java: -------------------------------------------------------------------------------- 1 | package com.example.tinyrpc.config; 2 | 3 | import com.example.tinyrpc.common.extension.ExtensionLoader; 4 | import com.example.tinyrpc.common.domain.URL; 5 | import com.example.tinyrpc.protocol.Invoker; 6 | import com.example.tinyrpc.protocol.Protocol; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | import java.util.Map; 11 | import java.util.concurrent.ConcurrentHashMap; 12 | 13 | /** 14 | * @auther zhongshunchao 15 | * @date 29/05/2020 09:07 16 | */ 17 | public class ServiceConfig { 18 | 19 | private static final Logger logger = LoggerFactory.getLogger(ServiceConfig.class); 20 | 21 | public static final Map INVOKER_MAP = new ConcurrentHashMap<>(); 22 | 23 | //将服务发布到ZK并开启server 24 | public void export(URL url) { 25 | Protocol protocol = ExtensionLoader.getExtensionLoader().getExtension( Protocol.class, url.getProtocol()); 26 | protocol.export(url); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /tiny-rpc-core/src/main/java/com/example/tinyrpc/proxy/impl/JavassistProxyFactory.java: -------------------------------------------------------------------------------- 1 | package com.example.tinyrpc.proxy.impl; 2 | 3 | 4 | import com.example.tinyrpc.common.domain.Invocation; 5 | import com.example.tinyrpc.protocol.Invoker; 6 | import com.example.tinyrpc.proxy.InvokerInvocationHandler; 7 | import com.example.tinyrpc.proxy.JavassistProxy; 8 | import com.example.tinyrpc.proxy.ProxyFactory; 9 | 10 | /** 11 | * @auther zhongshunchao 12 | * @date 05/07/2020 17:05 13 | */ 14 | /* 15 | * 这种方式不需要事先创建 16 | * 要代理的对象 17 | * 18 | * */ 19 | public class JavassistProxyFactory implements ProxyFactory { 20 | 21 | @Override 22 | public Object getProxy(Invoker invoker, Invocation invocation) { 23 | try { 24 | return JavassistProxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), invoker.getInterface(), new InvokerInvocationHandler(invoker, invocation)); 25 | } catch (Exception e) { 26 | e.printStackTrace(); 27 | } 28 | return null; 29 | } 30 | } 31 | 32 | -------------------------------------------------------------------------------- /tiny-rpc-springboot-stater/src/main/java/com.example.autoconfig/annotation/Reference.java: -------------------------------------------------------------------------------- 1 | package com.example.autoconfig.annotation; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * @auther zhongshunchao 10 | * @date 2020/5/21 2:55 下午 11 | */ 12 | @Target(ElementType.FIELD) 13 | @Retention(RetentionPolicy.RUNTIME) 14 | public @interface Reference { 15 | boolean async() default false; 16 | boolean oneway() default false; 17 | long timeout() default 100; 18 | int actives() default 0; 19 | String serializer() default ""; 20 | String proxy() default ""; 21 | String protocol() default ""; 22 | String[] filter() default {}; 23 | String registry() default ""; 24 | /** 25 | * Load balance strategy, legal values include: random, roundrobin, leastactive 26 | */ 27 | String loadbalance() default ""; 28 | /** 29 | * When enable, prefer to call local service in the same JVM if it's present, default value is true 30 | */ 31 | boolean injvm() default true; 32 | } 33 | -------------------------------------------------------------------------------- /tiny-rpc-core/src/main/java/com/example/tinyrpc/codec/Codec.java: -------------------------------------------------------------------------------- 1 | package com.example.tinyrpc.codec; 2 | 3 | import io.netty.buffer.ByteBuf; 4 | import io.netty.buffer.Unpooled; 5 | 6 | /** 7 | * @auther zhongshunchao 8 | * @date 29/05/2020 22:06 9 | */ 10 | public interface Codec { 11 | // message flag. 12 | byte FLAG_REQUEST = (byte) 0x80; 13 | byte FLAG_TWOWAY = (byte) 0x40; 14 | byte FLAG_EVENT = (byte) 0x20; 15 | 16 | byte SERIALIZATION_MASK = 0x0f; 17 | // status 18 | int STATUS = 0xf0; 19 | 20 | //自定义魔数"0xdeff" 21 | byte [] MAGIC_ARRAY = {(byte)0xde, (byte)0xff}; 22 | ByteBuf MAGIC = Unpooled.copiedBuffer(MAGIC_ARRAY); // 头部长度固定8个字节 23 | // byte [] MAGIC_ARRAY = "$$".getBytes(); 24 | // public static final ByteBuf MAGIC = Unpooled.copiedBuffer(MAGIC_ARRAY); 25 | int HEAD_LENGTH = 8; 26 | int HEADER_LENGTH = 16; 27 | // 最大报文长度 8M 28 | int MAX_LENGTH = 1024 * 10; 29 | int MAX_FRAME_LENGTH = 1024 * 10; 30 | int LENGTH_FIELD_OFFSET = 0; 31 | int LENGTH_FIELD_LENGTH = 4; 32 | int LENGTH_ADJUSTMENT = 0; 33 | // 这样下一个Handler接收到的就不包含length了,直接就是message 34 | int INITIAL_BYTES_TO_STRIP = 4; 35 | } 36 | -------------------------------------------------------------------------------- /tiny-rpc-core/src/main/java/com/example/tinyrpc/transport/server/ServerHandler.java: -------------------------------------------------------------------------------- 1 | package com.example.tinyrpc.transport.server; 2 | 3 | import com.alibaba.fastjson.JSON; 4 | import com.example.tinyrpc.common.domain.Request; 5 | import com.example.tinyrpc.transport.Server; 6 | import com.example.tinyrpc.transport.client.ClientHandler; 7 | import io.netty.channel.ChannelHandlerContext; 8 | import io.netty.channel.SimpleChannelInboundHandler; 9 | import org.slf4j.Logger; 10 | import org.slf4j.LoggerFactory; 11 | 12 | 13 | /** 14 | * @auther zhongshunchao 15 | * @date 05/05/2020 15:02 16 | */ 17 | // 消息被读取后,会自动释放资源 18 | public class ServerHandler extends SimpleChannelInboundHandler { 19 | 20 | private static final Logger logger = LoggerFactory.getLogger(ClientHandler.class); 21 | 22 | private Server server; 23 | 24 | ServerHandler(NettyServer nettyServer) { 25 | this.server = nettyServer; 26 | } 27 | 28 | @Override 29 | protected void channelRead0(ChannelHandlerContext ctx, Request request) throws Exception { 30 | logger.info("服务端 ServerHandler 收到Request为:{}", JSON.toJSONString(request)); 31 | server.received(ctx, request); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /tiny-rpc-core/src/main/java/com/example/tinyrpc/filter/impl/ConsumerContextFilter.java: -------------------------------------------------------------------------------- 1 | package com.example.tinyrpc.filter.impl; 2 | 3 | import com.alibaba.fastjson.JSON; 4 | import com.example.tinyrpc.common.domain.Invocation; 5 | import com.example.tinyrpc.common.domain.RpcContext; 6 | import com.example.tinyrpc.filter.Filter; 7 | import com.example.tinyrpc.protocol.Invoker; 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | import static com.example.tinyrpc.common.domain.Constants.CLIENT_SIDE; 11 | 12 | /** 13 | * 消费端。分两种情况,1、属于第一个调用端 2、injvm,这两种情况都不用更新RpcContext中的Attachments 14 | * 消费端的filter执行顺序:ConsumerContextFilter -> ActiveLimitFilter -> TraceFilter 15 | * @auther zhongshunchao 16 | * @date 03/07/2020 22:57 17 | */ 18 | public class ConsumerContextFilter implements Filter { 19 | 20 | private static final Logger logger = LoggerFactory.getLogger(ConsumerContextFilter.class); 21 | 22 | @Override 23 | public Object invoke(Invoker invoker, Invocation invocation) throws Exception { 24 | logger.info("###ConsumerContextFilter start filtering invocation" + JSON.toJSONString(invocation)); 25 | invocation.setSide(CLIENT_SIDE); 26 | RpcContext.getContext().setInvocation(invocation); 27 | return invoker.invoke(invocation); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /tiny-rpc-core/src/main/java/com/example/tinyrpc/config/ReferenceConfig.java: -------------------------------------------------------------------------------- 1 | package com.example.tinyrpc.config; 2 | 3 | import com.example.tinyrpc.common.extension.ExtensionLoader; 4 | import com.example.tinyrpc.common.domain.Invocation; 5 | import com.example.tinyrpc.common.domain.URL; 6 | import com.example.tinyrpc.protocol.Invoker; 7 | import com.example.tinyrpc.protocol.Protocol; 8 | import com.example.tinyrpc.protocol.impl.InvokerClientWrapper; 9 | import com.example.tinyrpc.proxy.ProxyFactory; 10 | 11 | /** 12 | * ReferenceConfig的getProxy()方法返回的是InvokerClientWrapper包装类 13 | * @see InvokerClientWrapper 14 | * @auther zhongshunchao 15 | * @date 23/05/2020 10:39 16 | */ 17 | public class ReferenceConfig { 18 | 19 | public Object get(Invocation invocation) { 20 | URL url = invocation.getUrl(); 21 | Protocol protocol = ExtensionLoader.getExtensionLoader().getExtension(Protocol.class, url.getProtocol()); 22 | ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader().getExtension(ProxyFactory.class, url.getProxy()); 23 | Invoker invoker = protocol.refer(invocation); 24 | Object proxy = proxyFactory.getProxy(invoker, invocation); 25 | if (proxy == null) { 26 | throw new IllegalStateException("getProxy error :proxy == null"); 27 | } 28 | return proxy; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /tiny-rpc-core/src/main/java/com/example/tinyrpc/transport/AbstractEndpoint.java: -------------------------------------------------------------------------------- 1 | package com.example.tinyrpc.transport; 2 | 3 | import com.example.tinyrpc.common.extension.ExtensionLoader; 4 | import io.netty.channel.Channel; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | import java.util.concurrent.ExecutorService; 8 | 9 | /** 10 | * @auther zhongshunchao 11 | * @date 07/07/2020 23:24 12 | */ 13 | public abstract class AbstractEndpoint implements Endpoint{ 14 | 15 | private static final Logger logger = LoggerFactory.getLogger(AbstractEndpoint.class); 16 | 17 | protected Channel channel; 18 | 19 | protected String address; 20 | 21 | protected ExecutorService executor = ExtensionLoader.getExtensionLoader().getDefaultExecutor(); 22 | 23 | public AbstractEndpoint(String address) { 24 | this.address = address; 25 | } 26 | 27 | public void close() { 28 | executor.submit(() -> { 29 | logger.info("正在关闭 Client:{}", address); 30 | if (this.channel != null && channel.isOpen()) { 31 | try { 32 | this.channel.closeFuture().sync(); 33 | } catch (InterruptedException e) { 34 | logger.error("Fail to close client, address:" + address); 35 | } 36 | } 37 | }); 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | pom 6 | 7 | tiny-rpc-springboot-stater 8 | tiny-rpc-core 9 | 10 | 11 | com.example.rpc 12 | tiny-rpc-parent 13 | 1.0-SNAPSHOT 14 | tiny-rpc 15 | 16 | 17 | 1.8 18 | 19 | 20 | 21 | 22 | 23 | 24 | org.apache.maven.plugins 25 | maven-source-plugin 26 | 3.0.1 27 | 28 | 29 | attach-sources 30 | verify 31 | 32 | jar-no-fork 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /tiny-rpc-core/src/main/java/com/example/tinyrpc/common/domain/Request.java: -------------------------------------------------------------------------------- 1 | package com.example.tinyrpc.common.domain; 2 | 3 | import java.io.Serializable; 4 | 5 | /** 6 | * @auther zhongshunchao 7 | * @date 2020/5/20 8:35 下午 8 | */ 9 | public class Request implements Serializable { 10 | 11 | private long requestId; 12 | private boolean event = false; 13 | private boolean oneway = false; 14 | private byte serializationId = 0x00; 15 | private Invocation data; 16 | 17 | public Request(long requestId, byte serializationId) { 18 | this.requestId = requestId; 19 | this.serializationId = serializationId; 20 | } 21 | 22 | 23 | public Invocation getData() { 24 | return data; 25 | } 26 | 27 | public void setData(Invocation data) { 28 | this.data = data; 29 | } 30 | 31 | public long getRequestId() { 32 | return requestId; 33 | } 34 | 35 | public boolean isEvent() { 36 | return event; 37 | } 38 | 39 | public void setEvent(boolean event) { 40 | this.event = event; 41 | } 42 | 43 | public boolean isOneway() { 44 | return oneway; 45 | } 46 | 47 | public void setOneway(boolean oneway) { 48 | this.oneway = oneway; 49 | } 50 | 51 | public byte getSerializationId() { 52 | return serializationId; 53 | } 54 | 55 | public void setSerializationId(byte serializationId) { 56 | this.serializationId = serializationId; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /tiny-rpc-core/src/main/java/com/example/tinyrpc/common/utils/CodecSupport.java: -------------------------------------------------------------------------------- 1 | package com.example.tinyrpc.common.utils; 2 | 3 | import com.example.tinyrpc.common.domain.Constants; 4 | import org.springframework.util.StringUtils; 5 | 6 | import java.util.HashMap; 7 | import java.util.Map; 8 | 9 | /** 10 | * @auther zhongshunchao 11 | * @date 26/05/2020 09:21 12 | */ 13 | public class CodecSupport { 14 | 15 | private static final Map SERIALIZER_CACHE = new HashMap<>(); 16 | 17 | private static Map SERIALIZATIONNAME_ID_MAP = new HashMap<>(); 18 | 19 | public static final HashMap SERIALIZER_MAP = new HashMap<>(); 20 | 21 | private CodecSupport() { 22 | } 23 | 24 | static { 25 | SERIALIZER_CACHE.put((byte) 0x00, "hessian"); 26 | SERIALIZER_CACHE.put((byte) 0x01, "protostuff"); 27 | SERIALIZER_CACHE.put((byte) 0x02, "fastjson"); 28 | 29 | SERIALIZATIONNAME_ID_MAP.put("hessian", (byte) 0x00); 30 | SERIALIZATIONNAME_ID_MAP.put("protostuff", (byte) 0x01); 31 | SERIALIZATIONNAME_ID_MAP.put("fastjson", (byte) 0x02); 32 | 33 | } 34 | 35 | public static byte getIDByName(String name) { 36 | if (StringUtils.isEmpty(name)) { 37 | name = Constants.DEFAULT_SERIALIATION; 38 | } 39 | return SERIALIZATIONNAME_ID_MAP.get(name); 40 | } 41 | 42 | public static String getNameById(byte id) { 43 | return SERIALIZER_CACHE.get(id); 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /tiny-rpc-core/src/main/java/com/example/tinyrpc/filter/impl/ServerContextFilter.java: -------------------------------------------------------------------------------- 1 | package com.example.tinyrpc.filter.impl; 2 | 3 | import com.alibaba.fastjson.JSON; 4 | import com.example.tinyrpc.common.domain.Invocation; 5 | import com.example.tinyrpc.common.domain.RpcContext; 6 | import com.example.tinyrpc.filter.Filter; 7 | import com.example.tinyrpc.protocol.Invoker; 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | import java.util.Map; 11 | import static com.example.tinyrpc.common.domain.Constants.SERVER_SIDE; 12 | 13 | /** 14 | * 服务端 15 | * @auther zhongshunchao 16 | * @date 03/07/2020 23:07 17 | */ 18 | public class ServerContextFilter implements Filter { 19 | 20 | private static final Logger logger = LoggerFactory.getLogger(ServerContextFilter.class); 21 | 22 | @Override 23 | public Object invoke(Invoker invoker, Invocation invocation) throws Exception { 24 | logger.info("###ServerContextFilter start filtering invocation" + JSON.toJSONString(invocation)); 25 | invocation.setSide(SERVER_SIDE); 26 | Map attachments = invocation.getAttachments(); 27 | if (attachments != null) { 28 | if (RpcContext.getContext().getAttachments() != null) { 29 | RpcContext.getContext().getAttachments().putAll(attachments); 30 | } else { 31 | RpcContext.getContext().setAttachments(attachments); 32 | } 33 | } 34 | return invoker.invoke(invocation); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /tiny-rpc-core/src/main/java/com/example/tinyrpc/filter/impl/ActiveLimitFilter.java: -------------------------------------------------------------------------------- 1 | package com.example.tinyrpc.filter.impl; 2 | 3 | import com.alibaba.fastjson.JSON; 4 | import com.example.tinyrpc.common.domain.Invocation; 5 | import com.example.tinyrpc.common.domain.URL; 6 | import com.example.tinyrpc.filter.Filter; 7 | import com.example.tinyrpc.filter.RpcStatus; 8 | import com.example.tinyrpc.protocol.Invoker; 9 | import org.slf4j.Logger; 10 | import org.slf4j.LoggerFactory; 11 | 12 | /** 13 | * 实现了消费端的流量控制 14 | * @auther zhongshunchao 15 | * @date 28/06/2020 13:59 16 | */ 17 | public class ActiveLimitFilter implements Filter { 18 | 19 | private static final Logger logger = LoggerFactory.getLogger(ActiveLimitFilter.class); 20 | 21 | @Override 22 | public Object invoke(Invoker invoker, Invocation invocation) throws Exception{ 23 | logger.info("###ActiveLimitFilter start filtering invocation" + JSON.toJSONString(invocation)); 24 | URL url = invocation.getUrl(); 25 | Object result; 26 | try { 27 | if(RpcStatus.beginCount(url)) { 28 | result = invoker.invoke(invocation); 29 | } else { 30 | logger.warn(">>>the connect num is more than permitted actives!"); 31 | return null; 32 | } 33 | } catch (Exception e) { 34 | logger.error("###ActiveLimitFiltercatch exception, decCount...,{}", JSON.toJSONString(invocation)); 35 | RpcStatus.endCount(url); 36 | throw e; 37 | } 38 | logger.info("###ActiveLimitFilter finished, decCount"); 39 | RpcStatus.endCount(url); 40 | return result; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /tiny-rpc-core/src/main/java/com/example/tinyrpc/filter/RpcStatus.java: -------------------------------------------------------------------------------- 1 | package com.example.tinyrpc.filter; 2 | 3 | import com.example.tinyrpc.common.domain.URL; 4 | import java.util.Map; 5 | import java.util.concurrent.ConcurrentHashMap; 6 | import java.util.concurrent.atomic.AtomicInteger; 7 | 8 | /** 9 | * @auther zhongshunchao 10 | * @date 28/06/2020 14:00 11 | */ 12 | public class RpcStatus { 13 | 14 | private static final Map METHOD_STATISTICS = new ConcurrentHashMap<>(); 15 | 16 | private final AtomicInteger active = new AtomicInteger(); 17 | 18 | private RpcStatus() { 19 | } 20 | 21 | public static RpcStatus getStatus(URL url) { 22 | String uri = url.toIdentityString(); 23 | return METHOD_STATISTICS.computeIfAbsent(uri, key -> new RpcStatus()); 24 | } 25 | 26 | public int getActive() { 27 | return active.get(); 28 | } 29 | 30 | public static void removeStatus(URL url) { 31 | String uri = url.toIdentityString(); 32 | METHOD_STATISTICS.remove(uri); 33 | } 34 | 35 | public static boolean beginCount(URL url) { 36 | int max = url.getActives(); 37 | max = (max <= 0) ? Integer.MAX_VALUE : max; 38 | RpcStatus methodStatus = getStatus(url); 39 | if (methodStatus.active.get() == Integer.MAX_VALUE) { 40 | return false; 41 | } 42 | int i; 43 | do { 44 | i = methodStatus.active.get(); 45 | if (i > max) { 46 | return false; 47 | } 48 | } while (!methodStatus.active.compareAndSet(i, i + 1)); 49 | methodStatus.active.incrementAndGet(); 50 | return true; 51 | } 52 | 53 | public static void endCount(URL url) { 54 | RpcStatus status = getStatus(url); 55 | status.active.decrementAndGet(); 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /tiny-rpc-springboot-stater/src/main/java/com.example.autoconfig/processor/ProviderBeanPostProcessor.java: -------------------------------------------------------------------------------- 1 | package com.example.autoconfig.processor; 2 | 3 | import com.example.autoconfig.annotation.Provider; 4 | import com.example.tinyrpc.common.domain.URL; 5 | import com.example.tinyrpc.config.ServiceConfig; 6 | import org.springframework.beans.BeansException; 7 | import org.springframework.beans.factory.config.BeanPostProcessor; 8 | 9 | import static com.example.tinyrpc.common.domain.Constants.LOCAL_HOST; 10 | 11 | /** 12 | * @auther zhongshunchao 13 | * @date 29/05/2020 09:04 14 | */ 15 | //解析@Providdr,将该类添加至缓存 16 | public class ProviderBeanPostProcessor implements BeanPostProcessor { 17 | 18 | @Override 19 | public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { 20 | Class beanClass = bean.getClass(); 21 | if (!beanClass.isAnnotationPresent(Provider.class)) { 22 | return bean; 23 | } 24 | Provider rpcService = beanClass.getAnnotation(Provider.class); 25 | URL url = new URL(); 26 | String interfaceName = rpcService.interfaceClass().getName(); 27 | url.setInterfaceName(interfaceName).setPort(rpcService.port()).setWeight(rpcService.weight()) 28 | .setIp(LOCAL_HOST).setAddress(LOCAL_HOST + ":" + rpcService.port()) 29 | .setProtocol(rpcService.protocol()).setFilters(rpcService.filter()).setProxy(rpcService.proxy()) 30 | .setSerialization(rpcService.serializer()).setRegistry(rpcService.registry()); 31 | ServiceConfig serviceConfig = new ServiceConfig(); 32 | url.setRef(bean); 33 | serviceConfig.export(url); 34 | return bean; 35 | } 36 | 37 | @Override 38 | public Object postProcessAfterInitialization(Object o, String s) throws BeansException { 39 | return o; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TINY-RPC 2 | TINY-RPC 3 | 基于Netty的小型RPC项目 4 | 目前为version_2.0版本,java代码总计约3500行 5 | 里程碑,总工时45pd 6 | 项目文档记录在了我的个人博客:http://www.shunchao.ink/tiny-rpc/ 7 | 8 | | 功能点 | 完成时间 | 9 | | :----------------------------------------------------------: | :--------: | 10 | | 实现基本的RPC通信 | 2020.06.06 | 11 | | 添加 SpringBoot Stater、实现了类Dubbo协议 | 2020.06.14 | 12 | | 添加Zookeeper,提供服务发现和注册, 添加负载均衡策略LoadBalancer | 2020.06.27 | 13 | | 添加ExtensionLoader、@SPI注解 | 2020.06.30 | 14 | | 添加同步、异步调用方式、添加业务线程池 | 2020.07.03 | 15 | | 添加Filter,实现链路跟踪和限流 | 2020.07.04 | 16 | | 支持多种负载均衡策略 | 2020.07.05 | 17 | 18 | 19 | Zookeeper中的节点格式为 20 | 21 | | path | value | 22 | | :--------------------------------------: | :--------------------: | 23 | | /TINY-RPC/com.example.tinyrpc.AService | 192.168.1.1:1221&100 | 24 | | /TINY-RPC/com.example.tinyrpc.BService | 192.168.1.4:1221&200 | 25 | | /TINY-RPC/com.example.tinyrpc.AService | 192.168.1.2:1221&300 | 26 | 27 | 28 | 29 | 由于本人临近毕业时间仓促,项目只能暂时进行到这。后面有时间会补上高并发测试和一些想实现但目前尚未实现的功能点。 30 | 31 | | 未来的Feature | 32 | | ---------------------------------------------------- | 33 | | 添加Netty长连接,添加心跳机制,实现断线重连 | 34 | | 添加javassist,代替原有的JDK动态代理 | 35 | | 添加FailOver,FailFast等多种集群容错 | 36 | | 支持callback回调 | 37 | | 实现多种协议,如HTTP | 38 | ----- 39 | 40 | 参考书籍: 41 | 《深入理解Apache Dubbo与实践》、《深度剖析Apache Dubbo核心技术内幕》 42 | 个人博客:[shunchao.ink](http://www.shunchao.ink) 43 | -------------------------------------------------------------------------------- /tiny-rpc-core/src/main/java/com/example/tinyrpc/proxy/InvokerInvocationHandler.java: -------------------------------------------------------------------------------- 1 | package com.example.tinyrpc.proxy; 2 | 3 | import com.example.tinyrpc.common.domain.Invocation; 4 | import com.example.tinyrpc.protocol.Invoker; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | import java.lang.reflect.InvocationHandler; 8 | import java.lang.reflect.Method; 9 | 10 | /** 11 | * @auther zhongshunchao 12 | * @date 19/06/2020 23:35 13 | */ 14 | public class InvokerInvocationHandler implements InvocationHandler { 15 | 16 | private static final Logger logger = LoggerFactory.getLogger(InvokerInvocationHandler.class); 17 | 18 | private final Invoker invoker; 19 | 20 | private final Invocation invocation; 21 | 22 | public InvokerInvocationHandler(Invoker invoker, Invocation invocation) { 23 | this.invoker = invoker; 24 | this.invocation = invocation; 25 | } 26 | 27 | @Override 28 | public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 29 | String methodName = method.getName(); 30 | logger.info("start invoking method {}", methodName); 31 | if (method.getDeclaringClass() == Object.class) { 32 | return method.invoke(invoker, args); 33 | } 34 | Class[] parameterTypes = method.getParameterTypes(); 35 | if (parameterTypes.length == 0) { 36 | if ("toString".equals(methodName)) { 37 | return invoker.toString(); 38 | } else if ("hashCode".equals(methodName)) { 39 | return invoker.hashCode(); 40 | } 41 | } else if (parameterTypes.length == 1 && "equals".equals(methodName)) { 42 | return invoker.equals(args[0]); 43 | } 44 | invocation.setMethodName(methodName); 45 | invocation.setArguments(args); 46 | invocation.setParameterTypes(parameterTypes); 47 | return invoker.invoke(invocation); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /tiny-rpc-core/src/main/java/com/example/tinyrpc/common/domain/RpcContext.java: -------------------------------------------------------------------------------- 1 | package com.example.tinyrpc.common.domain; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | import java.util.concurrent.Future; 6 | 7 | /** 8 | * RpcContext的作用是提供隐式传参,可以让用户已添加Filter的方法参与到整个链路调用逻辑中来,同时可用于链路追踪的场景 9 | * @auther zhongshunchao 10 | * @date 03/07/2020 09:00 11 | */ 12 | public class RpcContext { 13 | 14 | private static final ThreadLocal LOCAL = new ThreadLocal(){ 15 | @Override 16 | protected RpcContext initialValue() { 17 | return new RpcContext(); 18 | } 19 | }; 20 | 21 | private Map attachments = new HashMap<>(); 22 | 23 | private long requestId; 24 | 25 | private Invocation invocation; 26 | 27 | private Future future; 28 | 29 | /** 30 | * get context. 31 | * 32 | * @return context 33 | */ 34 | public static RpcContext getContext() { 35 | return LOCAL.get(); 36 | } 37 | 38 | public Map getAttachments() { 39 | return attachments; 40 | } 41 | 42 | public void setAttachments(Map attachments) { 43 | this.attachments = attachments; 44 | } 45 | 46 | public long getRequestId() { 47 | return requestId; 48 | } 49 | 50 | public void setRequestId(long requestId) { 51 | this.requestId = requestId; 52 | } 53 | 54 | public Invocation getInvocation() { 55 | return invocation; 56 | } 57 | 58 | public void setInvocation(Invocation invocation) { 59 | this.invocation = invocation; 60 | } 61 | 62 | public void clearAttachments() { 63 | this.attachments.clear(); 64 | } 65 | 66 | public static void removeContext() { 67 | LOCAL.remove(); 68 | } 69 | 70 | public Future getFuture() { 71 | return future; 72 | } 73 | 74 | public void setFuture(Future future) { 75 | this.future = future; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /tiny-rpc-springboot-stater/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 4.0.0 6 | 7 | com.example.rpc 8 | tiny-rpc-springboot-stater 9 | 1.0-SNAPSHOT 10 | 11 | jar 12 | 13 | 14 | UTF-8 15 | 1.8 16 | 1.8 17 | 1.5.9.RELEASE 18 | 4.3.9.RELEASE 19 | 20 | 21 | 22 | 23 | com.example.rpc 24 | tiny-rpc-core 25 | 1.0-SNAPSHOT 26 | 27 | 28 | org.springframework.boot 29 | spring-boot-autoconfigure 30 | ${springboot.version} 31 | 32 | 33 | org.springframework.boot 34 | spring-boot-configuration-processor 35 | ${springboot.version} 36 | 37 | 38 | org.springframework 39 | spring-context 40 | ${spring.version} 41 | provided 42 | 43 | 44 | 45 | 46 | 47 | 48 | org.apache.maven.plugins 49 | maven-source-plugin 50 | 3.0.1 51 | 52 | 53 | attach-sources 54 | verify 55 | 56 | jar-no-fork 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /tiny-rpc-core/src/main/java/com/example/tinyrpc/serialization/impl/ProtostuffSerialization.java: -------------------------------------------------------------------------------- 1 | package com.example.tinyrpc.serialization.impl; 2 | 3 | import com.dyuproject.protostuff.LinkedBuffer; 4 | import com.dyuproject.protostuff.ProtostuffIOUtil; 5 | import com.dyuproject.protostuff.Schema; 6 | import com.dyuproject.protostuff.runtime.RuntimeSchema; 7 | import com.example.tinyrpc.serialization.Serialization; 8 | 9 | import java.util.Map; 10 | import java.util.concurrent.ConcurrentHashMap; 11 | 12 | /** 13 | * @auther zhongshunchao 14 | * @date 2020/5/20 6:23 下午 15 | */ 16 | public class ProtostuffSerialization implements Serialization { 17 | private static Map, Schema> cachedSchema = new ConcurrentHashMap<>(); 18 | 19 | @Override 20 | public byte[] serialize(T obj) { 21 | Class cls = (Class) obj.getClass(); 22 | LinkedBuffer buffer = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE); 23 | try { 24 | Schema schema = getSchema(cls); 25 | byte[] bytes = ProtostuffIOUtil.toByteArray(obj, schema, buffer); 26 | return bytes; 27 | } catch (Exception e) { 28 | e.printStackTrace(); 29 | throw new IllegalStateException(e.getMessage(), e); 30 | } finally { 31 | buffer.clear(); 32 | } 33 | } 34 | 35 | @Override 36 | public T deserialize(byte[] data, Class cls) { 37 | try { 38 | T message = cls.getDeclaredConstructor().newInstance(); 39 | Schema schema = getSchema(cls); 40 | ProtostuffIOUtil.mergeFrom(data, message, schema); 41 | return message; 42 | } catch (Exception e) { 43 | throw new IllegalStateException(e.getMessage(), e); 44 | } 45 | } 46 | 47 | private static Schema getSchema(Class cls) { 48 | Schema schema = (Schema) cachedSchema.get(cls); 49 | if (schema == null) { 50 | schema = RuntimeSchema.createFrom(cls); 51 | if (schema != null) { 52 | cachedSchema.put(cls, schema); 53 | } 54 | } 55 | return schema; 56 | } 57 | 58 | protected T deserializeOnObject(byte[] data, Class cls, T t) { 59 | try { 60 | Schema schema = getSchema(cls); 61 | ProtostuffIOUtil.mergeFrom(data, t, schema); 62 | return t; 63 | } catch (Exception e) { 64 | throw new IllegalStateException(e.getMessage(), e); 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /tiny-rpc-core/src/main/java/com/example/tinyrpc/serialization/impl/HessianSerialization.java: -------------------------------------------------------------------------------- 1 | package com.example.tinyrpc.serialization.impl; 2 | 3 | import com.alibaba.fastjson.JSON; 4 | import com.caucho.hessian.io.HessianSerializerInput; 5 | import com.caucho.hessian.io.HessianSerializerOutput; 6 | import com.example.tinyrpc.common.exception.BusinessException; 7 | import com.example.tinyrpc.serialization.Serialization; 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | 11 | import java.io.ByteArrayInputStream; 12 | import java.io.ByteArrayOutputStream; 13 | import java.io.IOException; 14 | 15 | /** 16 | * @auther zhongshunchao 17 | * @date 23/05/2020 14:26 18 | */ 19 | public class HessianSerialization implements Serialization { 20 | 21 | private static final Logger logger = LoggerFactory.getLogger(HessianSerialization.class); 22 | 23 | @Override 24 | public byte[] serialize(T obj) { 25 | ByteArrayOutputStream os = null; 26 | HessianSerializerOutput hessianOutput; 27 | try { 28 | os = new ByteArrayOutputStream(); 29 | hessianOutput = new HessianSerializerOutput(os); 30 | hessianOutput.writeObject(obj); 31 | return os.toByteArray(); 32 | } catch (Exception e) { 33 | e.printStackTrace(); 34 | } finally { 35 | try { 36 | if (os != null) { 37 | os.close(); 38 | } 39 | } catch (IOException e) { 40 | logger.error("Fail to close Hessian serialization IO", e); 41 | } 42 | } 43 | return null; 44 | } 45 | 46 | @Override 47 | public T deserialize(byte[] data, Class cls) { 48 | if (data == null) { 49 | throw new NullPointerException(); 50 | } 51 | ByteArrayInputStream is = null; 52 | try { 53 | is = new ByteArrayInputStream(data); 54 | HessianSerializerInput hessianInput = new HessianSerializerInput(is); 55 | return cls.cast(hessianInput.readObject()); 56 | } catch (Exception e) { 57 | logger.error("Can not deserialize data:", JSON.toJSONString(data)); 58 | e.printStackTrace(); 59 | throw new BusinessException("Can not deserialize data:" + JSON.toJSONString(data)); 60 | } finally { 61 | try { 62 | if (is != null) { 63 | is.close(); 64 | } 65 | } catch (IOException e) { 66 | logger.error("Fail to close Hessian serialization IO", e); 67 | } 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /tiny-rpc-core/src/main/java/com/example/tinyrpc/protocol/impl/RegistryProtocol.java: -------------------------------------------------------------------------------- 1 | package com.example.tinyrpc.protocol.impl; 2 | 3 | import com.example.tinyrpc.common.extension.ExtensionLoader; 4 | import com.example.tinyrpc.common.domain.Invocation; 5 | import com.example.tinyrpc.common.domain.URL; 6 | import com.example.tinyrpc.protocol.Invoker; 7 | import com.example.tinyrpc.protocol.Protocol; 8 | import com.example.tinyrpc.registry.Registry; 9 | import com.example.tinyrpc.transport.Endpoint; 10 | import com.example.tinyrpc.transport.Server; 11 | import com.example.tinyrpc.transport.server.NettyServer; 12 | import org.slf4j.Logger; 13 | import org.slf4j.LoggerFactory; 14 | 15 | import java.util.Map; 16 | import java.util.concurrent.ConcurrentHashMap; 17 | 18 | /** 19 | * 一个address对应一个Client,一个Client对应一个Invoker,Invoker可以理解为对Client的封装 20 | * @auther zhongshunchao 21 | * @date 20/06/2020 15:42 22 | */ 23 | public class RegistryProtocol implements Protocol { 24 | 25 | private static final Logger logger = LoggerFactory.getLogger(RegistryProtocol.class); 26 | 27 | /** 28 | * InvokerClientWrapper缓存 29 | */ 30 | private static final Map WRAPPED_INVOKER_CACHE = new ConcurrentHashMap<>(); 31 | 32 | private Registry zkServiceRegistry; 33 | 34 | /** 35 | * 服务端侧只有一个server实例,不同服务可以共享同一个端口 36 | */ 37 | private static final Map SERVER_MAP = new ConcurrentHashMap<>(); 38 | 39 | @Override 40 | public Invoker refer(Invocation invocation) { 41 | zkServiceRegistry = ExtensionLoader.getExtensionLoader().getExtension(Registry.class, invocation.getUrl().getRegistry()); 42 | Invoker invoker = WRAPPED_INVOKER_CACHE.get(invocation); 43 | if (invoker != null) { 44 | return invoker; 45 | } else { 46 | invoker = new InvokerClientWrapper(invocation); 47 | WRAPPED_INVOKER_CACHE.put(invocation, invoker); 48 | return invoker; 49 | } 50 | } 51 | 52 | @Override 53 | public Endpoint export(URL url) { 54 | zkServiceRegistry = ExtensionLoader.getExtensionLoader().getExtension(Registry.class, url.getRegistry()); 55 | String address = url.getAddress(); 56 | Server server = SERVER_MAP.get(address); 57 | if (server == null) { 58 | synchronized (this) { 59 | server = SERVER_MAP.get(address); 60 | if (server == null) { 61 | server = new NettyServer(address); 62 | server.start(); 63 | SERVER_MAP.put(address, server); 64 | } 65 | } 66 | } 67 | zkServiceRegistry.register(url); 68 | return server; 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /tiny-rpc-core/src/main/java/com/example/tinyrpc/common/domain/Response.java: -------------------------------------------------------------------------------- 1 | package com.example.tinyrpc.common.domain; 2 | 3 | import java.io.Serializable; 4 | 5 | /** 6 | * @auther zhongshunchao 7 | * @date 19/05/2020 07:50 8 | */ 9 | //封装对方发送过来的response请求 10 | public class Response implements Serializable { 11 | /** 12 | * ok. 13 | */ 14 | public static final byte OK = 20; 15 | 16 | /** 17 | * client side timeout. 18 | */ 19 | public static final byte CLIENT_TIMEOUT = 30; 20 | 21 | /** 22 | * server side timeout. 23 | */ 24 | public static final byte SERVER_TIMEOUT = 31; 25 | 26 | /** 27 | * channel inactive, directly return the unfinished requests. 28 | */ 29 | public static final byte CHANNEL_INACTIVE = 35; 30 | 31 | /** 32 | * request format error. 33 | */ 34 | public static final byte BAD_REQUEST = 40; 35 | 36 | /** 37 | * response format error. 38 | */ 39 | public static final byte BAD_RESPONSE = 50; 40 | 41 | /** 42 | * service not found. 43 | */ 44 | public static final byte SERVICE_NOT_FOUND = 60; 45 | 46 | /** 47 | * service error. 48 | */ 49 | public static final byte SERVICE_ERROR = 70; 50 | 51 | /** 52 | * internal server error. 53 | */ 54 | public static final byte SERVER_ERROR = 80; 55 | 56 | /** 57 | * internal server error. 58 | */ 59 | public static final byte CLIENT_ERROR = 90; 60 | 61 | private boolean isEvent = false; 62 | 63 | private long requestId; 64 | 65 | private byte status = OK; 66 | 67 | private byte serializationId; 68 | 69 | private ResponseBody responseBody; 70 | 71 | public void setStatus(byte status) { 72 | this.status = status; 73 | } 74 | 75 | public byte getStatus() { 76 | return status; 77 | } 78 | 79 | public boolean isEvent() { 80 | return isEvent; 81 | } 82 | 83 | public void setEvent(boolean event) { 84 | isEvent = event; 85 | } 86 | 87 | public long getRequestId() { 88 | return requestId; 89 | } 90 | 91 | public void setRequestId(long requestId) { 92 | this.requestId = requestId; 93 | } 94 | 95 | public ResponseBody getResponseBody() { 96 | return responseBody; 97 | } 98 | 99 | public void setResponseBody(ResponseBody responseBody) { 100 | this.responseBody = responseBody; 101 | } 102 | 103 | public byte getSerializationId() { 104 | return serializationId; 105 | } 106 | 107 | public void setSerializationId(byte serializationId) { 108 | this.serializationId = serializationId; 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /tiny-rpc-core/src/main/java/com/example/tinyrpc/codec/Encoder.java: -------------------------------------------------------------------------------- 1 | package com.example.tinyrpc.codec; 2 | 3 | import com.alibaba.fastjson.JSON; 4 | import com.example.tinyrpc.common.domain.Request; 5 | import com.example.tinyrpc.common.domain.Response; 6 | import com.example.tinyrpc.serialization.impl.FastJsonSerialization; 7 | import io.netty.buffer.ByteBuf; 8 | import io.netty.channel.ChannelHandlerContext; 9 | import io.netty.handler.codec.MessageToByteEncoder; 10 | import org.slf4j.Logger; 11 | import org.slf4j.LoggerFactory; 12 | 13 | /** 14 | * @auther zhongshunchao 15 | * @date 08/05/2020 08:23 16 | */ 17 | public class Encoder extends MessageToByteEncoder implements Codec{ 18 | 19 | private static final Logger logger = LoggerFactory.getLogger(Encoder.class); 20 | 21 | @Override 22 | protected void encode(ChannelHandlerContext ctx, Object msg, ByteBuf buffer) throws Exception { 23 | logger.info("###Encoder will send msg :" + JSON.toJSONString(msg)); 24 | int savedStart = buffer.writerIndex(); 25 | buffer.writerIndex(savedStart + 4); 26 | buffer.writeBytes(MAGIC_ARRAY); 27 | if (msg instanceof Request) { 28 | encodeRequest((Request) msg, buffer); 29 | } else { 30 | encodeResponse((Response) msg, buffer); 31 | } 32 | int end = buffer.writerIndex(); 33 | buffer.writerIndex(savedStart); 34 | buffer.writeInt(end - savedStart -4); 35 | buffer.writerIndex(end); 36 | } 37 | 38 | private void encodeRequest(Request request, ByteBuf buffer) { 39 | // header. 40 | byte [] flag = new byte[2]; 41 | if (request.isOneway()) { 42 | flag[0] |= FLAG_TWOWAY; 43 | } 44 | flag[0] |= FLAG_REQUEST; 45 | // is PING 46 | if (request.isEvent()) { 47 | flag[0] |= FLAG_EVENT; 48 | } 49 | // set serialization id. 50 | flag[0] |= request.getSerializationId(); 51 | buffer.writeBytes(flag); 52 | long requestId = request.getRequestId(); 53 | buffer.writeLong(requestId); 54 | byte[] body = new FastJsonSerialization().serialize(request.getData()); 55 | buffer.writeBytes(body); 56 | } 57 | 58 | //比request多了个status 59 | private void encodeResponse(Response response, ByteBuf buffer) { 60 | // header. 61 | byte [] flag = new byte[2]; 62 | // is PONG 63 | if (response.isEvent()) { 64 | flag[0] |= FLAG_EVENT; 65 | } 66 | // set serialization id. 67 | flag[0] |= response.getSerializationId(); 68 | flag[1] |= response.getStatus(); 69 | buffer.writeBytes(flag); 70 | long requestId = response.getRequestId(); 71 | buffer.writeLong(requestId); 72 | byte[] body = new FastJsonSerialization().serialize(response.getResponseBody()); 73 | buffer.writeBytes(body); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /tiny-rpc-core/src/main/java/com/example/tinyrpc/cluster/impl/LeastActiveLoadBalance.java: -------------------------------------------------------------------------------- 1 | package com.example.tinyrpc.cluster.impl; 2 | 3 | import com.example.tinyrpc.cluster.LoadBalance; 4 | import com.example.tinyrpc.common.domain.URL; 5 | import com.example.tinyrpc.filter.RpcStatus; 6 | import com.example.tinyrpc.protocol.Invoker; 7 | 8 | import java.util.List; 9 | import java.util.concurrent.ThreadLocalRandom; 10 | 11 | /** 12 | * 基于权重的最小活跃数算法:活跃调用数越小,表明该服务提供者效率越高,单位时间内可处理更多的请求。此时应优先将请求分配给该服务提供者。 13 | * 14 | */ 15 | public class LeastActiveLoadBalance implements LoadBalance { 16 | 17 | @Override 18 | public Invoker select(List invokers) { 19 | int length = invokers.size(); // 总个数 20 | int leastActive = -1; // 最小的活跃数 21 | int leastCount = 0; // 相同最小活跃数的个数 22 | int[] leastIndexs = new int[length]; // 记录相同最小活跃数的下标 23 | int totalWeight = 0; // 总权重 24 | int firstWeight = 0; // 第一个权重,用于计算是否相同 25 | boolean sameWeight = true; // 是否所有权重相同,如果所有权重相同则无差别均等随机 26 | for (int i = 0; i < length; i++) { 27 | Invoker invoker = invokers.get(i); 28 | URL url = invoker.getUrl(); 29 | int active = RpcStatus.getStatus(url).getActive(); // 活跃数 30 | int weight = invoker.getUrl().getWeight(); 31 | if (leastActive == -1 || active < leastActive) { // 发现更小的活跃数,重新开始 32 | leastActive = active; // 记录最小活跃数 33 | leastCount = 1; // 重新统计相同最小活跃数的个数 34 | leastIndexs[0] = i; // 重新记录最小活跃数下标 35 | totalWeight = weight; // 重新累计总权重 36 | firstWeight = weight; // 记录第一个权重 37 | sameWeight = true; // 还原权重相同标识 38 | } else if (active == leastActive) { // 累计相同最小的活跃数 39 | leastIndexs[leastCount ++] = i; // 累计相同最小活跃数下标 40 | totalWeight += weight; // 累计总权重 41 | // 判断所有权重是否一样 42 | if (sameWeight && i > 0 43 | && weight != firstWeight) { 44 | sameWeight = false; 45 | } 46 | } 47 | } 48 | if (leastCount == 1) { 49 | // 如果只有一个最小则直接返回 50 | return invokers.get(leastIndexs[0]); 51 | } 52 | // 在相同最小或约数中按总权重数随机 53 | if (! sameWeight && totalWeight > 0) { 54 | // 如果权重不相同且权重大于0则按总权重数随机 55 | int offsetWeight = ThreadLocalRandom.current().nextInt(totalWeight); 56 | // 并确定随机值落在哪个片断上 57 | for (int i = 0; i < leastCount; i++) { 58 | int leastIndex = leastIndexs[i]; 59 | offsetWeight -= invokers.get(leastIndex).getUrl().getWeight(); 60 | if (offsetWeight <= 0) 61 | return invokers.get(leastIndex); 62 | } 63 | } 64 | // 如果权重相同或权重为0则均等随机 65 | return invokers.get(leastIndexs[ThreadLocalRandom.current().nextInt(leastCount)]); 66 | } 67 | } -------------------------------------------------------------------------------- /tiny-rpc-springboot-stater/src/main/java/com.example.autoconfig/processor/ReferenceBeanPostProcessor.java: -------------------------------------------------------------------------------- 1 | package com.example.autoconfig.processor; 2 | 3 | 4 | import com.example.autoconfig.annotation.Reference; 5 | import com.example.tinyrpc.common.domain.Invocation; 6 | import com.example.tinyrpc.common.domain.URL; 7 | import com.example.tinyrpc.config.ReferenceConfig; 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | import org.springframework.beans.BeansException; 11 | import org.springframework.beans.factory.config.BeanPostProcessor; 12 | import java.lang.reflect.Field; 13 | 14 | /** 15 | * @auther zhongshunchao 16 | * @date 2020/5/21 3:06 下午 17 | */ 18 | //解析@Reference 19 | public class ReferenceBeanPostProcessor implements BeanPostProcessor { 20 | 21 | private static final Logger logger = LoggerFactory.getLogger(ReferenceBeanPostProcessor.class); 22 | 23 | @Override 24 | public Object postProcessBeforeInitialization(Object o, String s) throws BeansException { 25 | return o; 26 | } 27 | 28 | @Override 29 | public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { 30 | Class curClass = bean.getClass(); 31 | Field[] fields = curClass.getDeclaredFields(); 32 | //扫描含有@Reference的字段 33 | for (Field field : fields) { 34 | if (field.isAnnotationPresent(Reference.class)){ 35 | Class interfaceClass = field.getType(); 36 | Reference reference = field.getAnnotation(Reference.class); 37 | if (reference != null) { 38 | Invocation invocation = new Invocation(); 39 | URL url = new URL(); 40 | url.setOneWay(reference.oneway()).setTimeout(reference.timeout()).setProtocol(reference.protocol()) 41 | .setProxy(reference.proxy()).setLoadbalance(reference.loadbalance()).setActives(reference.actives()) 42 | .setSerialization(reference.serializer()).setFilters(reference.filter()).setRegistry(reference.registry()).setAync(reference.async()); 43 | invocation.setInterfaceClass(interfaceClass); 44 | invocation.setServiceName(interfaceClass.getName()); 45 | invocation.setUrl(url); 46 | invocation.setInjvm(reference.injvm()); 47 | //将含有@Reference的字段的属性替换成代理对象 48 | try { 49 | field.setAccessible(true); 50 | ReferenceConfig referenceConfig = new ReferenceConfig(); 51 | Object proxy = referenceConfig.get(invocation); 52 | field.set(bean, proxy); 53 | } catch (IllegalAccessException e) { 54 | logger.error("Unable to set field", e); 55 | } 56 | } 57 | } 58 | } 59 | return bean; 60 | } 61 | 62 | 63 | } 64 | -------------------------------------------------------------------------------- /tiny-rpc-core/src/main/java/com/example/tinyrpc/filter/Span.java: -------------------------------------------------------------------------------- 1 | package com.example.tinyrpc.filter; 2 | 3 | import java.io.Serializable; 4 | 5 | /** 6 | * @auther zhongshunchao 7 | * @date 03/07/2020 23:26 8 | */ 9 | public class Span implements Serializable { 10 | private long traceId; // 一次请求的全局唯一id 11 | private String spanId; // 调用关系id, 标识一次trace中的某一次rpc调用, 签名方式命名, EG : 0, 0.1, 0.2, 01.1 12 | private int currentSpanNum = 1; // 标识分配到了第几个span, 用于生成调用下游的spanId 13 | private String spanName; // 调用接口的Class Name + "." + Method Name 14 | // private transient Span parentSpan; // 上游的span 15 | /** 16 | * 远端的appKey, ip, port 信息 17 | */ 18 | private String remoteAddress; 19 | private int side; // 是client span 还是 server span, 0 client, 1 server 20 | /** 21 | * 服务返回状态 22 | */ 23 | private String status; 24 | private boolean async = true; // 异步标识 25 | 26 | public Span(long traceId, String remoteAddress, String spanName, int side, String spanId) { 27 | this.traceId = traceId; 28 | this.remoteAddress = remoteAddress; 29 | this.spanName = spanName; 30 | this.side = side; 31 | this.spanId = spanId; 32 | } 33 | 34 | public String getSpanId() { 35 | return spanId; 36 | } 37 | 38 | public int getCurrentSpanNum() { 39 | return currentSpanNum; 40 | } 41 | 42 | public void setCurrentSpanNum(int currentSpanNum) { 43 | this.currentSpanNum = currentSpanNum; 44 | } 45 | 46 | public void incCurrentSpanNum() { 47 | ++currentSpanNum; 48 | } 49 | 50 | public long getTraceId() { 51 | return traceId; 52 | } 53 | 54 | public void setTraceId(long traceId) { 55 | this.traceId = traceId; 56 | } 57 | 58 | public void setSpanId(String spanId) { 59 | this.spanId = spanId; 60 | } 61 | 62 | public String getSpanName() { 63 | return spanName; 64 | } 65 | 66 | public void setSpanName(String spanName) { 67 | this.spanName = spanName; 68 | } 69 | 70 | public String getRemoteAddress() { 71 | return remoteAddress; 72 | } 73 | 74 | public void setRemoteAddress(String remoteAddress) { 75 | this.remoteAddress = remoteAddress; 76 | } 77 | 78 | public int getSide() { 79 | return side; 80 | } 81 | 82 | public void setSide(int side) { 83 | this.side = side; 84 | } 85 | 86 | public String getStatus() { 87 | return status; 88 | } 89 | 90 | public void setStatus(String status) { 91 | this.status = status; 92 | } 93 | 94 | public boolean isAsync() { 95 | return async; 96 | } 97 | 98 | public void setAsync(boolean async) { 99 | this.async = async; 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /tiny-rpc-core/src/main/java/com/example/tinyrpc/filter/impl/TraceFilter.java: -------------------------------------------------------------------------------- 1 | package com.example.tinyrpc.filter.impl; 2 | 3 | import com.alibaba.fastjson.JSON; 4 | import com.alibaba.fastjson.JSONObject; 5 | import com.example.tinyrpc.common.domain.Constants; 6 | import com.example.tinyrpc.common.domain.Invocation; 7 | import com.example.tinyrpc.common.domain.RpcContext; 8 | import com.example.tinyrpc.common.domain.URL; 9 | import com.example.tinyrpc.common.utils.UUIDUtils; 10 | import com.example.tinyrpc.filter.Filter; 11 | import com.example.tinyrpc.filter.Span; 12 | import com.example.tinyrpc.protocol.Invoker; 13 | import org.slf4j.Logger; 14 | import org.slf4j.LoggerFactory; 15 | 16 | import java.util.Map; 17 | 18 | import static com.example.tinyrpc.common.domain.Constants.SPAN_KEY; 19 | 20 | /** 21 | * 链路跟踪,简单理解就是跨方法调用的日志打印和查询。TraceFilter是可以用注解上的字段配置开启还是关闭的,默认关闭 22 | * @auther zhongshunchao 23 | * @date 03/07/2020 08:48 24 | */ 25 | public class TraceFilter implements Filter { 26 | 27 | private static final Logger logger = LoggerFactory.getLogger(TraceFilter.class); 28 | 29 | @Override 30 | public Object invoke(Invoker invoker, Invocation invocation) throws Exception{ 31 | logger.info("###TraceFilter Start tracing..."); 32 | Map attachments = RpcContext.getContext().getAttachments(); 33 | // 这里默认的序列化方式为fastjson 34 | Span parentSpan; 35 | Object obj = attachments.get(SPAN_KEY); 36 | if (obj instanceof Span) { 37 | parentSpan = (Span) obj; 38 | } else { 39 | JSONObject parentSpanObject = (JSONObject) attachments.get(SPAN_KEY); 40 | parentSpan = JSONObject.toJavaObject(parentSpanObject, Span.class); 41 | } 42 | URL url = invocation.getUrl(); 43 | String spanName = invocation.getServiceName() + "#" + invocation.getMethodName(); 44 | Span curSpan; 45 | if (parentSpan == null) { 46 | curSpan = new Span(UUIDUtils.getUUID(), url.getAddress(), spanName, invocation.getSide(), "0"); 47 | parentSpan = curSpan; 48 | parentSpan.incCurrentSpanNum(); 49 | logger.warn("###Tracing Result, curSpan == {}, thread id == {}", JSON.toJSONString(curSpan), Thread.currentThread().getId()); 50 | } else { 51 | curSpan = new Span(parentSpan.getTraceId(), url.getAddress(), spanName, invocation.getSide(), parentSpan.getSpanId() + "." + parentSpan.getCurrentSpanNum()); 52 | if (invocation.getSide() == Constants.SERVER_SIDE) { 53 | logger.warn("###Tracing Result, curSpan == {}, thread id == {}", JSON.toJSONString(curSpan), Thread.currentThread().getId()); 54 | } else { 55 | parentSpan.incCurrentSpanNum(); 56 | curSpan = parentSpan; 57 | } 58 | } 59 | attachments.put(SPAN_KEY, curSpan); 60 | // start invoke 61 | Object result = invoker.invoke(invocation); 62 | // end invoke 63 | // 恢复parent span 64 | attachments.put(SPAN_KEY, parentSpan); 65 | return result; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /tiny-rpc-core/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | tiny-rpc-parent 7 | com.example.rpc 8 | 1.0-SNAPSHOT 9 | 10 | 4.0.0 11 | tiny-rpc-core 12 | 1.0-SNAPSHOT 13 | 14 | UTF-8 15 | 1.8 16 | 1.8 17 | 18 | 19 | 20 | 21 | io.netty 22 | netty-all 23 | 4.1.42.Final 24 | 25 | 26 | com.dyuproject.protostuff 27 | protostuff-core 28 | 1.1.3 29 | 30 | 31 | com.dyuproject.protostuff 32 | protostuff-runtime 33 | 1.1.3 34 | 35 | 36 | com.caucho 37 | hessian 38 | 4.0.38 39 | 40 | 41 | org.springframework.boot 42 | spring-boot-starter-logging 43 | 1.5.9.RELEASE 44 | 45 | 46 | slf4j-api 47 | org.slf4j 48 | 49 | 50 | 51 | 52 | com.alibaba 53 | fastjson 54 | 1.2.71 55 | 56 | 57 | org.apache.zookeeper 58 | zookeeper 59 | 3.4.14 60 | 61 | 62 | org.slf4j 63 | slf4j-log4j12 64 | 65 | 66 | log4j 67 | log4j 68 | 69 | 70 | io.netty 71 | netty 72 | 73 | 74 | 75 | 76 | org.springframework 77 | spring-context 78 | 4.3.9.RELEASE 79 | provided 80 | 81 | 82 | javassist 83 | javassist 84 | 3.12.1.GA 85 | 86 | 87 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /tiny-rpc-core/src/main/java/com/example/tinyrpc/protocol/impl/ProtocolFilterWrapper.java: -------------------------------------------------------------------------------- 1 | package com.example.tinyrpc.protocol.impl; 2 | 3 | import com.example.tinyrpc.common.domain.Constants; 4 | import com.example.tinyrpc.common.domain.Invocation; 5 | import com.example.tinyrpc.common.domain.URL; 6 | import com.example.tinyrpc.common.extension.ExtensionLoader; 7 | import com.example.tinyrpc.config.ServiceConfig; 8 | import com.example.tinyrpc.filter.Filter; 9 | import com.example.tinyrpc.protocol.Invoker; 10 | import com.example.tinyrpc.protocol.Protocol; 11 | import com.example.tinyrpc.transport.Endpoint; 12 | import org.slf4j.Logger; 13 | import org.slf4j.LoggerFactory; 14 | 15 | import java.util.List; 16 | 17 | /** 18 | * 该类为被SPI加载的扩展点,负责包装RegistryProtocol,以及生产InvokerChain 19 | * @auther zhongshunchao 20 | * @date 27/06/2020 20:29 21 | */ 22 | public class ProtocolFilterWrapper implements Protocol { 23 | 24 | private static final Logger logger = LoggerFactory.getLogger(ProtocolFilterWrapper.class); 25 | 26 | private static final Protocol PROTOCOL = new RegistryProtocol(); 27 | 28 | private URL url; 29 | 30 | /** 31 | * 返回责任链头部的Filter 32 | * @param invoker 33 | * @return 34 | */ 35 | private Invoker buildInvokerChain(final Invoker invoker, int side) { 36 | Invoker last = invoker; 37 | // 获得所有激活的Filter(简化处理,不进行排序) 38 | List filters = ExtensionLoader.getExtensionLoader().buidFilterChain(url.getFilters(), side); 39 | logger.info("Build Filter Successful, Filters:{}", filters); 40 | if (filters.size() > 0) { 41 | for (int i = filters.size() - 1; i >= 0; i--) { 42 | final Filter filter = filters.get(i); 43 | // 复制引用,构建filter调用链 44 | final Invoker next = last; 45 | // 这里只是构造一个最简化的Invoker作为调用链的载体Invoker 46 | last = new Invoker() { 47 | @Override 48 | public URL getUrl() { 49 | return invoker.getUrl(); 50 | } 51 | 52 | @Override 53 | public Class getInterface() { 54 | return invoker.getInterface(); 55 | } 56 | 57 | @Override 58 | public Object invoke(Invocation invocation) throws Exception { 59 | return filter.invoke(next, invocation); 60 | } 61 | 62 | @Override 63 | public void destroy() { 64 | invoker.destroy(); 65 | } 66 | }; 67 | } 68 | } 69 | return last; 70 | } 71 | 72 | 73 | @Override 74 | public Invoker refer(Invocation invocation) { 75 | this.url = invocation.getUrl(); 76 | // 进行负载均衡之后得到invoker 77 | Invoker invoker; 78 | String serviceName = invocation.getServiceName(); 79 | if (invocation.isInjvm() && ServiceConfig.INVOKER_MAP.containsKey(serviceName)) { 80 | return ServiceConfig.INVOKER_MAP.get(serviceName); 81 | } else { 82 | // 构建invoker chain 83 | invoker = PROTOCOL.refer(invocation); 84 | return buildInvokerChain(invoker, Constants.CLIENT_SIDE); 85 | } 86 | } 87 | 88 | @Override 89 | public Endpoint export(URL url) { 90 | this.url = url; 91 | Endpoint endpoint = PROTOCOL.export(url); 92 | RealInvoker invoker = new RealInvoker(null, 0, url); 93 | invoker.setRef(url.getRef()); 94 | invoker.setEndpoint(endpoint); 95 | ServiceConfig.INVOKER_MAP.put(url.getInterfaceName(), buildInvokerChain(invoker, Constants.SERVER_SIDE)); 96 | return endpoint; 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /tiny-rpc-core/src/main/java/com/example/tinyrpc/codec/Decoder.java: -------------------------------------------------------------------------------- 1 | package com.example.tinyrpc.codec; 2 | 3 | 4 | import com.alibaba.fastjson.JSON; 5 | import com.example.tinyrpc.common.domain.Invocation; 6 | import com.example.tinyrpc.common.domain.Request; 7 | import com.example.tinyrpc.common.domain.Response; 8 | import com.example.tinyrpc.common.domain.ResponseBody; 9 | import com.example.tinyrpc.common.extension.ExtensionLoader; 10 | import com.example.tinyrpc.serialization.Serialization; 11 | import io.netty.buffer.ByteBuf; 12 | import io.netty.channel.ChannelHandlerContext; 13 | import io.netty.handler.codec.ByteToMessageDecoder; 14 | import org.slf4j.Logger; 15 | import org.slf4j.LoggerFactory; 16 | 17 | import java.util.List; 18 | 19 | 20 | 21 | /** 22 | * @auther zhongshunchao 23 | * @date 08/05/2020 08:23 24 | * 仿照Dubbo协议 25 | * ------------ (32 bits) 26 | * totoal length except this // 往下的所有字段长度的总和 27 | * ------------ (32 bits) 28 | * | magic (16 bits) // 魔数,为"0xdeff" 29 | * | Req/Res (1 bit) // Req/Res, 请求: 1; 响应: 0 30 | * | 2way (1 bit) // 仅在 Req/Res 为1(请求)时才有用,标记是否期望从服务器返回值。如果需要来自服务器的返回值,则设置为1。 31 | * | event (1 bit) // 表示为PING/PONG事件,如果是req,则为PING请求;如果是res,则为PONG请求 32 | * | default (1 bit) // 缺省字段,占位用 33 | * | serialization_id (4 bits) // 序列化器的ID 34 | * | status (8 bits) // 消息的状态:200/404/500等 35 | * ----------- (64 bits) 36 | * | RPC Request ID (64 bits) 37 | * ----------- 38 | * | Data // 支持变长格式 39 | */ 40 | public class Decoder extends ByteToMessageDecoder implements Codec{ 41 | 42 | private static final Logger logger = LoggerFactory.getLogger(Decoder.class); 43 | 44 | // 先读头部,解析出消息的长度 45 | @Override 46 | protected void decode(ChannelHandlerContext ctx, ByteBuf buffer, List out) throws Exception { 47 | logger.info("###Decoder received buffer" + JSON.toJSONString(buffer)); 48 | int len = buffer.readableBytes(); 49 | if (buffer.readableBytes() < 16) { 50 | // 头部异常 51 | throw new Exception("头部异常"); 52 | } 53 | // 解析头部序列化编号、状态码 54 | byte [] magic = new byte[2]; 55 | buffer.readBytes(magic); 56 | byte [] header = new byte[2]; 57 | buffer.readBytes(header); 58 | // 解析Request ID 59 | long requestId = buffer.readLong(); 60 | // 是否是请求消息 61 | boolean isRequest = (header[0] & FLAG_REQUEST) != 0; 62 | // 是否期望从服务器返回值 63 | boolean oneway = (header[0] & FLAG_TWOWAY) != 0; 64 | // 是否是事件 65 | boolean event = (header[0] & FLAG_TWOWAY) != 0; 66 | // 取后5位,解析出serializationId 67 | byte serializationId = (byte) (header[0] & SERIALIZATION_MASK); 68 | // 解析status 69 | byte status = header[1]; 70 | byte [] body = new byte [len - 12]; 71 | // 解析消息体 72 | buffer.readBytes(body); 73 | Serialization serialization = ExtensionLoader.getExtensionLoader().getDefaultExtension(Serialization.class); 74 | if (isRequest) { 75 | Request request = new Request(requestId, serializationId); 76 | request.setEvent(event); 77 | Invocation data = serialization.deserialize(body, Invocation.class); 78 | request.setData(data); 79 | out.add(request); 80 | } else { 81 | Response response = new Response(); 82 | response.setRequestId(requestId); 83 | response.setEvent(event); 84 | ResponseBody responseBody = serialization.deserialize(body, ResponseBody.class); 85 | response.setResponseBody(responseBody); 86 | out.add(response); 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /tiny-rpc-core/src/main/java/com/example/tinyrpc/proxy/JavassistProxy.java: -------------------------------------------------------------------------------- 1 | package com.example.tinyrpc.proxy; 2 | 3 | import javassist.*; 4 | 5 | import java.lang.reflect.InvocationHandler; 6 | import java.util.concurrent.atomic.AtomicInteger; 7 | 8 | /** 9 | * @auther zhongshunchao 10 | * @date 05/07/2020 17:54 11 | */ 12 | public class JavassistProxy { 13 | 14 | private static final String PREFIX = "$Proxy"; 15 | 16 | private static final AtomicInteger SUFFIX = new AtomicInteger(0); 17 | 18 | protected static InvocationHandler invocationHandler; 19 | 20 | protected JavassistProxy(InvocationHandler invocationHandler) { 21 | this.invocationHandler = invocationHandler; 22 | } 23 | 24 | public static Object newProxyInstance(ClassLoader loader, Class targetClass, InvocationHandler h) throws Exception { 25 | invocationHandler = h; 26 | ClassPool pool = ClassPool.getDefault(); 27 | CtClass ctClass = pool.get(targetClass.getName()); 28 | CtClass proxyCls = pool.makeClass(generateName(ctClass)); 29 | int methodIndex = 0; 30 | CtClass[] interfaces = ctClass.getInterfaces(); 31 | //将Proxy类设置成父类 关键!!! 32 | proxyCls.setSuperclass(pool.get(JavassistProxy.class.getName())); 33 | for (int i = 0; i < interfaces.length; i++) { 34 | CtClass ctInter = interfaces[i]; 35 | proxyCls.addInterface(ctInter); 36 | CtMethod[] methods = ctInter.getDeclaredMethods(); 37 | for (int j = 0; j < methods.length; j++) { 38 | String fieldSrc = String.format("private java.lang.reflect.Method method%d = " + 39 | "Class.forName(\"%s\").getDeclaredMethods()[%d];", methodIndex, ctInter.getName(), i); 40 | CtField field = CtField.make(fieldSrc, proxyCls); 41 | proxyCls.addField(field); 42 | CtMethod ctMethod = methods[i]; 43 | generateMethod(proxyCls, ctMethod, pool, methodIndex); 44 | 45 | } 46 | methodIndex++; 47 | 48 | } 49 | //生成构造函数 50 | generateConstructor(pool, proxyCls); 51 | // 持久化class到硬盘 52 | proxyCls.writeFile(JavassistProxy.class.getResource("/").getPath()); 53 | 54 | return proxyCls.toClass(loader, null).getConstructor(InvocationHandler.class).newInstance(invocationHandler); 55 | } 56 | 57 | private static void generateConstructor(ClassPool pool, CtClass proxy) throws NotFoundException, CannotCompileException { 58 | CtConstructor ctConstructor = new CtConstructor(new CtClass[]{pool.get(InvocationHandler.class.getName())}, proxy); 59 | String methodBodySrc = String.format("super(%s);", "$1"); 60 | ctConstructor.setBody(methodBodySrc); 61 | proxy.addConstructor(ctConstructor); 62 | } 63 | 64 | /** 65 | * 生成代理方法 66 | * 横切的实现 67 | * 68 | * @param proxyClass 69 | * @param ctMethod 70 | * @param pool 71 | * @param methodIndex 72 | */ 73 | private static void generateMethod(CtClass proxyClass, CtMethod ctMethod, ClassPool pool, int methodIndex) throws Exception { 74 | String body = String.format("super.invocationHandler.invoke(this,method%d,$args);", methodIndex); 75 | CtMethod method = CtNewMethod.make(ctMethod.getModifiers(), ctMethod.getReturnType(), 76 | ctMethod.getName(), ctMethod.getParameterTypes(), ctMethod.getExceptionTypes(), body, proxyClass); 77 | proxyClass.addMethod(method); 78 | } 79 | 80 | /** 81 | * 生成全限定代理类名 82 | * 83 | * @param ctClass 84 | * @return 85 | */ 86 | private static String generateName(CtClass ctClass) { 87 | String packageName = ctClass.getPackageName(); 88 | return packageName + PREFIX + SUFFIX.getAndIncrement(); 89 | } 90 | } -------------------------------------------------------------------------------- /tiny-rpc-core/src/main/java/com/example/tinyrpc/common/domain/Invocation.java: -------------------------------------------------------------------------------- 1 | package com.example.tinyrpc.common.domain; 2 | 3 | import java.io.Serializable; 4 | import java.util.Arrays; 5 | import java.util.HashMap; 6 | import java.util.Map; 7 | import java.util.Objects; 8 | 9 | /** 10 | * @auther zhongshunchao 11 | * @date 2020/5/19 6:30 下午 12 | */ 13 | // 封装对方发送过来的request请求 14 | public class Invocation implements Serializable { 15 | 16 | private String serviceName; 17 | 18 | private String methodName; 19 | 20 | private Class[] parameterTypes; 21 | 22 | private Object[] arguments; 23 | 24 | private Map attachments = new HashMap<>(); 25 | 26 | private transient int side = 1; 27 | 28 | private transient int timeout = 50000; 29 | 30 | private transient URL url; 31 | 32 | private transient Class interfaceClass; 33 | 34 | private transient boolean injvm; 35 | 36 | public String getServiceName() { 37 | return serviceName; 38 | } 39 | 40 | public void setServiceName(String serviceName) { 41 | this.serviceName = serviceName; 42 | } 43 | 44 | public String getMethodName() { 45 | return methodName; 46 | } 47 | 48 | public void setMethodName(String methodName) { 49 | this.methodName = methodName; 50 | } 51 | 52 | public Class[] getParameterTypes() { 53 | return parameterTypes; 54 | } 55 | 56 | public void setParameterTypes(Class[] parameterTypes) { 57 | this.parameterTypes = parameterTypes; 58 | } 59 | 60 | public Object[] getArguments() { 61 | return arguments; 62 | } 63 | 64 | public void setArguments(Object[] arguments) { 65 | this.arguments = arguments; 66 | } 67 | 68 | 69 | public Class getInterfaceClass() { 70 | return interfaceClass; 71 | } 72 | 73 | public void setInterfaceClass(Class interfaceClass) { 74 | this.interfaceClass = interfaceClass; 75 | } 76 | 77 | public boolean isInjvm() { 78 | return injvm; 79 | } 80 | 81 | public void setInjvm(boolean injvm) { 82 | this.injvm = injvm; 83 | } 84 | 85 | public URL getUrl() { 86 | return url; 87 | } 88 | 89 | public void setUrl(URL url) { 90 | this.url = url; 91 | } 92 | 93 | public int getTimeout() { 94 | return timeout; 95 | } 96 | 97 | public void setTimeout(int timeout) { 98 | this.timeout = timeout; 99 | } 100 | 101 | public Map getAttachments() { 102 | return attachments; 103 | } 104 | 105 | public void setAttachments(Map attachments) { 106 | this.attachments = attachments; 107 | } 108 | 109 | public int getSide() { 110 | return side; 111 | } 112 | 113 | public void setSide(int side) { 114 | this.side = side; 115 | } 116 | 117 | @Override 118 | public boolean equals(Object o) { 119 | if (this == o) return true; 120 | if (!(o instanceof Invocation)) return false; 121 | Invocation that = (Invocation) o; 122 | return getServiceName().equals(that.getServiceName()) && 123 | getMethodName().equals(that.getMethodName()) && 124 | Arrays.equals(getParameterTypes(), that.getParameterTypes()) && 125 | Arrays.equals(getArguments(), that.getArguments()) && 126 | getUrl().equals(that.getUrl()) && 127 | getInterfaceClass().equals(that.getInterfaceClass()); 128 | } 129 | 130 | @Override 131 | public int hashCode() { 132 | int result = Objects.hash(getServiceName(), getMethodName(), getUrl(), getInterfaceClass()); 133 | result = 31 * result + Arrays.hashCode(getParameterTypes()); 134 | result = 31 * result + Arrays.hashCode(getArguments()); 135 | return result; 136 | } 137 | 138 | } 139 | -------------------------------------------------------------------------------- /tiny-rpc-core/src/main/java/com/example/tinyrpc/registry/ZkSupport.java: -------------------------------------------------------------------------------- 1 | package com.example.tinyrpc.registry; 2 | 3 | import com.example.tinyrpc.common.exception.BusinessException; 4 | import org.apache.zookeeper.*; 5 | import org.apache.zookeeper.data.Stat; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | import java.nio.charset.Charset; 9 | import java.util.List; 10 | import java.util.concurrent.CountDownLatch; 11 | 12 | public class ZkSupport { 13 | 14 | private static final Logger logger = LoggerFactory.getLogger(ZkSupport.class); 15 | 16 | /** 17 | * zk变量 18 | */ 19 | private ZooKeeper zookeeper; 20 | /** 21 | * 信号量设置,用于等待zookeeper连接建立之后 通知阻塞程序继续向下执行 22 | */ 23 | private CountDownLatch connectedSemaphore = new CountDownLatch(1); 24 | 25 | private static final int ZK_SESSION_TIMEOUT = 10000; 26 | 27 | private static final String ZK_ADDRESS = "127.0.0.1:2181"; 28 | 29 | public ZkSupport() { 30 | try { 31 | this.zookeeper = new ZooKeeper(ZK_ADDRESS, ZK_SESSION_TIMEOUT, (WatchedEvent event) -> { 32 | //获取事件的状态 33 | Watcher.Event.KeeperState keeperState = event.getState(); 34 | Watcher.Event.EventType eventType = event.getType(); 35 | //如果是建立连接 36 | if (Watcher.Event.KeeperState.SyncConnected == keeperState) { 37 | if (Watcher.Event.EventType.None == eventType) { 38 | //如果建立连接成功,则发送信号量,让后续阻塞程序向下执行 39 | connectedSemaphore.countDown(); 40 | logger.info("Zookeeper connected successfully at address:" + ZK_ADDRESS); 41 | } 42 | } 43 | }); 44 | logger.info("Start connecting Zookeeper"); 45 | connectedSemaphore.await(); 46 | } catch (Exception e) { 47 | logger.error("Cannot connect Zookeeper :" + ZK_ADDRESS); 48 | e.printStackTrace(); 49 | throw new BusinessException("Cannot connect Zookeeper :" + ZK_ADDRESS); 50 | } 51 | } 52 | 53 | public void createNodeIfAbsent(String data, String path) { 54 | try { 55 | byte[] bytes = data.getBytes(Charset.forName("UTF-8")); 56 | zookeeper.create(path + "/" + data, bytes, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL); 57 | } catch (InterruptedException | KeeperException ex) { 58 | logger.error("Fail to register path :" + path + ": data ;" + data, ex); 59 | } 60 | } 61 | 62 | public List getChildren(final String path, Watcher watcher) throws KeeperException, InterruptedException { 63 | return zookeeper.getChildren(path, watcher); 64 | } 65 | 66 | public List getChildren(String path, boolean watch) throws KeeperException, InterruptedException { 67 | return zookeeper.getChildren(path, watch); 68 | } 69 | 70 | public byte[] getData(String path, Watcher watcher) throws KeeperException, InterruptedException { 71 | return zookeeper.getData(path, watcher, null); 72 | } 73 | 74 | /** 75 | * @param path 76 | * @param createMode 77 | */ 78 | public void createPathIfAbsent(String path, CreateMode createMode) throws KeeperException, InterruptedException { 79 | Stat s = zookeeper.exists(path, false); 80 | if (s == null) { 81 | zookeeper.create(path, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, createMode); 82 | } 83 | } 84 | 85 | public boolean hasNoRoot(String path) throws KeeperException, InterruptedException { 86 | Stat stat = zookeeper.exists(path, false); 87 | return stat == null; 88 | } 89 | 90 | /** 91 | * 关闭ZK连接 92 | */ 93 | public void close() { 94 | try { 95 | this.zookeeper.close(); 96 | } catch (InterruptedException e) { 97 | logger.error("Fail to close connection from Zookeeper, exception:" + e.getMessage()); 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /tiny-rpc-core/src/main/java/com/example/tinyrpc/protocol/impl/RealInvoker.java: -------------------------------------------------------------------------------- 1 | package com.example.tinyrpc.protocol.impl; 2 | 3 | import com.alibaba.fastjson.JSON; 4 | import com.example.tinyrpc.common.domain.*; 5 | import com.example.tinyrpc.common.exception.BusinessException; 6 | import com.example.tinyrpc.common.utils.CodecSupport; 7 | import com.example.tinyrpc.common.utils.UUIDUtils; 8 | import com.example.tinyrpc.protocol.Invoker; 9 | import com.example.tinyrpc.transport.Endpoint; 10 | import org.slf4j.Logger; 11 | import org.slf4j.LoggerFactory; 12 | 13 | import java.lang.reflect.Method; 14 | import java.util.concurrent.Future; 15 | import java.util.concurrent.TimeUnit; 16 | 17 | import static com.example.tinyrpc.common.domain.Constants.CLIENT_SIDE; 18 | 19 | /** 20 | * 原生的Invoker 21 | * @auther zhongshunchao 22 | * @date 26/06/2020 21:43 23 | */ 24 | public class RealInvoker implements Invoker { 25 | 26 | private static final Logger logger = LoggerFactory.getLogger(RealInvoker.class); 27 | 28 | private Endpoint endpoint; 29 | 30 | private int weight; 31 | 32 | private Class interfaceClass; 33 | 34 | private URL url; 35 | 36 | private Object ref; 37 | 38 | public RealInvoker(Class interfaceClass, int weight, URL url) { 39 | this.interfaceClass = interfaceClass; 40 | this.weight = weight; 41 | this.url = url; 42 | } 43 | 44 | @Override 45 | public Object invoke(Invocation invocation) throws Exception { 46 | logger.info("###RealInvoker start invoke invocation:" + JSON.toJSONString(invocation)); 47 | invocation.getAttachments().putAll(RpcContext.getContext().getAttachments()); 48 | if (invocation.getSide() == CLIENT_SIDE) { 49 | URL url = invocation.getUrl(); 50 | if (url == null) { 51 | throw new IllegalStateException("###RealInvoker: url is null while invoking:" + JSON.toJSONString(invocation)); 52 | } 53 | if (endpoint == null) { 54 | throw new IllegalStateException("###RealInvoker:endpoint is null while invoking:" + JSON.toJSONString(invocation)); 55 | } 56 | Request request = new Request(UUIDUtils.getUUID(), CodecSupport.getIDByName(url.getSerialization())); 57 | request.setOneway(!url.isOneWay()); 58 | request.setSerializationId(CodecSupport.getIDByName(url.getSerialization())); 59 | request.setData(invocation); 60 | Future future = endpoint.send(request); 61 | Object response = null; 62 | if (url.isAync()) { 63 | RpcContext.getContext().setFuture(future); 64 | } else if (url.isOneWay()) { 65 | RpcContext.getContext().setFuture(null); 66 | return new Response(); 67 | } else { 68 | RpcContext.getContext().setFuture(null); 69 | try { 70 | if (future != null) { 71 | response = future.get(invocation.getTimeout(), TimeUnit.MILLISECONDS); 72 | } 73 | } catch (Exception e) { 74 | logger.error("Server timeout : Fail to get result from Server when invoking invocation:" + JSON.toJSONString(invocation)); 75 | e.printStackTrace(); 76 | throw new BusinessException("Server timeout : Fail to get result from Server when invoking invocation:" + JSON.toJSONString(invocation)); 77 | } 78 | } 79 | return response; 80 | } else { 81 | Method method = ref.getClass().getMethod(invocation.getMethodName(), invocation.getParameterTypes()); 82 | method.setAccessible(true); 83 | return method.invoke(ref, invocation.getArguments()); 84 | } 85 | 86 | } 87 | 88 | @Override 89 | public URL getUrl() { 90 | return url; 91 | } 92 | 93 | @Override 94 | public Class getInterface() { 95 | return interfaceClass; 96 | } 97 | 98 | public void setEndpoint(Endpoint endpoint) { 99 | this.endpoint = endpoint; 100 | } 101 | 102 | @Override 103 | public void destroy() { 104 | if (this.endpoint != null) { 105 | endpoint.close(); 106 | endpoint = null; 107 | } 108 | } 109 | 110 | public void setRef(Object ref) { 111 | this.ref = ref; 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /tiny-rpc-core/src/main/java/com/example/tinyrpc/common/domain/URL.java: -------------------------------------------------------------------------------- 1 | package com.example.tinyrpc.common.domain; 2 | 3 | /** 4 | * @auther zhongshunchao 5 | * @date 24/06/2020 18:24 6 | * dubbo中的URL中定义了一些扩展点的实现名、应用名、方法名,所属类目、版本号等 7 | */ 8 | public class URL { 9 | 10 | private String interfaceName; 11 | 12 | private int weight; 13 | 14 | private String ip; 15 | 16 | private int port; 17 | 18 | private String address; 19 | 20 | private boolean event = false; 21 | 22 | private boolean oneWay = false; 23 | 24 | private boolean aync = false; 25 | 26 | private long timeout; 27 | 28 | private int actives; 29 | 30 | private String serialization; 31 | 32 | private String protocol; 33 | 34 | private String proxy; 35 | 36 | private String loadbalance; 37 | 38 | private String[] filters; 39 | 40 | private String registry; 41 | 42 | private Object ref; 43 | 44 | private int threads; 45 | 46 | private int queues; 47 | 48 | private String threadFactoryName; 49 | 50 | public boolean isEvent() { 51 | return event; 52 | } 53 | 54 | public void setEvent(boolean event) { 55 | this.event = event; 56 | } 57 | 58 | public boolean isOneWay() { 59 | return oneWay; 60 | } 61 | 62 | public URL setOneWay(boolean oneWay) { 63 | this.oneWay = oneWay; 64 | return this; 65 | } 66 | 67 | public boolean isAync() { 68 | return aync; 69 | } 70 | 71 | public URL setAync(boolean aync) { 72 | this.aync = aync; 73 | return this; 74 | } 75 | 76 | public long getTimeout() { 77 | return timeout; 78 | } 79 | 80 | public URL setTimeout(long timeout) { 81 | this.timeout = timeout; 82 | return this; 83 | } 84 | 85 | public int getActives() { 86 | return actives; 87 | } 88 | 89 | public URL setActives(int actives) { 90 | this.actives = actives; 91 | return this; 92 | } 93 | 94 | public String getSerialization() { 95 | return serialization; 96 | } 97 | 98 | public URL setSerialization(String serialization) { 99 | this.serialization = serialization; 100 | return this; 101 | } 102 | 103 | public String getProtocol() { 104 | return protocol; 105 | } 106 | 107 | public URL setProtocol(String protocol) { 108 | this.protocol = protocol; 109 | return this; 110 | } 111 | 112 | public String getProxy() { 113 | return proxy; 114 | } 115 | 116 | public URL setProxy(String proxy) { 117 | this.proxy = proxy; 118 | return this; 119 | } 120 | 121 | public String getLoadbalance() { 122 | return loadbalance; 123 | } 124 | 125 | public URL setLoadbalance(String loadbalance) { 126 | this.loadbalance = loadbalance; 127 | return this; 128 | } 129 | 130 | public String[] getFilters() { 131 | return filters; 132 | } 133 | 134 | public URL setFilters(String[] filters) { 135 | this.filters = filters; 136 | return this; 137 | } 138 | 139 | public String getInterfaceName() { 140 | return interfaceName; 141 | } 142 | 143 | public URL setInterfaceName(String interfaceName) { 144 | this.interfaceName = interfaceName; 145 | return this; 146 | } 147 | 148 | public int getWeight() { 149 | return weight; 150 | } 151 | 152 | public URL setWeight(int weight) { 153 | this.weight = weight; 154 | return this; 155 | } 156 | 157 | public String getIp() { 158 | return ip; 159 | } 160 | 161 | public URL setIp(String ip) { 162 | this.ip = ip; 163 | return this; 164 | } 165 | 166 | public int getPort() { 167 | return port; 168 | } 169 | 170 | public URL setPort(int port) { 171 | this.port = port; 172 | return this; 173 | } 174 | 175 | public String exposeURL() { 176 | return getAddress() + "&" + weight; 177 | } 178 | 179 | 180 | public URL setAddress(String address) { 181 | this.address = address; 182 | return this; 183 | } 184 | 185 | public String getAddress() { 186 | return address; 187 | } 188 | 189 | public String getRegistry() { 190 | return registry; 191 | } 192 | 193 | public URL setRegistry(String registry) { 194 | this.registry = registry; 195 | return this; 196 | } 197 | 198 | public Object getRef() { 199 | return ref; 200 | } 201 | 202 | public void setRef(Object ref) { 203 | this.ref = ref; 204 | } 205 | 206 | public int getThreads() { 207 | return threads; 208 | } 209 | 210 | public void setThreads(int threads) { 211 | this.threads = threads; 212 | } 213 | 214 | public int getQueues() { 215 | return queues; 216 | } 217 | 218 | public void setQueues(int queues) { 219 | this.queues = queues; 220 | } 221 | 222 | public String getThreadFactoryName() { 223 | return threadFactoryName; 224 | } 225 | 226 | public void setThreadFactoryName(String threadFactoryName) { 227 | this.threadFactoryName = threadFactoryName; 228 | } 229 | 230 | public String toIdentityString() { 231 | return address + "/" + interfaceName; 232 | } 233 | } 234 | -------------------------------------------------------------------------------- /tiny-rpc-core/src/main/java/com/example/tinyrpc/transport/server/NettyServer.java: -------------------------------------------------------------------------------- 1 | package com.example.tinyrpc.transport.server; 2 | 3 | import com.example.tinyrpc.codec.Decoder; 4 | import com.example.tinyrpc.codec.Encoder; 5 | import com.example.tinyrpc.common.domain.*; 6 | import com.example.tinyrpc.common.exception.BusinessException; 7 | import com.example.tinyrpc.config.ServiceConfig; 8 | import com.example.tinyrpc.protocol.Invoker; 9 | import com.example.tinyrpc.transport.AbstractEndpoint; 10 | import com.example.tinyrpc.transport.Server; 11 | import io.netty.bootstrap.ServerBootstrap; 12 | import io.netty.channel.*; 13 | import io.netty.channel.nio.NioEventLoopGroup; 14 | import io.netty.channel.socket.SocketChannel; 15 | import io.netty.channel.socket.nio.NioServerSocketChannel; 16 | import io.netty.handler.codec.LengthFieldBasedFrameDecoder; 17 | import io.netty.handler.timeout.IdleStateHandler; 18 | import org.slf4j.Logger; 19 | import org.slf4j.LoggerFactory; 20 | 21 | import java.util.concurrent.Future; 22 | 23 | import static com.example.tinyrpc.codec.Codec.*; 24 | /** 25 | * RPC的执行流程 26 | * 1)在项目启动的时候,进行服务的注册和发现。设计到的层次为Protocol、Registry 27 | * 28 | * 2)进入RPC调用的过程 29 | * 收到Client发来的请求or响应消息,根据序列化框架反序列化出消息体中的内容,用Invoker表示该内容。 30 | * 利用Proxy实现透明化的服务调用,将调用的结果回传给Client端 31 | * @auther zhongshunchao 32 | * @date 13/04/2020 12:54 33 | */ 34 | public class NettyServer extends AbstractEndpoint implements Server { 35 | 36 | private static final Logger logger = LoggerFactory.getLogger(NettyServer.class); 37 | 38 | private ServerBootstrap bootstrap; 39 | 40 | public NettyServer(String address) { 41 | super(address); 42 | this.bootstrap = new ServerBootstrap(); 43 | } 44 | 45 | @Override 46 | public void start() { 47 | executor.submit(() -> { 48 | EventLoopGroup bossGroup = new NioEventLoopGroup(); 49 | EventLoopGroup workGroup = new NioEventLoopGroup(); 50 | try { 51 | bootstrap.group(bossGroup, workGroup) 52 | .channel(NioServerSocketChannel.class) 53 | .childHandler(new ChannelInitializer() { 54 | @Override 55 | protected void initChannel(SocketChannel ch) { 56 | // 插入到ChannelHandlerContext这个双链表当中 57 | ch.pipeline() 58 | .addLast("IdleStateHandler", new IdleStateHandler(Constants.HEART_BEAT_TIME_OUT_MAX_TIME * Constants.HEART_BEAT_TIME_OUT, 0, 0)) 59 | .addLast(new Encoder()) 60 | .addLast("LengthFieldBasedFrameDecoder", new LengthFieldBasedFrameDecoder(MAX_FRAME_LENGTH, LENGTH_FIELD_OFFSET, LENGTH_FIELD_LENGTH, LENGTH_ADJUSTMENT, INITIAL_BYTES_TO_STRIP)) 61 | .addLast(new Decoder()) 62 | .addLast(new ServerHandler(NettyServer.this)); 63 | } 64 | }); 65 | String[] ipAndPort = address.trim().split(":"); 66 | ChannelFuture future = bootstrap.bind(ipAndPort[0], Integer.valueOf(ipAndPort[1])).sync(); 67 | channel = future.channel(); 68 | logger.info("Server successfully bind at : {}" + address); 69 | } catch (InterruptedException e) { 70 | logger.error("Server can not bind address:" + address); 71 | e.printStackTrace(); 72 | throw new BusinessException("Server can not bind address:" + address); 73 | } 74 | }); 75 | } 76 | 77 | @Override 78 | public Future send(Request request) { 79 | channel.writeAndFlush(request); 80 | return null; 81 | } 82 | 83 | @Override 84 | public void received(ChannelHandlerContext ctx, Object msg) { 85 | executor.submit(() -> { 86 | if (msg instanceof Request) { 87 | Request request = (Request) msg; 88 | //调用代理,通过反射的方式调用本地jvm中的方法 89 | long requestId = request.getRequestId(); 90 | Response response = new Response(); 91 | response.setRequestId(requestId); 92 | Invocation invocation = request.getData(); 93 | invocation.setSide(Constants.SERVER_SIDE); 94 | URL url = new URL(); 95 | url.setOneWay(request.isOneway()); 96 | invocation.setUrl(url); 97 | String className = invocation.getServiceName(); 98 | Invoker invoker = ServiceConfig.INVOKER_MAP.get(className); 99 | try { 100 | Object result = invoker.invoke(invocation); 101 | // oneway调用则直接返回 102 | if (invocation.getUrl().isOneWay()) { 103 | return; 104 | } 105 | ResponseBody responseBody = new ResponseBody(); 106 | responseBody.setResult(result); 107 | response.setResponseBody(responseBody); 108 | ctx.writeAndFlush(response); 109 | } catch (Exception e) { 110 | logger.error("HandleRequest error, exception:{}", e.getMessage()); 111 | e.printStackTrace(); 112 | throw new BusinessException("HandleRequest error, exception:" + e.getMessage()); 113 | } 114 | } 115 | }); 116 | } 117 | 118 | } 119 | -------------------------------------------------------------------------------- /tiny-rpc-core/src/main/java/com/example/tinyrpc/protocol/impl/InvokerClientWrapper.java: -------------------------------------------------------------------------------- 1 | package com.example.tinyrpc.protocol.impl; 2 | 3 | import com.alibaba.fastjson.JSON; 4 | import com.example.tinyrpc.cluster.LoadBalance; 5 | import com.example.tinyrpc.common.extension.ExtensionLoader; 6 | import com.example.tinyrpc.common.domain.Invocation; 7 | import com.example.tinyrpc.common.domain.URL; 8 | import com.example.tinyrpc.common.exception.BusinessException; 9 | import com.example.tinyrpc.protocol.Invoker; 10 | import com.example.tinyrpc.registry.Registry; 11 | import com.example.tinyrpc.transport.Client; 12 | import com.example.tinyrpc.transport.client.NettyClient; 13 | import org.slf4j.Logger; 14 | import org.slf4j.LoggerFactory; 15 | import org.springframework.util.CollectionUtils; 16 | 17 | import java.util.*; 18 | import java.util.concurrent.ConcurrentHashMap; 19 | 20 | /** 21 | * 该类对象被当作代理对象,替换掉被@Reference标识的类对象。该类包装了RealInvoker,实现了invoker之间的负载均衡 22 | * @auther zhongshunchao 23 | * @date 2020/5/21 11:16 上午 24 | */ 25 | public class InvokerClientWrapper implements Invoker { 26 | 27 | private static final Logger logger = LoggerFactory.getLogger(InvokerClientWrapper.class); 28 | 29 | private Invoker realInvoker; 30 | 31 | private Invocation invocation; 32 | 33 | /** 34 | * address -> Client(Invoker) :全局InvokerMap 35 | */ 36 | private final Map invokerMap = new ConcurrentHashMap<>(); 37 | 38 | private final Registry zkServiceRegistry; 39 | 40 | private final LoadBalance loadBalance; 41 | 42 | InvokerClientWrapper(Invocation invocation) { 43 | this.invocation = invocation; 44 | zkServiceRegistry = ExtensionLoader.getExtensionLoader().getExtension(Registry.class, invocation.getUrl().getRegistry()); 45 | loadBalance = ExtensionLoader.getExtensionLoader().getExtension(LoadBalance.class, invocation.getUrl().getLoadbalance()); 46 | init(); 47 | } 48 | 49 | /** 50 | * 初始化方法,每个对象只执行一次,从Zookeeper拉取address,封装成invokerMap。并在InvokerClientWrapper初始化的过程中进行负载均衡 51 | */ 52 | private void init() { 53 | String serviceName = this.invocation.getInterfaceClass().getName(); 54 | Set serviceUrlList = zkServiceRegistry.subscribe(serviceName, this::handleZkCallBack); 55 | for (String url : serviceUrlList) { 56 | createInvoker(url); 57 | } 58 | logger.info("###init()系统开始进行负载均衡..."); 59 | logger.info("###init()全局zk地址缓存invokerMap为{}", JSON.toJSONString(invokerMap)); 60 | this.realInvoker = loadBalance.select(new ArrayList<>(invokerMap.values())); 61 | logger.info("###init()此次被LoadBalancer选中的invoker 为 :{}", JSON.toJSONString(realInvoker)); 62 | } 63 | 64 | //这里之前犯过一个错误,不能异步删除和添加Client,防止子线程还没来的及删除该client,然后该client被select了,这样就会导致使用了无效的client 65 | private void handleZkCallBack(List addUrlList, Set closeUrlSet) { 66 | if (CollectionUtils.isEmpty(addUrlList) && CollectionUtils.isEmpty(closeUrlSet)) { 67 | return; 68 | } 69 | logger.info("接收到来自Zookeeper的CallBack, 需要添加的地址为 addUrlList : {}", addUrlList, " ; 需要关闭的url为 closeUrlSet :{}", closeUrlSet); 70 | 71 | // open Client 72 | for (String url : addUrlList) { 73 | createInvoker(url); 74 | } 75 | 76 | // close Client 77 | for (String url : closeUrlSet) { 78 | String[] splits = getSplitsFromUrlString(url); 79 | String address = splits[0]; 80 | Invoker invoker = invokerMap.get(address); 81 | if (invoker != null) { 82 | invoker.destroy(); 83 | invokerMap.remove(address); 84 | } 85 | } 86 | 87 | logger.info("###handleZkCallBack()系统开始进行负载均衡..."); 88 | logger.info("###handleZkCallBack()全局zk地址缓存invokerMap为{}", JSON.toJSONString(invokerMap)); 89 | this.realInvoker = loadBalance.select(new ArrayList<>(invokerMap.values())); 90 | logger.info("###handleZkCallBack()此次被LoadBalancer选中的invoker 为 :{}", JSON.toJSONString(realInvoker)); 91 | 92 | } 93 | 94 | private String[] getSplitsFromUrlString(String url) { 95 | String[] split = url.split("&"); 96 | if (split.length < 2) { 97 | logger.error("###UrlUtils: url parse failed, url : " + url + " is invalid"); 98 | throw new BusinessException("###UrlUtils: url parse failed, url : " + url + " is invalid"); 99 | } 100 | return split; 101 | } 102 | 103 | private void createInvoker(String url) { 104 | String[] splits = getSplitsFromUrlString(url); 105 | String address = splits[0]; 106 | int weight = Integer.valueOf(splits[1]); 107 | Client client = new NettyClient(address); 108 | client.start(); 109 | URL realUrl = new URL(); 110 | realUrl.setAddress(address); 111 | RealInvoker invoker = new RealInvoker(invocation.getInterfaceClass(), weight, realUrl); 112 | invoker.setEndpoint(client); 113 | invokerMap.put(address, invoker); 114 | } 115 | 116 | 117 | @Override 118 | public URL getUrl() { 119 | return realInvoker.getUrl(); 120 | } 121 | 122 | @Override 123 | public Class getInterface() { 124 | return invocation.getInterfaceClass(); 125 | } 126 | 127 | @Override 128 | public Object invoke(Invocation invocation) throws Exception { 129 | return this.realInvoker.invoke(invocation); 130 | } 131 | 132 | @Override 133 | public void destroy() { 134 | this.realInvoker.destroy(); 135 | } 136 | 137 | } 138 | -------------------------------------------------------------------------------- /tiny-rpc-core/src/main/java/com/example/tinyrpc/transport/client/NettyClient.java: -------------------------------------------------------------------------------- 1 | package com.example.tinyrpc.transport.client; 2 | 3 | import com.alibaba.fastjson.JSON; 4 | import com.example.tinyrpc.codec.Decoder; 5 | import com.example.tinyrpc.codec.Encoder; 6 | import com.example.tinyrpc.common.domain.Constants; 7 | import com.example.tinyrpc.common.domain.Request; 8 | import com.example.tinyrpc.common.domain.Response; 9 | import com.example.tinyrpc.common.domain.RpcContext; 10 | import com.example.tinyrpc.common.exception.BusinessException; 11 | import com.example.tinyrpc.common.utils.FutureContext; 12 | import com.example.tinyrpc.transport.AbstractEndpoint; 13 | import com.example.tinyrpc.transport.Client; 14 | import io.netty.bootstrap.Bootstrap; 15 | import io.netty.channel.ChannelFuture; 16 | import io.netty.channel.ChannelHandlerContext; 17 | import io.netty.channel.ChannelInitializer; 18 | import io.netty.channel.ChannelOption; 19 | import io.netty.channel.epoll.Epoll; 20 | import io.netty.channel.epoll.EpollSocketChannel; 21 | import io.netty.channel.nio.NioEventLoopGroup; 22 | import io.netty.channel.socket.SocketChannel; 23 | import io.netty.channel.socket.nio.NioSocketChannel; 24 | import io.netty.handler.codec.LengthFieldBasedFrameDecoder; 25 | import io.netty.handler.timeout.IdleStateHandler; 26 | import org.slf4j.Logger; 27 | import org.slf4j.LoggerFactory; 28 | 29 | import java.util.concurrent.CompletableFuture; 30 | import java.util.concurrent.Future; 31 | 32 | import static com.example.tinyrpc.codec.Codec.*; 33 | import static com.example.tinyrpc.common.domain.Response.SERVICE_ERROR; 34 | 35 | /** 36 | * @auther zhongshunchao 37 | * @date 13/04/2020 12:54 38 | */ 39 | public class NettyClient extends AbstractEndpoint implements Client { 40 | 41 | private static final Logger logger = LoggerFactory.getLogger(NettyClient.class); 42 | 43 | private static final NioEventLoopGroup NIO_EVENT_LOOP_GROUP = new NioEventLoopGroup(); 44 | 45 | private Bootstrap bootstrap; 46 | 47 | 48 | public NettyClient(String address) { 49 | super(address); 50 | this.bootstrap = new Bootstrap(); 51 | connect(); 52 | } 53 | 54 | public void connect() { 55 | executor.submit(() -> { 56 | String[] ipAndPort = address.trim().split(":"); 57 | ChannelFuture future = bootstrap.connect(ipAndPort[0], Integer.valueOf(ipAndPort[1])); 58 | channel = future.channel(); 59 | logger.info("Client CONNECTED at: {}", address); 60 | }); 61 | } 62 | 63 | @Override 64 | public void start() { 65 | bootstrap.group(NIO_EVENT_LOOP_GROUP) 66 | .channel(Epoll.isAvailable() ? EpollSocketChannel.class : NioSocketChannel.class) 67 | .handler(new ChannelInitializer() { 68 | @Override 69 | protected void initChannel(SocketChannel ch) { 70 | //Encoder: netty自带编码器,可以自动将长度加到头部 71 | ch.pipeline() 72 | .addLast("IdleStateHandler", new IdleStateHandler(Constants.HEART_BEAT_TIME_OUT_MAX_TIME * Constants.HEART_BEAT_TIME_OUT, 0, 0)) 73 | //Encoder: message to byte 74 | .addLast(new Encoder()) 75 | .addLast("LengthFieldBasedFrameDecoder", new LengthFieldBasedFrameDecoder(MAX_FRAME_LENGTH, LENGTH_FIELD_OFFSET, LENGTH_FIELD_LENGTH, LENGTH_ADJUSTMENT, INITIAL_BYTES_TO_STRIP)) 76 | .addLast(new Decoder()) 77 | //自定义处理逻辑,解析请求 78 | .addLast(new ClientHandler(NettyClient.this)); 79 | } 80 | }) 81 | .option(ChannelOption.SO_KEEPALIVE, true) 82 | .option(ChannelOption.TCP_NODELAY, true); 83 | logger.info("Client OPEN!"); 84 | } 85 | 86 | @Override 87 | public Future send(Request request) { 88 | CompletableFuture responseFuture = new CompletableFuture<>(); 89 | executor.submit(() -> { 90 | FutureContext.FUTURE_CACHE.putIfAbsent(request.getRequestId(), responseFuture); 91 | channel.writeAndFlush(request); 92 | }); 93 | return responseFuture; 94 | } 95 | 96 | @Override 97 | @SuppressWarnings("unchecked") 98 | public void received(ChannelHandlerContext ctx, Object msg) { 99 | executor.submit(() -> { 100 | // 延迟删除Attachments 101 | RpcContext.getContext().clearAttachments(); 102 | if (msg == null) { 103 | logger.error("msg is null"); 104 | throw new BusinessException("msg is null"); 105 | } 106 | if (msg instanceof Response) { 107 | Response response = (Response) msg; 108 | logger.info("客户端 ClientHandler 收到Response为:{}" + JSON.toJSONString(response)); 109 | //解析状态码,如果是500,则不要向上传递result了,直接抛出异常 110 | if (response.getStatus() == SERVICE_ERROR) { 111 | logger.error(response.getResponseBody().getErrorMsg()); 112 | throw new BusinessException(response.getResponseBody().getErrorMsg()); 113 | } 114 | long requestId = response.getRequestId(); 115 | CompletableFuture future = FutureContext.FUTURE_CACHE.remove(requestId); 116 | if (future == null) { 117 | throw new BusinessException("requestId错误,response没有对应的request相匹配"); 118 | } 119 | future.complete(response.getResponseBody().getResult()); 120 | } 121 | }); 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /tiny-rpc-core/src/main/java/com/example/tinyrpc/registry/impl/ZkServiceRegistry.java: -------------------------------------------------------------------------------- 1 | package com.example.tinyrpc.registry.impl; 2 | 3 | import com.example.tinyrpc.common.domain.URL; 4 | import com.example.tinyrpc.common.exception.BusinessException; 5 | import com.example.tinyrpc.registry.Registry; 6 | import com.example.tinyrpc.registry.UpdateAddressCallBack; 7 | import com.example.tinyrpc.registry.ZkSupport; 8 | import org.apache.zookeeper.CreateMode; 9 | import org.apache.zookeeper.KeeperException; 10 | import org.apache.zookeeper.WatchedEvent; 11 | import org.apache.zookeeper.Watcher; 12 | import org.slf4j.Logger; 13 | import org.slf4j.LoggerFactory; 14 | import java.util.*; 15 | import java.util.concurrent.ConcurrentHashMap; 16 | 17 | /** 18 | * dubbo中zookeeper的数据存储格式为/dubbo/serviceName/providers, configurators, consumer, routers 19 | * 这里参考Dubbo的做法,初始化的时候拉取全量的数据。之后每当注册中心的值变化时,更新之前缓存过的全量数据中的被改变的部分。 20 | * 本项目的Zookeeper目录格式为 : 21 | * |path | value | 22 | * |/TINY-RPC/com.example.tinyrpc.AService | 192.168.1.1:1221&100 | 23 | * |/TINY-RPC/com.example.tinyrpc.BService | 192.168.1.4:1221&200 | 24 | * |/TINY-RPC/com.example.tinyrpc.AService | 192.168.1.2:1221&300 | 25 | * @auther zhongshunchao 26 | * @date 20/06/2020 17:16 27 | */ 28 | public class ZkServiceRegistry implements Registry { 29 | 30 | private static final Logger logger = LoggerFactory.getLogger(ZkServiceRegistry.class); 31 | 32 | private static final String ZK_REGISTRY_PATH = "/TINY-RPC"; 33 | 34 | private static final ZkSupport ZK_SUPPORT = new ZkSupport(); 35 | 36 | /** 37 | * serviceName -> addressList 38 | * 本地缓存的Zookeeper的全量信息。无法连接Zookeeper时,可以从该缓存中获取地址。每次调用updateAddress()时被更新 39 | */ 40 | private static final Map> SERVICE_URL_MAP = new ConcurrentHashMap<>(); 41 | 42 | // 初始化时拉取全量的数据,dubbo把Zookeeper中所有服务的地址都拉取过来并持久化到硬盘,这里为了实现方便,不持久化到硬盘 43 | static { 44 | try { 45 | //检查是否有根目录 46 | if (ZK_SUPPORT.hasNoRoot(ZK_REGISTRY_PATH)) { 47 | ZK_SUPPORT.createPathIfAbsent(ZK_REGISTRY_PATH, CreateMode.PERSISTENT); 48 | } 49 | List allServiceNameList = ZK_SUPPORT.getChildren(ZK_REGISTRY_PATH, false); 50 | if (allServiceNameList.isEmpty()) { 51 | logger.warn("There is no Node under root path:{}", ZK_REGISTRY_PATH); 52 | } else { 53 | for (String serviceName : allServiceNameList) { 54 | List addressList = ZK_SUPPORT.getChildren(getPath(serviceName), false); 55 | SERVICE_URL_MAP.put(serviceName, new HashSet<>(addressList)); 56 | } 57 | } 58 | } catch (KeeperException | InterruptedException e) { 59 | logger.error("Error when initializing SERVICE_URL_MAP, exception:", e.getMessage()); 60 | } 61 | } 62 | 63 | /** 64 | * 初始化之后,采用订阅-通知模式进行部分数据同步 65 | * @param interfaceName 被代理的接口名 66 | * @param updateAddressCallBack 回调函数,用于更新缓存中的zk地址 67 | */ 68 | @Override 69 | public Set subscribe(String interfaceName, UpdateAddressCallBack updateAddressCallBack) { 70 | try { 71 | //在与Zookeeper非失联的情况下,从Zookeeper拉取最新的数据。如果在客户端已经启动的情况下,处于RPC通信状态,此时Zookeeper的数据变化,那么会通过Watcher回调来更新缓存中的地址 72 | List newUrlList = ZK_SUPPORT.getChildren(getPath(interfaceName), new Watcher() { 73 | @Override 74 | public void process(WatchedEvent event) { 75 | if (event.getType() == Event.EventType.NodeChildrenChanged) { 76 | subscribe(interfaceName, updateAddressCallBack); 77 | } 78 | } 79 | }); 80 | if (newUrlList == null) { 81 | logger.error("Fail to find interfaceName in Zookeeper:" + interfaceName); 82 | throw new BusinessException("Fail to find interfaceName in Zookeeper:" + interfaceName); 83 | } 84 | return updateAddress(interfaceName, newUrlList, updateAddressCallBack); 85 | } catch (KeeperException | InterruptedException e) { 86 | logger.info("更新服务地址缓存失败"); 87 | //如果和Zookeeper断线失联,则从缓存中获取 88 | return SERVICE_URL_MAP.get(interfaceName); 89 | } 90 | } 91 | 92 | private Set updateAddress(String interfaceName, List newUrlList, UpdateAddressCallBack updateAddressCallBack) { 93 | Set oldUrlSet = SERVICE_URL_MAP.get(interfaceName); 94 | if (oldUrlSet == null) { 95 | Set newUrlSet = new HashSet<>(newUrlList); 96 | synchronized (this) { 97 | oldUrlSet = SERVICE_URL_MAP.get(interfaceName); 98 | if (oldUrlSet == null) { 99 | SERVICE_URL_MAP.put(interfaceName, newUrlSet); 100 | return newUrlSet; 101 | } 102 | } 103 | } 104 | Set newAddressSet = new HashSet<>(); 105 | //与最新的地址进行比较,如果有多出来的地址,则关闭对应的连接,如果少了,则新增对应的连接 106 | Iterator iterator = newUrlList.iterator(); 107 | while (iterator.hasNext()) { 108 | String address = iterator.next(); 109 | //两个集合都有,则保留 110 | if (oldUrlSet.contains(address)) { 111 | oldUrlSet.remove(address); 112 | iterator.remove(); 113 | newAddressSet.add(address); 114 | } 115 | } 116 | //开启之后将它们加入到cache中 117 | newAddressSet.addAll(newUrlList); 118 | SERVICE_URL_MAP.put(interfaceName, newAddressSet); 119 | //此时oldUrlSet为需要被关闭的连接,newUrlList为新增的连接,分别对这两种情况关闭和建立连接,用线程池处理 120 | //这里用lambada表达式做回调 121 | updateAddressCallBack.updateAddress(newUrlList, oldUrlSet); 122 | return newAddressSet; 123 | } 124 | 125 | @Override 126 | public void register(URL url) { 127 | String path = getPath(url.getInterfaceName()); 128 | String data = url.exposeURL(); 129 | //先创建目录然后创建目录下的值 130 | try { 131 | ZK_SUPPORT.createPathIfAbsent(path, CreateMode.PERSISTENT); 132 | } catch (KeeperException | InterruptedException e) { 133 | logger.error("Fail to create Zookeeper path" + path); 134 | e.printStackTrace(); 135 | throw new BusinessException("Fail to create Zookeeper path" + path); 136 | } 137 | ZK_SUPPORT.createNodeIfAbsent(data, path); 138 | } 139 | 140 | private static String getPath(String path) { 141 | return ZK_REGISTRY_PATH + "/" + path; 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /tiny-rpc-springboot-stater/src/main/resources/logback-spring.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | logback-spring 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | debug 26 | 27 | 28 | ${CONSOLE_LOG_PATTERN} 29 | 30 | UTF-8 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | ${logging.path}/web_debug.log 39 | 40 | 41 | %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n 42 | UTF-8 43 | 44 | 45 | 46 | 47 | ${logging.path}/web-debug-%d{yyyy-MM-dd}.%i.log 48 | 49 | 100MB 50 | 51 | 52 | 15 53 | 54 | 55 | 56 | debug 57 | ACCEPT 58 | DENY 59 | 60 | 61 | 62 | 63 | 64 | 65 | ${logging.path}/web_info.log 66 | 67 | 68 | %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n 69 | UTF-8 70 | 71 | 72 | 73 | 74 | ${logging.path}/web-info-%d{yyyy-MM-dd}.%i.log 75 | 76 | 100MB 77 | 78 | 79 | 15 80 | 81 | 82 | 83 | info 84 | ACCEPT 85 | DENY 86 | 87 | 88 | 89 | 90 | 91 | 92 | ${logging.path}/tracer.log 93 | 94 | 95 | %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n 96 | UTF-8 97 | 98 | 99 | 100 | ${logging.path}/web-warn-%d{yyyy-MM-dd}.%i.log 101 | 102 | 100MB 103 | 104 | 105 | 15 106 | 107 | 108 | 109 | warn 110 | ACCEPT 111 | DENY 112 | 113 | 114 | 115 | 116 | 117 | 118 | ${logging.path}/web_error.log 119 | 120 | 121 | %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n 122 | UTF-8 123 | 124 | 125 | 126 | ${logging.path}/web-error-%d{yyyy-MM-dd}.%i.log 127 | 128 | 100MB 129 | 130 | 131 | 15 132 | 133 | 134 | 135 | ERROR 136 | ACCEPT 137 | DENY 138 | 139 | 140 | 141 | 153 | 154 | 160 | 161 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | -------------------------------------------------------------------------------- /tiny-rpc-core/src/main/java/com/example/tinyrpc/common/extension/ExtensionLoader.java: -------------------------------------------------------------------------------- 1 | package com.example.tinyrpc.common.extension; 2 | 3 | import com.example.tinyrpc.common.domain.Constants; 4 | import com.example.tinyrpc.common.exception.BusinessException; 5 | import com.example.tinyrpc.filter.Filter; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | import org.springframework.scheduling.concurrent.CustomizableThreadFactory; 9 | import org.springframework.util.StringUtils; 10 | 11 | import java.io.File; 12 | import java.io.FileInputStream; 13 | import java.io.InputStream; 14 | import java.util.*; 15 | import java.util.concurrent.*; 16 | 17 | 18 | /** 19 | * @auther zhongshunchao 20 | * @date 27/06/2020 18:07 21 | * dubbo中的@SPI注解:可以认为是定义默认实现类。如果在@Reference中没有定义,则使用@SPI注解中定义的value。 22 | * 该类在BeanPostProcessor中首次生成,因此会在Spring IOC 容器初始化时加载扩展点而不是真正的请求调用阶段 23 | * 先把用户自定义的类全部先加载到内存缓存,如果再缓存中找不到,再使用延迟加载策略加载系统内部定义的加载器。(注意:项目的路径不能包含中文名) 24 | */ 25 | public class ExtensionLoader { 26 | 27 | private static final Logger logger = LoggerFactory.getLogger(ExtensionLoader.class); 28 | 29 | // alias -> Class 30 | private static final Map EXTERNAL_ALIAS_CLASS_MAP = new HashMap<>(); 31 | 32 | // Class -> Instance 33 | private static final Map INSTANCE_MAP = new ConcurrentHashMap<>(); 34 | 35 | private static List defaultClientFilterList = new ArrayList<>(); 36 | 37 | private static List defaultServerFilterList = new ArrayList<>(); 38 | 39 | private static final String [] CLIENT_SIDE_FILTERS = {"consumer-context-filter", "active-limit-filter", "trace-filter"}; 40 | 41 | private static final String [] SERVER_SIDE_FILTERS = {"context-filter", "trace-filter"}; 42 | 43 | private static volatile ExtensionLoader instance; 44 | 45 | public static ExtensionLoader getExtensionLoader() { 46 | if (instance == null) { 47 | synchronized (ExtensionLoader.class) { 48 | if (instance == null) { 49 | instance = new ExtensionLoader(); 50 | } 51 | } 52 | } 53 | return instance; 54 | } 55 | 56 | // 这里的corePoolSize和maxmumPoolSize是按经验公式设置的。队列长度如果设置过长,会导致调用超时;如果设置过短,会导致大量请求被拒绝。 57 | private static final ExecutorService DEFAULT_EXECUTOR_SERVICE = new ThreadPoolExecutor(8, 16, 60, TimeUnit.SECONDS, new ArrayBlockingQueue<>(100) 58 | , new CustomizableThreadFactory("default-business-thread-"), new ThreadPoolExecutor.AbortPolicy()); 59 | 60 | // dubbo 源码中采用策略模式获取不同的目录 61 | private ExtensionLoader() { 62 | loadExternalDirectory(this.getClass().getClassLoader().getResource(Constants.EXTERNAL_PATH)); 63 | logger.info("Current ALIAS_CLASS_MAP:{}, INSTANCE_MAP:{}", EXTERNAL_ALIAS_CLASS_MAP, INSTANCE_MAP); 64 | buidDefaultFilterChain(); 65 | } 66 | 67 | private void loadExternalDirectory(java.net.URL parent) { 68 | if (parent != null) { 69 | logger.info("start reading Extension Files, under path:" + Constants.EXTERNAL_PATH); 70 | File dir = new File(parent.getFile()); 71 | File[] files = dir.listFiles(); 72 | if (files != null) { 73 | for (File file : files) { 74 | handleFile(file); 75 | } 76 | logger.info("Extension configuration file read complete"); 77 | } 78 | } 79 | } 80 | 81 | private void handleFile(File file) { 82 | logger.info("start reading file:{}", file); 83 | Properties prop = new Properties(); 84 | String interfaceName = file.getName(); 85 | try { 86 | InputStream input = new FileInputStream(file); 87 | prop.load(input); 88 | for (String key : prop.stringPropertyNames()) { 89 | String implName = prop.getProperty(key); 90 | Class interfaceClass = Class.forName(interfaceName); 91 | //检查是否是该接口的实现类 92 | Class impl = checkImpl(interfaceClass, implName); 93 | EXTERNAL_ALIAS_CLASS_MAP.putIfAbsent(key, impl); 94 | } 95 | } catch (Exception e) { 96 | logger.error("Fail to load file,filename->" + interfaceName + ", exception:" + e.getMessage()); 97 | } 98 | } 99 | 100 | private Class checkImpl(Class interfaceClass, String implClassName) throws ClassNotFoundException { 101 | Class impl = Class.forName(implClassName); 102 | if (!interfaceClass.isAssignableFrom(impl)) { 103 | logger.error("实现类{}不是该接口{}的子类", impl, interfaceClass); 104 | throw new IllegalStateException("实现类{}不是该接口{}的子类"); 105 | } 106 | return impl; 107 | } 108 | 109 | //如果map里没有,则通过反射生成 110 | @SuppressWarnings("unchecked") 111 | public T getExtension(Class type, String alias) { 112 | if (INSTANCE_MAP.containsKey(type)) { 113 | return (T) INSTANCE_MAP.get(type); 114 | } else if (!StringUtils.isEmpty(alias) && EXTERNAL_ALIAS_CLASS_MAP.containsKey(alias)){ 115 | //从map中取出class反射出instance 116 | Class clazz = EXTERNAL_ALIAS_CLASS_MAP.get(alias); 117 | if (clazz == null) { 118 | logger.error("Fail to get Extension. alias=" + alias); 119 | throw new BusinessException("Fail to get Extension. alias=" + alias); 120 | } 121 | Object instance = INSTANCE_MAP.get(clazz); 122 | if (instance == null) { 123 | synchronized (ExtensionLoader.class) { 124 | instance = INSTANCE_MAP.get(clazz); 125 | if (instance == null) { 126 | try { 127 | instance = clazz.newInstance(); 128 | INSTANCE_MAP.put(clazz, instance); 129 | } catch (Exception e) { 130 | logger.error("Fail to instantiate class " + clazz.getTypeName(), e); 131 | } 132 | } 133 | } 134 | } 135 | return (T) INSTANCE_MAP.get(clazz); 136 | } else { 137 | return loadInternalExtension(type); 138 | } 139 | } 140 | 141 | public T getDefaultExtension(Class type) { 142 | return getExtension(type, null); 143 | } 144 | 145 | @SuppressWarnings("unchecked") 146 | private T loadInternalExtension(Class type) { 147 | // 获取默认的扩展点别名 148 | String alias = getSPIValue(type); 149 | // 加载默认扩展点 150 | InputStream resourceAsStream = ExtensionLoader.class.getResourceAsStream(Constants.INTERNAL_PATH + type.getTypeName()); 151 | Properties prop = new Properties(); 152 | try { 153 | prop.load(resourceAsStream); 154 | for (String key : prop.stringPropertyNames()) { 155 | if (key.equals(alias)) { 156 | String impl = prop.getProperty(key); 157 | Class clazz = Class.forName(impl); 158 | Object instance = clazz.newInstance(); 159 | INSTANCE_MAP.put(clazz, instance); 160 | return (T) instance; 161 | } 162 | } 163 | } catch (Exception e) { 164 | logger.error("Fail to instantiate class " + type.getTypeName(), ".exception:", e); 165 | } 166 | return null; 167 | } 168 | private String getSPIValue(Class type) { 169 | if (type == null) { 170 | throw new IllegalArgumentException("Extension type == null"); 171 | } 172 | if (!type.isInterface()) { 173 | throw new IllegalArgumentException("Extension type (" + type + ") is not an interface!"); 174 | } 175 | if (!type.isAnnotationPresent(SPI.class)) { 176 | throw new IllegalArgumentException("Extension type (" + type + 177 | ") is not an extension, because it is NOT annotated with @" + SPI.class.getSimpleName() + "!"); 178 | } 179 | final SPI defaultAnnotation = type.getAnnotation(SPI.class); 180 | String value = defaultAnnotation.value(); 181 | if ((value.trim()).length() <= 0) { 182 | throw new IllegalArgumentException("Default Extension SPI value == null"); 183 | } 184 | return value; 185 | } 186 | 187 | private void loadInternalFilterExtension(String alias, int side) { 188 | // 加载默认扩展点 189 | InputStream resourceAsStream = ExtensionLoader.class.getResourceAsStream(Constants.INTERNAL_PATH + Filter.class.getTypeName()); 190 | Properties prop = new Properties(); 191 | try { 192 | prop.load(resourceAsStream); 193 | for (String key : prop.stringPropertyNames()) { 194 | if (key.equals(alias)) { 195 | String impl = prop.getProperty(key); 196 | Class clazz = Class.forName(impl); 197 | Filter instance = (Filter) clazz.newInstance(); 198 | if (side == Constants.CLIENT_SIDE) { 199 | defaultClientFilterList.add(instance); 200 | } else { 201 | defaultServerFilterList.add(instance); 202 | } 203 | } 204 | } 205 | } catch (Exception e) { 206 | logger.error("Fail to instantiate Internal Filter Extension", e); 207 | } 208 | } 209 | 210 | private void buidDefaultFilterChain() { 211 | for (String filter : CLIENT_SIDE_FILTERS) { 212 | loadInternalFilterExtension(filter, Constants.CLIENT_SIDE); 213 | } 214 | for (String filter : SERVER_SIDE_FILTERS) { 215 | loadInternalFilterExtension(filter, Constants.SERVER_SIDE); 216 | } 217 | } 218 | public List buidFilterChain(String[] filters, int side) { 219 | List filterList = new ArrayList<>(); 220 | // 加载默认的Filter Chain 221 | if (side == Constants.CLIENT_SIDE) { 222 | filterList.addAll(defaultClientFilterList); 223 | } else { 224 | filterList.addAll(defaultServerFilterList); 225 | } 226 | // 加载用户自定义的Filter Chain 227 | if (filters != null) { 228 | List newFilterList = new ArrayList<>(); 229 | for (String filter : filters) { 230 | Filter newFilter = getExtension(Filter.class, filter); 231 | newFilterList.add(newFilter); 232 | } 233 | filterList.addAll(newFilterList); 234 | } 235 | logger.info("###global filterList: " + filterList); 236 | return filterList; 237 | } 238 | 239 | public ExecutorService getDefaultExecutor() { 240 | return DEFAULT_EXECUTOR_SERVICE; 241 | } 242 | } 243 | --------------------------------------------------------------------------------