Thrift高级
在之前的案例中,使用的都是TCP服务,但是在实际网络中,TCP端口要求很严,我们不可能为每个微服务都提供一个端口,这也是近几年HTTP越来越占主流的一个很重要原因。这里,我们研究一下 Thrift Over HTTP。
依赖与感谢
版本必须为最新0.10.0
,这个版本提供了THttpServer和THttpClient这两个核心类。
这里要感谢QQ群【thrift技术交流群】里的loading,他给予了我很多帮助。他的Demohttps://github.com/cdmeister/Thrift-Javascript-to-CPP
服务器改造
#include "MathService.h"
#include <thrift/protocol/TBinaryProtocol.h>
#include <thrift/server/TSimpleServer.h>
#include <thrift/server/TNonblockingServer.h>
#include <thrift/server/TThreadedServer.h>
#include <thrift/transport/TServerSocket.h>
#include <thrift/transport/TBufferTransports.h>
#include <thrift/transport/THttpServer.h>
#include <thrift/protocol/TJSONProtocol.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 THttpServerTransportFactory());
shared_ptr<TProtocolFactory> protocolFactory(new TBinaryProtocolFactory()); // 协议JSON和Bin都行
TThreadedServer _server(processor, serverTransport, transportFactory,
protocolFactory);
_server.serve();
return 0;
}
客户端改造
#include "MathService.h"
#include <thrift/protocol/TBinaryProtocol.h>
#include <thrift/transport/TSocket.h>
#include <thrift/transport/TBufferTransports.h>
#include <thrift/transport/THttpClient.h>
#include <thrift/protocol/TJSONProtocol.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> clientTransport2(new THttpClient("127.0.0.1",port,"/")); // 这里有大坑
shared_ptr<TProtocol> clientProtocol(new TBinaryProtocol(clientTransport2));
MathServiceClient client(clientProtocol);
try
{
clientTransport2->open();
printf("Open Remote Transport, wait call it!\n");
//getchar();
long tmCur = clock();
for (int i = 0; i < 100; i++)
{
int nRet = client.Add(i, i);
printf("[%03d] %d + %d = %d\n", i, i, i, nRet);
}
long lTm = clock() - tmCur;
printf("Used Time: %d\r\n", lTm);
}
catch (TException& e)
{
printf("ERROR:%s\n", e.what());
}
system("pause");
return 0;
}
坑与总结
说到这里,不得不提一下这个大坑,new THttpClient("127.0.0.1",port,"/")
,它的原型为THttpClient(boost::shared_ptr<TTransport> transport, std::string host, std::string path = "");
其中 path 的默认值为空,这样,Client请求时,一定会失败,但是你又不能不填。这里建议默认使用"/"
。
在参考的Git Demo中,这里时随意填写的,可能会有一些误导。
只要这里搞定,这个例子就已经基本没啥问题了。下面,还需要做一个实验,来验证URL path的用途。
深入测试
既然是thrift over HTTP,那么path这个参数肯定是有用的,它用来在进行http请求时,提交请求的url地址,我们在通过nginx一类的代理改变子url时,就显得很重要了。
首先,配置一个nginx:
worker_processes 1;
events {
worker_connections 1024;
}
http {
# 省略很多不关心的内容
server {
listen 80;
server_name localhost;
location / {
root html;
index index.html index.htm;
}
location = /thrift {
proxy_pass http://127.0.0.1:9090/;
}
}
}
在nginx中将http://127.0.0.1/thrift
这个url转向到http://127.0.0.1:9090/
,当我们进行thrift客户端访问时,如果path参数不正确shared_ptr<TTransport> clientTransport2(new THttpClient("127.0.0.1",80,"/thrift2"));
,就会发生如下错误:
D:\CodePakage\vsSrc\thriftTest\Debug>thriftTest.exe
Open Remote Transport, wait call it!
ERROR:Bad Status: HTTP/1.1
如果path参数正确shared_ptr<TTransport> clientTransport2(new THttpClient("127.0.0.1",80,"/thrift"));
则调用成功!
按照这种方式组合服务程序,则可以很方便地通过一个端口(如80)进行多个服务的整合。如果可以再利用TMultiplexedxxxxx
进行端口复用,则基本上能够组合出我们想要的任何服务形式了。