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

三丰SanFeng 三丰SanFeng     2022-07-30     113

关键词:

本文首先介绍了什么是Apache Thrift,接着介绍了Thrift的安装部署及如何利用Thrift来实现一个简单的RPC应用,并简单的探究了一下Thrift的内部实现原理,最后给出一个基于Thrift的可扩展的分布式RPC调用框架,在中小型项目中是一个常见的SOA实践。

Thrift介绍

Apache ThriftFacebook 开发的远程服务调用框架,它采用接口描述语言(IDL)定义并创建服务,支持可扩展的跨语言服务开发,所包含的代码生成引擎可以在多种语言中,如 C++, Java, Python, PHP, Ruby, Erlang, Perl, Haskell, C#, Cocoa, Smalltalk 等创建高效的、无缝的服务,其传输数据采用二进制格式,相对 XML JSON 体积更小,对于高并发、大数据量和多语言的环境更有优势。本文将详细介绍 Thrift 的使用,并简要分析Thrift的底层运行原理,最后给出一个基于Thrift的可扩展分布式RPC框架。

Thrift安装部署

首先安装make toolsbison

yum -y install automake gcc gcc-c++

wget http://ftp.gnu.org/gnu/bison/bison-2.5.1.tar.gz

tar -zxvf bison-2.5.1.tar.gz

cd bison-2.5.1

./configure --prefix=/usr/local/

make;make install

然后安装thrift底层依赖库和boost

yum -y install libevent2-devel zlib-devel openssl-devel

wget http://sourceforge.net/projects/boost/files/boost/1.55.0/boost_1_55_0.tar.gz

tar -zxvf boost_1_55_0.tar.gz

cd boost_1_55_0

./bootstrap.sh

./b2 install

安装Thrift

Wget http://www.apache.org/dyn/closer.cgi?path=/thrift/0.9.3/thrift-0.9.3.tar.gz

cd thrift-0.9.3

./configure;make;make install

Thrift cpp源码类介绍

Thrift代码包(位于thrift-0.9.3/lib/cpp/src)有以下几个目录:

concurrency:并发和时钟管理方面的库

processorProcessor相关类

protocolProtocal相关类

transporttransport相关类

serverserver相关类

async:异步rpc相关类

Thrift实现实例

这里介绍一个简单的 Thrift 实现实例,使读者能够快速直观地了解什么是 Thrift 以及如何使用 Thrift 构建服务。

创建一个简单的服务Log

首先根据 Thrift 的语法规范编写脚本文件 log.thrift,代码如下:

struct LogInfo {
1: required string name,
2: optional string content,
}

service LogSender {
void SendLog(1:list<LogInfo> loglist);
string GetLog(1:string logname);
}

其中定义了服务 Log 的两个方法,每个方法包含一个方法名,参数列表和返回类型。每个参数包括参数序号,参数类型以及参数名。 Thrift 是对 IDL(Interface Definition Language) 描述性语言的一种具体实现。因此,以上的服务描述文件使用 IDL 语法编写。使用 Thrift 工具编译 log.thrift,就会生成相应的 LogSender.cpp 文件。该文件包含了在 log.thrift 文件中描述的服务Log的接口定义以及服务调用的底层通信细节,用于构建客户端和服务器端的功能。

调用thrift命令生成代码,命令为thrift --gen <language> <Thrift filename>

[[email protected] log_thrift]# thrift -gen cpp log.thrift

[[email protected] log_thrift]# tree gen-cpp/

gen-cpp/

── log_constants.cpp

── log_constants.h

── LogSender.cpp

── LogSender.h

── LogSender_server.skeleton.cpp

── log_types.cpp

└── log_types.h

Thrift文件与生成的代码对应关系

每个thrift文件会产生四个文件,分别为:${thrift_name}_constants.h${thrift_name}_constants.cpp${thrift_name}_types.h${thrift_name}_types.cpp

对于含有servicethrift文件,会额外生成两个文件,分别为:${service_name}.h${service_name}.cpp

对于含有servicethrift文件,会生成一个可用的server桩:${service_name}_server.skeleton.cpp

 

一个阻塞式服务器实现server.cpp

#include "gen-cpp/LogSender.h"
#include <map>
#include <thrift/protocol/TBinaryProtocol.h>
#include <thrift/server/TSimpleServer.h>
#include <thrift/transport/TServerSocket.h>
#include <thrift/transport/TBufferTransports.h>

using namespace ::apache::thrift;
using namespace ::apache::thrift::protocol;
using namespace ::apache::thrift::transport;
using namespace ::apache::thrift::server;

using boost::shared_ptr;
std::map<std::string, std::string> logMap;

class LogSenderHandler : virtual public LogSenderIf {
 public:
  LogSenderHandler() {
    // Your initialization goes here
  }

  void SendLog(const std::vector<LogInfo> & loglist) {
    // Your implementation goes here
    sleep(5);
    time_t now = time(NULL);
    printf("SendLog, now = %s
", ctime(&now));
    for (size_t i = 0; i < loglist.size(); ++i)
    {
      if (logMap.find(loglist[i].name) == logMap.end())
      {
        printf("name=[%s], content=[%s]
", loglist[i].name.c_str(), loglist[i].content.c_str());
        logMap.insert(std::make_pair(loglist[i].name, loglist[i].content));
      }
    }
  }

  void GetLog(std::string& _return, const std::string& logname) {
    // Your implementation goes here
    std::map<std::string,std::string>::iterator iter = logMap.find(logname);
    if (iter != logMap.end())
    {
      _return = iter->second;
    }
    else
    {
      _return = "Not Found!";
    }
  }

};

int main(int argc, char **argv) 
{
  int port = 9090;
  shared_ptr<LogSenderHandler> handler(new LogSenderHandler());
  shared_ptr<TProcessor> processor(new LogSenderProcessor(handler));
  shared_ptr<TServerTransport> serverTransport(new TServerSocket(port));
  shared_ptr<TTransportFactory> transportFactory(new TBufferedTransportFactory());
  shared_ptr<TProtocolFactory> protocolFactory(new TBinaryProtocolFactory());

  TSimpleServer server(processor, serverTransport, transportFactory, protocolFactory);
  server.serve();
  return 0;
}

阻塞式服务器对应客户端实现client.cpp

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdint.h>使用rust构建grpc服务器

...以更轻松地探索API的模式定义,而无需阅读和理解实现。ApacheThrift在历史上一直是一个流行的选择。然而近年来,由于缺乏来自Facebook的持续支持,以及与fbthrift的分叉分叉,慢慢失去了人气。与此同时,gRPC已经赶上了越来越多... 查看详情

具有静态内部类的构建器模式与具有一个抽象和一个+具体实现的构建器设计模式有啥区别

】具有静态内部类的构建器模式与具有一个抽象和一个+具体实现的构建器设计模式有啥区别【英文标题】:WhatisthedifferencebetweenBuilderPatternwithstaticinnerclassandBuilderdesignpatternswithOneAbstractandone+concreteimplimentations具有静态内部类的构... 查看详情

如何利用azuredevops快速实现自动化构建测试打包及部署

前两天有朋友问我,微软的Azure好用吗,适不适合国人的使用习惯,我就跟他讲了下,Azue很好用,这也是为什么微软云营收一直涨涨涨的原因,基本可以再1个小时内实现自动化构建、打包以及部署到Azure服务器上。利用周末的时... 查看详情

决策树原理及实现

...位立志于脱单的单身男女在找对象的时候就已经完完全全使用了决策树的思想。假设一位母亲在给女儿介绍对象时, 查看详情

自定义栈的实现及使用两个栈模拟队列

一,使用单链表实现栈①栈需要一个栈顶指针②栈的基本操作有出栈和入栈,以及判断栈是否为空③单链表中每个结点表示一个栈元素,每个结点有指向下一个结点的指针。因此,在栈内部需要实现一个单链表。代码如下:public... 查看详情

无法为内部测试选择构建

...【发布时间】:2016-10-1208:18:42【问题描述】:我正在尝试使用iTunes发送构建进行内部测试。我已经使用XCode上传了一个构建(构建4)。此构建卡在“处理中”状态。我找不到任何可以取消它的地方。我已经上传了另一个构建(构... 查看详情

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

...。因此,后期涌现出了很多RPC(远程过程调用)的框架,如ApacheThrift、Hessian、gRPC等。然而,随着RPC框架的推广和使用的日益深入,服务越来越多的情况也衍 查看详情

使用带有内部库的 maven 构建 android 项目

】使用带有内部库的maven构建android项目【英文标题】:Buildandroidprojectusingmavenwithinternallibraries【发布时间】:2014-04-2215:08:51【问题描述】:我有一个使用原生android库和内部库的android库。如果我像android应用程序一样从eclipse执行所... 查看详情

vuejs使用笔记---component内部实现

...据驱动的视图概念,这意味着我们能在普通的HTML模板中使用特殊的用法将DOM“绑定”到底层数据。一旦创建了绑定,DOM将于数据保持同步。以下参考代码与上面的模型相对应<!--这是我们的View--> 查看详情

使用 Laravel 的查询构建器或 Eloquent 将一个表与一个临时表进行内部连接

】使用Laravel的查询构建器或Eloquent将一个表与一个临时表进行内部连接【英文标题】:UsingLaravel\'squerybuilderorEloquenttoinnerjoinatablewithatemporaryone【发布时间】:2020-12-3018:51:44【问题描述】:我有一个相当复杂的查询,我将对其进行... 查看详情

静态内部类方法构建单例模式创建threadpoolexecutor线程池

参考技术A之前写过一篇java线程池ThreadPoolExecutor使用无界队列LinkedBlockingQueue实现多线程简单记录了下ThreadPoolExecutor使用无界队列LinkedBlockingQueue实现多线程的用法。但是在实际应用中,有些并发量大的请求场景,直接如此用会被... 查看详情

使用 Apache Thrift 实现具有 HTTP 协议的服务器/客户端

】使用ApacheThrift实现具有HTTP协议的服务器/客户端【英文标题】:ImplementingaServer/ClientwithHTTPprotocolwithApacheThrift【发布时间】:2016-08-0121:30:36【问题描述】:我是apachethrift的新手。我想用它来帮助我实现一个服务器,它接受来自客... 查看详情

java容器深入浅出之maphashmaphashtable及其它实现类

在Java中,Set的底层事实上是基于Map实现的,Map内部封装了一个Entry内部接口,由实现类来封装key-value对,当value值均为null时,key的集合就形成了Set。因此,Map集合具有如下的一些特点:1.Key集因为是Set的实现,因此是无顺序、不... 查看详情

async及await(代码片段)

...,就将该函数返回一个 Promise,async 直接将返回值使用 Promise.resolve() 进行包裹(与then处理效果相同)。await 只能配套 async 使用,await 内部实现了 generator,await 就是 generator 加上 P... 查看详情

事件监听及处理(代码片段)

...多个if语句来决定是哪个组件产生的事件;第二种方法是使用多个内部类来响应不同组件产生的各种事件,其具体实现又分两种方式,一种是匿名内部类,一种是一般内部类。方法一publicclasstestextendsJFrameimplementsActionListenertestJButt... 查看详情

java-深入理解java嵌套类内部类以及内部类builder构建构造函数(代码片段)

...两种类型:静态嵌套类和非静态嵌套类。静态嵌套类使用很少,最重要的是非静态嵌套类,也即是被称作为内部类(inner)。嵌套类从JDK1.1开始引入。其中inner类 查看详情

threadlocal的使用场景及实现原理(代码片段)

...此时ThreadLocal变量相当于成为了线程内部的全局变量)2.使用场景单例的对象中属性线程内共享,线程间无关;工具类属性线程内共享,线程间无关。为什么这么说呢?下面看4个问题:(1)对象为什么要是单例的?如果对象不是... 查看详情

建造者模式(代码片段)

...得到它们,建造过程及细节不需要知道类型:创建型2、使用场景如果一个对象有非常复杂的内部结构(很多属性)想把复杂对象的创建和使用分离3、优点封装性好,创建和使用分离扩展性好、建造类之间独立、一定程度上解耦4... 查看详情