常用的RPC框架介绍以及示例

RPC简介

RPC(Remote Procedure Call Protocol)远程过程调用协议。一个通俗的描述是:客户端在不知道调用细节的情况下,调用存在于远程计算机上的某个对象,就像调用本地应用程序中的对象一样。比较正式的描述是:一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。那么我们至少从这样的描述中挖掘出几个要点:

  • RPC是协议:既然是协议就只是一套规范,那么就需要有人遵循这套规范来进行实现。目前典型的RPC实现包括:Dubbo、Thrift、GRPC、Hetty等。这里要说明一下,目前技术的发展趋势来看,实现了RPC协议的应用工具往往都会附加其他重要功能。

  • 网络协议和网络IO模型对其透明:既然RPC的客户端认为自己是在调用本地对象。那么传输层使用的是TCP/UDP还是HTTP协议,又或者是一些其他的网络协议它就不需要关心了。既然网络协议对其透明,那么调用过程中,使用的是哪一种网络IO模型调用者也不需要关心。

  • 信息格式对其透明:我们知道在本地应用程序中,对于某个对象的调用需要传递一些参数,并且会返回一个调用结果。至于被调用的对象内部是如何使用这些参数,并计算出处理结果的,调用方是不需要关心的。那么对于远程调用来说,这些参数会以某种信息格式传递给网络上的另外一台计算机,这个信息格式是怎样构成的,调用方是不需要关心的。

  • 应该有跨语言能力:为什么这样说呢?因为调用方实际上也不清楚远程服务器的应用程序是使用什么语言运行的。那么对于调用方来说,无论服务器方使用的是什么语言,本次调用都应该成功,并且返回值也应该按照调用方程序语言所能理解的形式进行描述。

 

为什么要用RPC

其实这是应用开发到一定的阶段的强烈需求驱动的。

  1. 如果我们开发简单的单一应用,逻辑简单、用户不多、流量不大,那我们用不着;

  2. 当我们的系统访问量增大、业务增多时,我们会发现一台单机运行此系统已经无法承受。此时,我们可以将业务拆分成几个互不关联的应用,分别部署在各自机器上,以划清逻辑并减小压力。此时,我们也可以不需要RPC,因为应用之间是互不关联的。

  3. 当我们的业务越来越多、应用也越来越多时,自然的,我们会发现有些功能已经不能简单划分开来或者划分不出来。此时,可以将公共业务逻辑抽离出来,将之组成独立的服务Service应用 。而原有的、新增的应用都可以与那些独立的Service应用 交互,以此来完成完整的业务功能。所以此时,我们急需一种高效的应用程序之间的通讯手段来完成这种需求,所以你看,RPC大显身手的时候来了!
    其实3描述的场景也是服务化 、微服务 和分布式系统架构 的基础场景。即RPC框架就是实现以上结构的有力方式。

常用的RPC框架

目前常用的RPC框架如下:

  1. Thrift:thrift是一个软件框架,用来进行可扩展且跨语言的服务的开发。它结合了功能强大的软件堆栈和代码生成引擎,以构建在 C++, Java, Python, PHP, Ruby, Erlang, Perl, Haskell, C#, Cocoa, JavaScript, Node.js, Smalltalk, and OCaml 这些编程语言间无缝结合的、高效的服务。

  2. Dubbo:Dubbo是一个分布式服务框架,以及SOA治理方案。其功能主要包括:高性能NIO通讯及多协议集成,服务动态寻址与路由,软负载均衡与容错,依赖分析与降级等。 Dubbo是阿里巴巴内部的SOA服务化治理方案的核心框架,Dubbo自2011年开源后,已被许多非阿里系公司使用。

  3. Spring Cloud:Spring Cloud由众多子项目组成,如Spring Cloud Config、Spring Cloud Netflix、Spring Cloud Consul 等,提供了搭建分布式系统及微服务常用的工具,如配置管理、服务发现、断路器、智能路由、微代理、控制总线、一次性token、全局锁、选主、分布式会话和集群状态等,满足了构建微服务所需的所有解决方案。Spring Cloud基于Spring Boot, 使得开发部署极其简单。

  4. gRPC: 一开始由 google 开发,是一款语言中立、平台中立、开源的远程过程调用(RPC)系统。

Thrift实例

Php rpc 服务端提供计算器服务(加减乘除),Java rpc 客户端通过 thrift api 对其进行调用。改编自thrift官方教程,主要是为了看起来更加简单。

  1. 编写接口描述文件 calculator.thrift

 

namespace java app.rpc.learn
namespace php App.Rpc.Learn

enum Operation {
  ADD = 1,
  SUBTRACT = 2,
  MULTIPLY = 3,
  DIVIDE = 4
}

exception InvalidOperation {
  1: i32 whatOp,
  2: string why
}

service Calculator {
   double calculate(1:double num1, 2:double num2, 3:Operation op) throws (1:InvalidOperation ouch),
}
  1. 使用 thrift编译器生成 PHP服务端及Java客户端基础代码

 

// 服务端命令
thrift -gen php:server calculator.thrift 

// 客户端命令
thrift -gen java calculator.thrift
  1. php 服务端实现计算器服务

 

<?php


namespace App\Services;

use App\Rpc\Learn\CalculatorIf;
use App\Rpc\Learn\Operation;
use App\Rpc\Learn\InvalidOperation;


class CalculatorService implements CalculatorIf {
    /**
     * @param double $num1
     * @param double $num2
     * @param int $op
     * @return double
     * @throws InvalidOperation
     */
    public function calculate($num1, $num2, $op) {
        switch ($op) {
            case Operation::ADD:
                $val = $num1 + $num2;
                break;
            case Operation::SUBTRACT:
                $val = $num1 - $num2;
                break;
            case Operation::MULTIPLY:
                $val = $num1 * $num2;
                break;
            case Operation::DIVIDE:
                if ($num2 == 0) {
                    $io = new InvalidOperation();
                    $io->whatOp = $op;
                    $io->why = "Cannot divide by 0";
                    throw $io;
                }
                $val = $num1 / $num2;
                break;
            default:
                $io = new InvalidOperation();
                $io->whatOp = $op;
                $io->why = "Invalid Operation";
                throw $io;
        }
        return $val;
    }
}
  1. 搭建并开启服务
    本文使用了laravel 5.8 框架来搭建php rpc服务器,由composer管理系统依赖,为了引入 thrift 依赖,在项目根目录下执行以下命令即可:

 

composer create-project laravel/laravel Laravel5
cd Larvavel5
composer require apache/thrift

编写 RpcController:

 

<?php


namespace App\Http\Controllers;


use App\Rpc\Learn\CalculatorProcessor;
use App\Services\CalculatorService;
use Illuminate\Http\Request;
use Thrift\Protocol\TBinaryProtocol;
use Thrift\Transport\TBufferedTransport;
use Thrift\Transport\TPhpStream;

class RpcController extends Controller
{
    function handleRequest(Request $request) {
        header('Content-Type', 'application/x-thrift');
        $handler = new CalculatorService();
        $processor = new CalculatorProcessor($handler);

        $transport = new TBufferedTransport(new TPhpStream(TPhpStream::MODE_R | TPhpStream::MODE_W));
        $protocol = new TBinaryProtocol($transport, true, true);

        $transport->open();
        $processor->process($protocol, $protocol);
        $transport->close();
    }
}

在 routes/api.php 文件中配置路由:

 

Route::post('/rpc', [
    'as' => 'rpc',
    'uses' => 'RpcController@handleRequest'
]);

在项目根目录下执行以下命令启动测试服务器:

 

Laravel5> php artisan serve 
  1. 编写 Java rpc 客户端
    thrift 0.11.0 版本目前尚未加入maven 仓库,因此使用以下方式将无法下载到thrift 0.11.0版本的依赖包

 

<dependency>
  <groupId>org.apache.thrift</groupId>
  <artifactId>libthrift</artifactId>
  <version>0.12.0</version>
</dependency>

目前如果要使用0.11.0版,在编译构建thrift源代码后,lib/java/build 目录下有所有需要依赖的jar包,将其加入类路径即可。

该例子中的客户端代码非常简单:

 

package app;

import org.apache.thrift.protocol.TBinaryProtocol;
import org.apache.thrift.protocol.TProtocol;
import org.apache.thrift.transport.THttpClient;
import org.apache.thrift.transport.TTransport;

import app.rpc.learn.Calculator;
import app.rpc.learn.InvalidOperation;
import app.rpc.learn.Operation;

public class RpcClient {

    public static void main(String[] args) {
        TTransport transport = null;
        try {
            transport = new THttpClient("http://localhost:8000/api/rpc");
            TProtocol protocol = new TBinaryProtocol(transport, true, true);
            Calculator.Client rpcCalculator = new Calculator.Client(protocol);
            System.out.println("Start Access Calculate Service From PHP Rpc Server...");
            System.out.print("12 + 5 = ");
            System.out.println(rpcCalculator.calculate(12, 5, Operation.ADD));
            System.out.print("12 - 5 = ");
            System.out.println(rpcCalculator.calculate(12, 5, Operation.SUBTRACT));
            System.out.print("12 * 5 = ");
            System.out.println(rpcCalculator.calculate(12, 5, Operation.MULTIPLY));
            System.out.print("12 / 5 = ");
            System.out.println(rpcCalculator.calculate(12, 5, Operation.DIVIDE));
            System.out.print("1 / 0 = ");
            System.out.println(rpcCalculator.calculate(1, 0, Operation.DIVIDE));
        } catch (InvalidOperation e) {
            System.out.println("Exception: " + e.why);
        } catch (Exception e) {
            System.out.println(e.getMessage());
        } finally {
            if (transport != null) {
                transport.close();
            }
        }   
    }
}

运行结果:

 

Start Access Calculate Service From PHP Rpc Server...
12 + 5 = 17.0
12 - 5 = 7.0
12 * 5 = 60.0
12 / 5 = 2.4
1 / 0 = Exception: Cannot divide by 0

本文设计的全部源代码,可以从这里下载。

https://download.csdn.net/download/robinhunan/12466402 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章