Thrift初探
目标是编写一个最简单的服务器+客户端
1 接口文件
我们先实现一个最简单的加法服务器:
// MathService.thrift
service MathService {
i32 Add(1:i32 A, 2:i32 B)
}
使用下载(或编译)的Thrift工具,生成调用代码:
mkdir mysrc // 必须先创建输出目录
thrift -o . -out mysrc --gen cpp MathService.thrift //c++源代码
thrift -o . -out mysrc --gen go MathService.thrift //golang源代码
目录结构:
C:\xxx\MYSRC
│ MathService.cpp
│ MathService.h
│ MathService_constants.cpp
│ MathService_constants.h
│ MathService_server.skeleton.cpp
│ MathService_types.cpp
│ MathService_types.h
│
└─mathservice
│ GoUnusedProtection__.go
│ MathService-consts.go
│ MathService.go
│
└─math_service-remote
math_service-remote.go
其中,mathservice目录下是go的代码。根目录下的h和cpp文件是c++代码。
2 编写服务器
其实生成的代码中已经将服务器写好了,MathService_server.skeleton.cpp
如果需要其他特殊需求,可参考这个Demo进行修改。
我们创建一个项目,然后将它们都加入进入进去。
通过Nuget安装Boost-vc140,libevent-vc120, openssl。(后续编译过程很多坑,反而不建议了)
#include "MathService.h"
#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;
class MathServiceHandler : virtual public MathServiceIf {
public:
MathServiceHandler() {
// Your initialization goes here
}
// Add 函数的服务器实现
int32_t Add(const int32_t A, const int32_t B) {
printf("Add %d + %d\n", A, B);
return A+B;
}
};
int main(int argc, char **argv) {
int port = 9090;
shared_ptr<MathServiceHandler> handler(new MathServiceHandler());
shared_ptr<TProcessor> processor(new MathServiceProcessor(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;
}
Thrift代码的大坑
在编译libthrift时各种通不过,主要有如下问题:
boost开头的文件找不到
解决办法:直接手动指定boost库的头文件所在。一大堆的libThrift函数找不到(这个问题太坑,网上搜不到比较准确的答案)
unresolved external symbol "public: virtual void __cdecl apache::thrift::server::TServerFramework::serve(void)" (?serve@TServerFramework@server@thrift@apache@@UEAAXXZ)
解决办法:将server文件夹中的,TConnectedClient 和 TServerFramework 头文件和cpp都手工添加到libthrift项目中。MathService函数错误
解决办法:在 MathService_server.skeleton.cpp 中,对Add函数进行了实现,但是没有返回值,只需要在Add中增加一个return 返回就行了。
3 编写客户端
#include "MathService.h"
#include <thrift/protocol/TBinaryProtocol.h>
#include <thrift/transport/TSocket.h>
#include <thrift/transport/TBufferTransports.h>
using namespace ::apache::thrift;
using namespace ::apache::thrift::protocol;
using namespace ::apache::thrift::transport;
using boost::shared_ptr;
int main(int argc, char **argv) {
int port = 9090;
shared_ptr<TTransport> clientSocket(new TSocket("127.0.0.1", port));
shared_ptr<TTransport> clientTransport(new TBufferedTransport(clientSocket));
shared_ptr<TProtocol> clientProtocol(new TBinaryProtocol(clientTransport));
MathServiceClient client(clientProtocol);
try
{
clientTransport->open();
printf("Open Remote Transport, wait call it!\n");
getchar();
for ( int i=0;i<100;i++ )
{
int nRet = client.Add(i, i);
printf("[%03d] %d + %d = %d\n", i, i, i, nRet);
}
}
catch (TException& e)
{
printf("ERROR:%s\n", e.what());
}
system("pause");
return 0;
}
4 Go语言版本
前提:安装配置好Go环境。本人使用go1.8.3版本测试通过。
需要将mathservice文件夹放到$GOPATH/src
文件夹下,然后在math_service-remote文件夹下编写client.go和server.go文件。
4.1 服务器端
// server.go
package main
import (
"fmt"
"mathservice"
"os"
"git.apache.org/thrift.git/lib/go/thrift"
)
const (
NetWorkAddr = "127.0.0.1:9090"
)
type MyMathService struct {
}
func (this *MyMathService) Add(A int32, B int32) (r int32, err error) {
r = A + B
err = nil
fmt.Println("Add", A, B)
return
}
func main() {
handler := &MyMathService{}
processor := mathservice.NewMathServiceProcessor(handler)
serverTransport, err := thrift.NewTServerSocket(NetWorkAddr)
if err != nil {
fmt.Println("Error!", err)
os.Exit(1)
}
transportFactory := thrift.NewTBufferedTransportFactory(512) // c++版本默认大小是512
//protocolFactory := thrift.NewTCompactProtocolFactory()
protocolFactory := thrift.NewTBinaryProtocolFactoryDefault()
server := thrift.NewTSimpleServer4(processor, serverTransport, transportFactory, protocolFactory)
fmt.Println("thrift server in", NetWorkAddr)
server.Serve()
}
4.2 客户端版
// client.go
package main
import (
"mathservice"
"fmt"
"os"
"git.apache.org/thrift.git/lib/go/thrift"
)
func main() {
client_socket, _ := thrift.NewTSocket("127.0.0.1:9090")
client_transport := thrift.NewTBufferedTransport(client_socket, 512)
protocolFactory := thrift.NewTBinaryProtocolFactoryDefault()
client := mathservice.NewMathServiceClientFactory(client_transport, protocolFactory)
if err := client_transport.Open(); err != nil {
fmt.Fprintln(os.Stderr, "Error opening socket", err)
os.Exit(1)
}
defer client_transport.Close()
for i := int32(0); i < 100; i++ {
nRet, _ := client.Add(i, i)
fmt.Println(i, "Add", nRet)
}
fmt.Println("Over!")
}
4.3 自带Remote客户端
自带的math_service-remote.go
是一个很不错的Client学习对象,-P可指定编码方式,-frame可指定帧传输模式,还可以随意指定函数和参数,真的是做的很全面啦!
测试指令go run math_service-remote.go -P binary -h 127.0.0.1 -p 9090 Add 123 888
5 简单总结
使用C++编译使用时,有很多坑存在,作为一个大名鼎鼎的开源项目,文档很少,学习体验很差,真的是让人心烦!相对而言,使用Go编写使用时,体验很不错,唯一的问题就是,文档太少!!!!!!!!