netty手写rpc框架
原理和思想
1.当前生产者会把接口信息注册到zk(注册中心)上去,路径是以com.shanhe.api.service.UserService 下面是多个地址 ,存储结构是像存储结构分析那样注册的, 消费者通过com.shanhe.api.service.UserService 这样的路径去访问userService多个调用接口地址,再通过本地负载均衡算法 选用一个地址,选择到一个地址之后 这个时候订单服务再去拼接一下 调用到哪个接口和哪个方法,会员服务收到这个地址之后 去做解析 通过userService找到它对应的实现 再通过java的反射机制执行到GetUser方法。getUser方法执行完毕 再把结果 生产者发送给消费者
1.服务注册
存储结构分析:
com.shanhe.service.api.MemberService
--providers
----shanherpc://127.0.0.1:8080
----shanherpc://127.0.0.1:8081
2.消费者会根据com.shanhe.service.api.MemberService
去到zk上获取接口的调用地址.
----shanherpc://127.0.0.1:8080
----shanherpc://127.0.0.1:8081
自定义rpc注解
package com.shanhe.annotation; import java.lang.annotation.*; /** * 自定义rpc注解 */ @Documented @Inherited @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface RpcAnnotation { Class value(); }
公共api接口
package com.shanhe.api; public interface MemberService { String getUser(Long userId); int delUser(Long userId); }
实现
package com.shanhe.api.impl; import com.shanhe.dubbo.RpcAnnotation; import com.shanhe.service.api.MemberService; @RpcAnnotation(MemberService.class) public class MemberServiceImpl implements MemberService { public String getUser(Long userId) { return "userId=1"; } public int delUser(Long userId) { return 1; } }
服务注册
package com.shanhe.register; import org.I0Itec.zkclient.ZkClient; /** * 将服务注册到zk上 */ public class ServiceRegistration { /** * zk连接地址 */ private final String zkServers = "127.0.0.1"; /** * 会话时间 */ private final int connectionTimeout = 5000; /*** * zkClient */ private ZkClient zkClient; private String rootNamePath = "/shanherpc"; public ServiceRegistration() { // 1.创建zk连接 zkClient = new ZkClient(zkServers, connectionTimeout); } public void registr(String serviceName, String addres) { // 路径:/shanherpc if (!zkClient.exists(rootNamePath)) { zkClient.createPersistent(rootNamePath); } ///shanherpc/com.shanhe.service.api.MemberService String serviceNode = rootNamePath + "/" + serviceName; if (!zkClient.exists(serviceNode)) { zkClient.createPersistent(serviceNode); } //shanherpc/com.shanhe.service.api.MemberService/providers String providers = serviceNode + "/providers"; if (!zkClient.exists(providers)) { zkClient.createPersistent(providers); } //shanherpc/com.shanhe.service.api.MemberService/providers//shanherpc://127.0.0.1:9001 String servicePathAddres = providers + "/" + addres; if (zkClient.exists(servicePathAddres)) { zkClient.delete(servicePathAddres); } // 临时节点缓存地址 zkClient.createEphemeral(servicePathAddres); } }
服务发现
package com.shanhe.consumer; import org.I0Itec.zkclient.ZkClient; import java.util.List; /** * 服务发现 */ public class ServiceDiscover { /** * zk连接地址 */ private final String zkServers = "127.0.0.1"; /** * 会话时间 */ private final int connectionTimeout = 5000; /*** * zkClient */ private ZkClient zkClient; private String rootNamePath = "/shanherpc"; public ServiceDiscover() { // 1. 连接zk连接 zkClient = new ZkClient(zkServers, connectionTimeout); } public List<String> getDiscover(String serviceName) { List<String> children = zkClient.getChildren(rootNamePath + "/" + serviceName + "/providers"); return children; } }
server端
package com.shanhe.register; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.*; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioServerSocketChannel; import org.apache.commons.lang3.StringUtils; import com.shanhe.ioc.RpContainer; import com.shanhe.req.RpcRequest; import java.lang.reflect.Method; public class RpcNettyServer { public void bind(int port) throws InterruptedException { EventLoopGroup boss = new NioEventLoopGroup(); EventLoopGroup worker = new NioEventLoopGroup(); try { ServerBootstrap b = new ServerBootstrap(); b.group(boss, worker) .channel(NioServerSocketChannel.class) .option(ChannelOption.SO_BACKLOG, 1280) .childHandler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) throws Exception { // 新增Marshaling编码器 ch.pipeline().addLast( MarshallingCodeCFactory.buildMarshallingDecoder() ); ch.pipeline().addLast( MarshallingCodeCFactory.buildMarshallingEncoder() ); ch.pipeline().addLast( new ServerHandler() ); } }); ChannelFuture f = b.bind(port).sync(); f.channel().closeFuture().sync(); } finally { boss.shutdownGracefully(); worker.shutdownGracefully(); } } /** * 通过反射执行方法 */ @ChannelHandler.Sharable public class ServerHandler extends ChannelInboundHandlerAdapter { @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { // com.shanhe.service.api.MemberService.getUser(Int) RpcRequest rpcRequest = (RpcRequest) msg; String className = rpcRequest.getClassName(); if (StringUtils.isEmpty(className)) { return; } // 从容器中查找到 Object objectImpl = RpContainer.get(className); if (objectImpl == null) { return; } //className 反射机制 String methodName = rpcRequest.getMethodName(); Method method = objectImpl.getClass().getMethod(methodName, rpcRequest.getParameterTypes()); // 真正调用实现类方法 Object result = method.invoke(objectImpl, rpcRequest.getParamsValue()); // 返回结果给消费者 ctx.writeAndFlush(result); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { cause.printStackTrace(); ctx.close(); } } }
将服务绑定到zk
package com.shanhe.register; import com.shanhe.annotation.RpcAnnotation; import com.shanhe.ioc.RpContainer; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; /** * * 将服务绑定到zk * 启动netty服务实现监听 * */ public class ShanHeRpcServer { /** * host */ private String host; /** * 端口号 */ private int port; private ServiceRegistration zkServiceRegistration; private RpcNettyServer rpcNettyServer; public ShanHeRpcServer(String host, int port) { this.host = host; this.port = port; zkServiceRegistration = new ServiceRegistration(); rpcNettyServer = new RpcNettyServer(); } // 需要发布服务 public ShanHeRpcServer bind(Object object) throws UnsupportedEncodingException { // object MemberServiceImpl // 1.需要将该接口注册到zk上 RpcAnnotation declaredAnnotation = object.getClass().getDeclaredAnnotation(RpcAnnotation.class); //com.shanhe.service.api.MemberService String sericeName = declaredAnnotation.value().getName(); String addres = URLEncoder.encode("shanherpc://" + host + ":" + port, "UTF-8"); zkServiceRegistration.registr(sericeName, addres); // 2.将对应接口的实现类注册到容器中 // com.shanhe.service.api.MemberService RpContainer.put(sericeName, object); return this; } public void start() throws InterruptedException { // 2.启动一个netty服务器端监听消费者发送的消息 rpcNettyServer.bind(port); } }
定义容器
package com.shanhe.ioc; import java.util.HashMap; import java.util.Map; /** * 容器用来装接口的实现类 */ public class RpContainer { private static Map<String, Object> containers = new HashMap<>(); public static void put(String key, Object value) { containers.put(key, value); } public static Object get(String key) { return containers.get(key); } }
传送Request的实体类
package com.shanhe.req; import java.io.Serializable; /** * 传送Request的实体类 */ public class RpcRequest implements Serializable { private static final long SerialVersionUID = 1L; // shanherpc://192.168.1.1:8080/com.shanhe.api.UserService.getUser /** * 类的className */ private String className; /** * 方法名称 */ private String methodName; /** * 参数类型 */ Class<?> parameterTypes[]; /** * 参数value */ Object paramsValue[]; public RpcRequest(String className, String methodName, Class<?>[] parameterTypes, Object[] paramsValue) { this.className = className; this.methodName = methodName; this.parameterTypes = parameterTypes; this.paramsValue = paramsValue; } public String getClassName() { return className; } public String getMethodName() { return methodName; } public Class<?>[] getParameterTypes() { return parameterTypes; } public Object[] getParamsValue() { return paramsValue; } public void setClassName(String className) { this.className = className; } public void setMethodName(String methodName) { this.methodName = methodName; } public void setParameterTypes(Class<?>[] parameterTypes) { this.parameterTypes = parameterTypes; } public void setParamsValue(Object[] paramsValue) { this.paramsValue = paramsValue; } @Override public String toString() { return className + "," + methodName + "," + parameterTypes + paramsValue; } }
客户端通过代理模式执行方法
package com.shanhe.consumer; import com.shanhe.ioc.RpContainer; import com.shanhe.register.MarshallingCodeCFactory; import com.shanhe.req.RpcRequest; import io.netty.bootstrap.Bootstrap; import io.netty.channel.*; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioSocketChannel; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.net.InetSocketAddress; import java.net.URLDecoder; import java.util.List; import java.util.concurrent.CountDownLatch; /** * 客户端通过代理模式执行方法 */ public class RpcClientProxy { private ServiceDiscover serviceDiscover = new ServiceDiscover(); public <T> T create(Class<T> tClass) { return (T) Proxy.newProxyInstance(tClass.getClassLoader(), new Class[]{tClass}, new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { String serviceName = tClass.getName(); // 1. 根据服务名称 去zk上获取接口调用地址 shanherpc://127.0.0.1:8080 List<String> discover = serviceDiscover.getDiscover(serviceName); //shanherpc://127.0.0.1:8080 String addres = discover.get(0); RpcRequest rpcRequest = new RpcRequest( serviceName, method.getName(), method.getParameterTypes(), args ); // 2.拼接调用接口地址shanherpc://127.0.0.1:8080/com.shanhe.service.api.MemberService getgetUser(1) tClass.getName // 3.使用nettyclient向netty服务器端发送消息 String[] splitAddres = URLDecoder.decode(addres, "UTF-8").split(":"); String host = splitAddres[1].replace("//",""); String port = splitAddres[2]; return sendMsg(host, Integer.parseInt(port), rpcRequest); } }); } public Object sendMsg(String host, int port, RpcRequest rpcRequest) { DubboClientHandler dubboClientHandler = new DubboClientHandler(); //创建nioEventLoopGroup NioEventLoopGroup group = new NioEventLoopGroup(); Bootstrap bootstrap = new Bootstrap(); bootstrap.group(group).channel(NioSocketChannel.class) .remoteAddress(new InetSocketAddress(host, port)) .handler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) throws Exception { ch.pipeline().addLast(MarshallingCodeCFactory.buildMarshallingDecoder()); ch.pipeline().addLast(MarshallingCodeCFactory.buildMarshallingEncoder()); ch.pipeline().addLast(dubboClientHandler); } }); try { // 发起同步连接 ChannelFuture sync = bootstrap.connect().sync(); sync.channel().writeAndFlush(rpcRequest); countDownLatch.await(); } catch (Exception e) { e.printStackTrace(); } finally { group.shutdownGracefully(); } return dubboClientHandler.getResponse(); } private CountDownLatch countDownLatch = new CountDownLatch(1); public class DubboClientHandler extends ChannelInboundHandlerAdapter { private Object response; @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { System.out.println("消费者获取到消息:" + msg); this.response = msg; countDownLatch.countDown(); ; } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { cause.printStackTrace(); ctx.close(); } public Object getResponse() { return this.response; } } }
启动server演示下
早年同窗始相知,三载瞬逝情却萌。年少不知愁滋味,犹读红豆生南国。别离方知相思苦,心田红豆根以生。