├── src ├── main │ ├── resources │ │ ├── rpc.properties │ │ ├── log4j.properties │ │ └── spring.xml │ └── java │ │ └── com │ │ └── nettyrpc │ │ ├── client │ │ ├── AsyncRPCCallback.java │ │ ├── proxy │ │ │ ├── IAsyncObjectProxy.java │ │ │ └── ObjectProxy.java │ │ ├── RpcClientInitializer.java │ │ ├── RpcClient.java │ │ ├── RpcClientHandler.java │ │ ├── RPCFuture.java │ │ └── ConnectManage.java │ │ ├── registry │ │ ├── Constant.java │ │ ├── ServiceRegistry.java │ │ └── ServiceDiscovery.java │ │ ├── server │ │ ├── RpcService.java │ │ ├── RpcHandler.java │ │ └── RpcServer.java │ │ └── protocol │ │ ├── RpcResponse.java │ │ ├── RpcEncoder.java │ │ ├── RpcDecoder.java │ │ ├── RpcRequest.java │ │ ├── SerializationUtil.java │ │ └── JsonUtil.java └── test │ ├── resources │ ├── rpc.properties │ ├── log4j.properties │ ├── client-spring.xml │ └── server-spring.xml │ └── java │ └── com │ └── nettyrpc │ └── test │ ├── client │ ├── HelloService.java │ ├── HelloPersonService.java │ └── Person.java │ ├── server │ ├── RpcBootstrap.java │ ├── HelloServiceImpl.java │ └── HelloPersonServiceImpl.java │ ├── app │ ├── Benchmark.java │ ├── HelloPersonCallbackTest.java │ ├── BenchmarkAsync.java │ └── HelloServiceTest.java │ └── util │ └── JsonTest.java ├── .gitignore ├── README.md ├── pom.xml └── NettyRpc.iml /src/main/resources/rpc.properties: -------------------------------------------------------------------------------- 1 | # zookeeper server 2 | registry.address=127.0.0.1:2181 3 | # rpc server 4 | server.address=127.0.0.1:18866 -------------------------------------------------------------------------------- /src/test/resources/rpc.properties: -------------------------------------------------------------------------------- 1 | # zookeeper server 2 | registry.address=127.0.0.1:2181 3 | # rpc server 4 | server.address=127.0.0.1:18866 -------------------------------------------------------------------------------- /src/test/java/com/nettyrpc/test/client/HelloService.java: -------------------------------------------------------------------------------- 1 | package com.nettyrpc.test.client; 2 | 3 | public interface HelloService { 4 | 5 | String hello(String name); 6 | 7 | String hello(Person person); 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/com/nettyrpc/client/AsyncRPCCallback.java: -------------------------------------------------------------------------------- 1 | package com.nettyrpc.client; 2 | 3 | /** 4 | * Created by luxiaoxun on 2016-03-17. 5 | */ 6 | public interface AsyncRPCCallback { 7 | 8 | void success(Object result); 9 | 10 | void fail(Exception e); 11 | 12 | } 13 | -------------------------------------------------------------------------------- /src/test/java/com/nettyrpc/test/client/HelloPersonService.java: -------------------------------------------------------------------------------- 1 | package com.nettyrpc.test.client; 2 | 3 | import java.util.List; 4 | 5 | /** 6 | * Created by luxiaoxun on 2016-03-10. 7 | */ 8 | public interface HelloPersonService { 9 | List GetTestPerson(String name,int num); 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/com/nettyrpc/client/proxy/IAsyncObjectProxy.java: -------------------------------------------------------------------------------- 1 | package com.nettyrpc.client.proxy; 2 | 3 | import com.nettyrpc.client.RPCFuture; 4 | 5 | /** 6 | * Created by luxiaoxun on 2016/3/16. 7 | */ 8 | public interface IAsyncObjectProxy { 9 | public RPCFuture call(String funcName, Object... args); 10 | } -------------------------------------------------------------------------------- /src/test/java/com/nettyrpc/test/server/RpcBootstrap.java: -------------------------------------------------------------------------------- 1 | package com.nettyrpc.test.server; 2 | 3 | import org.springframework.context.support.ClassPathXmlApplicationContext; 4 | 5 | public class RpcBootstrap { 6 | 7 | public static void main(String[] args) { 8 | new ClassPathXmlApplicationContext("server-spring.xml"); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/com/nettyrpc/registry/Constant.java: -------------------------------------------------------------------------------- 1 | package com.nettyrpc.registry; 2 | 3 | /** 4 | * ZooKeeper constant 5 | * 6 | * @author huangyong 7 | */ 8 | public interface Constant { 9 | 10 | int ZK_SESSION_TIMEOUT = 5000; 11 | 12 | String ZK_REGISTRY_PATH = "/registry"; 13 | String ZK_DATA_PATH = ZK_REGISTRY_PATH + "/data"; 14 | } 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | Thumbs.db 2 | *.obj 3 | *.exe 4 | *.pdb 5 | *.user 6 | *.aps 7 | *.pch 8 | *.vspscc 9 | *_i.c 10 | *_p.c 11 | *.ncb 12 | *.suo 13 | *.sln.docstates 14 | *.tlb 15 | *.tlh 16 | *.bak 17 | *.cache 18 | *.ilk 19 | *.log 20 | [Bb]in 21 | [Dd]ebug*/ 22 | *.lib 23 | *.sbr 24 | obj/ 25 | [Rr]elease*/ 26 | _ReSharper*/ 27 | [Tt]est[Rr]esult* 28 | *.vssscc 29 | $tf*/ 30 | logs/ 31 | out/ 32 | target/ 33 | .idea/ -------------------------------------------------------------------------------- /src/main/java/com/nettyrpc/server/RpcService.java: -------------------------------------------------------------------------------- 1 | package com.nettyrpc.server; 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 | import org.springframework.stereotype.Component; 8 | 9 | /** 10 | * RPC annotation for RPC service 11 | * 12 | * @author huangyong 13 | */ 14 | @Target({ElementType.TYPE}) 15 | @Retention(RetentionPolicy.RUNTIME) 16 | @Component 17 | public @interface RpcService { 18 | Class value(); 19 | } 20 | -------------------------------------------------------------------------------- /src/test/java/com/nettyrpc/test/server/HelloServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.nettyrpc.test.server; 2 | 3 | import com.nettyrpc.test.client.HelloService; 4 | import com.nettyrpc.test.client.Person; 5 | import com.nettyrpc.server.RpcService; 6 | 7 | @RpcService(HelloService.class) 8 | public class HelloServiceImpl implements HelloService { 9 | 10 | @Override 11 | public String hello(String name) { 12 | return "Hello! " + name; 13 | } 14 | 15 | @Override 16 | public String hello(Person person) { 17 | return "Hello! " + person.getFirstName() + " " + person.getLastName(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/resources/log4j.properties: -------------------------------------------------------------------------------- 1 | log4j.rootLogger=Debug,console,file 2 | 3 | log4j.appender.console=org.apache.log4j.ConsoleAppender 4 | log4j.appender.console.target=System.out 5 | log4j.appender.console.layout=org.apache.log4j.PatternLayout 6 | log4j.appender.console.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n 7 | 8 | log4j.appender.file = org.apache.log4j.DailyRollingFileAppender 9 | log4j.appender.file.File = logs/log.log 10 | log4j.appender.file.Append = true 11 | log4j.appender.file.Threshold = Error 12 | log4j.appender.file.layout = org.apache.log4j.PatternLayout 13 | log4j.appender.file.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} %5p %c{1}:%L - %m%n -------------------------------------------------------------------------------- /src/test/resources/log4j.properties: -------------------------------------------------------------------------------- 1 | log4j.rootLogger=Info,console,file 2 | 3 | log4j.appender.console=org.apache.log4j.ConsoleAppender 4 | log4j.appender.console.target=System.out 5 | log4j.appender.console.layout=org.apache.log4j.PatternLayout 6 | log4j.appender.console.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n 7 | 8 | log4j.appender.file = org.apache.log4j.DailyRollingFileAppender 9 | log4j.appender.file.File = logs/log.log 10 | log4j.appender.file.Append = true 11 | log4j.appender.file.Threshold = Error 12 | log4j.appender.file.layout = org.apache.log4j.PatternLayout 13 | log4j.appender.file.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} %5p %c{1}:%L - %m%n -------------------------------------------------------------------------------- /src/test/java/com/nettyrpc/test/server/HelloPersonServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.nettyrpc.test.server; 2 | 3 | import com.nettyrpc.server.RpcService; 4 | import com.nettyrpc.test.client.HelloPersonService; 5 | import com.nettyrpc.test.client.Person; 6 | 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | 10 | /** 11 | * Created by luxiaoxun on 2016-03-10. 12 | */ 13 | @RpcService(HelloPersonService.class) 14 | public class HelloPersonServiceImpl implements HelloPersonService { 15 | 16 | @Override 17 | public List GetTestPerson(String name, int num) { 18 | List persons = new ArrayList<>(num); 19 | for (int i = 0; i < num; ++i) { 20 | persons.add(new Person(Integer.toString(i), name)); 21 | } 22 | return persons; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/nettyrpc/protocol/RpcResponse.java: -------------------------------------------------------------------------------- 1 | package com.nettyrpc.protocol; 2 | 3 | /** 4 | * RPC Response 5 | * @author huangyong 6 | */ 7 | public class RpcResponse { 8 | 9 | private String requestId; 10 | private String error; 11 | private Object result; 12 | 13 | public boolean isError() { 14 | return error != null; 15 | } 16 | 17 | public String getRequestId() { 18 | return requestId; 19 | } 20 | 21 | public void setRequestId(String requestId) { 22 | this.requestId = requestId; 23 | } 24 | 25 | public String getError() { 26 | return error; 27 | } 28 | 29 | public void setError(String error) { 30 | this.error = error; 31 | } 32 | 33 | public Object getResult() { 34 | return result; 35 | } 36 | 37 | public void setResult(Object result) { 38 | this.result = result; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/com/nettyrpc/protocol/RpcEncoder.java: -------------------------------------------------------------------------------- 1 | package com.nettyrpc.protocol; 2 | 3 | import io.netty.buffer.ByteBuf; 4 | import io.netty.channel.ChannelHandlerContext; 5 | import io.netty.handler.codec.MessageToByteEncoder; 6 | 7 | /** 8 | * RPC Encoder 9 | * @author huangyong 10 | */ 11 | public class RpcEncoder extends MessageToByteEncoder { 12 | 13 | private Class genericClass; 14 | 15 | public RpcEncoder(Class genericClass) { 16 | this.genericClass = genericClass; 17 | } 18 | 19 | @Override 20 | public void encode(ChannelHandlerContext ctx, Object in, ByteBuf out) throws Exception { 21 | if (genericClass.isInstance(in)) { 22 | byte[] data = SerializationUtil.serialize(in); 23 | //byte[] data = JsonUtil.serialize(in); // Not use this, have some bugs 24 | out.writeInt(data.length); 25 | out.writeBytes(data); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/test/resources/client-spring.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/main/java/com/nettyrpc/client/RpcClientInitializer.java: -------------------------------------------------------------------------------- 1 | package com.nettyrpc.client; 2 | 3 | import com.nettyrpc.protocol.RpcDecoder; 4 | import com.nettyrpc.protocol.RpcEncoder; 5 | import com.nettyrpc.protocol.RpcRequest; 6 | import com.nettyrpc.protocol.RpcResponse; 7 | import io.netty.channel.ChannelInitializer; 8 | import io.netty.channel.ChannelPipeline; 9 | import io.netty.channel.socket.SocketChannel; 10 | import io.netty.handler.codec.LengthFieldBasedFrameDecoder; 11 | 12 | /** 13 | * Created by luxiaoxun on 2016-03-16. 14 | */ 15 | public class RpcClientInitializer extends ChannelInitializer { 16 | @Override 17 | protected void initChannel(SocketChannel socketChannel) throws Exception { 18 | ChannelPipeline cp = socketChannel.pipeline(); 19 | cp.addLast(new RpcEncoder(RpcRequest.class)); 20 | cp.addLast(new LengthFieldBasedFrameDecoder(65536, 0, 4, 0, 0)); 21 | cp.addLast(new RpcDecoder(RpcResponse.class)); 22 | cp.addLast(new RpcClientHandler()); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/test/resources/server-spring.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/test/java/com/nettyrpc/test/client/Person.java: -------------------------------------------------------------------------------- 1 | package com.nettyrpc.test.client; 2 | 3 | public class Person { 4 | 5 | private String firstName; 6 | private String lastName; 7 | 8 | public Person() { 9 | } 10 | 11 | public Person(String firstName, String lastName) { 12 | this.firstName = firstName; 13 | this.lastName = lastName; 14 | } 15 | 16 | public String getFirstName() { 17 | return firstName; 18 | } 19 | 20 | public void setFirstName(String firstName) { 21 | this.firstName = firstName; 22 | } 23 | 24 | public String getLastName() { 25 | return lastName; 26 | } 27 | 28 | public void setLastName(String lastName) { 29 | this.lastName = lastName; 30 | } 31 | 32 | @Override 33 | public String toString(){ 34 | return firstName + " " + lastName; 35 | } 36 | 37 | @Override 38 | public int hashCode() { 39 | return this.firstName.hashCode()^this.lastName.hashCode(); 40 | } 41 | 42 | @Override 43 | public boolean equals(Object obj) { 44 | if(!(obj instanceof Person) ) return false; 45 | Person p = (Person)obj; 46 | return this.firstName.equals(p.firstName) && this.lastName.equals(p.lastName); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/com/nettyrpc/protocol/RpcDecoder.java: -------------------------------------------------------------------------------- 1 | package com.nettyrpc.protocol; 2 | 3 | import io.netty.buffer.ByteBuf; 4 | import io.netty.channel.ChannelHandlerContext; 5 | import io.netty.handler.codec.ByteToMessageDecoder; 6 | import java.util.List; 7 | 8 | /** 9 | * RPC Decoder 10 | * @author huangyong 11 | */ 12 | public class RpcDecoder extends ByteToMessageDecoder { 13 | 14 | private Class genericClass; 15 | 16 | public RpcDecoder(Class genericClass) { 17 | this.genericClass = genericClass; 18 | } 19 | 20 | @Override 21 | public final void decode(ChannelHandlerContext ctx, ByteBuf in, List out) throws Exception { 22 | if (in.readableBytes() < 4) { 23 | return; 24 | } 25 | in.markReaderIndex(); 26 | int dataLength = in.readInt(); 27 | /*if (dataLength <= 0) { 28 | ctx.close(); 29 | }*/ 30 | if (in.readableBytes() < dataLength) { 31 | in.resetReaderIndex(); 32 | return; 33 | } 34 | byte[] data = new byte[dataLength]; 35 | in.readBytes(data); 36 | 37 | Object obj = SerializationUtil.deserialize(data, genericClass); 38 | //Object obj = JsonUtil.deserialize(data,genericClass); // Not use this, have some bugs 39 | out.add(obj); 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/com/nettyrpc/protocol/RpcRequest.java: -------------------------------------------------------------------------------- 1 | package com.nettyrpc.protocol; 2 | 3 | /** 4 | * RPC Request 5 | * @author huangyong 6 | */ 7 | public class RpcRequest { 8 | 9 | private String requestId; 10 | private String className; 11 | private String methodName; 12 | private Class[] parameterTypes; 13 | private Object[] parameters; 14 | 15 | public String getRequestId() { 16 | return requestId; 17 | } 18 | 19 | public void setRequestId(String requestId) { 20 | this.requestId = requestId; 21 | } 22 | 23 | public String getClassName() { 24 | return className; 25 | } 26 | 27 | public void setClassName(String className) { 28 | this.className = className; 29 | } 30 | 31 | public String getMethodName() { 32 | return methodName; 33 | } 34 | 35 | public void setMethodName(String methodName) { 36 | this.methodName = methodName; 37 | } 38 | 39 | public Class[] getParameterTypes() { 40 | return parameterTypes; 41 | } 42 | 43 | public void setParameterTypes(Class[] parameterTypes) { 44 | this.parameterTypes = parameterTypes; 45 | } 46 | 47 | public Object[] getParameters() { 48 | return parameters; 49 | } 50 | 51 | public void setParameters(Object[] parameters) { 52 | this.parameters = parameters; 53 | } 54 | } -------------------------------------------------------------------------------- /src/main/resources/spring.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NettyRpc 2 | An RPC framework based on Netty, ZooKeeper and Spring 3 | 中文详情:[Chinese Details](http://www.cnblogs.com/luxiaoxun/p/5272384.html) 4 | ### Features: 5 | * Simple code and framework 6 | * Non-blocking asynchronous call and Synchronous call support 7 | * Long lived persistent connection 8 | * High availability, load balance and failover 9 | * Service Discovery support by ZooKeeper 10 | 11 | #### How to use 12 | 1. Define an interface: 13 | 14 | public interface HelloService { 15 | String hello(String name); 16 | String hello(Person person); 17 | } 18 | 19 | 2. Implement the interface with annotation @RpcService: 20 | 21 | @RpcService(HelloService.class) 22 | public class HelloServiceImpl implements HelloService { 23 | @Override 24 | public String hello(String name) { 25 | return "Hello! " + name; 26 | } 27 | 28 | @Override 29 | public String hello(Person person) { 30 | return "Hello! " + person.getFirstName() + " " + person.getLastName(); 31 | } 32 | } 33 | 34 | 3. Run zookeeper 35 | 36 | 4. Start server: 37 | 38 | RpcBootstrap 39 | 40 | 5. Use the client: 41 | 42 | ServiceDiscovery serviceDiscovery = new ServiceDiscovery("127.0.0.1:2181"); 43 | final RpcClient rpcClient = new RpcClient(serviceDiscovery); 44 | // Sync call 45 | HelloService helloService = rpcClient.create(HelloService.class); 46 | String result = helloService.hello("World"); 47 | // Async call 48 | IAsyncObjectProxy client = rpcClient.createAsync(HelloService.class); 49 | RPCFuture helloFuture = client.call("hello", "World"); 50 | String result = (String) helloFuture.get(3000, TimeUnit.MILLISECONDS); 51 | -------------------------------------------------------------------------------- /src/main/java/com/nettyrpc/client/RpcClient.java: -------------------------------------------------------------------------------- 1 | package com.nettyrpc.client; 2 | 3 | import com.nettyrpc.client.proxy.IAsyncObjectProxy; 4 | import com.nettyrpc.client.proxy.ObjectProxy; 5 | import com.nettyrpc.registry.ServiceDiscovery; 6 | 7 | import java.lang.reflect.Proxy; 8 | import java.util.concurrent.ArrayBlockingQueue; 9 | import java.util.concurrent.ThreadPoolExecutor; 10 | import java.util.concurrent.TimeUnit; 11 | 12 | /** 13 | * RPC Client(Create RPC proxy) 14 | * @author luxiaoxun 15 | */ 16 | public class RpcClient { 17 | 18 | private String serverAddress; 19 | private ServiceDiscovery serviceDiscovery; 20 | private static ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(16, 16, 600L, TimeUnit.SECONDS, new ArrayBlockingQueue(65536)); 21 | 22 | public RpcClient(String serverAddress) { 23 | this.serverAddress = serverAddress; 24 | } 25 | 26 | public RpcClient(ServiceDiscovery serviceDiscovery) { 27 | this.serviceDiscovery = serviceDiscovery; 28 | } 29 | 30 | @SuppressWarnings("unchecked") 31 | public static T create(Class interfaceClass) { 32 | return (T) Proxy.newProxyInstance( 33 | interfaceClass.getClassLoader(), 34 | new Class[]{interfaceClass}, 35 | new ObjectProxy(interfaceClass) 36 | ); 37 | } 38 | 39 | public static IAsyncObjectProxy createAsync(Class interfaceClass) { 40 | return new ObjectProxy(interfaceClass); 41 | } 42 | 43 | public static void submit(Runnable task){ 44 | threadPoolExecutor.submit(task); 45 | } 46 | 47 | public void stop() { 48 | threadPoolExecutor.shutdown(); 49 | serviceDiscovery.stop(); 50 | ConnectManage.getInstance().stop(); 51 | } 52 | } 53 | 54 | -------------------------------------------------------------------------------- /src/test/java/com/nettyrpc/test/app/Benchmark.java: -------------------------------------------------------------------------------- 1 | package com.nettyrpc.test.app; 2 | 3 | import com.nettyrpc.client.RpcClient; 4 | import com.nettyrpc.registry.ServiceDiscovery; 5 | import com.nettyrpc.test.client.HelloService; 6 | 7 | /** 8 | * Created by luxiaoxun on 2016-03-11. 9 | */ 10 | public class Benchmark { 11 | 12 | public static void main(String[] args) throws InterruptedException { 13 | 14 | ServiceDiscovery serviceDiscovery = new ServiceDiscovery("127.0.0.1:2181"); 15 | final RpcClient rpcClient = new RpcClient(serviceDiscovery); 16 | 17 | int threadNum = 10; 18 | final int requestNum = 100; 19 | Thread[] threads = new Thread[threadNum]; 20 | 21 | long startTime = System.currentTimeMillis(); 22 | //benchmark for sync call 23 | for(int i = 0; i < threadNum; ++i){ 24 | threads[i] = new Thread(new Runnable(){ 25 | @Override 26 | public void run() { 27 | for (int i = 0; i < requestNum; i++) { 28 | final HelloService syncClient = rpcClient.create(HelloService.class); 29 | String result = syncClient.hello(Integer.toString(i)); 30 | if (!result.equals("Hello! " + i)) 31 | System.out.print("error = " + result); 32 | } 33 | } 34 | }); 35 | threads[i].start(); 36 | } 37 | for(int i=0; i persons = (List) result; 31 | for (int i = 0; i < persons.size(); ++i) { 32 | System.out.println(persons.get(i)); 33 | } 34 | countDownLatch.countDown(); 35 | } 36 | 37 | @Override 38 | public void fail(Exception e) { 39 | System.out.println(e); 40 | countDownLatch.countDown(); 41 | } 42 | }); 43 | 44 | } catch (Exception e) { 45 | System.out.println(e); 46 | } 47 | 48 | try { 49 | countDownLatch.await(); 50 | } catch (InterruptedException e) { 51 | e.printStackTrace(); 52 | } 53 | 54 | rpcClient.stop(); 55 | 56 | System.out.println("End"); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/com/nettyrpc/protocol/SerializationUtil.java: -------------------------------------------------------------------------------- 1 | package com.nettyrpc.protocol; 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 java.util.Map; 8 | import java.util.concurrent.ConcurrentHashMap; 9 | import org.objenesis.Objenesis; 10 | import org.objenesis.ObjenesisStd; 11 | 12 | /** 13 | * Serialization Util(Based on Protostuff) 14 | * @author huangyong 15 | */ 16 | public class SerializationUtil { 17 | 18 | private static Map, Schema> cachedSchema = new ConcurrentHashMap<>(); 19 | 20 | private static Objenesis objenesis = new ObjenesisStd(true); 21 | 22 | private SerializationUtil() { 23 | } 24 | 25 | @SuppressWarnings("unchecked") 26 | private static Schema getSchema(Class cls) { 27 | Schema schema = (Schema) cachedSchema.get(cls); 28 | if (schema == null) { 29 | schema = RuntimeSchema.createFrom(cls); 30 | if (schema != null) { 31 | cachedSchema.put(cls, schema); 32 | } 33 | } 34 | return schema; 35 | } 36 | 37 | /** 38 | * 序列化(对象 -> 字节数组) 39 | */ 40 | @SuppressWarnings("unchecked") 41 | public static byte[] serialize(T obj) { 42 | Class cls = (Class) obj.getClass(); 43 | LinkedBuffer buffer = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE); 44 | try { 45 | Schema schema = getSchema(cls); 46 | return ProtostuffIOUtil.toByteArray(obj, schema, buffer); 47 | } catch (Exception e) { 48 | throw new IllegalStateException(e.getMessage(), e); 49 | } finally { 50 | buffer.clear(); 51 | } 52 | } 53 | 54 | /** 55 | * 反序列化(字节数组 -> 对象) 56 | */ 57 | public static T deserialize(byte[] data, Class cls) { 58 | try { 59 | T message = (T) objenesis.newInstance(cls); 60 | Schema schema = getSchema(cls); 61 | ProtostuffIOUtil.mergeFrom(data, message, schema); 62 | return message; 63 | } catch (Exception e) { 64 | throw new IllegalStateException(e.getMessage(), e); 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/test/java/com/nettyrpc/test/app/BenchmarkAsync.java: -------------------------------------------------------------------------------- 1 | package com.nettyrpc.test.app; 2 | 3 | import com.nettyrpc.client.RPCFuture; 4 | import com.nettyrpc.client.RpcClient; 5 | import com.nettyrpc.client.proxy.IAsyncObjectProxy; 6 | import com.nettyrpc.registry.ServiceDiscovery; 7 | import com.nettyrpc.test.client.HelloService; 8 | 9 | import java.util.concurrent.TimeUnit; 10 | 11 | /** 12 | * Created by luxiaoxun on 2016/3/16. 13 | */ 14 | public class BenchmarkAsync { 15 | public static void main(String[] args) throws InterruptedException { 16 | ServiceDiscovery serviceDiscovery = new ServiceDiscovery("127.0.0.1:2181"); 17 | final RpcClient rpcClient = new RpcClient(serviceDiscovery); 18 | 19 | int threadNum = 10; 20 | final int requestNum = 20; 21 | Thread[] threads = new Thread[threadNum]; 22 | 23 | long startTime = System.currentTimeMillis(); 24 | //benchmark for async call 25 | for (int i = 0; i < threadNum; ++i) { 26 | threads[i] = new Thread(new Runnable() { 27 | @Override 28 | public void run() { 29 | for (int i = 0; i < requestNum; i++) { 30 | try { 31 | IAsyncObjectProxy client = rpcClient.createAsync(HelloService.class); 32 | RPCFuture helloFuture = client.call("hello", Integer.toString(i)); 33 | String result = (String) helloFuture.get(3000, TimeUnit.MILLISECONDS); 34 | //System.out.println(result); 35 | if (!result.equals("Hello! " + i)) 36 | System.out.println("error = " + result); 37 | } catch (Exception e) { 38 | System.out.println(e); 39 | } 40 | } 41 | } 42 | }); 43 | threads[i].start(); 44 | } 45 | for (int i = 0; i < threads.length; i++) { 46 | threads[i].join(); 47 | } 48 | long timeCost = (System.currentTimeMillis() - startTime); 49 | String msg = String.format("Async call total-time-cost:%sms, req/s=%s", timeCost, ((double) (requestNum * threadNum)) / timeCost * 1000); 50 | System.out.println(msg); 51 | 52 | rpcClient.stop(); 53 | 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/com/nettyrpc/client/RpcClientHandler.java: -------------------------------------------------------------------------------- 1 | package com.nettyrpc.client; 2 | 3 | import com.nettyrpc.protocol.RpcRequest; 4 | import com.nettyrpc.protocol.RpcResponse; 5 | import io.netty.buffer.Unpooled; 6 | import io.netty.channel.Channel; 7 | import io.netty.channel.ChannelFutureListener; 8 | import io.netty.channel.ChannelHandlerContext; 9 | import io.netty.channel.SimpleChannelInboundHandler; 10 | import org.slf4j.Logger; 11 | import org.slf4j.LoggerFactory; 12 | 13 | import java.net.SocketAddress; 14 | import java.util.concurrent.ConcurrentHashMap; 15 | 16 | /** 17 | * Created by luxiaoxun on 2016-03-14. 18 | */ 19 | public class RpcClientHandler extends SimpleChannelInboundHandler { 20 | private static final Logger LOGGER = LoggerFactory.getLogger(RpcClientHandler.class); 21 | 22 | private ConcurrentHashMap pendingRPC = new ConcurrentHashMap<>(); 23 | 24 | private volatile Channel channel; 25 | private SocketAddress remotePeer; 26 | 27 | public Channel getChannel() { 28 | return channel; 29 | } 30 | 31 | public SocketAddress getRemotePeer() { 32 | return remotePeer; 33 | } 34 | 35 | @Override 36 | public void channelActive(ChannelHandlerContext ctx) throws Exception { 37 | super.channelActive(ctx); 38 | this.remotePeer = this.channel.remoteAddress(); 39 | } 40 | 41 | @Override 42 | public void channelRegistered(ChannelHandlerContext ctx) throws Exception { 43 | super.channelRegistered(ctx); 44 | this.channel = ctx.channel(); 45 | } 46 | 47 | @Override 48 | public void channelRead0(ChannelHandlerContext ctx, RpcResponse response) throws Exception { 49 | String requestId = response.getRequestId(); 50 | RPCFuture rpcFuture = pendingRPC.get(requestId); 51 | if (rpcFuture != null) { 52 | pendingRPC.remove(requestId); 53 | rpcFuture.done(response); 54 | } 55 | } 56 | 57 | @Override 58 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { 59 | LOGGER.error("client caught exception", cause); 60 | ctx.close(); 61 | } 62 | 63 | public void close() { 64 | channel.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE); 65 | } 66 | 67 | public RPCFuture sendRequest(RpcRequest request) { 68 | RPCFuture rpcFuture = new RPCFuture(request); 69 | pendingRPC.put(request.getRequestId(), rpcFuture); 70 | channel.writeAndFlush(request); 71 | 72 | return rpcFuture; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/main/java/com/nettyrpc/registry/ServiceRegistry.java: -------------------------------------------------------------------------------- 1 | package com.nettyrpc.registry; 2 | 3 | import java.io.IOException; 4 | import java.util.concurrent.CountDownLatch; 5 | import org.apache.zookeeper.CreateMode; 6 | import org.apache.zookeeper.KeeperException; 7 | import org.apache.zookeeper.WatchedEvent; 8 | import org.apache.zookeeper.Watcher; 9 | import org.apache.zookeeper.ZooDefs; 10 | import org.apache.zookeeper.ZooKeeper; 11 | import org.apache.zookeeper.data.Stat; 12 | import org.slf4j.Logger; 13 | import org.slf4j.LoggerFactory; 14 | import sun.rmi.runtime.Log; 15 | 16 | /** 17 | * 服务注册 18 | * 19 | * @author huangyong 20 | * @author luxiaoxun 21 | */ 22 | public class ServiceRegistry { 23 | 24 | private static final Logger LOGGER = LoggerFactory.getLogger(ServiceRegistry.class); 25 | 26 | private CountDownLatch latch = new CountDownLatch(1); 27 | 28 | private String registryAddress; 29 | 30 | public ServiceRegistry(String registryAddress) { 31 | this.registryAddress = registryAddress; 32 | } 33 | 34 | public void register(String data) { 35 | if (data != null) { 36 | ZooKeeper zk = connectServer(); 37 | if (zk != null) { 38 | AddRootNode(zk); // Add root node if not exist 39 | createNode(zk, data); 40 | } 41 | } 42 | } 43 | 44 | private ZooKeeper connectServer() { 45 | ZooKeeper zk = null; 46 | try { 47 | zk = new ZooKeeper(registryAddress, Constant.ZK_SESSION_TIMEOUT, new Watcher() { 48 | @Override 49 | public void process(WatchedEvent event) { 50 | if (event.getState() == Event.KeeperState.SyncConnected) { 51 | latch.countDown(); 52 | } 53 | } 54 | }); 55 | latch.await(); 56 | } catch (IOException e) { 57 | LOGGER.error("", e); 58 | } 59 | catch (InterruptedException ex){ 60 | LOGGER.error("", ex); 61 | } 62 | return zk; 63 | } 64 | 65 | private void AddRootNode(ZooKeeper zk){ 66 | try { 67 | Stat s = zk.exists(Constant.ZK_REGISTRY_PATH, false); 68 | if (s == null) { 69 | zk.create(Constant.ZK_REGISTRY_PATH, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); 70 | } 71 | } catch (KeeperException e) { 72 | LOGGER.error(e.toString()); 73 | } catch (InterruptedException e) { 74 | LOGGER.error(e.toString()); 75 | } 76 | } 77 | 78 | private void createNode(ZooKeeper zk, String data) { 79 | try { 80 | byte[] bytes = data.getBytes(); 81 | String path = zk.create(Constant.ZK_DATA_PATH, bytes, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL); 82 | LOGGER.debug("create zookeeper node ({} => {})", path, data); 83 | } catch (KeeperException e) { 84 | LOGGER.error("", e); 85 | } 86 | catch (InterruptedException ex){ 87 | LOGGER.error("", ex); 88 | } 89 | } 90 | } -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.nettyrpc 8 | NettyRpc 9 | 1.0-SNAPSHOT 10 | 11 | 12 | 13 | 14 | junit 15 | junit 16 | 4.11 17 | test 18 | 19 | 20 | 21 | 22 | org.slf4j 23 | slf4j-log4j12 24 | 1.7.7 25 | 26 | 27 | 28 | 29 | org.springframework 30 | spring-context 31 | 3.2.12.RELEASE 32 | 33 | 34 | org.springframework 35 | spring-test 36 | 3.2.12.RELEASE 37 | test 38 | 39 | 40 | 41 | 42 | io.netty 43 | netty-all 44 | 4.0.24.Final 45 | 46 | 47 | 48 | 49 | com.dyuproject.protostuff 50 | protostuff-core 51 | 1.0.8 52 | 53 | 54 | com.dyuproject.protostuff 55 | protostuff-runtime 56 | 1.0.8 57 | 58 | 59 | 60 | 61 | org.apache.zookeeper 62 | zookeeper 63 | 3.4.8 64 | 65 | 66 | 67 | 68 | org.apache.commons 69 | commons-collections4 70 | 4.0 71 | 72 | 73 | 74 | 75 | org.objenesis 76 | objenesis 77 | 2.1 78 | 79 | 80 | 81 | 82 | cglib 83 | cglib 84 | 3.1 85 | 86 | 87 | 88 | com.fasterxml.jackson.core 89 | jackson-databind 90 | 2.7.2 91 | 92 | 93 | 94 | com.fasterxml.jackson.core 95 | jackson-core 96 | 2.7.2 97 | 98 | 99 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /src/main/java/com/nettyrpc/server/RpcHandler.java: -------------------------------------------------------------------------------- 1 | package com.nettyrpc.server; 2 | 3 | import com.nettyrpc.protocol.RpcRequest; 4 | import com.nettyrpc.protocol.RpcResponse; 5 | import io.netty.channel.ChannelFuture; 6 | import io.netty.channel.ChannelFutureListener; 7 | import io.netty.channel.ChannelHandlerContext; 8 | import io.netty.channel.SimpleChannelInboundHandler; 9 | import java.util.Map; 10 | import net.sf.cglib.reflect.FastClass; 11 | import net.sf.cglib.reflect.FastMethod; 12 | import org.slf4j.Logger; 13 | import org.slf4j.LoggerFactory; 14 | 15 | /** 16 | * RPC Handler(RPC request processor) 17 | * @author luxiaoxun 18 | */ 19 | public class RpcHandler extends SimpleChannelInboundHandler { 20 | 21 | private static final Logger LOGGER = LoggerFactory.getLogger(RpcHandler.class); 22 | 23 | private final Map handlerMap; 24 | 25 | public RpcHandler(Map handlerMap) { 26 | this.handlerMap = handlerMap; 27 | } 28 | 29 | @Override 30 | public void channelRead0(final ChannelHandlerContext ctx,final RpcRequest request) throws Exception { 31 | RpcServer.submit(new Runnable() { 32 | @Override 33 | public void run() { 34 | LOGGER.debug("Receive request " + request.getRequestId()); 35 | RpcResponse response = new RpcResponse(); 36 | response.setRequestId(request.getRequestId()); 37 | try { 38 | Object result = handle(request); 39 | response.setResult(result); 40 | } catch (Throwable t) { 41 | response.setError(t.toString()); 42 | LOGGER.error("RPC Server handle request error",t); 43 | } 44 | ctx.writeAndFlush(response).addListener(new ChannelFutureListener() { 45 | @Override 46 | public void operationComplete(ChannelFuture channelFuture) throws Exception { 47 | LOGGER.debug("Send response for request " + request.getRequestId()); 48 | } 49 | }); 50 | } 51 | }); 52 | } 53 | 54 | private Object handle(RpcRequest request) throws Throwable { 55 | String className = request.getClassName(); 56 | Object serviceBean = handlerMap.get(className); 57 | 58 | Class serviceClass = serviceBean.getClass(); 59 | String methodName = request.getMethodName(); 60 | Class[] parameterTypes = request.getParameterTypes(); 61 | Object[] parameters = request.getParameters(); 62 | 63 | LOGGER.debug(serviceClass.getName()); 64 | LOGGER.debug(methodName); 65 | for (int i = 0; i < parameterTypes.length; ++i) { 66 | LOGGER.debug(parameterTypes[i].getName()); 67 | } 68 | for (int i = 0; i < parameters.length; ++i) { 69 | LOGGER.debug(parameters[i].toString()); 70 | } 71 | 72 | // JDK reflect 73 | /*Method method = serviceClass.getMethod(methodName, parameterTypes); 74 | method.setAccessible(true); 75 | return method.invoke(serviceBean, parameters);*/ 76 | 77 | // Cglib reflect 78 | FastClass serviceFastClass = FastClass.create(serviceClass); 79 | FastMethod serviceFastMethod = serviceFastClass.getMethod(methodName, parameterTypes); 80 | return serviceFastMethod.invoke(serviceBean, parameters); 81 | } 82 | 83 | @Override 84 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { 85 | LOGGER.error("server caught exception", cause); 86 | ctx.close(); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /NettyRpc.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /src/main/java/com/nettyrpc/registry/ServiceDiscovery.java: -------------------------------------------------------------------------------- 1 | package com.nettyrpc.registry; 2 | 3 | import java.io.IOException; 4 | import java.util.ArrayList; 5 | import java.util.List; 6 | import java.util.concurrent.CountDownLatch; 7 | import java.util.concurrent.ThreadLocalRandom; 8 | 9 | import com.nettyrpc.client.ConnectManage; 10 | import org.apache.zookeeper.KeeperException; 11 | import org.apache.zookeeper.WatchedEvent; 12 | import org.apache.zookeeper.Watcher; 13 | import org.apache.zookeeper.ZooKeeper; 14 | import org.slf4j.Logger; 15 | import org.slf4j.LoggerFactory; 16 | 17 | /** 18 | * 服务发现 19 | * 20 | * @author huangyong 21 | * @author luxiaoxun 22 | */ 23 | public class ServiceDiscovery { 24 | 25 | private static final Logger LOGGER = LoggerFactory.getLogger(ServiceDiscovery.class); 26 | 27 | private CountDownLatch latch = new CountDownLatch(1); 28 | 29 | private volatile List dataList = new ArrayList<>(); 30 | 31 | private String registryAddress; 32 | private ZooKeeper zookeeper; 33 | 34 | public ServiceDiscovery(String registryAddress) { 35 | this.registryAddress = registryAddress; 36 | zookeeper = connectServer(); 37 | if (zookeeper != null) { 38 | watchNode(zookeeper); 39 | } 40 | } 41 | 42 | public String discover() { 43 | String data = null; 44 | int size = dataList.size(); 45 | if (size > 0) { 46 | if (size == 1) { 47 | data = dataList.get(0); 48 | LOGGER.debug("using only data: {}", data); 49 | } else { 50 | data = dataList.get(ThreadLocalRandom.current().nextInt(size)); 51 | LOGGER.debug("using random data: {}", data); 52 | } 53 | } 54 | return data; 55 | } 56 | 57 | private ZooKeeper connectServer() { 58 | ZooKeeper zk = null; 59 | try { 60 | zk = new ZooKeeper(registryAddress, Constant.ZK_SESSION_TIMEOUT, new Watcher() { 61 | @Override 62 | public void process(WatchedEvent event) { 63 | if (event.getState() == Event.KeeperState.SyncConnected) { 64 | latch.countDown(); 65 | } 66 | } 67 | }); 68 | latch.await(); 69 | } catch (IOException | InterruptedException e) { 70 | LOGGER.error("", e); 71 | } 72 | return zk; 73 | } 74 | 75 | private void watchNode(final ZooKeeper zk) { 76 | try { 77 | List nodeList = zk.getChildren(Constant.ZK_REGISTRY_PATH, new Watcher() { 78 | @Override 79 | public void process(WatchedEvent event) { 80 | if (event.getType() == Event.EventType.NodeChildrenChanged) { 81 | watchNode(zk); 82 | } 83 | } 84 | }); 85 | List dataList = new ArrayList<>(); 86 | for (String node : nodeList) { 87 | byte[] bytes = zk.getData(Constant.ZK_REGISTRY_PATH + "/" + node, false, null); 88 | dataList.add(new String(bytes)); 89 | } 90 | LOGGER.debug("node data: {}", dataList); 91 | this.dataList = dataList; 92 | 93 | LOGGER.debug("Service discovery triggered updating connected server node."); 94 | UpdateConnectedServer(); 95 | } catch (KeeperException | InterruptedException e) { 96 | LOGGER.error("", e); 97 | } 98 | } 99 | 100 | private void UpdateConnectedServer(){ 101 | ConnectManage.getInstance().updateConnectedServer(this.dataList); 102 | } 103 | 104 | public void stop(){ 105 | if(zookeeper!=null){ 106 | try { 107 | zookeeper.close(); 108 | } catch (InterruptedException e) { 109 | LOGGER.error("", e); 110 | } 111 | } 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/test/java/com/nettyrpc/test/app/HelloServiceTest.java: -------------------------------------------------------------------------------- 1 | package com.nettyrpc.test.app; 2 | 3 | import com.nettyrpc.client.RPCFuture; 4 | import com.nettyrpc.client.RpcClient; 5 | import com.nettyrpc.client.proxy.IAsyncObjectProxy; 6 | import com.nettyrpc.test.client.HelloPersonService; 7 | import com.nettyrpc.test.client.HelloService; 8 | import com.nettyrpc.test.client.Person; 9 | import org.junit.After; 10 | import org.junit.Assert; 11 | import org.junit.Test; 12 | import org.junit.runner.RunWith; 13 | import org.springframework.beans.factory.annotation.Autowired; 14 | import org.springframework.test.context.ContextConfiguration; 15 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 16 | 17 | import java.util.ArrayList; 18 | import java.util.List; 19 | import java.util.concurrent.ExecutionException; 20 | 21 | import static org.hamcrest.MatcherAssert.assertThat; 22 | import static org.hamcrest.core.IsEqual.equalTo; 23 | 24 | @RunWith(SpringJUnit4ClassRunner.class) 25 | @ContextConfiguration(locations = "classpath:client-spring.xml") 26 | public class HelloServiceTest { 27 | 28 | @Autowired 29 | private RpcClient rpcClient; 30 | 31 | @Test 32 | public void helloTest1() { 33 | HelloService helloService = rpcClient.create(HelloService.class); 34 | String result = helloService.hello("World"); 35 | Assert.assertEquals("Hello! World", result); 36 | } 37 | 38 | @Test 39 | public void helloTest2() { 40 | HelloService helloService = rpcClient.create(HelloService.class); 41 | Person person = new Person("Yong", "Huang"); 42 | String result = helloService.hello(person); 43 | Assert.assertEquals("Hello! Yong Huang", result); 44 | } 45 | 46 | @Test 47 | public void helloPersonTest(){ 48 | HelloPersonService helloPersonService = rpcClient.create(HelloPersonService.class); 49 | int num = 5; 50 | List persons = helloPersonService.GetTestPerson("xiaoming",num); 51 | List expectedPersons = new ArrayList<>(); 52 | for (int i = 0; i < num; i++) { 53 | expectedPersons.add(new Person(Integer.toString(i), "xiaoming")); 54 | } 55 | assertThat(persons, equalTo(expectedPersons)); 56 | 57 | for (int i = 0; i persons = (List) result.get(); 83 | List expectedPersons = new ArrayList<>(); 84 | for (int i = 0; i < num; i++) { 85 | expectedPersons.add(new Person(Integer.toString(i), "xiaoming")); 86 | } 87 | assertThat(persons, equalTo(expectedPersons)); 88 | 89 | for (int i = 0; i < num; ++i) { 90 | System.out.println(persons.get(i)); 91 | } 92 | } 93 | 94 | @After 95 | public void setTear(){ 96 | if(rpcClient != null) { 97 | rpcClient.stop(); 98 | } 99 | } 100 | 101 | } 102 | -------------------------------------------------------------------------------- /src/main/java/com/nettyrpc/protocol/JsonUtil.java: -------------------------------------------------------------------------------- 1 | package com.nettyrpc.protocol; 2 | 3 | import com.fasterxml.jackson.annotation.JsonInclude; 4 | import com.fasterxml.jackson.core.JsonGenerator; 5 | import com.fasterxml.jackson.core.JsonParser; 6 | import com.fasterxml.jackson.core.JsonProcessingException; 7 | import com.fasterxml.jackson.databind.DeserializationFeature; 8 | import com.fasterxml.jackson.databind.JavaType; 9 | import com.fasterxml.jackson.databind.ObjectMapper; 10 | import com.fasterxml.jackson.databind.SerializationFeature; 11 | 12 | import java.io.IOException; 13 | import java.text.SimpleDateFormat; 14 | import java.util.HashMap; 15 | 16 | /** 17 | * Json Util 18 | * Created by luxiaoxun on 2016-03-09. 19 | */ 20 | public class JsonUtil { 21 | private static ObjectMapper objMapper = new ObjectMapper(); 22 | 23 | static { 24 | SimpleDateFormat dateFormat = new SimpleDateFormat( 25 | "yyyy-MM-dd HH:mm:ss"); 26 | objMapper.setDateFormat(dateFormat); 27 | objMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); 28 | objMapper.enable(SerializationFeature.INDENT_OUTPUT); 29 | objMapper.configure(JsonGenerator.Feature.AUTO_CLOSE_TARGET, false); 30 | objMapper.configure(JsonGenerator.Feature.AUTO_CLOSE_JSON_CONTENT, false); 31 | objMapper.disable(SerializationFeature.FLUSH_AFTER_WRITE_VALUE); 32 | objMapper.disable(SerializationFeature.CLOSE_CLOSEABLE); 33 | objMapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS); 34 | objMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); 35 | objMapper.configure(JsonParser.Feature.IGNORE_UNDEFINED, true); 36 | } 37 | 38 | public static byte[] serialize(T obj){ 39 | byte[] bytes = new byte[0]; 40 | try { 41 | bytes = objMapper.writeValueAsBytes(obj); 42 | } catch (JsonProcessingException e) { 43 | throw new IllegalStateException(e.getMessage(), e); 44 | } 45 | return bytes; 46 | } 47 | 48 | public static T deserialize(byte[] data, Class cls) { 49 | T obj = null; 50 | try { 51 | obj = objMapper.readValue(data,cls); 52 | } catch (IOException e) { 53 | throw new IllegalStateException(e.getMessage(), e); 54 | } 55 | return obj; 56 | } 57 | 58 | 59 | public static type jsonToObject(String json, Class cls) { 60 | type obj = null; 61 | JavaType javaType = objMapper.getTypeFactory().constructType(cls); 62 | try { 63 | obj = objMapper.readValue(json, javaType); 64 | } catch (IOException e) { 65 | throw new IllegalStateException(e.getMessage(), e); 66 | } 67 | return obj; 68 | } 69 | 70 | public static type jsonToObjectList(String json, 71 | Class collectionClass, Class... elementClass) { 72 | type obj = null; 73 | JavaType javaType = objMapper.getTypeFactory().constructParametricType( 74 | collectionClass, elementClass); 75 | try { 76 | obj = objMapper.readValue(json, javaType); 77 | } catch (IOException e) { 78 | throw new IllegalStateException(e.getMessage(), e); 79 | } 80 | return obj; 81 | } 82 | 83 | public static type jsonToObjectHashMap(String json, 84 | Class keyClass, Class valueClass) { 85 | type obj = null; 86 | JavaType javaType = objMapper.getTypeFactory().constructParametricType(HashMap.class, keyClass,valueClass); 87 | try { 88 | obj = objMapper.readValue(json, javaType); 89 | } catch (IOException e) { 90 | throw new IllegalStateException(e.getMessage(), e); 91 | } 92 | return obj; 93 | } 94 | 95 | public static String objectToJson(Object o) { 96 | String json = ""; 97 | try { 98 | json = objMapper.writeValueAsString(o); 99 | } catch (IOException e) { 100 | throw new IllegalStateException(e.getMessage(), e); 101 | } 102 | return json; 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/main/java/com/nettyrpc/server/RpcServer.java: -------------------------------------------------------------------------------- 1 | package com.nettyrpc.server; 2 | 3 | import com.nettyrpc.protocol.RpcDecoder; 4 | import com.nettyrpc.protocol.RpcEncoder; 5 | import com.nettyrpc.protocol.RpcRequest; 6 | import com.nettyrpc.protocol.RpcResponse; 7 | import com.nettyrpc.registry.ServiceRegistry; 8 | import io.netty.bootstrap.ServerBootstrap; 9 | import io.netty.channel.ChannelFuture; 10 | import io.netty.channel.ChannelInitializer; 11 | import io.netty.channel.ChannelOption; 12 | import io.netty.channel.EventLoopGroup; 13 | import io.netty.channel.nio.NioEventLoopGroup; 14 | import io.netty.channel.socket.SocketChannel; 15 | import io.netty.channel.socket.nio.NioServerSocketChannel; 16 | import java.util.HashMap; 17 | import java.util.Map; 18 | import java.util.concurrent.*; 19 | 20 | import io.netty.handler.codec.LengthFieldBasedFrameDecoder; 21 | import org.apache.commons.collections4.MapUtils; 22 | import org.slf4j.Logger; 23 | import org.slf4j.LoggerFactory; 24 | import org.springframework.beans.BeansException; 25 | import org.springframework.beans.factory.InitializingBean; 26 | import org.springframework.context.ApplicationContext; 27 | import org.springframework.context.ApplicationContextAware; 28 | 29 | /** 30 | * RPC Server 31 | * @author huangyong,luxiaoxun 32 | */ 33 | public class RpcServer implements ApplicationContextAware, InitializingBean { 34 | 35 | private static final Logger LOGGER = LoggerFactory.getLogger(RpcServer.class); 36 | 37 | private String serverAddress; 38 | private ServiceRegistry serviceRegistry; 39 | 40 | private Map handlerMap = new HashMap<>(); 41 | 42 | private static ThreadPoolExecutor threadPoolExecutor; 43 | 44 | public RpcServer(String serverAddress) { 45 | this.serverAddress = serverAddress; 46 | } 47 | 48 | public RpcServer(String serverAddress, ServiceRegistry serviceRegistry) { 49 | this.serverAddress = serverAddress; 50 | this.serviceRegistry = serviceRegistry; 51 | } 52 | 53 | @Override 54 | public void setApplicationContext(ApplicationContext ctx) throws BeansException { 55 | Map serviceBeanMap = ctx.getBeansWithAnnotation(RpcService.class); 56 | if (MapUtils.isNotEmpty(serviceBeanMap)) { 57 | for (Object serviceBean : serviceBeanMap.values()) { 58 | String interfaceName = serviceBean.getClass().getAnnotation(RpcService.class).value().getName(); 59 | handlerMap.put(interfaceName, serviceBean); 60 | } 61 | } 62 | } 63 | 64 | @Override 65 | public void afterPropertiesSet() throws Exception { 66 | EventLoopGroup bossGroup = new NioEventLoopGroup(); 67 | EventLoopGroup workerGroup = new NioEventLoopGroup(); 68 | try { 69 | ServerBootstrap bootstrap = new ServerBootstrap(); 70 | bootstrap.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class) 71 | .childHandler(new ChannelInitializer() { 72 | @Override 73 | public void initChannel(SocketChannel channel) throws Exception { 74 | channel.pipeline() 75 | .addLast(new LengthFieldBasedFrameDecoder(65536,0,4,0,0)) 76 | .addLast(new RpcDecoder(RpcRequest.class)) 77 | .addLast(new RpcEncoder(RpcResponse.class)) 78 | .addLast(new RpcHandler(handlerMap)); 79 | } 80 | }) 81 | .option(ChannelOption.SO_BACKLOG, 128) 82 | .childOption(ChannelOption.SO_KEEPALIVE, true); 83 | 84 | String[] array = serverAddress.split(":"); 85 | String host = array[0]; 86 | int port = Integer.parseInt(array[1]); 87 | 88 | ChannelFuture future = bootstrap.bind(host, port).sync(); 89 | LOGGER.debug("Server started on port {}", port); 90 | 91 | if (serviceRegistry != null) { 92 | serviceRegistry.register(serverAddress); 93 | } 94 | 95 | future.channel().closeFuture().sync(); 96 | } finally { 97 | workerGroup.shutdownGracefully(); 98 | bossGroup.shutdownGracefully(); 99 | } 100 | } 101 | 102 | public static void submit(Runnable task){ 103 | if(threadPoolExecutor == null){ 104 | synchronized (RpcServer.class) { 105 | if(threadPoolExecutor == null){ 106 | threadPoolExecutor = new ThreadPoolExecutor(16, 16, 600L, TimeUnit.SECONDS, new ArrayBlockingQueue(65536)); 107 | } 108 | } 109 | } 110 | threadPoolExecutor.submit(task); 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /src/main/java/com/nettyrpc/client/proxy/ObjectProxy.java: -------------------------------------------------------------------------------- 1 | package com.nettyrpc.client.proxy; 2 | 3 | import com.nettyrpc.client.ConnectManage; 4 | import com.nettyrpc.client.RPCFuture; 5 | import com.nettyrpc.client.RpcClientHandler; 6 | import com.nettyrpc.protocol.RpcRequest; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | import java.lang.reflect.InvocationHandler; 11 | import java.lang.reflect.Method; 12 | import java.util.UUID; 13 | 14 | /** 15 | * Created by luxiaoxun on 2016-03-16. 16 | */ 17 | public class ObjectProxy implements InvocationHandler, IAsyncObjectProxy { 18 | private static final Logger LOGGER = LoggerFactory.getLogger(ObjectProxy.class); 19 | private Class clazz; 20 | 21 | public ObjectProxy(Class clazz) { 22 | this.clazz = clazz; 23 | } 24 | 25 | @Override 26 | public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 27 | if (Object.class == method.getDeclaringClass()) { 28 | String name = method.getName(); 29 | if ("equals".equals(name)) { 30 | return proxy == args[0]; 31 | } else if ("hashCode".equals(name)) { 32 | return System.identityHashCode(proxy); 33 | } else if ("toString".equals(name)) { 34 | return proxy.getClass().getName() + "@" + 35 | Integer.toHexString(System.identityHashCode(proxy)) + 36 | ", with InvocationHandler " + this; 37 | } else { 38 | throw new IllegalStateException(String.valueOf(method)); 39 | } 40 | } 41 | 42 | RpcRequest request = new RpcRequest(); 43 | request.setRequestId(UUID.randomUUID().toString()); 44 | request.setClassName(method.getDeclaringClass().getName()); 45 | request.setMethodName(method.getName()); 46 | request.setParameterTypes(method.getParameterTypes()); 47 | request.setParameters(args); 48 | // Debug 49 | LOGGER.debug(method.getDeclaringClass().getName()); 50 | LOGGER.debug(method.getName()); 51 | for (int i = 0; i < method.getParameterTypes().length; ++i) { 52 | LOGGER.debug(method.getParameterTypes()[i].getName()); 53 | } 54 | for (int i = 0; i < args.length; ++i) { 55 | LOGGER.debug(args[i].toString()); 56 | } 57 | 58 | RpcClientHandler handler = ConnectManage.getInstance().chooseHandler(); 59 | RPCFuture rpcFuture = handler.sendRequest(request); 60 | return rpcFuture.get(); 61 | } 62 | 63 | @Override 64 | public RPCFuture call(String funcName, Object... args) { 65 | RpcClientHandler handler = ConnectManage.getInstance().chooseHandler(); 66 | RpcRequest request = createRequest(this.clazz.getName(), funcName, args); 67 | RPCFuture rpcFuture = handler.sendRequest(request); 68 | return rpcFuture; 69 | } 70 | 71 | private RpcRequest createRequest(String className, String methodName, Object[] args) { 72 | RpcRequest request = new RpcRequest(); 73 | request.setRequestId(UUID.randomUUID().toString()); 74 | request.setClassName(className); 75 | request.setMethodName(methodName); 76 | request.setParameters(args); 77 | 78 | Class[] parameterTypes = new Class[args.length]; 79 | // Get the right class type 80 | for (int i = 0; i < args.length; i++) { 81 | parameterTypes[i] = getClassType(args[i]); 82 | } 83 | request.setParameterTypes(parameterTypes); 84 | // Method[] methods = clazz.getDeclaredMethods(); 85 | // for (int i = 0; i < methods.length; ++i) { 86 | // // Bug: if there are 2 methods have the same name 87 | // if (methods[i].getName().equals(methodName)) { 88 | // parameterTypes = methods[i].getParameterTypes(); 89 | // request.setParameterTypes(parameterTypes); // get parameter types 90 | // break; 91 | // } 92 | // } 93 | 94 | LOGGER.debug(className); 95 | LOGGER.debug(methodName); 96 | for (int i = 0; i < parameterTypes.length; ++i) { 97 | LOGGER.debug(parameterTypes[i].getName()); 98 | } 99 | for (int i = 0; i < args.length; ++i) { 100 | LOGGER.debug(args[i].toString()); 101 | } 102 | 103 | return request; 104 | } 105 | 106 | private Class getClassType(Object obj){ 107 | Class classType = obj.getClass(); 108 | String typeName = classType.getName(); 109 | switch (typeName){ 110 | case "java.lang.Integer": 111 | return Integer.TYPE; 112 | case "java.lang.Long": 113 | return Long.TYPE; 114 | case "java.lang.Float": 115 | return Float.TYPE; 116 | case "java.lang.Double": 117 | return Double.TYPE; 118 | case "java.lang.Character": 119 | return Character.TYPE; 120 | case "java.lang.Boolean": 121 | return Boolean.TYPE; 122 | case "java.lang.Short": 123 | return Short.TYPE; 124 | case "java.lang.Byte": 125 | return Byte.TYPE; 126 | } 127 | 128 | return classType; 129 | } 130 | 131 | } 132 | -------------------------------------------------------------------------------- /src/main/java/com/nettyrpc/client/RPCFuture.java: -------------------------------------------------------------------------------- 1 | package com.nettyrpc.client; 2 | 3 | import com.nettyrpc.protocol.RpcRequest; 4 | import com.nettyrpc.protocol.RpcResponse; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | import java.util.concurrent.ExecutionException; 11 | import java.util.concurrent.Future; 12 | import java.util.concurrent.TimeUnit; 13 | import java.util.concurrent.TimeoutException; 14 | import java.util.concurrent.locks.AbstractQueuedSynchronizer; 15 | import java.util.concurrent.locks.ReentrantLock; 16 | 17 | /** 18 | * RPCFuture for async RPC call 19 | * Created by luxiaoxun on 2016-03-15. 20 | */ 21 | public class RPCFuture implements Future { 22 | private static final Logger LOGGER = LoggerFactory.getLogger(RPCFuture.class); 23 | 24 | private Sync sync; 25 | private RpcRequest request; 26 | private RpcResponse response; 27 | private long startTime; 28 | 29 | private long responseTimeThreshold = 5000; 30 | 31 | private List pendingCallbacks = new ArrayList(); 32 | private ReentrantLock lock = new ReentrantLock(); 33 | 34 | public RPCFuture(RpcRequest request) { 35 | this.sync = new Sync(); 36 | this.request = request; 37 | this.startTime = System.currentTimeMillis(); 38 | } 39 | 40 | @Override 41 | public boolean isDone() { 42 | return sync.isDone(); 43 | } 44 | 45 | @Override 46 | public Object get() throws InterruptedException, ExecutionException { 47 | sync.acquire(-1); 48 | if (this.response != null) { 49 | return this.response.getResult(); 50 | } else { 51 | return null; 52 | } 53 | } 54 | 55 | @Override 56 | public Object get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { 57 | boolean success = sync.tryAcquireNanos(-1, unit.toNanos(timeout)); 58 | if (success) { 59 | if (this.response != null) { 60 | return this.response.getResult(); 61 | } else { 62 | return null; 63 | } 64 | } else { 65 | throw new RuntimeException("Timeout exception. Request id: " + this.request.getRequestId() 66 | + ". Request class name: " + this.request.getClassName() 67 | + ". Request method: " + this.request.getMethodName()); 68 | } 69 | } 70 | 71 | @Override 72 | public boolean isCancelled() { 73 | throw new UnsupportedOperationException(); 74 | } 75 | 76 | @Override 77 | public boolean cancel(boolean mayInterruptIfRunning) { 78 | throw new UnsupportedOperationException(); 79 | } 80 | 81 | public void done(RpcResponse reponse) { 82 | this.response = reponse; 83 | sync.release(1); 84 | invokeCallbacks(); 85 | // Threshold 86 | long responseTime = System.currentTimeMillis() - startTime; 87 | if (responseTime > this.responseTimeThreshold) { 88 | LOGGER.warn("Service response time is too slow. Request id = " + reponse.getRequestId() + ". Response Time = " + responseTime + "ms"); 89 | } 90 | } 91 | 92 | private void invokeCallbacks() { 93 | lock.lock(); 94 | try { 95 | for (final AsyncRPCCallback callback : pendingCallbacks) { 96 | runCallback(callback); 97 | } 98 | } finally { 99 | lock.unlock(); 100 | } 101 | } 102 | 103 | public RPCFuture addCallback(AsyncRPCCallback callback) { 104 | lock.lock(); 105 | try { 106 | if (isDone()) { 107 | runCallback(callback); 108 | } else { 109 | this.pendingCallbacks.add(callback); 110 | } 111 | } finally { 112 | lock.unlock(); 113 | } 114 | return this; 115 | } 116 | 117 | private void runCallback(final AsyncRPCCallback callback) { 118 | final RpcResponse res = this.response; 119 | RpcClient.submit(new Runnable() { 120 | @Override 121 | public void run() { 122 | if (!res.isError()) { 123 | callback.success(res.getResult()); 124 | } else { 125 | callback.fail(new RuntimeException("Response error", new Throwable(res.getError()))); 126 | } 127 | } 128 | }); 129 | } 130 | 131 | static class Sync extends AbstractQueuedSynchronizer { 132 | 133 | private static final long serialVersionUID = 1L; 134 | 135 | //future status 136 | private final int done = 1; 137 | private final int pending = 0; 138 | 139 | protected boolean tryAcquire(int acquires) { 140 | return getState() == done ? true : false; 141 | } 142 | 143 | protected boolean tryRelease(int releases) { 144 | if (getState() == pending) { 145 | if (compareAndSetState(pending, done)) { 146 | return true; 147 | } 148 | } 149 | return false; 150 | } 151 | 152 | public boolean isDone() { 153 | getState(); 154 | return getState() == done; 155 | } 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /src/main/java/com/nettyrpc/client/ConnectManage.java: -------------------------------------------------------------------------------- 1 | package com.nettyrpc.client; 2 | 3 | import io.netty.bootstrap.Bootstrap; 4 | import io.netty.channel.ChannelFuture; 5 | import io.netty.channel.ChannelFutureListener; 6 | import io.netty.channel.EventLoopGroup; 7 | import io.netty.channel.nio.NioEventLoopGroup; 8 | import io.netty.channel.socket.nio.NioSocketChannel; 9 | import org.slf4j.Logger; 10 | import org.slf4j.LoggerFactory; 11 | 12 | import java.net.InetSocketAddress; 13 | import java.net.SocketAddress; 14 | import java.util.HashSet; 15 | import java.util.List; 16 | import java.util.Map; 17 | import java.util.concurrent.*; 18 | import java.util.concurrent.atomic.AtomicInteger; 19 | import java.util.concurrent.locks.Condition; 20 | import java.util.concurrent.locks.ReentrantLock; 21 | 22 | /** 23 | * RPC Connect Manage of ZooKeeper 24 | * Created by luxiaoxun on 2016-03-16. 25 | */ 26 | public class ConnectManage { 27 | 28 | private static final Logger LOGGER = LoggerFactory.getLogger(ConnectManage.class); 29 | private volatile static ConnectManage connectManage; 30 | 31 | EventLoopGroup eventLoopGroup = new NioEventLoopGroup(4); 32 | private static ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(16, 16, 600L, TimeUnit.SECONDS, new ArrayBlockingQueue(65536)); 33 | 34 | private CopyOnWriteArrayList connectedHandlers = new CopyOnWriteArrayList<>(); 35 | private Map connectedServerNodes = new ConcurrentHashMap<>(); 36 | //private Map connectedServerNodes = new ConcurrentHashMap<>(); 37 | 38 | private ReentrantLock lock = new ReentrantLock(); 39 | private Condition connected = lock.newCondition(); 40 | protected long connectTimeoutMillis = 6000; 41 | private AtomicInteger roundRobin = new AtomicInteger(0); 42 | private volatile boolean isRuning = true; 43 | 44 | private ConnectManage() { 45 | } 46 | 47 | public static ConnectManage getInstance() { 48 | if (connectManage == null) { 49 | synchronized (ConnectManage.class) { 50 | if (connectManage == null) { 51 | connectManage = new ConnectManage(); 52 | } 53 | } 54 | } 55 | return connectManage; 56 | } 57 | 58 | public void updateConnectedServer(List allServerAddress) { 59 | if (allServerAddress != null) { 60 | if (allServerAddress.size() > 0) { // Get available server node 61 | //update local serverNodes cache 62 | HashSet newAllServerNodeSet = new HashSet(); 63 | for (int i = 0; i < allServerAddress.size(); ++i) { 64 | String[] array = allServerAddress.get(i).split(":"); 65 | if (array.length == 2) { // Should check IP and port 66 | String host = array[0]; 67 | int port = Integer.parseInt(array[1]); 68 | final InetSocketAddress remotePeer = new InetSocketAddress(host, port); 69 | newAllServerNodeSet.add(remotePeer); 70 | } 71 | } 72 | 73 | // Add new server node 74 | for (final InetSocketAddress serverNodeAddress : newAllServerNodeSet) { 75 | if (!connectedServerNodes.keySet().contains(serverNodeAddress)) { 76 | connectServerNode(serverNodeAddress); 77 | } 78 | } 79 | 80 | // Close and remove invalid server nodes 81 | for (int i = 0; i < connectedHandlers.size(); ++i) { 82 | RpcClientHandler connectedServerHandler = connectedHandlers.get(i); 83 | SocketAddress remotePeer = connectedServerHandler.getRemotePeer(); 84 | if (!newAllServerNodeSet.contains(remotePeer)) { 85 | LOGGER.info("Remove invalid server node " + remotePeer); 86 | RpcClientHandler handler = connectedServerNodes.get(remotePeer); 87 | handler.close(); 88 | connectedServerNodes.remove(remotePeer); 89 | connectedHandlers.remove(connectedServerHandler); 90 | } 91 | } 92 | 93 | } else { // No available server node ( All server nodes are down ) 94 | LOGGER.error("No available server node. All server nodes are down !!!"); 95 | for (final RpcClientHandler connectedServerHandler : connectedHandlers) { 96 | SocketAddress remotePeer = connectedServerHandler.getRemotePeer(); 97 | RpcClientHandler handler = connectedServerNodes.get(remotePeer); 98 | handler.close(); 99 | connectedServerNodes.remove(connectedServerHandler); 100 | } 101 | connectedHandlers.clear(); 102 | } 103 | } 104 | } 105 | 106 | public void reconnect(final RpcClientHandler handler, final SocketAddress remotePeer){ 107 | if(handler!=null){ 108 | connectedHandlers.remove(handler); 109 | connectedServerNodes.remove(handler.getRemotePeer()); 110 | } 111 | connectServerNode((InetSocketAddress)remotePeer); 112 | } 113 | 114 | private void connectServerNode(final InetSocketAddress remotePeer) { 115 | threadPoolExecutor.submit(new Runnable() { 116 | @Override 117 | public void run() { 118 | Bootstrap b = new Bootstrap(); 119 | b.group(eventLoopGroup) 120 | .channel(NioSocketChannel.class) 121 | .handler(new RpcClientInitializer()); 122 | 123 | ChannelFuture channelFuture = b.connect(remotePeer); 124 | channelFuture.addListener(new ChannelFutureListener() { 125 | @Override 126 | public void operationComplete(final ChannelFuture channelFuture) throws Exception { 127 | if (channelFuture.isSuccess()) { 128 | LOGGER.debug("Successfully connect to remote server. remote peer = " + remotePeer); 129 | RpcClientHandler handler = channelFuture.channel().pipeline().get(RpcClientHandler.class); 130 | addHandler(handler); 131 | } 132 | } 133 | }); 134 | } 135 | }); 136 | } 137 | 138 | private void addHandler(RpcClientHandler handler) { 139 | connectedHandlers.add(handler); 140 | InetSocketAddress remoteAddress = (InetSocketAddress) handler.getChannel().remoteAddress(); 141 | connectedServerNodes.put(remoteAddress, handler); 142 | signalAvailableHandler(); 143 | } 144 | 145 | private void signalAvailableHandler() { 146 | lock.lock(); 147 | try { 148 | connected.signalAll(); 149 | } finally { 150 | lock.unlock(); 151 | } 152 | } 153 | 154 | private boolean waitingForHandler() throws InterruptedException { 155 | lock.lock(); 156 | try { 157 | return connected.await(this.connectTimeoutMillis, TimeUnit.MILLISECONDS); 158 | } finally { 159 | lock.unlock(); 160 | } 161 | } 162 | 163 | public RpcClientHandler chooseHandler() { 164 | CopyOnWriteArrayList handlers = (CopyOnWriteArrayList) this.connectedHandlers.clone(); 165 | int size = handlers.size(); 166 | while (isRuning && size <= 0) { 167 | try { 168 | boolean available = waitingForHandler(); 169 | if (available) { 170 | handlers = (CopyOnWriteArrayList) this.connectedHandlers.clone(); 171 | size = handlers.size(); 172 | } 173 | } catch (InterruptedException e) { 174 | LOGGER.error("Waiting for available node is interrupted! ", e); 175 | throw new RuntimeException("Can't connect any servers!", e); 176 | } 177 | } 178 | int index = (roundRobin.getAndAdd(1) + size) % size; 179 | return handlers.get(index); 180 | } 181 | 182 | public void stop(){ 183 | isRuning = false; 184 | for (int i = 0; i < connectedHandlers.size(); ++i) { 185 | RpcClientHandler connectedServerHandler = connectedHandlers.get(i); 186 | connectedServerHandler.close(); 187 | } 188 | signalAvailableHandler(); 189 | threadPoolExecutor.shutdown(); 190 | eventLoopGroup.shutdownGracefully(); 191 | } 192 | } 193 | --------------------------------------------------------------------------------