1 利用 thrift 创建客户端和服务端的接口 , 利用该接口来完成一个匹配系统。
thrift 是一种接口描述语言和二进制通讯协议, 它被用来定义和创建跨语言的服务。。。 被当做一个远程过程调用(RPC)框架来使用, 是有facebook为大规模跨语言服务而开发的
基础的数据类型 bool i32 i16, i8, i64, double, string, binary, map[HTML_REMOVED], list[HTML_REMOVED], set[HTML_REMOVED]
可以转化成任何类型的语言 namespace c1 tutorial等
结构体定义
struct work
{
1: i32 num = 0,
2: i32 num2,
3: string name,
}
functional 定义 i32 add(1: i32 num, 2: i32 num 2)
实现的框架有
游戏应用端 : 与匹配系统服务器的服务端交互
匹配系统服务端:
1. 服务端: 与游戏应用端的客户端交互
2, 客户端: 与数据存储服务器的服务端交互、
数据存储服务器(已经实现)
服务端:与匹配系统服务器的客户端交互
首先 创建一个matc。thrift 接口文件
匹配系统服务器的 服务端 用于实现游戏应用端与匹配服务器的交互的service;
对于一个用户 保留他的分数 名字, id , photo , openid 在服务端 每当添加用户的时候, 同时相匹配词中添加该用户,
删除的时候, 删除用户的信息, 同时在匹配池中将其删除。
匹配系统服务器的客户端 运行match.thrift接口文件在匹配系统服务器端生成C++ 源文件
原始的thrift生成文件语法: thrift -r –gen cpp ../../thrift/match.thrift
match _server 与游戏应用端进行交互, client_server 与数据存储服务器交互
编译 所有的。cpp文件生成 .o 文件 g -c main.cpp match_server/*.cpp(-c后面的都是文件名)
链接 所有的.o文件生成可执行文件 .exe g [文件1.o][文件2.o].....-o[需要额外添加动态库] lthrift的thrift动态库
g++ .o -o main -lthrift
运行可执行文件 ./main
git add .
git restore –stage.o #.o文件是编译文件, 不加入暂存区
git restore –stage main ## main是可执行文件, 不加入暂存区
git commit -m “add match server”
git push
游戏应用端的一个客户端
运行match.thrift 在游戏应用端生成python3源文件, 这里只有客户端
利用官方文档提供的模板编写客户端文件 client.py
在终端输入的信息的话需要from sys import stdin
将通信中的main函数可以改写成operator 函数, 每次需要的时候调用一次建立通信传递传递信息 目的是可以一直不断处理信息, 然后重写main函数, 使其不断地从终端读入信息
在 匹配系统服务器中给匹配系统开一个线程
运用到了 操作系统中的 pv 原语以及生产者和消费者模型
需要一个线程, 能够引入一个头文件 [HTML_REMOVED]
include[HTML_REMOVED] 互斥信号量 [HTML_REMOVED] 条件变量, 用于阻塞和唤醒 线程
include[HTML_REMOVED] 用于模拟消息队列 其中消息队列中的的元素有用户名 和 string type
消息队列结构体包含 消息队列本体, mutex互斥信号量 condition_mutex条件变量 , 用于阻塞唤醒线程 message_queue
匹配池的实现
可以向匹配池中加入用户, 删除用户(根据id来进行删除), 记录成功匹配的信息,
采用vector来保存匹配池中的用户
匹配机制 :
添加用户以及实现删除用户
这两个均是下面的过程
访问临界区的时候 unique_lock[HTML_REMOVED] lck(message_queue.m); 先上锁
message_queue.q.push({user, “add”}) 把新消息加入消息队列
message_queue.cv.notify_all(); 唤醒阻塞的线程
对于生产者-消费者模型 的线程
访问临界区的时候一定要先上锁, unique_loack[HTML_REMOVED] lck(mesaage_queue.m);
如果该队列为空就要阻塞进程 messsage_queue.cv.wait(lck)
否则的话:
取出消息队列的对头元素
临界区访问结束之后直接解锁, lck.unlock();避免后续没有用到临界区的信息, 而长时间占用锁
在进行后续的任务
之后调用线程运行 thread matching_thread(consumer_task); // 调用一个线程运行 consumer_task
数据存储器与服务器交互
数据存储器服务的服务端
主要存储 用户名以及密码采用MD5sum加密后的前8 为MD5哈希函数可以获得服务器密码的哈希值
用户名密码验证成功之后会返回0, 验证失败后会返回1
验证成功后结构将会保存到云服务器中
i32 save_data (1:string username,2: stringpassword, 3: i32 player1_id, 4: i32 player2_id);
匹配系统客户端中, 记录用户匹配的信息
重写save_result(int a, int b) ; 记录匹配结果,将数据存储到数据存储服务器中
之后运行的时候,只运行save_client即可
g -c main.cpp save_client/*.cpp
g *.o -o main -lthrift -pthread
./main
可以在游戏应用端, 中输入用户信息(编号, 名字, 分数) 并添加到匹配系统服务器中, 进行匹配, 可以在数据存储服务器中查看存储的数据
该匹配系统有一个消息队列 + 生产者——消费者模型 + 匹配池完成
对于匹配 每次匹配分差小于50的用户
可以考虑重写main.cpp文件中的部分功能
当消息队列为空的时候, 不再是阻塞直到唤醒为止, 可以让他每次经过一秒就进行一次match()调用, 可能会出现匹配池中有用户等待, 而消息队列此时仍然为空
若采用先前的策略, 可能会导致进程卡死
当队列为空的时候, 每一秒都要进行一次匹配,做法是对临界资源进行解锁, 调用match()
对队列中的用户通过分数进行排序, 然后匹配
调用官方模板文件启动多线程
可以用一个额外的数组记录每个用户的等待时间, 在消息队列为空的时候, 线程会每一秒调用一次match函数,
然后每次调用match函数,会首先对匹配池中的所有用户的wt值自增1, 从而实现用wt记录每个用户的等待时间
然后每一个单位的wt会扩大50分的匹配域, 从而达到模拟题意的功能。
开一个负责匹配的进程, 进程与服务器进程之间通过thrift实现通信,
前端向服务端请求, 服务端会向匹配端请求,