Using thrift to realize some learning notes of matching system (c + + to realize the part of match_system)

Time:2021-10-6

match_system

Based on thrift, the goal is to simulate a matching system, which is similar to the matching of two users in the game

Parameters received:

  • OneUserVariable representing a user
  • OnestringVariable, optional additional information added, set tostringType is also used to facilitate future maintenance. In this way, you can pass in ajsonTable, so it has good expansibility

version 1.0

It preliminarily realizes the transfer of adding user information and deleting user information. This part is very simple. Let’s talk about it briefly

C + + code part

// This autogenerated skeleton file illustrates how to build a server.
// You should copy it to another filename to avoid overwriting it.

#include "match_server/Match.h"
#include 
#include 
#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;

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_user\n");

            return 0;
        }

        int32_t remove_user(const User& user, const std::string& info) {
            // Your implementation goes here
            printf("remove_user\n");

            return 0;
        }

};

int main(int argc, char **argv) {
    int port = 9090;
    ::std::shared_ptr handler(new MatchHandler());
    ::std::shared_ptr processor(new MatchProcessor(handler));
    ::std::shared_ptr serverTransport(new TServerSocket(port));
    ::std::shared_ptr transportFactory(new TBufferedTransportFactory());
    ::std::shared_ptr protocolFactory(new TBinaryProtocolFactory());
    
    std::cout<

So when we start frommatch_cilentWhen the client calls the related function, it can be called remotelymatch_serverEnd related functions, add and delete users, and pass the user’s information to the past

version 2.0

Using multithreading to implementclientThis part is very important and will be expanded in detail

C + + code part

// This autogenerated skeleton file illustrates how to build a server.
// You should copy it to another filename to avoid overwriting it.

#include "match_server/Match.h"
#include 
#include 
#include 
#include 

#include 
#include 
#include 
#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 std;

class Task{
    public:
        User user;
        string type;
    private:
};

class MessageQueue{
    public:
        queue q;
        mutex m;
        condition_variable cv;
    private:
}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(user.id = users[i].id ){
                    users.erase(users.begin() + i);
                    break;
                }
        }
    private:
        vector users;
}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_user\n");

            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_user\n");

            unique_lock lck(message_queue.m);
            message_queue.q.push({user, "remove"});
            message_queue.cv.notify_all();


            return 0;
        }

};

void consume_task(){
    while(true){
        unique_lock lck(message_queue.m);
        if(message_queue.q.empty()){
            message_queue.cv.wait(lck);
        }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);

            pool.match();
        }
    }

}

int main(int argc, char **argv) {
    int port = 9090;
    ::std::shared_ptr handler(new MatchHandler());
    ::std::shared_ptr processor(new MatchProcessor(handler));
    ::std::shared_ptr serverTransport(new TServerSocket(port));
    ::std::shared_ptr transportFactory(new TBufferedTransportFactory());
    ::std::shared_ptr protocolFactory(new TBinaryProtocolFactory());

    std::cout<

consumer:

  • consume_task()

producer:

  • MatchHandlerMediumadd_user()
  • MatchHandlerMediumremove_user()

resources:

  • message_queue

We are forclientThe request sent from the end has two steps:

  • Receive and store requests
  • Processing requests

So with the above setProduction consumersystem

Because we are rightUser poolOperation, onlyadd_user()andremove_user()These two operations already belong to the above setProduction consumerModel, so there is no need to build a new model

Next, let’s talk about the code implementation

First, explain the general meaning of several related functions (because I haven’t systematically studied the relevant C + + documents, so write some personal understandings. If there are errors, you are welcome to point them out)

  • unique_lock lck(message_queue.m): thislckThe variable represents a mutex. After it is created,Allusemessage_queue.mThe lock can only be obtained in one place after initialization, that is, only one place can continue to execute until the obtained lock is released. Therefore, it needs to be read or modified for eachmessage_queueAll places need to be locked, that is, all in frontproducerandconsumer
    • MatchHandlerMediumadd_user()
    • MatchHandlerMediumremove_user()
    • consume_task()
  • message_queue.cv.wait(lck)andmessage_queue.cv.notify_all(): if message queuingmessage_queueIf it is empty, then we areconsume_taskFor the sake of efficiency, if you stay all the timeconsume_taskofwhileIn the cycle, it will occupy a lot of unnecessary resources. There is a proper noun calledBusy waiting。 So we can put this thread temporarilyHangI don’t have the resources I want now, so I’ll rest first and call (wake) me when I have the resources I need. In terms of operating system, it is implementedmessage_queue.cv.wait(lck)After the operation, this thread will be added to aWaiting queueOn, execute after the conditions are metmessage_queue.cv.notify_all()Wake it up because we have only one thread here that can happenBusy waitingPhenomenon, all we makemessage_queue.cv.notify_all()andmessage_queue.cv.notify_one()Are equivalent, will beconsume_task()MediumwhileThat Awakening

stayconsume_task()In,elseIn the branch, we take out the next one that can be processedtaskAfter, you canlckThe lock is unlocked because we have removed the team headtask, other threads can start accessing resources when they no longer need to access resources. So willlck.unlock()Put onmessage_queue.q.pop()Later, not laterpool.match()Then, just out of the optimization of efficiency, the results of the two are actually the same

PoolThe related operations of are relatively simple and will not be repeated