├── .gitignore ├── README.md ├── migo-core ├── pom.xml └── src │ └── main │ └── java │ └── com │ └── nia │ └── rpc │ └── core │ ├── bootstrap │ ├── ClientBuilder.java │ └── ServerBuilder.java │ ├── client │ ├── ChannelConf.java │ ├── Client.java │ ├── ClientImpl.java │ └── RpcClientHandler.java │ ├── exception │ └── RequestTimeoutException.java │ ├── protocol │ ├── Request.java │ ├── Response.java │ ├── RpcDecoder.java │ └── RpcEncoder.java │ ├── rpcproxy │ ├── CglibRpcProxy.java │ └── RpcProxy.java │ ├── serializer │ ├── KryoSerializer.java │ └── Serializer.java │ ├── server │ ├── RpcServerHandler.java │ ├── Server.java │ └── ServerImpl.java │ └── utils │ ├── ConnectionObjectFactory.java │ ├── Constant.java │ ├── NetUtils.java │ └── ResponseMapHelper.java ├── migo-example ├── pom.xml └── src │ └── main │ └── java │ └── com │ └── nia │ └── rpc │ └── example │ ├── RpcApplication.java │ ├── client │ └── SpringClientConfig.java │ ├── server │ └── SpringServerConfig.java │ └── service │ ├── HelloWorld.java │ └── HelloWorldImpl.java ├── migo-rpc-provider ├── pom.xml └── src │ └── main │ └── java │ └── com │ └── nia │ └── rpc │ └── factory │ ├── ClientFactoryBean.java │ └── ServerFactoryBean.java └── pom.xml /.gitignore: -------------------------------------------------------------------------------- 1 | # maven ignore 2 | target/ 3 | *.jar 4 | *.war 5 | *.zip 6 | *.tar 7 | *.tar.gz 8 | 9 | # eclipse ignore 10 | .settings/ 11 | .project 12 | .classpath 13 | 14 | # idea ignore 15 | .idea/ 16 | *.ipr 17 | *.iml 18 | *.iws 19 | 20 | # temp ignore 21 | *.log 22 | *.cache 23 | *.diff 24 | *.patch 25 | *.tmp 26 | 27 | # system ignore 28 | .DS_Store 29 | <<<<<<< HEAD 30 | Thumbs.db 31 | ======= 32 | Thumbs.db 33 | >>>>>>> e704ead569657bdb826d1d159a140d2c8278d779 34 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## 此项目的开发细节 请参考以下博文: 2 | 3 | [一个轻量级分布式 RPC 框架 上](https://muyinchen.github.io/2017/03/16/%E8%BD%BB%E9%87%8F%E7%BA%A7%E5%88%86%E5%B8%83%E5%BC%8F%20RPC%20%E6%A1%86%E6%9E%B6%20%E4%B8%8A/) 4 | 5 | [一个轻量级分布式 RPC 框架 下](https://muyinchen.github.io/2017/03/27/%E4%B8%80%E4%B8%AA%E8%BD%BB%E9%87%8F%E7%BA%A7%E5%88%86%E5%B8%83%E5%BC%8F%20RPC%20%E6%A1%86%E6%9E%B6%20%E4%B8%8B/) 6 | 7 | 8 | ## 示例使用 9 | 10 | #### 这里我们通过一个`SpringbootDemo`来演示如何使用: 11 | 12 | 具体代码结构请看源码 13 | 14 | ##### 定义一个测试service接口: 15 | 16 | ```java 17 | package com.nia.rpc.example.service; 18 | 19 | /** 20 | * Author 知秋 21 | * Created by Auser on 2017/2/19. 22 | */ 23 | public interface HelloWorld { 24 | String say(String hello); 25 | 26 | int sum(int a, int b); 27 | int max(Integer a, Integer b); 28 | } 29 | 30 | ``` 31 | 32 | ##### 编写其实现类: 33 | 34 | ```java 35 | package com.nia.rpc.example.service; 36 | 37 | /** 38 | * Author 知秋 39 | * Created by Auser on 2017/2/19. 40 | */ 41 | public class HelloWorldImpl implements HelloWorld { 42 | @Override 43 | public String say(String hello) { 44 | return "server: "+hello; 45 | } 46 | 47 | @Override 48 | public int sum(int a, int b) { 49 | return a+b; 50 | } 51 | 52 | @Override 53 | public int max(Integer a, Integer b) { 54 | return a <= b ? b : a; 55 | } 56 | } 57 | 58 | ``` 59 | 60 | 61 | 62 | ##### 编写`Springboot`服务端启动类: 63 | 64 | ###### `SpringServerConfig` 65 | 66 | ```java 67 | package com.nia.rpc.example.server; 68 | 69 | import com.nia.rpc.core.utils.NetUtils; 70 | import com.nia.rpc.example.service.HelloWorld; 71 | import com.nia.rpc.example.service.HelloWorldImpl; 72 | import com.nia.rpc.factory.ServerFactoryBean; 73 | import org.springframework.boot.SpringApplication; 74 | import org.springframework.boot.autoconfigure.SpringBootApplication; 75 | import org.springframework.context.annotation.Bean; 76 | 77 | /** 78 | * Author 知秋 79 | * Created by Auser on 2017/2/19. 80 | */ 81 | @SpringBootApplication 82 | public class SpringServerConfig { 83 | @Bean 84 | public HelloWorld hello() { 85 | return new HelloWorldImpl(); 86 | } 87 | 88 | @Bean 89 | public ServerFactoryBean serverFactoryBean() { 90 | final ServerFactoryBean serverFactoryBean = new ServerFactoryBean(); 91 | serverFactoryBean.setPort(9090); 92 | serverFactoryBean.setServiceInterface(HelloWorld.class); 93 | //此处自定义的注册名字就相当于注解了,未来迭代的时候会加入自定义注解方式 94 | serverFactoryBean.setServiceName("hello"); 95 | serverFactoryBean.setServiceImpl(hello()); 96 | serverFactoryBean.setZkConn("127.0.0.1:2181"); 97 | 98 | new Thread(() -> { 99 | try { 100 | serverFactoryBean.start(); 101 | } catch (Exception e) { 102 | e.printStackTrace(); 103 | } 104 | }, "RpcServer").start(); 105 | return serverFactoryBean; 106 | } 107 | 108 | public static void main(String[] args) { 109 | 110 | SpringApplication.run(SpringServerConfig.class, "--server.port=8082"); 111 | } 112 | } 113 | 114 | ``` 115 | 116 | 117 | 118 | ##### 编写服务调用端启动类: 119 | 120 | ###### `SpringClientConfig`: 121 | 122 | ```java 123 | package com.nia.rpc.example.client; 124 | 125 | import com.nia.rpc.example.service.HelloWorld; 126 | import com.nia.rpc.factory.ClientFactoryBean; 127 | import org.springframework.boot.SpringApplication; 128 | import org.springframework.boot.autoconfigure.SpringBootApplication; 129 | import org.springframework.context.annotation.Bean; 130 | import org.springframework.context.annotation.Configuration; 131 | import org.springframework.web.bind.annotation.RequestMapping; 132 | import org.springframework.web.bind.annotation.RestController; 133 | 134 | import javax.annotation.Resource; 135 | 136 | /** 137 | * Author 知秋 138 | * Created by Auser on 2017/2/19. 139 | */ 140 | @Configuration 141 | @RestController 142 | @SpringBootApplication 143 | @RequestMapping("/test") 144 | public class SpringClientConfig { 145 | @Bean 146 | public HelloWorld clientFactoryBean() throws Exception { 147 | ClientFactoryBean clientFactoryBean = new ClientFactoryBean<>(); 148 | clientFactoryBean.setZkConn("127.0.0.1:2181"); 149 | clientFactoryBean.setServiceName("hello"); 150 | clientFactoryBean.setServiceInterface(HelloWorld.class); 151 | return clientFactoryBean.getObject(); 152 | } 153 | @Resource 154 | private HelloWorld helloWorld; 155 | 156 | @RequestMapping("/hello") 157 | public String hello(String say) { 158 | return helloWorld.say(say); 159 | } 160 | public static void main(String[] args) { 161 | SpringApplication.run(SpringClientConfig.class, "--server.port=8081"); 162 | } 163 | } 164 | 165 | ``` 166 | 167 | 测试截图: 168 | 169 | ![](http://og0sybnix.bkt.clouddn.com/sp170220_022654.png) -------------------------------------------------------------------------------- /migo-core/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | parent 7 | com.nia.rpc 8 | 1.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | migo-core 13 | 14 | 15 | 16 | 17 | org.slf4j 18 | slf4j-api 19 | 20 | 21 | cglib 22 | cglib 23 | 24 | 25 | io.netty 26 | netty-all 27 | 28 | 29 | org.projectlombok 30 | lombok 31 | 32 | 33 | 34 | org.apache.commons 35 | commons-pool2 36 | 37 | 38 | 39 | io.protostuff 40 | protostuff-core 41 | 42 | 43 | io.protostuff 44 | protostuff-runtime 45 | 46 | 47 | org.apache.zookeeper 48 | zookeeper 49 | 50 | 51 | slf4j-log4j12 52 | org.slf4j 53 | 54 | 55 | log4j 56 | log4j 57 | 58 | 59 | 60 | 61 | org.apache.curator 62 | curator-framework 63 | 64 | 65 | org.apache.curator 66 | curator-recipes 67 | 68 | 69 | org.apache.curator 70 | curator-x-discovery 71 | 72 | 73 | org.apache.commons 74 | commons-collections4 75 | 76 | 77 | junit 78 | junit 79 | test 80 | 81 | 82 | org.slf4j 83 | slf4j-simple 84 | test 85 | 86 | 87 | com.esotericsoftware 88 | kryo 89 | 90 | 91 | -------------------------------------------------------------------------------- /migo-core/src/main/java/com/nia/rpc/core/bootstrap/ClientBuilder.java: -------------------------------------------------------------------------------- 1 | package com.nia.rpc.core.bootstrap; 2 | 3 | import com.google.common.base.Preconditions; 4 | import com.nia.rpc.core.client.ClientImpl; 5 | import com.nia.rpc.core.rpcproxy.CglibRpcProxy; 6 | import com.nia.rpc.core.rpcproxy.RpcProxy; 7 | 8 | /** 9 | * Author 知秋 10 | * Created by Auser on 2017/2/19. 11 | */ 12 | public class ClientBuilder { 13 | 14 | private String serviceName; 15 | private String zkConn; 16 | private Class serviceInterface; 17 | private int requestTimeoutMillis = 10000; 18 | private Class clientProxyClass = CglibRpcProxy.class; 19 | 20 | public static ClientBuilder builder() { 21 | return new ClientBuilder<>(); 22 | } 23 | 24 | public ClientBuilder serviceName(String serviceName) { 25 | this.serviceName = serviceName; 26 | return this; 27 | } 28 | 29 | public ClientBuilder zkConn(String zkConn) { 30 | this.zkConn = zkConn; 31 | return this; 32 | } 33 | 34 | public ClientBuilder serviceInterface(Class serviceInterface) { 35 | this.serviceInterface = serviceInterface; 36 | return this; 37 | } 38 | 39 | public ClientBuilder requestTimeout(int requestTimeoutMillis) { 40 | this.requestTimeoutMillis = requestTimeoutMillis; 41 | return this; 42 | } 43 | 44 | public ClientBuilder clientProxyClass(Class clientProxyClass) { 45 | this.clientProxyClass = clientProxyClass; 46 | return this; 47 | } 48 | 49 | public T build() { 50 | //因Curator底层依赖guava,刚好可以拿来验证 51 | Preconditions.checkNotNull(serviceInterface); 52 | Preconditions.checkNotNull(zkConn); 53 | Preconditions.checkNotNull(serviceName); 54 | ClientImpl client = new ClientImpl(this.serviceName); 55 | client.setZkConn(this.zkConn); 56 | client.setRequestTimeoutMillis(this.requestTimeoutMillis); 57 | client.init(); 58 | return client.proxyInterface(this.serviceInterface); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /migo-core/src/main/java/com/nia/rpc/core/bootstrap/ServerBuilder.java: -------------------------------------------------------------------------------- 1 | package com.nia.rpc.core.bootstrap; 2 | 3 | import com.google.common.base.Preconditions; 4 | import com.nia.rpc.core.server.Server; 5 | import com.nia.rpc.core.server.ServerImpl; 6 | 7 | /** 8 | * Author 知秋 9 | * Created by Auser on 2017/2/19. 10 | */ 11 | public class ServerBuilder { 12 | private int port; 13 | private String serviceName; 14 | private Object serviceImpl; 15 | private String zkConn; 16 | 17 | private ServerBuilder() {} 18 | 19 | public static ServerBuilder builder() { 20 | return new ServerBuilder(); 21 | } 22 | 23 | public ServerBuilder port(int port) { 24 | this.port = port; 25 | return this; 26 | } 27 | 28 | public ServerBuilder serviceName(String serviceName) { 29 | this.serviceName = serviceName; 30 | return this; 31 | } 32 | 33 | public ServerBuilder serviceImpl(Object serviceImpl) { 34 | this.serviceImpl = serviceImpl; 35 | return this; 36 | } 37 | 38 | public ServerBuilder zkConn(String zkConn) { 39 | this.zkConn = zkConn; 40 | return this; 41 | } 42 | 43 | public Server build() { 44 | Preconditions.checkNotNull(serviceImpl); 45 | Preconditions.checkNotNull(serviceName); 46 | Preconditions.checkNotNull(zkConn); 47 | Preconditions.checkArgument(port > 0); 48 | return new ServerImpl(this.port, this.serviceImpl, this.serviceName, this.zkConn); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /migo-core/src/main/java/com/nia/rpc/core/client/ChannelConf.java: -------------------------------------------------------------------------------- 1 | package com.nia.rpc.core.client; 2 | 3 | import com.nia.rpc.core.utils.ConnectionObjectFactory; 4 | import io.netty.channel.Channel; 5 | import lombok.Data; 6 | import org.apache.commons.pool2.ObjectPool; 7 | import org.apache.commons.pool2.impl.GenericObjectPool; 8 | 9 | /** 10 | * Author 知秋 11 | * Created by Auser on 2017/2/18. 12 | */ 13 | @Data 14 | public class ChannelConf { 15 | private String connStr; 16 | private String host; 17 | private int ip; 18 | private Channel channel; 19 | private ObjectPool channelObjectPool; 20 | 21 | public ChannelConf(String host, int port) { 22 | this.host = host; 23 | this.ip = port; 24 | this.connStr = host + ":" + ip; 25 | channelObjectPool = new GenericObjectPool<>(new ConnectionObjectFactory(host, port)); 26 | } 27 | 28 | public void close() { 29 | channelObjectPool.close(); 30 | } 31 | @Override 32 | public String toString() { 33 | final StringBuilder sb = new StringBuilder("ChannelConf{"); 34 | sb.append("connStr='").append(connStr).append('\''); 35 | sb.append(", host='").append(host).append('\''); 36 | sb.append(", ip=").append(ip); 37 | sb.append(", channel=").append(channel); 38 | sb.append(", channelObjectPool=").append(channelObjectPool); 39 | sb.append('}'); 40 | return sb.toString(); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /migo-core/src/main/java/com/nia/rpc/core/client/Client.java: -------------------------------------------------------------------------------- 1 | package com.nia.rpc.core.client; 2 | 3 | import com.nia.rpc.core.protocol.Response; 4 | 5 | import java.lang.reflect.Method; 6 | 7 | /** 8 | * 服务的发现与使用 9 | * 10 | * Author 知秋 11 | * Created by Auser on 2017/2/18. 12 | */ 13 | public interface Client { 14 | Response sendMessage(Class clazz, Method method, Object[] args); 15 | T proxyInterface(Class serviceInterface); 16 | void close(); 17 | } 18 | -------------------------------------------------------------------------------- /migo-core/src/main/java/com/nia/rpc/core/client/ClientImpl.java: -------------------------------------------------------------------------------- 1 | package com.nia.rpc.core.client; 2 | 3 | import com.google.common.base.Splitter; 4 | import com.nia.rpc.core.exception.RequestTimeoutException; 5 | import com.nia.rpc.core.protocol.Request; 6 | import com.nia.rpc.core.protocol.Response; 7 | import com.nia.rpc.core.rpcproxy.CglibRpcProxy; 8 | import com.nia.rpc.core.rpcproxy.RpcProxy; 9 | import com.nia.rpc.core.utils.ResponseMapHelper; 10 | import io.netty.channel.Channel; 11 | import io.netty.channel.EventLoopGroup; 12 | import io.netty.channel.nio.NioEventLoopGroup; 13 | import org.apache.commons.collections4.CollectionUtils; 14 | import org.apache.curator.framework.CuratorFramework; 15 | import org.apache.curator.framework.CuratorFrameworkFactory; 16 | import org.apache.curator.framework.api.GetChildrenBuilder; 17 | import org.apache.curator.framework.recipes.cache.PathChildrenCache; 18 | import org.apache.curator.retry.ExponentialBackoffRetry; 19 | import org.slf4j.Logger; 20 | import org.slf4j.LoggerFactory; 21 | 22 | import java.lang.reflect.Method; 23 | import java.util.List; 24 | import java.util.Random; 25 | import java.util.concurrent.ArrayBlockingQueue; 26 | import java.util.concurrent.BlockingQueue; 27 | import java.util.concurrent.CopyOnWriteArrayList; 28 | import java.util.concurrent.TimeUnit; 29 | import java.util.concurrent.atomic.AtomicLong; 30 | 31 | import static com.nia.rpc.core.utils.Constant.ZK_DATA_PATH; 32 | 33 | /** 34 | * Author 知秋 35 | * Created by Auser on 2017/2/18. 36 | */ 37 | public class ClientImpl implements Client{ 38 | private static final Logger LOGGER = LoggerFactory.getLogger(ClientImpl.class); 39 | 40 | private static AtomicLong atomicLong = new AtomicLong(); 41 | // 通过此发布的服务名称,来寻找对应的服务提供者 42 | private String serviceName; 43 | private int requestTimeoutMillis = 10 * 1000; 44 | private EventLoopGroup eventLoopGroup = new NioEventLoopGroup(2); 45 | private String zkConn; 46 | private CuratorFramework curatorFramework; 47 | private Class clientProxyClass; 48 | private RpcProxy rpcProxy; 49 | 50 | // 存放ChannelConf到一个CopyOnWriteArrayList中,这个本就是读多写少的场景(服务注册后很少会发生状态改变),所以很适用 51 | public static CopyOnWriteArrayList channelWrappers = new CopyOnWriteArrayList<>(); 52 | 53 | public ClientImpl(String serviceName) { 54 | this.serviceName = serviceName; 55 | } 56 | 57 | public void init() { 58 | 59 | 60 | // 注册中心不可用时,保存本地缓存 61 | 62 | curatorFramework = CuratorFrameworkFactory.newClient(getZkConn(), new ExponentialBackoffRetry(1000, 3)); 63 | curatorFramework.start(); 64 | 65 | 66 | final GetChildrenBuilder children = curatorFramework.getChildren(); 67 | try { 68 | final String serviceZKPath = ZK_DATA_PATH + serviceName; 69 | //通过curator API 的Path Cache用来监控一个ZNode的子节点. 70 | // 当一个子节点增加, 更新,删除时, Path Cache会改变它的状态, 71 | // 会包含最新的子节点, 子节点的数据和状态。 72 | // 这也正如它的名字表示的那样, 那监控path。 73 | PathChildrenCache pathChildrenCache = new PathChildrenCache(curatorFramework, serviceZKPath, true); 74 | pathChildrenCache.start(); 75 | 76 | pathChildrenCache.getListenable().addListener((client, event) -> { 77 | LOGGER.info("Listen Event {}", event); 78 | //通过路径拿到此节点下可以提供服务的实现类节点连接地址 79 | List newServiceData = children.forPath(serviceZKPath); 80 | LOGGER.info("Server {} list change {}", serviceName, newServiceData); 81 | 82 | // 关闭删除本地缓存中多出的channel 83 | 84 | for (ChannelConf cw : channelWrappers) { 85 | String connStr = cw.getConnStr(); 86 | if (!newServiceData.contains(connStr)) { 87 | cw.close(); 88 | LOGGER.info("Remove channel {}", connStr); 89 | channelWrappers.remove(cw); 90 | } 91 | } 92 | 93 | // 增加本地缓存中不存在的连接地址 94 | for (String connStr : newServiceData) { 95 | boolean containThis = false; 96 | for (ChannelConf cw : channelWrappers) { 97 | if (connStr != null && connStr.equals(cw.getConnStr())) { 98 | containThis = true; 99 | } 100 | } 101 | if (!containThis) { 102 | addNewChannel(connStr); 103 | } 104 | } 105 | }); 106 | 107 | List strings = children.forPath(serviceZKPath); 108 | if (CollectionUtils.isEmpty(strings)) { 109 | throw new RuntimeException("No Service available for " + serviceName); 110 | } 111 | 112 | LOGGER.info("Found Server {} List {}", serviceName, strings); 113 | for (String connStr : strings) { 114 | try { 115 | addNewChannel(connStr); 116 | } catch (Exception e) { 117 | LOGGER.error("Add New Channel Exception", e); 118 | } 119 | } 120 | 121 | } catch (Exception e) { 122 | e.printStackTrace(); 123 | } 124 | } 125 | 126 | 127 | private void addNewChannel(String connStr) { 128 | try { 129 | List strings = Splitter.on(":").splitToList(connStr); 130 | if (strings.size() != 2) { 131 | throw new RuntimeException("Error connection str " + connStr); 132 | } 133 | String host = strings.get(0); 134 | int port = Integer.parseInt(strings.get(1)); 135 | ChannelConf channelWrapper = new ChannelConf(host, port); 136 | channelWrappers.add(channelWrapper); 137 | LOGGER.info("Add New Channel {}, {}", connStr, channelWrapper); 138 | } catch (Exception e) { 139 | e.printStackTrace(); 140 | } 141 | } 142 | 143 | 144 | private ChannelConf selectChannel() { 145 | Random random = new Random(); 146 | //同一个服务下有好几个链接地址的实现,那就选一个就是,其实为集群部署考虑, 147 | // 每一台服务器部署有相同的服务,选择其一来处理即可,假如是nginx代理那就无所谓了 148 | int size = channelWrappers.size(); 149 | if (size < 1) { 150 | return null; 151 | } 152 | int i = random.nextInt(size); 153 | return channelWrappers.get(i); 154 | } 155 | 156 | @Override 157 | public Response sendMessage(Class clazz, Method method, Object[] args) { 158 | 159 | Request request = new Request(); 160 | request.setRequestId(atomicLong.incrementAndGet()); 161 | request.setMethod(method.getName()); 162 | request.setParams(args); 163 | request.setClazz(clazz); 164 | request.setParameterTypes(method.getParameterTypes()); 165 | 166 | ChannelConf channelWrapper = selectChannel(); 167 | 168 | if (channelWrapper == null) { 169 | Response response = new Response(); 170 | RuntimeException runtimeException = new RuntimeException("Channel is not active now"); 171 | response.setThrowable(runtimeException); 172 | return response; 173 | } 174 | //当channel的配置链接不为空的时候,就可以取到channel了 175 | Channel channel = null; 176 | try { 177 | channel = channelWrapper.getChannelObjectPool().borrowObject(); 178 | } catch (Exception e) { 179 | e.printStackTrace(); 180 | } 181 | if (channel == null) { 182 | Response response = new Response(); 183 | RuntimeException runtimeException = new RuntimeException("Channel is not available now"); 184 | response.setThrowable(runtimeException); 185 | return response; 186 | } 187 | 188 | 189 | try { 190 | channel.writeAndFlush(request); 191 | //建立一个ResponseMap,将RequestId作为键,服务端回应的内容作为值保存于BlockingQueue, 192 | // 最后一起保存在这个ResponseMap中 193 | BlockingQueue blockingQueue = new ArrayBlockingQueue<>(1); 194 | ResponseMapHelper.responseMap.put(request.getRequestId(), blockingQueue); 195 | //poll(time):取走BlockingQueue里排在首位的对象,若不能立即取出,则可以等time参数规定的时间,取不到时返回null 196 | 197 | return blockingQueue.poll(requestTimeoutMillis, TimeUnit.MILLISECONDS); 198 | } catch (InterruptedException e) { 199 | throw new RequestTimeoutException("service" + serviceName + " method " + method + " timeout"); 200 | } finally { 201 | try { 202 | //拿出去的channel记得还回去 203 | channelWrapper.getChannelObjectPool().returnObject(channel); 204 | } catch (Exception e) { 205 | e.printStackTrace(); 206 | } 207 | //删除此键值对,help GC 208 | ResponseMapHelper.responseMap.remove(request.getRequestId()); 209 | } 210 | } 211 | 212 | @Override 213 | public T proxyInterface(Class serviceInterface) { 214 | if (clientProxyClass == null) { 215 | clientProxyClass = CglibRpcProxy.class; 216 | } 217 | try { 218 | rpcProxy = clientProxyClass.newInstance(); 219 | } catch (InstantiationException | IllegalAccessException e) { 220 | e.printStackTrace(); 221 | } 222 | return rpcProxy.proxyInterface(this, serviceInterface); 223 | } 224 | 225 | @Override 226 | public void close() { 227 | //注意要关三处地方,一个是先关闭zookeeper的连接,另一个是channel池对象,最后是netty的断开关闭 228 | if (curatorFramework != null) { 229 | curatorFramework.close(); 230 | } 231 | try { 232 | for (ChannelConf cw : channelWrappers) { 233 | cw.close(); 234 | } 235 | } finally { 236 | eventLoopGroup.shutdownGracefully(); 237 | } 238 | } 239 | 240 | public String getZkConn() { 241 | return zkConn; 242 | } 243 | 244 | public void setZkConn(String zkConn) { 245 | this.zkConn = zkConn; 246 | } 247 | 248 | public int getRequestTimeoutMillis() { 249 | return requestTimeoutMillis; 250 | } 251 | 252 | public void setRequestTimeoutMillis(int requestTimeoutMillis) { 253 | this.requestTimeoutMillis = requestTimeoutMillis; 254 | } 255 | } 256 | -------------------------------------------------------------------------------- /migo-core/src/main/java/com/nia/rpc/core/client/RpcClientHandler.java: -------------------------------------------------------------------------------- 1 | package com.nia.rpc.core.client; 2 | 3 | import com.nia.rpc.core.protocol.Response; 4 | import io.netty.channel.ChannelHandler; 5 | import io.netty.channel.ChannelHandlerContext; 6 | import io.netty.channel.SimpleChannelInboundHandler; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | import java.util.concurrent.BlockingQueue; 11 | 12 | import static com.nia.rpc.core.utils.ResponseMapHelper.responseMap; 13 | 14 | /** 15 | * Author 知秋 16 | * Created by Auser on 2017/2/18. 17 | */ 18 | @ChannelHandler.Sharable/*因为要在不同channel中共享使用responseMap的blockingQueue,所以要加此注解*/ 19 | public class RpcClientHandler extends SimpleChannelInboundHandler { 20 | private static final Logger LOGGER = LoggerFactory.getLogger(RpcClientHandler.class); 21 | 22 | //因为此处这个要公用,故拿出来单独放到一个类中来调用 23 | // public static ConcurrentMap> responseMap = new ConcurrentHashMap>(); 24 | 25 | @Override 26 | protected void channelRead0(ChannelHandlerContext ctx, Response msg) throws Exception { 27 | //此处的业务逻辑就是拿到对应id,讲返回信息放入相应blockingQueue中 28 | BlockingQueue blockingQueue = responseMap.get(msg.getRequestId()); 29 | if (blockingQueue != null) { 30 | blockingQueue.put(msg); 31 | } 32 | 33 | } 34 | 35 | @Override 36 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { 37 | LOGGER.error("Exception caught on {}, ", ctx.channel(), cause); 38 | ctx.channel().close(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /migo-core/src/main/java/com/nia/rpc/core/exception/RequestTimeoutException.java: -------------------------------------------------------------------------------- 1 | package com.nia.rpc.core.exception; 2 | 3 | /** 4 | * Author 知秋 5 | * Created by Auser on 2017/2/18. 6 | */ 7 | public class RequestTimeoutException extends RuntimeException { 8 | 9 | public RequestTimeoutException(String message) { 10 | super(message); 11 | } 12 | 13 | public RequestTimeoutException(Throwable cause) { 14 | super(cause); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /migo-core/src/main/java/com/nia/rpc/core/protocol/Request.java: -------------------------------------------------------------------------------- 1 | package com.nia.rpc.core.protocol; 2 | 3 | import lombok.Data; 4 | 5 | /** 6 | * Author 知秋 7 | * Created by Auser on 2017/2/17. 8 | */ 9 | @Data 10 | public class Request { 11 | private long requestId; 12 | private Class clazz; 13 | private String method; 14 | private Class[] parameterTypes; 15 | private Object[] params; 16 | private long requestTime; 17 | 18 | } 19 | -------------------------------------------------------------------------------- /migo-core/src/main/java/com/nia/rpc/core/protocol/Response.java: -------------------------------------------------------------------------------- 1 | package com.nia.rpc.core.protocol; 2 | 3 | import lombok.Getter; 4 | import lombok.Setter; 5 | 6 | /** 7 | * Author 知秋 8 | * Created by Auser on 2017/2/17. 9 | */ 10 | @Setter 11 | @Getter 12 | public class Response { 13 | private long requestId; 14 | private Object response; 15 | private Throwable throwable; 16 | } 17 | -------------------------------------------------------------------------------- /migo-core/src/main/java/com/nia/rpc/core/protocol/RpcDecoder.java: -------------------------------------------------------------------------------- 1 | package com.nia.rpc.core.protocol; 2 | 3 | import com.nia.rpc.core.serializer.KryoSerializer; 4 | import com.nia.rpc.core.serializer.Serializer; 5 | import io.netty.buffer.ByteBuf; 6 | import io.netty.channel.ChannelHandlerContext; 7 | import io.netty.handler.codec.LengthFieldBasedFrameDecoder; 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | 11 | /** 12 | * Author 知秋 13 | * Created by Auser on 2017/2/17. 14 | */ 15 | //常用的处理大数据分包传输问题的解决类:LengthFieldBasedFrameDecoder 16 | public class RpcDecoder extends LengthFieldBasedFrameDecoder { 17 | private static final Logger LOGGER = LoggerFactory.getLogger(RpcDecoder.class); 18 | private Serializer serializer = new KryoSerializer(); 19 | 20 | public RpcDecoder(int maxFrameLength) { 21 | super(maxFrameLength, 0, 4, 0, 4); 22 | } 23 | 24 | @Override 25 | protected Object decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception { 26 | ByteBuf decode = (ByteBuf) super.decode(ctx, in); 27 | if (decode != null) { 28 | int byteLength = decode.readableBytes(); 29 | byte[] byteHolder = new byte[byteLength]; 30 | decode.readBytes(byteHolder); 31 | Object deserialize = serializer.deserialize(byteHolder); 32 | return deserialize; 33 | } 34 | LOGGER.debug("Decoder Result is null"); 35 | return null; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /migo-core/src/main/java/com/nia/rpc/core/protocol/RpcEncoder.java: -------------------------------------------------------------------------------- 1 | package com.nia.rpc.core.protocol; 2 | 3 | import com.nia.rpc.core.serializer.KryoSerializer; 4 | import com.nia.rpc.core.serializer.Serializer; 5 | import io.netty.buffer.ByteBuf; 6 | import io.netty.channel.ChannelHandlerContext; 7 | import io.netty.handler.codec.MessageToByteEncoder; 8 | 9 | /** 10 | * Author 知秋 11 | * Created by Auser on 2017/2/17. 12 | */ 13 | public class RpcEncoder extends MessageToByteEncoder { 14 | private Serializer serializer = new KryoSerializer(); 15 | 16 | @Override 17 | protected void encode(ChannelHandlerContext channelHandlerContext, Object msg, ByteBuf out) throws Exception { 18 | 19 | byte[] bytes = serializer.serialize(msg); 20 | int length = bytes.length; 21 | out.writeInt(length); 22 | out.writeBytes(bytes); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /migo-core/src/main/java/com/nia/rpc/core/rpcproxy/CglibRpcProxy.java: -------------------------------------------------------------------------------- 1 | package com.nia.rpc.core.rpcproxy; 2 | 3 | import com.nia.rpc.core.client.Client; 4 | import net.sf.cglib.proxy.Enhancer; 5 | import net.sf.cglib.proxy.MethodInterceptor; 6 | import net.sf.cglib.proxy.MethodProxy; 7 | 8 | import java.lang.reflect.Method; 9 | 10 | /** 11 | * Author 知秋 12 | * Created by Auser on 2017/2/18. 13 | */ 14 | public class CglibRpcProxy implements RpcProxy { 15 | 16 | @Override 17 | public T proxyInterface(Client client, Class serviceInterface) { 18 | Enhancer enhancer = new Enhancer(); 19 | enhancer.setSuperclass(serviceInterface); 20 | enhancer.setCallback(new CglibInteceptor(client, serviceInterface)); 21 | Object enhancedObject = enhancer.create(); 22 | return (T)enhancedObject; 23 | } 24 | /** 25 | * 搞个静态内部类来做Method的cglib代理 26 | */ 27 | private static class CglibInteceptor implements MethodInterceptor { 28 | 29 | //首先判断所要代理的方法是通用方法,是的话就此返回此代理对象的相关内容 30 | 31 | private static Method hashCodeMethod; 32 | private static Method equalsMethod; 33 | private static Method toStringMethod; 34 | 35 | static { 36 | try { 37 | hashCodeMethod = Object.class.getMethod("hashCode"); 38 | equalsMethod = Object.class.getMethod("equals", Object.class); 39 | toStringMethod = Object.class.getMethod("toString"); 40 | } catch (NoSuchMethodException e) { 41 | throw new NoSuchMethodError(e.getMessage()); 42 | } 43 | } 44 | 45 | /** 46 | * 针对这几个方法做相应的策略 47 | * @param proxy 48 | * @return 49 | */ 50 | private int proxyHashCode(Object proxy) { 51 | return System.identityHashCode(proxy); 52 | } 53 | 54 | private boolean proxyEquals(Object proxy, Object other) { 55 | return (proxy == other); 56 | } 57 | 58 | private String proxyToString(Object proxy) { 59 | return proxy.getClass().getName() + '@' + Integer.toHexString(proxy.hashCode()); 60 | } 61 | 62 | /** 63 | * 加入rpc客户端和传入所调用服务的接口 64 | */ 65 | private Client client; 66 | 67 | private Class serviceInterface; 68 | public CglibInteceptor(Client client, Class serviceInterface) { 69 | this.client = client; 70 | this.serviceInterface = serviceInterface; 71 | } 72 | 73 | @Override 74 | public Object intercept(Object o, Method method, Object[] args, MethodProxy proxy) throws Throwable { 75 | //先对方法进行判断是否是通用方法,假如都不是,最后再通过client来调用 76 | if (hashCodeMethod.equals(method)) { 77 | return proxyHashCode(proxy); 78 | } 79 | if (equalsMethod.equals(method)) { 80 | return proxyEquals(proxy, args[0]); 81 | } 82 | if (toStringMethod.equals(method)) { 83 | return proxyToString(proxy); 84 | } 85 | return client.sendMessage(serviceInterface, method, args).getResponse(); 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /migo-core/src/main/java/com/nia/rpc/core/rpcproxy/RpcProxy.java: -------------------------------------------------------------------------------- 1 | package com.nia.rpc.core.rpcproxy; 2 | 3 | import com.nia.rpc.core.client.Client; 4 | 5 | /** 6 | * Author 知秋 7 | * Created by Auser on 2017/2/18. 8 | */ 9 | public interface RpcProxy { 10 | T proxyInterface(Client client, final Class serviceInterface); 11 | } 12 | -------------------------------------------------------------------------------- /migo-core/src/main/java/com/nia/rpc/core/serializer/KryoSerializer.java: -------------------------------------------------------------------------------- 1 | package com.nia.rpc.core.serializer; 2 | 3 | import com.esotericsoftware.kryo.Kryo; 4 | import com.esotericsoftware.kryo.io.Input; 5 | import com.esotericsoftware.kryo.io.Output; 6 | 7 | import java.io.ByteArrayInputStream; 8 | import java.io.ByteArrayOutputStream; 9 | 10 | /** 11 | * Author 知秋 12 | * Created by Auser on 2017/2/17. 13 | */ 14 | public class KryoSerializer implements Serializer { 15 | @Override 16 | public byte[] serialize(Object obj) { 17 | Kryo kryo=new Kryo(); 18 | ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); 19 | Output output = new Output(byteArrayOutputStream); 20 | kryo.writeClassAndObject(output,obj); 21 | output.close(); 22 | return byteArrayOutputStream.toByteArray(); 23 | } 24 | 25 | @Override 26 | public T deserialize(byte[] bytes) { 27 | Kryo kryo=new Kryo(); 28 | ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes); 29 | Input input = new Input(byteArrayInputStream); 30 | input.close(); 31 | 32 | return (T) kryo.readClassAndObject(input); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /migo-core/src/main/java/com/nia/rpc/core/serializer/Serializer.java: -------------------------------------------------------------------------------- 1 | package com.nia.rpc.core.serializer; 2 | 3 | /** 4 | * Author 知秋 5 | * Created by Auser on 2017/2/17. 6 | */ 7 | public interface Serializer { 8 | byte[] serialize(Object obj); 9 | T deserialize(byte[] bytes); 10 | } 11 | -------------------------------------------------------------------------------- /migo-core/src/main/java/com/nia/rpc/core/server/RpcServerHandler.java: -------------------------------------------------------------------------------- 1 | package com.nia.rpc.core.server; 2 | 3 | import com.nia.rpc.core.protocol.Request; 4 | import com.nia.rpc.core.protocol.Response; 5 | import io.netty.channel.ChannelHandlerContext; 6 | import io.netty.channel.SimpleChannelInboundHandler; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | import java.lang.reflect.Method; 11 | 12 | /** 13 | * Author 知秋 14 | * Created by Auser on 2017/2/17. 15 | */ 16 | public class RpcServerHandler extends SimpleChannelInboundHandler { 17 | private static final Logger LOGGER = LoggerFactory.getLogger(RpcServerHandler.class); 18 | 19 | private Object service; 20 | 21 | //此处传入service的实现类对象 22 | public RpcServerHandler(Object service) { 23 | this.service = service; 24 | } 25 | 26 | 27 | protected void channelRead0(ChannelHandlerContext channelHandlerContext, Request msg) throws Exception { 28 | 29 | 30 | String methodName = msg.getMethod(); 31 | Object[] params = msg.getParams(); 32 | Class[] parameterTypes = msg.getParameterTypes(); 33 | long requestId = msg.getRequestId(); 34 | //通过反射来获取客户端所要调用的方法并执行 35 | Method method = service.getClass().getDeclaredMethod(methodName, parameterTypes); 36 | method.setAccessible(true); 37 | Object invoke = method.invoke(service, params); 38 | Response response = new Response(); 39 | response.setRequestId(requestId); 40 | response.setResponse(invoke); 41 | channelHandlerContext.pipeline().writeAndFlush(response); 42 | } 43 | 44 | @Override 45 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { 46 | LOGGER.error("Exception caught on {}, ", ctx.channel(), cause); 47 | ctx.channel().close(); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /migo-core/src/main/java/com/nia/rpc/core/server/Server.java: -------------------------------------------------------------------------------- 1 | package com.nia.rpc.core.server; 2 | 3 | /** 4 | * Author 知秋 5 | * Created by Auser on 2017/2/17. 6 | */ 7 | public interface Server { 8 | void start(); 9 | void shutdown(); 10 | } 11 | -------------------------------------------------------------------------------- /migo-core/src/main/java/com/nia/rpc/core/server/ServerImpl.java: -------------------------------------------------------------------------------- 1 | package com.nia.rpc.core.server; 2 | 3 | import com.nia.rpc.core.protocol.RpcDecoder; 4 | import com.nia.rpc.core.protocol.RpcEncoder; 5 | import com.nia.rpc.core.utils.NetUtils; 6 | import io.netty.bootstrap.ServerBootstrap; 7 | import io.netty.channel.Channel; 8 | import io.netty.channel.ChannelFuture; 9 | import io.netty.channel.ChannelInitializer; 10 | import io.netty.channel.EventLoopGroup; 11 | import io.netty.channel.nio.NioEventLoopGroup; 12 | import io.netty.channel.socket.SocketChannel; 13 | import io.netty.channel.socket.nio.NioServerSocketChannel; 14 | import io.netty.handler.logging.LogLevel; 15 | import io.netty.handler.logging.LoggingHandler; 16 | import org.apache.curator.framework.CuratorFramework; 17 | import org.apache.curator.framework.CuratorFrameworkFactory; 18 | import org.apache.curator.retry.ExponentialBackoffRetry; 19 | import org.apache.zookeeper.CreateMode; 20 | import org.slf4j.Logger; 21 | import org.slf4j.LoggerFactory; 22 | 23 | import static com.nia.rpc.core.utils.Constant.ZK_DATA_PATH; 24 | 25 | /** 26 | * Author 知秋 27 | * Created by Auser on 2017/2/17. 28 | */ 29 | public class ServerImpl implements Server { 30 | private static final Logger LOGGER = LoggerFactory.getLogger(ServerImpl.class); 31 | 32 | private String localIp; 33 | private int port; 34 | private boolean started = false; 35 | private Channel channel; 36 | private Object serviceImpl; 37 | private String serviceName; 38 | private String zkConn; 39 | private String serviceRegisterPath; 40 | 41 | private EventLoopGroup bossGroup = new NioEventLoopGroup(); 42 | private EventLoopGroup workerGroup = new NioEventLoopGroup(); 43 | 44 | private CuratorFramework curatorFramework; 45 | 46 | public ServerImpl(int port, Object serviceImpl, String serviceName) { 47 | this.port = port; 48 | this.serviceImpl = serviceImpl; 49 | this.serviceName = serviceName; 50 | } 51 | 52 | public ServerImpl(int port, Object serviceImpl, String serviceName, String zkConn) { 53 | this.port = port; 54 | this.serviceImpl = serviceImpl; 55 | this.serviceName = serviceName; 56 | this.zkConn = zkConn; 57 | } 58 | 59 | @Override 60 | public void start() { 61 | 62 | ServerBootstrap serverBootstrap = new ServerBootstrap(); 63 | serverBootstrap.group(bossGroup,workerGroup) 64 | .channel(NioServerSocketChannel.class) 65 | .childHandler(new ChannelInitializer() { 66 | @Override 67 | protected void initChannel(SocketChannel socketChannel) throws Exception { 68 | socketChannel.pipeline() 69 | .addLast(new LoggingHandler(LogLevel.INFO)) 70 | .addLast(new RpcDecoder(10 * 1024 * 1024)) 71 | .addLast(new RpcEncoder()) 72 | .addLast(new RpcServerHandler(serviceImpl)); 73 | } 74 | }); 75 | try { 76 | //调用bind等待客户端来连接 77 | ChannelFuture future = serverBootstrap.bind(port).sync(); 78 | //接着注册服务 79 | registerService(); 80 | 81 | LOGGER.info("Server Started At {}", port); 82 | started = true; 83 | this.channel = future.channel(); 84 | } catch (InterruptedException e) { 85 | e.printStackTrace(); 86 | } 87 | } 88 | 89 | private void registerService() { 90 | zkConn = getZkConn(); 91 | localIp = NetUtils.getLocalIp(); 92 | String serviceIp=localIp+":"+port; 93 | curatorFramework = CuratorFrameworkFactory.newClient(zkConn, 94 | new ExponentialBackoffRetry(1000, 3)); 95 | curatorFramework.start(); 96 | //连接上zk然后开始注册服务节点 97 | String serviceBasePath=ZK_DATA_PATH+serviceName; 98 | //添加基础服务节点 99 | try { 100 | curatorFramework.create() 101 | .creatingParentContainersIfNeeded() 102 | .forPath(serviceBasePath); 103 | } catch (Exception e) { 104 | if (e.getMessage().contains("NodeExist")) { 105 | LOGGER.info("This Path Service has already Exist"); 106 | } else { 107 | LOGGER.error("Create Path Error ", e); 108 | throw new RuntimeException("Register error"); 109 | } 110 | } 111 | 112 | boolean registerSuccess=false; 113 | 114 | serviceRegisterPath = serviceBasePath + "/" + serviceIp; 115 | 116 | //如果添加成功,添加标识服务具体路径的节点 117 | while (!registerSuccess){ 118 | try { 119 | curatorFramework.create() 120 | .withMode(CreateMode.EPHEMERAL) 121 | .forPath(serviceRegisterPath); 122 | //这里测试出现无限注册,特么坑死了,忘添加状态修改了 123 | registerSuccess = true; 124 | 125 | } catch (Exception e) { 126 | //出错重新注册(要先删除下节点再重新注册) 127 | try { 128 | Thread.sleep(1000); 129 | } catch (InterruptedException e1) { 130 | e1.printStackTrace(); 131 | } 132 | LOGGER.info("Retry Register ZK, {}", e.getMessage()); 133 | try { 134 | curatorFramework.delete().forPath(serviceRegisterPath); 135 | } catch (Exception e1) { 136 | e1.printStackTrace(); 137 | } 138 | } 139 | 140 | } 141 | 142 | } 143 | 144 | @Override 145 | public void shutdown() { 146 | //关停相关服务的逻辑 147 | LOGGER.info("Shutting down server {}", serviceName); 148 | unRegister(); 149 | if (curatorFramework != null) { 150 | curatorFramework.close(); 151 | } 152 | bossGroup.shutdownGracefully(); 153 | workerGroup.shutdownGracefully(); 154 | } 155 | 156 | private void unRegister() { 157 | LOGGER.info("unRegister zookeeper"); 158 | try { 159 | curatorFramework.delete().forPath(serviceRegisterPath); 160 | } catch (Exception e) { 161 | e.printStackTrace(); 162 | } 163 | } 164 | 165 | public String getZkConn() { 166 | return zkConn; 167 | } 168 | 169 | public void setZkConn(String zkConn) { 170 | this.zkConn = zkConn; 171 | } 172 | 173 | public String getLocalIp() { 174 | return localIp; 175 | } 176 | 177 | public void setLocalIp(String localIp) { 178 | this.localIp = localIp; 179 | } 180 | 181 | public Channel getChannel() { 182 | return channel; 183 | } 184 | 185 | public void setChannel(Channel channel) { 186 | this.channel = channel; 187 | } 188 | 189 | } 190 | -------------------------------------------------------------------------------- /migo-core/src/main/java/com/nia/rpc/core/utils/ConnectionObjectFactory.java: -------------------------------------------------------------------------------- 1 | package com.nia.rpc.core.utils; 2 | 3 | import com.nia.rpc.core.client.RpcClientHandler; 4 | import com.nia.rpc.core.protocol.RpcDecoder; 5 | import com.nia.rpc.core.protocol.RpcEncoder; 6 | import io.netty.bootstrap.Bootstrap; 7 | import io.netty.channel.*; 8 | import io.netty.channel.nio.NioEventLoopGroup; 9 | import io.netty.channel.socket.nio.NioSocketChannel; 10 | import io.netty.handler.logging.LogLevel; 11 | import io.netty.handler.logging.LoggingHandler; 12 | import org.apache.commons.pool2.BasePooledObjectFactory; 13 | import org.apache.commons.pool2.PooledObject; 14 | import org.apache.commons.pool2.impl.DefaultPooledObject; 15 | import org.slf4j.Logger; 16 | import org.slf4j.LoggerFactory; 17 | 18 | /** 19 | * 池对象工厂(PooledObjectFactory接口): 20 | * 用来创建池对象, 将不用的池对象进行钝化(passivateObject), 21 | * 对要使用的池对象进行激活(activeObject), 22 | * 对池对象进行验证(validateObject), 23 | * 对有问题的池对象进行销毁(destroyObject)等工作 24 | * 25 | * Author 知秋 26 | * Created by Auser on 2017/2/18. 27 | */ 28 | public class ConnectionObjectFactory extends BasePooledObjectFactory{ 29 | private static final Logger LOGGER = LoggerFactory.getLogger(ConnectionObjectFactory.class); 30 | 31 | private String ip; 32 | private int port; 33 | 34 | public ConnectionObjectFactory(String ip, int port) { 35 | this.ip = ip; 36 | this.port = port; 37 | } 38 | 39 | private Channel createNewConChannel() { 40 | Bootstrap bootstrap = new Bootstrap(); 41 | bootstrap.channel(NioSocketChannel.class) 42 | .group(new NioEventLoopGroup(1)) 43 | .handler(new ChannelInitializer() { 44 | protected void initChannel(Channel ch) throws Exception { 45 | ch.pipeline().addLast(new LoggingHandler(LogLevel.INFO)) 46 | .addLast(new RpcDecoder(10 * 1024 * 1024)) 47 | .addLast(new RpcEncoder()) 48 | .addLast(new RpcClientHandler()) 49 | ; 50 | } 51 | }); 52 | try { 53 | final ChannelFuture f = bootstrap.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 3000) 54 | .option(ChannelOption.TCP_NODELAY, true) 55 | .connect(ip, port).sync(); 56 | f.addListener((ChannelFutureListener) future -> { 57 | if (future.isSuccess()) { 58 | LOGGER.info("Connect success {} ", f); 59 | } 60 | }); 61 | final Channel channel = f.channel(); 62 | channel.closeFuture().addListener((ChannelFutureListener) future -> LOGGER.info("Channel Close {} {}", ip, port)); 63 | return channel; 64 | } catch (InterruptedException e) { 65 | e.printStackTrace(); 66 | } 67 | return null; 68 | } 69 | 70 | @Override 71 | public Channel create() throws Exception { 72 | return createNewConChannel(); 73 | } 74 | 75 | @Override 76 | public PooledObject wrap(Channel obj) { 77 | //排查出错,之前直接返回个null,未对方法进行重写,导致出错,拿不出对象 78 | return new DefaultPooledObject<>(obj); 79 | } 80 | 81 | @Override 82 | public void destroyObject(PooledObject p) throws Exception { 83 | p.getObject().close().addListener((ChannelFutureListener) future -> LOGGER.info("Close Finish")); 84 | } 85 | 86 | @Override 87 | public boolean validateObject(PooledObject p) { 88 | Channel object = p.getObject(); 89 | return object.isActive(); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /migo-core/src/main/java/com/nia/rpc/core/utils/Constant.java: -------------------------------------------------------------------------------- 1 | package com.nia.rpc.core.utils; 2 | 3 | /** 4 | * Author 知秋 5 | * Created by Auser on 2017/2/17. 6 | */ 7 | public interface Constant { 8 | int ZK_SESSION_TIMEOUT = 5000; 9 | 10 | int MAX_FRAME_LENGTH = 1024 * 1024; // 1MB 11 | 12 | String ZK_REGISTRY_PATH = "/registry"; 13 | String ZK_DATA_PATH = ZK_REGISTRY_PATH + "/services/"; 14 | } 15 | -------------------------------------------------------------------------------- /migo-core/src/main/java/com/nia/rpc/core/utils/NetUtils.java: -------------------------------------------------------------------------------- 1 | package com.nia.rpc.core.utils; 2 | 3 | import java.net.InetAddress; 4 | import java.net.UnknownHostException; 5 | 6 | /** 7 | * IP and Port Helper for RPC 8 | * Author 知秋 9 | * Created by Auser on 2017/2/17. 10 | */ 11 | public class NetUtils { 12 | 13 | /** 14 | * 此处实现的并不到位,暂时就这样处理的 15 | * 用Java获取本机IP地址,需要处理: 16 | *1. 多块网卡。 17 | *2. 排除loopback设备、虚拟网卡 18 | *看似简单的代码,写起来还是要小心一些的。 19 | * @return 20 | */ 21 | public static String getLocalIp() { 22 | 23 | try { 24 | return InetAddress.getLocalHost().getHostAddress(); 25 | } catch (UnknownHostException e) { 26 | e.printStackTrace(); 27 | } 28 | return null; 29 | } 30 | 31 | 32 | } 33 | -------------------------------------------------------------------------------- /migo-core/src/main/java/com/nia/rpc/core/utils/ResponseMapHelper.java: -------------------------------------------------------------------------------- 1 | package com.nia.rpc.core.utils; 2 | 3 | import com.nia.rpc.core.protocol.Response; 4 | 5 | import java.util.concurrent.BlockingQueue; 6 | import java.util.concurrent.ConcurrentHashMap; 7 | import java.util.concurrent.ConcurrentMap; 8 | 9 | /** 10 | * Author 知秋 11 | * Created by Auser on 2017/2/18. 12 | */ 13 | public class ResponseMapHelper { 14 | public static ConcurrentMap> responseMap = new ConcurrentHashMap<>(); 15 | } 16 | 17 | 18 | -------------------------------------------------------------------------------- /migo-example/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | parent 7 | com.nia.rpc 8 | 1.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | migo-example 13 | 14 | 15 | 16 | 17 | com.nia.rpc 18 | migo-rpc-provider 19 | 1.0-SNAPSHOT 20 | 21 | 22 | org.springframework.boot 23 | spring-boot-starter-web 24 | 25 | 26 | org.springframework.boot 27 | spring-boot-starter-logging 28 | 29 | 30 | LATEST 31 | 32 | 33 | org.springframework.boot 34 | spring-boot-starter-logging 35 | LATEST 36 | 37 | 38 | -------------------------------------------------------------------------------- /migo-example/src/main/java/com/nia/rpc/example/RpcApplication.java: -------------------------------------------------------------------------------- 1 | package com.nia.rpc.example; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | /** 7 | * Author 知秋 8 | * Created by Auser on 2017/2/19. 9 | */ 10 | @SpringBootApplication 11 | public class RpcApplication { 12 | 13 | public static void main(String[] args) { 14 | SpringApplication.run(RpcApplication.class); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /migo-example/src/main/java/com/nia/rpc/example/client/SpringClientConfig.java: -------------------------------------------------------------------------------- 1 | package com.nia.rpc.example.client; 2 | 3 | import com.nia.rpc.example.service.HelloWorld; 4 | import com.nia.rpc.factory.ClientFactoryBean; 5 | import org.springframework.boot.SpringApplication; 6 | import org.springframework.boot.autoconfigure.SpringBootApplication; 7 | import org.springframework.context.annotation.Bean; 8 | import org.springframework.context.annotation.Configuration; 9 | import org.springframework.web.bind.annotation.RequestMapping; 10 | import org.springframework.web.bind.annotation.RestController; 11 | 12 | import javax.annotation.Resource; 13 | 14 | /** 15 | * Author 知秋 16 | * Created by Auser on 2017/2/19. 17 | */ 18 | @Configuration 19 | @RestController 20 | @SpringBootApplication 21 | @RequestMapping("/test") 22 | public class SpringClientConfig { 23 | @Bean 24 | public HelloWorld clientFactoryBean() throws Exception { 25 | ClientFactoryBean clientFactoryBean = new ClientFactoryBean<>(); 26 | clientFactoryBean.setZkConn("127.0.0.1:2181"); 27 | clientFactoryBean.setServiceName("hello"); 28 | clientFactoryBean.setServiceInterface(HelloWorld.class); 29 | return clientFactoryBean.getObject(); 30 | } 31 | @Resource 32 | private HelloWorld helloWorld; 33 | 34 | @RequestMapping("/hello") 35 | public String hello(String say) { 36 | return helloWorld.say(say); 37 | } 38 | public static void main(String[] args) { 39 | SpringApplication.run(SpringClientConfig.class, "--server.port=8081"); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /migo-example/src/main/java/com/nia/rpc/example/server/SpringServerConfig.java: -------------------------------------------------------------------------------- 1 | package com.nia.rpc.example.server; 2 | 3 | import com.nia.rpc.core.utils.NetUtils; 4 | import com.nia.rpc.example.service.HelloWorld; 5 | import com.nia.rpc.example.service.HelloWorldImpl; 6 | import com.nia.rpc.factory.ServerFactoryBean; 7 | import org.springframework.boot.SpringApplication; 8 | import org.springframework.boot.autoconfigure.SpringBootApplication; 9 | import org.springframework.context.annotation.Bean; 10 | 11 | /** 12 | * Author 知秋 13 | * Created by Auser on 2017/2/19. 14 | */ 15 | @SpringBootApplication 16 | public class SpringServerConfig { 17 | @Bean 18 | public HelloWorld hello() { 19 | return new HelloWorldImpl(); 20 | } 21 | 22 | @Bean 23 | public ServerFactoryBean serverFactoryBean() { 24 | final ServerFactoryBean serverFactoryBean = new ServerFactoryBean(); 25 | serverFactoryBean.setPort(9090); 26 | serverFactoryBean.setServiceInterface(HelloWorld.class); 27 | serverFactoryBean.setServiceName("hello"); 28 | serverFactoryBean.setServiceImpl(hello()); 29 | serverFactoryBean.setZkConn("127.0.0.1:2181"); 30 | 31 | new Thread(() -> { 32 | try { 33 | serverFactoryBean.start(); 34 | } catch (Exception e) { 35 | e.printStackTrace(); 36 | } 37 | }, "RpcServer").start(); 38 | return serverFactoryBean; 39 | } 40 | 41 | public static void main(String[] args) { 42 | System.out.println(NetUtils.getLocalIp()); 43 | SpringApplication.run(SpringServerConfig.class, "--server.port=8082"); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /migo-example/src/main/java/com/nia/rpc/example/service/HelloWorld.java: -------------------------------------------------------------------------------- 1 | package com.nia.rpc.example.service; 2 | 3 | /** 4 | * Author 知秋 5 | * Created by Auser on 2017/2/19. 6 | */ 7 | public interface HelloWorld { 8 | String say(String hello); 9 | 10 | int sum(int a, int b); 11 | int max(Integer a, Integer b); 12 | } 13 | -------------------------------------------------------------------------------- /migo-example/src/main/java/com/nia/rpc/example/service/HelloWorldImpl.java: -------------------------------------------------------------------------------- 1 | package com.nia.rpc.example.service; 2 | 3 | /** 4 | * Author 知秋 5 | * Created by Auser on 2017/2/19. 6 | */ 7 | public class HelloWorldImpl implements HelloWorld { 8 | @Override 9 | public String say(String hello) { 10 | return "server: "+hello; 11 | } 12 | 13 | @Override 14 | public int sum(int a, int b) { 15 | return a+b; 16 | } 17 | 18 | @Override 19 | public int max(Integer a, Integer b) { 20 | return a <= b ? b : a; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /migo-rpc-provider/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | parent 7 | com.nia.rpc 8 | 1.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | migo-rpc-provider 13 | 14 | 15 | 4.3.6.RELEASE 16 | 17 | 18 | 19 | 20 | 21 | com.nia.rpc 22 | migo-core 23 | ${parent.version} 24 | 25 | 26 | 27 | org.projectlombok 28 | lombok 29 | 30 | 31 | 32 | org.springframework 33 | spring-core 34 | ${spring.version} 35 | true 36 | 37 | 38 | org.springframework 39 | spring-context 40 | ${spring.version} 41 | true 42 | 43 | 44 | org.springframework 45 | spring-beans 46 | ${spring.version} 47 | true 48 | 49 | 50 | -------------------------------------------------------------------------------- /migo-rpc-provider/src/main/java/com/nia/rpc/factory/ClientFactoryBean.java: -------------------------------------------------------------------------------- 1 | package com.nia.rpc.factory; 2 | 3 | import com.nia.rpc.core.bootstrap.ClientBuilder; 4 | import lombok.Data; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | import org.springframework.beans.factory.FactoryBean; 8 | 9 | /** 10 | * Author 知秋 11 | * Created by Auser on 2017/2/19. 12 | */ 13 | @Data 14 | public class ClientFactoryBean implements FactoryBean { 15 | private static final Logger LOGGER = LoggerFactory.getLogger(ClientFactoryBean.class); 16 | 17 | private Class serviceInterface; 18 | private String serviceName; 19 | private String zkConn; 20 | 21 | @Override 22 | public T getObject() throws Exception { 23 | return ClientBuilder 24 | .builder() 25 | .zkConn(zkConn) 26 | .serviceName(serviceName) 27 | .serviceInterface(serviceInterface) 28 | .build(); 29 | } 30 | 31 | @Override 32 | public Class getObjectType() { 33 | return serviceInterface; 34 | } 35 | 36 | @Override 37 | public boolean isSingleton() { 38 | return true; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /migo-rpc-provider/src/main/java/com/nia/rpc/factory/ServerFactoryBean.java: -------------------------------------------------------------------------------- 1 | package com.nia.rpc.factory; 2 | 3 | import com.nia.rpc.core.bootstrap.ServerBuilder; 4 | import com.nia.rpc.core.server.Server; 5 | import lombok.Data; 6 | import org.springframework.beans.factory.FactoryBean; 7 | 8 | import javax.annotation.PreDestroy; 9 | 10 | /** 11 | * Author 知秋 12 | * Created by Auser on 2017/2/19. 13 | */ 14 | @Data 15 | public class ServerFactoryBean implements FactoryBean{ 16 | 17 | private Class serviceInterface; 18 | private Object serviceImpl; 19 | private String ip; 20 | private int port; 21 | private String serviceName; 22 | private String zkConn; 23 | private Server rpcServer; 24 | 25 | //服务注册并提供 26 | public void start(){ 27 | rpcServer = ServerBuilder 28 | .builder() 29 | .serviceImpl(serviceImpl) 30 | .serviceName(serviceName) 31 | .zkConn(zkConn) 32 | .port(port) 33 | .build(); 34 | rpcServer.start(); 35 | } 36 | //服务下线 37 | @PreDestroy 38 | public void serviceOffline(){ 39 | rpcServer.shutdown(); 40 | } 41 | @Override 42 | public Object getObject() throws Exception { 43 | return this; 44 | } 45 | 46 | @Override 47 | public Class getObjectType() { 48 | return this.getClass(); 49 | } 50 | 51 | @Override 52 | public boolean isSingleton() { 53 | return true; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.nia.rpc 8 | parent 9 | 1.0-SNAPSHOT 10 | 11 | migo-core 12 | migo-example 13 | migo-rpc-provider 14 | 15 | pom 16 | 17 | MIGO-RPC 18 | 19 | 20 | UTF-8 21 | 22 | 23 | 24 | 25 | 26 | org.slf4j 27 | slf4j-api 28 | 1.7.21 29 | 30 | 31 | cglib 32 | cglib 33 | 3.2.0 34 | 35 | 36 | io.netty 37 | netty-all 38 | 4.1.7.Final 39 | 40 | 41 | org.projectlombok 42 | lombok 43 | 1.16.10 44 | 45 | 46 | 47 | org.apache.commons 48 | commons-pool2 49 | 2.4.2 50 | 51 | 52 | 53 | io.protostuff 54 | protostuff-core 55 | 1.3.7 56 | 57 | 58 | io.protostuff 59 | protostuff-runtime 60 | 1.3.7 61 | 62 | 63 | org.apache.zookeeper 64 | zookeeper 65 | 3.4.8 66 | 67 | 68 | slf4j-log4j12 69 | org.slf4j 70 | 71 | 72 | log4j 73 | log4j 74 | 75 | 76 | 77 | 78 | org.apache.curator 79 | curator-framework 80 | 2.9.1 81 | 82 | 83 | org.apache.curator 84 | curator-recipes 85 | 2.9.1 86 | 87 | 88 | org.apache.curator 89 | curator-x-discovery 90 | 2.9.1 91 | 92 | 93 | org.apache.commons 94 | commons-collections4 95 | 4.1 96 | 97 | 98 | junit 99 | junit 100 | 4.12 101 | test 102 | 103 | 104 | org.slf4j 105 | slf4j-simple 106 | 1.7.16 107 | test 108 | 109 | 110 | com.esotericsoftware 111 | kryo 112 | 4.0.0 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | org.apache.maven.plugins 121 | maven-source-plugin 122 | 2.2.1 123 | 124 | 125 | attach-sources 126 | 127 | jar-no-fork 128 | 129 | 130 | 131 | 132 | 133 | org.apache.maven.plugins 134 | maven-javadoc-plugin 135 | 2.9.1 136 | 137 | 138 | attach-javadocs 139 | 140 | jar 141 | 142 | 143 | 144 | 145 | 146 | 147 | org.apache.maven.plugins 148 | maven-resources-plugin 149 | 2.7 150 | 151 | UTF-8 152 | 153 | 154 | 155 | 156 | org.apache.maven.plugins 157 | maven-compiler-plugin 158 | 3.2 159 | 160 | 1.8 161 | 1.8 162 | UTF-8 163 | 164 | 165 | 166 | 167 | 168 | --------------------------------------------------------------------------------