自定义RPC

一、概述

RPC(remote procedure call),即远程过程调用,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络实现的技术。
常见的RPC框架有:阿里的Dubbo、Spring cloud 、Google 出品的 grpc 等等。
在这里插入图片描述

  1. 服务消费方(Client)调用本地方法
  2. client stub 接收到调用后负责将方法、参数等信息封装成能够进行网络传输的消息体
  3. client stub 将消息体进行编码发送到服务端
  4. server stub 收到消息后解码
  5. server stub 根据解码结果调用本地服务
  6. 本地服务执行并将结果返回给server stub
  7. server stub 将结果封装并编码发送给客户端
  8. client stub 接收到消息解码
  9. client stub 解码成功得到结果

RPC的目标就是将 2-8步骤 都封装起来,用户无需关系这些细节,就像可以调用本地接口一样。

二、设计和实现

2.1 结构设计

在这里插入图片描述

  • client (客户端) :创建两个接口 HelloRPC、HelloNetty,还有一个测试类TestNettyRPC
  • client stub : 一个客户端代理类 NettyRPCProxy、一个客户端业务处理类 ResultHandler
  • Server(服务端): 创建两个名称和客户端一样的接口HelloRPC、HelloNetty,并且实现这个接口里面的方法。
  • Server stub :一个网络处理器InvokeHandler、一个服务器的业务处理器NettyRPCServer

注意:客户端的接口必须和服务端的接口保持一致。

在这里插入图片描述
这里设计到一个jar,主要作用是获取接口实现类

        <!-- https://mvnrepository.com/artifact/org.reflections/reflections -->
        <dependency>
            <groupId>org.reflections</groupId>
            <artifactId>reflections</artifactId>
            <version>0.9.11</version>
        </dependency>

client.HelloNetty

/*
 * Copyright (C), 2013-2019, 天津大海云科技有限公司
 */
package com.jikang.rpc.client;

/**
 * @author yangjikang
 * @date 2019/7/17 15:36
 * @modified By yangjikang
 */
public interface HelloNetty {
    String hello();
}

cilent.HelloRPC

/*
 * Copyright (C), 2013-2019, 天津大海云科技有限公司
 */
package com.jikang.rpc.client;

/**
 * @author yangjikang
 * @date 2019/7/17 15:37
 * @modified By yangjikang
 */
public interface HelloRPC {
    String hello(String name);
}

client.TestNettyRPC

/*
 * Copyright (C), 2013-2019, 天津大海云科技有限公司
 */
package com.jikang.rpc.client;

import com.jikang.rpc.clientStub.NettyRPCProxy;

/**
 * @author yangjikang
 * @date 2019/7/17 15:37
 * @modified By yangjikang
 */
public class TestNettyRPC {

    public static void main(String[] args) {
        //第一次远程调用
        HelloNetty helloNetty = (HelloNetty) NettyRPCProxy.create(HelloNetty.class);
        System.out.println(helloNetty.hello());

        //第二次远程调用
        HelloRPC helloRPC = (HelloRPC) NettyRPCProxy.create(HelloRPC.class);
        System.out.println(helloRPC.hello("RPC"));
    }
}

clientStub.NettyRPCProxy

/*
 * Copyright (C), 2013-2019, 天津大海云科技有限公司
 */
package com.jikang.rpc.clientStub;

import com.jikang.rpc.serverStub.ClassInfo;
import com.jikang.rpc.serverStub.InvokeHandler;
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.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;

/**
 *
 * 客户端代理类
 *
 * @author yangjikang
 * @date 2019/7/17 15:38
 * @modified By yangjikang
 */
public class NettyRPCProxy {

    //根据接口创建代理对象
    public static  Object create (Class target){

        return Proxy.newProxyInstance(target.getClassLoader(), new Class[]{target}, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                //封装ClassInfo
                ClassInfo classInfo = new ClassInfo();
                classInfo.setClassName(target.getName());
                classInfo.setMethodName(method.getName());
                classInfo.setObjects(args);
                classInfo.setTypes(method.getParameterTypes());

                //开始用Netty发送数据
                EventLoopGroup group = new NioEventLoopGroup();
                ResultHandler resultHandler = new ResultHandler();
                try{
                    Bootstrap bootstrap = new Bootstrap();
                    bootstrap.group(group)
                            .channel(NioSocketChannel.class)
                            .handler(new ChannelInitializer<SocketChannel>() {

                                @Override
                                protected void initChannel(SocketChannel socketChannel) throws Exception {
                                    ChannelPipeline pipeline = socketChannel.pipeline();
                                    pipeline.addLast("encoder",new ObjectEncoder());//编码器
                                    pipeline.addLast("decoder",new ObjectDecoder(Integer.MAX_VALUE,
                                            ClassResolvers.cacheDisabled(null)));//解码器
                                    pipeline.addLast(resultHandler);
                                }
                            });
                    ChannelFuture channelFuture = bootstrap.connect("127.0.0.1", 9999).sync();
                    channelFuture.channel().writeAndFlush(classInfo).sync();
                    channelFuture.channel().closeFuture().sync();
                }catch (Exception e){
                    e.printStackTrace();
                }finally {
                        group.shutdownGracefully();
                }
                return resultHandler.getResponse();
            }
        });
    }


}

clientStub.ResultHandler

/*
 * Copyright (C), 2013-2019, 天津大海云科技有限公司
 */
package com.jikang.rpc.clientStub;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;

/**
 *
 * 客户端业务处理类
 *
 * @author yangjikang
 * @date 2019/7/17 15:38
 * @modified By yangjikang
 */
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;
        ctx.close();
    }
}

server.HelloNetty

/*
 * Copyright (C), 2013-2019, 天津大海云科技有限公司
 */
package com.jikang.rpc.server;

/**
 * @author yangjikang
 * @date 2019/7/17 15:38
 * @modified By yangjikang
 */
public interface HelloNetty {
    String hello();
}

server.HelloNettyImpl

/*
 * Copyright (C), 2013-2019, 天津大海云科技有限公司
 */
package com.jikang.rpc.server;

/**
 * @author yangjikang
 * @date 2019/7/17 15:39
 * @modified By yangjikang
 */
public class HelloNettyImpl implements HelloNetty {

    @Override
    public String hello() {
        return "hello,netty";
    }
}

server.HelloRPC

/*
 * Copyright (C), 2013-2019, 天津大海云科技有限公司
 */
package com.jikang.rpc.server;

/**
 * @author yangjikang
 * @date 2019/7/17 15:39
 * @modified By yangjikang
 */
public interface HelloRPC {
    String hello(String name);
}

server.HelloRPCImpl

/*
 * Copyright (C), 2013-2019, 天津大海云科技有限公司
 */
package com.jikang.rpc.server;

/**
 * @author yangjikang
 * @date 2019/7/17 15:39
 * @modified By yangjikang
 */
public class HelloRPCImpl implements HelloRPC {

    @Override
    public String hello(String name) {
        return "hello,"+name;
    }
}

serverStub.ClassInfo

/*
 * Copyright (C), 2013-2019, 天津大海云科技有限公司
 */
package com.jikang.rpc.serverStub;

import java.io.Serializable;

/**
 *
 * 封装类信息
 *
 *
 * @author yangjikang
 * @date 2019/7/17 15:40
 * @modified By yangjikang
 */
public class ClassInfo implements Serializable {

    private static final long serialVersionUID = 2989221128581296303L;

    private String className;//类名

    private String methodName;//方法名

    private Class<?>[] types;//参数类型

    private Object[] objects;//参数列表

    public static long getSerialVersionUID() {
        return serialVersionUID;
    }

    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;
    }
}

serverStub.InvokeHandler

/*
 * Copyright (C), 2013-2019, 天津大海云科技有限公司
 */
package com.jikang.rpc.serverStub;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import org.reflections.Reflections;

import java.lang.reflect.Method;
import java.util.Set;

/**
 *
 * 服务器端业务处理类
 *
 * @author yangjikang
 * @date 2019/7/17 15:40
 * @modified By yangjikang
 */
public class InvokeHandler extends ChannelInboundHandlerAdapter {


    /**
     * 得到某个接口下某个实现类的名字
     * @param classInfo
     * @return
     * @throws Exception
     */
    private String getImplClassName(ClassInfo classInfo) throws Exception {
        //服务方接口和实现类所在包路径
        String interfacePath = "com.jikang.rpc.server";
        int lastDot = classInfo.getClassName().lastIndexOf(".");
        String interfaceName = classInfo.getClassName().substring(lastDot);
        Class superaClass = Class.forName(interfacePath + interfaceName);
        //得到某接口下所有实现类
        Reflections reflections = new Reflections(interfacePath);
        Set<Class> implClassSet = reflections.getSubTypesOf(superaClass);
        if (implClassSet.size() == 0) {
            System.out.println("未找到实现类");
            return null;
        }else if (implClassSet.size()>1){
            System.out.println("找到多个实现类,未明确使用哪一个");
            return null;
        }else{
            //把集合转化成数组
            Class[] classes = implClassSet.toArray(new Class[0]);
            return classes[0].getName();//得到实现类的名称
        }

    }
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        ClassInfo classInfo = (ClassInfo) msg;
        Object object = Class.forName(getImplClassName(classInfo)).newInstance();
        Method method = object.getClass().getMethod(classInfo.getMethodName(), classInfo.getTypes());
        //通过反射调用实现类的方法
        Object result = method.invoke(object, classInfo.getObjects());
        ctx.writeAndFlush(result);
    }
}

serverStub.NettyRPCServer

/*
 * Copyright (C), 2013-2019, 天津大海云科技有限公司
 */
package com.jikang.rpc.serverStub;

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.serialization.ClassResolver;
import io.netty.handler.codec.serialization.ClassResolvers;
import io.netty.handler.codec.serialization.ObjectDecoder;
import io.netty.handler.codec.serialization.ObjectEncoder;

/**
 *
 * 网络处理服务器
 *
 * @author yangjikang
 * @date 2019/7/17 15:40
 * @modified By yangjikang
 */
public class NettyRPCServer {
    private int port;

    public NettyRPCServer(int port) {
        this.port = port;
    }

    public void start(){
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try{
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            serverBootstrap.group(bossGroup,workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .option(ChannelOption.SO_BACKLOG,128)
                    .childOption(ChannelOption.SO_KEEPALIVE,true)
                    .localAddress(port)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            ChannelPipeline pipeline = socketChannel.pipeline();
                            pipeline.addLast("encoder",new ObjectEncoder());//编码器
                            pipeline.addLast("decoder",new ObjectDecoder(Integer.MAX_VALUE,
                                    ClassResolvers.cacheDisabled(null)));//解码器
                            pipeline.addLast(new InvokeHandler());
                        }
                    });
            ChannelFuture channelFuture = serverBootstrap.bind(port).sync();
            System.out.println("-------server is ready------");
            channelFuture.channel().closeFuture().sync();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            workerGroup.shutdownGracefully();
            bossGroup.shutdownGracefully();
        }
    }

    public static void main(String[] args) {
        NettyRPCServer nettyRPCServer = new NettyRPCServer(9999);
        nettyRPCServer.start();
    }
}

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值