2021年1月30日星期六

面试阿里被质问:ConcurrentHashMap线程安全吗

没啥深入实践的理论系同学,在使用并发工具时,总是认为把HashMap改为ConcurrentHashMap,就完美解决并发了呀。或者使用写时复制的CopyOnWriteArrayList,性能更佳呀!技术言论虽然自由,但面对魔鬼面试官时,我们更在乎的是这些真的正确吗?2021Java面试宝典

1 线程重用导致用户信息错乱

生产环境中,有时获取到的用户信息是别人的。查看代码后,发现是使用了ThreadLocal缓存获取到的用户信息。

ThreadLocal适用于变量在线程间隔离,而在方法或类间共享的场景。
若用户信息的获取比较昂贵(比如从DB查询),则在ThreadLocal中缓存比较合适。
问题来了,为什么有时会出现用户信息错乱?

1.1 案例

使用ThreadLocal存放一个Integer值,代表需要在线程中保存的用户信息,初始null。
先从ThreadLocal获取一次值,然后把外部传入的参数设置到ThreadLocal中,模拟从当前上下文获取用户信息,随后再获取一次值,最后输出两次获得的值和线程名称。
图片
固定思维认为,在设置用户信息前第一次获取的值始终是null,但要清楚程序运行在Tomcat,执行程序的线程是Tomcat的工作线程,其基于线程池。
而线程池会重用固定线程,一旦线程重用,那么很可能首次从ThreadLocal获取的值是之前其他用户的请求遗留的值。这时,ThreadLocal中的用户信息就是其他用户的信息。

1.2 bug 重现

在配置文件设置Tomcat参数-工作线程池最大线程数设为1,这样始终是同一线程在处理请求:

server.tomcat.max-threads=1

先让用户1请求接口,第一、第二次获取到用户ID分别是null和1,符合预期
图片

用户2请求接口,bug复现!第一、第二次获取到用户ID分别是1和2,显然第一次获取到了用户1的信息,因为Tomcat线程池重用了线程。两次请求线程都是同一线程:http-nio-45678-exec-1
图片

写业务代码时,首先要理解代码会跑在什么线程上:

  • Tomcat服务器下跑的业务代码,本就运行在一个多线程环境(否则接口也不可能支持这么高的并发),并不能认为没有显式开启多线程就不会有线程安全问题

  • 线程创建较昂贵,所以Web服务器会使用线程池处理请求,线程会被重用。使用类似ThreadLocal工具存放数据时,需注意在代码运行完后,显式清空设置的数据。

1.3 解决方案

在finally代码块显式清除ThreadLocal中数据。即使新请求过来,使用了之前的线程,也不会获取到错误的用户信息。
修正后代码:
图片

ThreadLocal利用独占资源的解决线程安全问题,若就是要资源在线程间共享怎么办?就需要用到线程安全的容器。
使用了线程安全的并发工具,并不代表解决了所有线程安全问题。

1.4 ThreadLocalRandom 可将其实例设置到静态变量,在多线程下重用吗?

current()的时候初始化一个初始化种子到线程,每次nextseed再使用之前的种子生成新的种子:

UNSAFE.putLong(t = Thread.currentThread(), SEED,r = UNSAFE.getLong(t, SEED) + GAMMA);

如果你通过主线程调用一次current生成一个ThreadLocalRandom实例保存,那么其它线程来获取种子的时候必然取不到初始种子,必须是每一个线程自己用的时候初始化一个种子到线程。
可以在nextSeed设置一个断点看看:

UNSAFE.getLong(Thread.currentThread(),SEED);

2 ConcurrentHashMap真的安全吗?

我们都知道ConcurrentHashMap是个线程安全的哈希表容器,但它仅保证提供的原子性读写操作线程安全。

2.1 案例

有个含900个元素的Map,现在再补充100个元素进去,这个补充操作由10个线程并发进行。

开发人员误以为使用ConcurrentHashMap就不会有线程安全问题,于是不加思索地写出了下面的代码:在每一个线程的代码逻辑中先通过size方法拿到当前元素数量,计算ConcurrentHashMap目前还需要补充多少元素,并在日志中输出了这个值,然后通过putAll方法把缺少的元素添加进去。

为方便观察问题,我们输出了这个Map一开始和最后的元素个数。
图片

访问接口
图片

分析日志输出可得:

  • 初始大小900符合预期,还需填充100个元素

  • worker13线程查询到当前需要填充的元素为49,还不是100的倍数

  • 最后HashMap的总项目数是1549,也不符合填充满1000的预期

2.2 bug 分析

ConcurrentHashMap就像是一个大篮子,现在这个篮子里有900个桔子,我们期望把这个篮子装满1000个桔子,也就是再装100个桔子。有10个工人来干这件事儿,大家先后到岗后会计算还需要补多少个桔子进去,最后把桔子装入篮子。

ConcurrentHashMap这篮子本身,可以确保多个工人在装东西进去时,不会相互影响干扰,但无法确保工人A看到还需要装100个桔子但是还未装时,工人B就看不到篮子中的桔子数量。你往这个篮子装100个桔子的操作不是原子性的,在别人看来可能会有一个瞬间篮子里有964个桔子,还需要补36个桔子。

ConcurrentHashMap对外提供能力的限制:

  • 使用不代表对其的多个操作之间的状态一致,是没有其他线程在操作它的。如果需要确保需要手动加锁

  • 诸如size、isEmpty和containsValue等聚合方法,在并发下可能会反映ConcurrentHashMap的中间状态。因此在并发情况下,这些方法的返回值只能用作参考,而不能用于流程控制。显然,利用size方法计算差异值,是一个流程控制

  • 诸如putAll这样的聚合方法也不能确保原子性,在putAll的过程中去获取数据可能会获取到部分数据

2.3 解决方案

整段逻辑加锁:
图片

只有一个线程查询到需补100个元素,其他9个线程查询到无需补,最后Map大小1000

图片

既然使用ConcurrentHashMap还要全程加锁,还不如使用HashMap呢?
不完全是这样。

ConcurrentHashMap提供了一些原子性的简单复合逻辑方法,用好这些方法就可以发挥其威力。这就引申出代码中常见的另一个问题:在使用一些类库提供的高级工具类时,开发人员可能还是按照旧的方式去使用这些新类,因为没有使用其真实特性,所以无法发挥其威力。

3 知己知彼,百战百胜

3.1 案例

使用Map来统计Key出现次数的场景。

  • 使用ConcurrentHashMap来统计,Key的范围是10

  • 使用最多10个并发,循环操作1000万次,每次操作累加随机的Key

  • 如果Key不存在的话,首次设置值为1。

show me code:
图片

有了上节经验,我们这直接锁住Map,再做

  • 判断

  • 读取现在的累计值

  • +1

  • 保存累加后值

这段代码在功能上的确毫无没有问题,但却无法充分发挥ConcurrentHashMap的性能,优化后:
图片

ConcurrentHashMap的原子性方法computeIfAbsent做复合逻辑操作,判断K是否存在V,若不存在,则把Lambda运行后结果存入Map作为V,即新创建一个LongAdder对象,最后返回V

因为computeIfAbsent返回的V是LongAdder,是个线程安全的累加器,可直接调用其increment累加。

这样在确保线程安全的情况下达到极致性能,且代码行数骤减。

3.2 性能测试

使用StopWatch测试两段代码的性能,最后的断言判断Map中元素的个数及所有V的和是否符合预期来校验代码正确性
图片

性能测试结果:

图片

比使用锁性能提升至少5倍。

3.3 computeIfAbsent高性能之道

Java的Unsafe实现的CAS。
它在JVM层确保写入数据的原子性,比加锁效率高:

static final <K,V> boolean casTabAt(Node<K,V>[] tab, int i,         Node<K,V> c, Node<K,V> v) { return U.compareAndSetObject(tab, ((long)i << ASHIFT) + ABASE, c, v);}

所以不要以为只要用了ConcurrentHashMap并发工具就是高性能的高并发程序。

辨明 computeIfAbsent、putIfAbsent

  • 当Key存在的时候,如果Value获取比较昂贵的话,putIfAbsent就白白浪费时间在获取这个昂贵的Value上(这个点特别注意)

  • Key不存在的时候,putIfAbsent返回null,小心空指针,而computeIfAbsent返回计算后的值

  • 当Key不存在的时候,putIfAbsent允许put null进去,而computeIfAbsent不能,之后进行containsKey查询是有区别的(当然了,此条针对HashMap,ConcurrentHashMap不允许put null value进去)

3.4 CopyOnWriteArrayList 之殇

再比如一段简单的非 DB操作的业务逻辑,时间消耗却超出预期时间,在修改数据时操作本地缓存比回写DB慢许多。原来是有人使用了CopyOnWriteArrayList缓存大量数据,而该业务场景下数据变化又很频繁。

CopyOnWriteArrayList虽然是一个线程安全版的ArrayList,但其每次修改数据时都会复制一份数据出来,所以只适用读多写少或无锁读场景。

所以一旦使用CopyOnWriteArrayList,一定是因为场景适宜而非炫技。

CopyOnWriteArrayList V.S 普通加锁ArrayList读写性能

测试并发写性能

图片

测试结果:高并发写,CopyOnWriteArray比同步ArrayList慢百倍

图片

测试并发读性能

图片

测试结果:高并发读(100万次get操作),CopyOnWriteArray比同步ArrayList快24倍

图片

高并发写时,CopyOnWriteArrayList为何这么慢呢?因为其每次add时,都用Arrays.copyOf创建新数组,频繁add时内存申请释放性能消耗大。

4 总结

4.1 Don't !!!

  • 不要只会用并发工具,而不熟悉线程原理

  • 不要觉得用了并发工具,就怎么都线程安全

  • 不熟悉并发工具的优化本质,就难以发挥其真正性能

  • 不要不结合当前业务场景,就随意选用并发工具,可能导致系统性能更差

  • 2021Java面试宝典

4.2 Do !!!

  • 认真阅读官方文档,理解并发工具适用场景及其各API的用法,并自行测试验证,最后再使用

  • 并发bug本就不易复现, 多自行进行性能压力测试


 









原文转载:http://www.shaoqun.com/a/521206.html

跨境电商:https://www.ikjzd.com/

卖家精灵:https://www.ikjzd.com/w/532

wangwei:https://www.ikjzd.com/w/1744


没啥深入实践的理论系同学,在使用并发工具时,总是认为把HashMap改为ConcurrentHashMap,就完美解决并发了呀。或者使用写时复制的CopyOnWriteArrayList,性能更佳呀!技术言论虽然自由,但面对魔鬼面试官时,我们更在乎的是这些真的正确吗?2021Java面试宝典1线程重用导致用户信息错乱生产环境中,有时获取到的用户信息是别人的。查看代码后,发现是使用了ThreadLo
1淘网:1淘网
声网agora:声网agora
Bol.com:Bol.com
有做电商的,小心这群职业打假人!:有做电商的,小心这群职业打假人!
Amy跨境:墨西哥市场的电商发展机会:Amy跨境:墨西哥市场的电商发展机会

147分+9人上双!篮网刷爆纪录,队史37年神迹,对手主帅被打服_比赛

原标题:147分+9人上双!篮网刷爆纪录,队史37年神迹,对手主帅被打服

今天NBA常规赛结束了一场焦点战,篮网147-125大胜雷霆,迎来4连胜。这场比赛杜兰特缺阵,布朗进入到首发,欧文和哈登带队出战。篮网和雷霆近期都处于连胜中,势头不错,不过整体实力上篮网还是要高出不少。比赛中篮网在哈登的带领下早早取得领先,纳什的球队上半场就轰下76分,领先雷霆17分,下半场篮网保持优势,成功拿下了胜利,近期豪取4连胜。

这场比赛篮网也创造了很多的纪录,其中包括:

篮网9人得分上双,球队37年来第一次,也是队史上第二次。上一次还要追溯到1984年4月8号对阵骑士,篮网也是9人得分上双。

147分的分数平队史不加时比赛得分纪录。

这也是哈登和欧文双核带队第一次赢球,此前杜兰特有过轮休,可惜"欧哈组合"带队输球。

哈登全场14投9中得到25分,11助攻,10篮板,三双数据,这也是哈登个人本赛季的第3次,生涯第49次三双,排名NBA历史第8位。

比赛结束之后,雷霆主帅马克-戴格诺很显然被打服了,他盛赞篮网的进攻:"必须称赞他们(篮网)一番,他们的进攻很优秀,我们的防守做得不到位。"这场比赛篮网单节得分都在30分以上。

不得不说,今天的比赛篮网确实火力全开,这还是在没有核心之一杜兰特的情况下取得的成就,确实是非常难得。现在的篮网已经升至东部第二名,也期待接下来他们的表现,争取三巨头磨合的更好,全力冲击总冠军。

返回搜狐,查看更多

责任编辑:

原文转载:http://sport.shaoqun.com/a/389628.html

跨境电商:https://www.ikjzd.com/

欧苏丹:https://www.ikjzd.com/w/1756

派代:https://www.ikjzd.com/w/2197


原标题:147分+9人上双!篮网刷爆纪录,队史37年神迹,对手主帅被打服今天NBA常规赛结束了一场焦点战,篮网147-125大胜雷霆,迎来4连胜。这场比赛杜兰特缺阵,布朗进入到首发,欧文和哈登带队出战。篮网和雷霆近期都处于连胜中,势头不错,不过整体实力上篮网还是要高出不少。比赛中篮网在哈登的带领下早早取得领先,纳什的球队上半场就轰下76分,领先雷霆17分,下半场篮网保持优势,成功拿下了胜利,近期豪
杨颜:杨颜
isbn:isbn
欧盟对亚马逊展开初期调查,亚马逊是否滥用第三方卖家销售数据?:欧盟对亚马逊展开初期调查,亚马逊是否滥用第三方卖家销售数据?
提高亚马逊销量的8点建议:提高亚马逊销量的8点建议
兄弟连2018跨境电商年会盛典:兄弟连2018跨境电商年会盛典

浓烟之下的流行病:绝大多数人忽略烟雾也能带来传染病|流行病|烟雾

  来源:乐天行动派 

  烟雾吸入对肺部有害,这已不是什么秘密。但现在,科学家们认为,烟雾可能还会携带、传播传染病。

  一项研究发现,野火产生的烟雾中弥漫着成千上万种微生物,其中一些已经被知晓会引发疾病,例如细菌和真菌孢子。《科学》杂志上近期就有一篇报告指出,"烟雾可能会携带、传播传染病"。

烟雾的组成 / EnviroKlenz Air Purifier烟雾的组成 / EnviroKlenz Air Purifier

  迄今为止,人们普遍认为,烟雾对人体健康的最大威胁是燃烧木材等材料产生的微小颗粒,其中一些颗粒微小到足以被人类吸入肺部,这会引发一些人产生过敏反应,加剧一些人的肺部疾病,比如哮喘。

  但最近的研究认为,当一场野火在燃烧植物或动物甚至破坏土壤时,成千上万种原本不易在空气中传播的细菌和真菌就会被暴露出来。你可能觉得火的高温会杀死这些生物,但还有一些细菌甚至会在火情过后大量繁殖。

  科学家们表示,因为这些细菌或真菌会吸附在烟雾颗粒上穿行千里、跨越大洲,所以具备传播传染病的可能性。

  虽然烟雾传播的微生物是否可能会导致人类感染还有待进一步证实,但一部分科学家认为这种可能性是相当大的。因为在某些地区,真菌相关疾病的发病率增长与山火烟雾的增加呈现正相关。而美国疾病预防控制中心还提示说,消防员罹患谷热的风险更高,而谷热(Valley Fever)学名为球孢子菌病,指的是人因为吸入土壤中的孢子而出现发烧、皮疹等症状。

澳大利亚的山火,图片来自wiki澳大利亚的山火,图片来自wiki

  美国爱达荷大学野外火灾科学专业副教授莱达·科布齐尔表示:"我认为,过去没有建立(烟雾与传染性微生物)两者之间联系是因为把烟作为含活体成分的物质来考量是一个非常新的想法。"

  长时间处于烟雾中是否会引起感染?烟雾是否可以预测疾病的传播?科布齐尔希望医生和科学家将在这些领域进行更多的研究。

  洛杉矶西达赛奈医疗中心(Cedars-Sinai Medical Center)肺部和重症监护医学科主任皮特·陈医生对这一理论"很感兴趣",但他对烟雾中的微生物会引起感染表示些许怀疑。陈医生表示,许多细菌和真菌都不会引起肺部感染,但当细菌和真菌达到一定剂量,肯定会加剧本就患有肺部疾病的病人的症状。

  陈医生补充说:"我一直以为是烟雾中的微粒引起了疾病,但现在我开始思考这些微生物会不会也可能导致肺部疾病加重了呢?"

  加州大学圣塔芭芭拉分校环境与发展经济学副教授安利捷(Abdul Latif Jameel)扶贫行动研究室气候小组的联合主席凯尔西·杰克说,无论烟雾中的微生物真的会引起感染还是只是会加剧潜在的呼吸系统疾病,这提出了一种新的健康威胁,这"肯定令人震惊"。

  杰克表示,这对低收入人群而言尤其骇人听闻,因为经济状况较差的人往往更容易受到环境的影响。如果烟雾影响了某个特定区域的空气质量,那么,相比开车去办公室的人,在户外工作或不得不步行、骑自行车通勤的人会吸入更多的烟雾。

  此外,发展中国家的空气污染更趋严重,这意味着这些人群的呼吸健康基准线相对较低。肺炎是5岁以下儿童死亡的首要原因,而呼吸系统疾病和肺部感染也是贫穷国家成年人死亡的主要原因之一。

在一些发展中国家,肺炎是5岁以下儿童死亡的首要原因 / The Logical Indian在一些发展中国家,肺炎是5岁以下儿童死亡的首要原因 / The Logical Indian

  杰克表示,发展中国家其他常见来源,如室内烧火烹饪或农民在丰收后放火焚烧秸秆产生的烟雾,是否会与野火燃烧产生一样多的传染性微生物呢?如果是这样,这是否会导致这些人群生病呢?

  杰克补充说:"在某种程度上,这一领域是污染物排放与健康影响关系认知的缺失部分,研究这些是非常有价值的。"

  而陈医生则表示,在完成更多研究之前,人们尽最大可能能够做到的事情就是在空气质量差的情况下遵循现有的建议,如呆在室内、保持门窗关闭、使用高效客气过滤器和运行空调。

  因为在世界各地,气候变化将使雾霾天成为"季节性常规而不是罕见现象。

  科布齐尔表示:"鉴于我们将长期与烟雾共存这一事实,我们只想鼓励人们采取预防措施。"

原文转载:http://tech.shaoqun.com/a/317677.html

跨境电商:https://www.ikjzd.com/

邮乐:https://www.ikjzd.com/w/1776

笨鸟海淘:https://www.ikjzd.com/w/1550


来源:乐天行动派  烟雾吸入对肺部有害,这已不是什么秘密。但现在,科学家们认为,烟雾可能还会携带、传播传染病。  一项研究发现,野火产生的烟雾中弥漫着成千上万种微生物,其中一些已经被知晓会引发疾病,例如细菌和真菌孢子。《科学》杂志上近期就有一篇报告指出,"烟雾可能会携带、传播传染病"。烟雾的组成/EnviroKlenzAirPurifier  迄今为止,人们普遍认为,烟雾对人体健康的最大威胁是燃烧
1号团:1号团
farfetch:farfetch
排雷!广告投放的这些坑,你都踩过吗?:排雷!广告投放的这些坑,你都踩过吗?
每周精选:美国各州可强征销售税,亚马逊推出全球收款服务!:每周精选:美国各州可强征销售税,亚马逊推出全球收款服务!
爆舱缺箱!舱位被抢订一空?天价运费之下,船公司利润翻5倍,单月盈利超十年总和…:爆舱缺箱!舱位被抢订一空?天价运费之下,船公司利润翻5倍,单月盈利超十年总和…

熬夜后皮肤暗黄怎么办?要怎么拯救我的脸

核心提示:现在人的生活方式快,无形中已经形成了一种晚睡晚起的生活习惯。有的时候因为加班又不得不熬夜。

现在人的生活方式快,无形中已经形成了一种晚睡晚起的生活习惯。有的时候因为加班又不得不熬夜。


一旦熬夜,很多人的脸上就会出现暗黄的情况,要怎么拯救呢?

1、保持充足的睡眠。

如果发现肤色暗黄,要及时调整自己的作息时间。

晚上11点之前一定要睡觉,因为晚上11点到凌晨1点是一天中阴气最重之时,主静,应静卧养生。而且这一时间人体内部自我调节功能开始活跃,会将人在白天残留体内的各种黑色素排出体外,这个时间如果休息不好,黑色素排解不出,皮肤就会出现暗黄的情况。所以,想要恢复白嫩皮肤,就要保持充足的睡眠。

2、饮食规律

如果实在不能早睡,也要在其他方面多多注意,特别是饮食上,多吃清淡食物,少吃辛辣油腻食物。还可以多吃一些养生食物,比如葛根,葛根有美白、平皱、改善面部肌肤、增加面部弹性、使面部红润有活力、疮痘减少消失等诸多功效。

3、及时补救


一定要注意第二天的皮肤补救,首先将面部清洁干净,然后在敷张面膜,最后在做好皮肤的保湿工作,这样可以将熬夜对皮肤的伤害降到最低,避免暗黄、色斑、痘痘等的出现。

4、多喝水

因为夜晚睡眠时间是皮肤作为表层器官更新代谢的好时间,如果在这个时间段熬夜就会使皮肤的废物和毒素排泄不出去,就容易在毛孔中堵塞,形成痘痘、痤疮等一系列皮肤的问题。想要促进皮肤的新陈代谢,加快毒素的排出,多补充一些水分能够使身体的新陈代谢功能更加的好,也能提高人的血液循环,使体内的废物得以排出体外。


原文转载:http://lady.shaoqun.com/a/265117.html

跨境电商:https://www.ikjzd.com/

wangwei:https://www.ikjzd.com/w/1744

乐一番:https://www.ikjzd.com/w/1562


核心提示:现在人的生活方式快,无形中已经形成了一种晚睡晚起的生活习惯。有的时候因为加班又不得不熬夜。 现在人的生活方式快,无形中已经形成了一种晚睡晚起的生活习惯。有的时候因为加班又不得不熬夜。一旦熬夜,很多人的脸上就会出现暗黄的情况,要怎么拯救呢?1、保持充足的睡眠。如果发现肤色暗黄,要及时调整自己的作息时间。晚上11点之前一定要睡觉,因为晚上11点到凌晨1点是一天中阴气最重之时,主静,应静卧养生
DMM:DMM
米兰网:米兰网
卖家注意!亚马逊印度已停用"search-seeding"工具!:卖家注意!亚马逊印度已停用"search-seeding"工具!
亚马逊正式宣布白色星期五将于11月24日开始:亚马逊正式宣布白色星期五将于11月24日开始
香港律师公证的那些事!:香港律师公证的那些事!

2021年1月29日星期五

《王者荣耀》背后的数据秘密

从找不到需求险些被叫停,到支撑亿级DAU的数据库行业标杆,这款支撑了《王者荣耀》多个系统的腾讯云数据库TcaplusDB在风雨中走过了整整10年。辉映日月破风浪,十年一剑破九天。百万行代码就像淙淙流淌的数据溪流,终于在十年后汇成不可逾越的护城河。

出发

2010年前后,QQ空间很火,带动了基于SNS互动页游(WebSNS)的火爆,腾讯内部开始考虑怎么做页游。也开始建设页游基础技术体系,其中最重要的产出是研发自己的分布式数据库TcaplusDB。与MMOG游戏不同,通常WebSNS游戏是全区全服的,数据集中存储;而其游戏逻辑服务器是对等的,web客户端通过短连接与服务器进行通信,也就是说玩家游戏过程,后端交互的逻辑服务器随时会变动;这些特性导致在逻辑层不方便对用户数据做缓存。

此外,WebSNS游戏基于玩家之间社会关系的互动场景非常多,玩家的一个互动操作,需要读取和修改与此关联的其他多个玩家的数据;这意味着web游戏中数据读写访问频率高于同时在线数,其访问频率实际上是在线数的N倍;假如某个100万在线的web游戏,平均每个玩家有50个好友,如果同时有1%的玩家触发一个好友动作,则可能会触发50万次数据访问。也就是说,基于这样的场景,web游戏的数据库层需要支持比传统MMOG游戏大的多的访问频率,如果仍然采用传统的数据库管理系统(如MySQL),要支持这么大的访问频率,其代价非常大。

因此,在游戏逻辑层和传统数据库管理系统之间架设高速的缓存系统,对web游戏来说至关重要。

当时业界已经有比较多的提供高速数据访问的NoSQL产品,不可否认,这些NoSQL产品在一定场景下使用是很优秀的,但在部分场景或需求下存在不支持或不够出色的情况——比如全内存带来的成本问题或对异步数据读写支持不够等。

2011年起,腾讯内部开始着手研发一款自己的分布式游戏数据库系统——TcaplusDB。这是个很美好的愿景。但从零开始自己研发一款数据库又谈何容易?2009 年开始,大量新的NoSQL数据库涌现,在整个行业掀起了一场NoSQL 革命,如今赫赫有名的 Redis、MongoDB 皆诞生于那一年。研发TcaplusDB是高楼万丈平地起的事情,我们一边调研友商的产品,一边抓住游戏行业特定的场景和需求,做最小化的产品验证。经过不断的验证,第一个版本在2012年初终于出炉。

时机

2012年4月,TcaplusDB首次正式提供服务,支持腾讯自研的页游《夜店之王》。《夜店之王》是腾讯自主研发的一款以时尚夜店为题材,以夜店经营为核心,融入吸血鬼元素的模拟经营类社交游戏。

当时的游戏,大部分是"分区分服"的形式,每个区域内的玩家自己玩。但基于社交场景的WebSNS需要所有的好友在一起玩。《夜店之王》就是这样典型的场景,TcaplusDB的高性能和低成本赢得了项目组的青睐。由于采用了包括 TcaplusDB 数据库在内的新技术,《夜店之王》通过"全区全服",让玩家在一个池子里"大乱斗",并通过实时派对邀请好友体验。《夜店之王》通过QQgame游戏大厅、空间、游戏人生、朋友等诸多渠道进入,取得了相当不错的成绩。

发展过程中,挫折并不是没有,2013年9月腾讯移动游戏《天天酷跑》火爆公测,开启QQ、微信双平台登陆,随时与QQ、微信好友一起玩。上线后,在不到一天的时间内就迅速登上了苹果App Store畅销榜第一位。

当初公测的时候,内部霸气地给《天天酷跑》配了100台服务器,对于一款新游戏,这显然是一个很充裕的配置。然而谁都没想到《天天酷跑》会这么火爆,准备的100台服务器很快就要高容量了。最后,在多部门的协同下,扩容的50台服务器快上架。硬件上架、网络打通、操作系统安装妥当,TcaplusDB的系统软件也很快部署完成。

但接下来就要通过部分数据的搬迁来实现数据库承载能力的扩容。按照正常逻辑,哪怕是停服维护,通常的扩容都有一个提前准备的过程,这次来得实在太快了,眼看旧数据库在逼近存储极限,那边不断涌入的玩家还在不断地制造新数据,搬迁的速度必须足够快,才能避免数据库爆仓。当时在业内深耕多年的大牛苦笑着说:"按照当前的情况,24小时后数据库就会崩溃,游戏只能暂时停服。"

谁都不敢面对这样的结局。

24小时很短,也能做很多事情,一个字,干!

于是,我们和当时酷跑的业务团队一起挺身而出,背水一战,连夜调试搬迁工具,在凌晨紧急上线,硬是抗住了一波又一波海量的用户访问冲击。有了这次的经历,我们下定决心,我们要做得更好。之后的几个月,我们都在重复一件事——一行一行地码代码,看得最多的是星夜、喝得最多的是咖啡,终于把这套数据库的自动扩容系统打磨得稳定高效。

成长

2014年2月,TcaplusDB已经深度优化了存储引擎,推出全托管的分布式存储服务。

但还不够,远远不够。

还记得上面说的《夜店之王》吗?经过2年多的发展,此时的《夜店之王》已经是当时排名第一的经营类社交游戏。而《夜店之王》使用的却还是TcaplusDB的老版本。TcaplusDB的全托管版本,性能得到了较大的提升,在服务上给项目组带来了较大的便利性。因此,将《夜店之王》TcaplusDB切换到全托管版本被提升日程。

由于《夜店之王》是线上业务,且此次切换TcaplusDB不能停服,还得解决新老协议不同、落地数据格式不同、数据分布不同等问题。对于任何一个团队,都是一个很大的挑战。

最终,在跟《夜店之王》团队的联合攻坚之下,我们通过数据双写、增加协议转换层、数据一致性校验工具等工作。最终完美实施了服务的迁移,在这个时期积累的数据迁移方案,也为后续TcaplusDB在无损水平扩展、无损数据迁移能力的产品化打下了坚实的基础。

2015年年底《王者荣耀》正式公测。这款游戏的火爆程度又一次突破了大家的想象力,这个游戏就像一个永远不能满足的巨兽,势不可挡地吞噬着各种后台资源。在打破各种游戏记录的同时,这个逆天的游戏每一秒钟都在创造数据库的新纪录。最疯狂的游戏浪潮,一般出现在周末。回想那个时候,一到周末,我们就在家盯着电脑,看着各种数字疯狂飙升。前期的技术积累终于派上了用场,严阵以待的技术人也终究见证了历史。几千台服务器在预先设定好的统一调度机制下不停服自动扩容,完美扛过流量高峰。同年,TcaplusDB又相继推出故障自动恢复、不停服升级、不停服扩缩容功能,成功支持了《天天爱消除》、《全民飞机大战》、《全民突击》、《CF手游》和《火影忍者手游》等游戏。

2016年,TcaplusDB陆续推出细粒度备份回档、软硬件升级、机房裁撤搬迁、软硬件故障对业务无损等核心能力。同时优化单业务海量数据访问能力,提供稳健高性能的数据存储服务。

2017年元宵晚会,在春晚小品《回家》中,主持人提到了《王者荣耀》,这让团队既紧张又兴奋。接着,又是一路不断飙升的QPS陪伴着我们度过一个不眠之夜,好在,最后又是有惊无险。

同年,TcaplusDB支持protobuf数据格式定义及访问,协议更加开放,兼容行业使用习惯。

2018年,TcaplusDB将高可用、无损扩缩容做到极致,深度优化性能降低成本,提升Api多语言、多平台、多模式、易使用的能力,陆续为《绝地求生-刺激战场》、《绝地求生-全军出击》、《QQ飞车》、《无限法则》等游戏提供高品质数据存储服务。

开放

2019年凭借多年的积累和在游戏分布式系统情景中适配能力,TcaplusDB做为腾讯完全自研的NoSQL数据库,正式成为腾讯云Tcaplus,通过腾讯云对外提供服务。结合了腾讯云的优势后,腾讯云Tcaplus又在多租户、安全性、开放API和多语言SDK等方面有了极大的提升。

2020年NEXON、上海盛趣等游戏厂商开始选择腾讯云TcaplusDB做为游戏的核心数据库支撑业务。根据客户的测试与使用反馈,TcaplusDB的毫秒级时延、千万级QPS、无限水平扩展无需分库分表、细粒度回档、合服和无损弹性变配等能力能够实实在在的帮助到游戏业务。

腾讯云TcaplusDB的核心能力得到了客户的认可的同时,不得不提一提我们强大的迁移服务。当时上海盛趣使用的是DynamoDB,上海盛趣的技术团队需要解决开发者不熟悉腾讯云TcaplusDB的用法和已有代码迁移的问题。通过沟通,腾讯云TcaplusDB的接口的易用性得到了客户的充分认可,重点转移到了已有代码如何迁移到腾讯云TcaplusDB 接口上。经过双方技术团队的沟通,上海盛趣的架构中有一个数据访问适配层,所有的数据库访问均通过该适配层,问题一下变得清晰和简单了起来。腾讯云派出技术专家驻场,仅仅用了两周时间就和上海盛趣的研发团队一起完成了代码迁移的工作。至此,TcaplusDB开始全面为公有云客户提供服务。

2020年12月14日,中国信息通信研究院官方权威公布第十一批大数据产品能力评测结果,腾讯云TcaplusDB成为首批通过键值型内存数据库功能评测的分布式NoSQL数据库产品。

2021年1月,TcaplusDB社区上线(地址https://tcaplusdb.tencent.com/),这标志着TcaplusDB将通过和广大的用户及行业合作伙伴进行交流和共建,持续推动国产分布式NoSQL数据库生态的繁荣。

企业级定制的内核下一个十年:为更多行业助力

自诞生以来,腾讯云TcaplusDB就以服务更多开发者为目标,面向拥有使用高性能数据库的研发人员,分享经过腾讯内部检验的存储研发经验、工具和行业资源。而在未来,腾讯云TcaplusDB还将以国产数据库领航者的身份,在这条道路上走得更远,根据行业动态为平台引入更多元化的功能。同时,腾讯云TcaplusDB将和行业合作伙伴一起,继续分享腾讯分布式数据库方面的经验,并将积极投入基于多模和多负载能力的一站式低成本数据处理能力的研发;满足基于全球分布式能力,助力企业解决业务出海、全球同服/多活、跨域数据迁移等关键业务领域需求。

在下一个十年,诞生于游戏的TcaplusDB,还将继续为更多行业优化数据服务能力,贡献自己的力量。

什么数据库能抗住《王者荣耀》的1亿DAU?

带妹上分,团战五杀,光有技术可不行

本文由博客一文多发平台 OpenWrite 发布!









原文转载:http://www.shaoqun.com/a/521158.html

跨境电商:https://www.ikjzd.com/

名人堂是什么:https://www.ikjzd.com/w/1082

灰色清关:https://www.ikjzd.com/w/1409


从找不到需求险些被叫停,到支撑亿级DAU的数据库行业标杆,这款支撑了《王者荣耀》多个系统的腾讯云数据库TcaplusDB在风雨中走过了整整10年。辉映日月破风浪,十年一剑破九天。百万行代码就像淙淙流淌的数据溪流,终于在十年后汇成不可逾越的护城河。出发2010年前后,QQ空间很火,带动了基于SNS互动页游(WebSNS)的火爆,腾讯内部开始考虑怎么做页游。也开始建设页游基础技术体系,其中最重要的产出
jpgoodbuy:jpgoodbuy
grab:grab
reddit:reddit
评分常规算法:评分常规算法
Wish再发误导性产品政策提醒!(附常见问题解答):Wish再发误导性产品政策提醒!(附常见问题解答)

RestTemplate post请求

以前一开始用原生的http请求,那叫一个累,后来找到一个第三方的工具包,用起来是真的舒服,不过有一说一,第三方工具包依赖性真的强,除非和组长商量过,不然能少用,还是少用点。话说搞微服务的肯定少不了和HTTP或RPC打交道的,以前了解过Spring 的RestTemplate,但是一旦实践就给忘光光了。不过经过几次折腾,总算是搞明白了。get请求就不说了,主要是说一下post请求。上代码:

 

 1 RestTemplate restTemplate = new RestTemplate(); 2   HttpHeaders headers = new HttpHeaders(); 3   MultiValueMap<String, Object> map = new LinkedMultiValueMap<>(); 4   map.add("subFun", CommConfig.Otrs_SubFun_CTIDUTYPERSON); 5   map.add("paramEncoded", base(params)); 6   //以form-data形式发送请求 7   headers.setContentType(MediaType.MULTIPART_FORM_DATA); 8   HttpEntity<MultiValueMap<String, Object>> request = new HttpEntity<>(map, headers); 9   ResponseEntity<String> response = restTemplate.postForEntity(url, request, String.class);10   //对数据结果的处理11   return JSONObject.parseObject(response.getBody());

 

第一行是new一个对象

第二行是new一个头部信息

第三行是new一个map对象,注意要用LinkedMultiValueMap

第四、五行都是添加请求参数

第七行是设置请求方式,这里要特别注意,因为post有很多的发送方式,比如,json,form-data,xxx-form-data等,具体的点进去找就行了,里面也有注释说得很清楚

第八行是组装请求体

第九行是发送请求,注意String.class,如果你返回的参数属性确定,就可以用一个实体类来接收

第十一行是我把接收到的参数转为了json格式

这样一个最实用的post请求就出来了。由于我的代码中多处用到了post请求,所以我就拆分成一个方法,为啥不是工具类,因为我觉得他还不够灵活。上截图:

 

 

 1  /** 2   * 发送post请求 3   * @param map 请求参数 4   * @param url 请求地址 5   * @return 响应结果 6  */ 7  public JSONObject post(MultiValueMap<String, Object> map, String url) { 8   RestTemplate restTemplate = new RestTemplate(); 9   HttpHeaders headers = new HttpHeaders();10   //以form-data形式发送请求11   headers.setContentType(MediaType.MULTIPART_FORM_DATA);12   HttpEntity<MultiValueMap<String, Object>> request = new HttpEntity<>(map, headers);13   ResponseEntity<String> response = restTemplate.postForEntity(url, request, String.class);14   //对数据结果的处理15   return JSONObject.parseObject(response.getBody());16  }

 

调用方式,组装好一个map,然后直接调用post()方法就好了

 

 

1   //1、RestTemplate发送http post请求2   MultiValueMap<String, Object> map = new LinkedMultiValueMap<>();3   map.add("subFun", CommConfig.Otrs_SubFun_CTISTATISTICSCALLRECORD);4   map.add("paramEncoded", base(params));5   //2、拿到返回的数据,转换为JSONObject6   JSONObject results = post(map, resUrl("base","otrsUrlBase")+resUrl("base","otrsUrlInf"));

 

之所以说不够灵活,就是请求方式目前是固定的,没有分解出来。最近项目比较赶,没多少时间研究了,先这样子吧。

好了,下课









原文转载:http://www.shaoqun.com/a/521156.html

跨境电商:https://www.ikjzd.com/

easel:https://www.ikjzd.com/w/1721

萌店:https://www.ikjzd.com/w/1538


以前一开始用原生的http请求,那叫一个累,后来找到一个第三方的工具包,用起来是真的舒服,不过有一说一,第三方工具包依赖性真的强,除非和组长商量过,不然能少用,还是少用点。话说搞微服务的肯定少不了和HTTP或RPC打交道的,以前了解过Spring的RestTemplate,但是一旦实践就给忘光光了。不过经过几次折腾,总算是搞明白了。get请求就不说了,主要是说一下post请求。上代码:1RestT
tradeindia:tradeindia
cares:cares
全球下载次数最多的购物APP, Wish如何赢得全球消费市场?:全球下载次数最多的购物APP, Wish如何赢得全球消费市场?
跨境电商的起步之难究竟难在哪里?:跨境电商的起步之难究竟难在哪里?
同样是做无货源店铺,为什么亚马逊能够成为广大卖家的焦点?:同样是做无货源店铺,为什么亚马逊能够成为广大卖家的焦点?