分布式架构的基石,简单的rpc框架实现(java)

zhaww      2022-04-26     145

关键词:

  RPC架构

  RPC 的全称是 Remote Procedure Call,它是一种进程间通信方式。允许像调用本地服务一样调用远程服务。

 

  1.RPC 框架原理

  RPC 框架的目标就是让远程过程(服务)调用更加简单、透明,RPC框架负责屏蔽底层的传输方式TCP 或者 UDP)、序列化方式XML、JSON、二进制)和通信细节。

  框架使用者只需要了解谁在什么位置,提供了什么样的远程服务接口即可,开发者不需要关心底层通信细节和调用过程。

 

  2.最简单的 RPC 框架实现

·  下面通过 JAVA 原生的序列化TCP Socket通信、动态代理反射机制,实现最简单的 RPC 框架。它由三部分组成:

  • 服务提供者:它运行在服务端,负责提供服务接口定义和服务实现类。(EchoServiceEchoServiceImpl
  • 服务发布者,它运行在 RPC 服务端,负责将本地服务发布成远程服务,供其他消费者调用。(RPCExporter
  • 本地服务代理,它运行在 RPC 客户端,通过代理调用远程服务提供者,然后将结果进行封装返回给本地消费者。(RPCImporter

 

  下面看具体代码,首先是服务端接口定义和服务实现类。

  代码清单 :EchoService 

package com.rpc.test;

/**
 * @Description - 调用接口
 * @Author zww
 * @Date 2018/12/10 17:29
 */
public interface EchoService {
    String echo(String ping);
}

  代码清单:EchoServiceImpl

package com.rpc.test;

/**
 * @Description - 调用接口实现
 * @Author zww
 * @Date 2018/12/10 17:30
 */
public class EchoServiceImpl implements EchoService {
    @Override
    public String echo(String ping) {
        return ping != null ? ping + "挺不错的。" : "挺不错的。";
    }
}

  

  RPC 服务端发布者代码实现如下:

  代码清单:RPCExporter

package com.rpc.test;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Method;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

/**
 * @Description - 服务端发布者(提供服务)
 * @Author zww
 * @Date 2018/12/10 17:33
 */
public class RPCExporter {
    //线程池
    static Executor executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());

    public static void exporter(String hostName, int port) throws Exception {
        ServerSocket server = new ServerSocket(); //店家
        server.bind(new InetSocketAddress(hostName, port)); //开店地址
        try {
            while (true) { //开启营业模式
                executor.execute(new ExporterTask(server.accept())); //accept : 来客人了
            }
        } finally {
            server.close();
        }
    }

    //根据约定规则解析请求,返回结果
    private static class ExporterTask implements Runnable {
        Socket client = null; //客户
        public ExporterTask(Socket client) {
            this.client = client;
        }

        @Override
        public void run() { 
            ObjectInputStream inputStream = null;
            ObjectOutputStream outputStream = null;
            try {
                System.out.println("老板娘:诶,来咯!您要点什么!");
                inputStream = new ObjectInputStream(client.getInputStream()); //接收请求
                System.out.println("老板娘:要个回锅肉!");
                String interfaceName = inputStream.readUTF();
                Class<?> service = Class.forName(interfaceName);
                System.out.println("老板娘:微辣!");
                String methodName = inputStream.readUTF();
                System.out.println("老板娘:少油!");
                Class<?>[] parameterType = (Class<?>[])inputStream.readObject();
                System.out.println("老板娘:别放香菜!");
                Object[] arguments = (Object[])inputStream.readObject();
                System.out.println("老板娘:做菜快点!");
                Method method = service.getMethod(methodName, parameterType);
                Object result = method.invoke(service.newInstance(), arguments);
                System.out.println("老板娘:老头子,听清没!");
                System.out.println("老板闷头做菜中!!!!");
                System.out.println("老板娘:帅哥,你的菜好了!");
                outputStream = new ObjectOutputStream(client.getOutputStream()); //返回结果
                outputStream.writeObject(result);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if (outputStream != null) {
                    try {
                        outputStream.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                if (inputStream != null) {
                    try {
                        inputStream.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                if (client != null) {
                    try {
                        client.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
}

  服务发布者的主要职责

  • 监听客户端的 TCP 连接,接收到新的客户端连接之后,将其封装成 Task,由线程池执行。
  • 将客户端发送的码流反序列化成对象,反射调用服务实现者,获取执行结果。
  • 将执行结果反序列化,通过 Socket 发送给客户端。
  • 远程服务调用完成后,释放 Socket 等连接资源,防止句柄泄露。

 

  RPC 客户端本地服务代理代码:

  代码清单:RPCImporter

package com.rpc.test;

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.InetSocketAddress;
import java.net.Socket;

/**
 * @Description -  路人(请求服务)
 * @Author zww
 * @Date 2018/12/11 10:31
 */
public class RPCImporter<S> {

    public S importer(final Class<?> serviceClass, final InetSocketAddress address) {
        //启用远端代理
        return (S) Proxy.newProxyInstance(serviceClass.getClassLoader(), new Class<?>[]{ serviceClass.getInterfaces()[0] }, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                Socket socket = null;
                ObjectOutputStream outputStream = null;
                ObjectInputStream inputStream = null;
                try {
                    socket = new Socket();
                    socket.connect(address);
                    //使用 TCP 方式请求远端方法,以下为约定的传输方式
                    System.out.println("路人:老板娘,点菜咯!");
                    outputStream = new ObjectOutputStream(socket.getOutputStream()); //发送请求
                    System.out.println("路人:要个回锅肉");
                    outputStream.writeUTF(serviceClass.getName());
                    System.out.println("路人:微辣!");
                    outputStream.writeUTF(method.getName());
                    System.out.println("路人:少油!");
                    outputStream.writeObject(method.getParameterTypes());
                    System.out.println("路人:别放香菜!");
                    outputStream.writeObject(args);
                    System.out.println("路人:上菜快点!");
                    inputStream = new ObjectInputStream(socket.getInputStream()); //获取结果
                    System.out.println("路人:吧唧吧唧!");
                    return inputStream.readObject();
                } finally {
                    if (socket != null) socket.close();
                }
            }
        });
    }

}

  本地服务代理的主要功能如下:

  • 将本地的接口调用转换成 JDK 的动态代理,在动态代理中实现接口的远程调用。
  • 创建 Socket 客户端,根据指定地址链接远程服务提供者。
  • 将远程服务调用所需的接口类、方法名、参数列表、返回参数 等编码后发送给服务提供者。
  • 同步阻塞服务端返回应答,获取应答之后返回。

 

  最后:编写测试代码,并看看执行结果

package com.rpc.test;

import java.net.InetSocketAddress;

/**
 * 测试类
 */
public class TestApplication {
    public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    //启用服务提供端(设置 地址端口)
                    RPCExporter.exporter("localhost", 8080);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start();

        //发起服务请求
        RPCImporter<EchoService> importer = new RPCImporter<>();
        //使用远端代理(访问 地址端口)
        EchoService echo = importer.importer(EchoServiceImpl.class, new InetSocketAddress("localhost", 8080));
        System.out.println(echo.echo("这家店味道咋样? 
"));
    }
}

  执行测试结果:

Connected to the target VM, address: ‘127.0.0.1:57656‘, transport: ‘socket‘
路人:老板娘,点菜咯!
老板娘:诶,来咯!您要点什么!
路人:要个回锅肉
路人:微辣!
路人:少油!
路人:别放香菜!
路人:上菜快点!
老板娘:要个回锅肉!
老板娘:微辣!
老板娘:少油!
老板娘:别放香菜!
老板娘:做菜快点!
老板娘:老头子,听清没!
老板闷头做菜中!!!!
老板娘:帅哥,你的菜好了!
路人:吧唧吧唧!味道不错
这家店味道咋样? 
挺不错的。

 

  

 

简述rpc原理实现(代码片段)

...业务对技术的要求,技术架构需要从单体应用架构升级到分布式服务架构,来降低公司的技术成本,更好的适应业务的发展。分布式服务架构的诸多优势,这里就不一一列举了,今天围绕的话题是服务框架,为了推行服务化,必... 查看详情

分布式理论,架构设计自定义rpc(代码片段)

分布式理论,架构设计(四)自定义RPC自定义RPCRMI基于netty实现RPC框架代码实现服务端代码客户端代码自定义RPC在分布式服务框架中,一个最基础的问题就是远程服务是怎么通讯的,在Java领域中有很多可实现... 查看详情

分布式理论,架构设计自定义rpc(代码片段)

分布式理论,架构设计(四)自定义RPC自定义RPCRMI基于netty实现RPC框架代码实现服务端代码客户端代码自定义RPC在分布式服务框架中,一个最基础的问题就是远程服务是怎么通讯的,在Java领域中有很多可实现... 查看详情

dubbo的配置过程,实现原理及架构详解

...更,业务持续高速增长,网站早已从单一应用架构演变为分布式服务架构及流动计算架构。在分布式架构的背景下,在本地调用非本进程内(远程)的资源就变得在所难免。因此,后期涌现出了很多RPC(远程过程调用)的框架,如A... 查看详情

rpc框架实现(持续更新)

...应用的规模不断扩大,常规的垂直应用架构已无法应对,分布式服务架构以及流动计算架构势在必行,rpc基于长连接的远程过程调用应用而生。一:A服务调用B服务,整个调用过程,主要经历如下几个步骤:(摘自优知学院:http... 查看详情

分布式rpc服务调用框架选型:使用dubbo实现分布式服务调用

Dubbo概念Dubbo是一个高性能,轻量级的RPC分布式服务框架提供了三核心能力:面向接口的远程方法调用(@Reference)智能容错负载均衡Dubbo特点:按照分层的方式来架构,可以使各个层之间解耦合Dubbo的角色:提供方:Provider消费方:ConsumerDubbo的... 查看详情

基于框架的rpc通信技术原理解析

...应用的规模不断扩大,常规的垂直应用架构已无法应对,分布式服务架构以及流动计算架构势在必行,亟需一个治理系统确保架构有条不紊的演进。 单一应用架构 当网站流量很小时,只需一个应用,将所有功能都部署在... 查看详情

什么是rpc以及rpc的简单实现

...RemoteProcedureCall):远程过程调用说起RPC,就不能不提到分布式,这个促使RPC诞生的领域。RPC要解决的两个问题:  1.解决分布式系统中,服务之间的调用问题。  2.远程调用时,要能够像本地调用一样方便,让调用者感知不... 查看详情

分布式理论,架构设计自定义rpc(代码片段)

分布式理论,架构设计(四)自定义RPC自定义RPCRMI基于netty实现RPC框架代码实现服务端代码客户端代码自定义RPC在分布式服务框架中,一个最基础的问题就是远程服务是怎么通讯的,在Java领域中有很多可实现... 查看详情

架构师之路—分布式系统—rpc远程过程调用

目录文章目录目录RPCRPC架构成熟的开演RPC框架RPCRPC(RemoteProcedureCall,远程过程调用)是一种计算机程序通信方式,允许运行于一台计算机中的程序调用运行于另一台计算机种的子程序。如果涉及的程序采用了面向... 查看详情

rpc框架dubbo学习入门及环境搭建(springboot+kotlin)

...recall的简写,意思为远程过程调用。rpc应用较多的情景是分布式开发,那什么是分布式开发呢?原本我也是想自己解释的,奈何网上大佬解释得很清楚了,这里就不献丑了,建议阅读完下面推荐的几篇再继续往下【转】分布式架... 查看详情

java实现简单的rpc框架(代码片段)

0引言  RPC,全称为RemoteProcedureCall,即远程过程调用,它是一个计算机通信协议。它允许像调用本地服务一样调用远程服务。它可以有不同的实现方式。如RMI(远程方法调用)、Hessian、Httpinvoker等。另外,RPC是与语言无关的。 ... 查看详情

apachethrift-使用,内部实现及构建一个可扩展的rpc框架

...Thrift的内部实现原理,最后给出一个基于Thrift的可扩展的分布式RPC调用框架,在中小型项目中是一个常见的SOA实践。Thrift介绍ApacheThrift是Facebook开发的远程服务调用框架,它采用接口描述语言(IDL)定义并创建服务,支 查看详情

架构rpc框架的原理

...模不断扩大,常规的垂直应用架构已无法应对,分布式服务架构以及流动计算架构势在必行,需要一个治理系统确保架构有条不紊的演进单一应用架构当网站流量很小时,只需一个应用,将所有功能都部署在... 查看详情

如何为分布式系统优雅的更换rpc

为啥需要更换RPC?很多小伙伴都遇到过需要为分布式系统调用更换RPC的问题,为什么会遇到这种事呢?其实,在系统搭建初期,需求简单,架构简单,最重要的是请求量也少,所以很多系统都采用快速原型开发模式,对rpc的要求... 查看详情

设计一个分布式rpc框架(代码片段)

0前言提前先祝大家春节快乐!好了,先简单聊聊。我从事的是大数据开发相关的工作,主要负责的是大数据计算这块的内容。最近Hive集群跑任务总是会出现Thrift连接HS2相关问题,研究了解了下内部原理,突然来了兴趣,就想着... 查看详情

java实现简单rpc框架

RPC简介RPC,全称为RemoteProcedureCall,即远程过程调用,它是一个计算机通信协议。它允许像调用本地服务一样调用远程服务。它可以有不同的实现方式。如RMI(远程方法调用)、Hessian、Httpinvoker等。另外,RPC是与语言... 查看详情

常用rpc框架及如何设计一个rpc框架

...阅读:<<<架构演变之单体架构<<<架构演变之分布式架构<<<架构演变之面向服务架构(SOA)<<<WebService使用实例<<<架构演变之微服务架构<<<微服务架构与SOA面向服务架构的区别<<<SpringCloud... 查看详情