freeeyes 发表于 2014-2-28 14:34:06

中间件thirft的Linux部署与运用

前一段时间一直在想一个问题,一般的web服务器是如何和当前我的C++服务器进行通讯的。
当然方法有很多,比如我现在正值使用的gsoap,对于远程web主机来说,我只要提供一个内网的IP,它们就可以像调用webServer那样直接调用。
但是也遇到过几起因为gsoap组件出现的诡异问题,最奇怪的是,机房服务器有几次存储挂掉居然是因为goap崩溃了?虽然系统管理员给我的日志以及解释很站不住脚。
不过鉴于越写越庞大,以及越来越难以维护的gsoap代码(这里有历史原因)。我还是想找一个gsoap的替换方案。替换当前的方式。
以前写过一些中间件组件的代码,但是都太庞大。别说用了,光部署就要折腾半天时间。有没有简单好用的,可以支持不同语言,不同服务之间的通讯呢?
这里强烈感谢7CAT。他给了我很大的支持,并推荐了thrift这个组件。
但是,从安装到最后达成我自己写的测试用例,还是废了一番周折的,走了一些弯路,但是最后的结果是值得的,达到了我的预期效果。

首先,thrift可以从官方网站下载。http://thrift.apache.org/
实际大小很小,大概也就3.3M,当前最新版本0.9.1


下载以后,在Linux下开始编译。
但是编译顺序远非它的说明文档里面的那样简洁。
如果按照它的说明文档编译,你会遇到各种错误,
比如,你要加入各种 ./configure --with-qt4=no 这样的限制条件,默认它会采用认为你有qt4,对于纯命令行的服务器而言,这个是无用的。
就算这样,你还是会遇到编译失败(例子代码无法编译)。查询说必须去下载它的git,git也下载了,错误依然。
是在是没办法,于是只好手动去编译它,不让它去编译自己的例子。
以下是编译步骤,按照这个步骤0.9.1会编译成功。
1、编译compiler,
2、编译lib\cpp
3、进tutoials\cpp里编译。首先进入compiler的cpp目录,去make
再去lib\cpp里面去make
最后编译它的教程tutoials\cpp

好了,组件编译好了,下面来写一个标准的idl吧,以前写过ice的idl文件,看到这样的语法很亲切的感觉。

namespace cpp mytest
namespace java mytest

/* Tag数据结构体声明 */
struct Tag {
1: required i32 ntag = 0,
2: required string strData = "",
}

/* 相应的Tag列表 */
typedef list<Tag> vecTags

/* 声明相关接口 */
service WebInfo{
        void GetWebInfo(1:vecTags objData),
}
一个简单的例子,一个GetWebInfo方法,获得一个vector容器,并把容器内的数据打印出来
idl文件写好了,记得保存在compiler/cpp目录下。存为文件名mytest.thrift

实现相关代码
class CWebInfo : public WebInfoIf
{
public:
                CWebInfo() {} ;
               
                void GetWebInfo(const vecTags& objData)
                {
                        printf("Begin(%d).\n", (int)objData.size());
                        for(int i = 0; i < (int)objData.size(); i++)
                        {
                                printf("ntag=%d,strData=%s.\n", objData.ntag, objData.strData.c_str());
                        }
                        printf("End.\n");
                }
};



然后在linux下编译,让它生成中间文件。
./thrift --gen cpp mytest.thrift如果程序没问题的话,会在当前目录上生成 一个gen-cpp文件夹。
这些都是你需要的中间文件,你把这些文件拷贝到你自己的工程目录下。
比如,我把这些文件拷贝到了我的idl目录下。
好了,东西都准备好了。
写一个server和client吧。
#include <thrift/concurrency/ThreadManager.h>
#include <thrift/concurrency/PosixThreadFactory.h>
#include <thrift/protocol/TBinaryProtocol.h>
#include <thrift/server/TSimpleServer.h>
#include <thrift/server/TThreadPoolServer.h>
#include <thrift/server/TThreadedServer.h>
#include <thrift/transport/TServerSocket.h>
#include <thrift/transport/TTransportUtils.h>

#include <iostream>
#include <stdexcept>
#include <sstream>

#include "./idl/WebInfo.h"

using namespace std;
using namespace apache::thrift;
using namespace apache::thrift::protocol;
using namespace apache::thrift::transport;
using namespace apache::thrift::server;

using namespace mytest;

using namespace boost;

class CWebInfo : public WebInfoIf
{
public:
                CWebInfo() {} ;
               
                void GetWebInfo(vecTags& _return)
                {
                        printf("Begin(%d).\n", (int)_return.size());
                        for(int i = 0; i < (int)_return.size(); i++)
                        {
                                printf("ntag=%d,strData=%s.\n", _return.ntag, _return.strData.c_str());
                        }
                        printf("End.\n");
                }
};

int main(int argc, char **argv)
{
shared_ptr<TProtocolFactory> protocolFactory(new TBinaryProtocolFactory());
shared_ptr<CWebInfo> handler(new CWebInfo());
shared_ptr<TProcessor> processor(new WebInfoProcessor(handler));
shared_ptr<TServerTransport> serverTransport(new TServerSocket(10090));
shared_ptr<TTransportFactory> transportFactory(new TBufferedTransportFactory());

TSimpleServer server(processor,
                     serverTransport,
                     transportFactory,
                     protocolFactory);

printf("Starting the server...\n");
server.serve();
printf("done.\n");
return 0;
}

然后再写一个客户端
#include <stdio.h>
#include <unistd.h>
#include <sys/time.h>

#include <thrift/protocol/TBinaryProtocol.h>
#include <thrift/transport/TSocket.h>
#include <thrift/transport/TTransportUtils.h>

#include "./idl/WebInfo.h"

using namespace std;
using namespace apache::thrift;
using namespace apache::thrift::protocol;
using namespace apache::thrift::transport;

using namespace mytest;

using namespace boost;

int main(int argc, char** argv) {
shared_ptr<TTransport> socket(new TSocket("localhost", 10090));
shared_ptr<TTransport> transport(new TBufferedTransport(socket));
shared_ptr<TProtocol> protocol(new TBinaryProtocol(transport));
WebInfoClient client(protocol);

Tag objTag1;
Tag objTag2;
vecTags objvecTags;

objTag1.ntag = 1;
objTag1.strData = "freeeyes";

objTag2.ntag = 2;
objTag2.strData = "shiqiang";

objvecTags.push_back(objTag1);
objvecTags.push_back(objTag2);

//打开连接
transport->open();

printf("Client is Begin(%d).\n", objvecTags.size());
//连接远程方法
client.GetWebInfo(objvecTags);
printf("Client is End.\n");

//关闭连接
transport->close();
}

为了方便,再写两个make文件。
先写一个make.define,用于公共调用。
# *****************************
# 预定义
# *****************************
CC = g++
AR = ar
CFLAGS = -g -O2 -D__LINUX__
#设置THRIFT_INCLUDE头文件路径和库文件位置
THRIFT_INCLUDE = /home/m2mjk/src/thrift-0.9.1/lib/cpp/src
THRIFT_LIB=/home/m2mjk/src/thrift-0.9.1/lib/cpp/.libs

INCLUDES = -I./ -I../ -I/usr/include -I${ACE_ROOT} -I../include -I${THRIFT_INCLUDE}
LIBS = -L/usr/lib64 -L/usr/lib -L/usr/local/lib64 -L./ -L${THRIFT_LIB}-L../ -ldl -lthrift
# *****************************
# 变换规则
# *****************************
# Here are some rules for converting .cpp -> .o
.SUFFIXES: .cpp .o
.cpp.o:
        @$(CC) -fPIC $(CFLAGS) ${INCLUDES} -c -g $*.cpp
        @echo '----- '$*.cpp' is compiled ok!'

# Here are some rules for converting .c -> .o
.SUFFIXES: .c .o
.c.o:
        @$(CC) $(CFLAGS) -c $*.c
        @echo '----- '$*.c' is compiled ok!'
再写两个make 分别是makefile_server和makefile_client
include makefile.define

# 默认超作
default:all

# 主应用文件列表
PATS = ./idl/mytest_constants.o \
       ./idl/mytest_types.o \
       ./idl/WebInfo.o \
       ./myServer.o

      
OBJS = mytest_constants.o \
       mytest_types.o \
       WebInfo.o \
       myServer.o
      
                       
# 主应用程序
APP_NAME = MytestServer

all:$(APP_NAME) makefile_server

$(APP_NAME):$(PATS)
        $(CC) -rdynamic -o $(APP_NAME) $(OBJS) $(LIBS)

# 清理
clean:
        rm -rf *.o $(APP_NAME)
       
cl:
        rm -rf *.o

include makefile.define

# 默认超作
default:all

# 主应用文件列表
PATS = ./idl/mytest_constants.o \
       ./idl/mytest_types.o \
       ./idl/WebInfo.o \
       ./myClient.o

      
OBJS = mytest_constants.o \
       mytest_types.o \
       WebInfo.o \
       myClient.o
      
                       
# 主应用程序
APP_NAME = MytestClient

all:$(APP_NAME) makefile_client

$(APP_NAME):$(PATS)
        $(CC) -rdynamic -o $(APP_NAME) $(OBJS) $(LIBS)

# 清理
clean:
        rm -rf *.o $(APP_NAME)
       
cl:
        rm -rf *.o



编译的时候, make -f makefile_server 和make -f makefile_client。
好了,编译一切顺利,运行。
输出结果:
(客户端输出)
-bash-4.1$ ./MytestClient
Client is Begin(2).
Client is End.

(服务端输出)
-bash-4.1$ ./MytestServer
Starting the server...
Begin(2).
ntag=1,strData=freeeyes.
ntag=2,strData=shiqiang.
End.


OK,全部达成。
此代码在Linux下测试通过。代码包如下。



zzappler 发表于 2014-2-28 15:46:10

楼主厉害~
:victory:

winston 发表于 2014-2-28 15:57:41

这个thirft 打通了不同语言、系统之间的通信机制,让交互变得更为简单、可靠、标准化。
官网的例子也很简明:定义:
struct UserProfile {
      1: i32 uid,
      2: string name,
      3: string blurb
      }
      service UserStorage {
      void store(1: UserProfile user),
      UserProfile retrieve(1: i32 uid)
      }
python 客户端代码:
# Make an object
up = UserProfile(uid=1,
               name="Test User",
               blurb="Thrift is great")

# Talk to a server via TCP sockets, using a binary protocol
transport = TSocket.TSocket("localhost", 9090)
transport.open()
protocol = TBinaryProtocol.TBinaryProtocol(transport)

# Use the service we already defined
service = UserStorage.Client(protocol)
service.store(up)

# Retrieve something as well
up2 = service.retrieve(2)

C++服务器端:
class UserStorageHandler : virtual public UserStorageIf {
   public:
    UserStorageHandler() {
      // Your initialization goes here
    }

    void store(const UserProfile& user) {
      // Your implementation goes here
      printf("store\n");
    }

    void retrieve(UserProfile& _return, const int32_t uid) {
      // Your implementation goes here
      printf("retrieve\n");
    }
};

int main(int argc, char **argv) {
    int port = 9090;
    shared_ptr<UserStorageHandler> handler(new UserStorageHandler());
    shared_ptr<TProcessor> processor(new UserStorageProcessor(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;
}




freeeyes 发表于 2014-2-28 16:48:15

对,我准备测试后,上这个,非常好使,而且小巧。
页: [1]
查看完整版本: 中间件thirft的Linux部署与运用