Thrift的安装及分布式调试

1.Thrift的简单介绍

Thrift是一个跨语言跨平台的服务部署框架,由Facebook在2007年开发,2008年进入Apache开源项目。Thrift通过接口定义语言来定义RPC的接口和数据类型,再通过Thrift编译器生成不同的语言代码,并由生成的代码负责RPC协议层和传输层的实现。其架构如下:
在这里插入图片描述
在上图中,最上层的是用户的业务逻辑代码,中间两层则是thrift编译器生成的代码,用于结构化数的解析,发送和接收等。TProtocol则是协议层,定义了数据的传输格式,主要支持数据格式有二进制格式、压缩格式和json格式等。TTransport则是Thrift的传输层,支持阻塞和非阻塞的IO传输方式。最底层的传输协议则是采用常规的传输协议,例如TCP/IP,Http等等。
下面我根据自己的ubuntu和windows上的环境搭建做一个整理。

2.Ubuntu下安装thrift

关于ubuntu下安装thrift,在这里分两种情况,一种是离线安装,一种是在线安装。先介绍一下在线安装。

1.在线安装
a.在thrift官网(https://thrift.apache.org/download)下载thritf的源码压缩包,如下:
在这里插入图片描述
b.执行以下指令,安装编译thrift所需的第三方依赖库:

sudo apt-get install libboost-dev libboost-test-dev libboost-program-options-dev libevent-dev automake libtool flex bison pkg-config g++ libssl-dev

c.第三方依赖库安装完毕后开始编译thrift源码,解压源码压缩包。
d.编译thrift源码

1. cd thrift-0.13.0
2. sudo ./configure
3. sudo make
4. sudo make install

正常情况下,第三方库安装没问题的话,编译thrift源码也不会有什么问题,thrift安装完成后可以在终端输入thrift -version,如果出现thrift版本号则说明thrift安装成功。

2.离线安装
离线安装最主要的也是要把第三方库安装正确。安装过程中最好都使用root权限,避免一些操作权限不足引起失败。
a.分别下载boost,openssl,libevent第三方库的源码。
b.编译安装openssl

1. cd openssl-1.1.1
2. sudo ./config shared
3. sudo make
4. sudo make install

c.编译安装libevent

5. cd libevent-2.0.22-stable
6. sudo . /configure --prefix=/usr
7. sudo make
8. sudo make install 

d.编译安装boost

9. cd boost_1_66_0
10. sudo ./bootstrap.sh
11. sudo ./b2
12. sudo ./b2 install

e.编译安装thrift源码的步骤则是和在线安装是一样的。
同样的在终端输入thrift -version可以验证thrift是否安装完成。在离线安装过程中,如果编译thrift编译报错,有可能是第三方库没有安装正确。

3.windows上安装Thrift

接下来讲一下在windows上搭建thrift的环境,在此我会介绍两种语言的搭建方法,一种是csharp,另一种则是c++。不管哪一种语言,都需要先下载thrift.exe,以支持在windows的CMD中执行thrift指令。thrift.exe可以在官网进行下载,如下:
在这里插入图片描述
下载此exe后无需安装,只需要将它放在某个目录,然后将该目录添加到环境变量即可。

1.搭建csharp thrift环境
a. 编译thrift csharp部分的源码,生成dll。Csharp源码路径在thrift-0.13.0\lib\csharp\src,使用VS打开项目工程,如下:

在这里插入图片描述
b. 打开项目工程后可以看到是有好几个项目工程,Thrift和Thrift.45。这里以设置Thrift工程为启动项目为例,需要先去掉项目工程中的依赖项thrift.snk这个文件。在最新的源码中是没有这个文件的,如下:
在这里插入图片描述
c.直接编译该项目,可以直接生成Thrift.dll,路径在thrift-0.13.0\lib\csharp\src\bin\Debug下。
到这,csharp的dll生成了,csharp的环境也就搭建完成。

2.搭建c++ thrift环境
a. 在windows上搭建thrift环境和ubuntu差不多,同样第一步都要先把boost,opnessl,libevent三个库正确安装。
b. 编译thrift的cpp部分源码,先打开cpp的项目工程,路径在:thrift-0.13.0\lib\cpp,如下:
在这里插入图片描述
c.打开项目文件后会有两个项目,一个是生成阻塞的库,一个是生成非阻塞库:两个库的编译上的设置有所差别。
d. 先编译非阻塞的库:设置编译时的头文件搜索路径和库的搜索路径,分别有三个头文件和库文件的路径,分别是boost,libevent,OpenSSL。打开项目的属性页,如下设置头文件和库路径:

在这里插入图片描述
在这里插入图片描述
e. 直接编译,可目录thrift-0.13.0\lib\cpp\Debug中生成libthriftnb.lib的库。
f. 接下来配置thrift阻塞库的编译环境。
g. 和配置非阻塞的库一样先配置相关的头文件和库文件的目录。
h. 从目录thrift-0.13.0\中将config.h文件拷贝到thrift-0.13.0\lib\cpp\src\thrift目录下。
i. 把thrift的源码路径添加到头文件搜索路径中,例如:
D:\thrift-0.13.0\lib\cpp\src\thrift 否则config.h会找不到。

j. 如此此时编译一下可以看到很多头文件是找不到的,如下:
在这里插入图片描述
这是因为这些头文件都是在linux中才有的。因此需要屏蔽相关的宏,使所有的linux的头文件都不被包含,而是使用windows上对应的头文件。
k. 屏蔽的宏如下:在config.h中进行注释:
1) HAVE_NETINET_IN_H
2) HAVE_SYS_SOCKET_H
3) HAVE_NETDB_H
4) HAVE_ARPA_INET_H
5) HAVE_SYS_TIME_H
l. 再编译,报错如下:
在这里插入图片描述
原因是VC不允许静态数据成员被定义成dllimport。解决办法,如下修改源码:
在这里插入图片描述
把上面文件中的THRIFT_EXPORT给注释掉了,直接用static bool来定义。
m. 编译,提示有几个文件找不到,实际上是源码中也没有这几个文件,可以从解决方案中直接移除。
在这里插入图片描述
n. 编译生成c++库。

4.示例

说了这么多,环境也搭建好了,现在就来看一下如何使用thrift吧。
本示例采用在windows端创建c#的客户端,在ubuntu上运行c++服务端。

1.客户端编写

1.编写IDL,命名为readlist.thrift:

service ReadDataService{
    list<double> readdata(),
}

2.在cmd中cd到readlist.thrift所在目录,执行如下指令生成代码:

thrift -r --gen csharp readlist.thrift

3.使用VS创建c#控制台项目,并将c#源码生成的Thrift.dll引入到项目中如下:
在这里插入图片描述
4.将第二步中通过thrift编译器生成的代码添加到项目中。
5.编写客户端代码,示例代码如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Thrift.Protocol;
using Thrift.Transport;
using System.Timers;
using Thrift;

namespace thrfitCsharp
{
    class ReadDataServiceClient
    {
        public const string SERVERIP = "192.168.100.130";//此IP地址为服务端IP地址
        public static int SERVERPORT = 9090;//端口和服务端端口一致
        public static TTransport transport = new TSocket(SERVERIP, SERVERPORT);
        public static TProtocol protocol = new TBinaryProtocol(transport);
        public static ReadDataService.Client client = new ReadDataService.Client(protocol);
        public static List<double> data;
        public static int counts = 0;
     
        public static long GetTimeStamp()
        {
            TimeSpan ts = DateTime.Now - new DateTime(1970, 1, 1, 0, 0, 0, 0);
            return Convert.ToInt64(ts.TotalMilliseconds);
        }
        
        public static void call(object source, System.Timers.ElapsedEventArgs e)
        {
            Console.WriteLine("call time = " + counts);
            counts++;
;           long time1 = ReadDataServiceClient.GetTimeStamp();
            for(int i = 0;i<50;i++)
            {
                data = client.readdata();
            }
            
            long time2 = ReadDataServiceClient.GetTimeStamp();
            Console.WriteLine("TIME = " + (time2 - time1));
        }

        static void Main()
        {
            System.Timers.Timer time = new System.Timers.Timer(200);

            //TTransport transport = null;
            try
            {
                data = new List<double>();
                /*data.Add(1.1);
                data.Add(2.2);
                foreach(var it in data)
                {
                    Console.WriteLine(it);
                }*/
                // transport = new TSocket(SERVERIP, SERVERPORT);
                //协议要和服务端一致
                //TProtocol protocol = new TBinaryProtocol(transport);
               // ReadDataService.Client client = new ReadDataService.Client(protocol);
                transport.Open();
                time.Elapsed += new System.Timers.ElapsedEventHandler(call);
                time.AutoReset = true;//设置是执行一次(false)还是一直执行(true);

                time.Enabled = true;//是否执行System.Timers.Timer.Elapsed事件;
                                 //string time1 = ReadDataServiceClient.GetTimeStamp();
                                 // long time1 = ReadDataServiceClient.GetTimeStamp();
                                 //Int32 t1 = Int32.Parse(time1);
                                 //Console.WriteLine(time1);
                                // for (int i = 0; i < 50; i++)
                                 //{
                                    // data = client.readdata();
                                // }
                                 //long time2 = ReadDataServiceClient.GetTimeStamp();
                                 //Int32 t2 = Int32.Parse(time2);
                                // Console.WriteLine(time2);

                //foreach(var it in data)
                //{
                //    Console.WriteLine(it);
                //}

                 //Console.WriteLine("time = " + (time2 - time1));
                //Console.WriteLine("Thrift client result =: " + data);

            }
            catch (Exception e)
            {
                Console.WriteLine(1);
                Console.WriteLine(e.StackTrace);
            }
            finally
            {
                if (null != transport)
                {
                    //close
                    
                    transport.Close();
                }
            }
            Console.ReadLine();
        }


    }
}

2.ubuntu上的c++服务端
1.编写和windows端的IDL完全一样的IDL
2. 执行以下指令,生成server端代码

thrift -r --gen cpp readlist.thrift 

3.用户自己的服务端的逻辑,主要修改ReadListService_server.skeleton.cpp,如下:
在这里插入图片描述
4. 编译生成可执行文件:
g++ -g -std=c++11 -I/usr/local/include -L/usr/local/lib ReadDataService.cpp readlist_types.cpp readlist_constants.cpp ReadDataService_server.skeleton.cpp -o server -lthrift
5.先运行服务端,再运行客户端即可。

总结

虽然通过RPC框架,可以让程序员编写代码不用太关注底层协议,例如TCP/IP,HTTP的传输,但是为了修炼强大的内功,多去了解底层的知识才能更有利于程序员写出更健壮的代码。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值