Java Rpc的简单实现

 

 

1、环境

  jdk1.8、 protobuf、 netty5.0final、zookeeper3.4.12

2、模块概要 

    

  Rpc_Client 模拟Rpc_client链接

  Rpc_Server Rpc服务器

  Rpc_Registry 注册中心

  Rpc_Util rpc的公共集合

3、各模块详解  

  

package nanhui.wang.client;


import nanhui.wang.rpc.registry.ZkClientUtil;
import util.RpcService;
import util.RpcUtil;
import java.io.IOException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.List;
import java.util.UUID;

public class ClientProxy implements InvocationHandler {

    private  String host ;
    private int port ;
    public ClientProxy(String host , int port){
        this.host = host ;
        this.port = port ;
    }


    /**
     * 重写Invoke方法 加入自己要处理的业务逻辑
     * @param proxy
     * @param method
     * @param args
     * @return
     * @throws IOException
     */

    public Object invoke(Object proxy, Method method, Object[] args) throws IOException {
        //封装request 然后发送到Server端
        RpcUtil.RpcRequestProto.Builder requestProto =   RpcUtil.RpcRequestProto.newBuilder() ;
        requestProto.setClassName(method.getDeclaringClass().getName());
        requestProto.setMethodName(method.getName()) ;
        requestProto.setRequestId(UUID.randomUUID().toString());
        requestProto.setRequest((RpcUtil.Request) args[0]); 
        
        RpcService rpc = method.getDeclaringClass().getAnnotation(RpcService.class) ;



        String address = getServiceAddress(rpc.name()) ;//注册中心用了zookeeper 通过Service的名称去查找 Server端是否已经注册了此服务

        String [] adds = address.split(":") ; //获取注册此服务的服务器所在地址

        RpcClient client = new RpcClient(adds[0],Integer.parseInt(adds[1]));
        RpcUtil.RpcResponseProto responseProto = client.connect(requestProto.build()) ;//封装后通过RpcClient发送请求到Server端


        return responseProto.getResponseResult();
    }

    public  String getServiceAddress(String serviceName){

        List<String> address =  ZkClientUtil.getAvilied(serviceName) ;

       if(address == null){
           return null ;
       }
       return address.get(0).replace("/",".") ;

    }


}




package nanhui.wang.client;

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.protobuf.ProtobufDecoder;
import io.netty.handler.codec.protobuf.ProtobufEncoder;
import io.netty.handler.codec.protobuf.ProtobufVarint32FrameDecoder;
import io.netty.handler.codec.protobuf.ProtobufVarint32LengthFieldPrepender;
import util.RpcUtil;

/**
 *
 */
public class RpcClient {

    private String host;
    private int port;

    private RpcUtil.RpcResponseProto responseProto;

    public RpcClient(String host, int port) {
        this.host = host;
        this.port = port;

    }

    public RpcUtil.RpcResponseProto connect(final RpcUtil.RpcRequestProto req) {

        EventLoopGroup group = new NioEventLoopGroup();
        try {
            Bootstrap bootstrap = new Bootstrap();

            final RpcClientHandler handler = new RpcClientHandler(req);//自己的处理逻辑 其实啥也没处理 就是直接返回了

            bootstrap.group(group).option(ChannelOption.CONNECT_TIMEOUT_MILLIS,2000)
                    .option(ChannelOption.TCP_NODELAY, true).channel(NioSocketChannel.class)
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel socketChannel) {
                            ChannelPipeline pipeline = socketChannel.pipeline();
                            /**
                             * 处理半包和粘包
                             */
                            pipeline.addLast(new ProtobufVarint32FrameDecoder());
                            pipeline.addLast(new ProtobufVarint32LengthFieldPrepender());

                            //反序列化respinse
                            pipeline.addLast(new ProtobufDecoder(RpcUtil.RpcResponseProto.getDefaultInstance()));
                            //netty 对protobuf支持
                            pipeline.addLast(new ProtobufEncoder());
                            pipeline.addLast(handler);

                        }
                    });






            ChannelFuture future = bootstrap.connect(host, port).sync();//链接到Server端去执行方法
            future.channel().closeFuture().sync();
            responseProto = handler.getResponse();

            return responseProto;
        } catch (InterruptedException e) {
            e.printStackTrace();
            return responseProto;
        } finally {
            group.shutdownGracefully();
            return responseProto;
        }

    }

}


class RpcClientHandler extends ChannelHandlerAdapter {
    private RpcUtil.RpcRequestProto requestProto;
    private RpcUtil.RpcResponseProto proto;

    public RpcClientHandler(RpcUtil.RpcRequestProto requestProto) {
        this.requestProto = requestProto;
    }


    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        proto = (RpcUtil.RpcResponseProto) msg;
        ctx.close();


    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        ctx.writeAndFlush(requestProto);
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {

        ctx.flush();


    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();
    }

    public RpcUtil.RpcResponseProto getResponse() {


        return proto;

    }
}

 

  Server端

 

package nanhui.wang.server;

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.protobuf.ProtobufDecoder;
import io.netty.handler.codec.protobuf.ProtobufEncoder;
import io.netty.handler.codec.protobuf.ProtobufVarint32FrameDecoder;
import io.netty.handler.codec.protobuf.ProtobufVarint32LengthFieldPrepender;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import nanhui.wang.rpc.registry.ZkClientUtil;
import org.msgpack.MessagePack;
import util.RpcService;
import util.RpcUtil;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class RpcServer {

    static Map<String, RpcServer> cache = new ConcurrentHashMap<String, RpcServer>();

    private int port;
    private String host;


    public RpcServer(int port, String host) {

        this.port = port;
        this.host = host;
    }


    public void bind(final Object service) {

        RpcService rpc = service.getClass().getAnnotation(RpcService.class) ;

        ZkClientUtil.register(host, port, rpc.name());//注册服务的地址


        if (!cache.containsKey(service.getClass().getName())) {
            cache.put(service.getClass().getName(), this);
        }


        EventLoopGroup worker = new NioEventLoopGroup();
        EventLoopGroup boss = new NioEventLoopGroup();

        try {
            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.group(boss, worker).option(ChannelOption.SO_BACKLOG, 1024).channel(NioServerSocketChannel.class).handler(new LoggingHandler(LogLevel.INFO)).childHandler(new ChannelInitializer<SocketChannel>() {
                @Override
                protected void initChannel(SocketChannel socketChannel) {
                    ChannelPipeline pipeline = socketChannel.pipeline();
                    //和client端处理类似 添加半包粘包支持
                    pipeline.addLast(new ProtobufVarint32FrameDecoder());
                    pipeline.addLast(new ProtobufVarint32LengthFieldPrepender());
                    //request 反序列化 
                    pipeline.addLast(new ProtobufDecoder(RpcUtil.RpcRequestProto.getDefaultInstance()));
                    //添加netty对protobuf的支持
                    pipeline.addLast(new ProtobufEncoder());
                    //添加自己的业务逻辑
                    pipeline.addLast(new ServerHandler(service));
                }
            });
            ChannelFuture future = bootstrap.bind(port).sync();
            future.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();

        } finally {
            worker.shutdownGracefully();
            boss.shutdownGracefully();
        }

    }

}


class ServerHandler extends ChannelHandlerAdapter {

    private Object service;

    public ServerHandler(Object service) {
        this.service = service;
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {


        RpcUtil.RpcRequestProto req = (RpcUtil.RpcRequestProto) msg;

        Object obj = invoke(req);

        ctx.writeAndFlush(obj);


    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();
    }

    /**
     * 用反射去查找Service中方法
     * 
     * 
     * @param req
     * @return
     * @throws IOException
     * @throws NoSuchMethodException
     * @throws InvocationTargetException
     * @throws IllegalAccessException
     */
    public Object invoke(RpcUtil.RpcRequestProto req) throws IOException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        long time = System.currentTimeMillis();



        String methodName = req.getMethodName();
        String requestId = req.getRequestId();
        Object[] objs = new Object[1]; //我这里请求封装在request中 所以objes 和type的长度都为1 
        objs[0] = req.getRequest();
        Class<?>[] types = new Class<?>[1];
        types[0] = objs[0].getClass() ;

        Method method = service.getClass().getMethod(methodName, types); //找到后 invoke 反射
        Object obj = method.invoke(service, objs);
        
        //封装到response中返回
        RpcUtil.RpcResponseProto responseProto = RpcUtil.RpcResponseProto.newBuilder().setResponseCost((System.currentTimeMillis() - time) + "").setResponseId(requestId).setResponseResult(obj.toString()).build();
        return responseProto;


    }


}

 

  registry

package nanhui.wang.rpc.registry.nanhui.wang.rpc.register.impl;

import nanhui.wang.rpc.registry.Register;
import org.I0Itec.zkclient.ZkClient;

import java.util.List;

/**
 * 这里面没有写的很复杂 就是简单的使用
 * 使用的是Zkclient
 * 也可以使用apacheCurtor
 */
public class ZookeeperRegister implements Register {
    static final String root = "/nanhui/wang/rpc/";
    private static final String zkHost = "192.168.217.45:2181";

    private ZkClient zkClient;


    public ZookeeperRegister() {

        if (zkClient == null) {

            zkClient = new ZkClient(zkHost);

//            zkClient.connect(2000, new Watcher() {
//                public void process(WatchedEvent event) {
//                    Event.EventType type = event.getType();
//                    String path = event.getPath();
//                    Event.KeeperState state = event.getState();
//
//                    switch (state) {
//                        case Expired:
//                            break;
//                        case AuthFailed:
//                            break;
//                        case Disconnected:
//                            break;
//                        case SyncConnected:
//                            break;
//                        case ConnectedReadOnly:
//                            break;
//                        case SaslAuthenticated:
//                            break;
//
//                        default:
//                            break;
//                    }
//
//
//                    switch (type) {
//
//                        case None:
//                            break;
//                        case NodeCreated:
//                            System.out.println(path + "\t注册节点");
//
//
//                            break;
//                        case NodeDeleted:
//                            System.out.println(path + "\t节点删除");
//
//                            break;
//                        case NodeDataChanged:
//                            System.out.println(path + "\t节点数据发生改变");
//
//                            break;
//                        case NodeChildrenChanged:
//                            System.out.println(path + "\t子节点发生改变");
//                            break;
//
//
//                    }
//                }
//            });


        }

    }


    /**
     * 如果存在注册的地址 先删除 再重新注册
     * zkClient  先注册parent 在注册child  有个参数好像可以直接生成一个完整的地址 
     * @param ip
     * @param port
     * @param serviceName
     * @return
     */
    public boolean register(String ip, int port, String serviceName) {

        String registryPath = root + serviceName;

        boolean exist = zkClient.exists(registryPath);
        if (exist) {
            zkClient.delete(registryPath);
        }

        String[] list = registryPath.split("/");
        String p = "";
        for (int i = 0; i < list.length; i++) {
            if (list[i].equals("") )
                continue;
            p += "/" + list[i];
            if(zkClient.exists(p))
                continue;

            zkClient.createPersistent(p);
        }
        //创建临时节点 如果服务器下线 该节点自动删除
        zkClient.createEphemeral(p + "/" + ip + ":" + port);


        return true;


    }

    public List<String> getValidService(String serviceName) {

        String registryPath = root + serviceName;
        boolean exist = zkClient.exists(registryPath);
        if (exist) {

            return zkClient.getChildren(registryPath);


        } else {

            System.out.println("没有可用节点!!!!");
            return null;

        }


    }


}

 

 

  util 

package util;

@RpcService(name =  "Hello")
public interface Hello {

    String say(RpcUtil.Request request);

}





package util;

@RpcService(name = "Hello")
public class HelloImpl implements Hello {
    public  String say(RpcUtil.Request t ) {
        return t.getRequestPar() + " : hello RPC ";

    }
}




package util;

import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;

public class IPLocation {

    public static List<String> loadLocalIPList() {
        List<String> ips = new ArrayList<String>(1);

        Enumeration<NetworkInterface> allNIC = null;
        try {
            allNIC = NetworkInterface.getNetworkInterfaces();
        } catch (Exception ex) {
            System.err.println("获取本机 ip 地址发生错误"+ex.getMessage());
        }

        while (allNIC != null && allNIC.hasMoreElements()) {
            NetworkInterface nic = allNIC.nextElement();
            Enumeration<InetAddress> addresses = nic.getInetAddresses();
            while (addresses.hasMoreElements()) {
                InetAddress ip = addresses.nextElement();
                if (ip != null && (ip instanceof Inet4Address) && (!ip.isAnyLocalAddress() && !ip.isLoopbackAddress())) {
                    ips.add(ip.getHostAddress());
                }
            }
        }

        return ips;
    }
}



package util;



import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;


@Retention(RetentionPolicy.RUNTIME)
public @interface RpcService {
    String name  () default "";


}



syntax ="proto2" ;
option java_package="nanhui.wang.util" ;
option java_outer_classname="RpcUtil";
import "google/protobuf/any.proto";
message RpcRequestProto{
     required  string requestId  = 1 ;
     required  string className = 2;
     required  string methodName = 3;
     required  Request request = 4 ;
}
message Request{
    required  string requestPar = 1;


}



message RpcResponseProto{
    required  string responseId = 1 ;
    required  string responseCost = 2;
    required  string responseResult = 3;

}

 

 

test测试类 

package nanhui.wang.client;

import util.Hello;
import util.RpcUtil;

import java.lang.reflect.Proxy;
/**
* client 端 默认用本机去链接
**/
public class RpcClientDemo { public static void main(String[] args) { Hello obj = clientProxy(Hello.class,"127.0.0.1",8888) ; RpcUtil.Request request = RpcUtil.Request.newBuilder().setRequestPar("Hello Rpc").build() ; System.out.println(obj.say(request)); }
/**
*生成一个冬天代理类
**/
public static <T> T clientProxy(final Class<?> interfceClass , final String host , final int port) { return (T)Proxy.newProxyInstance(interfceClass.getClassLoader(), new Class[] {interfceClass}, new ClientProxy(host,port)); } } package nanhui.wang.server; import util.Hello; import util.HelloImpl; import util.IPLocation; import java.util.List; public class RpcServerBootStrap {
//server 端
public static void main(String[] args) { List<String> ips = IPLocation.loadLocalIPList(); //ip地址 int port = 8888; for (String ip : ips) { Hello hello = new HelloImpl(); RpcServer server = new RpcServer(port, ip); bindService(hello,server); } } public static <T> void bindService(T service, RpcServer server) { server.bind(service); } }

 

 

运行结果

 

 git地址 

  https://github.com/wangnanhui/Rpc_Parent

posted @ 2019-05-13 15:59  王南辉  阅读(463)  评论(0编辑  收藏  举报