Tars是騰訊從2008年到今天一直在使用的微服務開發框架,2018年成為Linux基金會開源項目,目前支持C++、Java、PHP、NodeJS與Go語言。該框架為用戶提供了涉及到開發、運維,以及測試的一整套解決方案,幫助一個產品或者服務快速開發、部署、測試、上線。它集可擴展協議編解碼、高性能RPC通信框架、名字路由與發現、發布監控、日誌統計、配置管理等於一體,通過它可以快速用微服務的方式構建自己的穩定可靠的分散式應用,並實現完整有效的服務治理。
TarsPHP作為Tars在PHP語言的解決方案,設計的時候主要考慮如下四個方面:
Protocol buffers (簡稱PB)是Google開源的語言中立,平台無關,可擴展的序列化數據的格式,可用於通信協議,數據存儲等。它和XML類似,但比XML更小,更快,更簡單。
PB是編碼協議,如果涉及到網路傳輸和RPC調用,就需要引入通訊協議。Google開源的RPC框架gRPC就使用Http2作為通訊協議,PB作為編碼協議。
Protoc 安裝
首先需要安裝protoc庫,這個庫的主要作用是打包解包protobuf協議數據。可以參考 https://github.com/protocolbuffers/protobuf/tree/master/src 直接安裝。
./autogen.sh
./configure
make
make install
如果 protoc –version 可以正常輸出,說明安裝完成
php protobuf安裝
之後需要安裝 php protobuf擴展,這個擴展主要用作php和protoc庫中間的一個橋樑。
git clone https://github.com/protocolbuffers/protobuf.git
cd ./php/ext/google/protobuf/
phpize
./configure
make
make install
(需要編輯php.ini文件,開啟protobuf擴展)
如果 php –ri protobuf 有輸出,說明安裝正常。
Swoole 安裝
建議使用4.4.0或以上版本,需要開啟http2 和 openssl支持。
參考TarsPHP中ActDemo中評論服務的tars文件,我們寫了一個actComment.proto的協議文件。
和tars協議文件不同,proto協議中規定輸入輸出參數必須也只能是一個message結構體,因此需要對輸入輸出參數單獨在封裝一個message。
syntax = "proto3";
package protocol.QD.ActCommentPbServer; //包名,會根據包名生成 php類路徑
service CommentObj {
rpc ping(PingRequest) returns (PingResponse) {};
rpc getCommentCount(CountRequest) returns (CountResponse) {};
rpc createComment(CreateRequest) returns (CreateResponse) {};
rpc getComment(GetRequest) returns (GetResponse) {};
}
//輸入參數通用結構體
message CommonInParam {
int32 appId = 1;
int32 areaId = 2;
int64 userId = 3; //用戶信息
string userIp = 4; //來源名稱
string serverIp = 5; //調用方伺服器ip
};
//輸出參數通用結構體
message CommonOutParam {
int32 code = 1; //介面返回碼
string message = 2; //介面返回提示信息
};
message SimpleComment {
int32 id = 1;
int64 activityId = 2;
int64 userId = 3;
string content = 4;
string title = 5;
string ext1 = 6;
int64 createTime = 7;
};
message QueryParam {
int64 activityId = 1;
int32 page = 2;
int32 size = 3;
int32 orderType = 4;
};
message CreateRequest {
CommonInParam inParam = 1;
SimpleComment comment = 2;
};
message CreateResponse {
CommonOutParam outParam = 1;
};
message GetRequest {
CommonInParam inParam = 1;
QueryParam queryParam = 2;
};
message GetResponse {
CommonOutParam outParam = 1;
repeated SimpleComment list = 2;
};
message PingRequest {
};
message PingResponse {
};
message CountRequest {
};
message CountResponse {
int32 count = 1;
};
protoc可以根據proto文件生成對應的php類代碼,但是官方並不支持proto文件生成server端代碼,可以使用gRPC插件生成client代碼。如果需要使用生成的client代碼我們還需要安裝grpc庫和grpc php擴展。
因此我們的思路是,先使用protoc生成php需要的類,然後自己解析proto文件生成server 端interface,這個過程非常像現有的tars2php的過程,因此我們叫它proto2php。
由於使用兩個工具生成還比較麻煩,我們把調用proto的過程集成到proto2php中方便大家使用。
我們先構建一個tars.proto.php設置一些基本信息。
return array(
'appName' => 'QD',
'serverName' => 'ActCommentPbServer',
'objName' => 'CommentObj',
'withServant' => true, //決定是服務端,還是客戶端的自動生成
'tarsFiles' => array(
'./actComment.proto',
),
'dstPath' => '../src/protocol', //這裡指定的是 impl 基礎interface 生成的位置
'protocDstPath' => '../src', //這裡指定的是 protoc 生成的問題
'namespacePrefix' => 'Protocol',
);
然後執行 php …/src/vendor/phptars/tars2php/src/proto2php.php ./tars.proto.php
之後會生成GPBMetadata目錄和protocol目錄。其中protocol中就是proto文件生成的php類,另外CommentObjServant.php就是proto2php文件生成的server端interface類。構建TarsPHP pb server需要實現這個類。
按照Demo中 Readme部署tarsphp pb server即可。
幾點注意:
{
"name" : "tars-tcp-server-demo",
"description": "tars tcp server",
"require": {
"phptars/tars-server": "~0.3",
"phptars/tars-deploy": "~0.1",
"phptars/tars-log": "~0.1",
"phptars/tars2php": "~0.1",
"ext-zip" : ">=0.0.1",
"google/protobuf": "^3.8"
},
"autoload": {
"psr-4": {
"Server\\" : "./",
"Protocol\\" : "./protocol",
"GPBMetadata\\" : "./GPBMetadata"
}
},
"minimum-stability": "stable",
"scripts" : {
"deploy" : "\\Tars\\deploy\\Deploy::run"
}
}
最後執行 composer run-script deploy,生成代碼包,上傳到Tars平台上發布。
可以使用gRPC生成的php客戶端訪問測試,也可以直接使用swoole 的http2客戶端構建一個grpc客戶端。
class TestGrpcClient
{
public static function callGrpc($ip, $port, $path, $requestBuf)
{
$cli = new Swoole\Coroutine\Http2\Client($ip, $port, false);
$cli->connect();
$req = new swoole_http2_request;
$req->method = 'POST';
$req->path = $path;
$req->headers = [
"user-agent" => 'grpc-c/7.0.0 (linux; chttp2; gale)',
"content-type" => "application/grpc",
"grpc-accept-encoding" => "identity,deflate,gzip",
"accept-encoding" => "identity,gzip",
"te" => "trailers",
];
$req->pipeline = false;
$req->data = $requestBuf;
$cli->send($req);
$response = $cli->recv();
return $response->data;
}
public static function main()
{
$commonIn = new CommonInParam();
$commonIn->setUserId(0);
$commonIn->setAppId(1);
$commonIn->setAreaId(10);
$commonIn->setServerIp('127.0.0.1');
$commonIn->setUserIp('');
$query = new QueryParam();
$query->setActivityId(123);
$query->setPage(1);
$query->setSize(10);
$query->setOrderType(1);
$request = new GetRequest();
$request->setInParam($commonIn);
$request->setQueryParam($query);
$requestBuf = $request->serializeToString();
$packBuf = pack('CN', 0, strlen($requestBuf)) . $requestBuf;
go(function () use ($packBuf){
$path = "/protocol.QD.ActCommentServer.CommentObj/getComment";
$ret = self::callGrpc('127.0.0.1', 10008, $path, $packBuf); //這裡注意要修改成你服務在tars上綁定的ip 127.0.0.1不一定可以
$response = new GetResponse();
$response->mergeFromString(substr($ret, 5));
foreach ($response->getList() as $row) {
var_dump($row->getContent());
}
});
}
}
執行php client.php觀察返回。
前面提到的client,只是我們訪問PB server 的簡單demo,可以幫助我們測試PB server的狀態。如果需要在其他Tars服務中調用PB server應該如何使用呢?和Tars類似我們也提供了生成PB client端代碼的方式。
這裡使用TarsActDemo下的QD.ActHttpServer為範例演示如何生成Tars PB client代碼並調用PB服務。
範例如下:
$inParam = new CountRequest();
$outParam = new CountResponse();
$conf = self::getConfig();
$conf->setSocketMode(4);
$servant = new CommentObjServant($conf);
$servant->getCommentCount($inParam, $outParam);
return $outParam->getCount();
TarsGo中關於PB的支持,本質是對proto協議文件的支持,提供將proto協議文件轉換為tars協議的能力,在相互調用中實際使用的是tars協議。這個服務可以和其他Tars服務相互工作。
TarsPHP中關於PB的支持,是構建了一個gRPC服務,這個服務部署在Tars平台上,參與Tars平台定址,受Tars平台管理。這個服務使用gRPC on Http2作為網路通訊協議,使用Protobuf作為編碼協議,可以和其他PB client 相互工作。
兩者方向不同,不能混合使用,希望大家區分。
我們使用相同Http服務,分別使用Tars和Pb協議和後端服務通訊並進行壓測。
QDPS | PB | TARS |
---|---|---|
服務空跑 | 3800 | 6200 |
單次簡單RPC | 3600 | 6150 |
單次複雜RPC | 1050 | 1150 |
從壓測數據來看,Tars性能比PB高出一截,但對比兩者打包解包性能發現PB打包解包性能略優於Tars,導致這樣結果的主要原因我認為是gRPC使用Http2作為通訊協議相比Tars的自定義通訊協議需要更多開銷。
歡迎給項目點 star 並參與貢獻。
https://github.com/TarsPHP/TarsPHP
https://github.com/TarsCloud/Tars
作者:張勇
[admin
]