Netty 实现简单RPC调用

RPC,即 Remote Procedure Call(远程过程调用),说得通俗一点就是:调用远程计算机上的服务,就像调用本地服务一样。

RPC 可基于 HTTP 或 TCP 协议,Web Service 就是基于 HTTP 协议的 RPC,它具有良好的跨平台性,但其性能却不如基于 TCP 协议的 RPC。会两方面会直接影响 RPC 的性能,一是传输方式,二是序列化。

众所周知,TCP 是传输层协议,HTTP 是应用层协议,而传输层较应用层更加底层,在数据传输方面,越底层越快,因此,在一般情况下,TCP 一定比 HTTP 快。就序列化而言,Java 提供了默认的序列化方式,但在高并发的情况下,这种方式将会带来一些性能上的瓶颈,于是市面上出现了一系列优秀的序列化框架,比如:Protobuf、Kryo、Hessian、Jackson 等,它们可以取代 Java 默认的序列化,从而提供更高效的性能。
下面是简单实现的基于netty的RPC调用。

一、首先定义消息传递的实体类

span style="font-size:14px;">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>() {  
  
                        @Override  
                        protected 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();    
    }    
}  
  1. 服务端操作,由服务端我们看到具体的数据传输操作是进行序列化的,具体的操作还是比较简单的,就是获取发送过来的信息,这样就可以通过反射获得类名,根据函数名和参数值,执行具体的操作,将执行结果发送给客户端
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(){  
  
            @Override  
            public 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();  
            }  
        });  
    }  
}  
  1. 获取远程调用返回的结果值
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 {  
  
    @Override  
    public String hello(String name) {  
        return "hello "+name;  
    }  
  
}  
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
使用Netty模拟RPC调用需要先了解RPC的基本概念和原理,以及Netty框架的使用方法。 RPC(Remote Procedure Call)远程过程调用是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。它允许程序调用另一个地址空间(通常是共享网络的另一台机器上)的过程或函数,而不用显式地编写远程调用的代码。RPC通常基于客户端/服务器模型,客户端向服务器发送RPC请求,服务器响应请求并返回结果。 Netty是一个高性能的、异步的、事件驱动的网络编程框架,它可以轻松地实现RPC调用。 下面是一个简单的Java代码示例,演示如何使用Netty模拟RPC调用: 1. 首先需要定义一个接口,这个接口定义了要远程调用的方法: ```java public interface HelloService { String sayHello(String name); } ``` 2. 接下来创建一个实现类,实现HelloService接口: ```java public class HelloServiceImpl implements HelloService { @Override public String sayHello(String name) { return "Hello, " + name + "!"; } } ``` 3. 创建一个服务端程序,启动Netty服务端,并将HelloServiceImpl注册到服务端: ```java public class Server { public static void main(String[] args) throws Exception { EventLoopGroup bossGroup = new NioEventLoopGroup(); EventLoopGroup workerGroup = new NioEventLoopGroup(); try { ServerBootstrap b = new ServerBootstrap(); b.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) .childHandler(new ChannelInitializer<SocketChannel>() { @Override public void initChannel(SocketChannel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); pipeline.addLast(new ObjectDecoder(ClassResolvers.cacheDisabled(null))); pipeline.addLast(new ObjectEncoder()); pipeline.addLast(new ServerHandler()); } }); ChannelFuture f = b.bind(8888).sync(); f.channel().closeFuture().sync(); } finally { workerGroup.shutdownGracefully(); bossGroup.shutdownGracefully(); } } private static class ServerHandler extends SimpleChannelInboundHandler<Object> { @Override protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception { if (msg instanceof RpcRequest) { RpcRequest request = (RpcRequest) msg; String className = request.getClassName(); String methodName = request.getMethodName(); Class<?>[] parameterTypes = request.getParameterTypes(); Object[] parameters = request.getParameters(); // 根据类名获取实现类 Class<?> clazz = Class.forName(className); Object service = clazz.newInstance(); // 根据方法名和参数类型获取方法 Method method = clazz.getMethod(methodName, parameterTypes); // 执行方法 Object result = method.invoke(service, parameters); // 返回结果 ctx.writeAndFlush(result); } } } } ``` 4. 创建一个客户端程序,通过Netty客户端向服务端发送RPC请求: ```java public class Client { public static void main(String[] args) throws Exception { EventLoopGroup group = new NioEventLoopGroup(); try { Bootstrap b = new Bootstrap(); b.group(group) .channel(NioSocketChannel.class) .handler(new ChannelInitializer<SocketChannel>() { @Override public void initChannel(SocketChannel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); pipeline.addLast(new ObjectEncoder()); pipeline.addLast(new ObjectDecoder(ClassResolvers.cacheDisabled(null))); pipeline.addLast(new ClientHandler()); } }); ChannelFuture f = b.connect("localhost", 8888).sync(); // 发送RPC请求 RpcRequest request = new RpcRequest(); request.setClassName("com.example.HelloServiceImpl"); request.setMethodName("sayHello"); request.setParameterTypes(new Class<?>[] { String.class }); request.setParameters(new Object[] { "world" }); f.channel().writeAndFlush(request); // 等待响应 f.channel().closeFuture().sync(); } finally { group.shutdownGracefully(); } } private static class ClientHandler extends SimpleChannelInboundHandler<Object> { @Override protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception { // 处理响应结果 System.out.println(msg); ctx.channel().close(); } } } ``` 这样,我们就通过Netty模拟了一次RPC调用。当客户端向服务端发送RPC请求时,服务端会根据请求参数调用相应的方法并返回结果,客户端收到响应结果后输出到控制台。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值