游戏终于上线啦,加班结束,来总结一下前一段时间的所学吧。先从上线前的压力测试说起。

1 网络相关优化

1.1 背景

单位的网络库使用的时候,网络和逻辑处理是两个线程。

网络线程负责:接收数据, 解析出protobuf结构,放入网络队列。

逻辑线程负责:从网络队列取出protobuf,处理。

1.2 思考:网络线程是否有意义?

如果网络线程处理完直接处理,其实也是可以的。但是分离网络线程会避免当逻辑线程处理过慢,导致socket接收缓冲区满了,客户端发送数据失败。不过逻辑线程处理太慢也会导致客户端发出请求没有回应。

1.3 优化:逻辑线程,每次处理网络队列的数量应该有个上限

逻辑线程的主循环伪代码如下:

while(1)
{
    // 处理网络队列里的请求
    while(!pack.empty())
    {
        Msg *p = pack.pop();
        process(p);
    }
    // 一些其他的循环,例如发送ping包,每隔5分钟发送体力,libevent库的循环。。。
    check_ping();
    check_present_energy();
}

压力测试时,因为逻辑线程处理的速度比网络线程慢,所以逻辑线程会一直卡在处理网路请求的循环中,因为pack永远不为空,修改方案是在while循环的条件加上一个每次处理包的个数上限,这样其他循环也会有机会执行。

发现这个问题是因为测试时,服务器的状态数据是用libevent库写的一个http接口获取的,但是测试时发现调用这个接口一直超时,后来发现时因为libevent的循环一直没有执行。

2 mysql数据库相关优化

每个玩家在数据库的角色表是一行数据,每次玩家下线时的保存数据的sql语句是这个样子的:

updata role_table set val1=xxx,val2=xxx,val3=xxx ….. where roleid=yyy;

会更新所有字段无论是否有修改,有时玩家仅仅修改了几个字段却需要更新几十个字段。

然后做了优化,把玩家的信息在上线时保存了一个备份,下线保存时会比较只把修改的字段拼到update语句中,因为使用的protobuf支持反射,所以可以写一个通用接口,不必担心字段变化。

3 redis相关优化

游戏中使用了redis存储了一些数据,因为用的时redis的同步api,每次使用相当于向redis server发送一个请求,然后“原地”等候redis的返回,这里开销还是很大的。

优化时时采取了redis支持的lua脚本,将一些简单逻辑让redis server来执行,来减少和redis server的网路交互次数。