手寫RPC框架(六)整合Netty
Netty簡介:
Netty是一個基於NIO的,提供非同步,事件驅動的網路應用工具,具有高效能高可靠性等特點。
使用傳統的Socket來進行網路通訊,服務端每一個連線都要新建一個執行緒,清楚處理完成後通過輸出流返回給客戶端。而Netty通過NIO的方式,服務端實現為一個請求一個執行緒,客戶端傳送的連線請求會註冊到多路複用器上,多路複用器輪詢到連線有I/O請求時才會啟動一個執行緒進行處理。
這次我們通過Netty來實現網路通訊,替代Socket,提高框架效能。
-
引入Netty
<!-- https://mvnrepository.com/artifact/io.netty/netty-all --> <dependency> <groupId>io.netty</groupId> <artifactId>netty-all</artifactId> <version>4.1.73.Final</version> </dependency>
-
netty服務端
public class NettyServer{ private static final Logger logger = LoggerFactory.getLogger(NettyServer.class); private String serverAddress; //啟動地址 private int serverPort; //啟動埠 private EventLoopGroup boss = null; private EventLoopGroup worker = null; public NettyServer(String serverAddress, int serverPort) { this.serverAddress = serverAddress; this.serverPort = serverPort; } public void startNettyServer() throws Exception { //netty排程模組,負責接收請求 NioEventLoopGroup bossGroup = new NioEventLoopGroup(); //netty排程模組,負責處理請求 NioEventLoopGroup workGroup = new NioEventLoopGroup(); //啟動類 ServerBootstrap bootstrap = new ServerBootstrap(); bootstrap.group(bossGroup,workGroup); bootstrap.channel(NioServerSocketChannel.class); bootstrap.childHandler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) throws Exception { //傳輸資料的channel ChannelPipeline pipeline = ch.pipeline(); //解碼器 pipeline.addLast(new StringDecoder()); //編碼器 pipeline.addLast(new StringEncoder()); //業務邏輯 pipeline.addLast(new RpcServerHandler()); } }); try { //埠繫結 ChannelFuture sync = bootstrap.bind(serverAddress, serverPort).sync(); sync.channel().closeFuture().sync(); } catch (InterruptedException e) { e.printStackTrace(); }finally { bossGroup.shutdownGracefully(); workGroup.shutdownGracefully(); } } @PreDestroy public void destory() throws InterruptedException { boss.shutdownGracefully().sync(); worker.shutdownGracefully().sync(); logger.info("關閉Netty"); } }
在這裡通過startServer方法會啟動netty服務端,當有請求時,會進入
RpcServerHandler()
方法中進行處理。@ChannelHandler.Sharable public class RpcServerHandler extends SimpleChannelInboundHandler<String> { @Override protected void channelRead0(ChannelHandlerContext ctx, String msg) { //msg為接收到的請求資訊 RpcResponse response = new RpcResponse(); //將請求資訊解碼 RpcRegisterEntity rpcRegisterEntity=JSON.parseObject(msg,RpcRegisterEntity.class); //通過反射得到遠端呼叫的類並執行該方法 Object result = invoke(rpcRegisterEntity); try { //返回體 response.setResult(result); } catch (Exception exception) { exception.printStackTrace(); response.setException(exception); } //寫入返回資料 ctx.writeAndFlush(JSON.toJSONString(response)); } private Object invoke(RpcRegisterEntity entity) { try { //介面名 String interfaceName = entity.getServiceImplClassFullName(); // String implClassName = RegisterCenter.getProviderData(interfaceName); //類名 String implClassName = entity.getServiceImplClassFullName(); Class<?> clazz = Class.forName(implClassName); String methodName = entity.getMethodName(); Class<?>[] parameterTypes = entity.getParameterTypes(); Object[] parameters = entity.getParameters(); Method method = clazz.getMethod(methodName, parameterTypes); //通過反射得到結果 return method.invoke(clazz.newInstance(), parameters); } catch (ClassNotFoundException | NoSuchMethodException | InvocationTargetException | IllegalAccessException | InstantiationException e) { e.printStackTrace(); return e; } } //當Channel處理於活動狀態時被呼叫 @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { // System.out.println(ctx.channel().remoteAddress().toString()); super.channelActive(ctx); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { // System.out.println(JSON.toJSONString(cause)); super.exceptionCaught(ctx, cause); } }
-
netty消費端
public class RpcClient{ private static final Logger logger = LoggerFactory.getLogger(RpcClient.class); private EventLoopGroup group; private Channel channel; private String ip; private int port; private RpcConsumerHandler rpcConsumerHandler=new RpcConsumerHandler(); private ExecutorService executorService = Executors.newCachedThreadPool(); public RpcClient(String ip, int port) { this.ip = ip; this.port = port; initClient(); } public void initClient() { try { //1.建立執行緒組 group = new NioEventLoopGroup(); //2.建立啟動助手 Bootstrap bootstrap = new Bootstrap(); //3.設定引數 bootstrap.group(group) //傳輸資料用的channel .channel(NioSocketChannel.class) .option(ChannelOption.SO_KEEPALIVE, Boolean.TRUE) .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 3000) .handler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel channel) throws Exception { ChannelPipeline pipeline = channel.pipeline(); pipeline.addLast(new StringEncoder()); pipeline.addLast(new StringDecoder()); //新增客戶端處理類 pipeline.addLast(rpcConsumerHandler); } }); //4.連線Netty服務端 connect(bootstrap, ip, port, 5); } catch (Exception exception) { exception.printStackTrace(); if (channel != null) { channel.close(); } if (group != null) { group.shutdownGracefully(); } } } private void connect(Bootstrap bootstrap, String host, int port, int retry) { ChannelFuture channelFuture = bootstrap.connect(host, port).addListener(future -> { if (future.isSuccess()) { logger.info("連線服務端成功"); } else if (retry == 0) { logger.error("重試次數已用完,放棄連線"); } else { //第幾次重連: int order = (5 - retry) + 1; //本次重連的間隔 int delay = 1 << order; logger.error("{} : 連線失敗,第 {} 重連....", new Date(), order); bootstrap.config().group().schedule(() -> connect(bootstrap, host, port, retry - 1), delay, TimeUnit.SECONDS); } }); channel = channelFuture.channel(); } /** * 提供給呼叫者主動關閉資源的方法 */ public void close() { if (channel != null) { channel.close(); } if (group != null) { group.shutdownGracefully(); } } /** * 提供訊息傳送的方法 */ public Object send(String msg) throws ExecutionException, InterruptedException { rpcConsumerHandler.setRequestMsg(msg); Future submit = executorService.submit(rpcConsumerHandler); return submit.get(); } public void destroy() throws Exception { if (channel != null) { channel.close(); } if (group != null) { group.shutdownGracefully(); } } public String getIp() { return ip; } public void setIp(String ip) { this.ip = ip; } public int getPort() { return port; } public void setPort(int port) { this.port = port; } }
當建立好客戶端時,傳送請求時資料會交由
rpcConsumerHandler
處理,public class RpcConsumerHandler extends SimpleChannelInboundHandler<String> implements Callable { ChannelHandlerContext context; //傳送的訊息 String requestMsg; //服務端返回的訊息 String responseMsg; public void setRequestMsg(String requestMsg) { this.requestMsg = requestMsg; } @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { context = ctx; } //接收由服務端返回的資料 @Override protected synchronized void channelRead0(ChannelHandlerContext channelHandlerContext, String msg) throws Exception { System.out.println("客戶端結果:"+msg); responseMsg = msg; //喚醒等待的執行緒 notify(); } //傳送資料 @Override public synchronized Object call() throws Exception { //訊息傳送 context.writeAndFlush(requestMsg); //執行緒等待 wait(); return responseMsg; } }
-
呼叫
//新建連線 RpcClient rpcClient = getClient(rpcRegisterEntity.getHost(), rpcRegisterEntity.getPort()); //傳送資料 Object responseMsg = rpcClient.send(JSON.toJSONString(rpcRegisterEntity)); //解析返回的資料 RpcResponse rpcResponse = JSON.parseObject(responseMsg.toString(), RpcResponse.class);