参考:https://zhuanlan.zhihu.com/p/85033562
https://blog.csdn.net/winwill2012/article/details/71627886
RPC(Remote Procedure Call),远程过程调用,是一个分布式系统间通信的技术。最核心要解决的问题是,如何调用执行另一个地址空间上的函数、方法,就感觉如同在本地调用一样。这个是什么意思的呢?假设有两台主机host A和host B,host B中有一个函数,比如add()函数,那么host A调用host B的add()的过程,就叫做RPC。
那么针对RPC通过上图可以看到,在整个RPC通信过程中,需要考虑的主要问题有以下两点
- 序列化和反序列化,在请求端需要做到序列化将对象转换为二进制,在服务端需要做到反序列化将收到的二进制转化为对象。当然这边还需要涉及到一定的协议结构,这些觉得都是为了保证请求端和服务端能正确的处理发送相关调用信息;
- 传输的问题,针对RPC来说,需要确保通信的可靠,所以一般来说通信是建立在TCP之上的。
Thrift是一个融合了序列化 +RPC的跨语言的RPC框架,最初由Facebook于2007年开发,2008年进入Apache开源项目。Thrift通过一个中间语言(IDL, 接口定义语言)来定义RPC的接口和数据类型,然后通过一个编译器生成不同语言的代码(目前支持C++,Java, Python, PHP, Ruby, Erlang, Perl, Haskell, C#, Cocoa, Smalltalk和OCaml等),并由生成的代码负责RPC协议层和传输层的实,RPC是C-S模式的。
thrift使用步骤:
-
Download Apache Thrift
-
Build and Install the Apache Thrift compiler and libraries
-
Writing a .thrift file
After the Thrift compiler is installed you will need to create a .thrift file. This file is an interface definition made up of thrift types and Services. The services you define in this file are implemented by the server and are called by any clients. -
Generate Thrift file to source code
The Thrift compiler is used to generate your Thrift file into source code which is used by the different client libraries and the server you write. To generate the source from a Thrift file runthrift –gen
To recursivly generate source code from a Thrift file and all other Thrift files included by it, run
thrift -r --gen
具体Tutorial见:https://thrift.apache.org/tutorial/
2.1 match_service(服务端)C++实现: 2.1.1 在thrift文件夹中创建match.thriftnamespace cpp match_service
struct User {
1: i32 id,
2: string name,
3: i32 score
}
service Match {
i32 add_user(1: User user, 2: string info),
i32 remove_user(1: User user, 2: string info),
}
2.1.2 在match_system文件夹中执行
thrift -r --gen cpp ../../thrift/match.thrift
生成文件,将生成的gen-cpp文件夹改名为match_server,将文件夹中的Match_server.skeleton.cpp 拿出来作为main.cpp进行编译:
g++ -c main.cpp match_server
i32 save_data(1: string username, 2: string password, 3: i32 player1_id, 4: i32 player2_id)
}
2.3.2 在match_system文件夹中执行
thrift -r --gen cpp ../../thrift/save.thrift
将生成的文件夹改名为save_client
并删除生成文件夹中的Save_server.skeleton.cpp(避免出现两个main函数)
编辑main.cpp,实现保存信息与用户匹配功能
#include "match_server/Match.h" #include "save_client/Save.h" #include2.4 编译文件,运行#include #include #include #include #include #include #include #include #include #include #include #include #include //条件变量,对锁mutex进行封装 #include #include #include using namespace ::apache::thrift; using namespace ::apache::thrift::protocol; using namespace ::apache::thrift::transport; using namespace ::apache::thrift::server; using namespace ::match_service; using namespace ::save_service; using namespace std; struct Task { User user; string type; }; struct MessageQueue //为实现消费队列,生产者与消费者通信的媒介 { queue q; mutex m; condition_variable cv; }message_queue; class Pool //匹配池 { public: void save_result(int a, int b) //存储数据函数(在thrift官网复制下来然后改改) { printf("Match Result: %d %dn", a, b); 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_746", "a677dcd3", a, b); if (!res) puts("success"); else puts("failed"); transport->close(); } catch (TException& tx) { cout << "ERROR: " << tx.what() << endl; } } bool check_match(uint32_t i, uint32_t j) //判断a,b是否匹配 { auto a = users[i], b = users[j]; int dt = abs(a.score - b.score); //分差 int a_max_dif = wt[i] * 50; int b_max_dif = wt[j] * 50; return dt <= a_max_dif && dt <= b_max_dif; } void match() //匹配函数 { for (uint32_t i = 0; i < wt.size(); i ++ ) wt[i] ++ ; // 等待秒数 + 1 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_match(i, j)) //判断a,b是否匹配 { auto a = users[i], b = users[j]; users.erase(users.begin() + j); users.erase(users.begin() + i); wt.erase(wt.begin() + j); wt.erase(wt.begin() + i); save_result(a.id, b.id); flag = false; break; } } if (!flag) break; } if (flag) break; } } void add(User user) //添加函数 { users.push_back(user); wt.push_back(0); } void remove(User user) //删除函数 { for (uint32_t i = 0; i < users.size(); i ++ ) if (users[i].id == user.id) { users.erase(users.begin() + i); wt.erase(wt.begin() + i); break; } } private: vector users; vector wt; // 等待时间, 单位:s }pool; class MatchHandler : virtual public MatchIf { public: MatchHandler() { // Your initialization goes here } int32_t add_user(const User& user, const std::string& info) { // Your implementation goes here printf("add_usern"); unique_lock lck(message_queue.m); //加锁,保证同一时间只有一个线程在工作 message_queue.q.push({user, "add"}); //将任务加入消费队列 message_queue.cv.notify_all(); //唤醒条件变量 return 0; } int32_t remove_user(const User& user, const std::string& info) { // Your implementation goes here printf("remove_usern"); unique_lock lck(message_queue.m); message_queue.q.push({user, "remove"}); message_queue.cv.notify_all(); //唤醒条件变量 return 0; } }; class MatchCloneFactory : virtual public MatchIfFactory { //多线程函数 public: ~MatchCloneFactory() override = default; MatchIf* getHandler(const ::apache::thrift::TConnectionInfo& connInfo) override { std::shared_ptr sock = std::dynamic_pointer_cast (connInfo.transport); return new MatchHandler; } void releaseHandler(MatchIf* handler) override { delete handler; } }; void consume_task() //生产者-消费者模型 { while (true) //一个死循环,查看有没有玩家匹配到一起 { unique_lock lck(message_queue.m); if (message_queue.q.empty()) //如果队列为空(游戏刚开始时队列为空 { // message_queue.cv.wait(lck); //等待唤醒条件变量,唤醒后才会继续执行 lck.unlock(); pool.match(); //匹配 sleep(1); //休息一秒,实现每一秒匹配一次 } else { auto task = message_queue.q.front(); message_queue.q.pop(); lck.unlock(); if (task.type == "add") pool.add(task.user); else if (task.type == "remove") pool.remove(task.user); } } } 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); //开启多线程 server.serve(); return 0; }
g++ -c main.cpp match_server/*.cpp save_client/*.cpp
g++ *.o -o main -lthrift -pthread
运行查看效果:



