基於Netty手寫的rpc框架時序圖
本項目一共分爲三個模塊:rpcServer、rpcClient、rpcCommon
rpcCommon的依賴
<dependencies> <dependency> <groupId>io.netty</groupId> <artifactId>netty-all</artifactId> <version>5.0.0.Alpha2</version> </dependency> <!-- 服務提供方根據調用信息反射獲取實現類時需要 --> <dependency> <groupId>org.reflections</groupId> <artifactId>reflections</artifactId> <version>0.9.11</version> </dependency> </dependencies>
rpcServer、rpcClient的依賴
<dependencies> <dependency> <groupId>rpc</groupId> <artifactId>rpc-common</artifactId> <version>1.0-SNAPSHOT</version> </dependency> </dependencies>
rpcCommon目錄結構
rpcCommon--ClassInfo類
package com.hk.common; import java.io.Serializable; /** * 使用JDK的序列化技術必須實現接口Serializable */ public class ClassInfo implements Serializable { private int id; private String className; private String methodName; private Class[] parameterTypes; private Object [] paramters; public int getId() { return id; } public void setId(int id) { this.id = id; } 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[] getParameterTypes() { return parameterTypes; } public void setParameterTypes(Class[] parameterTypes) { this.parameterTypes = parameterTypes; } public Object[] getParamters() { return paramters; } public void setParamters(Object[] paramters) { this.paramters = paramters; } }
接口
有參接口
package com.hk.common; public interface HasArgsHelloService { String hello(String msg); } 無參接口
package com.hk.common; public interface NoArgsHelloService { String hello(); }
rpcServer目錄結構
rpc服務端代碼
//利用netty創建服務ServerBootstrap,粘合兩個NioEventLoopGroup,配置端口ip,設置編碼解碼信息
package com.hk.rpc.server; import com.hk.rpc.server_stub.RpcServerHandler; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelOption; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.handler.codec.serialization.ClassResolvers; import io.netty.handler.codec.serialization.ObjectDecoder; import io.netty.handler.codec.serialization.ObjectEncoder; public class RpcServer { public static void main(String[] args) { RpcServer.start("127.0.0.1", 9999); } public static void start(String ip, Integer port) { //創建boss線程組(有點像負責招人的包工頭) NioEventLoopGroup boss = new NioEventLoopGroup(); //創建建立鏈接的線程 NioEventLoopGroup worker = new NioEventLoopGroup(); ServerBootstrap bootstrap = new ServerBootstrap(); try { bootstrap.group(boss, worker) .channel(NioServerSocketChannel.class) .option(ChannelOption.SO_BACKLOG, 128) .childHandler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) throws Exception { ch.pipeline().addLast(new ObjectEncoder()); ch.pipeline().addLast(new ObjectDecoder(1024 * 64, ClassResolvers.cacheDisabled(null))); ch.pipeline().addLast(new RpcServerHandler()); } }); //bind初始化端口是異步的,但調用sync則會同步阻塞等待端口綁定成功 ChannelFuture future = bootstrap.bind(ip, port).sync(); System.out.println(ip + ":服務已啓動,端口爲:" + port); future.channel().closeFuture().sync(); } catch (Exception e) { e.printStackTrace(); } finally { boss.shutdownGracefully(); worker.shutdownGracefully(); } } }
服務端處理器代碼
//真正通過反射找到實現類,調用方法返回結果
package com.hk.rpc.server_stub;
import com.hk.common.ClassInfo;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
import java.lang.reflect.Method;
import java.util.Set;
import org.reflections.Reflections;
public class RpcServerHandler extends ChannelHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
//獲取調用信息,尋找服務實現類
ClassInfo classInfo = (ClassInfo) msg;
String implName = getImplClassName(classInfo.getClassName());
Class<?> clazz = Class.forName(implName);
Method method = clazz.getMethod(classInfo.getMethodName(), classInfo.getParameterTypes());
Object result = method.invoke(clazz.newInstance(), classInfo.getParamters());
ctx.writeAndFlush(result);
}
private String getImplClassName(String interfaceName) throws ClassNotFoundException {
Class interClass = Class.forName(interfaceName);
String servicePath = "com.hk.rpc.service";
Reflections reflections = new Reflections(servicePath);
Set<Class> implClasses = reflections.getSubTypesOf(interClass);
if (implClasses.isEmpty()) {
System.err.println("impl class is not found!");
} else if (implClasses.size() > 1) {
System.err.println("there are many impl classes, not sure invoke which");
} else {
Class[] classes = implClasses.toArray(new Class[1]);
return classes[0].getName();
}
return null;
}
}
兩個實現類代碼
package com.hk.rpc.service; import com.hk.common.HasArgsHelloService; public class HasArgsHelloServiceImpl implements HasArgsHelloService { public String hello(String msg) { return "hello "+msg; } }
package com.hk.rpc.service;
import com.hk.common.NoArgsHelloService;
public class NoArgsHelloServiceImpl implements NoArgsHelloService {
public String hello() {
return "hello";
}
}
項目結構:rpcClient
客戶端代碼
//利用代理類來測試流程
package com.hk.rpc.server; import com.hk.common.HasArgsHelloService; import com.hk.common.NoArgsHelloService; import com.hk.rpc.client_stub.RpcProxy; public class RPCClient { public static void main(String[] args){ NoArgsHelloService noArgsHelloService = (NoArgsHelloService) RpcProxy.create(NoArgsHelloService.class); System.out.println(noArgsHelloService.hello()); HasArgsHelloService hasArgsHelloService = (HasArgsHelloService) RpcProxy.create(HasArgsHelloService.class); System.out.println(hasArgsHelloService.hello("hello 黃凱!")); } }
客戶端處理器代碼
//接收到服務端的請求,並賦值以及關閉連接
package com.hk.rpc.client_stub; import io.netty.channel.ChannelHandlerAdapter; import io.netty.channel.ChannelHandlerContext; public class RpcClientHandler extends ChannelHandlerAdapter { /** * RPC調用返回的結果 */ private Object rpcResult; public Object getRpcResult() { return rpcResult; } public void setRpcResult(Object rpcResult) { this.rpcResult = rpcResult; } @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { setRpcResult(msg); ctx.close(); } }
客戶端代碼類
//根據接口類信息通過jdk代理來調用實現類,在Bootstrap建立連接後,添加客戶端處理器,並返回信息
package com.hk.rpc.client_stub; import com.hk.common.ClassInfo; import io.netty.bootstrap.Bootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelOption; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioSocketChannel; import io.netty.handler.codec.serialization.ClassResolvers; import io.netty.handler.codec.serialization.ObjectDecoder; import io.netty.handler.codec.serialization.ObjectEncoder; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class RpcProxy { public static Object create(final Class clazz) { return Proxy.newProxyInstance(clazz.getClassLoader(), new Class[]{clazz}, new InvocationHandler() { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //構造調用信息 ClassInfo classInfo = new ClassInfo(); classInfo.setClassName(clazz.getName()); classInfo.setMethodName(method.getName()); classInfo.setParameterTypes(method.getParameterTypes()); classInfo.setParamters(args); //使用netty發送調用信息給服務提供方 NioEventLoopGroup group = new NioEventLoopGroup(); // Bootstrap bootstrap = new Bootstrap(); final RpcClientHandler rpcClientHandler = new RpcClientHandler(); try { bootstrap.group(group) .channel(NioSocketChannel.class) .option(ChannelOption.SO_KEEPALIVE, true) .option(ChannelOption.TCP_NODELAY, true) .handler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) throws Exception { ch.pipeline().addLast(new ObjectEncoder()); //反序列化對象時指定類解析器,null表示使用默認的類加載器 ch.pipeline().addLast(new ObjectDecoder(1024 * 64, ClassResolvers.cacheDisabled(null))); ch.pipeline().addLast(rpcClientHandler); } }); //connect是異步的,但調用其future的sync則是同步等待連接成功 ChannelFuture future = bootstrap.connect("127.0.0.1", 9999).sync(); //同步等待調用信息發送成功 future.channel().writeAndFlush(classInfo).sync(); //同步等待RPCClientHandler的channelRead被觸發後(意味着收到了調用結果) future.channel().closeFuture().sync(); } catch (Exception e) { e.printStackTrace(); } finally { group.shutdownGracefully(); } //返回調用結果 return rpcClientHandler.getRpcResult(); } }); } }
最後測試,先啓動RpcServer服務類,打印了信息之後,再啓動客戶端RPCClient進行測試
測試結果如下:
hello
hello hello 黃凱!
測試成功!!!