前言
架构服务化后,需要实现一套方便调用各服务的框架,现在开源如日中天,优先会寻找开源实现,如果没有合适自家公司业务的,才会考虑从零开发,尤其是一切以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. [Swift通天遁地]九、拔剑吧-(1)实现在程序中跳转到微信、App Store、地图

    ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★➤微信公众号:山青咏芝(shanqingyongzhi)➤博客园地址:山青咏芝(https://www.cnblogs. ...

  2. 判断人员js

    var allchooseEmpID = "";var allchooseEmpName = "";//自选经办人 function getJbrWinForM ...

  3. JS/JQuery操作DOM元素笔记

    原因 自己目前在搭建一个.NET Core的框架,正在构建权限这块的东西,今天设置权限界面,需要使用JavaScript操作DOM元素,记录一下. 页面大概是酱紫的(我使用的AdminLTE和LayU ...

  4. eclipse上ndk环境的搭建&&so文件的生成&&jni文件的调用

    JNI是java语言提供的Java和C/C++相互沟通的机制,Java可以通过JNI调用本地的C/C++代码,本地的C/C++的代码也可以调用java代码.JNI 是本地编程接口,Java和C/C++ ...

  5. BZOJ 4310 二分+SA+RMQ

    思路: 首先求出后缀数组和height数组,这样能得到本质不同的子串数目 这里利用:本质不同的子串=∑(Len−SA[i]−height[i])=∑(Len−SA[i]−height[i])利用SA[ ...

  6. hdu2027

    http://acm.hdu.edu.cn/showproblem.php?pid=2027 #include<iostream> #include<stdio.h> #inc ...

  7. 通过UDP建立TCP连接

    解释 通过UDP广播查询服务器的IP地址,然后再建立TCP点对点连接. 应用场景 在服务器IP未知时,并且已知服务器与客户端明确在一个局域网或者允许组播的子网下. 通过UDP发现服务器地址然后再进行T ...

  8. HTML TabIndex属性

    TabIndex作用: tabindex:全局属性.指示其元素是否可以聚焦(获得焦点),以及它是否/在何处参与顺序键盘导航(因通常使用tab键操作,顾因此得名). 当使用tab键在网页控件中进行导航时 ...

  9. PHP魔术法__set和__get

    __set: 在给不可访问属性赋值时,__set() 会被调用.语法如下: public void __set ( string $name , mixed $value ) __get: 读取不可访 ...

  10. iframe监听unload事件

    阻止默认事件 event.preventDefault(); 阻止事件冒泡 event.stopPropagation(); event.cancelBubble = true; //IE <a ...