专栏首页后台及大数据开发RPC远程过程调用学习之路(一):用最原始代码还原PRC框架

RPC远程过程调用学习之路(一):用最原始代码还原PRC框架

RPC: Remote Procedure Call 远程过程调用,即业务的具体实现不是在自己系统中,需要从其他系统中进行调用实现,所以在系统间进行数据交互时经常使用。

rpc的实现方式有很多,可以通过http和tcp协议进行实现

通过http协议的主要有:  

  1. webService    可以参考我之前的博客  WebService 学习之路(一):了解并使用webService

webService学习之路(二):springMVC集成CXF快速发布webService

webService学习之路(三):springMVC集成CXF后调用已知的wsdl接口

  1. restful           可以参考我之前的博客  Restful 介绍及SpringMVC+restful 实例讲解

而今天要讲的是通过TCP协议实现的远程调用。

为啥已经掌握了webservice和restful等通过http协议实现rpc技术外,还要研究tcp协议实现rpc呢?

因为网络七层协议中,http位于tcp之上,而从传输上而言,越底层同等条件下传输速度更快

另外影响rpc调用的除了传输方式外,另一个就是序列化,而java的阻塞式IO往往成为瓶颈,所以这里设计到了NIO,

NIO知识点就多了,请自己搜索学习。

言归正传,今天不借助其他仁和框架,用简单的代码还原rpc的过程。

大致可以分为下面几部(先了解过程,再看代码更容易理解):

  1. 书写好服务接口和实现,就是我们项目中的业务层,看着service,service.imp就熟悉了 o(∩_∩)o
  2. 把1写好的接口暴露给其他系统,以便调用
  3. 根据暴露了接口的地址和接口信息,进行调用

是不是感觉和调用本地的service一样, 最终就是要达到的这个效果。

文采不好,就要开始贴代码了:

首先是写接口和接口的实现类,和平时看见的、写的没任何区别

接口定义

package com.xiaochangwei.rpc;

public interface RpcTestService {
    String testRpcCall(int count);
}

接口实现类

package com.xiaochangwei.rpc;

import java.text.SimpleDateFormat;
import java.util.Date;

public class RpcTestServiceImpl implements RpcTestService {

    public final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:sss");
    
    @Override
    public String testRpcCall(int count) {
        return dateFormat.format(new Date())+" 调用rpc次数为:" + count;
    }

}

然后就是rpc的精髓了,怎么把服务暴露给其他系统的

其实说白了就是使用java自带的

import java.net.ServerSocket; import java.net.Socket;

网络编程相关的东西socket和对应的IO,因为我们要向服务器发送请求,然后服务器又要返回数据给请求方,

import java.io.ObjectInputStream; import java.io.ObjectOutputStream;

同时还要用到反射,因为我们不可能每个接口实现类的暴露都去写一套方法吧,得共通化吧, 先简单理解为泛型把    代码中看见过Class T 吧 o(∩_∩)o 

先简单理解为过程和socket调用过程一样吧:

  1. 根据约定的端口,服务端起一个ServerSocket,并一直监听该端口
  2. 监听到有请求时,server端通过inputStream取得请求的相关信息
  3. 根据请求信息调用相应方法处理,并返回结果

简易PRC框架

package com.xiaochangwei.rpc;

import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * rpc简易框架
 */
public class RpcFramework {

    /**
     * 暴露服务
     */
    @SuppressWarnings("resource")
    public static void export(final Object service, int port) throws Exception {
        if (service == null)
            throw new IllegalArgumentException("服务实例为空");
        if (port <= 0 || port > 65535)
            throw new IllegalArgumentException("端口号不正确");
        System.out.println("通过端口 [" + port +"] 暴露服务[" + service.getClass().getName() + "]" );
        
        ServerSocket server = new ServerSocket(port);
        while(true){
            try {
                final Socket socket = server.accept();
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            try {
                                ObjectInputStream inputStream = new ObjectInputStream(socket.getInputStream());
                                try {
                                    String methodName = inputStream.readUTF();
                                    Class<?>[] parameterTypes = (Class<?>[]) inputStream.readObject();
                                    Object[] arguments = (Object[]) inputStream.readObject();
                                    ObjectOutputStream output = new ObjectOutputStream(socket.getOutputStream());
                                    try {
                                        Method method = service.getClass().getMethod(methodName,parameterTypes);
                                        Object result = method.invoke(service,arguments);
                                        output.writeObject(result);
                                    } catch (Throwable t) {
                                        output.writeObject(t);
                                    } finally {
                                        output.close();
                                    }
                                } finally {
                                    inputStream.close();
                                }
                            } finally {
                                socket.close();
                            }
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }).start();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 引用服务
     */
    @SuppressWarnings("unchecked")
    public static <T> T refer(final Class<T> interfaceClass, final String host,
            final int port) throws Exception {
        if (interfaceClass == null)
            throw new IllegalArgumentException("接口为空");
        if (!interfaceClass.isInterface())
            throw new IllegalArgumentException(interfaceClass.getName() + " 不是接口");
        if (host == null || host.length() == 0)
            throw new IllegalArgumentException("主机地址为空");
        if (port <= 0 || port > 65535)
            throw new IllegalArgumentException("端口不正确" + port);
        System.out.println("从服务器[" + host + ":" + port + "]取得远程服务[" + interfaceClass.getName() + "]" );
        
        return (T) Proxy.newProxyInstance(interfaceClass.getClassLoader(),
                new Class<?>[] { interfaceClass }, new InvocationHandler() {
                    public Object invoke(Object proxy, Method method,Object[] arguments) throws Throwable {
                        Socket socket = new Socket(host, port);
                        try {
                            ObjectOutputStream output = new ObjectOutputStream(socket.getOutputStream());
                            try {
                                output.writeUTF(method.getName());
                                output.writeObject(method.getParameterTypes());
                                output.writeObject(arguments);
                                ObjectInputStream input = new ObjectInputStream(socket.getInputStream());
                                try {
                                    Object result = input.readObject();
                                    if (result instanceof Throwable) {
                                        throw (Throwable) result;
                                    }
                                    return result;
                                } finally {
                                    input.close();
                                }
                            } finally {
                                output.close();
                            }
                        } finally {
                            socket.close();
                        }
                    }
                });
    }

    /**
     * 暴露RPC服务主调函数
     */
    public static void main(String[] args) throws Exception {
        RpcTestService rpcTestService = new RpcTestServiceImpl();
        RpcFramework.export(rpcTestService, 3125);
    }
}

指定其中的main后,我们的接口就通过指定的端口暴露给其他系统了

其他系统调用也很简单

package com.xiaochangwei.rpc;

public class RpcConsumer {
    public static void main(String[] args) throws Exception {
        RpcTestService rpcTestService = RpcFramework.refer(RpcTestService.class,"127.0.0.1", 3125);
        for (int i = 0; i < 10; i++) {
            String response = rpcTestService.testRpcCall(i);
            System.out.println(response);
            Thread.sleep(1000);
        }
    }
}

是不是感觉和本地调用一样 o(∩_∩)o 

看下效果吧,先暴露接口,再调用

执行调用

测试发现,调用是成功的 o(∩_∩)o  

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

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Java开发中的23种设计模式详解(转)

                                      ——可复用面向对象软件的基础

    肖哥哥
  • 【高并发】秒杀系统高并发请求排队处理

    今天无意中看见了这位兄弟的文章 通过请求队列的方式来缓解高并发抢购(初探)  但文章最后说并发超过500 就会出现超发,看了下代码,的确有这个问题

    肖哥哥
  • java 导出数据为word文档(保持模板格式)

    ①:设计好word文档格式,需要用数据填充的地方用便于识别的长字符串替换  如  aaaaaaaaaaaaaaaa

    肖哥哥
  • ASP.NET MVC三个重要的描述对象:ParameterDescriptor

    Model绑定是为作为目标Action的方法准备参数列表的过程,所以针对参数的描述才是Model绑定的核心。在ASP.NET MVC应用编程接口中,服务于Mod...

    蒋金楠
  • MediaPlayer(一)--Android MediaPlayer的使用方法

    为了模拟实现Android MediaPlayer的实现,需要先了解下MediaPlayer的简单使用方法, 这里只列举其中一种使用方式, 以这个为模板利用ff...

    小蚂蚁与大象
  • 判断监听系统网络状态

    麦克劳林
  • 第三十六课 以太坊solidity如何实现海量空投代币?

    通证token项目启动时,短期内繁荣生态,要舍得给粉丝们打币,把利益分出去。本文聚焦在技术层面,实现如何快速完成TOKEN海量空投,既要节约时间,又要节省TOK...

    辉哥
  • 【设计模式自习室】原型模式

    原型模式要求对象实现一个可以克隆自身的接口(类型)。这样一来,通过原型实例创建新的对象。

    Rude3Knife的公众号
  • 【设计模式自习室】原型模式

    原型模式要求对象实现一个可以克隆自身的接口(类型)。这样一来,通过原型实例创建新的对象。

    后端技术漫谈
  • Java开发中的23种设计模式详解(转)

                                      ——可复用面向对象软件的基础

    肖哥哥

扫码关注云+社区

领取腾讯云代金券