AcWing Linux基础课第六节thrift笔记

12 篇文章 0 订阅

1、该系列为ACWing中Linux基础课,已购买正版,课程作者为yxc
2、y总培训真的是业界良心,大家有时间可以报一下
3、为啥写在这儿,问就是oneNote的内存不够了QAQ

thrift教程

概述

写一个游戏/网站,并运行在一台或多台服务器上,此时每个同学会选择匹配对手,此时需要匹配服务(实现匹配池)。玩家点开匹配则向匹配系统发出请求或调用函数(例如add_user,表示在系统中添加一名玩家,remove_user匹配池中取消一名玩家)。匹配池负责不断添加玩家,删除玩家,发现分数相近的玩家将匹配结果发送给另外一个服务器(如数据存储服务器)。thrift是里面的有向边,想调用服务器的某个函数可以用thrift实现,负责传送数据,接收结果。
游戏系统框架
每一个节点是不同的进程,不同的进程可以用不同的语言来写。

thrift又被称为RPC框架(远程函数调用)。实质是操作系统的远程进程调用,将对方机器的进程通过协议进行调用,对方机器会判定这个调用是不是合法的,比如这个用户是不是我这里的注册用户。

写接口是为了使用thrift命令生成服务端中接口的实现函数,客户端通过client调用服务端的函数即可。

使用场景

1、定义接口
2、服务端生成server;
3、请求端用client请求服务;

实现

上课代码地址:acwing_git_thrift_lesson
1、实现游戏节点(server和client端)
2、实现匹配池(client端)
3、数据存储(已实现)

步骤

1、mkdir match_system game thrift创建匹配系统、游戏端、存放各种thrift接口
2、定义添加玩家和删除玩家的接口;
3、在thrift文件夹下acs@61f2b97ca5ed:~/thrift_lesson/thrift$ vim match.thrift,内容如下:

  • 定义命名空间namespace;
  • 定义结构体struct,传输用户信息;
  • 定义函数service,里面定义了两个接口,返回值是i32(int);
    namespace cpp match_service 
    # namespace 
    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),
    }
    

4、用接口实现具体的节点
(1)实现服务端的match_service, 通过接口实现c++版本服务器

  • 进入match_service文件夹,创建src文件夹存源文件;
  • 通过thrift -r --gen cpp ../../thrift/match.thrift创建c++版本服务器,其中../../thrift/match.thrift是接口路径,生成gen-cpp文件夹;
  • mv gen-cpp/ match_server修改文件名;
  • mv match_server/Match_server.skeleton.cpp main.cpp
  • 修改main.cpp接口定义好了,业务逻辑还没有定义好;
  • main.cpp添加模块并编译g++ -c main.cpp match_server/*.cpp
  • 链接文件g++ *.o -o main -lthrift用了thrift动态链接库要加-lthrift
  • 运行文件命令./main
  • git保存(除.o和可执行文件main)

(2)实现客户端的match_service

  • game文件夹中创建src源文件夹;
  • 生成python服务器端,命令thrift -r --gen py ../../thrift/match.thrift
  • 重命名mv gen-py/ match_client
  • 删除文件rm Match-remote,因为只实现客户端;
  • 添加/src/client.py文件,写入python客户端的例子:
    from match_client.match import Match
    from tutorial.ttypes import InvalidOperation, Operation, Work
    
    from thrift import Thrift
    from thrift.transport import TSocket
    from thrift.transport import TTransport
    from thrift.protocol import TBinaryProtocol
    
    from sys import stdin 
    
    def operate(op, user_id, username, score):
        # Make socket
        transport = TSocket.TSocket('localhost', 9090)
    
        # Buffering is critical. Raw sockets are very slow
        transport = TTransport.TBufferedTransport(transport)
    
        # Wrap in a protocol
        protocol = TBinaryProtocol.TBinaryProtocol(transport)
    
        # Create a client to use the protocol encoder
        client = Match.Client(protocol)
    
        # Connect!
        transport.open()  
        
    	user = User(user_id, username, score)
    	if op == "add":
    		client.add_user(user,"")
    	elif op == "remove":
    		client.remove_user(user,"")
    	
        # Close!
        transport.close()
        
    def main():
    	for line in stdin:
    		op, user_id, username, score = line.split(' ')
    		operate(op, int(user_id), username, int(score))
    		
    if __name__ == "__main__": 
    	main()
    
  • python3 client.py运行(同时运行./main能够实现用python调用c++的代码);
  • git保存(除.pyc文件git restore --stage *.pyc)

(2)实现匹配的server端

  • 采用并行,在部分线程加入/删除用户的同时,另外一个线程进行匹配,并传给数据存储服务器;

  • 修改main.cpp,加入线程操作函数(生产者-消费者模型),代码如下;

  • 生产者和消费者之间通信的媒介可以用:消息队列;

    #include "match_server/Match.h" 
    #include <thrift/protocol/TBinaryProtocol.h>
    #include <thrift/server/TSimpleServer.h>
    #include <thrift/transport/TServerSocket.h>
    #include <thrift/transport/TBufferTransports.h>
    
    #include <iostream>
    #include <thread> 
    #include <mutex>
    #include <condition_variable>
    #include <queue>
    #include <vector>
    
    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 std;
    
    struct Task
     {
        User user;
        string type;
    };
     
    struct MessageQueue
    {
         queue<Task> q; 
         mutex m;
         condition_variable cv;
     }message_queue; 
    
    class Pool
    {                                                                
        public:
             void save_result(int a, int b)
             {
                printf("Match  Result: %d %d\n", a, b);
             }
            
            void match()
            {
                while (users.size()>1)
                {
                   auto a = users[0],b = users[1];
                   users.erase(users.begin());
                   users.erase(users.begin());
    
                   save_result(a.id, b.id);
                }
           }
    	   void add(User user)
           {
                users.push_back(user);
           }
     
           void remove(User user)
           {
               for (uint32_t i = 0; i<users.size(); i++)
                  if (users[i].id == user.id)
                  {
                      users.erase(users.begin() + i);
                      break;
                  }
           }
       private:
            vector<User> users;
    }
    
    class MatchHandler : virtual public MatchIf {
    	public: 
    		Match Handler() { 
    		// Your initialization goes here
    		}
      		int32_t add_user(const User& user, const std::string& info) {
                // Your implementation goes here
                printf("add_user\n");
    			
    		    unique_lock<mutex> lck(message_queue.m);//lock上锁
                message_queue.q.push({user,"add"});
                message_queue.cv.notify_all(); //huan xing
                
                return 0;
             }
    		int32_t remove_user(const User& user, const std::string& info) {
    			// Your implementation goes here             
    			printf("remove_user\n");   
    
    			unique_lock<mutex> lck(message_queue.m);//lock
    			message_queue.q.push({user,"remove"});    
    			message_queue.cv.notify_all(); //huan xing
    			                                
                return 0;
              }
    };
    
    void consumer_task()
     {
         while(true)
         {
             unique_lock<mutex> lck(message_queue.m);
             if (message_queue.q.empty())
             {
                message_queue.cv.wait(lck); //zu se
             }
             else
             {                                                        
                 auto task = message_queue.q.front();
                 message_queue.q.pop();
                 lck.unlock();//for doing task and add/remove users in     time
     
                 // do task
                 if (task.type == "add") pool.add(task.user);
                 else if (task.type == "remove") pool.remove(task.user);
                 pool.match();
             }
        }
    } 
     
    int main(int argc, char **argv) {
    	int port = 9090;
    	::std::shared_ptr<MatchHandler> handler(new MatchHandler());
    	::std::shared_ptr<TProcessor> processor(new MatchProcessor(handler));
    	::std::shared_ptr<TServerTransport> serverTransport(new TServerSocket(    port));
     	::std::shared_ptr<TTransportFactory> transportFactory(new TBufferedTra    nsportFactory());
     	::std::shared_ptr<TProtocolFactory> protocolFactory(new TBinaryProtoco    lFactory());
    
    	TSimpleServer server(processor, serverTransport, transportFactory, pro    tocolFactory);
    
    	cout << "Start Match Server" << endl;
    
    	thread matching_thread(consumer_task);
    
    	server.serve();
    	return 0;
     }
    
  • 编译时加入线程的动态链接库g++ *.o -o main -lthrift -pthread

(3)实现数据存入的server端

  • username: myserver的名称

  • md5sum输入myserver的密码(+回车+ctrl+d)加密后的前8位;

    namespace cpp save_service
    
    service Save {
        /**
         * username: myserver的名称
         * password: myserver的密码的md5sum的前8位
         * 用户名密码验证成功会返回0,验证失败会返回1
         * 验证成功后,结果会被保存到myserver:homework/lesson_6/result.txt中
         */
        i32 save_data(1: string username, 2: string password, 3: i32 player1_id, 4: i32 player2_id)
    }
    

(4)实现数据存入的client端

  • match_system/src/文件夹内生成``,命令thrift -r --gen cpp ../../thrift/save.thrift

  • mv gen-cpp/ save_client

  • rm Save_server.skeleton.cpp

  • 将save的client端代码写入项目:进入main.cpp,将thrift官方教程手册抄下来,代码中加入:

    #include "save_client/Save.h"
    #include <thrift/transport/TSocket.h>
    #include <thrift/transport/TTransportUtils.h>
    
    using namespace ::save_service;
     class Pool
     {
         public:
             void save_result(int a, int b)
             {
              printf("Match  Result: %d %d\n", a, b);
    		  std::shared_ptr<TTransport> socket(new TSocket("123.57.47.211", 9090));
    		  std::shared_ptr<TTransport> transport(new TBufferedTransport(socket));
    		  std::shared_ptr<TProtocol> protocol(new TBinaryProtocol(transport));
    		  SaveClient client(protocol);
    		
    		  try {
    		    transport->open();
    		
    		    client.save_data("acs_xxxx","xxxxxxxx", a, b);
    		
    		    transport->close();
    		  } catch (TException& tx) {
    		    cout << "ERROR: " << tx.what() << endl;
    		  }
    
  • myserver修改IP地址,在client.pytransport = TSocket.TSocket('127.0.0.1', 9090),在main.cpp中;

(5)升级匹配系统

  • main.cpp中修改,按照分差匹配,每1秒钟看一次;
    #include <unistd.h>
    
     70         void match()                                                               
     71         {
     72             while (users.size()>1)
     73             {
     74                 sort(users.begin(), users.end(), [&](User& a, User b){
     75                     return a.score < b.score;
     76                         });
     77 
     78                 bool flag = true;
     79                 for (uint32_t i = 1; i < users.size(); i++)
     80                 {
     81                     auto a = users[i - 1], b = users[i];
     82                     if (b.score - a.score <= 50)
     83                     {
     84                         users.erase(users.begin() + i -1, users.begin() + i + 1);
     85                         save_result(a.id, b.id);
     86 
     87                         flag = false;
     88                         break;
     89                                                                                    
     90                     }
     91                 }
     92                 if (flag) break;
     93             }
     94         }
    =============================================================
    145 void consumer_task()
    146 {                                                                                  
    147     while(true)
    148     {
    149         unique_lock<mutex> lck(message_queue.m);
    150         if (message_queue.q.empty())
    151         {
    152             //message_queue.cv.wait(lck); //zu se
    153             lck.unlock();
    154             pool.match();
    155             sleep(1);
    156         }
    157         else
    158         {
    159             auto task = message_queue.q.front();
    160             message_queue.q.pop();
    161             lck.unlock();//for doing task and add/remove users in time
    162 
    163             // do task
    164             if (task.type == "add") pool.add(task.user);
    165             else if (task.type == "remove") pool.remove(task.user);
    166             pool.match();                                                          
    167         }
    168     }
    169 }
    

(6)升级服务端为多线程

  • 修改原有的main.cpp文件;
    #include <thrift/concurrency/ThreadManager.h>
    #include <thrift/concurrency/ThreadFactory.h>
    #include <thrift/TToString.h>
    #include <thrift/server/TThreadPoolServer.h>
    #include <thrift/server/TThreadedServer.h>
    
    150 class MatchCloneFactory : virtual public MatchIfFactory {
    151     public:
    152         ~MatchCloneFactory() override = default;
    153         MatchIf* getHandler(const ::apache::thrift::TConnectionInfo& connInfo) override
    154         {
    155             std::shared_ptr<TSocket> sock = std::dynamic_pointer_cast<TSocket>(connInfo.trans    port);
    156             cout << "Incoming connection\n";
    157             cout << "\tSocketInfo: "  << sock->getSocketInfo() << "\n";
    158             cout << "\tPeerHost: "    << sock->getPeerHost() << "\n";
    159             cout << "\tPeerAddress: " << sock->getPeerAddress() << "\n";
    160             cout << "\tPeerPort: "    << sock->getPeerPort() << "\n";
    161             return new MatchHandler;
    162         }
    163         void releaseHandler(MatchIf* handler) override {                                     
    164             delete handler;
    165         }
    166 };
    ==============================================
    195 int main(int argc, char **argv) {
    196                                                                                              
    197     TThreadedServer server(
    198             std::make_shared<MatchProcessorFactory>(std::make_shared<MatchCloneFactory>()),
    199             std::make_shared<TServerSocket>(9090), //port
    200             std::make_shared<TBufferedTransportFactory>(),
    201             std::make_shared<TBinaryProtocolFactory>());
    202 
    203 
    204     cout << "Start Match Server" << endl;
    205 
    206     thread matching_thread(consumer_task);
    207 
    208     server.serve();
    209     return 0;
    210 }
    
    

**(7)匹配机制优化,**在没有分数相近的用户时,寻找其他分数段的用户

  • 增加玩家已经被循环了多少次(等待时间);

     75         bool check_match(uint32_t i, uint32_t j)
     76         {                                                                                 
     77             auto a = users[i], b = users[j];
     78 
     79             int dt  = abs(a.score - b.score);
     80             int a_max_dif = wt[i] * 50;
     81             int b_max_dif = wt[j] * 50;
     82 
     83             return dt <= a_max_dif && dt <= b_max_dif;
     84         }
     85 
     86         void match()
     87         {
     88             for (uint32_t i=0; i < wt.size(); i ++)
     89                 wt[i] ++;   // waiting time +1
     90             while (users.size() > 1)
     91             {
     92                 bool flag = true;
     93                 for (uint32_t i = 0; i < users.size(); i++)
     94                 {
     95                     for (uint32_t j = i + 1; j < users.size(); j++)
     96                     {
     97                         if (check_match(i, j))
     98                         {
     99                             auto a = users[i],  b = users[j];
    100                             users.erase(users.begin() + j);
    101                             users.erase(users.begin() + i);
    102                             wt.erase(wt.begin() + j);
    103                             wt.erase(wt.begin() + i);
    104                             save_result(a.id, b.id);
    105                             flag = false;
    106                             break;
    107                         }
    108 
    109                     }
    110                     if (!flag) break;
    111                 }
    112                 if (flag) break;
    113             }
    114         }
    115 
    116         void add(User user)                                                               
    117         {
    118             users.push_back(user);
    119             wt.push_back(0);
    120         }
    121 
    122         void remove(User user)
    123         {
    124             for (uint32_t i = 0; i<users.size(); i++)
    125                 if (users[i].id == user.id)                                               
    126                 {
    127                     users.erase(users.begin() + i);
    128                     wt.erase(wt.begin() +i);
    129                     break;
    130                 }
    131         }
    132     private:
    133         vector<User> users;                                                               
    134         vector<int> wt; //waiting time
    

全部项目已上传至:
https://git.acwing.com/zxy_12138/thrift_lesson/

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值