1、环境
jdk1.8、 protobuf、 netty5.0final、zookeeper3.4.12
2、模块概要
Rpc_Client 模拟Rpc_client链接
Rpc_Server Rpc服务器
Rpc_Registry 注册中心
Rpc_Util rpc的公共集合
3、各模块详解
package nanhui.wang.client; import nanhui.wang.rpc.registry.ZkClientUtil; import util.RpcService; import util.RpcUtil; import java.io.IOException; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.util.List; import java.util.UUID; public class ClientProxy implements InvocationHandler { private String host ; private int port ; public ClientProxy(String host , int port){ this.host = host ; this.port = port ; } /** * 重写Invoke方法 加入自己要处理的业务逻辑 * @param proxy * @param method * @param args * @return * @throws IOException */ public Object invoke(Object proxy, Method method, Object[] args) throws IOException { //封装request 然后发送到Server端 RpcUtil.RpcRequestProto.Builder requestProto = RpcUtil.RpcRequestProto.newBuilder() ; requestProto.setClassName(method.getDeclaringClass().getName()); requestProto.setMethodName(method.getName()) ; requestProto.setRequestId(UUID.randomUUID().toString()); requestProto.setRequest((RpcUtil.Request) args[0]); RpcService rpc = method.getDeclaringClass().getAnnotation(RpcService.class) ; String address = getServiceAddress(rpc.name()) ;//注册中心用了zookeeper 通过Service的名称去查找 Server端是否已经注册了此服务 String [] adds = address.split(":") ; //获取注册此服务的服务器所在地址 RpcClient client = new RpcClient(adds[0],Integer.parseInt(adds[1])); RpcUtil.RpcResponseProto responseProto = client.connect(requestProto.build()) ;//封装后通过RpcClient发送请求到Server端 return responseProto.getResponseResult(); } public String getServiceAddress(String serviceName){ List<String> address = ZkClientUtil.getAvilied(serviceName) ; if(address == null){ return null ; } return address.get(0).replace("/",".") ; } } package nanhui.wang.client; 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 io.netty.handler.codec.protobuf.ProtobufDecoder; import io.netty.handler.codec.protobuf.ProtobufEncoder; import io.netty.handler.codec.protobuf.ProtobufVarint32FrameDecoder; import io.netty.handler.codec.protobuf.ProtobufVarint32LengthFieldPrepender; import util.RpcUtil; /** * */ public class RpcClient { private String host; private int port; private RpcUtil.RpcResponseProto responseProto; public RpcClient(String host, int port) { this.host = host; this.port = port; } public RpcUtil.RpcResponseProto connect(final RpcUtil.RpcRequestProto req) { EventLoopGroup group = new NioEventLoopGroup(); try { Bootstrap bootstrap = new Bootstrap(); final RpcClientHandler handler = new RpcClientHandler(req);//自己的处理逻辑 其实啥也没处理 就是直接返回了 bootstrap.group(group).option(ChannelOption.CONNECT_TIMEOUT_MILLIS,2000) .option(ChannelOption.TCP_NODELAY, true).channel(NioSocketChannel.class) .handler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel socketChannel) { ChannelPipeline pipeline = socketChannel.pipeline(); /** * 处理半包和粘包 */ pipeline.addLast(new ProtobufVarint32FrameDecoder()); pipeline.addLast(new ProtobufVarint32LengthFieldPrepender()); //反序列化respinse pipeline.addLast(new ProtobufDecoder(RpcUtil.RpcResponseProto.getDefaultInstance())); //netty 对protobuf支持 pipeline.addLast(new ProtobufEncoder()); pipeline.addLast(handler); } }); ChannelFuture future = bootstrap.connect(host, port).sync();//链接到Server端去执行方法 future.channel().closeFuture().sync(); responseProto = handler.getResponse(); return responseProto; } catch (InterruptedException e) { e.printStackTrace(); return responseProto; } finally { group.shutdownGracefully(); return responseProto; } } } class RpcClientHandler extends ChannelHandlerAdapter { private RpcUtil.RpcRequestProto requestProto; private RpcUtil.RpcResponseProto proto; public RpcClientHandler(RpcUtil.RpcRequestProto requestProto) { this.requestProto = requestProto; } @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { proto = (RpcUtil.RpcResponseProto) msg; ctx.close(); } @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { ctx.writeAndFlush(requestProto); } @Override public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { ctx.flush(); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { cause.printStackTrace(); ctx.close(); } public RpcUtil.RpcResponseProto getResponse() { return proto; } }
Server端
package nanhui.wang.server; 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 io.netty.handler.codec.protobuf.ProtobufDecoder; import io.netty.handler.codec.protobuf.ProtobufEncoder; import io.netty.handler.codec.protobuf.ProtobufVarint32FrameDecoder; import io.netty.handler.codec.protobuf.ProtobufVarint32LengthFieldPrepender; import io.netty.handler.logging.LogLevel; import io.netty.handler.logging.LoggingHandler; import nanhui.wang.rpc.registry.ZkClientUtil; import org.msgpack.MessagePack; import util.RpcService; import util.RpcUtil; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; public class RpcServer { static Map<String, RpcServer> cache = new ConcurrentHashMap<String, RpcServer>(); private int port; private String host; public RpcServer(int port, String host) { this.port = port; this.host = host; } public void bind(final Object service) { RpcService rpc = service.getClass().getAnnotation(RpcService.class) ; ZkClientUtil.register(host, port, rpc.name());//注册服务的地址 if (!cache.containsKey(service.getClass().getName())) { cache.put(service.getClass().getName(), this); } EventLoopGroup worker = new NioEventLoopGroup(); EventLoopGroup boss = new NioEventLoopGroup(); try { ServerBootstrap bootstrap = new ServerBootstrap(); bootstrap.group(boss, worker).option(ChannelOption.SO_BACKLOG, 1024).channel(NioServerSocketChannel.class).handler(new LoggingHandler(LogLevel.INFO)).childHandler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel socketChannel) { ChannelPipeline pipeline = socketChannel.pipeline(); //和client端处理类似 添加半包粘包支持 pipeline.addLast(new ProtobufVarint32FrameDecoder()); pipeline.addLast(new ProtobufVarint32LengthFieldPrepender()); //request 反序列化 pipeline.addLast(new ProtobufDecoder(RpcUtil.RpcRequestProto.getDefaultInstance())); //添加netty对protobuf的支持 pipeline.addLast(new ProtobufEncoder()); //添加自己的业务逻辑 pipeline.addLast(new ServerHandler(service)); } }); ChannelFuture future = bootstrap.bind(port).sync(); future.channel().closeFuture().sync(); } catch (InterruptedException e) { e.printStackTrace(); } finally { worker.shutdownGracefully(); boss.shutdownGracefully(); } } } class ServerHandler extends ChannelHandlerAdapter { private Object service; public ServerHandler(Object service) { this.service = service; } @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { RpcUtil.RpcRequestProto req = (RpcUtil.RpcRequestProto) msg; Object obj = invoke(req); ctx.writeAndFlush(obj); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { cause.printStackTrace(); ctx.close(); } /** * 用反射去查找Service中方法 * * * @param req * @return * @throws IOException * @throws NoSuchMethodException * @throws InvocationTargetException * @throws IllegalAccessException */ public Object invoke(RpcUtil.RpcRequestProto req) throws IOException, NoSuchMethodException, InvocationTargetException, IllegalAccessException { long time = System.currentTimeMillis(); String methodName = req.getMethodName(); String requestId = req.getRequestId(); Object[] objs = new Object[1]; //我这里请求封装在request中 所以objes 和type的长度都为1 objs[0] = req.getRequest(); Class<?>[] types = new Class<?>[1]; types[0] = objs[0].getClass() ; Method method = service.getClass().getMethod(methodName, types); //找到后 invoke 反射 Object obj = method.invoke(service, objs); //封装到response中返回 RpcUtil.RpcResponseProto responseProto = RpcUtil.RpcResponseProto.newBuilder().setResponseCost((System.currentTimeMillis() - time) + "").setResponseId(requestId).setResponseResult(obj.toString()).build(); return responseProto; } }
registry
package nanhui.wang.rpc.registry.nanhui.wang.rpc.register.impl; import nanhui.wang.rpc.registry.Register; import org.I0Itec.zkclient.ZkClient; import java.util.List; /** * 这里面没有写的很复杂 就是简单的使用 * 使用的是Zkclient * 也可以使用apacheCurtor */ public class ZookeeperRegister implements Register { static final String root = "/nanhui/wang/rpc/"; private static final String zkHost = "192.168.217.45:2181"; private ZkClient zkClient; public ZookeeperRegister() { if (zkClient == null) { zkClient = new ZkClient(zkHost); // zkClient.connect(2000, new Watcher() { // public void process(WatchedEvent event) { // Event.EventType type = event.getType(); // String path = event.getPath(); // Event.KeeperState state = event.getState(); // // switch (state) { // case Expired: // break; // case AuthFailed: // break; // case Disconnected: // break; // case SyncConnected: // break; // case ConnectedReadOnly: // break; // case SaslAuthenticated: // break; // // default: // break; // } // // // switch (type) { // // case None: // break; // case NodeCreated: // System.out.println(path + "\t注册节点"); // // // break; // case NodeDeleted: // System.out.println(path + "\t节点删除"); // // break; // case NodeDataChanged: // System.out.println(path + "\t节点数据发生改变"); // // break; // case NodeChildrenChanged: // System.out.println(path + "\t子节点发生改变"); // break; // // // } // } // }); } } /** * 如果存在注册的地址 先删除 再重新注册 * zkClient 先注册parent 在注册child 有个参数好像可以直接生成一个完整的地址 * @param ip * @param port * @param serviceName * @return */ public boolean register(String ip, int port, String serviceName) { String registryPath = root + serviceName; boolean exist = zkClient.exists(registryPath); if (exist) { zkClient.delete(registryPath); } String[] list = registryPath.split("/"); String p = ""; for (int i = 0; i < list.length; i++) { if (list[i].equals("") ) continue; p += "/" + list[i]; if(zkClient.exists(p)) continue; zkClient.createPersistent(p); } //创建临时节点 如果服务器下线 该节点自动删除 zkClient.createEphemeral(p + "/" + ip + ":" + port); return true; } public List<String> getValidService(String serviceName) { String registryPath = root + serviceName; boolean exist = zkClient.exists(registryPath); if (exist) { return zkClient.getChildren(registryPath); } else { System.out.println("没有可用节点!!!!"); return null; } } }
util
package util; @RpcService(name = "Hello") public interface Hello { String say(RpcUtil.Request request); } package util; @RpcService(name = "Hello") public class HelloImpl implements Hello { public String say(RpcUtil.Request t ) { return t.getRequestPar() + " : hello RPC "; } } package util; import java.net.Inet4Address; import java.net.InetAddress; import java.net.NetworkInterface; import java.util.ArrayList; import java.util.Enumeration; import java.util.List; public class IPLocation { public static List<String> loadLocalIPList() { List<String> ips = new ArrayList<String>(1); Enumeration<NetworkInterface> allNIC = null; try { allNIC = NetworkInterface.getNetworkInterfaces(); } catch (Exception ex) { System.err.println("获取本机 ip 地址发生错误"+ex.getMessage()); } while (allNIC != null && allNIC.hasMoreElements()) { NetworkInterface nic = allNIC.nextElement(); Enumeration<InetAddress> addresses = nic.getInetAddresses(); while (addresses.hasMoreElements()) { InetAddress ip = addresses.nextElement(); if (ip != null && (ip instanceof Inet4Address) && (!ip.isAnyLocalAddress() && !ip.isLoopbackAddress())) { ips.add(ip.getHostAddress()); } } } return ips; } } package util; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @Retention(RetentionPolicy.RUNTIME) public @interface RpcService { String name () default ""; } syntax ="proto2" ; option java_package="nanhui.wang.util" ; option java_outer_classname="RpcUtil"; import "google/protobuf/any.proto"; message RpcRequestProto{ required string requestId = 1 ; required string className = 2; required string methodName = 3; required Request request = 4 ; } message Request{ required string requestPar = 1; } message RpcResponseProto{ required string responseId = 1 ; required string responseCost = 2; required string responseResult = 3; }
test测试类
package nanhui.wang.client; import util.Hello; import util.RpcUtil; import java.lang.reflect.Proxy; /**
* client 端 默认用本机去链接
**/
public class RpcClientDemo { public static void main(String[] args) { Hello obj = clientProxy(Hello.class,"127.0.0.1",8888) ; RpcUtil.Request request = RpcUtil.Request.newBuilder().setRequestPar("Hello Rpc").build() ; System.out.println(obj.say(request)); }
/**
*生成一个冬天代理类
**/ public static <T> T clientProxy(final Class<?> interfceClass , final String host , final int port) { return (T)Proxy.newProxyInstance(interfceClass.getClassLoader(), new Class[] {interfceClass}, new ClientProxy(host,port)); } } package nanhui.wang.server; import util.Hello; import util.HelloImpl; import util.IPLocation; import java.util.List; public class RpcServerBootStrap {
//server 端 public static void main(String[] args) { List<String> ips = IPLocation.loadLocalIPList(); //ip地址 int port = 8888; for (String ip : ips) { Hello hello = new HelloImpl(); RpcServer server = new RpcServer(port, ip); bindService(hello,server); } } public static <T> void bindService(T service, RpcServer server) { server.bind(service); } }
运行结果
git地址