Apache Thrift软件框架用于可伸缩的跨语言服务开发,它将软件栈和代码生成引擎结合在一起,以构建在C++、Java、Python、PHP、Ruby、Erlang、Perl、Haskell、C#、Cocoa、JavaScript、Node.js、Smalltalk、OCaml和Delphi等语言之间高效、无缝地工作的服务。
Thrift 采用IDL(Interface Definition Language)来定义通用的服务接口,然后通过Thrift提供的编译器,可以将服务接口编译成不同语言编写的代码,通过这个方式来实现跨语言的功能。
通过命令调用Thrift提供的编译器将服务接口编译成不同语言编写的代码
这些代码又分为服务端和客户端,将所在不同进程(或服务器)的功能连接起来
thrift -r --gen
创建一个Thrift服务
-
定义服务接口(存放接口的文件夹就是thrift文件)
-
作为服务端的服务,需要生成server
-
作为请求端的服务,需要生成client
mkdir thrift_lesson cd thrift_lesson git init mkdir thrift # 存放 thrift 源文件 mkdir game # 实现 游戏应用端 的 客户端 功能 mkdir match_system # 实现 匹配系统服务器 的 服务端 和 客户端 功能 touch readme.md # 远程创建好git上的repo git remote add origin git@git.acwing.com:lansfair/thrift_learning.git git add . git commit -m "init repo" git push -u origin master2.2 初步实现 游戏应用端 与 匹配系统服务器 的交互 2.2.1 创建 match.thrift 接口文件
match.thrift:用于实现 游戏应用端 与 匹配系统服务器 交互的 service
namespace cpp match_service //声明转换为C++语言
struct User { //定义结构体 User
1: i32 id,
2: string name,
3: i32 score
}
service Match { //定义service服务
i32 add_user(1: User user, 2: string info),
i32 remove_user(1: User user, 2: string info),
}
2.2.2 匹配系统的服务端
利用match.thrift接口文件生成C++的匹配系统服务端
-
进入 match_system 中新建文件夹 src (之后所有的 匹配服务器 源文件放在 src 下)
-
并在 src 文件夹中运行 thrift 脚本生成 C++ 版本的文件
# 原始的thrift生成文件语法:thrift -r --genmkdir match_system/src cd match_system/src thrift -r --gen cpp ../../thrift/match.thrift # 将该文件夹重命名为 match_server(区别于之后要此处生成的client server) # match_server 与 游戏应用端交互 ; client_server 与 数据存储服务器交互 mv gen-cpp match_server # 把 Match_server.skeleton.cpp 移动到当前 src 目录下并重命名为 main.cpp # 方便之后调试 main.cpp 文件,其他的源文件仍被存放在 src 文件夹下 mv match_server/Match_server.skeleton.cpp main.cpp # 移动后,需要修改一下 main.cpp 中头文件里Math.cpp 的引用路径 # 并且先暂时让main.cpp中的函数return 0
生成完成后的工作区路径
编译并运行 cpp 文件
# 1. 编译 所有的 .cpp 文件生成 .o 文件
# g++ -c [文件1.cpp] [文件2.cpp] ...
g++ -c main.cpp match_server
i32 save_data(1: string username, 2: string password, 3: i32 player1_id, 4: i32 player2_id)
}
在 匹配系统服务器 利用 thrift 生成 C++ 文件
并删掉不必要 服务端 文件,因为在该交互功能里,匹配系统服务器 是作为客户端的(C++ 只能有一个main函数)
cd match_system/src thrift -r --gen cpp ../../thrift/save.thrift mv gen-cpp save_client rm save_client/Save_server.skeleton.cpp
利用 md5 哈希函数获得服务器密码的 哈希值
md5sum xxxxxxx [] [Ctrl + D] # 哈希值前 8 位就是我们要调用服务器接口时用的密码
从 thrift官网 复制 Client 端的模板到 main.cpp 下与 数据存储服务器 交互的函数中
(数据存储服务器已经写好了相关服务并开启)
// 需要额外引入的头文件 #include#include // 需要额外声明的命名空间 using namespace ::save_service; //重写 save_result 内的内容,使其能够与 "数据存储服务器" 交互 void save_result(int a, int b) // 记录成功匹配的信息 { printf("Match Result: %d %dn", a, b); // Client端的板子 std::shared_ptr socket(new TSocket("123.57.47.211", 9090)); std::shared_ptr transport(new TBufferedTransport(socket)); std::shared_ptr protocol(new TBinaryProtocol(transport)); SaveClient client(protocol); try { transport->open(); //调用接口,把信息存储 "数据存储服务器" 中 int res = client.save_data("acs_2511", "ac046a27", a, b); //输出匹配结果 if (!res) puts("success"); else puts("fail"); transport->close(); } catch (TException& tx) { cout << "ERROR: " << tx.what() << endl; } }
编译,运行,上传
g++ -c main.cpp save_client
return new MatchHandler;
}
void releaseHandler(MatchIf* handler) override { //改为MatchIf*
delete handler;
}
};
// 重写main函数,启用多线程服务器
int main(int argc, char **argv) {
TThreadedServer server(
std::make_shared(std::make_shared()),
std::make_shared(9090), //port
std::make_shared(),
std::make_shared());
cout << "Start Match Server" << endl;
thread matching_thread(consume_task); // 调用一个线程运行 consume_task
server.serve();
return 0;
}
编译,运行,上传
g++ -c main.cpp g++ *.o -o main -lthrift -pthread ./main # 上传到 git 服务器 git add main.cpp git commit -m "match server:4.0" git push2.5 匹配系统 5.0
随时间扩大匹配域:用额外一个数组 wt 来记录每个用户的等待时间,在消息队列为空时,线程会每 1 秒调用一次 match 函数,然后每次调用 match 函数,会首先对 匹配池中所有用户的 wt 值自增 1,从而实现用 wt 记录每个用户的等待时间,然后,每一单位的 wt 会扩大 50分 的匹配域。
修改main.cpp
// 主要修改的是 pool 类中关于 match 函数的部分
class Pool
{
public:
....
bool check(int i, int j)
{
User a = users[i], b = users[j];
int diff = abs(a.score - b.score);
int a_diff_max = wt[i] * 50;
int b_diff_max = wt[j] * 50;
return diff <= a_diff_max && diff <= b_diff_max;
}
void match() // 匹配池中的第一、第二个用户进行匹配
{
// 每 1 秒调用 1 次 match,实现 wt 自增 1,从而实现使所有用户等待时间增加
for (auto &t: wt)
t ++ ;
while (users.size() > 1)
{
bool flag = true;
for (uint32_t i = 0; i < users.size(); i ++ )
{
for (uint32_t j = i + 1; j < users.size(); j ++ )
{
if (check(i, j))
{
users.erase(users.begin() + j);
users.erase(users.begin() + i);
wt.erase(wt.begin() + j);
wt.erase(wt.begin() + i);
save_result(users[i].id, users[j].id);
flag = false;
break;
}
if (!flag) break;
}
}
if (flag) break; // 一轮扫描后,发现没有能够匹配的用户,就停止扫描,等待下次调用
}
}
....
private:
....
vector wt; //wait_time 记录每个用户的等待时间
}pool;
编译,运行,上传
g++ -c main.cpp g++ *.o -o main -lthrift -pthread ./main # 上传到 git 服务器 git add main.cpp git commit -m "match server:5.0" git push



