Thrift安裝與伺服器、客戶端的編寫執行演示(windows版本)
Thrift安裝與伺服器、客戶端的編寫執行演示(windows版本)
第一部分: thrift的安裝使用
1.1 簡介
thrift是一個軟體框架,用來進行可擴充套件且跨語言的服務的開發。它結合了功能強大的軟體堆疊和程式碼生成引擎,以構建在 C++, Java, Python, PHP, Ruby, Erlang, Perl, Haskell, C#, Cocoa, JavaScript, Node.js, Smalltalk, and OCaml 這些程式語言間無縫結合的、高效的服務。
1.2 下載
https://download.csdn.net/download/xiao__jia__jia/10974167
在windows的系統的高階環境-》環境變數的Path配置放置的目錄
執行cmd,檢視是否成功
thrift -version
第二部分:Thrift語法
2.1 型別
Thrift型別系統包括預定義基本型別,使用者自定義結構體,容器型別,異常和服務定義。
2.1.1 基本型別
-
bool: 布林值 (true or false), one byte
-
byte: 有符號位元組
-
i16: 16位有符號整型
-
i32: 32位有符號整型
-
i64: 64位有符號整型
-
double: 64位浮點型
-
string: Encoding agnostic text or binary string
Note that: Thrift不支援無符號整型,因為Thrift目標語言沒有無符號整型,無法轉換。
2.1.2 容器(Containers)
Thrift容器與流行程式語言的容器型別相對應,採用Java泛型風格。它有3種可用容器型別:
-
list<t1>: 元素型別為t1的有序表,容許元素重複。(有序表ordered list不知道如何理解?排序的?c++的vector不排序)
-
set<t1>:元素型別為t1的無序表,不容許元素重複。
-
map<t1,t2>: 鍵型別為t1,值型別為t2的kv對,鍵不容許重複。
容器中元素型別可以是除了service外的任何合法Thrift型別(包括結構體和異常)。
2.1.3 結構體和異常(Structs and Exceptions)
Thrift結構體在概念上類似於(similar to)C語言結構體型別--將相關屬性封裝在一起的簡便方式。Thrift結構體將會被轉換成面嚮物件語言的類。
異常在語法和功能上類似於(equivalent to)結構體,差別是異常使用關鍵字exception而不是struct宣告。但它在語義上不同於結構體:當定義一個RPC服務時,開發者可能需要宣告一個遠端方法丟擲一個異常。
結構體和異常的宣告將在下一節介紹。
2.1.4 服務(Services)
服務的定義方法在語義(semantically)上等同於面嚮物件語言中的介面。Thrift編譯器會產生執行這些介面的client和server stub。具體參見下一節。
2.2 型別定義(Typedef)
Thrift支援C/C++型別定義。
typedef i32 MyInteger // a typedef T ReT // b
說明:a. 末尾沒有逗號。b. struct也可以使用typedef。
2.3 列舉(Enums)
很多語言都有列舉,意義都一樣。比如,當定義一個訊息型別時,它只能是預定義的值列表中的一個,可以用列舉實現。
enum TweetType {
TWEET, // (1)
RETWEET = 2, // (2)
DM = 0xa, // (3)
REPLY // (4)
}
struct Tweet {
1: required i32 userId;
2: required string userName;
3: required string text;
4: optional Location loc;
5: optional TweetType tweetType = TweetType.TWEET; // (5)
16: optional string language = "english"
}
說明:
(1). 編譯器預設從0開始賦值
(2). 可以賦予某個常量某個整數
(3). 允許常量是十六進位制整數
(4). 末尾沒有分號
(5). 給常量賦預設值時,使用常量的全稱
注意,不同於protocal buffer,thrift不支援列舉類巢狀,列舉常量必須是32位的正整數
2.4 註釋(Comment)
Thrift支援shell風格, C多行風格和Java/C++單行風格。
/*
* This is a multi-line comment.
* Just like in C.
*/
// C++/Java style single-line comments work just as well.
2.5 名字空間(Namespace)
Thrift中的名稱空間類似於C++中的namespace和java中的package,它們提供了一種組織(隔離)程式碼的簡便方式。名字空間也可以用於解決型別定義中的名字衝突。
由於每種語言均有自己的名稱空間定義方式(如python中有module), thrift允許開發者針對特定語言定義namespace:
namespace cpp com.example.project // (1)
namespace java com.example.project // (2)
namespace php com.example.project
(1). 轉化成namespace com { namespace example { namespace project {
(2). 轉換成package com.example.project
2.6 Includes
便於管理、重用和提高模組性/組織性,我們常常分割Thrift定義在不同的檔案中。包含檔案搜尋方式與c++一樣。Thrift允許檔案包含其它thrift檔案,使用者需要使用thrift檔名作為字首訪問被包含的物件,如:
include "tweet.thrift" // (1)
...
struct TweetSearchResult {
1: tweet.Tweet tweet; // (2)
}
說明:
(1). thrift檔名要用雙引號包含,末尾沒有逗號或者分號
(2). 注意tweet字首
2.7 常量(Constant)
Thrift允許定義跨語言使用的常量,複雜的型別和結構體可使用JSON形式表示。
const i32 INT_CONST = 1234; // (1)
說明:
(1) 分號可有可無。支援16進位制。
2.8 結構體定義(Defining Struct)
struct是Thrift IDL中的基本組成塊,由域組成,每個域有唯一整數識別符號,型別,名字和可選的預設引數組成。如定義一個類似於Twitter服務:
struct Tweet {
1: required i32 userId; // (1)
2: required string userName; // (2)
3: required string text;
4: optional Location loc; // (3)
16: optional string language = "english" // (4)
}
struct Location { // (5)
1: required double latitude;
2: required double longitude;
}
(1) 每個域有一個唯一的正整數識別符號;
(2) 每個域可標識為required或optional;
(3) 結構體可以包含其它結構體
(4) 域可有預設值,與required或optional無關。
(5) Thrift檔案可以定義多個結構體,並在同一檔案中引用,也可加入檔案限定詞在其它Thrift檔案中引用。
如上所見,訊息定義中的每個域都有一個唯一數字標籤,這些數字標籤在傳輸時用來確定域,一旦使用訊息型別,標籤不可改變。(隨著專案的進展,可以要變更Thrift檔案,最好不要改變原有的數字標籤)
規範的struct定義中的每個域均會使用required或者 optional關鍵字進行標識。如果required標識的域沒有賦值,Thrift將給予提示;如果optional標識的域沒有賦值,該域將不會被 序列化傳輸;如果某個optional標識域有預設值而使用者沒有重新賦值,則該域的值一直為預設值;如果某個optional標識域有預設值或者使用者已經重新賦值,而不設定它的__isset為true,也不會被序列化傳輸。(不被序列化傳輸的後果是什麼?為空為零?還是預設值,下次試試)
與services不同,結構體不支援繼承。
2.9 服務定義(Defining Services)
在流行的序列化/反序列化框架(如protocal buffer)中,Thrift是少有的提供多語言間RPC服務的框架。這是Thrift的一大特色。
Thrift編譯器會根據選擇的目標語言為server產生服務介面程式碼,為client產生stubs。
service Twitter {
// A method definition looks like C code. It has a return type, arguments,
// and optionally a list of exceptions that it may throw. Note that argument
// lists and exception list are specified using the exact same syntax as
// field lists in structs.
void ping(), // (1)
bool postTweet(1:Tweet tweet); // (2)
TweetSearchResult searchTweets(1:string query); // (3)
// The 'oneway' modifier indicates that the client only makes a request and
// does not wait for any response at all. Oneway methods MUST be void.
oneway void zip() // (4)
}
(1) 有點亂,介面支援以逗號和分號結束;
(2) 引數可以是基本型別和結構體;(引數是cosnt的,轉換為c++語言是const&)
(3) 返回值同引數一樣;
(4) 返回值是void,注意oneway;
Note that:引數列表的定義與結構體一樣。服務支援繼承。
第三部分:伺服器和客戶端的編寫與執行(java實現)
3.1 編寫thrift檔案
TestThrift.thrift
namespace java com.xiaomi.wanjia.thrift
struct Blog {
1:string topic
2:binary content
3:i64 createTime
4:string id
5:string ipAddress
6:map<string,string> props
}
service ThriftCase {
i32 testCase1(1:i32 num1, 2:i32 num2,3:string num3)
list<string> testCase2(1:map<string,string> num1)
void testCase3()
void testCase4(1:list<Blog> blog)
}
3.2 生成程式碼 (以java語言為例),在放置thrift.exe的目錄上輸入cmd,進入終端,輸入下面的命令,在目中下生成相應的類
thrift -gen java TestThrift.thrift
3.3 建立工程
在idea下新建一個java的 maven project,將生成的程式碼整個資料夾(com/xiaomi/winwill/service/*)拷貝到工程的src目錄下。
pom.xml
<dependency>
<groupId>thrift</groupId>
<artifactId>libthrift</artifactId>
<version>0.5.0</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.7</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.5</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.5</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>1.7.5</version>
<scope>test</scope>
</dependency>
3.4 實現介面
TestThrift.thrift檔案中定義類一個service(ThriftCase),生成java程式碼之後將會變成一個介面,這些介面的作用是實現跨平臺通訊,但是真正的業務邏輯並未實現,所以,要做什麼?怎麼做?這些詳細的設計應該由我們自己來實現。在工程的com.xiaomi.winwill.service包中建立一個類:Business.java,該類實現ThriftCase的Iface介面:
Business
package com.xiaomi.wanjia.service;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.xiaomi.wanjia.thrift.Blog;
import org.apache.thrift.TException;
import com.xiaomi.wanjia.thrift.ThriftCase;
public class Business implements ThriftCase.Iface {
private Set<String> keySet;
@Override
public int testCase1(int num1, int num2, String num3) throws TException {
System.out.println("#1: ");
if(num3.equals("+")){
System.out.println(num1+num3+num2+"="+(num1+num2));
return num1 + num2;
} else if(num3.equals("-")){
System.out.println(num1+num3+num2+"="+(num1-num2));
return num1 - num2;
} else if(num3.equals("*")){
System.out.println(num1+num3+num2+"="+(num1*num2));
return num1 * num2;
} else if(num3.equals("/")){
if(num2 == 0){ System.out.println("error, can not divided by zero!");
return 0;
} else{
System.out.println(num1+num3+num2+"="+(num1/num2));
return num1 / num2;
}
}
return 0;
}
@Override
public List<String> testCase2(Map<String, String> num1) throws TException {
System.out.println("#2: ");
List<String> res = new ArrayList<String>();
keySet = num1.keySet();
for(String str:keySet){
res.add(str);
}
System.out.println(res);
return res;
}
@Override
public void testCase3() throws TException {
System.out.println("#3: ");
System.out.println("Output nothing!");
}
@Override public void testCase4(List<Blog> blog) throws TException {
System.out.println("#4: ");
for (Blog blog2 : blog) {
System.out.println("id: "+blog2.getId());
System.out.println("ipAddress: "+blog2.getIpAddress());
System.out.println("createTime: "+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(blog2.getCreateTime()));
System.out.println("topic: "+blog2.getTopic());
}
}
}
Client
package com.xiaomi.wanjia.service;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.thrift.TException;
import org.apache.thrift.protocol.TBinaryProtocol;
import org.apache.thrift.protocol.TProtocol;
import org.apache.thrift.transport.TSocket;
import org.apache.thrift.transport.TTransport;
import com.xiaomi.wanjia.thrift.Blog;
import com.xiaomi.wanjia.thrift.ThriftCase;
public class Client {
public static void main(String[] args) throws TException {
Map<String, String> map = new HashMap<String, String>();
List<Blog> blogs = new ArrayList<Blog>();
TTransport transport = new TSocket("localhost", 7912);
TProtocol protocol = new TBinaryProtocol(transport);
ThriftCase.Client client = new ThriftCase.Client(protocol);
Blog blog = new Blog(); blog.createTime = System.currentTimeMillis();
blog.id = "SCUQIFUGUANG";
blog.ipAddress = "127.0.0.1";
blog.topic = "this is blog topic"; blogs.add(blog);
transport.open();
map.put("MyBlog", "http://blog.163.com/scuqifuguang@126/");
System.out.println("Client calling");
client.testCase1(10, 21, "+");
client.testCase2(map);
client.testCase3();
client.testCase4(blogs);
transport.close();
}
}
Server
package com.xiaomi.wanjia.service;
import java.io.IOException;
import java.net.ServerSocket;
import org.apache.thrift.server.TServer;
import org.apache.thrift.server.TSimpleServer;
import org.apache.thrift.transport.TServerSocket;
import org.apache.thrift.transport.TTransportException;
import com.xiaomi.wanjia.thrift.ThriftCase;
public class Server {
public static void main(String[] args) throws TTransportException, IOException {
ServerSocket socket = new ServerSocket(7912);
TServerSocket serverTransport = new TServerSocket(socket);
ThriftCase.Processor processor = new ThriftCase.Processor(new Business());
TServer server = new TSimpleServer(processor, serverTransport);
System.out.println("Running server...");
server.serve();
}
}
最終的專案結構
3.8 執行
首先執行Server:
然後執行Client:
最後再看看Server的Console視窗:
從執行結果可以看出,Client端已經成功地與伺服器通訊,並且遠端呼叫了伺服器的四個TestCase方法。
相關文章
- Windows下同時安裝執行多個版本的jmeter客戶端WindowsJMeter客戶端
- redis伺服器/客戶端安裝與配置Redis伺服器客戶端
- Windows下安裝redis客戶端WindowsRedis客戶端
- windows下編譯安裝thriftWindows編譯
- windows 上安裝與資料庫不同版本的客戶端引起的ora 12557Windows資料庫客戶端
- Android實現Thrift服務端與客戶端Android服務端客戶端
- windows7下安裝oracle客戶端WindowsOracle客戶端
- 初探Thrift客戶端非同步模式客戶端非同步模式
- Windows下svn客戶端TortoiseSVN的安裝和操作Windows客戶端
- Ceph的客戶端安裝客戶端
- Swift編寫自己的API客戶端SwiftAPI客戶端
- WINDOWS8.1安裝ORACLE客戶端及配置WindowsOracle客戶端
- Nagios 監控windows客戶端安裝教程iOSWindows客戶端
- Skywalking PHP客戶端編譯安裝PHP客戶端編譯
- Oracle 客戶端安裝Oracle客戶端
- MySQL 客戶端安裝MySql客戶端
- zabbix 客戶端安裝客戶端
- oracle客戶端安裝Oracle客戶端
- grpc套路客戶端編寫RPC客戶端
- VNC客戶端是Windows,VNC客戶端是Windows如何進行操作VNC客戶端Windows
- [轉載+補充]windows下SVN客戶端的安裝Windows客戶端
- go+h5寫客戶端,安裝zserge/webviewGoH5客戶端WebView
- aix安裝oracle客戶端AIOracle客戶端
- Git-客戶端安裝Git客戶端
- centos安裝oracle客戶端CentOSOracle客戶端
- CentOS安裝git客戶端CentOSGit客戶端
- 跟蹤客戶端執行的SQL客戶端SQL
- graylog 客戶端的安裝配置客戶端
- 【windows socket+TCP伺服器客戶端】WindowsTCP伺服器客戶端
- 【windows socket+UDP伺服器客戶端】WindowsUDP伺服器客戶端
- 【windows socket+HTTP伺服器客戶端】WindowsHTTP伺服器客戶端
- oracle 客戶端與伺服器端的關係Oracle客戶端伺服器
- js 客戶端與伺服器端的通訊JS客戶端伺服器
- db2 客戶端安裝DB2客戶端
- linux安裝mysql客戶端LinuxMySql客戶端
- zabbix監控客戶端安裝客戶端
- 安裝 Oracle 即時客戶端Oracle客戶端
- 配置安裝版Oracle客戶端Oracle客戶端