標籤:amt shu read 運行 except main hub invoke sync
所謂RPC就是遠程方法調用(Remote Process Call ),簡單的來說就是通過MQ,TCP,HTTP或者自己寫的網路通訊協定來傳輸我要調用對方的什麼介面,對方處理之後再把結果返回給我.就這麼簡單的一個過程。
運行時,一次客戶機對伺服器的RPC調用,其內部操作大致有如下十步:
1、調用用戶端控制代碼;執行傳送參數
2、調用本地系統核心發送網路訊息
3、訊息傳送到遠程主機
4、伺服器控制代碼得到訊息並取得參數
5、執行遠程過程
6、執行的過程將結果返回伺服器控制代碼
7、伺服器控制代碼返回結果,調用遠程系統核心
8、訊息傳回本地主機
9、客戶控制代碼由核心接收訊息
10、客戶接收控制代碼返回的資料
之前一篇文章簡單RPC之Socket實現我們通過socket通訊實現了簡單的RPC調用,接下來我們基於Netty來實現一個簡單的RPC調用過程,當然還有很多不完善的地方,只供參考學習RPC使用。
一、首先定義訊息傳遞的實體類
public class ClassInfo implements Serializable {private static final long serialVersionUID = -8970942815543515064L;private String className;//類名private String methodName;//函數名稱private Class<?>[] types;//參數類型 private Object[] objects;//參數列表 public String getClassName() {return className;}public void setClassName(String className) {this.className = className;}public String getMethodName() {return methodName;}public void setMethodName(String methodName) {this.methodName = methodName;}public Class<?>[] getTypes() {return types;}public void setTypes(Class<?>[] types) {this.types = types;}public Object[] getObjects() {return objects;}public void setObjects(Object[] objects) {this.objects = objects;}}二、建立Netty操作的服務端,以及具體操作
(1)服務端
public class RPCServer {private int port;public RPCServer(int port){this.port = port;}public void start(){EventLoopGroup bossGroup = new NioEventLoopGroup();EventLoopGroup workerGroup = new NioEventLoopGroup();try {ServerBootstrap serverBootstrap = new ServerBootstrap().group(bossGroup, workerGroup).channel(NioServerSocketChannel.class).localAddress(port).childHandler(new ChannelInitializer<SocketChannel>() {@Overrideprotected void initChannel(SocketChannel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); pipeline.addLast(new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 0, 4, 0, 4)); pipeline.addLast(new LengthFieldPrepender(4)); pipeline.addLast("encoder", new ObjectEncoder()); pipeline.addLast("decoder", new ObjectDecoder(Integer.MAX_VALUE, ClassResolvers.cacheDisabled(null))); pipeline.addLast(new InvokerHandler()); }}).option(ChannelOption.SO_BACKLOG, 128) .childOption(ChannelOption.SO_KEEPALIVE, true);ChannelFuture future = serverBootstrap.bind(port).sync(); System.out.println("Server start listen at " + port ); future.channel().closeFuture().sync(); } catch (Exception e) { bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully();}}public static void main(String[] args) throws Exception { int port; if (args.length > 0) { port = Integer.parseInt(args[0]); } else { port = 8080; } new RPCServer(port).start(); } }(2)服務端操作,由服務端我們看到具體的資料轉送操作是進行序列化的,具體的操作還是比較簡單的,就是擷取發送過來的資訊,這樣就可以通過反射獲得類名,根據函數名和參數值,執行具體的操作,將執行結果發送給用戶端。
public class InvokerHandler extends ChannelInboundHandlerAdapter {public static ConcurrentHashMap<String, Object> classMap = new ConcurrentHashMap<String,Object>();@Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { ClassInfo classInfo = (ClassInfo)msg; Object claszz = null;if(!classMap.containsKey(classInfo.getClassName())){try {claszz = Class.forName(classInfo.getClassName()).newInstance();classMap.put(classInfo.getClassName(), claszz);} catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) {e.printStackTrace();}}else {claszz = classMap.get(classInfo.getClassName());}Method method = claszz.getClass().getMethod(classInfo.getMethodName(), classInfo.getTypes()); Object result = method.invoke(claszz, classInfo.getObjects()); ctx.write(result); ctx.flush(); ctx.close(); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { cause.printStackTrace(); ctx.close(); } }三、用戶端,通過代理機制來觸發遠程調用
(1)用戶端,當執行具體的函數時會調用遠程操作,將具體操作的類、函數及參數資訊發送到服務端
public class RPCProxy {@SuppressWarnings("unchecked")public static <T> T create(Object target){ return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(), new InvocationHandler(){@Overridepublic Object invoke(Object proxy, Method method, Object[] args)throws Throwable {ClassInfo classInfo = new ClassInfo();classInfo.setClassName(target.getClass().getName());classInfo.setMethodName(method.getName());classInfo.setObjects(args);classInfo.setTypes(method.getParameterTypes());ResultHandler resultHandler = new ResultHandler(); EventLoopGroup group = new NioEventLoopGroup(); try { Bootstrap b = new Bootstrap(); b.group(group) .channel(NioSocketChannel.class) .option(ChannelOption.TCP_NODELAY, true) .handler(new ChannelInitializer<SocketChannel>() { @Override public void initChannel(SocketChannel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); pipeline.addLast("frameDecoder", new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 0, 4, 0, 4)); pipeline.addLast("frameEncoder", new LengthFieldPrepender(4)); pipeline.addLast("encoder", new ObjectEncoder()); pipeline.addLast("decoder", new ObjectDecoder(Integer.MAX_VALUE, ClassResolvers.cacheDisabled(null))); pipeline.addLast("handler",resultHandler); } }); ChannelFuture future = b.connect("localhost", 8080).sync(); future.channel().writeAndFlush(classInfo).sync(); future.channel().closeFuture().sync(); } finally { group.shutdownGracefully(); } return resultHandler.getResponse();}});}}(2)擷取遠程調用返回的結果值
public class ResultHandler extends ChannelInboundHandlerAdapter {private Object response; public Object getResponse() { return response; } @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { response=msg; System.out.println("client接收到伺服器返回的訊息:" + msg); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { System.out.println("client exception is general"); } }
四、介面、實作類別及Main操作
介面:
public interface HelloRpc {String hello(String name);}
實作類別:
public class HelloRpcImpl implements HelloRpc {@Overridepublic String hello(String name) {return "hello "+name;}}
Main操作:
public class Main {public static void main(String [] args){HelloRpc helloRpc = new HelloRpcImpl();helloRpc = RPCProxy.create(helloRpc);System.err.println(helloRpc.hello("rpc"));}}
完整代碼地址github
簡單RPC實現之Netty實現