Java中使用KCP协议——性能测试及应用

上一篇文章简单介绍了KCP基本的机制和原理,以及github上的三种java版本。但是上次留了一个小小的坑,缺少了性能测试部分。
这几天通过写测试服务器和测试客户端,简单测了一下TCP和KCP分别在内网和外网的延迟。

一. Java版KCP

上一篇文章介绍的github的三种java版本,对比之下,我最终选择了 这个版本 进行测试。
不过当我测完之后,发现这个版本的实现有个小小的缺陷:虽然它底层也是Netty的实现版本,但是它的底层实在封装的太严实了,而对我们设计中以下两点不太友好:

  1. 由于我们打算根据C原版作者的建议使用TCP/KCP双通道共同运作,原TCPServer我们是基于Netty实现的,有一套相对完整的抽象接口,本来我打算把KCPServer也通过封装和抽象集成到这套接口里面,但这套库的对外接口全部是自己封装的,完全不涉及Netty,以致于两套Server完全是不一样的实现思路。解决办法就是把KCP的层级做的高一点,就不太好利用一些公共方法了,可能还需要再做一层抽象和封装。
  2. 之前实现TCPServer的时候,编解码操作是作为Handler添加到Netty的响应链中的,也就是说编解码操作都是在Netty的IO线程操作的,而这套库的所有对外接口都是以ByteBuf为单位的。这意味着我们需要自己在外部实现编解码,这就涉及到线程的使用。目前是以下解决方案:
    • 修改底层源码,改动可能较大
    • 自定义一套编解码处理线程,代替Netty的IO线程处理编解码的效果,相对改动较小
    • 换一个基于Netty实现的版本

很不幸,最终我换了一个基于Netty实现的版本,主要我们当前的Server接口比较融合,准备先接进来,和前端联调,同时做好抽象,随时准备切换回这一套版本,并使用上面的第二个解决方案。主要考虑到这个库是有线上项目经验验证

二、测试方式

如果大家有需要,我可以把测试代码上传到github

自定一个测试协议TestKcp

// KCP测试协议
message TestKcp {
 required int64 clientTime = 1;
 required int32 msgIndex = 2;
 optional string content = 3;
}

// KCP测试协议
message TestKcp_S2C {
 required int64 clientTime = 1;
 required int64 serverTime = 2;
 required int32 msgIndex = 3;
 optional string content = 4;
}

测试客户端逻辑

  1. 客户端启动并连接服务端
  2. 启动定时器,每100ms,Proto编码一个条Test消息,消息中content长度为M,发送N条
  3. 发送编码后的消息
  4. 收到服务端回复的SC消息
  5. Proto解码,统计延迟(客户端收到第N条消息的时候,关闭客户端,统计所有消息的来回延迟)

测试服务端逻辑

  1. 启动服务端并监听端口
  2. 收到客户端消息
  3. Proto解码消息
  4. Proto编码SC消息
  5. 回复SC消息

内网环境(同机房机器),外网环境(上海机器)分别部署TCP和KCP的服务端

三、测试结果

以下是通过我的测试逻辑跑出来的测试结果,结果是通过把测试数据交给python的matplotlib库所画出来(几行代码搞定,很简单

1. KCP测试

1.1 KCP:内网环境 VS 外网环境平均延迟对比

KCP平均延迟测试

1.2 KCP:内网环境 VS 外网环境最高延迟对比

KCP最高延迟测试

1.3 KCP:内网环境 VS 外网环境最低延迟对比

KCP最低延迟测试

2. TCP测试

2.1 TCP:内网环境 VS 外网环境平均延迟对比

TCP平均延迟测试

2.2 TCP:内网环境 VS 外网环境最高延迟对比

TCP最高延迟测试

2.3 TCP:内网环境 VS 外网环境最低延迟对比

TCP最低延迟测试

3. TCP VS KCP对比测试

3.1 TCP VS KCP:平均延迟对比

TCP VS KCP平均延迟测试

TCP VS KCP平均延迟测试

3.2 TCP VS KCP:最高延迟对比

TCP VS KCP最高延迟测试

TCP VS KCP最高延迟测试

3.3 TCP VS KCP:最低延迟对比

TCP VS KCP最低延迟测试

TCP VS KCP最低延迟测试

四、 测试结论

现象描述:

  • KCP的外网环境平均延迟稳定在30ms左右,内网环境平均延迟稳定在3ms左右
  • KCP的外网环境比内网环境延迟要高,但差距不大,和内网环境相比,平均延迟最高达到10倍(内网最高1ms,外网最高33ms,即使达到外网最高延迟,相对也是比较稳定的)
  • TCP的外网环境非常不稳定,最高达到3400ms,最高与内网相差几乎达到3400倍,而最低也有150ms
  • KCP和TCP对比,KCP内网,TCP内网,KCP外网,平均延迟都相对稳定,内网环境下,TCP大部分时间都优于KCP,一旦到了外网环境,KCP表现非常稳定,而TCP外网非常不稳定,平均延迟忽高忽低

结论

  • 较好的网络环境下(测试中的内网环境),TCP大部分时候都优于KCP
  • 较差的网络环境下(测试中的外网环境),KCP延迟稳定性远大于TCP
  • 国内部分网络防火墙可能丢弃UDP包
  • 建议战斗内(Lua层)同步协议使用KCP,战斗进入前其他协议继续使用TCP
  • 国内部分网络可能出现UDP不通的情况,因此建议同时保留TCP和KCP,指定一个通信策略,比如优先KCP,KCP不通的情况下,退回到TCP通信;或者根据网络状况,动态选择TCP还是KCP
  • KCP作者的使用建议(https://github.com/skywind3000/kcp/wiki/Cooperate-With-Tcp-Server

这个测试代码也可以用来测试并调整KCP参数

五、KCP实际应用

根据KCP的特性,以下是实际应用设计

1. TCP登录

  1. BattleLogin的时候,服务器创建KcpChannel对象,设置convId规则:index * 100000 + 6位随机数,绑定TCP和KCP的关系
/**
 * 生成kcp唯一id
 * FIXME 这个方法到INT最大值的临界点会有多线程问题,可能产生相同id,想想怎么优化
 * KCP convId规则:redis index * 100000
 */
public int generateKcpConvId() {
    long incrNum = RedisManager.getTemplate().incr(DBEnum.global_new_kcp_conv_Id, 0);
    if (incrNum >= KCP_CONV_REDIS_MAX_INDEX) {
        RedisManager.getTemplate().set(DBEnum.global_new_kcp_conv_Id, 0, "0");
    }
    // 生成6位随机数
    int random = MathUtils.intRand(kcpConvRandom, KCP_CONV_RANDOM_MIN, KCP_CONV_RANDOM_MAX);
    return (int) (incrNum * KCP_CONV_RANDOM_MIN + random);
}

  1. BattleLogin_S2C返回该convId

2.KCP登录

  1. 检测KCP传过来的uid的玩家是否有Human对象,有则该玩家完成了TCP登录
  2. 检测该玩家身上是否有KCP连接的convId,是否和当前KCP的convId是同一个id
  3. 检测TCP的IP和KCP的IP是否是同一个来源
  4. 服务器增加KCP通道管理

如果有必要,可以在消息包里再加个随机的token,防止udp包造假,或者在每条心跳消息都下发新的token

3.TCP/KCP网络切换

由于国内部分地区UDP无法击穿,因此需要TCP/UDP双线路设计。

网络切换.png

服务端同时提供TCP和KCP网络,并且可随时切换,把网络选择权交给客户端

  • 服务器启动时同时启动TCPServer和KCPServer,同时工作,同时处理消息
  • Human对象身上记录当前网络状态,默认TCP
  • TCP Server收到战斗消息(Lua层的状态同步消息),切换Human为TCP模式
  • KCP Server收到战斗消息(Lua层的状态同步消息),切换Human为KCP模式
  • Human提供统一push方法,其中读取当前网络方法:
protected NetMessageType getNetMessageType(Packet packet) {
    // 默认推送TCP网络
    NetMessageType netMessageType = NetMessageType.NET_TCP;
    int code = packet.getHeader();
    // 战斗消息
    IPSEnum ipsEnum = ThrudServerApplicationContext.getEnum(code);
    if (ipsEnum != null && ipsEnum.isBattleMsg()) {
        // 战斗消息,根据玩家当前网络通道推送
        netMessageType = this.battleMessageNet;
    }
    return netMessageType;
}

4.TCP/KCP Idle超时

  • 服务器TCP和KCP均设定超时30s,客户端需要定时发送心跳消息(BattleHeartBeat),发送TCP或KCP都可以
  • TCP和KCP均触发读超时后,服务器断开玩家所有连接,并踢下线

5.其他

不管任何情况下,TCP断开,服务器也会同时断开KCP(清除KCP连接信息),因此TCP走重连的时候,也需要同时走KCP的重连

个人微信公众号:Henry游戏开发
个人博客:https://hjcenry.com
CSDN:https://blog.csdn.net/hjcenry
简书:https://www.jianshu.com/u/7fa742801aeb

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,542评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,596评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,021评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,682评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,792评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,985评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,107评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,845评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,299评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,612评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,747评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,441评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,072评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,828评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,069评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,545评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,658评论 2 350

推荐阅读更多精彩内容