前言
架构服务化后,需要实现一套方便调用各服务的框架,现在开源如日中天,优先会寻找开源实现,如果没有合适自家公司业务的,才会考虑从零开发,尤其是一切以KPI为准绳的公司,谁会跟钱过不去?N个月之前,公司大神就开始调研了,最后选中了Thrift这个RPC服务框架。使用不熟悉的技术,我会感到很恐惧,它就相当于一个黑盒,我对它一无所知,它是如何运转的?出了问题该如何解决?带着一丝不安,查阅了相关技术文档。

RPC
很早之前听说过soap,restful api,rpc之类的服务协议,一直都没有机会深入实践,对它们理解的不够深。它们的目的都是提供本地调用远程服务的能力,只是实现方式不同而已。RPC(remote procedure call)意思是远程过程调用,编码时可以把它当作本地方法一样调用,无需关心内部的实现细节,对于调用方很友好很简单。我查阅资料,发现RPC之类的东西很早很早以前就出现了,存在即是合理的,肯定有它的理由。跟本地调用相比有什么优点缺点呢?根据查阅的资料以及自己的理解总结如下:
优势
1 提高系统吞吐能力
2 业务服务解耦
3 更易构建服务分布式集群
4 基础服务重用更方便
劣势
1 因网络开销方法执行时间更长
2 系统更复杂对运维挑战很大
3 排错成本增加
4 数据序列化消耗CPU资源
现在是移动互联时代,数据无时无刻不在产生着,随着时间的推移,随着用户数的增加,随着更多业务的开展,随着功能迭代的频繁,只有业务服务化只有更易构建分布式集群的架构才能应对这些挑战,毕竟单台服务器的能力是有限的,即使是IOE这种高端设备。尤其是现在地下黑产如此猖獗的今天,时不时的遭受到类DDOS攻击,可以方便扩展节点的架构是多么重要,否则会死的很惨。
因此通过RPC协议实现微服务架构是利大于弊的。

Thrift
Thrift是RPC服务协议的一种实现,它是由Facebook开发,2007年开源并且2008年成为Apache开源项目。它的实现目标是支持多语言跨平台简单易使用高性能对业务开发透明屏蔽实现细节专注业务开发。大部分语言提供了类库,可以让开发人员只专注在业务接口实现部分。

实现原理
它由传输层,协议层,处理器,服务层所组成,每个部分不互相依赖,职责单一。
传输层:流协议传输数据,支持socket,http,文件等媒介
协议层:数据封包解包,支持binary,compact,json等
处理器:接口代理,请求转发给相应接口处理
服务层:组装传输层,协议层,处理器,提供RPC服务
客户端调用接口-》客户端数据封包-》传输层-》服端解包-》服端处理器-》服端接口处理。处理完成遵循同样的链路响应。

服端网络模型
简单说明,不做深入了解。
1 单进程
2 单进程多线程
3 单进程事件驱动

IDL
IDL(interface description language)接口描述语言,它包含简单数据类型定义,复杂数据类型结构体定义,复杂数据类型列表定义集合定义,异常类型定义,命名空间声明,接口定义规范等,通过这些规范的组合可以定义好任意复杂的接口,定义好之后,使用IDL编译器可以生成指定语言源码,传输层协议层处理器这些代码会自动生成,只需要专注业务接口具体实现即可。具体类型定义参考官方文档吧。

安装
安装很简单,不同的系统官方文档都有说明,不需要完全死板按照步骤执行,相关依赖如果存在可以跳过相应步骤。安装成功之后就可以使用thrift命令编译IDL文件了。

命令
定义好的IDL文件名以.thrift结尾,通过thrift编译。命令参数说明如下:

 Usage: thrift [options] file
Options:
-version Print the compiler version
-o dir Set the output directory for gen-* packages
(default: current directory)
-out dir Set the ouput location for generated files.
(no gen-* folder will be created)
-I dir Add a directory to the list of directories
searched for include directives
-nowarn Suppress all compiler warnings (BAD!)
-strict Strict compiler warnings on
-v[erbose] Verbose mode
-r[ecurse] Also generate included files
-debug Parse debug trace to stdout
--allow-neg-keys Allow negative field keys (Used to preserve protocol
compatibility with older .thrift files)
--allow-64bit-consts Do not print warnings about using -bit constants
--gen STR Generate code with a dynamically-registered generator.
STR has the form language[:key1=val1[,key2[,key3=val3]]].
Keys and values are options passed to the generator.
Many options will not require values. Options related to audit operation
--audit OldFile Old Thrift file to be audited with 'file'
-Iold dir Add a directory to the list of directories
searched for include directives for old thrift file
-Inew dir Add a directory to the list of directories
searched for include directives for new thrift file Available generators (and options):
as3 (AS3):
bindable: Add [bindable] metadata to all the struct classes.
c_glib (C, using GLib):
cocoa (Cocoa):
log_unexpected: Log every time an unexpected field ID or type is encountered.
debug_descriptions:
Allow use of debugDescription so the app can add description via a cateogory/extension
validate_required:
Throws exception if any required field is not set.
async_clients: Generate clients which invoke asynchronously via block syntax.
pods: Generate imports in Cocopods framework format.
promise_kit: Generate clients which invoke asynchronously via promises.
cpp (C++):
cob_style: Generate "Continuation OBject"-style classes.
no_client_completion:
Omit calls to completion__() in CobClient class.
no_default_operators:
Omits generation of default operators ==, != and <
templates: Generate templatized reader/writer methods.
pure_enums: Generate pure enums instead of wrapper classes.
include_prefix: Use full include paths in generated files.
moveable_types: Generate move constructors and assignment operators.
csharp (C#):
async: Adds Async support using Task.Run.
wcf: Adds bindings for WCF to generated classes.
serial: Add serialization support to generated classes.
nullable: Use nullable types for properties.
hashcode: Generate a hashcode and equals implementation for classes.
union: Use new union typing, which includes a static read function for union types.
d (D):
dart (Dart):
library_name: Optional override for library name.
library_prefix: Generate code that can be used within an existing library.
Use a dot-separated string, e.g. "my_parent_lib.src.gen"
pubspec_lib: Optional override for thrift lib dependency in pubspec.yaml,
e.g. "thrift: 0.x.x". Use a pipe delimiter to separate lines,
e.g. "thrift:| git:| url: git@foo.com"
delphi (delphi):
ansistr_binary: Use AnsiString for binary datatype (default is TBytes).
register_types: Enable TypeRegistry, allows for creation of struct, union
and container instances by interface or TypeInfo()
constprefix: Name TConstants classes after IDL to reduce ambiguities
events: Enable and use processing events in the generated code.
xmldoc: Enable XMLDoc comments for Help Insight etc.
erl (Erlang):
legacynames: Output files retain naming conventions of Thrift 0.9. and earlier.
maps: Generate maps instead of dicts.
otp16: Generate non-namespaced dict and set instead of dict:dict and sets:set.
go (Go):
package_prefix= Package prefix for generated files.
thrift_import= Override thrift package import path (default:git.apache.org/thrift.git/lib/go/thrift)
package= Package name (default: inferred from thrift file name)
ignore_initialisms
Disable automatic spelling correction of initialisms (e.g. "URL")
read_write_private
Make read/write methods private, default is public Read/Write
gv (Graphviz):
exceptions: Whether to draw arrows from functions to exception.
haxe (Haxe):
callbacks Use onError()/onSuccess() callbacks for service methods (like AS3)
rtti Enable @:rtti for generated classes and interfaces
buildmacro=my.macros.Class.method(args)
Add @:build macro calls to generated classes and interfaces
hs (Haskell):
html (HTML):
standalone: Self-contained mode, includes all CSS in the HTML files.
Generates no style.css file, but HTML files will be larger.
noescape: Do not escape html in doc text.
java (Java):
beans: Members will be private, and setter methods will return void.
private-members: Members will be private, but setter methods will return 'this' like usual.
nocamel: Do not use CamelCase field accessors with beans.
fullcamel: Convert underscored_accessor_or_service_names to camelCase.
android: Generated structures are Parcelable.
android_legacy: Do not use java.io.IOException(throwable) (available for Android 2.3 and above).
option_type: Wrap optional fields in an Option type.
java5: Generate Java 1.5 compliant code (includes android_legacy flag).
reuse-objects: Data objects will not be allocated, but existing instances will be used (read and write).
sorted_containers:
Use TreeSet/TreeMap instead of HashSet/HashMap as a implementation of set/map.
generated_annotations=[undated|suppress]:
undated: suppress the date at @Generated annotations
suppress: suppress @Generated annotations entirely
javame (Java ME):
js (Javascript):
jquery: Generate jQuery compatible code.
node: Generate node.js compatible code.
ts: Generate TypeScript definition files.
json (JSON):
merge: Generate output with included files merged
lua (Lua):
omit_requires: Suppress generation of require 'somefile'.
ocaml (OCaml):
perl (Perl):
php (PHP):
inlined: Generate PHP inlined files
server: Generate PHP server stubs
oop: Generate PHP with object oriented subclasses
rest: Generate PHP REST processors
nsglobal=NAME: Set global namespace
validate: Generate PHP validator methods
json: Generate JsonSerializable classes (requires PHP >= 5.4)
py (Python):
twisted: Generate Twisted-friendly RPC services.
tornado: Generate code for use with Tornado.
no_utf8strings: Do not Encode/decode strings using utf8 in the generated code. Basically no effect for Python .
coding=CODING: Add file encoding declare in generated file.
slots: Generate code using slots for instance members.
dynamic: Generate dynamic code, less code generated but slower.
dynbase=CLS Derive generated classes from class CLS instead of TBase.
dynfrozen=CLS Derive generated immutable classes from class CLS instead of TFrozenBase.
dynexc=CLS Derive generated exceptions from CLS instead of TExceptionBase.
dynimport='from foo.bar import CLS'
Add an import line to generated code to find the dynbase class.
package_prefix='top.package.'
Package prefix for generated files.
old_style: Deprecated. Generate old-style classes.
rb (Ruby):
rubygems: Add a "require 'rubygems'" line to the top of each generated file.
namespaced: Generate files in idiomatic namespaced directories.
st (Smalltalk):
swift (Swift):
log_unexpected: Log every time an unexpected field ID or type is encountered.
debug_descriptions:
Allow use of debugDescription so the app can add description via a cateogory/extension
async_clients: Generate clients which invoke asynchronously via block syntax.
promise_kit: Generate clients which invoke asynchronously via promises.
xml (XML):
merge: Generate output with included files merged
no_default_ns: Omit default xmlns and add idl: prefix to all elements
no_namespaces: Do not add namespace definitions to the XML model
xsd (XSD):

实践
平常使用PHP开发,所以使用PHP实践下,对它有更深入的了解,一般服端开发使用编译型的语言,执行效率更高,以实现简单的NoSQL功能为例子(源码里有各种语言的实现例子)。
1 接口定义(nosql.thrift)

/*
* thrift简单示例 模仿thrift源码教程
* nosql数据库简单实现
*/ #由wadeyu创建 wadeyu.cnblogs.com /**
* 命名空间
*/
namespace cpp NoSql
namespace java NoSql
namespace php NoSql /**
* 异常:无效参数
*/
exception InvalidParametorException{
} /**
* 定义服务
*/
service NoSqlService{
/**
* 获取key的值
*/
string get(1:string key) throws (1:InvalidParametorException ex), /**
* 设置值
*/
bool set_(1:string key, 2:string value) throws (1:InvalidParametorException ex), /**
* 自增
*/
i32 incr(1:string key) throws (1:InvalidParametorException ex),
}

2 编译(生成服端代码)

[wadeyu@localhost thriftdemo]$ thrift --gen php:server -out ./server ./meta/nosql.thrift

3 编写服端启动代码

 <?php
/*
* php简单服端脚本
*/ #author by wadeyu: wadeyu.cnblogs.com define('BASE_DIR',dirname(__FILE__) . '/');
define('VENDOR_DIR',BASE_DIR.'vendor/'); use Server\NoSqlHandler;
use NoSql\NoSqlServiceProcessor;
use Thrift\Factory\TBinaryProtocolFactory;
use Thrift\Factory\TTransportFactory;
use Thrift\Server\TServerSocket;
use Thrift\Server\TSimpleServer; $loader = include_once VENDOR_DIR.'autoload.php';
$loader->addPsr4('Server\\',BASE_DIR.'server/'); include_once BASE_DIR.'server/NoSql/NoSqlService.php';
include_once BASE_DIR.'server/NoSql/Types.php'; $serverTransport = new TServerSocket('localhost',9090);
$clientTransport = new TTransportFactory;
$binaryProtocol = new TBinaryProtocolFactory;
$nosqlProcessor = new NoSqlServiceProcessor( new NoSqlHandler );
$simpleServer = new TSimpleServer(
$nosqlProcessor,
$serverTransport,
$clientTransport,
$clientTransport,
$binaryProtocol,
$binaryProtocol
);
echo "start listening:localhost:9090 \n";
$simpleServer->serve();

4 启动服务

[wadeyu@localhost thriftdemo]$ php phpserver.php
start listening:localhost:

5 编译(生成客户端代码)

[wadeyu@localhost thriftdemo]$ thrift --gen php -out ./client ./meta/nosql.thrift

6 客户端接口调用

 <?php
/*
* php简单客户端脚本
*/ #author by wadeyu: wadeyu.cnblogs.com define('BASE_DIR',dirname(__FILE__) . '/');
define('VENDOR_DIR',BASE_DIR.'vendor/'); use Thrift\Protocol\TBinaryProtocol;
use Thrift\Transport\TSocket;
use NoSql\NoSqlServiceClient; $loader = include_once VENDOR_DIR.'autoload.php'; include_once BASE_DIR.'client/NoSql/NoSqlService.php';
include_once BASE_DIR.'client/NoSql/Types.php'; $transport = new TSocket('localhost',9090);
$protocol = new TBinaryProtocol($transport);
$service = new NoSqlServiceClient($protocol);
$startTime = microtime(true);
try{
$transport->open();
$key1 = 'test1';
$ret = $service->get($key1);
var_dump($ret);
$ret = $service->set_($key1,'test1');
var_dump($ret);
$key2 = 'test2';
$ret = $service->incr($key2);
var_dump($ret);
$transport->close();
}catch(Exception $ex){
throw $ex;
}
var_dump('cost:'.(microtime(true)-$startTime));
 [wadeyu@localhost thriftdemo]$ php phpclient.php
string() "NULL"
bool(true)
int()
string() "cost:0.22478580474854"

示例源码下载
https://github.com/wadeyu/thriftdemo/archive/master.zip

后记
纸上得来终觉浅,得知此事要躬行。其实很早就开始写这篇文章了,自己很懒,拖了几个星期才完成,以后要改掉这个毛病了。

参考资料
【1】thrift官方文档

http://thrift.apache.org/docs/
【2】thrift安装

http://thrift.apache.org/docs/install/
【3】thrift IDL规范

http://thrift.apache.org/docs/idl

http://thrift.apache.org/docs/types
【4】thrift实现论文

http://thrift.apache.org/static/files/thrift-20070401.pdf

RPC服务框架探索之Thrift的更多相关文章

  1. Thrift 个人实战--Thrift RPC服务框架日志的优化

    前言: Thrift作为Facebook开源的RPC框架, 通过IDL中间语言, 并借助代码生成引擎生成各种主流语言的rpc框架服务端/客户端代码. 不过Thrift的实现, 简单使用离实际生产环境还 ...

  2. 基于netty轻量的高性能分布式RPC服务框架forest<下篇>

    基于netty轻量的高性能分布式RPC服务框架forest<上篇> 文章已经简单介绍了forest的快速入门,本文旨在介绍forest用户指南. 基本介绍 Forest是一套基于java开 ...

  3. 基于netty轻量的高性能分布式RPC服务框架forest<上篇>

    工作几年,用过不不少RPC框架,也算是读过一些RPC源码.之前也撸过几次RPC框架,但是不断的被自己否定,最近终于又撸了一个,希望能够不断迭代出自己喜欢的样子. 顺便也记录一下撸RPC的过程,一来作为 ...

  4. RSF 分布式 RPC 服务框架的分层设计

    RSF 是个什么东西? 一个高可用.高性能.轻量级的分布式服务框架.支持容灾.负载均衡.集群.一个典型的应用场景是,将同一个服务部署在多个Server上提供 request.response 消息通知 ...

  5. RPC服务框架dubbo(一):简介和原理解析

    前置概念 在学习dubbo前,需要先了解SOA和RPC这两个概念. SOA 1.英文名称(Service Oriented Ambiguity) 2.中文名称:面向服务架构 2.1 有一个专门提供服务 ...

  6. NET Core微服务之路:自己动手实现Rpc服务框架,基于DotEasy.Rpc服务框架的介绍和集成

    本篇内容属于非实用性(拿来即用)介绍,如对框架设计没兴趣的朋友,请略过. 快一个月没有写博文了,最近忙着两件事;    一:阅读刘墉先生的<说话的魅力>,以一种微妙的,你我大家都会经常遇见 ...

  7. 唯品会RPC服务框架与容器化演进--转

    原文地址:http://mp.weixin.qq.com/s?__biz=MzAwMDU1MTE1OQ==&mid=405781868&idx=1&sn=cbb10d37e25 ...

  8. 基于开源Dubbo分布式RPC服务框架的部署整合

    一.前言 Dubbo 作为SOA服务化治理方案的核心框架,用于提高业务逻辑的复用.整合.集中管理,具有极高的可靠性(HA)和伸缩性,被应用于阿里巴巴各成员站点,同时在包括JD.当当在内的众多互联网项目 ...

  9. 【Rpc】基于开源Dubbo分布式RPC服务框架的部署整合

    一.前言 Dubbo 作为SOA服务化治理方案的核心框架,用于提高业务逻辑的复用.整合.集中管理,具有极高的可靠性(HA)和伸缩性,被应用于阿里巴巴各成员站点,同时在包括JD.当当在内的众多互联网项目 ...

随机推荐

  1. Parlay Wagering

    http://acm.sdut.edu.cn/sdutoj/problem.php?action=showproblem&problemid=2833 题意:讲述了一种投小钱赢大钱的赌博方式, ...

  2. Unity学习-鼠标的常用操作(八)

    本次主要介绍5个鼠标事件 void OnMouseEnter():鼠标进入 void OnMouseExit():鼠标移出 void OnMouseDown():鼠标点击 void OnMouseUp ...

  3. 【转】Java 集合系列12之 TreeMap详细介绍(源码解析)和使用示例

    概要 这一章,我们对TreeMap进行学习.我们先对TreeMap有个整体认识,然后再学习它的源码,最后再通过实例来学会使用TreeMap.内容包括:第1部分 TreeMap介绍第2部分 TreeMa ...

  4. FFmpeg编译Android版本

    FFmpeg是很好用的一个音视频库,功能强大,但是用起来并不是很方便.之前一直不想用FFmpeg,因为感觉编译太麻烦,但是到了不得不用的时候了,没办法,参考了网上大神的方法,在这里自己也记录一下方便以 ...

  5. ionic2\ionic3 自定义弹窗

    ionic2及ionic3没有了popup及 其templateUrl属性 那我们如何对弹窗里加入自定义元素 从而达到自定义弹窗样式 那么就可以通过写h5页面来实现 自定义弹窗效果: 写个H5的弹窗及 ...

  6. Showplan 逻辑运算符和物理运算符参考

    本文档已存档,并且将不进行维护. 运算符说明了 SQL Server 如何执行查询或数据操作语言 (DML) 语句. 查询优化器使用运算符生成查询计划,以创建在查询中指定的结果或执行在 DML 语句中 ...

  7. Tcl之Lab1

    Task 1. Use help 1) What is the default switch for the redirect command? -file help -v redirect # or ...

  8. (二)Python 学习第二天--爬5068动漫图库小案例

    (注:代码和网站仅仅是学习用途,非营利行为,源代码参考网上大神代码,仅仅用来学习

  9. sql日期提取

    --插入数据修改不行:必须提供学号 insert into Student(生日类型) values('阳历') --把月份提取出来 显示两位数 select DATENAME(month,getda ...

  10. ipv4的设置

    有段时间自己的网总是连不上,别人的都可以,因为公司又wifi,就将就着用wifi了,没有去查看原因,后来由于公司1网段大部分ip号被占用,系统要接入32路主机测试,每个主机都要分配ip,只好开辟2网段 ...