Head of head
在golang的整个生态里,redis client lib全部都使用多连接或者连接池。这是让人难以理解的,所以我和xiaofei一起写了一个同时支持同步和异步的redis client lib:RedisGo-Async。
github地址:https://github.com/gistao/RedisGo-Async。
qq群:131958277。
同步模式
A -> B
A <- B
A请求,并获取结果,经历1个RTT,这里称之为同步模式。
为了实现高QPS,需要M个AB来处理任务,这样可以得到一个公式:M/RTT。
异步模式
A -> -> -> B
A <- <- <- B
A请求,不等待应答继续请求,并获取全部结果,经历1个RTT,这里称之为异步模式。
为了实现高QPS,同样可以得到一个公式:C,C表示发包频率。
复杂度
异步模式一般使用回调,较同步方式复杂的多。但GoRedis-Async提供的两种模式的使用是一样的。
conn := pool.Get()
ret, err := conn.Do()
doSomething(ret)
当你想在两种模式下切换时,这些代码都不用更改。
值得注意的是pipelining,同步模式的使用如下
conn.Send(A)
conn.Send(B)
conn.Flush()
conn.Recive() // ret <-A
conn.Recive() // ret <-B
而异步模式是天然支持pipelining的,所以使用还是
conn.Do(A)
conn.Do(B)
有时你会希望同时向Redis serverA,B,C请求,最后一次性来处理结果。同步模式的使用如下
connA.Send()
connA.Flush()
connB.Send()
connB.Flush()
connC.Send()
connC.Flush()
connA.Recive()
connB.Recive()
connC.Recive()
异步模式使用如下
connB.AsyncDo()
retC := connC.AsyncDo()
retA.Get()
retC.Get()
retB.Get()
扩展性
同步模式:M/RTT
在GoRedis-Async里,M表示
Pool.MaxActive
这个值是固定的,取决于当前测试环境:
- 应用程序和Redis server的网络RTT
- 应用程序里的并发度
- Redis server的连接数上限
- 本机的连接数上限
- 本机的CPU核数。
举个关于RTT的例子:
测试环境的网络RTT为1ms,应用程序经测试达到QPS要求后,M被确定到代码中,然后上线到生产环境,而生产环境的RTT为10ms,那么生产环境的QPS就会比预期要低10倍。或者在生产环境里,由于目标Redis server发生故障而被切到了备机,此时备机和应用程序极有可能会跨机房,这也会带来同样的问题。
异步模式:C
在GoRedis-Async里,C只和应用程序的routine数量有关,上边所述的RTT问题在异步模式下并不存在。
当然,在特定范围内都可以被应用程序接受的话,同步和异步模式选择哪种都是适合的。
MM(Min cost Max payload)
基础库好坏的一个重要衡量标准就是MM。
同步模式从理论上来说,相比较异步模式需要更多的线程和连接资源。下边是RedisGo-Async、redigo、官方redisclient的对比基准测试报告。Y轴是耗时ms,X轴为各对比库,测试数据为1千万条。
有疑问加站长微信联系(非本文作者)