专栏首页JAVA高级架构【原创】自己动手实现RPC服务调用框架

【原创】自己动手实现RPC服务调用框架

引言

本文利用java自带的socket编程实现了一个简单的rpc调用框架,由两个工程组成分别名为battercake-provider(服务提供者)、battercake-consumer(服务调用者)。

设计思路如下: 1、在battercake-provider中,写一个服务叫BatterCakeService

2、在battercake-provider中,启动RpcProvider,发布该服务

3、在battercake-consumer中,启动测试类RpcTest

4、在battercake-consumer中,利用jdk动态代理,获得BatterCakeService的动态代理类BatterCakeService$Proxy0

5、在battercake-consumer中,动态代理类BatterCakeService$Proxy0,与battercake-provider建立socket连接,battercake-provider针对每一个连接,都会启动一个ServerThread处理请求,代理类则发送服务参数等相关信息

6、在battercake-consumer中,接收battercake-provider的ServerThread请求返回的结果。

上述过程时序图如下所示

接下来上代码!!

服务提供者

本部分的工程为battercake-provider,项目结构图如下图所示

先上使用的部分的代码 先创建一个微服务,接口如下

package com.rjzheng.service;public interface BatterCakeService {    /**     * 卖煎饼的服务     * @param name     * @return     */
    public String sellBatterCake(String name);
}

实现类如下

package com.rjzheng.service.impl;import com.rjzheng.service.BatterCakeService;public class BatterCakeServiceImpl implements BatterCakeService {    @Override
    public String sellBatterCake(String name) {        // TODO Auto-generated method stub
        return name+"煎饼,卖的特别好";
    }

}

接下来就是发布服务

package com.rjzheng.start;import com.rjzheng.rpc.RpcProvider;import com.rjzheng.service.BatterCakeService;import com.rjzheng.service.impl.BatterCakeServiceImpl;public class RpcBootStrap {    public static void main(String[] args) throws Exception {
        BatterCakeService batterCakeService =new BatterCakeServiceImpl();        //发布卖煎饼的服务,注册在20006端口
        RpcProvider.export(20006,batterCakeService);
    }
}

接下来是rpc框架调用部分的代码,RpcProvider,该部分代码可以总结为两步

  1. 将需要发布的服务存储在一个内存变量serviceList中
  2. 启动socket,server.accept()方法阻塞在那,监听输入
  3. 针对每一个请求,单独启动一个线程处理

package com.rjzheng.rpc;import java.net.ServerSocket;import java.net.Socket;import java.util.ArrayList;import java.util.Arrays;import java.util.List;/** * RPC服务提供器 * @author zhengrongjun * */public class RpcProvider {    
    //存储注册的服务列表
    private static List<Object> serviceList;    
    /**     * 发布rpc服务     * @param object     * @param port     * @throws Exception     */
    public static void export(int port,Object... services) throws Exception {
        serviceList=Arrays.asList(services);
        ServerSocket server = new ServerSocket(port);
        Socket client = null;        while (true) {            //阻塞等待输入
            client = server.accept();            //每一个请求,启动一个线程处理
            new Thread(new ServerThread(client,serviceList)).start();
        }
    }
}

接下来ServerThread线程处理类的代码,ServerThread主要做以下几个步骤

  1. 读取客户端发送的服务名
  2. 判断服务是否发布
  3. 如果发布,则走反射逻辑,动态调用,返回结果
  4. 如果未发布,则返回提示通知

package com.rjzheng.rpc;import java.io.IOException;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;import java.lang.reflect.Method;import java.net.Socket;import java.util.List;public class ServerThread implements Runnable {    private Socket client = null;    private List<Object> serviceList = null;    public ServerThread(Socket client, List<Object> service) {        this.client = client;        this.serviceList = service;
    }    @Override
    public void run() {
        ObjectInputStream input = null;
        ObjectOutputStream output = null;        try {
            input = new ObjectInputStream(client.getInputStream());
            output = new ObjectOutputStream(client.getOutputStream());            // 读取客户端要访问那个service
            Class serviceClass = (Class) input.readObject();            // 找到该服务类
            Object obj = findService(serviceClass);            if (obj == null) {
                output.writeObject(serviceClass.getName() + "服务未发现");
            } else {                //利用反射调用该方法,返回结果
                try {
                    String methodName = input.readUTF();
                    Class<?>[] parameterTypes = (Class<?>[]) input.readObject();
                    Object[] arguments = (Object[]) input.readObject();
                    Method method = obj.getClass().getMethod(methodName, parameterTypes);  
                    Object result = method.invoke(obj, arguments);  
                    output.writeObject(result); 
                } catch (Throwable t) {
                    output.writeObject(t);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {            try {
                client.close();
                input.close();
                output.close();
            } catch (IOException e) {                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }

    }    private Object findService(Class serviceClass) {        // TODO Auto-generated method stub
        for (Object obj : serviceList) {            boolean isFather = serviceClass.isAssignableFrom(obj.getClass());            if (isFather) {                return obj;
            }
        }        return null;
    }

}

服务消费者

本部分的工程为battercake-consumer,项目结构图如下图所示

先上rpc框架调用部分的代码RpcConsumer,步骤分两步

  1. 封装一个代理类处理器
  2. 返回service的代理类对象

package com.rjzheng.rpc;import java.lang.reflect.Proxy;public class RpcConsumer {    
    public static <T> T getService(Class<T> clazz,String ip,int port) {
        ProxyHandler proxyHandler =new ProxyHandler(ip,port);        return (T)Proxy.newProxyInstance(RpcConsumer.class.getClassLoader(), new Class<?>[] {clazz}, proxyHandler);
    }
}

接下来上代理类处理器的代码,代理类处理步骤分以下几步

  1. 建立socket连接
  2. 封装请求数据,发送给服务提供者
  3. 返回结果

package com.rjzheng.rpc;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.net.Socket;import com.rjzheng.service.BatterCakeService;public class ProxyHandler implements InvocationHandler {    private String ip;    private int port;    public ProxyHandler(String ip, int port) {        // TODO Auto-generated constructor stub
        this.ip = ip;        this.port = port;
    }    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {        // TODO Auto-generated method stub
        Socket socket = new Socket(this.ip, this.port);
        ObjectOutputStream output = new ObjectOutputStream(socket.getOutputStream());
        ObjectInputStream input = new ObjectInputStream(socket.getInputStream());        try {
            output.writeObject(proxy.getClass().getInterfaces()[0]);
            output.writeUTF(method.getName());
            output.writeObject(method.getParameterTypes());
            output.writeObject(args);
            output.flush();
            Object result = input.readObject();            if(result instanceof Throwable) {                throw (Throwable) result;
            }                return result;
        } finally {
            socket.shutdownOutput();
        }
    }

}

接下来建立一个测试类RpcTest如下(跑该测试类前,记得运行在battercake-provider端的RpcBootstrap类发布BatterCakeService服务)

package com.rjzheng.start;import com.rjzheng.rpc.RpcConsumer;import com.rjzheng.service.BatterCakeService;public class RpcTest {    public static void main(String[] args) {
        BatterCakeService batterCakeService=RpcConsumer.getService(BatterCakeService.class, "127.0.0.1", 20006);
        String result=batterCakeService.sellBatterCake("双蛋");
        System.out.println(result);
    }
}

输出结果如下

双蛋煎饼,卖的特别好

至此,我们就实现了一个简易的rpc服务调用框架

本文分享自微信公众号 - JAVA高级架构(gaojijiagou)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2018-06-28

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 作为一名Java程序员的必修课

    上面的知识结构图,是基于对目前java程序员会涉及到的内容以及目前招聘时候的技术要求,另外结合自身对java程序员的一些认知而写出来的

    Java高级架构
  • Java常见面试题及答案 11-20(JVM)

    11.JVM内存分哪几个区,每个区的作用是什么? java虚拟机主要分为以下一个区: 方法区: 1. 有时候也成为永久代,在该区内很少发生垃圾回收,但是并不代表...

    Java高级架构
  • 【原创】自己动手写一个服务网关

    Java高级架构
  • 安卓开发_浅谈ListView之分页列表

    听着music睡
  • 使用dropwizard(3)-加入DI-dagger2

    前言 习惯了Spring全家桶,对spring的容器爱不释手。使用dropwizard,看起来确实很轻,然而,真正使用的时候不得不面临一个问题。我们不可能一个...

    Ryan-Miao
  • 卸载CentOS7-x64自带的OpenJDK并安装Sun的JDK7的方法

        java version "1.6.0"    OpenJDK Runtime Environment (build 1.6.0-b09)    Ope...

    庞小明
  • 通过WebSocket传输文件

    jeremyxu
  • mybatis缓存之一级缓存

    一级缓存:与数据库同一次会话期间查询到的数据会放在本地缓存中,以后如果需要获取相同的数据,直接从缓存中拿,没必要再去查询数据库。

    绝命生
  • Socket 实现聊天功能

    java404
  • Java基础-21(02)总结字符流,IO流编码问题,实用案例必做一遍

    C:把集合中的数据存储到文本文件 package cn.itcast_02; import java.io.BufferedWriter; import jav...

    Java帮帮

扫码关注云+社区

领取腾讯云代金券