关于高并发:高考成绩都出来了你的秒杀系统如何了

大家好,我是冰河~~ 高考问题都进去了,咱的《Seckill秒杀零碎》专栏如何了?明天给大家交个“期中作业”吧。 总体状况截止到目前,秒杀零碎从需要立项、架构设计、技术选型、环境搭建、编码实现、极致缓存设计到扣减库存防超卖设计,曾经实现16个大的篇章47篇文章。 这是不是更新完了?那必定没啊!尽管当初的秒杀零碎曾经实现了最外围的性能,然而还不是我现实中的样子,我想要大厂技术真正落地的秒杀零碎。咱做的是面向实在场景的秒杀零碎,应用的技术也是大厂中真正落地的技术,所以,咱的秒杀零碎内容还没有完结,波及到的核心技术如下图。 看到这张图后,小伙伴们肯定就明确了:尽管秒杀零碎曾经写了16个大的篇章,47篇文章了,然而基本就没有完结。因为大厂的秒杀零碎基本就没有这么简略的,如果47篇文章可能讲透并实现一个秒杀零碎,那这个所谓的秒杀零碎肯定是个Demo。然而咱们不做Demo,也杜绝做Demo,所以,这个秒杀零碎间隔完结还有一段时间! 专栏源码光说不练假把式,既然是带着大家做我的项目,那必定是少不了写代码,看看咱的代码吧,整体采纳DDD畛域驱动架构设计,在实战章节会每章对应一个代码分支,供小伙伴们对照章节练习。 值得注意的是:咱的代码里大量应用了设计模式,你能够在秒杀零碎里纵情感触设计模式的魅力。另外,因为咱做的秒杀零碎不是Demo,外面波及到的各种波及到高并发、高性能、高可扩大和高可保护的设计思维和编码实现,小伙伴们都能够使用到任何波及到高并发场景的业务零碎中。通过秒杀零碎,不仅仅能够从我的项目实战角度彻底把握高并发编程,更能亲自领会到大厂在高并发场景的应答之道。 视频安顿 冰河正在加班加点的为秒杀零碎录制对应的章节视频,其余专栏也必须安顿上,为的就是让大家更好的学习,更好的消化吸收,更好的学以致用。 好了,明天就到这儿吧,我是冰河,咱们下期见~~

June 30, 2023 · 1 min · jiezi

关于高并发:如何通过项目更好的学习并发编程

作者:冰河星球:http://m6z.cn/6aeFbs博客:https://binghe.gitcode.host文章汇总:https://binghe.gitcode.host/md/all/all.html源码获取地址:https://t.zsxq.com/0dhvFs5oR 积淀,成长,冲破,帮忙别人,成就自我。大家好,我是冰河~~ 明天,正式告诉大家一件事件:在今后一段时间内,冰河会带着大家一起从零开始搭建并开发一套可反对刹时百万流量的高并发秒杀零碎,这里会波及到很多互联网大厂开发过程中所应用的核心技术和架构设计模式,也有冰河在大厂工作过程中,自主研发和参加研发的企业级中间件技术。 在《Seckill秒杀零碎》专栏中,你学到的不仅仅是一套反对刹时百万流量的高并发、高性能、高可用、可扩大的秒杀零碎,更重要的是要学会大厂解决高并发、大流量场景的技术计划和架构设计思维,并学会如何将这些技术计划和架构设计思维落地到理论我的项目中。 一、秒杀专栏背景调研置信很多小伙伴学习了不少对于并发编程相干的基础知识,同时,不少小伙伴也看了冰河出版的《深刻了解高并发编程:外围原理与案例实战》与《深刻了解高并发编程:JDK核心技术》图书。当然,后续依据理论状况,可能会出版《深刻了解高并发编程》系列的其余书籍。然而很多小伙伴在和冰河交换的过程中,普遍存在如下几个问题: 始终在小公司做CRUD,并发编程没接触过,更别提如何高并发理论我的项目了。公司我的项目没什么并发,在线人数也不多,学了很多并发编程相干的常识不晓得怎么用。学了很多并发编程的常识,也晓得一些概念,能说出一些简略的计划,然而没理论我的项目教训。自我感觉把握了一些高并发编程的技术计划,然而如果真正做我的项目时,还是不晓得如何下手。简历上写了相熟并发编程,在面试过程中,面试官个别会问秒杀零碎,或者其余高并发我的项目实战问题,不晓得怎么答复。在大厂工作多年,参加了一些零碎的建设与研发,然而也没机会参加像秒杀零碎这样高并发、大流量的零碎的整个建设过程。其余问题。。。能够看到,从收集的这些并发编程相干的问题来看,存在的问题是普遍性的:小公司的小伙伴受限于业务,接触不到高并发、大流量的业务场景,大厂的小伙伴因为某些起因没有被分到高并发、大流量业务部门。但更多的是大体把握了并发编程的基础知识,而没有系统性落地成理论高并发我的项目的教训。 为了彻底解决小伙伴们的困惑,也为了进一步冲破大家的技术瓶颈,冰河带着他的《Seckill秒杀零碎》来了,在这里,你学到的不仅仅是一套反对刹时百万流量的高并发、高性能、高可用、可扩大的秒杀零碎,更重要的是要学会大厂解决高并发、大流量场景的技术计划和架构设计思维,并学会如何将这些技术计划和架构设计思维落地到理论我的项目中。。 二、为何要搞秒杀零碎说起秒杀零碎,我置信学习这个专栏的你肯定对秒杀零碎有所理解,甚至多多少少也参加了秒杀流动。为啥?因为国内头部电商平台每年的618、双11都会举办大促流动,置信你是参加过的,最起码你可能为这些流动奉献了流量。而每年的618、双11最直观的体现就是流量和交易额。 这些最直观的数字背地,却须要很多高并发编程的常识和技术作为撑持。作为程序员,或者说互联网技术人,尤其是理解一些并发编程技术和计划的开发者,肯定对每年618、双11大促背地的技术感兴趣。 单就秒杀零碎自身而言,它是高并发、大流量场景下最具代表性的零碎,如果你连秒杀零碎的各项技术细节都了然于胸了,那其余波及到并发的系统对你来说,还叫个事儿吗?另外,秒杀零碎背地波及到的高并发、高性能、高可用、可扩大的技术思路和架构模式与架构思维,你能够间接复用于任何须要撑持高并发、大流量的业务场景。 另外,个别只有互联网大厂才具备真正研发秒杀零碎的业务场景,所以,这次《Seckill秒杀零碎》专栏也是一次贴近大厂实在我的项目的机会,专栏中波及到的架构模式和技术点都是大厂在研发秒杀零碎过程中真正实际和优化过的架构模式与技术点,认真学习《Seckill秒杀零碎》专栏,认真消化吸收专栏中的每项知识点、技术思路和架构模式,并亲自动手实际每个并发编程的技巧和计划,置信你会有十分多的播种,而这些播种是实实在在的,也是互联网大厂常常应用的核心技术。 三、秒杀系统核心技术单就秒杀零碎自身而言,就是为应答刹时高并发、大流量场景而设计的反对高并发、大流量的零碎,其背地会波及到泛滥高并发、高性能、高可用的技术作为根底保障。同时,在零碎中,也要重点冲破库存与限购、防刷与风控、数据统一、热点隔离、动静拆散、削峰填谷、数据兜底、限流与降级、流控与容灾等核心技术问题。 所以,冰河总结了秒杀零碎所波及到的最外围的技术内容,整顿后如下图所示。 由图也能够看出,《Seckill秒杀零碎》专栏肯定不会让你悲观,它可能将真正的高并发编程常识串起来,造成知识面,并将这些高并发编程常识落地成秒杀零碎,而在开发秒杀零碎的过程中所应用到的技术,是互联网大厂真正在应用的核心技术。 留神:《Seckill秒杀零碎》专栏会大量应用到冰河出版的《深刻了解高并发编程:外围原理与案例实战》与《深刻了解高并发编程:JDK核心技术》图书中的常识,倡议学习《Seckill秒杀零碎》专栏的过程中,浏览《深刻了解高并发编程:外围原理与案例实战》与《深刻了解高并发编程:JDK核心技术》图书。 四、如何学习秒杀零碎1.退出 冰河技术 常识星球,能力查看星球专栏文章,查看置顶音讯,申请加入我的项目,能力看到我的项目代码和技术小册,如果未申请加入我的项目,点击我的项目链接,你会发现是404页面。 2.专栏的每一章会对应一个代码分支,须要切换对应的分支学习对应的文章的代码分支,同时,分支中的doc/assets/sql里是以后分支的最新SQL语句,在对应的分支查看SQL,更新到本人的数据库中即可。 3.学习过程中最好依照章节程序来学习,每一章前后都是比拟连贯的,并且每一章的代码实现也有先后顺序,这样依照从前往后的程序学习,最终你会实现一个残缺的秒杀零碎。 留神:学习的过程,不是复制粘贴代码的过程,赋值粘贴代码是没有任何意义的,最好的学习形式就是本人入手实现代码,而后思考、总结。 4.代码构造:master分支是最新的全量代码,专栏中每一章都会对应一个代码分支,切换到章节对应的代码分支后,即可依据以后章节学习对应的代码实现,不然,在master分支中看到的是全量的代码。 5.对应代码实现上的问题,能够在专栏对应的源码提issuse:https://gitcode.net/binghe001/seckill/-/issues。 6.冰河后续会为《Seckill秒杀零碎》专栏录制残缺的视频课程。 五、提交作业在学习秒杀零碎的过程中,为了有助于大家更好的消化吸收《Seckill秒杀零碎》的常识,冰河会为大家安排相应的作业。当然,也是为了心愿在学习的过程中,留下你实在的脚印,让咱们一起致力,冲破本身技术瓶颈。 1.代码作业作业空间:https://gitcode.net/seckillteam空间阐明:为常识星球的用户提供我的项目代码提交空间,不便针对我的项目进行技术交换,你能够把本人实现的《Seckill秒杀零碎》源码提交到空间中,依照 项目名称-用户星球编号-作者名称 的格局创立仓库,例如 seckill-1-binghe。 2.文字打卡大家能够在 冰河技术 常识星球中,提交文字作业也能够进行文字打卡,也能够到链接 https://gitcode.net/binghe001/seckill/-/issues 提交issues。次要依照如下形式进行总结: 1.明天你学了哪些章节?2.遇到的问题是什么?3.你是怎么解决问题的?4.明天的播种是什么? 基于大家的打卡或者作业反馈的问题,冰河会在后续以文章和直播的模式对立解决大家学习过程中的疑难。 写在最初对我的项目不太理解的小伙伴也能够到github:https://github.com/binghe001/distribute-seckill 进行理解,冰河会整顿局部源码和阐明到github,欢送各位小伙伴一起交换,一起学习,一起提高,在技术的路线上一起摸索,播种满满~~ 好了,明天就到这儿吧,我是冰河,咱们下期见~~

April 25, 2023 · 1 min · jiezi

关于高并发:横扫一线大厂面试的高并发笔记到底有多硬核

处处须要高并发 “为什么Java面试必问高并发?” 这个问题曾经让程序员们倍感头疼,尤其是想要跳槽到更大公司的程序员,是否丑陋的答复高并发的问题曾经成为求职者是否是一个优良程序员的评判规范,大厂面试尤为显著。 不得不说,高并发考验的就是程序员的实力和实战经验。随着流量变大,高并发曾经成为绕不开的话题,在解决高并发的时候,就会遇到越来越多的技术问题,例如接口响应超时、CPU load升高、死锁、GC频繁、大数据量存储等一系列问题。能够说,如果不会解决高并发,最初只能逐步被淘汰。所以,大厂的面试题里经常出现高并发也就不奇怪了。 因为文章篇幅无限,为了不耽搁大家贵重的工夫,这份PDF小编曾经为大家整顿好了,须要收费支付的敌人麻烦帮忙转发、转发、转发这篇文章(肯定要转发哦),而后【间接点击此处】即可收费获取。如何真正把握高并发第1章高并发时代的必备技能 随着5G利用、多终端利用、物联网利用、工业互联利用、大数据利用、人工智能利用的飞速发展,高并发开发时代未然到来,可能驾驭高并发和大数据的物联网架构师、高并发架构师、大数据架构师、Java高级工程师在人才市场也随之成为“香饽饽”,Netty、Redis、ZooKeeper、高性能HTTP服务器组件(如Nginx)、高并发Java组件(JUC包)等则成为宽广Java工程师所必须把握的开发技能 第2章 高并发IO的底层原理 本章从操作系统的底层原理动手,通过图文并茂的形式为大家深刻分析高并发IO的底层原理,并介绍如何通过设置来让操作系统反对高并发 第3章 Java NIO外围详解 高性能的Java通信相对离不开Java NIO组件,当初支流的技术框架或中间件服务器都应用了Java NIO组件,譬如Tomcat、Jetty、Netty。学习和把握Java NIO组件曾经不是一项加分技能,而是一项必备技能。 第4章 鼎鼎大名的Reactor模式 本书的准则是从根底讲起,而Reactor(反应器)模式是高性能网络编程在设计和架构层面的根底模式,算是根底的原理性知识。只有彻底理解反应器的原理,能力真正构建好高性能的网络应用、轻松地学习和把握高并发通信服务器与框架(如Netty框架、Nginx服务器)。 第5~8章重点解说Netty 目前Netty是高性能通信框架皇冠上当之无愧的明珠,是撑持其余泛滥驰名的高并发、分布式、大数据框架底层的框架。这几章从Reactor模式动手,以“四两拨千斤”的形式为大家介绍Netty原理。同时,还将介绍如何通过Netty来解决网络编程中的重点难题,如Protobuf序列化问题、半包问题等。 第9章 HTTP原理与Web服务器实战 本章介绍一个小的HTTP服务器程序——HTTP Echo回显服务器。 第10章 高并发HTTP通信的外围原理 HTTP是应用层协定,是建设在传输层TCP根底之上的。在通信过程中,TCP每一次连贯的建设和拆除都会经验三次握手和四次挥手,性能力和效率是比拟低的。HTTP一个显著的特点是无状态的,并且最后的设计初衷是用于短连贯场景,申请工夫连贯、申请完开释连贯,以尽快将开释服务资源供其余客户端应用。这就导致每一次原始HTTP协定的传输都须要进行连贯的建设和拆除,从而导致性能比拟低 第11章 WebSocket原理与实战 WebSocket协定的指标是在一个独立的长久连贯上提供全双工双向通信。客户端和服务器能够向对方被动发送和接收数据。WebSocket通信协议于2011年被IETF公布为RFC6455规范,后又公布了RFC7936规范补充标准。WebSocket API也被W3C(World Wide Web Consortium,万维网联盟)定为规范。 第12章 SSL/TLS外围原理与实战 SSL(Secure Sockets Layer,安全套接层)是1994年由网景公司为Netscape Navigator浏器设计和研发的平安传输技术。NetscapeNavigator浏览器是驰名的浏览器Firefox(Firefox是继Chrome和Safari之后最受欢迎的浏览器)的前身。 第13章 ZooKeeper分布式协调 高并发零碎为了应答流量增长须要进行节点的横向扩大,所以高并发零碎往往都是分布式系统。高并发零碎根本都须要进行节点与节点之间间的配合协调,这就须要用到分布式协调中间件(如ZooKeeper)。 ...

April 13, 2023 · 1 min · jiezi

关于高并发:并发提升-20-倍单节点数万-QPSApache-Doris-高并发特性解读

随着用户规模的极速扩张,越来越多用户将 Apache Doris 用于构建企业外部的对立剖析平台,这一方面须要 Apache Doris 去承当更大业务规模的解决和剖析——既蕴含了更大规模的数据量、也蕴含了更高的并发承载,而另一方面,也意味着须要应答企业更加多样化的数据分析诉求,从过来的统计报表、即席查问、交互式剖析等典型 OLAP 场景,拓展到举荐、风控、标签画像以及 IoT 等更多业务场景中,而数据服务(Data Serving)就是其中具备代表性的一类需要。Data Serving 通常指的是向用户或企业客户提供数据拜访服务,用户应用较为频繁的查问模式个别是依照 Key 查问一行或多行数据,例如: 订单详情查问商品详情查问物流状态查问交易详情查问用户信息查问用户画像属性查问...与面向大规模数据扫描与计算的 Adhoc 不同,Data Serving 在理论业务中通常出现为高并发的点查问—— 查问返回的数据量较少、通常只需返回一行或者大量行数据,但对于查问耗时极为敏感、冀望在毫秒内返回查问后果,并且面临着超高并发的挑战。 在过来面对此类业务需要时,通常采取不同的零碎组件别离承载对应的查问拜访。OLAP 数据库个别是基于列式存储引擎构建,且是针对大数据场景设计的查问框架,通常以数据吞吐量来掂量零碎能力,因而在 Data Serving 高并发点查场景的体现往往不迭用户预期。基于此,用户个别引入 Apache HBase 等 KV 零碎来应答点查问、Redis 作为缓存层来分担高并发带来的零碎压力。而这样的架构往往比较复杂,存在冗余存储、保护老本高的问题。交融对立的剖析范式为 Apache Doris 能承载的工作负载带来了挑战,也让咱们更加系统化地去思考如何更好地满足用户在此类场景的业务需要。基于以上思考,在行将公布的 2.0 版本中,咱们在原有性能根底上引入了一系列面向点查问的优化伎俩,单节点可达数万 QPS 的超高并发,极大拓宽了实用场景的能力边界。 #  如何应答高并发查问?始终以来高并发就是 Apache Doris 的劣势之一。对于高并发查问,其外围在于如何均衡无限的系统资源耗费与并发执行带来的高负载。换而言之,须要最大化升高单个 SQL 执行时的 CPU、内存和 IO 开销,其关键在于缩小底层数据的 Scan 以及随后的数据计算,其次要优化形式有如下几种: 分辨别桶裁剪Apache Doris 采纳两级分区,第一级是 Partition,通常能够将工夫作为分区键。第二级为 Bucket,通过 Hash 将数据打散至各个节点中,以此晋升读取并行度并进一步提高读取吞吐。通过正当地划分辨别桶,能够进步查问性能,以下列查问语句为例: select * from user_table where id = 5122 and create_date = '2022-01-01'用户以create_time作为分区键、ID 作为分桶键,并设置了 10 个 Bucket, 通过分辨别桶裁剪后可疾速过滤非必要的分区数据,最终只需读取极少数据,比方 1 个分区的 1 个 Bucket 即可疾速定位到查问后果,最大限度缩小了数据的扫描量、升高了单个查问的延时。 ...

March 23, 2023 · 3 min · jiezi

关于高并发:深入理解高并发编程JDK核心技术冰河新书上市

大家好,我是冰河~~ 废话说多了没用,并发编程技术始终是高级程序员进阶高级工程师的前提条件,也是成为大厂程序员的必备技能,更是冲破本身技术瓶颈的必经之路。 2022年6月我出版了“冰河技术丛书”之“深刻了解高并发编程”系列的第1部作品——《深刻了解高并发编程:外围原理与案例实战》,书中全面粗疏地介绍了高并发编程的基础知识、外围原理、实战案例和零碎架构等内容,帮忙读者从根本上了解并发编程呈现各种诡异Bug问题的本源,并从原理与实战层面找到解决问题的计划。随后这本书被翻译成繁体版——《深刻高平行開發:深度原理&專案實戰》进行发售。 明天,再次给大家隆重的发表一个好消息:继出版《海量数据处理与大数据技术实战》、《MySQL技术大全:开发、优化与运维实战》和《深刻了解分布式事务:原理与实战》、《深刻了解高并发编程:外围原理与案例实战》和《深刻高平行開發:深度原理&專案實戰》后,通过一年多的认真打磨,冰河的第6本图书——《深刻了解高并发编程:JDK核心技术》正式上市了。 写作背景2022年6月出版《深刻了解高并发编程:外围原理与案例实战》一书后,思来想去,隐约感觉书中尽管全面粗疏地介绍了高并发编程的基础知识、外围原理、实战案例和零碎架构等内容,可能帮忙读者从根本上了解并发编程呈现各种诡异Bug问题的本源,也可能从原理与实战层面找到解决问题的计划。然而,貌似还短少对于《深刻了解高并发编程:外围原理与案例实战》一书中提到的一些外围原理的深度落地实际分析。 那么问题来了:《深刻了解高并发编程:外围原理与案例实战》一书中,在外围原理篇具体介绍了并发编程的各种外围原理,那如何对这些外围原理的实际进行分析呢?再一个就是这些并发编程实际最好是大家都可能非常容易接触到的,这样,各位小伙伴就可能一边看书,一边实际了。 通过一段时间的调研,我把眼光聚焦到了JDK上,置信只有大家学习Java,就必定会接触JDK,而JDK中有很多并发编程工具类,各种并发编程类库,比方:并发容器类、并发阻塞队列、并发非阻塞队列、并发工具类、锁工具类、无锁原子类、线程工具类和线程池等等,都是JDK中对于并发编程外围原理的深度实际。并且JDK中这些并发编程的类库经验了理论生产环境中高并发、大流量的考验,是学习高并发编程十分好的实际案例,并且这些案例是任何一个学习Java的小伙伴非常容易取得的贵重资源。 所以,思来想去,最终将这本书聚焦到JDK上,并且为新书起了一个新的名字——《深刻了解高并发编程:JDK核心技术》,从名字上看,也可能看出,这是一本聚焦JDK并发编程核心技术的图书,并且是“冰河技术丛书”之“深刻了解高并发编程”系列的第2部作品,确定好书名和方向后,接下来,就是致力的去写作了。 本书全貌本书从理论需要登程,将全书分为三个大的篇章,别离是:JDK高并发编程的基础知识、外围工具和线程池核心技术。 第一篇 根底篇(第1~2章) 本篇简略地介绍了过程与线程的基本概念、线程调度与上下文切换、过程与线程的综合比照、如何查看过程与线程的运行时信息,以及线程和线程组的基本操作。 第二篇 外围工具篇(第3~13章) 本篇通过大量源码和案例具体介绍了JDK的各种并发工具,涵盖同步汇合、并发List汇合类、并发Set汇合类、并发Map汇合类、并发阻塞队列、并发非阻塞队列、并发工具类、锁工具类、无锁原子类、线程工具类和异步编程工具类。简直每个章节都配有JDK外围工具类的源码及实战案例,有助于读者了解。 第三篇 线程池核心技术篇(第14~16章) 本篇深刻分析了JDK中线程池的外围源码。包含线程池顶层接口和抽象类、线程池正确运行的外围流程、线程池执行工作的外围流程、Worker线程的外围流程、线程池优雅退出的外围流程、ScheduledThreadPoolExecutor类与Timer类的区别、定时工作线程池的初始化、调度流程和优雅敞开流程等。通过对本篇的学习,读者可能从源码级别深刻理解线程池的外围原理和执行流程。 为了进一步加深读者对线程池的了解,在本篇的随书源码中,会给出残缺的手写线程池的案例程序。 学完播种本书适宜:联网行业从业人员、高校师生、中高级开发人员、架构师、技术经理及技术专家和对高并发编程感兴趣的人员学习浏览。另外,因为本书是专一介绍JDK高并发编程技术的图书,强烈建议Java方向的小伙伴人手一册。 学完并把握书籍中的内容后,从此,你的简历上不再仅仅是简略的列举CRUD我的项目,还能够写上相熟或精通JDK中提供的各种并发编程类库,并且还能够具备本人手写一个线程池的能力。这下,你就能够和其他人在并发编程方面拉开差距了。 另外,“冰河技术丛书”之“深刻了解高并发编程”系列目前已出版两本图书,别离是《深刻了解高并发编程:外围原理与案例实战》和《深刻了解高并发编程:JDK核心技术》。后续依据理论状况,还会出版其余的并发书籍,如果想彻底深刻了解高并发编程,能够继续关注“冰河技术丛书”之“深刻了解高并发编程”系列图书。不仅仅是为你的简历和面试加分,更重要的是要让你彻底了解并发编程,为你的职业生涯保驾护航。 下手新书明天,冰河特意为大家申请了 5折优惠,名额不多,先到先得 ,兄弟们盘它,购买后退出图书专有读者群,由冰河自己亲自答疑解惑,我想把经验的真正大规模高并发我的项目架构与研发教训全副传授给你,跟冰河一起彻底深刻了解高并发编程。 https://u.jd.com/izMwOkE感激图书编辑:张晶、杨中兴 感激大佬举荐(排名局部先后):蒋涛(CSDN创始人、总裁)、邹欣(CSDN副总裁)、李海翔(腾讯数据库资深研究员、首席架构师)、林子熠(阿里巴巴JVM技术专家、CCF系统软件专委会执行委员)、于君泽(资深技术专家、公众号“技术琐话”作者)、沈剑(互联网架构专家,公众号“架构师之路”作者)、秦金卫(长亮科技平台技术部副总经理、Apache Dubbo/ShardingSphere PMC)、张开涛(《亿级流量网站架构核心技术》作者)、季敏(Seata开源社区创始人)、李鹏云(杭州任你说智能科技CTO)、程军(前饿了么技术总监,公众号“军哥手记”作者)、骆俊武(京东批发架构师)、纯净的微笑(公众号“纯净的微笑”作者)、黄哲铿/Mr.K(“顿悟山丘”征询创始人、公众号“技术领导力”作者)、李伟(Apache RocketMQ北京社区联结发起人 && Commiter)、翟永超(公众号“程序猿DD”维护者、《Spring Cloud微服务实战》作者)。 感激各位读者、粉丝敌人:始终以来对冰河的反对。 最初给大家上个长图。 好了,明天就到这儿吧,我是冰河,咱们下期见~~

March 17, 2023 · 1 min · jiezi

关于高并发:高并发系统设计的15个锦囊

记得很久之前,去面试过字节跳动。被三面的面试官问了一道场景设计题目:如何设计一个高并发零碎。过后我答复得比拟毛糙,最近回想起来,所以整顿了设计高并发零碎的15个锦囊,置信大家看完会有帮忙的。 如何了解高并发零碎所谓设计高并发零碎,就是设计一个零碎,保障它整体可用的同时,可能解决很高的并发用户申请,可能接受很大的流量冲击。 咱们要设计高并发的零碎,那就须要解决好一些常见的零碎瓶颈问题,如内存不足、磁盘空间有余,连接数不够,网络宽带不够等等,以应答突发的流量洪峰。 1. 分而治之,横向扩大如果你只部署一个利用,只部署一台服务器,那抗住的流量申请是十分无限的。并且,单体的利用,有单点的危险,如果它挂了,那服务就不可用了。 因而,设计一个高并发零碎,咱们能够分而治之,横向扩大。也就是说,采纳分布式部署的形式,部署多台服务器,把流量分流开,让每个服务器都承当一部分的并发和流量,晋升整体零碎的并发能力。 2. 微服务拆分(零碎拆分)要进步零碎的吞吐,进步零碎的解决并发申请的能力。除了采纳分布式部署的形式外,还能够做微服务拆分,这样就能够达到摊派申请流量的目标,进步了并发能力。 所谓的微服务拆分,其实就是把一个单体的利用,按性能单一性,拆分为多个服务模块。比方一个电商零碎,拆分为用户零碎、订单零碎、商品零碎等等。 3. 分库分表当业务量暴增的话,MySQL单机磁盘容量会撑爆。并且,咱们晓得数据库连接数是无限的。在高并发的场景下,大量申请拜访数据库,MySQL单机是扛不住的!高并发场景下,会呈现too many connections报错。 所以高并发的零碎,须要思考拆分为多个数据库,来抗住高并发的毒打。而如果你的单表数据量十分大,存储和查问的性能就会遇到瓶颈了,如果你做了很多优化之后还是无奈晋升效率的时候,就须要思考做分表了。个别千万级别数据量,就须要分表,每个表的数据量少一点,晋升SQL查问性能。 当面试官问要求你设计一个高并发零碎的时候,个别都要说到分库分表这个点。 4. 池化技术在高并发的场景下,数据库连接数可能成为瓶颈,因为连接数是无限的。 咱们的申请调用数据库时,都会先获取数据库的连贯,而后依附这个连贯来查问数据,搞完出工,最初敞开连贯,开释资源。如果咱们不必数据库连接池的话,每次执行SQL,都要创立连贯和销毁连贯,这就会导致每个查问申请都变得更慢了,相应的,零碎解决用户申请的能力就升高了。 因而,须要应用池化技术,即数据库连接池、HTTP 连接池、Redis 连接池等等。应用数据库连接池,能够防止每次查问都新建连贯,缩小不必要的资源开销,通过复用连接池,进步零碎解决高并发申请的能力。 同理,咱们应用线程池,也能让工作并行处理,更高效地实现工作。 5. 主从拆散通常来说,一台单机的MySQL服务器,能够反对500左右的TPS和10000左右的QPS,即单机撑持的申请拜访是无限的。因而你做了分布式部署,部署了多台机器,部署了主数据库、从数据库。 然而,如果双十一搞流动,流量必定会猛增的。如果所有的查问申请,都走主库的话,主库必定扛不住,因为查问申请量是十分十分大的。因而个别都要求做主从拆散,而后实时性要求不高的读申请,都去读从库,写的申请或者实时性要求高的申请,才走主库。这样就很好爱护了主库,也进步了零碎的吞吐。 当然,如果答复了主从拆散,面试官可能扩大开问你主从复制原理,问你主从提早问题等等,这块大家须要全方位温习好哈。 6. 应用缓存无论是操作系统,浏览器,还是一些简单的中间件,你都能够看到缓存的影子。咱们应用缓存,次要是晋升零碎接口的性能,这样高并发场景,你的零碎就能够反对更多的用户同时拜访。 罕用的缓存包含:Redis缓存,JVM本地缓存,memcached等等。就拿Redis来说,它单机就能轻轻松松应答几万的并发,你读场景的业务,能够用缓存来抗高并发。 缓存尽管用得爽,然而要留神缓存应用的一些问题: 缓存与数据库的一致性问题缓存雪崩缓存穿透缓存击穿7. CDN,减速动态资源拜访商品图片,icon等等动态资源,能够对页面做动态化解决,缩小拜访服务端的申请。如果用户散布在全国各地,有的在上海,有的在深圳,地区相差很远,网速也各不相同。为了让用户最快拜访到页面,能够应用CDN。CDN能够让用户就近获取所需内容。 什么是CDN? Content Delivery Network/Content Distribution Network,翻译过去就是内容散发网络,它示意将动态资源散发到位于多个地理位置机房的服务器,能够做到数据就近拜访,减速了动态资源的访问速度,因而让零碎更好解决失常别的动静申请。8. 音讯队列,削锋咱们搞一些双十一、双十二等经营流动时,须要防止流量暴涨,打垮利用零碎的危险。因而个别会引入音讯队列,来应答高并发的场景。 假如你的利用零碎每秒最多能够解决2k个申请,每秒却有5k的申请过去,能够引入音讯队列,利用零碎每秒从音讯队列拉2k申请解决得了。 有些搭档放心这样可能会呈现音讯积压的问题: 首先,搞一些经营流动,不会每时每刻都那么多申请过去你的零碎(除非有人歹意攻打),高峰期过来后,积压的申请能够缓缓解决;其次,如果音讯队列长度超过最大数量,能够间接摈弃用户申请或跳转到谬误页面;9. ElasticSearchElasticsearch,大家都应用得比拟多了吧,个别搜寻性能都会用到它。它是一个分布式、高扩大、高实时的搜寻与数据分析引擎,简称为ES。 咱们在聊高并发,为啥聊到ES呢?因为ES能够扩容不便,人造撑持高并发。当数据量大的时候,不必动不动就加机器扩容,分库等等,能够思考用ES来反对简略的查问搜寻、统计类的操作。 10. 降级熔断熔断降级是爱护零碎的一种伎俩。以后互联网零碎个别都是分布式部署的。而分布式系统中偶然会呈现某个根底服务不可用,最终导致整个零碎不可用的状况, 这种景象被称为服务雪崩效应。 比方分布式调用链路A->B->C....,下图所示: 如果服务C呈现问题,比方是因为慢SQL导致调用迟缓,那将导致B也会提早,从而A也会提早。堵住的A申请会耗费占用零碎的线程、IO、CPU等资源。当申请A的服务越来越多,占用计算机的资源也越来越多,最终会导致系统瓶颈呈现,造成其余的申请同样不可用,最初导致业务零碎解体。为了应答服务雪崩, 常见的做法是熔断和降级。最简略是加开关管制,当上游零碎出问题时,开关关上降级,不再调用上游零碎。还能够选用开源组件Hystrix来反对。 你要保障设计的零碎能应答高并发场景,那必定要思考熔断降级逻辑进来。 11. 限流限流也是咱们应答高并发的一种计划。咱们当然心愿,在高并发大流量过去时,零碎能全副申请都失常解决。然而有时候没方法,零碎的CPU、网络带宽、内存、线程等资源都是无限的。因而,咱们要思考限流。 如果你的零碎每秒扛住的申请是一千,如果一秒钟来了十万申请呢?换个角度就是说,高并发的时候,流量洪峰来了,超过零碎的承载能力,怎么办呢? 这时候,咱们能够采取限流计划。就是为了爱护零碎,多余的申请,间接抛弃。 什么是限流:在计算机网络中,限流就是管制网络接口发送或接管申请的速率,它可避免DoS攻打和限度Web爬虫。限流,也称流量管制。是指零碎在面临高并发,或者大流量申请的状况下,限度新的申请对系统的拜访,从而保证系统的稳定性。能够应用Guava的RateLimiter单机版限流,也能够应用Redis分布式限流,还能够应用阿里开源组件sentinel限流。 面试的时候,你说到限流这块的话?面试官很大概率会问你限流的算法,因而,大家在筹备面试的时候,须要温习一下这几种经典的限流算法哈 12. 异步回顾一下什么是同步,什么是异步呢?以办法调用为例,它代表调用方要阻塞期待被调用办法中的逻辑执行实现。这种形式下,当被调用办法响应工夫较长时,会造成调用方短暂的阻塞,在高并发下会造成整体零碎性能降落甚至产生雪崩。异步调用恰恰相反,调用方不须要期待办法逻辑执行实现就能够返回执行其余的逻辑,在被调用办法执行结束后再通过回调、事件告诉等形式将后果反馈给调用方。因而,设计一个高并发的零碎,须要在失当的场景应用异步。如何应用异步呢?后端能够借用音讯队列实现。比方在海量秒杀申请过去时,先放到音讯队列中,疾速响应用户,通知用户申请正在解决中,这样就能够开释资源来解决更多的申请。秒杀申请解决完后,告诉用户秒杀抢购胜利或者失败。 13. 接口的惯例优化设计一个高并发的零碎,须要设计接口的性能足够好,这样零碎在雷同工夫,就能够解决更多的申请。当说到这里的话,能够跟面试官说说接口优化的一些计划了。 14. 压力测试确定零碎瓶颈设计高并发零碎,离不开最重要的一环,就是压力测试。就是在零碎上线前,须要对系统进行压力测试,测分明你的零碎撑持的最大并发是多少,确定零碎的瓶颈点,让本人心里有底,最好预防措施。 压测完要剖析整个调用链路,性能可能呈现问题是网络层(如带宽)、Nginx层、服务层、还是数据路缓存等中间件等等。 ...

January 7, 2023 · 1 min · jiezi

关于高并发:InterruptedException异常会对并发编程产生哪些影响

写在后面InterruptedException异样可能没你想的那么简略!前言当咱们在调用Java对象的wait()办法或者线程的sleep()办法时,须要捕捉并解决InterruptedException异样。如果咱们对InterruptedException异样处理不当,则会产生咱们意想不到的结果! 程序案例例如,上面的程序代码,InterruptedTask类实现了Runnable接口,在run()办法中,获取以后线程的句柄,并在while(true)循环中,通过isInterrupted()办法来检测以后线程是否被中断,如果以后线程被中断就退出while(true)循环,同时,在while(true)循环中,还有一行Thread.sleep(100)代码,并捕捉了InterruptedException异样。整个代码如下所示。 package io.binghe.concurrent.lab08;/** * @author binghe * @version 1.0.0 * @description 线程测试中断 */public class InterruptedTask implements Runnable{ @Override public void run() { Thread currentThread = Thread.currentThread(); while (true){ if(currentThread.isInterrupted()){ break; } try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } }}上述代码的本意是通过isInterrupted()办法查看线程是否被中断了,如果中断了就退出while循环。其余线程通过调用执行线程的interrupt()办法来中断执行线程,此时会设置执行线程的中断标记位,从而使currentThread.isInterrupted()返回true,这样就可能退出while循环。 这看上去没啥问题啊!但真的是这样吗?咱们创立一个InterruptedTest类用于测试,代码如下所示。 package io.binghe.concurrent.lab08;/** * @author binghe * @version 1.0.0 * @description 测试线程中断 */public class InterruptedTest { public static void main(String[] args){ InterruptedTask interruptedTask = new InterruptedTask(); Thread interruptedThread = new Thread(interruptedTask); interruptedThread.start(); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } interruptedThread.interrupt(); }}咱们运行main办法,如下所示。 ...

November 25, 2022 · 1 min · jiezi

关于高并发:图解-聊聊秒杀

1.需要剖析“秒杀”这个词在电商行业中呈现的频率较高,如京东或者淘宝平台的各种“秒杀”流动,最典型的就是“双11抢购”。 “秒杀”是指在无限的工夫内对无限的商品数量进行抢购的一种行为,这是商家以“高价量少”的商品来获取用户的一种营销伎俩。 01. 功能性需要其实,整个秒杀的业务场景并不简单,可即查看参加秒杀的商品信息,加上购买和领取的动作,如下图所示。 秒杀业务最大的挑战在于3点: 刹时:持续时间极短,对于热门且具备极强竞争力的商品通常只有一秒。流量微小:因为价格低廉,商品性价比高,而且失常买是须要很高的价格,所以才会吸引大量的用户来争抢。数量无限:因为商品的高价且性价比高,所以只有很无限的商品数量参加秒杀。同时,在保障高并发流量承接的前提下,为了加强用户的体验和流动规定的公平性,以及避免受到歹意毁坏等,特此减少如下需要: (1)用户在秒杀页面无需始终刷新“抢购”按钮,待秒杀流动开始时,按钮主动点亮。 (2)在偏心以及避免歹意毁坏的准则下,在下单之前减少验证码的录入,或者答题的相干环节。 (3)库存不能呈现问题,即不多扣也不少扣。 (4)整个秒杀流动过程继续10分钟。 02. 性能指标预估通过秒杀的需要形容可得出,以后秒杀流动次要须要预估三块的性能指标:存储容量、并发量、网络带宽。 1)存储容量因为是秒杀流动,且参加的商品根本都是高价高性价比的,数量是十分无限的。所以,在订单存储上根本不必去过多思考。 2)并发量针对5000万用户均匀每人拜访2次,则并发量为每秒16.7万左右(5000w2/1060),在预留一部分,能够预估到每秒25万左右(也能够进行double下)。 3)网络带宽在带宽方面,须要进行相干优化,采取数据传输越少越好,假如单条传输在0.5KB,则依据并发量预估网络带宽为:977Mb左右(25w0.5KB=122MB8bit=977Mb)。 03. 非功能性需要做任何零碎都要思考非功能性需要,特地是公司的外围零碎,以后秒杀业务零碎非功能性需要次要体现在如下几点: 高可用,在秒杀流动的整个继续期间内,都能对用户提供服务。高性能,让每个用户都能感触到极快的秒杀响应,不能呈现大批量用户提早较高的景象。可扩大,当流量比预期更高时,有平滑扩大的策略(也有局部产品设计成敌对的回绝策略)。2.概要设计通过对秒杀业务的自身认知以及下面提到的秒杀业务需要,本次秒杀零碎须要着重设计如下几点: (1)动静拆散:如何保障用户在不刷新页面的状况下,仍然能进行秒杀相干数据的获取且不会耽搁秒杀流动的开始。 (2)流量分层,针对微小流量,如何进行无效的防控,免得造成后盾服务的不堪重负,以及如何防止前端页面的卡死。 (3)高可用:如何确保后盾继续提供服务。 (4)扣减库存:如何无效扣减库存。 01. 动静拆散动静拆散是指,将动态页面与动静页面(或者静态数据与动态数据)解耦拆散,用不同零碎承载对应流量。这样能够晋升整个服务拜访性能和可维护性。 商品秒杀页面的静态数据以及动态数据,均是不同的中央提供,如下图所示。 静态数据是指,页面中简直不怎么变动的数据(即不根据用户的Cookie、根本信息、地区,及工夫等各种属性来生成的数据),例如: CSS和JavaScript中的动态文件。流动页中的HTML动态文件。图片等相干资源文件。其余与用户信息无关的静态数据。对于这种分离出来的静态数据能够进行缓存。在缓存之后,这些静态数据的拜访效率就进步了,零碎也更快了。能够应用代理服务器进行静态数据的缓存。 动态数据是指,根据以后用户属性动静生成的数据,在浏览淘宝首页时,每个用户所看到的商品都是不一样的,这就是淘宝的“千人千面”——针对不同用户做不同的举荐;在百度搜寻中是根据不同用户的输出条件,以及用户的习惯给出不同的后果页。这其中的数据就是动态数据。 02. 流量分层在“秒杀”业务中,商品价格具备弱小的吸引力,所以会受到很多用户的关注,然而商品数量是无限的。所以,在千万的用户中可能只有100人能失去商品,对于零碎来说,有90%以上的流量属于有效流量。 “秒杀”业务心愿有大量的用户来关注“秒杀”流动,然而在用户真正下单时又不能将这些流量全副放过,所以,须要设计一套高效的流量管控计划,来无效地管制申请流量,过滤掉没必要的流量。 对于刹时流量洪峰能够采纳倒三角的分层级逐层管制形式,共分为CDN、反向代理(Nginx)、后端服务及DB这四个层级。接下来,就来看看每一层级是怎么管制流量的,如下图所示。 03. 高可用要想在整个“秒杀”流动继续期间内,仍然能对用户提供良好的体验,则秒杀零碎架构在设计时不能设计成单节点的架构。 单节点是所有零碎设计中的大忌,因为单节点零碎意味着零碎的不稳定性较高,可能会呈现不可用的状况,会给企业带来间接的损失。在零碎设计(特地是“秒杀”这类对高并发要求极高的零碎)时,必须保证系统的高可用,如下图所示。 04. 扣减库存对于“秒杀”流动,通常,公司是不容许商品超卖(即下单胜利的数量不能大于商品存存数量)的。一旦超卖,则会给公司造成损失。如果被歹意流量利用,则损失是微小的。 库存对于电商平台来说是一个重要的业务指标,所以在技术上须要正当设计扣减库存,不能呈现“超卖”景象。通常,扣减库存常有以下3种形式: 下单扣库存:在用户下单后就扣减库存。领取扣库存:用户付完款后再扣减库存。预扣库存:在用户下完订单后,零碎会为其锁定库存一段时间,在超过锁定工夫后会主动开释锁定的库存。05. 零碎架构设计依据下面探讨,针对以后秒杀架构如下图所示。 如上架构比拟简洁,次要分为以下5层。 用户层:用户端的展示局部,次要波及商品的相干信息及以后“秒杀”流动的信息。CDN层:缓存“秒杀”流动的动态资源文件。负载平衡层:拦挡申请及散发路由等。服务层:“秒杀”流动的具体交易的相干逻辑解决。基础设施层:数据存储、大数据计算及音讯推送相干操作。其部署架构图如下: 3.具体设计01. 动静拆散设计 施行动静拆散架构能够采纳“分而治之”的方法,行将动态数据和静态数据解耦,别离应用各自的架构零碎来承载对应的流量: 对于静态数据,举荐缩短用户申请门路,因为门路越短,访问速度也就越快。另外,即尽可能将静态数据缓存起来。对于动态数据,个别用户端须要和服务端进行交互能力获取,所以,申请门路较长,访问速度会慢一点。下图展现了动静拆散计划。静态数据访问速度很快,而动态数据访问速度较慢。那么试想下,能够将须要动静获取的数据给提前生成好,而后应用动态页面减速技术来拜访吗?如果这样能够,那动态数据拜访的速度就变快了。 这样是能够的,须要用到比拟风行的“页面动态化”技术。页面动态化技术是指,间接缓存HTTP连贯,而不仅是缓存数据。如下图所示,代理服务器依据申请的URL间接将HTTP对应的响应头及响应音讯体返回,流程简洁且高效。 02. 流量分层设计流量分层次要体现在对于CDN层、反向代理层、后端服务层以及数据层流量进行管制。 1)CDN层流量管制由动静拆散技术能够想到:应尽量将尽可能多的数据提前生成,而后将其放入CDN节点缓存中(因为CDN层在物理架构上离用户比拟近)。 所以,如果绝大部分的流量都在这一层获取数据,则达到后端的流量会缩小很多,如下图所示。 2)反向代理层流量管制在动静拆散计划中,讲到通过“页面动态化技术”减速动态数据的获取,即提前将动态数据生成好,而后对其进行动态化解决。 所以,这里就能够根据页面动态化减速技术,通过后端服务Job的形式定时提前生成前端须要动态的数据;而后,将其发送到内容散发服务上;最初,散发服务会将这些动态化页面数据散发到所有的反向代理服务器上,如下图所示。 在“秒杀”业务中,流动详情页上有一个倒计时的模块,用户能够看到以后“秒杀”流动还残余多少工夫开始。 这种逻辑简略的性能能够间接应用Nginx来实现:利用nginx-lua插件,应用lua脚本获取以后Nginx服务器的工夫进行计算倒计时。 另外,商品库存数据也能够通过Nginx间接拜访分布式缓存来获取,如下图所示。 “秒杀”业务中的商品价格很低,对于用户有很大的吸引力,所以可能会有人利用“秒杀器”进行不公平竞争,且有可能存在竞争对手歹意刷申请的状况。 如果存在这样的状况,那本次流动就是有危险的,万一被歹意流量独占了库存,则会导致失常用户不能抢购商品,也有可能这种歹意的申请会对后端系统造成重大冲击,甚至造成后端系统瘫痪。 对于这种歹意申请,最好有一套机制能提前感知,并将歹意申请提前封存。能够在Nginx层中管制;也能够在Nginx中配置用户的拜访频率(例如每分钟只能拜访10次);还能够应用Lua脚本编写一些简略业务逻辑的接口,例如,通过调用接口间接封掉指定IP地址或UserAgent的申请。 ...

October 30, 2022 · 1 min · jiezi

关于高并发:高并发深度解析ScheduledThreadPoolExecutor类的源代码

在【高并发专题】的专栏中,咱们深度剖析了ThreadPoolExecutor类的源代码,而ScheduledThreadPoolExecutor类是ThreadPoolExecutor类的子类。明天咱们就来一起手撕ScheduledThreadPoolExecutor类的源代码。 构造方法咱们先来看下ScheduledThreadPoolExecutor的构造方法,源代码如下所示。 public ScheduledThreadPoolExecutor(int corePoolSize) { super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS, new DelayedWorkQueue());}public ScheduledThreadPoolExecutor(int corePoolSize, ThreadFactory threadFactory) { super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS, new DelayedWorkQueue(), threadFactory);}public ScheduledThreadPoolExecutor(int corePoolSize, RejectedExecutionHandler handler) { super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS, new DelayedWorkQueue(), handler);}public ScheduledThreadPoolExecutor(int corePoolSize, ThreadFactory threadFactory, RejectedExecutionHandler handler) { super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS, new DelayedWorkQueue(), threadFactory, handler);}从代码构造上来看,ScheduledThreadPoolExecutor类是ThreadPoolExecutor类的子类,ScheduledThreadPoolExecutor类的构造方法实际上调用的是ThreadPoolExecutor类的构造方法。 schedule办法接下来,咱们看一下ScheduledThreadPoolExecutor类的schedule办法,源代码如下所示。 public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit) { //如果传递的Runnable对象和TimeUnit工夫单位为空 //抛出空指针异样 if (command == null || unit == null) throw new NullPointerException(); //封装工作对象,在decorateTask办法中间接返回ScheduledFutureTask对象 RunnableScheduledFuture<?> t = decorateTask(command, new ScheduledFutureTask<Void>(command, null, triggerTime(delay, unit))); //执行延时工作 delayedExecute(t); //返回工作 return t;}public <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit) //如果传递的Callable对象和TimeUnit工夫单位为空 //抛出空指针异样 if (callable == null || unit == null) throw new NullPointerException(); //封装工作对象,在decorateTask办法中间接返回ScheduledFutureTask对象 RunnableScheduledFuture<V> t = decorateTask(callable, new ScheduledFutureTask<V>(callable, triggerTime(delay, unit))); //执行延时工作 delayedExecute(t); //返回工作 return t;}从源代码能够看出,ScheduledThreadPoolExecutor类提供了两个重载的schedule办法,两个schedule办法的第一个参数不同。能够传递Runnable接口对象,也能够传递Callable接口对象。在办法外部,会将Runnable接口对象和Callable接口对象封装成RunnableScheduledFuture对象,实质上就是封装成ScheduledFutureTask对象。并通过delayedExecute办法来执行延时工作。 ...

October 24, 2022 · 3 min · jiezi

关于高并发:高并发场景微服务实战一

高并发场景微服务实战(一)你好,我是程序员Alan,很快乐遇见你. 说到高并发和微服务,你是不是和我一样有很多的困惑? 晓得微服务开发热门,但始终是在行看热闹,不晓得外面具体有哪些内容。晓得高并发零碎开发常识,是获取大厂Offer的利器,可是工作中遇不到高并发的需要场景。理解过微服务开发、高并发零碎开发实践,苦于没实战经验。晓得单个技术点的利用,但怎么将技术交融起来有些含糊。为了解决本人的这些困惑,将微服务架构开发体系,高并发零碎设计体系串联起来。在面对新机会时能把握住机会,在理论产品开发中,能做到胸有成竹。 我重复思考之后,决定以一个虚构的高并发场景的微服务零碎为主线,一步步将技术点串联起来,多线程 -> 高并发 -> 服务注册 -> 服务发现 -> 服务接口治理 -> 配置核心 -> 分布式事务 -> 对立网关 -> 服务限流降级 -> 性能测试等,一个点一个点缓缓啃,由点成线,由线成面, 系统性从 0 到 1 的发明一个高并发场景的微服务零碎。通过,场景 -> 原理 -> 实际 -> 压测 -> 发现问题 -> 学习常识 -> 优化零碎,周而复始。来帮忙本人更快、更深刻地了解和消化。 为了帮忙其余有这些困惑的敌人,我会一步步把本人的设计和实现过程记录下来,把散落在网络上各处的知识点整合起来,缩小大家筛选梳理这些常识所要花费的大量工夫老本。 最初我再具体说一下本人为什么要学习高并发零碎设计和微服务开发,供大家参考。 求职时加强技术自信。咱们都能显著的感触到往年是互联网的寒冬,经济局势很不好。很多公司都降本增效,也就是裁员、缩小招聘的人员数量,另一方面又冀望咱们打工人能够给公司带来更大的价值。 那么对于公司来说,仅仅懂得 CRUD, 简历中不足亮点的程序员,就不如有高并发、微服务零碎设计教训和有架构能力的程序员有吸引力了。 晋升技术实力,减少职业转型的可能性。减少职业转型的可能:在介绍工作和工作技能时,我和敌人经常会自嘲的称本人为互联网民工、搬砖的,只会 CV 和 CRUD。这是自嘲,也是写实。因为咱们都只是公司里一颗小小的螺丝钉长期从事部分性能开发。然而软件系统是一个简单工程,只有从更高的角度统观全局,思考业务的方方面面以及将来可能的演进方向,能力深刻理解一个产品或我的项目的外在含意,而这个话语权往往把握在更高职级的开发者、设计师、架构师手中,如果把握了一套微服务架构、开发理念,减少了向更高职级降职的可能性。 晋升技术实力:计算机领域里尽管知识点庞杂,但很多核心思想都是相通的。高并发零碎设计和微服务零碎开发的技术和设计思维,无论是对于初入职场的工程师还是对于有肯定工作教训的同学来说都有很大的帮忙。 解决工作中软件研发难题。微服务:随着公司业务复杂度和用户量的晋升,单体利用,大量性能代码沉积在一起,显得特地臃肿繁冗,开发保护老本很高。这在日常运维、降级保护时十分不便,一个小性能的变更都有可能导致整个工程呈现问题甚至宕机,如果是运行中的生产环境解体,由此所造成的经济损失或不好的社会影响,将是不可估量的。而引入微服务,能够更好的解决这一系列的问题。 高并发:即便公司业务流量安稳,并不示意不会遇到一些高并发的需要场景。同样是缓存的应用,在低并发下只须要理解根本的应用形式,但在高并发场景下须要关注缓存命中率,如何应答缓存穿透,如何防止雪崩,如何解决缓存一致性等问题,这就减少了设计方案的复杂度,对设计者能力的要求也会更高。 所以,为了防止遇到问题时慌手慌脚,有必要提前储备足够多的微服务和高并发常识,从而具备随时应答可能呈现的相干需要的能力。 我想说的是,解决产品问题不应该是最终的指标,晋升技术能力和技术视线才是咱们始终不变的谋求。 放弃技术的前瞻性。研发技术迭代突飞猛进,新概念新利用也是层出不穷,云原生架构、容器化运维、中台等等,都与微服务有着奥妙的关系,只有放弃技术的持续性,能力更好的学习新技术,否则会很不利于新技术的落地利用。 站在伟人的肩膀上: 码闻强—SpringCloud微服务实战唐扬—高并发零碎设计40问阿里开发者、大淘宝技术

October 23, 2022 · 1 min · jiezi

关于高并发:高并发ScheduledThreadPoolExecutor与Timer的区别和简单示例

JDK 1.5开始提供ScheduledThreadPoolExecutor类,ScheduledThreadPoolExecutor类继承ThreadPoolExecutor类重用线程池实现了工作的周期性调度性能。在JDK 1.5之前,实现工作的周期性调度次要应用的是Timer类和TimerTask类。本文,就简略介绍下ScheduledThreadPoolExecutor类与Timer类的区别,ScheduledThreadPoolExecutor类相比于Timer类来说,到底有哪些劣势,以及二者别离实现任务调度的简略示例。 二者的区别线程角度Timer是单线程模式,如果某个TimerTask工作的执行工夫比拟久,会影响到其余工作的调度执行。ScheduledThreadPoolExecutor是多线程模式,并且重用线程池,某个ScheduledFutureTask工作执行的工夫比拟久,不会影响到其余工作的调度执行。零碎工夫敏感度Timer调度是基于操作系统的相对工夫的,对操作系统的工夫敏感,一旦操作系统的工夫扭转,则Timer的调度不再准确。ScheduledThreadPoolExecutor调度是基于绝对工夫的,不受操作系统工夫扭转的影响。是否捕捉异样Timer不会捕捉TimerTask抛出的异样,加上Timer又是单线程的。一旦某个调度工作出现异常,则整个线程就会终止,其余须要调度的工作也不再执行。ScheduledThreadPoolExecutor基于线程池来实现调度性能,某个工作抛出异样后,其余工作仍能失常执行。工作是否具备优先级Timer中执行的TimerTask工作整体上没有优先级的概念,只是依照零碎的相对工夫来执行工作。ScheduledThreadPoolExecutor中执行的ScheduledFutureTask类实现了java.lang.Comparable接口和java.util.concurrent.Delayed接口,这也就阐明了ScheduledFutureTask类中实现了两个十分重要的办法,一个是java.lang.Comparable接口的compareTo办法,一个是java.util.concurrent.Delayed接口的getDelay办法。在ScheduledFutureTask类中compareTo办法办法实现了工作的比拟,间隔下次执行的工夫距离短的工作会排在后面,也就是说,间隔下次执行的工夫距离短的工作的优先级比拟高。而getDelay办法则可能返回间隔下次工作执行的工夫距离。是否反对对工作排序Timer不反对对工作的排序。ScheduledThreadPoolExecutor类中定义了一个动态外部类DelayedWorkQueue,DelayedWorkQueue类实质上是一个有序队列,为须要调度的每个工作依照间隔下次执行工夫距离的大小来排序是否获取返回的后果Timer中执行的TimerTask类只是实现了java.lang.Runnable接口,无奈从TimerTask中获取返回的后果。ScheduledThreadPoolExecutor中执行的ScheduledFutureTask类继承了FutureTask类,可能通过Future来获取返回的后果。通过以上对ScheduledThreadPoolExecutor类和Timer类的剖析比照,置信在JDK 1.5之后,就没有应用Timer来实现定时任务调度的必要了。 二者简略的示例这里,给出应用Timer和ScheduledThreadPoolExecutor实现定时调度的简略示例,为了简便,我这里就间接应用匿名外部类的模式来提交工作。 Timer类简略示例源代码示例如下所示。 package io.binghe.concurrent.lab09;import java.util.Timer;import java.util.TimerTask;/** * @author binghe * @version 1.0.0 * @description 测试Timer */public class TimerTest { public static void main(String[] args) throws InterruptedException { Timer timer = new Timer(); timer.scheduleAtFixedRate(new TimerTask() { @Override public void run() { System.out.println("测试Timer类"); } }, 1000, 1000); Thread.sleep(10000); timer.cancel(); }}运行后果如下所示。 测试Timer类测试Timer类测试Timer类测试Timer类测试Timer类测试Timer类测试Timer类测试Timer类测试Timer类测试Timer类ScheduledThreadPoolExecutor类简略示例源代码示例如下所示。 package io.binghe.concurrent.lab09;import java.util.concurrent.*;/** * @author binghe * @version 1.0.0 * @description 测试ScheduledThreadPoolExecutor */public class ScheduledThreadPoolExecutorTest { public static void main(String[] args) throws InterruptedException { ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(3); scheduledExecutorService.scheduleAtFixedRate(new Runnable() { @Override public void run() { System.out.println("测试测试ScheduledThreadPoolExecutor"); } }, 1, 1, TimeUnit.SECONDS); //主线程休眠10秒 Thread.sleep(10000); System.out.println("正在敞开线程池..."); // 敞开线程池 scheduledExecutorService.shutdown(); boolean isClosed; // 期待线程池终止 do { isClosed = scheduledExecutorService.awaitTermination(1, TimeUnit.DAYS); System.out.println("正在期待线程池中的工作执行实现"); } while(!isClosed); System.out.println("所有线程执行完结,线程池敞开"); }}运行后果如下所示。 ...

October 12, 2022 · 1 min · jiezi

关于高并发:教你如何让自己的商城系统扛得住高并发而不崩溃

本文次要围绕一下3个方面带大家理解高并发 1.什么是商城高并发 2.如何进步商城并发量 3.具备高并发的劣势和益处 一、什么是商城高并发? 高并发(High Concurrency)是零碎运行过程中遇到的一种“短时间内大量操作申请”的状况,次要产生在web零碎中通过大量拜访收到大量申请时(例如12306的抢票状况;双十一流动)。这种状况的产生会导致系统在此期间执行大量的操作,比方申请资源、数据库操作等。 高并发也是互联网分布式系统架构设计中必须思考的因素之一,它通常是指,通过设计保证系统可能同时并行处理很多申请。 高并发相干罕用的一些指标有响应工夫(Response Time),吞吐量(Throughput),每秒查问率QPS(Query Per Second),并发用户数等。 响应工夫:系统对申请做出响应的工夫。例如零碎解决一个HTTP申请须要200ms,这个200ms就是零碎的响应工夫。 吞吐量:单位工夫内解决的申请数量。 QPS:每秒响应申请数。在互联网畛域,这个指标和吞吐量辨别的没有这么显著。 并发用户数:同时承载失常应用零碎性能的用户数量。例如一个即时通讯零碎,同时在线量肯定水平上代表了零碎的并发用户数。 二、CRMEBpro商城零碎是如何进步并发率的呢? 1.分布式缓存:redis、memcached等,联合CDN解决图片文件的拜访等。 2.音讯队列中间件:activeMQ等。,能够解决大量音讯的异步解决能力。 3.利用拆分:将一个我的项目拆分成多个我的项目部署,用dubbo解决多个我的项目之间的通信。 4.数据库垂直拆分和程度拆分(子数据库和子表)等。 5.将数据库的读写离开,解决大数据的查问问题。 6.也能够用nosql,比方mongoDB联合mysql。 7.还须要建设大数据接入状况下的服务降级和限流机制。 8.采纳swoole架构,多线程Reactor+多过程Worker,因为reactor基于epoll,所以每个reactor能够解决无数个连贯申请。 9.加强单机硬件性能(优先):例如:减少CPU核数如32核,降级更好的网卡如万兆,降级更好的硬盘如SSD,裁减硬盘容量如2T,裁减零碎内存如128G。 三、高并发商城的劣势和益处 1、速度劣势: 多处理器:多处理器上的并发无疑会让程序运行的更快。 单处理器:如果是单处理器的机器,那么并发编程和程序编程相比可能没有什么变动。然而,如果其中一个工作可能被阻塞,即便是单处理器,应用并发编程也会带来很大的益处,这样当一个工作被阻塞时,其余工作能够持续运行。 反馈灵活的用户界面:单处理器上性能改良的最典型的例子是“事件驱动编程”,例如创立一个带有按钮的响应性用户界面。如果咱们不应用并发编程,那么咱们须要在咱们编写的每个代码段中检测用户输出。如果咱们应用并发编程,咱们只须要重启一个线程来监听用户输出。 并发实现:实现并发最间接的形式是在操作系统层面,应用过程,一个自蕴含的程序,应用本人的地址空间。操作系统会将过程互相隔离,所以对过程编程绝对容易,不须要思考共享资源的同步问题。然而在Java的并发编程中,因为线程共享内存或IO等雷同的资源,所以在Java多线程编程中要思考共享资源的同步问题。 过程和Java线程之间的抉择:过程确实是一种实现并发的形式,butunfortunately there are generally quantity and overhead limitations toprocesses that prevent their applicability across the concurrency spectrum. 2、设计上的劣势: 一般来说,线程使得你可能创立更加松耦合的设计。 单处理器:只管单处理器下面的并发编程在同一时刻处理器依然只能做一件事件,然而带来一个组织下面的重要劣势:就是你的设计(design)会极大地简化。比方仿真。 仿真举例:如果没有并发,仿真将变得十分艰难。 一般来说仿真波及到多个交互元素,其中每一个都有“本人的想法”,只管从程序员的角度来看每一个仿真元素都是被同一个处理器所驱动,然而设计上来看,每一个仿真元素都伪装有本人的处理器以及运行独立的工作。 总结: 咱们应用redis,音讯队列,读写拆散,swoole架构,集群部署,进步服务器配置等就能进步咱们商城的并发量,反对了高并发,咱们就能够发展各种商城流动不再放心因为用户量大而导致的服务器瘫痪造成的损失。如果商城不反对高并发,在秒杀等货送的时候可能将库存秒杀成正数,服务器内存爆满和瘫痪等问题。CRMEB PRO商城完满解决了这些问题。

October 11, 2022 · 1 min · jiezi

关于高并发:高并发通过源码深度解析ThreadPoolExecutor类是如何保证线程池正确运行的

大家好,我是冰河~~ 对于线程池的外围类ThreadPoolExecutor来说,有哪些重要的属性和外部类为线程池的正确运行提供重要的保障呢? ThreadPoolExecutor类中的重要属性在ThreadPoolExecutor类中,存在几个十分重要的属性和办法,接下来,咱们就介绍下这些重要的属性和办法。 ctl相干的属性AtomicInteger类型的常量ctl是贯通线程池整个生命周期的重要属性,它是一个原子类对象,次要用来保留线程的数量和线程池的状态,咱们看下与这个属性相干的代码如下所示。 //次要用来保留线程数量和线程池的状态,高3位保留线程状态,低29位保留线程数量private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));//线程池中线程的数量的位数(32-3)private static final int COUNT_BITS = Integer.SIZE - 3;//示意线程池中的最大线程数量//将数字1的二进制值向右移29位,再减去1private static final int CAPACITY = (1 << COUNT_BITS) - 1;//线程池的运行状态private static final int RUNNING = -1 << COUNT_BITS;private static final int SHUTDOWN = 0 << COUNT_BITS;private static final int STOP = 1 << COUNT_BITS;private static final int TIDYING = 2 << COUNT_BITS;private static final int TERMINATED = 3 << COUNT_BITS;//获取线程状态private static int runStateOf(int c) { return c & ~CAPACITY; }//获取线程数量private static int workerCountOf(int c) { return c & CAPACITY; }private static int ctlOf(int rs, int wc) { return rs | wc; }private static boolean runStateLessThan(int c, int s) { return c < s;}private static boolean runStateAtLeast(int c, int s) { return c >= s;}private static boolean isRunning(int c) { return c < SHUTDOWN;}private boolean compareAndIncrementWorkerCount(int expect) { return ctl.compareAndSet(expect, expect + 1);}private boolean compareAndDecrementWorkerCount(int expect) { return ctl.compareAndSet(expect, expect - 1);}private void decrementWorkerCount() { do {} while (! compareAndDecrementWorkerCount(ctl.get()));}对于线程池的各状态阐明如下所示。 ...

August 30, 2022 · 3 min · jiezi

关于高并发:一次线上事故我顿悟了异步的精髓

在高并发的场景下,异步是一个极其重要的优化方向。 前段时间,生产环境产生一次事变,笔者认为事变的场景十分具备典型性 。 写这篇文章,笔者想和大家深入探讨该场景的架构优化计划。心愿大家读完之后,能够对异步有更粗浅的了解。 1 业务场景老师登录教研平台,会看到课程列表,点击课程后,课程会以视频的模式展示进去。 拜访课程详情页面,蕴含两个外围动作: 读取课程视频信息 : 从缓存服务器 Redis 获取课程的视频信息 ,返回给前端,前端通过视频组件渲染。 写入课程观看行为记录 : 当老师观看视频的过程中,浏览器每隔3秒发动申请,教研服务将观看行为记录插入到数据库表中。而且随着用户在线人数越多,写操作的频率也会指数级增长。 上线初期,这种设计运行还算良好,但随着在线用户的增多,零碎响应越来越慢,大量线程阻塞在写入视频观看进度表上的 Dao 办法。上。 首先咱们会想到一个十分直观的计划,晋升写入数据库的能力。 优化 SQL 语句;晋升 MySQL 数据库硬件配置 ;分库分表。这种计划其实也能够满足咱们的需要,然而通过扩容硬件并不便宜,另外写操作能够容许适当提早和失落大量数据,那这种计划更显得性价比有余。 那么架构优化的方向应该是:“缩小写动作的耗时,晋升写动作的并发度”, 只有这样能力让零碎更顺畅的运行。 于是,咱们想到了第二种计划:写申请异步化。 线程池模式本地内存 + 定时工作MQ 模式Agent 服务 + MQ 模式2 线程池模式2014年,笔者在艺龙旅行网负责红包零碎相干工作。经营零碎会调用红包零碎给特定用户发送红包,当这些用户登录 app 后,app 端会调用红包零碎的激活红包接口 。 激活红包接口是一个写操作,速度也比拟快(20毫秒左右),接口的日申请量在2000万左右。 利用拜访高峰期,红包零碎会变得不稳固,激活接口常常超时,笔者为了疾速解决问题,采取了一个十分毛糙的计划: "控制器收到申请后,将写操作放入到独立的线程池中后,立刻返回给前端,而线程池会异步执行激活红包办法"。 坦白的讲,这是一个十分无效的计划,优化后,红包零碎十分稳固。 回到教研的场景,见下图,咱们也能够设计相似线程池模型的计划: 应用线程池模式,须要留神如下几点: 线程数不宜过高,防止占用过多的数据库连接池 ;须要思考评估线程池队列的大小,免得呈现内存溢出的问题。3 本地内存 + 定时工作开源中国统计浏览数的计划十分经典。 用户拜访过一次文章、新闻、代码详情页面,拜访次数字段加 1 , 在 oschina 上这个操作是异步的,拜访的时候只是将数据在内存中保留,每隔固定工夫将这些数据写入数据库。 示例代码如下: 咱们能够借鉴开源中国的计划 : 控制器接收申请后,观看进度信息存储到本地内存 LinkedBlockingQueue 对象里;异步线程每隔1分钟从队列里获取数据 ,组装成 List 对象,最初调用 Jdbc batchUpdate 办法批量写入数据库;批量写入次要是为了晋升零碎的整体吞吐量,每次批量写入的 List 大小也不宜过大 。这种计划长处是:不改变原有业务架构,简略易用,性能也高。该计划同样须要思考内存溢出的危险。 ...

July 11, 2022 · 1 min · jiezi

关于高并发:多年亿级流量下的高并发经验总结我毫无保留的写在了这本书中

大家好,我是冰河~~ 明天,给大家隆重的发表一个好消息:继出版《海量数据处理与大数据技术实战》、《MySQL技术大全:开发、优化与运维实战》和《深刻了解分布式事务:原理与实战》之后,冰河的第4本书——《深刻了解高并发编程:外围原理与案例实战》正式上市了。 明天,我正式为大家送上本书的首发,全书彩印,全彩插图。更为重要的是,明天我为大家争取到了全网最低价5折优惠!!!!! 情谊提醒:全网最低价5折优惠名额有限,先到先得,截止到2022-06-20,想要全网最低价5折优惠的小伙伴能够先拖到文末二维码下单,手慢无,锁定订单后再回来听我啰嗦。 写作背景回想起为何写这本书,记得我刚开明 冰河技术 微信公众号时,连载的就是 【精通高并发系列】的专栏文章。很多小伙伴反馈说在我公众号里可能零碎的学习高并发编程的常识了。之所以我会连载【精通高并发系列】的专栏文章,是因为并发编程对于程序员来说,始终是一项十分头疼的技术。并发编程并不像其余业务那样简单明了。在编写并发程序时,往往会呈现各种诡异的Bug问题,这种Bug问题会经常以某种诡异的景象呈现,这种景象又会迅速隐没,并且这种问题在大部分场景下又很难复现。 起初,我将公众号里【精通高并发系列】的专栏文章,整顿成《深刻了解高并发编程》开源小册。这本《深刻了解高并发编程》开源小册和其余的并发编程电子书不同,它涵盖了源码解析、根底案例、实战案例、面试和零碎架构的常识,不仅有实践,更有实战案例和企业级的实在架构场景。一经公布,便火便全网。 只管这本开源小册在网上很火,然而对于我来说,这本开源小册并没有达到我心中现实的模样。我想把它彻底重构、甚至是重写来为大家提供一本更加零碎、更具深度、场景更贴近理论的并发编程书籍。于是,我便开始了对本书的写作构思。 全书构造本书从理论需要登程,全面并且粗疏地介绍了无关高并发编程的基础知识、外围原理、实战案例和零碎架构的相干常识。 根底篇:介绍了操作系统线程调度的相干常识和并发编程的基础知识。操作系统线程调度的常识蕴含:冯诺依曼计算机模型、CPU架构、操作系统线程和Java与操作系统线程的关系。并发编程的基础知识蕴含:并发编程的基本概念、并发编程的危险和并发编程中的锁等。 外围原理篇:以大量图解的形式具体介绍了并发编程中各项技术的外围原理。涵盖并发编程的三大外围问题、并发编程的实质问题、原子性的外围原理、可见性与有序性的外围原理、synchronized外围原理、AQS外围原理、Lock锁外围原理、CAS外围原理、死锁外围原理、锁优化、线程池外围原理和ThreadLocal外围原理。深刻了解外围原理篇的相干技术,有助于更好的了解高并发编程。 实战案例篇:在外围原理篇的根底上,实现了四个残缺的实战案例。蕴含:手动开发线程池实战、基于CAS实现自旋锁实战、基于读写锁实现缓存实战和基于AQS实现可重入锁实战。每个实战案例都是外围原理篇的落地实现,把握这四个实战案例的实现形式,有助于更好的在理论我的项目中开发高并发程序。 零碎架构篇:以高并发、大流量场景下典型的分布式锁架构和秒杀零碎架构为例,深刻分析了分布式锁和秒杀零碎的架构细节,使读者可能站在更高的架构层面来了解高并发编程。 总之,每个章节依据理论须要配有相干的原理图和流程图,在实战案例篇章,本书会提供残缺的实战案例源码。书中的每个解决方案都通过高并发大流量的生产环境的考验,能够间接拿来解决生产环境理论产生的高并发问题。通过对本书的浏览和学习,可能让读者更加全面、深刻、透彻的了解高并发编程的基础知识,外围原理,实战案例和零碎架构,进步高并发编程问题的解决能力和我的项目的实战能力,以及站在更高层面思考高并发编程零碎架构的能力。 本书特色本书与其余并发编程书籍不同的中央在于:力求让读者在欠缺基础理论常识的同时,兼顾晋升并发实战能力,以及站在更高层面思考高并发编程零碎架构的能力。 1.成体系介绍高并发编程的图书 纵观整个图书市场,简直找不到一本全面并且粗疏地介绍无关高并发编程的基础知识、外围原理、实战案例和零碎架构的图书,本书从以上四个方面全面、粗疏并且层层递进的介绍了高并发编程的基础知识、各项技术的外围原理和实战案例以及零碎架构的相干常识。 2.大量图解和开发案例 为了不便读者的了解,笔者在介绍高并发编程的基础知识、外围原理和零碎架构章节中会配有大量的图解和图表,在实战案例章节中会配有残缺的高并发编程案例,读者依照本书的案例进行学习,并运行本书的案例代码,可能更加深刻的了解和把握高并发编程相干的常识。另外,这些案例代码和图解的draw.io原文件,会一起收录于随书材料里。读者也能够从上面的链接获取残缺的实战案例源码和相干的随书材料。 GitHub:https://github.com/binghe001/mykit-concurrent-principle。Gitee:https://gitee.com/binghe001/mykit-concurrent-principle。3.技术点全面 本书中,全面并且粗疏的介绍了高并发编程的各项常识,蕴含高并发编程的基础知识、外围原理、实战案例,零碎架构。通过本书的学习,读者可能全面的把握高并发编程的原理和利用。 4.案例应用性强 本书中对于高并发编程的各项技术点都配有相干的案例,都是实现高并发编程相干技术点的典型案例,具备很强的实用性,不便读者随时查阅和参考。 5.具备较高的实用价值 本书中大量的实战案例来源于笔者理论的工作总结,尤其是本书中实战案例篇与零碎架构篇波及的内容,均来源于笔者的理论工作经验总结,书中的残缺案例稍加批改与欠缺便可利用于理论的生产环境中。 本书浏览答疑 弱小的专家阵容举荐 如何购买本书说了这么多,置信小伙伴们最关怀的就是如何购买本书了。这里,冰河为大家申请了全网最低价5折优惠,名额有限,先到先得,截止到2022-06-20,具体关注 冰河技术 微信公众号或者加我微信 hacker_binghe 具体理解。 好了,明天就到这儿吧,我是冰河,咱们下期见~~

June 17, 2022 · 1 min · jiezi

关于高并发:千万级高并发进阶笔记基础实战解决方案都有了

市面上Java相干的书籍,大多比拟适宜初学者,只涵盖根底内容,并不多见那种深刻某个高级主题并富裕思想性的专题书籍。尽管本书对读者的Java根底有肯定的要求,但这本书胜在内容丰盛,解说深入浅出,置信对于这个专题有趣味的读者肯定不会感到乏味和艰涩。 须要获取的小伙伴能够【点击此处】即可收费获取到! Java高并发编程详解,多线程架构设计第一局部多线程根底 疾速意识线程深刻了解Thread构造函数Thread API的具体介绍线程平安与数据同步ThreadGroup具体解说Hook线程以及捕捉线程执行异样线程池原理以及自定义线程池 [外链图片转存失败,源站可能有防盗链机制,倡议将图片保留下来间接上传(img-YmSROT79-1655207562137)(https://upload-images.jianshu...)] [外链图片转存失败,源站可能有防盗链机制,倡议将图片保留下来间接上传(img-CAySOvJj-1655207562145)(https://upload-images.jianshu...)] 第二局部 Java ClassLoader 类的加载过程JVM类加载器线程上下文类加载器 第三局部 深刻了解volatile关键字 volatile关键字的介绍深刻volatile关键字7种单例设计模式的设计 [外链图片转存失败,源站可能有防盗链机制,倡议将图片保留下来间接上传(img-P4o3ETya-1655207562157)(https://upload-images.jianshu...)] 第四局部 多线程设计架构模式 监控工作的生命周期Single Thread Execution设计模式读写锁拆散设计模式不可变对象设计模式Future设计模式Guarded Suspension设计模式线程上下文设计模式Balking设计模式Latch设计模式Thread-Per-Message设计模式Twe Phase Termination设计模式Worker-Thread设计模式Active Objects设计模式Event Bus设计模式Event Driv设计模式 ...

June 14, 2022 · 1 min · jiezi

关于高并发:千万级高并发进阶笔记涵盖基础实战解决方案

市面上Java相干的书籍,大多比拟适宜初学者,只涵盖根底内容,并不多见那种深刻某个高级主题并富裕思想性的专题书籍。尽管本书对读者的Java根底有肯定的要求,但这本书胜在内容丰盛,解说深入浅出,置信对于这个专题有趣味的读者肯定不会感到乏味和艰涩。 须要获取的小伙伴能够间接【点击此处】即可收费获取!! Java高并发编程详解,多线程架构设计第一局部多线程根底 第二局部 Java ClassLoader 第三局部 深刻了解volatile关键字 第四局部 多线程设计架构模式 如果看完这些局部内容截图还是不太懂得话上面会给大家展现一下目录:须要获取的小伙伴能够间接【点击此处】即可收费获取!!

May 28, 2022 · 1 min · jiezi

关于高并发:高并发从源码角度分析创建线程池究竟有哪些方式

大家好,我是冰河~~ 在Java的高并发畛域,线程池始终是一个绕不开的话题。有些童鞋始终在应用线程池,然而,对于如何创立线程池仅仅停留在应用Executors工具类的形式,那么,创立线程池到底存在哪几种形式呢?就让咱们一起从创立线程池的源码来深入分析到底有哪些形式能够创立线程池。 应用Executors工具类创立线程池在创立线程池时,初学者用的最多的就是Executors 这个工具类,而应用这个工具类创立线程池时非常简单的,不须要关注太多的线程池细节,只须要传入必要的参数即可。Executors 工具类提供了几种创立线程池的办法,如下所示。 Executors.newCachedThreadPool:创立一个可缓存的线程池,如果线程池的大小超过了须要,能够灵便回收闲暇线程,如果没有可回收线程,则新建线程Executors.newFixedThreadPool:创立一个定长的线程池,能够控制线程的最大并发数,超出的线程会在队列中期待Executors.newScheduledThreadPool:创立一个定长的线程池,反对定时、周期性的工作执行Executors.newSingleThreadExecutor: 创立一个单线程化的线程池,应用一个惟一的工作线程执行工作,保障所有工作依照指定程序(先入先出或者优先级)执行Executors.newSingleThreadScheduledExecutor:创立一个单线程化的线程池,反对定时、周期性的工作执行Executors.newWorkStealingPool:创立一个具备并行级别的work-stealing线程池其中,Executors.newWorkStealingPool办法是Java 8中新增的创立线程池的办法,它可能为线程池设置并行级别,具备更高的并发度和性能。除了此办法外,其余创立线程池的办法实质上调用的是ThreadPoolExecutor类的构造方法。 例如,咱们能够应用如下代码创立线程池。 Executors.newWorkStealingPool();Executors.newCachedThreadPool();Executors.newScheduledThreadPool(3);应用ThreadPoolExecutor类创立线程池从代码构造上看ThreadPoolExecutor类继承自AbstractExecutorService,也就是说,ThreadPoolExecutor类具备AbstractExecutorService类的全副性能。 既然Executors工具类中创立线程池大部分调用的都是ThreadPoolExecutor类的构造方法,所以,咱们也能够间接调用ThreadPoolExecutor类的构造方法来创立线程池,而不再应用Executors工具类。接下来,咱们一起看下ThreadPoolExecutor类的构造方法。 ThreadPoolExecutor类中的所有构造方法如下所示。 public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) { this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, Executors.defaultThreadFactory(), defaultHandler);}public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory) {this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, defaultHandler);}public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler) {this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, Executors.defaultThreadFactory(), handler);}public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) { if (corePoolSize < 0 || maximumPoolSize <= 0 || maximumPoolSize < corePoolSize || keepAliveTime < 0) throw new IllegalArgumentException(); if (workQueue == null || threadFactory == null || handler == null) throw new NullPointerException(); this.acc = System.getSecurityManager() == null ? null : AccessController.getContext(); this.corePoolSize = corePoolSize; this.maximumPoolSize = maximumPoolSize; this.workQueue = workQueue; this.keepAliveTime = unit.toNanos(keepAliveTime); this.threadFactory = threadFactory; this.handler = handler;}由ThreadPoolExecutor类的构造方法的源代码可知,创立线程池最终调用的构造方法如下。 ...

May 26, 2022 · 3 min · jiezi

关于高并发:高并发深度解析线程池中那些重要的顶层接口和抽象类

大家好,我是冰河~~ 在上一篇《【高并发】不得不说的线程池与ThreadPoolExecutor类浅析》一文中,从整体上介绍了Java的线程池。如果细细品味线程池的底层源码实现,你会发现整个线程池体系的设计是十分优雅的!这些代码的设计值得咱们去细细品味和钻研,从中学习优雅代码的设计规范,造成本人的设计思维,为我所用!哈哈,说多了,接下来,咱们就来看看线程池中那些十分重要的接口和抽象类,深度剖析下线程池中是如何将形象这一思维使用的酣畅淋漓的! 通过对线程池中接口和抽象类的剖析,你会发现,整个线程池设计的是如此的优雅和弱小,从线程池的代码设计中,咱们学到的不只是代码而已!! 题外话:膜拜Java大神Doug Lea,Java中的并发包正是这位老爷子写的,他是这个世界上对Java影响力最大的一个人。 一、接口和抽象类总览说起线程池中提供的重要的接口和抽象类,基本上就是如下图所示的接口和类。 接口与类的简略阐明: Executor接口:这个接口也是整个线程池中最顶层的接口,提供了一个无返回值的提交工作的办法。ExecutorService接口:派生自Executor接口,扩大了很过性能,例如敞开线程池,提交工作并返回后果数据、唤醒线程池中的工作等。AbstractExecutorService抽象类:派生自ExecutorService接口,实现了几个十分实现的办法,供子类进行调用。ScheduledExecutorService定时工作接口,派生自ExecutorService接口,领有ExecutorService接口定义的全副办法,并扩大了定时工作相干的办法。接下来,咱们就别离从源码角度来看下这些接口和抽象类从顶层设计上提供了哪些性能。 二、Executor接口Executor接口的源码如下所示。 public interface Executor { //提交运行工作,参数为Runnable接口对象,无返回值 void execute(Runnable command);}从源码能够看出,Executor接口非常简单,只提供了一个无返回值的提交工作的execute(Runnable)办法。 因为这个接口过于简略,咱们无奈得悉线程池的执行后果数据,如果咱们不再应用线程池,也无奈通过Executor接口来敞开线程池。此时,咱们就须要ExecutorService接口的反对了。 三、ExecutorService接口ExecutorService接口是非定时工作类线程池的外围接口,通过ExecutorService接口可能向线程池中提交工作(反对有返回后果和无返回后果两种形式)、敞开线程池、唤醒线程池中的工作等。ExecutorService接口的源码如下所示。 package java.util.concurrent;import java.util.List;import java.util.Collection;public interface ExecutorService extends Executor { //敞开线程池,线程池中不再承受新提交的工作,然而之前提交的工作持续运行,直到实现 void shutdown(); //敞开线程池,线程池中不再承受新提交的工作,会尝试进行线程池中正在执行的工作。 List<Runnable> shutdownNow(); //判断线程池是否曾经敞开 boolean isShutdown(); //判断线程池中的所有工作是否完结,只有在调用shutdown或者shutdownNow办法之后调用此办法才会返回true。 boolean isTerminated(); //期待线程池中的所有工作执行完结,并设置超时工夫 boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException; //提交一个Callable接口类型的工作,返回一个Future类型的后果 <T> Future<T> submit(Callable<T> task); //提交一个Callable接口类型的工作,并且给定一个泛型类型的接管后果数据参数,返回一个Future类型的后果 <T> Future<T> submit(Runnable task, T result); //提交一个Runnable接口类型的工作,返回一个Future类型的后果 Future<?> submit(Runnable task); //批量提交工作并取得他们的future,Task列表与Future列表一一对应 <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) throws InterruptedException; //批量提交工作并取得他们的future,并限定解决所有工作的工夫 <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException; //批量提交工作并取得一个曾经胜利执行的工作的后果 <T> T invokeAny(Collection<? extends Callable<T>> tasks) throws InterruptedException, ExecutionException; //批量提交工作并取得一个曾经胜利执行的工作的后果,并限定解决工作的工夫 <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;}对于ExecutorService接口中每个办法的含意,间接上述接口源码中的正文即可,这些接口办法都比较简单,我就不一一反复列举形容了。这个接口也是咱们在应用非定时工作类的线程池中最常应用的接口。 ...

May 10, 2022 · 4 min · jiezi

关于高并发:没有高并发项目经验但经常被问高并发性能调优问题怎么办

这篇文章对很多没有高并发教训的程序员来说,会十分有帮忙。 很多程序员可能都遇到过相似的困惑: 我没有高并发我的项目教训,然而面试的时候常常被问到高并发、性能调优方面的问题,该怎么办?明天给大家说一本人学习高并发的办法。 你能够本人写一个小的电商我的项目,倡议最简略的单体构造的电商我的项目即可。 从最简略的单体我的项目开始,而后依照以下三个阶段来学习高并发。 第一阶段在高并发条件下,学习对单机性能进行优化。 用 Docker 容器先去运行电商我的项目,而后再用 jmeter、wrk 等工具去压测。 在压测期间,你会发现:因为零碎每个模块不同,所以性能体现就不一样。 这是失常的,不同模块、不同产品对并发指标的要求自身想·是不一样的。例如,商品浏览和下订单,一个读为主,一个写为主。 基于这种状况,你最好要编写简单的压测脚本,能主动实现不同模块的压测工作。 而后在这种一直地压测探测下,去探测问题,并且通过优化代码、JVM 去解决问题。 比方,解决误用 HashMap 导致死循环的问题。又比方,误用不带缓存的文件 IO 流氓,去读取文件的问题等等。 该程序和 JVM 优化结束后,你可能又会发现数据库也存在问题。于是,你又要去钻研如何优化数据库 SQL,如何对数据库分表等问题。 也是在这个阶段,你可能还会学到,缓存的必要性以及同步缓存数据状态的重要性等重要知识点。 在搞了单机优化后,没有方法再通过单机的压测学到什么新的货色了。于是,转向第二阶段。 第二阶段从阿里云买了两台机器,开始尝试应用负载平衡去分担高并发的压力。 同样的,也是借助压测工具去模仿了高并发。在压测期间,负载平衡和零碎每每呈现和单机齐全不一样的问题。 比方,负载平衡自身的性能问题。比方,在一些时候,负载平衡前面的机器负载是不均衡的,须要对负载算法进行调整。 这个阶段,你会接触到负载平衡中大部分的细节。 然而,高并发中,很多零碎的形成会很简单,以至于须要分布式架构零碎的水平。他们须要各种中间件做通信,做存储。 所以,持续第三阶段的练习。 第三阶段为了能相熟市面上各种中间件的应用,开始对单体的电商平台进行革新。 比方,把一些本地调用的办法,替换成 Dubbo 近程调用。 比方,将一些模块间接调用,替换成 MQ 中间件传音讯。 再比方,一些放在关系数据库的被频繁拜访的数据,改存在 MongoDB 中…… 当然,压测仍然持续。就这样,你能够实际到很多中间件和分布式框架的应用。 在模仿高并发练习的同时,别忘了去读各种高并发高性能的书籍。比方,《大型网站服务器容量布局》、《互联网守业核心技术:构建可伸缩的web利用》等书籍。 三个阶段的学习之后,面试的大部分根底问题你根本能够应酬了。 毕竟在程序员这个圈子里,90% 以上的人可能都没有真正的高并发教训。作为面试官来说: 为什么咱们须要找有高并发教训的人? 说白了,咱们想找的程序员其实就是: 不会乱写性能很差的代码能敏锐地感知到影响零碎的问题能独立的解决因为高并发引发的问题咱们找相熟高可用的人,其实并不要求这个人肯定能给出什么独特的高可用计划。咱们要求的是,他能晓得高可用的常识后,去意识到高可用的重要性。 比方限流性能呈现问题,他要能马上意识到这是个很重要的问题,从而把解决的优先级提到很高。 通过以上三个阶段的学习和练习,根本是能够把握这些技能的,这就够了,剩下的细节,就靠在理论工作再实际吧。 此外也心愿各位面试官,在招人的时候,如果遇到好苗子能够适当宽容一些,给新人们一点机会。

May 7, 2022 · 1 min · jiezi

关于高并发:高并发不得不说的线程池与ThreadPoolExecutor类浅析

大家好,我是冰河~~ 明天,咱们一起来简略聊聊线程池中的ThreadPoolExecutor类,好了,不多说了,开始进入明天的正题。 一、抛砖引玉既然Java中反对以多线程的形式来执行相应的工作,但为什么在JDK1.5中又提供了线程池技术呢?这个问题大家自行脑补,多动脑,必定没害处,哈哈哈。。。 说起Java中的线程池技术,在很多框架和异步解决中间件中都有波及,而且性能禁受起了短暂的考验。能够这样说,Java的线程池技术是Java最外围的技术之一,在Java的高并发畛域中,Java的线程池技术是一个永远绕不开的话题。既然Java的线程池技术这么重要(怎么能说是这么重要呢?那是相当的重要,那家伙老重要了,哈哈哈),那么,本文咱们就来简略的说下线程池与ThreadPoolExecutor类。至于线程池中的各个技术细节和ThreadPoolExecutor的底层原理和源码解析,咱们会在【高并发专题】专栏中进行深度解析。 引言:本文是高并发中线程池的开篇之作,就临时先不深刻解说,只是让大家从整体上意识下线程池中最外围的类之一——ThreadPoolExecutor,对于ThreadPoolExecutor的底层原理和源码实现,以及线程池中的其余技术细节的底层原理和源码实现,咱们会在【高并发专题】接下来的文章中,进行死磕。 二、Thread间接创立线程的弊病(1)每次new Thread新建对象,性能差。 (2)线程不足对立治理,可能无限度的新建线程,相互竞争,有可能占用过多系统资源导致死机或OOM。 (3)短少更多的性能,如更多执行、定期执行、线程中断。 (4)其余弊病,大家自行脑补,多动脑,没害处,哈哈。 三、线程池的益处(1)重用存在的线程,缩小对象创立、沦亡的开销,性能佳。 (2)能够无效管制最大并发线程数,进步系统资源利用率,同时能够防止过多资源竞争,防止阻塞。 (3)提供定时执行、定期执行、单线程、并发数管制等性能。 (4)提供反对线程池监控的办法,可对线程池的资源进行实时监控。 (5)其余益处,大家自行脑补,多动脑,没害处,哈哈。 四、线程池1.线程池类构造关系线程池中的一些接口和类的构造关系如下图所示。 后文会死磕这些接口和类的底层原理和源码。 2.创立线程池罕用的类——ExecutorsExecutors.newCachedThreadPool:创立一个可缓存的线程池,如果线程池的大小超过了须要,能够灵便回收闲暇线程,如果没有可回收线程,则新建线程Executors.newFixedThreadPool:创立一个定长的线程池,能够控制线程的最大并发数,超出的线程会在队列中期待Executors.newScheduledThreadPool:创立一个定长的线程池,反对定时、周期性的工作执行Executors.newSingleThreadExecutor: 创立一个单线程化的线程池,应用一个惟一的工作线程执行工作,保障所有工作依照指定程序(先入先出或者优先级)执行Executors.newSingleThreadScheduledExecutor:创立一个单线程化的线程池,反对定时、周期性的工作执行Executors.newWorkStealingPool:创立一个具备并行级别的work-stealing线程池3.线程池实例的几种状态Running:运行状态,能接管新提交的工作,并且也能解决阻塞队列中的工作Shutdown: 敞开状态,不能再接管新提交的工作,然而能够解决阻塞队列中曾经保留的工作,当线程池处于Running状态时,调用shutdown()办法会使线程池进入该状态Stop: 不能接管新工作,也不能解决阻塞队列中曾经保留的工作,会中断正在解决工作的线程,如果线程池处于Running或Shutdown状态,调用shutdownNow()办法,会使线程池进入该状态Tidying: 如果所有的工作都曾经终止,无效线程数为0(阻塞队列为空,线程池中的工作线程数量为0),线程池就会进入该状态。Terminated: 处于Tidying状态的线程池调用terminated()办法,会应用线程池进入该状态留神:不须要对线程池的状态做非凡的解决,线程池的状态是线程池外部依据办法自行定义和解决的。 4.合理配置线程的一些倡议(1)CPU密集型工作,就须要尽量压迫CPU,参考值能够设置为NCPU+1(CPU的数量加1)。 (2)IO密集型工作,参考值能够设置为2*NCPU(CPU数量乘以2) 五、线程池最外围的类之一——ThreadPoolExecutor1.构造方法ThreadPoolExecutor参数最多的构造方法如下: public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler rejectHandler) 其余的构造方法都是调用的这个构造方法来实例化对象,能够说,咱们间接剖析这个办法之后,其余的构造方法咱们也明确是怎么回事了!接下来,就对此构造方法进行具体的剖析。 留神:为了更加深刻的剖析ThreadPoolExecutor类的构造方法,会适当调整参数的程序进行解析,以便于大家更能深刻的了解ThreadPoolExecutor构造方法中每个参数的作用。 上述构造方法接管如下参数进行初始化: (1)corePoolSize:外围线程数量。 (2)maximumPoolSize:最大线程数。 (3)workQueue:阻塞队列,存储期待执行的工作,很重要,会对线程池运行过程产生重大影响。 其中,上述三个参数的关系如下所示: 如果运行的线程数小于corePoolSize,间接创立新线程解决工作,即便线程池中的其余线程是闲暇的。如果运行的线程数大于等于corePoolSize,并且小于maximumPoolSize,此时,只有当workQueue满时,才会创立新的线程解决工作。如果设置的corePoolSize与maximumPoolSize雷同,那么创立的线程池大小是固定的,此时,如果有新工作提交,并且workQueue没有满时,就把申请放入到workQueue中,期待闲暇的线程,从workQueue中取出工作进行解决。如果运行的线程数量大于maximumPoolSize,同时,workQueue曾经满了,会通过回绝策略参数rejectHandler来指定解决策略。根据上述三个参数的配置,线程池会对工作进行如下解决形式: 当提交一个新的工作到线程池时,线程池会依据以后线程池中正在运行的线程数量来决定该工作的解决形式。解决形式总共有三种:间接切换、应用有限队列、应用有界队列。 间接切换罕用的队列就是SynchronousQueue。应用有限队列就是应用基于链表的队列,比方:LinkedBlockingQueue,如果应用这种形式,线程池中创立的最大线程数就是corePoolSize,此时maximumPoolSize不会起作用。当线程池中所有的外围线程都是运行状态时,提交新工作,就会放入期待队列中。应用有界队列应用的是ArrayBlockingQueue,应用这种形式能够将线程池的最大线程数量限度为maximumPoolSize,能够升高资源的耗费。然而,这种形式使得线程池对线程的调度更艰难,因为线程池和队列的容量都是无限的了。依据下面三个参数,咱们能够简略得出如何升高系统资源耗费的一些措施: 如果想升高系统资源的耗费,包含CPU使用率,操作系统资源的耗费,上下文环境切换的开销等,能够设置一个较大的队列容量和较小的线程池容量。这样,会升高线程解决工作的吞吐量。如果提交的工作常常产生阻塞,能够思考调用设置最大线程数的办法,从新设置线程池最大线程数。如果队列的容量设置的较小,通常须要将线程池的容量设置的大一些,这样,CPU的使用率会高些。如果线程池的容量设置的过大,并发量就会减少,则须要思考线程调度的问题,反而可能会升高解决工作的吞吐量。接下来,咱们持续看ThreadPoolExecutor的构造方法的参数。 (4)keepAliveTime:线程没有工作执行时最多放弃多久工夫终止 当线程池中的线程数量大于corePoolSize时,如果此时没有新的工作提交,外围线程外的线程不会立刻销毁,须要期待,直到期待的工夫超过了keepAliveTime就会终止。 (5)unit:keepAliveTime的工夫单位 (6)threadFactory:线程工厂,用来创立线程 默认会提供一个默认的工厂来创立线程,当应用默认的工厂来创立线程时,会使新创建的线程具备雷同的优先级,并且是非守护的线程,同时也设置了线程的名称 (7)rejectHandler:回绝解决工作时的策略 如果workQueue阻塞队列满了,并且没有闲暇的线程池,此时,持续提交工作,须要采取一种策略来解决这个工作。 线程池总共提供了四种策略: 间接抛出异样,这也是默认的策略。实现类为AbortPolicy。用调用者所在的线程来执行工作。实现类为CallerRunsPolicy。抛弃队列中最靠前的工作并执行当前任务。实现类为DiscardOldestPolicy。间接抛弃当前任务。实现类为DiscardPolicy。2.ThreadPoolExecutor提供的启动和进行工作的办法(1)execute():提交工作,交给线程池执行(2)submit():提交工作,可能返回执行后果 execute+Future(3)shutdown():敞开线程池,期待工作都执行完(4)shutdownNow():立刻敞开线程池,不期待工作执行完 3.ThreadPoolExecutor提供的实用于监控的办法(1)getTaskCount():线程池已执行和未执行的工作总数(2)getCompletedTaskCount():已实现的工作数量(3)getPoolSize():线程池以后的线程数量(4)getCorePoolSize():线程池外围线程数(5)getActiveCount():以后线程池中正在执行工作的线程数量 好了,明天就到这儿吧,我是冰河,咱们下期见~~

April 21, 2022 · 1 min · jiezi

关于高并发:高并发两种异步模型与深度解析Future接口

大家好,我是冰河~~ 本文有点长,然而满满的干货,以理论案例的模式剖析了两种异步模型,并从源码角度深度解析Future接口和FutureTask类,心愿大家踏下心来,关上你的IDE,跟着文章看源码,置信你肯定播种不小! 一、两种异步模型在Java的并发编程中,大体上会分为两种异步编程模型,一类是间接以异步的模式来并行运行其余的工作,不须要返回工作的后果数据。一类是以异步的模式运行其余工作,须要返回后果。 1.无返回后果的异步模型 无返回后果的异步工作,能够间接将工作丢进线程或线程池中运行,此时,无奈间接取得工作的执行后果数据,一种形式是能够应用回调办法来获取工作的运行后果。 具体的计划是:定义一个回调接口,并在接口中定义接管工作后果数据的办法,具体逻辑在回调接口的实现类中实现。将回调接口与工作参数一起放进线程或线程池中运行,工作运行后调用接口办法,执行回调接口实现类中的逻辑来处理结果数据。这里,给出一个简略的示例供参考。 定义回调接口package io.binghe.concurrent.lab04;/** * @author binghe * @version 1.0.0 * @description 定义回调接口 */public interface TaskCallable<T> { T callable(T t);}便于接口的通用型,这里为回调接口定义了泛型。 定义工作后果数据的封装类package io.binghe.concurrent.lab04;import java.io.Serializable;/** * @author binghe * @version 1.0.0 * @description 工作执行后果 */public class TaskResult implements Serializable { private static final long serialVersionUID = 8678277072402730062L; /** * 工作状态 */ private Integer taskStatus; /** * 工作音讯 */ private String taskMessage; /** * 工作后果数据 */ private String taskResult; //省略getter和setter办法 @Override public String toString() { return "TaskResult{" + "taskStatus=" + taskStatus + ", taskMessage='" + taskMessage + '\'' + ", taskResult='" + taskResult + '\'' + '}'; }}创立回调接口的实现类回调接口的实现类次要用来对工作的返回后果进行相应的业务解决,这里,为了不便演示,只是将后果数据返回。大家须要依据具体的业务场景来做相应的剖析和解决。 ...

March 25, 2022 · 8 min · jiezi

关于高并发:请求合并与拆分在并发场景中应用

一、序言在并发场景中,当热点缓存Key生效时,流量霎时打到数据库中,此所谓缓存击穿景象;当大范畴的缓存Key生效时,流量也会打到数据库中,此所谓缓存雪崩景象。 当应用分布式行锁时,可能无效解决缓存击穿问题;当应用分布式表锁时,可能解决缓存雪崩问题。实际操作中,分布式表锁不在思考范畴,理由是升高并发量。 本文将从另一个角度登程,将申请流量合并和拆分,以进步零碎的并发量。 二、实践根底流量的合并与拆分原理是将多条申请合并成一条申请,执行后再将后果拆分。在数据库与缓存架构中,缓存Key生效的霎时,大量反复申请打到数据库中。实际上除了第一条申请为无效申请,随后的申请为有效申请,节约数据库连贯资源。 流量的合并与拆分实际是额定唤醒一个线程,每隔固定工夫(比方200毫秒)发送合并后的申请,执行实现后将查问后果进行拆分,散发到原始申请中,原始申请响应用户申请。 从利用到数据库之间连贯资源需要显著降落,从而进步数据库连贯资源利用率。 三、利用实际(一)编码与应用基于MybatisPlus提供一个内置封装的服务类QueueServiceImpl,通明的实现查问详情流量的合并与拆分,使用者可屏蔽外部实现。 <dependency> <groupId>xin.altitude.cms</groupId> <artifactId>ucode-cms-common</artifactId> <version>1.4.4</version></dependency>对于肯定工夫区间内的所有申请,合并成一条申请解决。 @Overridepublic BuOrder getOrderById(Long orderId) { return getById(orderId);}举例说明,如果特定工夫区间内会集了雷同的主键申请,那么合并后的申请查问一次数据库便可能响应所有的申请。 子类重写父类办法,可批改合并与拆分的行为。 @Overrideprotected RequstConfig createRequstConfig() { RequstConfig config = new RequstConfig(); /* 单次最大合并申请数量 */ config.setMaxRequestSize(100); /* 外围线程池大小 */ config.setCorePoolSize(1); /* 申请距离(毫秒) */ config.setRequestInterval(200); return config;}(二)实现细节1、ConcurrentLinkedQueue应用ConcurrentLinkedQueue并发平安队列用于缓冲和接管申请,定时工作以固定频率从队列中生产数据,将多条申请条件合并后汇总查问。 2、CompletableFutureCompletableFuture类是合并与拆分的要害类,原始申请将查问条件封装成CompletableFuture对象,提交到队列中后陷入阻塞,定时工作分批次组装查问条件,失去后果后将后果拆分并存入CompletableFuture对象中,原始申请线程被唤醒,持续响应用户申请。 3、ScheduledExecutorService以肯定的工夫距离发送合并后的申请。 (二)其它利用场景利用于数据库间流量的合并申请与拆分,首先进步数据库连贯资源(稀缺资源)利用率,其次进步网络间数据传输效率。100条数据收发100次与100条数据收发1次的效率差异。 1、服务间接口调用服务间API接口调用同样实用于流量的合并与拆分:比方向订单服务发送Http API申请,同一时刻有100个用户发动查问申请,应用流量合并与拆分的思维可将多个订单查问申请转换成批查问申请,失去后果后散发到不同的申请线程,响应用户申请。 四、小结在本文中,选用的队列是本地并发平安的队列,在分布式系统中,本地队列是否适合?此处选用本地队列基于两点思考:一是无严格的分布式的需要;二是CompletableFuture类不反对序列化。思考应用Redis做分布式队列的想法无奈实现,你用本地队列,只管会有大量查问条件数据冗余(不影响后果),回避了分布式队列的网络IO提早,反而有更优的查问效率。 本计划仅在高并发场景受害,属于针对并发场景进行架构的优化,一般我的项目应用惯例操作即可。 演示我的项目代码地址 喜爱本文点个♥️赞♥️反对一下,如有须要,可通过微信dream4s与我分割。相干源码在GitHub,视频解说在B站,本文珍藏在博客天地。

March 16, 2022 · 1 min · jiezi

关于高并发:高并发深入理解线程的执行顺序

大家好,我是冰河~~ 最近常常有读者问我:冰河,线程到底是依照怎么的程序执行的呀?为了同一答复大家的这个问题,明天我就独自写一篇文章吧。好了,不多说了,进入明天的正题。 一、线程的执行程序是不确定的调用Thread的start()办法启动线程时,线程的执行程序是不确定的。也就是说,在同一个办法中,间断创立多个线程后,调用线程的start()办法的程序并不能决定线程的执行程序。 例如,这里,看一个简略的示例程序,如下所示。 package io.binghe.concurrent.lab03;/** * @author binghe * @version 1.0.0 * @description 线程的程序,间接调用Thread.start()办法执行不能确保线程的执行程序 */public class ThreadSort01 { public static void main(String[] args){ Thread thread1 = new Thread(() -> { System.out.println("thread1"); }); Thread thread2 = new Thread(() -> { System.out.println("thread2"); }); Thread thread3 = new Thread(() -> { System.out.println("thread3"); }); thread1.start(); thread2.start(); thread3.start(); }}在ThreadSort01类中别离创立了三个不同的线程,thread1、thread2和thread3,接下来,在程序中依照程序别离调用thread1.start()、thread2.start()和thread3.start()办法来别离启动三个不同的线程。 那么,问题来了,线程的执行程序是否依照thread1、thread2和thread3的程序执行呢?运行ThreadSort01的main办法,后果如下所示。 thread1thread2thread3再次运行时,后果如下所示。 thread1thread3thread2第三次运行时,后果如下所示。 thread2thread3thread1能够看到,每次运行程序时,线程的执行程序可能不同。线程的启动程序并不能决定线程的执行程序。 二、如何确保线程的执行程序1.确保线程执行程序的简略示例在理论业务场景中,有时,后启动的线程可能须要依赖先启动的线程执行实现能力正确的执行线程中的业务逻辑。此时,就须要确保线程的执行程序。那么如何确保线程的执行程序呢? 能够应用Thread类中的join()办法来确保线程的执行程序。例如,上面的测试代码。 package io.binghe.concurrent.lab03;/** * @author binghe * @version 1.0.0 * @description 线程的程序,Thread.join()办法可能确保线程的执行程序 */public class ThreadSort02 { public static void main(String[] args) throws InterruptedException { Thread thread1 = new Thread(() -> { System.out.println("thread1"); }); Thread thread2 = new Thread(() -> { System.out.println("thread2"); }); Thread thread3 = new Thread(() -> { System.out.println("thread3"); }); thread1.start(); //实际上让主线程期待子线程执行实现 thread1.join(); thread2.start(); thread2.join(); thread3.start(); thread3.join(); }}能够看到,ThreadSot02类比ThreadSort01类,在每个线程的启动办法上面增加了调用线程的join()办法。此时,运行ThreadSort02类,后果如下所示。 ...

January 17, 2022 · 2 min · jiezi

关于高并发:阿里P8推荐NettyRedisZookeeper高并发实战看完真不错

挪动时代、5G时代、物联网时代的大幕曾经开启,它们对于高性能、高并发的开发常识和技术的要求,抬升了 Java工程师的学习台阶和面试门槛。 大公司的面试题从某个侧面映射出生产场景中对专项技术的要求。高并发的面试题以前根本是BAT等大公司的专利,当初简直蔓延至与Java我的项目相干的整个行业。例如,与Java NIO、Reactor模式、高性能通信、分布式锁、分布式ID、分布式缓存、高并发架构等技术相干的面试题,从以前的加分题变成了当初的根底题,这也映射出开发Java我的项目所必须的技术栈:分布式Java框架、Redis缓存、分布式搜寻ElasticSearch、分布式协调ZooKeeper、音讯队列Kafka、高性能通信框架Netty。 《Netty、Redis、Zookeeper高并发实战》为了让大家扎稳高性能根底,浅显易懂地分析高并发IO的底层原理,粗疏细腻地解析Reactor高性能模式,图文并茂地介绍Java异步回调模式。把握这些根底原理,可能帮忙大家解决Java后盾开发的一些理论问题。 本书共12章,次要介绍高性能通信框架Netty,并详尽介绍Netty的EventLoop、Handler、Pipeline、ByteBuf、Decoder、Encoder等重要组件,而后介绍单体IM的实战设计和模块实现。本书对ZooKeeper、 Curator API、Redis、Jedis API的应用也进行详尽的介绍,让大家具备高并发、可扩大零碎的设计和开发能力。 因为内容较多,本次将展现局部截图,如果看得不过瘾想更加深刻地理解本笔记彻底把握,只需转发后【戳此处】即可来获取收费支付形式了!第一章:高并发时代的必备技能 第二章:高并发IO的底层原理 第三章:Java NIO通信根底详解 第四章:鼎鼎大名的Reactor反应器模式 第五章:并发根底中的Future异步回调模式 因为内容较多,本次将展现局部截图,如果看得不过瘾想更加深刻地理解本笔记彻底把握,只需转发后【戳此处】即可来获取收费支付形式了!第六章:Netty原理与根底 第七章:Decoder与Encoder重要组件 第八章:JSON和ProtoBuf序列化 第九章:基于Netty的单体IM零碎的开发实际 第十章:ZooKeeper分布式协调 因为内容较多,本次将展现局部截图,如果看得不过瘾想更加深刻地理解本笔记彻底把握,只需转发后【戳此处】即可来获取收费支付形式了!第十一章:分布式缓存Redis 第十二章:亿级高并发IM架构的开发实际 因为内容较多,本次将展现局部截图,如果看得不过瘾想更加深刻地理解本笔记彻底把握,只需转发后【戳此处】即可来获取收费支付形式了!

December 28, 2021 · 1 min · jiezi

关于高并发:多点-DMALL-x-StarRocks实现存储引擎的收敛保障高查询并发及低延迟要求

多点 DMALL 成立于2015年,是一站式全渠道数字批发解决方案服务商。数字化解构重构批发产业,提供端到端的商业 SaaS 解决方案。目前,多点 DMALL 已与120多家连锁零售商、品牌商等达成单干,笼罩四个国家和地区15000家门店,模式受到宽泛验证。 多点大数据部门应用 StarRocks 逐渐代替了 Impala、Impala on Kudu、Apache Kylin 等存储引擎,实现了存储引擎的收敛,简化了实时数据处理链路,同时也能保障较高的查问并发以及较低的响应提早要求。 “作者:任伟,多点生存大数据部门资深研发工程师”背景介绍多点大数据部门为外部业务研发团队、数据分析师、内部用户以及合作伙伴,提供了根底的大数据产品、平台服务,帮忙批发企业解决了从根本的数据汇总治理、对立的数据计算利用、到各种场景下对数据的多模式应用的需要,可笼罩批发企业绝大部分数据诉求。 技术层面,多点大数据部门基于 Hadoop 开源技术栈,并进行了局部二次开发后构建起了以下的一个技术架构全景图。从下到上分为基础设施层、数据源层、数据集成层、离线/实时计算层、集市层、剖析存储层、数据服务/应用层,数据开发、数据模型核心与运维管理层对各层提供反对。 基础设施层:包含超大带宽的专线网络;私有云、公有云、机房托管的混合云部署; 数据源层:包含企业 OLTP 数据库、业务数据、日志数据、三方接入数据;数据集成层:DataBus 是多点自研数据同步平台,解决企业内各业务线之间、跨企业组织之间以及跨行业的数据汇聚、交融等问题,将不同零碎的数据互相买通,实现数据自在流动; 离线计算层:利用 Hive / Spark 高可扩大的批处理能力承当离线数仓的 ETL 和数据模型加工; 实时计算层:利用 Flink / Spark Streaming 实现实时数据的 ETL(包含维度裁减,多流 Join,实时汇总)等; 离线/实时集市层:应用数仓分层模型构建 ODS(原始数据层)、DWD(数据明细层)、DWS(汇总层)、DIM(维度层)、DWT(主题层)、ADS(应用层),并依据公司业务拆分不同的数据域; 剖析存储层:次要依赖 Druid、ClickHouse、Impala on Kudu、Apache Kylin、Elasticsearch、HBase、MySQL、StarRocks 提供 OLAP 查问能力; 数据服务/应用层:该层通过提供 BI 剖析产品、数据服务接口、营销、报表类产品,向外部经营人员、内部客户、合作伙伴提供数据分析决策能力。 原有架构痛点上述架构解决了多点绝大部分数据诉求,在整个架构中,无论是基于 Hive、Spark 的离线计算,基于 Flink、Spark Streaming 的实时计算;基于 HDFS、Kafka 的存储;基于数仓分层模型建设等计划都已根本成熟。然而在 OLAP 畛域,无论是多点还是业界依然处于百家争鸣,各有千秋的状态。纵观多点在 OLAP 引擎的摸索实际中,遇到了各种各样的问题,总结起来如下: 技术老本因为下层业务场景简单,各个场景的技术难点、外围点均不一样。多点生存在整个技术架构降级的过程中先后引入了 HBase、Elasticsearch、Druid、ClickHouse、Impala on Kudu、Apache Kylin 等 OLAP 引擎。然而随着技术栈增多,技术曲线平缓,没有短缺的资源进行多技术栈的保护,造成了比拟高的技术老本。 ...

December 15, 2021 · 2 min · jiezi

关于高并发:The-art-of-multipropcessor-programming-读书笔记3-自旋锁与争用2

本系列是 The art of multipropcessor programming 的读书笔记,在原版图书的根底上,联合 OpenJDK 11 以上的版本的代码进行了解和实现。并依据集体的查资料以及了解的经验,给各位想更深刻了解的人分享一些集体的材料自旋锁与争用3. 队列锁之前实现的基于回退的锁,除了通用性以外,还有如下两个问题: CPU 高速缓存一致性流量:尽管因为回退存在,所以流量比 TASLock 要小,然而多线程拜访锁的状态还是有肯定因为缓存一致性导致的流量耗费的。可能升高拜访临界区的效率:因为所有线程的 sleep 提早过大,导致以后所有线程都在 sleep,然而锁实际上曾经开释。能够将线程放入一个队列,来解决下面两个问题: 队列中,每个线程查看它的前驱线程是否曾经实现,判断锁是否被开释,不必拜访锁的状态。这样拜访的是不同的内存,缩小了锁开释批改状态导致的 CPU 高速缓存一致性流量不须要 sleep,能够通过前驱线程告知线程锁被开释,尝试获取锁,进步了拜访临界区的效率最初,通过队列,也是实现了 FIFO 的公平性。 3.1. 基于数组的锁咱们通过一个数组来实现队列的性能,其流程是: 须要的存储: boolean 数组,为 true 则代表对应槽位的线程获取到了锁,为 false 则为对应槽位的线程没有获取到了锁保留以后最新槽位的原子变量,每次上锁都会将这个原子变量加 1,之后对 boolean 数组的大小取余。这个值代表这个线程占用了 boolean 数组的这个地位,boolean 数组的这个地位的值代表这个线程是否获取到了锁。这也阐明,boolean 数组的容量决定了这个锁同时能够有多少线程进行争用ThreadLocal,记录以后线程占用的 boolean 数组的地位上锁流程: 原子变量 + 1,对 boolean 数组的大小取余失去 current将 current 记录到 ThreadLocal当 boolean 数组 cuurent 地位的值为 false 的时候,自旋期待解锁流程: 从 ThreadLocal 中获取以后线程对应的地位 mine将 boolean 数组的 mine 地位标记为 false将 boolean 数组的 mine + 1 对数组大小取余的地位(避免数组越界)标记为 true其源码是: ...

November 6, 2021 · 2 min · jiezi

关于高并发:Interrupted-Exception异常可能没你想的那么简单

摘要: 当咱们在调用Java对象的wait()办法或者线程的sleep()办法时,须要捕捉并解决InterruptedException异样。如果咱们对InterruptedException异样处理不当,则会产生咱们意想不到的结果!本文分享自华为云社区《【高并发】由InterruptedException异样引发的思考》,作者:冰 河。 前言当咱们在调用Java对象的wait()办法或者线程的sleep()办法时,须要捕捉并解决Interrupted Exception异样。如果咱们对Interrupted Exception异样处理不当,则会产生咱们意想不到的结果! 程序案例例如,上面的程序代码,Interrupted Task类实现了Runnable接口,在run()办法中,获取以后线程的句柄,并在while(true)循环中,通过isInterrupted()办法来检测以后线程是否被中断,如果以后线程被中断就退出while(true)循环,同时,在while(true)循环中,还有一行Thread.sleep(100)代码,并捕捉了Interrupted Exception异样。整个代码如下所示。 package io.binghe.concurrent.lab08;/** * @author binghe * @version 1.0.0 * @description 线程测试中断 */public class Interrupted Task implements Runnable{ @Override public void run() { Thread currentThread = Thread.currentThread(); while (true){ if(currentThread.isInterrupted()){ break; } try { Thread.sleep(100); } catch (Interrupted Exception e) { e.printStack Trace(); } } }}上述代码的本意是通过isInterrupted()办法查看线程是否被中断了,如果中断了就退出while循环。其余线程通过调用执行线程的interrupt()办法来中断执行线程,此时会设置执行线程的中断标记位,从而使currentThread.isInterrupted()返回true,这样就可能退出while循环。 这看上去没啥问题啊!但真的是这样吗?咱们创立一个Interrupted Test类用于测试,代码如下所示。 package io.binghe.concurrent.lab08;/** * @author binghe * @version 1.0.0 * @description 测试线程中断 */public class InterruptedTest { public static void main(String[] args){ InterruptedTask interruptedTask = new InterruptedTask(); Thread interruptedThread = new Thread(interruptedTask); interruptedThread.start(); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } interruptedThread.interrupt(); }}咱们运行main办法,如下所示。 ...

October 8, 2021 · 1 min · jiezi

关于高并发:你知道线程池是如何退出程序的吗

摘要:本文,咱们就来从源码角度深度解析线程池是如何优雅的退出程序的。本文分享自华为云社区《【高并发】从源码角度深度解析线程池是如何实现优雅退出的》,作者:冰 河 。 本文,咱们就来从源码角度深度解析线程池是如何优雅的退出程序的。首先,咱们来看下ThreadPoolExecutor类中的shutdown()办法。 shutdown()办法当应用线程池的时候,调用了shutdown()办法后,线程池就不会再承受新的执行工作了。然而在调用shutdown()办法之前放入工作队列中的工作还是要执行的。此办法是非阻塞办法,调用后会立刻返回,并不会期待工作队列中的工作全副执行结束后再返回。咱们看下shutdown()办法的源代码,如下所示。 public void shutdown() { //获取线程池的全局锁 final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { //查看是否有敞开线程池的权限 checkShutdownAccess(); //将以后线程池的状态设置为SHUTDOWN advanceRunState(SHUTDOWN); //中断Worker线程 interruptIdleWorkers(); //为ScheduledThreadPoolExecutor调用钩子函数 onShutdown(); // hook for } finally { //开释线程池的全局锁 mainLock.unlock(); } //尝试将状态变为TERMINATED tryTerminate();}总体来说,shutdown()办法的代码比较简单,首先查看了是否有权限来敞开线程池,如果有权限,则再次检测是否有中断工作线程的权限,如果没有权限,则会抛出SecurityException异样,代码如下所示。 //查看是否有敞开线程池的权限checkShutdownAccess();//将以后线程池的状态设置为SHUTDOWNadvanceRunState(SHUTDOWN);//中断Worker线程interruptIdleWorkers();其中,checkShutdownAccess()办法的实现代码如下所示。 private void checkShutdownAccess() { SecurityManager security = System.getSecurityManager(); if (security != null) { security.checkPermission(shutdownPerm); final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { for (Worker w : workers) security.checkAccess(w.thread); } finally { mainLock.unlock(); } }}对于checkShutdownAccess()办法的代码了解起来比较简单,就是检测是否具备敞开线程池的权限,期间应用了线程池的全局锁。 ...

September 10, 2021 · 2 min · jiezi

关于高并发:从源码角度解析线程池中顶层接口和抽象类

摘要:咱们就来看看线程池中那些十分重要的接口和抽象类,深度剖析下线程池中是如何将形象这一思维使用的酣畅淋漓的。本文分享自华为云社区《【高并发】深度解析线程池中那些重要的顶层接口和抽象类》,作者:冰 河。 通过对线程池中接口和抽象类的剖析,你会发现,整个线程池设计的是如此的优雅和弱小,从线程池的代码设计中,咱们学到的不只是代码而已!! 题外话:膜拜Java大神Doug Lea,Java中的并发包正是这位老爷子写的,他是这个世界上对Java影响力最大的一个人。 一、接口和抽象类总览说起线程池中提供的重要的接口和抽象类,基本上就是如下图所示的接口和类。 接口与类的简略阐明: Executor接口:这个接口也是整个线程池中最顶层的接口,提供了一个无返回值的提交工作的办法。ExecutorService接口:派生自Executor接口,扩大了很过性能,例如敞开线程池,提交工作并返回后果数据、唤醒线程池中的工作等。AbstractExecutorService抽象类:派生自ExecutorService接口,实现了几个十分实现的办法,供子类进行调用。ScheduledExecutorService定时工作接口,派生自ExecutorService接口,领有ExecutorService接口定义的全副办法,并扩大了定时工作相干的办法。接下来,咱们就别离从源码角度来看下这些接口和抽象类从顶层设计上提供了哪些性能。 二、Executor接口Executor接口的源码如下所示。 public interface Executor { //提交运行工作,参数为Runnable接口对象,无返回值 void execute(Runnable command);}从源码能够看出,Executor接口非常简单,只提供了一个无返回值的提交工作的execute(Runnable)办法。 因为这个接口过于简略,咱们无奈得悉线程池的执行后果数据,如果咱们不再应用线程池,也无奈通过Executor接口来敞开线程池。此时,咱们就须要ExecutorService接口的反对了。 三、ExecutorService接口ExecutorService接口是非定时工作类线程池的外围接口,通过ExecutorService接口可能向线程池中提交工作(反对有返回后果和无返回后果两种形式)、敞开线程池、唤醒线程池中的工作等。ExecutorService接口的源码如下所示。 package java.util.concurrent;import java.util.List;import java.util.Collection;public interface ExecutorService extends Executor { //敞开线程池,线程池中不再承受新提交的工作,然而之前提交的工作持续运行,直到实现 void shutdown(); //敞开线程池,线程池中不再承受新提交的工作,会尝试进行线程池中正在执行的工作。 List<Runnable> shutdownNow(); //判断线程池是否曾经敞开 boolean isShutdown(); //判断线程池中的所有工作是否完结,只有在调用shutdown或者shutdownNow办法之后调用此办法才会返回true。 boolean isTerminated(); //期待线程池中的所有工作执行完结,并设置超时工夫 boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException; //提交一个Callable接口类型的工作,返回一个Future类型的后果 <T> Future<T> submit(Callable<T> task); //提交一个Callable接口类型的工作,并且给定一个泛型类型的接管后果数据参数,返回一个Future类型的后果 <T> Future<T> submit(Runnable task, T result); //提交一个Runnable接口类型的工作,返回一个Future类型的后果 Future<?> submit(Runnable task); //批量提交工作并取得他们的future,Task列表与Future列表一一对应 <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) throws InterruptedException; //批量提交工作并取得他们的future,并限定解决所有工作的工夫 <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException; //批量提交工作并取得一个曾经胜利执行的工作的后果 <T> T invokeAny(Collection<? extends Callable<T>> tasks) throws InterruptedException, ExecutionException; //批量提交工作并取得一个曾经胜利执行的工作的后果,并限定解决工作的工夫 <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;}对于ExecutorService接口中每个办法的含意,间接上述接口源码中的正文即可,这些接口办法都比较简单,我就不一一反复列举形容了。这个接口也是咱们在应用非定时工作类的线程池中最常应用的接口。 ...

August 19, 2021 · 4 min · jiezi

关于高并发:jmmvolatile学习

一、古代计算机实践模型与工作形式古代计算机模型是基于-冯诺依曼计算机模型计算机在运行时,先从内存中取出第一条指令,通过控制器的译码,按指令的要求,从存储器中取出数据进行指定的运算和逻辑操作等加工,而后再按地址把后果送到内存中去。接下来,再取出第二条指令,在控制器的指挥下实现规定操作。依此进行上来。直至遇到进行指令。程序与数据一样存贮,按程序编排的程序,一步一步地取出指令,主动地实现指令规定的操作是计算机最根本的工作模型。这一原理最后是由美籍匈牙利数学家冯.诺依曼于1945年提进去的,故称为冯.诺依曼计算机模型。计算机五大外围组成部分: 控制器(Control):是整个计算机的中枢神经,其性能是对程序规定的管制信息进行解释,依据其要求进行管制,调度程序、数据、地址,协调计算机各局部工作及内存与外设的访问等。运算器(Datapath):运算器的性能是对数据进行各种算术运算和逻辑运算,即对数据进行加工解决。存储器(Memory):存储器的性能是存储程序、数据和各种信号、命令等信息,并在需要时提供这些信息。输出(Input system):输出设施是计算机的重要组成部分,输出设施与输出设备合你为外部设备,简称外设,输出设施的作用是将程序、原始数据、文字、字符、管制命令或现场采集的数据等信息输出到计算机。常见的输出设施有键盘、鼠标器、光电输出机、磁带机、磁盘机、光盘机等。输入(Output system):输出设备与输出设施同样是计算机的重要组成部分,它把外算机的两头后果或最初后果、机内的各种数据符号及文字或各种管制信号等信息输入进去。微机罕用的输出设备有显示终端CRT、打印机、激光印字机、绘图仪及磁带、光盘机等。下图-冯诺依曼计算机模型图古代计算机硬件构造原理图 CPU内部结构划分 1、管制单元管制单元是整个CPU的指挥控制中心,由指令寄存器IR(Instruction Register)、指令译码器ID(Instruction Decoder)和 操作控制器OC(Operation Controller) 等组成,对协调整个电脑有序工作极为重要。它依据用户事后编好的程序,顺次从存储器中取出各条指令,放在指令寄存器IR中,通过指令译码(剖析)确定应该进行什么操作,而后通过操作控制器OC,按确定的时序,向相应的部件收回微操作管制信号。操作控制器OC中次要包含:节奏脉冲发生器、管制矩阵、时钟脉冲发生器、复位电路和启停电路等管制逻辑。 2、运算单元运算单元是运算器的外围。能够执行算术运算(包含加减乘数等根本运算及其附加运算)和逻辑运算(包含移位、逻辑测试或两个值比拟)。绝对管制单元而言,运算器承受管制单元的命令而进行动作,即运算单元所进行的全副操作都是由管制单元收回的管制信号来指挥的,所以它是执行部件。 3、存储单元 存储单元包含 CPU 片内缓存Cache和寄存器组,是 CPU 中临时存放数据的中央,外面保留着那些期待解决的数据,或曾经解决过的数据,CPU 拜访寄存器所用的工夫要比拜访内存的工夫短。 寄存器是CPU外部的元件,寄存器领有十分高的读写速度,所以在寄存器之间的数据传送十分快。采纳寄存器,能够缩小 CPU 拜访内存的次数,从而进步了 CPU 的工作速度。寄存器组可分为专用寄存器和通用寄存器。专用寄存器的作用是固定的,别离存放相应的数据;而通用寄存器用处宽泛并可由程序员规定其用处。计算机硬件多CPU架构: 在window上的工作管理器能够看见 多CPU 一个古代计算机通常由两个或者多个CPU,如果要运行多个程序(过程)的话,如果只有一个CPU的话,就意味着要常常进行过程上下文切换,因为单CPU即使是多核的,也只是多个处理器外围,其余设施都是共用的,所以 多个过程就必然要常常进行过程上下文切换,这个代价是很高的。 CPU多核 一个古代CPU除了处理器外围之外还包含寄存器、L1L2L3缓存这些存储设备、浮点运算单元、整数运算单元等一些辅助运算设施以及外部总线等。一个多核的CPU也就是一个CPU上有多个处理器外围,这样有什么益处呢?比如说当初咱们要在一台计算机上跑一个多线程的程序,因为是一个过程里的线程,所以须要一些共享一些存储变量,如果这台计算机都是单核单线程CPU的话,就意味着这个程序的不同线程须要常常在CPU之间的内部总线上通信,同时还要解决不同CPU之间不同缓存导致数据不统一的问题,所以在这种场景下多核单CPU的架构就能施展很大的劣势,通信都在外部总线,共用同一个缓存。 CPU寄存器 ‘每个CPU都蕴含一系列的寄存器,它们是CPU内内存的根底。CPU在寄存器上执行操作的速度远大于在主存上执行的速度。这是因为CPU拜访寄存器的速度远大于主存。 CPU缓存 即高速缓冲存储器,是位于CPU与主内存间的一种容量较小但速度很高的存储器。因为 CPU的速度远高于主内存,CPU间接从内存中存取数据要期待肯定工夫周期,Cache中保留着CPU刚用过或循环应用的一部分数据,当CPU再次应用该局部数据时可从Cache中间接调用,缩小CPU的等待时间,进步了零碎的效率。 一级Cache(L1 Cache)二级Cache(L2 Cache)三级Cache(L3 Cache)内存 一个计算机还蕴含一个主存。所有的CPU都能够拜访主存。主存通常比CPU中的缓存大得多。 CPU读取存储器数据过程 CPU要取寄存器XX的值,只须要一步:间接读取。 CPU要取L1 cache的某个值,须要1-3步(或者更多):把cache行锁住,把某个数据拿来,解锁,如果没锁住就慢了。CPU要取L2 cache的某个值,先要到L1 cache里取,L1当中不存在,在L2里,L2开始加锁,加锁当前,把L2里的数据复制到L1,再执行读L1的过程,下面的3步,再解锁。CPU取L3 cache的也是一样,只不过先由L3复制到L2,从L2复制到L1,从L1到CPU。 CPU取内存则最简单:告诉内存控制器占用总线带宽,告诉内存加锁,发动内存读申请,期待回应,回应数据保留到L3(如果没有就到L2),再从L3/2到L1,再从L1到CPU,之后解除总线锁定。 多线程环境下存在的问题 缓存一致性问题 在多处理器零碎中,每个处理器都有本人的高速缓存,而它们又共享同一主内存 (MainMemory)。基于高速缓存的存储交互很好地解决了处理器与内存的速度矛盾,然而也引入了新的问题:缓存一致性(CacheCoherence)。当多个处理器的运算工作都波及同一块主内存区域时,将可能导致各自的缓存数据不统一的状况,如果真的产生这种状况,那同步回到主内存时以谁的缓存数据为准呢?为了解决一致性的问题,须要各个处理器拜访缓存时都遵循一些协定,在读写时要依据协定来进行操作,这类协定有MSI、MESI(IllinoisProtocol)、MOSI、Synapse、Firefly及DragonProtocol,等等MESI中每个缓存行都有四个状态,别离是E(exclusive)、M(modified)、S(shared)、I(invalid)。上面咱们介绍一下这四个状态别离代表什么意思。 M:代表该缓存行中的内容被批改了,并且该缓存行只被缓存在该CPU中。这个状态的缓存行中的数据和内存中的不一样,在将来的某个时刻它会被写入到内存中(当其余CPU要读取该缓存行的内容时。或者其余CPU要批改该缓存对应的内存中的内容时(集体了解CPU要批改该内存时先要读取到缓存中再进行批改),这样的话和读取缓存中的内容其实是一个情理)。 E:E代表该缓存行对应内存中的内容只被该CPU缓存,其余CPU没有缓存该缓存对应内存行中的内容。这个状态的缓存行中的内容和内存中的内容统一。该缓存能够在任何其余CPU读取该缓存对应内存中的内容时变成S状态。或者本地处理器写该缓存就会变成M状态。 S:该状态意味着数据不止存在本地CPU缓存中,还存在别的CPU的缓存中。这个状态的数据和内存中的数据是统一的。当有一个CPU批改该缓存行对应的内存的内容时会使该缓存行变成 I 状态。 I:代表该缓存行中的内容时有效的。例子:假如有两个CPU。 指令重排序问题 为了使得处理器外部的运算单元能尽量被充分利用,处理器可能会对输出代码进行乱序执行(Out-Of-Order Execution)优化,处理器会在计算之后将乱序执行的后果重组,保障该后果与程序执行的后果是统一的,但并不保障程序中各个语句计算的先后顺序与输出代码中的程序统一。因而,如果存在一个计算工作依赖另一个计算工作的两头后果,那么其程序性并不能靠代码的先后顺序来保障。与处理器的乱序执行优化相似,Java虚拟机的即时编译器中也有相似的指令重排序(Instruction Reorder)优化 指令重排序的证实(解决办法:应用volatile、锁或者手动增加内存屏障) package com.jiagouedu.jmm; public class VolatileReOrderSample { ...

July 4, 2021 · 3 min · jiezi

关于高并发:接招吧最强-高并发-系统设计-46-连问分分钟秒杀一众面试者

接招吧!最强 “高并发” 零碎设计 46 连问,分分钟秒杀一众面试者!谈起 “高并发” 零碎设计相干常识,我想你必须要认知到的一个问题是:它是你获取一线大厂Offer 必不可少的利器!!对于各大公司(比方阿里、腾讯、今日头条等)来说,仅仅懂得CRUD的程序员便不如有 “高并发”零碎设计 教训的程序员有吸引力! 所以当你面试时,面试官会要求你有高并发设计教训,且有的面试官会询问你的零碎在遭逢百万并发时可能有哪些瓶颈点,以及有什么优化思路等问题...为的就是测验你是否真的理解这方面的内容...为助你迅速把握 “高并发”零碎设计 的相干常识内容等,这46连问(含解析)你必须铭刻于心,蕴含:根底篇 - 数据库篇 - 缓存篇 - 音讯队列篇 - 分布式服务篇 - 保护篇 - 实战篇 留神:篇幅起因,以下皆以图片展现这份 最强 “高并发” 零碎设计 46 连问(附解析)有近400页 ☛ 原件(点击下载)“高并发” 零碎设计 - 根底篇01 为什么你要学习高并发零碎设计02 高并发零碎:它的通用设计办法是什么03 架构分层:咱们为什么肯定要这么做04 零碎设计指标(一):如何晋升零碎性能05 零碎设计指标(二):零碎怎么做到高可用06 零碎设计指标(三):如何让零碎易于扩大07 面试现场第一期:当问到组件实现原理时,面试官是在刁难你吗 “高并发” 零碎设计 - 根底篇 触类旁通坚固必问思考题: 高并发零碎演进是一个渐进的过程,并非欲速不达的,那么你在零碎演进过程中积攒了哪些教训又踩到了哪些坑呢?咱们提到了分层架构的多种模型,比方三层架构模型,阿里巴巴提出的分层架构模型,那么在你日常开发的过程中,会如何来做架构分层呢?你感觉如此分层的劣势是什么呢?咱们提到了一些性能优化的准则和根本的思考点,那么你在日常工作中有哪些性能优化的伎俩和教训呢?提到了很多保障高可用的伎俩,那么你在工作中会有哪些保证系统高可用的设计技巧呢?传统关系型数据库的可扩展性是很差的,那么在你看来,常见的 NoSQL 数据库是如何解决扩展性的问题呢?“高并发” 零碎设计 - 数据库篇08 池化技术:如何缩小频繁创立数据库连贯的性能损耗09 数据库优化计划(一):查问申请减少时,如何做主从拆散 10 数据库优化计划(二):写入数据量减少时,如何实现分库分表11 发号器:如何保障分库分表后ID的全局唯─性 12 NoSQL:在高并发场景下,数据库和 NoSQL 如何做到互补 “高并发” 零碎设计 - 数据库篇 触类旁通坚固必问思考题: 在理论的我的项目中,你可能会用到其余的池化技术,那么联合池化的内容,分享一下在研发过程中,还应用过哪些其它池化技术吗?又因池化技术踩过哪些坑,过后你是怎么解决的?咱们提到,存储节点间相互复制数据是一种常见的,晋升零碎可用性和性能的形式,那么你还理解哪些组件有应用这种形式呢?它们的复制形式又是如何的呢?分库分表实际上是分布式存储中一种数据分片的解决方案,那么你还理解哪些分布式存储组件也应用了相似的技术呢?它的实现形式你理解吗?理解了分布式发号器的实现原理和生成 ID 的个性,那么在你的零碎中你的 ID 是如何生成的呢?NoSQL 数据库是能够与传统的关系型数据库配合,一起解决数据存储问题的,那么在日常工作中,你用到了哪些 NoSQL 数据库呢?在选型的时候是基于什么样的思考呢?“高并发” 零碎设计 - 缓存篇13 缓存:数据库成为瓶颈后,动态数据的查问要如何减速14 缓存的应用姿态(一):如何抉择缓存的读写策略15 缓存的应用姿态(二):缓存如何做到高可用16 缓存的应用姿态(三):缓存穿透了怎么办17 C D N:动态资源如何减速 18 数据的迁徙应该如何做 ...

May 28, 2021 · 1 min · jiezi

关于高并发:互联网高并发技术方案选型思考和实践

本文试图通过渐进的技术剖析,和大家分享咱们在互联网高并发技术计划抉择上的一些思考和决策;从技术选型的高度为架构师在面对高并发业务设计时须要思考的方方面面做一个参考。 Beautiful Concurrency & Pretty Erlang ========================================== 前言 环信以“连贯人与人,连贯人与商业”为使命,旨在为宽广企业开发者提供最优质的寰球即时通讯PaaS服务。如何实现高并发场景下,弹性化的保障服务质量是咱们一贯的业务要求和技术谋求。 本文试图通过渐进的技术剖析,和大家分享咱们在互联网高并发技术计划抉择上的一些思考和决策;从技术选型的高度为架构师在面对高并发业务设计时须要思考的方方面面做一个参考。 接下来,enjoy: Beautiful Concurrency – 并发的必要性世界是并发的,软件也应该是并发的咱们生存在其中的世界,就是一个微小的并发零碎。每时每刻,在世界的每一个角落,人类的每一个个体都在和这个世界进行着频繁的能量交互,信息交互也是其中的重要组成部分;当咱们转换身份,以造物主的视角鸟瞰这个世界里所有的万物(动物,动物,陆地,土壤,机器等等)时,也能够看到它们同样在和这个世界进行着无休止的能量/信息交互。 为了与这个世界进行无效的交互,软件也应该是并发的。 世界是散布的,软件也应该是散布的地球是圆的,世界是平的,不管怎样,从宇宙大爆炸的那个奇点之后,就是散布的宇宙/世界。作为与世界万物(包含人类本人)交互的软件,天然也必须满足分布式要求。而这种地理分布(Geo. Distribution)个性,也仅是并发在空间维度下的反映而已。 世界不可预测,软件也应该是容错的没有完满的世界:抵触,劫难随时在产生,不论在什么样的维度上。作为软件,bug,crash也是不可躲避的事实挑战。即便存在完满的没有bug的程序,运行程序的硬件也可能呈现故障。为了加强软件的容错性,代码的独立性(指一个故障不会影响到故障工作以外的其它工作)和故障检测以及故障解决是要害:这所有都须要并发,因为串行程序的容错性远远不如并发程序。 并发计划概览“七个模型”来源于Paul Butcher著的《Seven Concurrency Models in Seven Weeks》,中文译名《七周七并发模型》,概览的介绍了并发畛域的常见计划,心愿能给架构师提供一个轮廓化的分类形容。自己在其根底上增加了一些本人的拓展思考(_见下文中斜体局部_): 1.线程与锁_:线程与锁模型有很多家喻户晓的有余,但仍是其余模型的技术根底,也是很多并发软件开发的首选。— _这个计划其实是一个anti-pattern,在高并发场景下如履薄冰(bug,dead lock如影随形),让开发者和运维人员胆战心惊。__Ugly Locks to Ugly Concurrency (俊俏的锁,俊俏的并发):locks and condition variables is fundamentally flawed! 2.函数式编程_:函数式编程日渐重要的起因之一,是其对并发编程和并行编程提供了良好的反对。函数式编程打消了可变状态,所以从根本上是线程平安的,而且易于并行执行。_— 函数之美,逻辑之美!置信很多从命令式语言(Imperative Programming)转战到函数式编程语言(Functional Programminmg)的时候都会收回这样的感叹。其实这一提高恰好体现了人类在一直的进化过程中,对这个世界认知一直提炼,思维模式逐渐由具象走向形象的演进轨迹。而回到高并发的话题上,函数式编程以不可变状态这一繁难的策略,博得了完满赞美,俨然已是君临天下的明日霸主! 3.拆散标识与状态:_如果一个线程援用了长久数据结构,那么其余线程对数据结构的批改对该线程就是不可见的。因而长久数据结构对并发编程的意义非比寻 常,其拆散了标识(identity)与状态(state)。 _— 这又是一个很妙的策略,而大家熟知的version control system如git,包含比特币/区块链的机制都是这一思维领导下的具体实际,限于篇幅,本文不做进一步开展。 4.Actor Model:_一种适用性很广的并发编程模型,实用于共享内存模型和分布式内存模型,也适宜解决地理分布型问题,能提供弱小的容错性。_— 最开始接触到Actor模型就是通过Erlang语言,起初又接触到Akka(基于Scala)等基于各种语言实现的框架,也越来越领会到这一模型在高并发场景下的熟能生巧。本文后续局部会开展介绍。另外,做个招聘小广告,环信通信云研发团队正在广纳英才,欢送懂Erlang,有相干高并发开发教训的小伙伴加盟,虚位以待!点这里间接分割咱们哟! 5.CSP(Communicating Sequential Processes, CSP)_:外表上看,CSP模型与Actor Model很类似,两者都基于消息传递。不过CSP模型侧重于传递信息的通道,而Actor Model侧重于通道两端的实体,应用CSP模型的代码会带有显著不同的格调。_— 这里就是channel能够大施拳脚的天地了,go go go!限于篇幅,本文不做进一步开展。同样的,欢送懂Golang,有相干高并发开发教训的小伙伴加盟,虚位以待! 6.数据级并行:_每个笔记本电脑里都藏着一台超级计算机——GPU。GPU利用了数据级并行,不仅能够疾速进行图像处理,也能够用于更 广大的畛域。如果要进行有限元剖析、流体力学计算或其余的大量数字计算,GPU的性能将是不二抉择。_— 在过来的两年里,华人之光,黄仁勋(Jensen Huang, CEO of Nvidia),从自家的壁炉里一次又一次给大家带来了震撼世界的变革产品,让之前遥不可及的GPU飞入寻常百姓家,也带来了一次又一次的算力之争。置信在不久的未来,有了GPU算力加持和人工智能算法的普惠应用,会有有数的AI利用层出不穷的涌现,它们会从云端落地到边缘,人类可能会比本人设想的更早的进入前途未卜的人机争霸时代。 7.Lambda架构:_Lambda架构综合了MapReduce和流式解决的特点,是一种能够解决多种大数据问题的架构。_— Lambda架构也是采纳了数据并行处理技术,然而它把并行算力的宏观场景放大到了一个更大的尺度:将数据和计算散布到成千上万台机器组成的集群上进行,将并发,散布个性整合到一套计划中,通过两层(批处理层-Batch Layer,减速层-Speed Layer/Streaming Process)的组合,实现了高计算效率和低提早的“鱼与熊掌兼得”。 ...

March 12, 2021 · 1 min · jiezi

关于高并发:从根上理解高性能高并发三深入操作系统彻底理解IO多路复用

1、系列文章引言1.1 文章目标作为即时通讯技术的开发者来说,高性能、高并发相干的技术概念早就了然与胸,什么线程池、零拷贝、多路复用、事件驱动、epoll等等名词信手拈来,又或者你对具备这些技术特色的技术框架比方:Java的Netty、Php的workman、Go的nget等熟练掌握。但真正到了面视或者技术实际过程中遇到无奈释怀的纳闷时,方知自已所把握的不过是皮毛。 返璞归真、回归实质,这些技术特色背地的底层原理到底是什么?如何能通俗易懂、毫不费力真正透彻了解这些技术背地的原理,正是《从根上了解高性能、高并发》系列文章所要分享的。 1.2 文章源起我整顿了相当多无关IM、音讯推送等即时通讯技术相干的资源和文章,从最开始的开源IM框架MobileIMSDK,到网络编程经典巨著《TCP/IP详解》的在线版本,再到IM开发纲领性文章《新手入门一篇就够:从零开发挪动端IM》,以及网络编程由浅到深的《网络编程懒人入门》、《脑残式网络编程入门》、《高性能网络编程》、《鲜为人知的网络编程》系列文章。 越往常识的深处走,越感觉对即时通讯技术理解的太少。于是起初,为了让开发者门更好地从根底电信技术的角度了解网络(尤其挪动网络)个性,我跨专业收集整理了《IM开发者的零根底通信技术入门》系列高阶文章。这系列文章未然是一般即时通讯开发者的网络通信技术常识边界,加上之前这些网络编程材料,解决网络通信方面的常识盲点根本够用了。 对于即时通讯IM这种零碎的开发来说,网络通信常识的确十分重要,但回归到技术实质,实现网络通信自身的这些技术特色:包含下面提到的线程池、零拷贝、多路复用、事件驱动等等,它们的实质是什么?底层原理又是怎么?这就是整顿本系列文章的目标,心愿对你有用。 1.3 文章目录《从根上了解高性能、高并发(一):深刻计算机底层,了解线程与线程池》《从根上了解高性能、高并发(二):深刻操作系统,了解I/O与零拷贝技术》《从根上了解高性能、高并发(三):深刻操作系统,彻底了解I/O多路复用》(* 本文)《从根上了解高性能、高并发(四):深刻操作系统,彻底了解同步与异步 (稍后公布..)》《从根上了解高性能、高并发(五):高并发高性能服务器到底是如何实现的 (稍后公布..)》1.4 本篇概述接上篇《深刻操作系统,了解I/O与零拷贝技术》,本篇是高性能、高并发系列的第3篇文章,上篇里咱们讲到了I/O技术,本篇将以更具象的文件这个话题动手,带你一步步了解高性能、高并发服务端编程时无奈回避的I/O多路复用及相干技术。 本文已同步公布于“即时通讯技术圈”公众号,欢送关注。公众号上的链接是:点此进入。 2、本文作者应作者要求,不提供真名,也不提供集体照片。 本文作者次要技术方向为互联网后端、高并发高性能服务器、检索引擎技术,网名是“码农的荒岛求生”,公众号“码农的荒岛求生”。感激作者的自私分享。 3、什么是文件?在正式开展本文的内容之前,咱们须要先预习一下文件以及文件描述符的概念。 程序员应用I/O最终都逃不过文件这个概念。 在Linux世界中文件是一个很简略的概念,作为程序员咱们只须要将其了解为一个N byte的序列就能够了: b1, b2, b3, b4, ....... bN实际上所有的I/O设施都被形象为了文件这个概念,所有皆文件(Everything is File),磁盘、网络数据、终端,甚至过程间通信工具管道pipe等都被当做文件看待。 所有的I/O操作也都能够通过文件读写来实现,这一十分优雅的形象能够让程序员应用一套接口就能对所有外设I/O操作。 罕用的I/O操作接口个别有以下几类: 1)关上文件,open;2)扭转读写地位,seek;3)文件读写,read、write;4)敞开文件,close。程序员通过这几个接口简直能够实现所有I/O操作,这就是文件这个概念的弱小之处。 4、什么是文件描述符?在上一篇《深刻操作系统,了解I/O与零拷贝技术》中咱们讲到:要想进行I/O读操作,像磁盘数据,咱们须要指定一个buff用来装入数据。 个别都是这样写的: read(buff);然而这里咱们疏忽了一个关键问题:那就是,尽管咱们指定了往哪里写数据,然而咱们该从哪里读数据呢? 从上一节中咱们晓得,通过文件这个概念咱们能实现简直所有I/O操作,因而这里少的一个配角就是文件。 那么咱们个别都怎么应用文件呢? 举个例子:如果周末你去比拟火的餐厅吃饭应该会有领会,个别周末人气高的餐厅都会排队,而后服务员会给你一个排队序号,通过这个序号服务员就能找到你,这里的益处就是服务员无需记住你是谁、你的名字是什么、来自哪里、爱好是什么、是不是保护环境爱护小动物等等,这里的关键点就是:服务员对你无所不知,但仍然能够通过一个号码就能找到你。 同样的:在Linux世界要想应用文件,咱们也须要借助一个号码,依据“弄不懂准则”,这个号码就被称为了文件描述符(file descriptors),在Linux世界中鼎鼎大名,其情理和下面那个排队号码一样。 因而:文件形容仅仅就是一个数字而已,然而通过这个数字咱们能够操作一个关上的文件,这一点要记住。 有了文件描述符,过程能够对文件无所不知,比方文件在磁盘的什么地位、加载到内存中又是怎么治理的等等,这些信息通通交由操作系统打理,过程无需关怀,操作系统只须要给过程一个文件描述符就足够了。 因而咱们来欠缺上述程序: int fd = open(file_name); // 获取文件描述符read(fd, buff);怎么样,是不是非常简单。 5、文件描述符太多了怎么办?通过了这么多的铺垫,终于要到高性能、高并发这一主题了。 从前几节咱们晓得,所有I/O操作都能够通过文件样的概念来进行,这当然包含网络通信。 如果你有一个IM服务器,当三次握手倡议长连贯胜利当前,咱们会调用accept来获取一个链接,调用该函数咱们同样会失去一个文件描述符,通过这个文件描述符就能够解决客户端发送的聊天音讯并且把音讯转发给接收者。 也就是说,通过这个描述符咱们就能够和客户端进行通信了: // 通过accept获取客户端的文件描述符int conn_fd = accept(...);Server端的解决逻辑通常是接管客户端音讯数据,而后执行转发(给接收者)逻辑: if(read(conn_fd, msg_buff) > 0) {do_transfer(msg_buff);}是不是非常简单,然而世界终归是简单的,当然也不是这么简略的。 接下来就是比较复杂的了。 ...

January 5, 2021 · 1 min · jiezi

关于高并发:并发编程中一种经典的分而治之的思想

写在后面在JDK中,提供了这样一种性能:它可能将简单的逻辑拆分成一个个简略的逻辑来并行执行,待每个并行执行的逻辑执行实现后,再将各个后果进行汇总,得出最终的后果数据。有点像Hadoop中的MapReduce。ForkJoin是由JDK1.7之后提供的多线程并发解决框架。ForkJoin框架的根本思维是分而治之。什么是分而治之?分而治之就是将一个简单的计算,依照设定的阈值分解成多个计算,而后将各个计算结果进行汇总。相应的,ForkJoin将简单的计算当做一个工作,而合成的多个计算则是当做一个个子工作来并行执行。 注:文章已同步收录到:https://github.com/sunshinely... 和 https://gitee.com/binghe001/t... 。如果文件对你有点帮忙,别忘记给个Star哦!如果小伙伴们有任何疑难,都能够加我微信【sun_shine_lyz】进行交换哦! Java并发编程的倒退对于Java语言来说,生来就反对多线程并发编程,在并发编程畛域也是在一直倒退的。Java在其倒退过程中对并发编程的反对越来越欠缺也正好印证了这一点。 Java 1 反对thread,synchronized。Java 5 引入了 thread pools, blocking queues, concurrent collections,locks, condition queues。Java 7 退出了fork-join库。Java 8 退出了 parallel streams。并发与并行并发和并行在实质上还是有所区别的。 并发并发指的是在同一时刻,只有一个线程可能获取到CPU执行工作,而多个线程被疾速的轮换执行,这就使得在宏观上具备多个线程同时执行的成果,并发不是真正的同时执行,并发能够应用下图示意。 并行并行指的是无论何时,多个线程都是在多个CPU外围上同时执行的,是真正的同时执行。 分治法根本思维把一个规模大的问题划分为规模较小的子问题,而后分而治之,最初合并子问题的解失去原问题的解。步骤①宰割原问题; ②求解子问题; ③合并子问题的解为原问题的解。 咱们能够应用如下伪代码来示意这个步骤。 if(工作很小){ 间接计算失去后果}else{ 分拆成N个子工作 调用子工作的fork()进行计算 调用子工作的join()合并计算结果}在分治法中,子问题个别是互相独立的,因而,常常通过递归调用算法来求解子问题。 典型利用二分搜寻大整数乘法Strassen矩阵乘法棋盘笼罩合并排序疾速排序线性工夫抉择汉诺塔ForkJoin并行处理框架ForkJoin框架概述Java 1.7 引入了一种新的并发框架—— Fork/Join Framework,次要用于实现“分而治之”的算法,特地是分治之后递归调用的函数。 ForkJoin框架的实质是一个用于并行执行工作的框架, 可能把一个大工作宰割成若干个小工作,最终汇总每个小工作后果后失去大工作的计算结果。在Java中,ForkJoin框架与ThreadPool共存,并不是要替换ThreadPool 其实,在Java 8中引入的并行流计算,外部就是采纳的ForkJoinPool来实现的。例如,上面应用并行流实现打印数组元组的程序。 public class SumArray { public static void main(String[] args){ List<Integer> numberList = Arrays.asList(1,2,3,4,5,6,7,8,9); numberList.parallelStream().forEach(System.out::println); }}这段代码的背地就应用到了ForkJoinPool。 说到这里,可能有读者会问:能够应用线程池的ThreadPoolExecutor来实现啊?为什么要应用ForkJoinPool啊?ForkJoinPool是个什么鬼啊?! 接下来,咱们就来答复这个问题。 ForkJoin框架原理ForkJoin框架是从jdk1.7中引入的新个性,它同ThreadPoolExecutor一样,也实现了Executor和ExecutorService接口。它应用了一个有限队列来保留须要执行的工作,而线程的数量则是通过构造函数传入,如果没有向构造函数中传入指定的线程数量,那么以后计算机可用的CPU数量会被设置为线程数量作为默认值。 ForkJoinPool次要应用分治法(Divide-and-Conquer Algorithm)来解决问题。典型的利用比方疾速排序算法。这里的要点在于,ForkJoinPool可能应用绝对较少的线程来解决大量的工作。比方要对1000万个数据进行排序,那么会将这个工作宰割成两个500万的排序工作和一个针对这两组500万数据的合并工作。以此类推,对于500万的数据也会做出同样的宰割解决,到最初会设置一个阈值来规定当数据规模到多少时,进行这样的宰割解决。比方,当元素的数量小于10时,会进行宰割,转而应用插入排序对它们进行排序。那么到最初,所有的工作加起来会有大略200万+个。问题的关键在于,对于一个工作而言,只有当它所有的子工作实现之后,它才可能被执行。 ...

December 15, 2020 · 2 min · jiezi

关于高并发:接下来冰河要有大动作了

写在后面置信看到这个题目后,不少小伙伴会想:题目党,能有啥大动作?不过,我要说的是:持续往下看,起码最近的一段时间我要调整下公众号的更新了,为啥?大家持续往下看吧。 注:文章已同步收录到:https://github.com/sunshinelyz/technology-binghe 和 https://gitee.com/binghe001/technology-binghe 。如果文件对你有点帮忙,别忘记给个Star哦!如果小伙伴们有任何疑难,都能够加我微信【sun_shine_lyz】进行交换哦! 出版实体书首先,往年出版了两本实体书,一本是《海量数据处理与大数据技术实战》。 一本是《MySQL技术大全:开发、优化与运维实战》两本书都是超硬核的技术干货。 两本书都是超硬核的技术干货,目前在天猫、京东和当当均已上架。 原创PDF电子书通过一直的积攒和输入,冰河整顿出8本原创的PDF,其中,《深刻了解高并发编程(第1版)》电子书,作为全网最牛逼的收费开源的高并发编程PDF,自开源以来,已累计全网下载6W+。目前,这个数字仍在上涨。小伙伴们能够通过如下形式收费支付这些PDF电子书,每一本都是超硬核的技术干货。而且,每一本电子书的内容后续都会一直迭代和更新,始终保持最新、最硬核的技术。 关注 冰河技术 微信公众号: 回复 “并发编程” 支付《深刻了解高并发编程(第1版)》PDF文档。 回复 “并发源码” 支付《并发编程外围常识(源码剖析篇 第1版)》PDF文档。 回复 ”限流“ 支付《亿级流量下的分布式解决方案》PDF文档。 回复 “设计模式” 支付《深入浅出Java23种设计模式》PDF文档。 回复 “Java8新个性” 支付 《Java8新个性教程》PDF文档。 回复 “分布式存储” 支付《跟冰河学习分布式存储技术》 PDF文档。 回复 “Nginx” 支付《跟冰河学习Nginx技术》PDF文档。 回复 “互联网工程” 支付《跟冰河学习互联网工程技术》PDF文档。 我的开源我的项目除了写书、写公众号、整顿原创PDF之外,我还在保护着本人的开源我的项目,什么?你没看错,都是集体开源的我的项目,有些曾经在某些公司的理论生产环境中应用。这些开源我的项目中,典型的有如下几个: mykit-serial全网首个齐全开源的分布式全局有序序列号(分布式ID生成器)服务mykit-serial,反对嵌入式(Jar包)、RPC(Dubbo)、Restful API形式公布服务。开源地址: GitHub: https://github.com/sunshinelyz/mykit-serial Gitee: https://gitee.com/binghe001/mykit-serial mykit-transaction-message基于可靠消息最终一致性的分布式事务框架mykit-transaction-message开源地址: GitHub: https://github.com/sunshinelyz/mykit-transaction-message Gitee: https://gitee.com/binghe001/mykit-transaction-message mykit-delay精准定时工作和提早队列框架mykit-delay开源地址: GitHub: https://github.com/sunshinelyz/mykit-delay Gitee: https://gitee.com/binghe001/mykit-delay technology-binghe次要收录冰河技术微信公众号的文章。 Github: https://github.com/sunshinelyz/technology-binghe Gitee: https://gitee.com/binghe001/technology-binghe 以上开源我的项目,我会重点保护,其余的开源我的项目,小伙伴们能够到我的Github上查看,根本以mykit结尾的我的项目都是我集体开源的。这里,我也心愿可能有更多的小伙伴退出,咱们一起开发和保护这些开源我的项目。 重点来了说了这么多,重点来了,冰河接下来要干什么呢? ...

December 13, 2020 · 1 min · jiezi

关于高并发:高并发系统设计

在业务量越来越大的时候,零碎访问量同时大了起来,这个时候就要思考零碎如果解决高并发的申请。 性能性能的话分为两个,一个是利用,一个是数据库。单个利用的性能晋升下来了,那集群的整体性能晋升作用会更大。利用就是咱们的代码优化,比方算法的优化,代码的设计形式,jvm的优化。数据库就是sql的优化,比方表构造怎么建,索引怎么建,sql语句怎么写等方面。这些标准能够参考阿里巴巴Java开发手册 拆分架构演进之路和数据库演进之路提到了为了进步零碎并发解决能力,对利用和数据库进行了程度拆分和垂直拆分。 应用层对利用的拆分会有以下几个问题: 分布式事务:在单机利用中,咱们的订单服务和库存服务是一起的,通过数据库的ACID来保障事务,然而拆分后,就没方法通过数据库的ACID来了,所以要如何保障分布式事务?罕用的有2PC/3PC、TCC、Saga分布式锁:在分布式系统下,java提供的各自锁曾经没方法解决这些共享资源的并发问题,所以要如何保障分布式锁呢?罕用的有数据库加锁、zookeeper加锁、redis加锁、etcd加锁。分布式作业调度:一个定时工作部署多份,比方ABC,如何保障他们的定时工作不反复执行?比方罕用的有elastic-job、xxl。数据库对数据库拆分会有以下几个问题: 读写拆散:如何保证数据的一致性。分库分表:分布式下的主键是怎么生成的、分库分表后怎么跨库分页、分库分表是怎么做的?分库分表的中间件有哪些(mycat、sharding-jdbc)。负载平衡在高可用中,咱们提到了冗余,然而仅仅只是当master挂了slave顶上去用,那这个资源就很节约了,所以咱们就想,通过负载平衡让多个服务同时启用,既达到了高可用的目标,也用于解决高并发的申请。但并不是多个服务一起用他就等于能够解决高并发的,比方mysql的主从,因为是异步更新的,所以salve的数据总是提早于master的,如果强一致性要求高的话,就不能用了。如果强一致性没什么要求的话能够用,相似的reidis也一样。所以无状态对于高并发来说,就很重要了。当申请量一上来,咱们就做多个节点就好了。比方咱们用nginx做负载平衡,压力上来了,就多加几个tomcat退出集群就好了,只是咱们要思考到,在这种状况下,session要如何解决?负载平衡罕用的硬件和软件如下: 硬件:F5软件:nginx、apache、lvs、haproxy平衡策略(见dubbo)如下: Random LoadBalance:随机,按权重设置随机概率。RoundRobin LoadBalance:* 轮询,按公约后的权重设置轮询比率。LeastActive LoadBalance:起码沉闷调用数,雷同沉闷数的随机,沉闷数指调用前后计数差。ConsistentHash LoadBalance: 一致性 Hash,雷同参数的申请总是发到同一提供者。缓存缓存对于读服务来说,是抗流量的银弹。然而如何用好缓存,也是很重要的。比方咱们引入了redis,咱们必须保障redis是高可用(redis - 哨兵(高可用)、redis - 集群(一))的,不然reidis挂了怎么办。咱们同样也要保障redis的高并发,要让他扛得住流量(redis - 主从(高性能))。咱们要保障reids挂了能够疾速复原(redis - 长久化)。引入redis,咱们要思考数据库和reids的数据一致性(缓存的数据一致性)。咱们要思考到缓存雪崩了怎么办,缓存穿透了怎么办。 音讯队列音讯队列的次要几个场景有异步、解耦、流量削峰。跟redis一样,咱们同样也要保障音讯队列的高可用,比方ActiveMQ - MasterSlave、RabbitMQ集群、RabbitMQ故障转移。ActiveMQ - 音讯可靠性、RabbitMQ - 发送方的可靠性、RabbitMQ - 音讯确认为了保障音讯的可靠性,发送方或者第三方中间件会反复发送音讯,咱们要保障接管方的幂等性,这个幂等性能够参考接口幂等设计。

November 18, 2020 · 1 min · jiezi

关于高并发:高并发下如何缩短响应时间

定义网站响应工夫是指系统对申请作出响应的工夫。艰深来讲就是咱们把网址输出进浏览器而后敲回车键开始始终到浏览器把网站的内容出现给用户的这段时间。网站响应工夫是越短越好,因为网站页面关上速度越快,就意味着咱们的用户能够更快的拜访站点或者咱们的服务器。个别咱们网站的响应工夫放弃在100~1000ms即可。1m=1000ms,关上速度越快对用户体验度越好。据说响应工夫还会影响到网站SEO成果(请行业专家留言通知我)。 响应工夫并不能间接反映网站性能的高下,然而在肯定水平上反馈了网站零碎的解决能力,也是给用户最直观上的感触。如果网站的响应工夫过长,比方10秒以上,用户的流失率会大大增加,所以把响应工夫管制在肯定范畴内是进步用户体验度的第一因素。 解决方案当用户申请一个网站数据的时候,实际上是发送了一个http申请,在宏观上能够分为两个局部: http申请达到指标网站服务器之前http申请达到指标网站服务器之后如果疏忽其中硬件局部和局部细节,申请一个网站数据的大体过程如下图所示(其中CDN和缓存局部能够省略): 咱们要想缩短一个网站的响应工夫,实质上是进步数据的返回速度,说的直白一点就是要把申请数据过程中的各个步骤进步速度,这样整体下来响应工夫就会缩短。 把数据放在离用户越近的中央响应工夫越快客户端客户端是发动一个网站申请的源头,其实这个源头能够施加肯定的策略来大大缩短某些数据的获取工夫。其中最为罕用的就是缓存,一些罕用的,很少变动的资源缓存在客户端,岂但能缩短获取资源的工夫,而且在很大水平上能加重服务端的压力。比方一些图片,css,js文件,甚至一些接口的数据或者整个网页内容都能够在客户端做缓存。另外http申请的合并也能够缩小对服务端的申请次数,在肯定水平上能够缩短申请的响应工夫。 DNS个别网站的拜访形式都采纳域名的形式(很少见IP形式),既然是域名就波及到DNS解析速度的问题,如果DNS服务解析的速度比较慢,整体过程的响应工夫也会加长,不过这个过程其实很少呈现慢的问题(不是说没有)。 DNS的解析过程能够查看这篇文章 网络客户端获取到网站IP之后通过网卡把Http申请发送进来,指标地址为相应的网站服务器。在这个过程当中如果客户端和服务器端有一方带宽比拟小的话,就会加大响应工夫。我司已经就因为服务器带宽过小导致客户端响应工夫很长的状况,过后排查了很长时间才发现。 当然网络是不牢靠的,这个过程的响应工夫其实取决于很多因素,比方路由器的路由策略是否最优,整个过程通过的网关数据量等。所以有很多网站其实是多地区多机房部署的,目标就是为了让用户通过很短的网络门路就能达到网站(其实这个过程运营商的抉择也有影响)。 网站当一个申请达到网站服务器,服务器便开始解决申请,个别会有专门解决业务申请的一个业务层,有的体现为rpc协定的微服务,有的体现为简略的一个代码分层。最终申请的数据会通过查询数据库来返回。其实这个过程和车站购票流程一样,每个窗口的解决能力是无限的,对应到服务器解决能力。因为这个起因,所以诞生了负载平衡的策略,核心思想就是:分。一台服务器不够,那就两台,三台,四台..... 直到并发的所有申请的响应工夫都在可控范畴之内。 数据库的状况相似,一个数据库扛不住压力,就加到N个数据库扩散压力。一个表扛不住压力,就把这个表拆离开,拆分成多个表,甚至拆分到多个不同服务器数据库,这就是咱们罕用的拆表策略。有的时候在同一个数据库中进行表拆分,性能的晋升并非最大化,因为一台服务器的磁盘IO是有下限的,就算拆成100个表,还是在同一个物理磁盘上,当然这样可缓解锁单表的状况。 当初有很多的场景采纳NOsql代替关系型数据库来缩短响应工夫,在失常状况下,因为关系型数据库的自身因素在特定场景下的读写速度比Nosql要慢很多,所以零碎设计初期,能够思考采纳关系型数据库和Nosql混用的计划。 缓存当并发的申请达到肯定水平,瓶颈大部分状况下产生在DB层面,甚至DB无论怎么优化总有下限。为了防止频繁查询数据库产生瓶颈,诞生了缓存。在拜访数据库之前退出了缓存层,当然这里的缓存采纳的计划在数据的响应工夫上要比数据库小很多,比方罕用的redis,Memcache,然而这些第三方的缓存组件还是要走网络,比起过程内的缓存还是要慢的多。 详情请参考以前文章 当初个别风行的设计在网站层和服务层都有缓存策略,只不过缓存的数据和策略有所不同,然而最终目标都是为了放慢申请的响应。当然加了缓存之后,数据的一致性须要认真设计才能够,如果产生数据不统一的状况,程序员可能要背锅了。 缓解数据库压力并不是引入缓存的惟一因素CDN减速一些小厂可能用不到cdn,然而cdn带来的减速还是很主观的。cdn依附部署在各地的边缘服务器,通过核心平台的负载平衡、内容散发、调度等功能模块,使用户就近获取所需内容,升高网络拥塞,进步用户拜访响应速度和命中率。CDN就是把离用户最近的数据返回给用户。 写在最初程序异步化其实并不能缩短响应工夫,然而对进步吞吐量有很大作用。更多精彩文章 分布式大并发系列架构设计系列趣学算法和数据结构系列设计模式系列

September 26, 2020 · 1 min · jiezi

关于高并发:分布式高并发下Actor模型如此优秀

写在开始一般来说有两种策略用来在并发线程中进行通信:共享数据和消息传递。应用共享数据形式的并发编程面临的最大的一个问题就是数据条件竞争。解决各种锁的问题是让人非常头痛的一件事。 传统少数风行的语言并发是基于多线程之间的共享内存,应用同步办法避免写抢夺,Actors应用音讯模型,每个Actor在同一时间解决最多一个音讯,能够发送音讯给其余Actor,保障了独自写准则。从而奇妙防止了多线程写抢夺。和共享数据形式相比,消息传递机制最大的长处就是不会产生数据竞争状态。实现消息传递有两种常见的类型:基于channel(golang为典型代表)的消息传递和基于Actor(erlang为代表)的消息传递。 Actor简介Actor模型(Actor model)首先是由Carl Hewitt在1973定义, 由Erlang OTP 推广,其 消息传递更加合乎面向对象的原始用意。Actor属于并发组件模型,通过组件形式定义并发编程范式的高级阶段,防止使用者间接接触多线程并发或线程池等根底概念。Actor模型=数据+行为+音讯。 Actor模型是一个通用的并发编程模型,而非某个语言或框架所有,简直能够用在任何一门编程语言中,其中最典型的是erlang,在语言层面就提供了Actor模型的反对,杀手锏利用RabbitMQ 就是基于erlang开发的。 更加面向对象Actor相似面向对象编程(OO)中的对象,每个Actor实例封装了本人相干的状态,并且和其余Actor处于物理隔离状态。举个游戏玩家的例子,每个玩家在Actor零碎中是Player 这个Actor的一个实例,每个player都有本人的属性,比方Id,昵称,攻击力等,体现到代码级别其实和咱们OO的代码并无多大区别,在零碎内存级别也是呈现了多个OO的实例 class PlayerActor { public int Id { get; set; } public string Name { get; set; } }无锁在应用Java/C# 等语言进行并发编程时须要特地的关注锁和内存原子性等一系列线程问题,而Actor模型外部的状态由它本人保护即它外部数据只能由它本人批改(通过消息传递来进行状态批改),所以应用Actors模型进行并发编程能够很好地防止这些问题。Actor外部是以单线程的模式来执行的,相似于redis,所以Actor齐全能够实现分布式锁相似的利用。 异步每个Actor都有一个专用的MailBox来接管音讯,这也是Actor实现异步的根底。当一个Actor实例向另外一个Actor发消息的时候,并非间接调用Actor的办法,而是把消息传递到对应的MailBox里,就如同邮递员,并不是把邮件间接送到收信人手里,而是放进每家的邮箱,这样邮递员就能够疾速的进行下一项工作。所以在Actor零碎里,Actor发送一条音讯是十分快的。 这样的设计次要劣势就是解耦了Actor,数万个Actor并发的运行,每个actor都以本人的步调运行,且发送音讯,接管音讯都不会被阻塞。 隔离每个Actor的实例都保护这本人的状态,与其余Actor实例处于物理隔离状态,并非像 多线程+锁 模式那样基于共享数据。Actor通过音讯的模式与其余Actor进行通信,与OO式的消息传递形式不同,Actor之间音讯的传递是真正物理上的消息传递。 天生分布式每个Actor实例的地位通明,无论Actor地址是在本地还是在近程机器上对于代码来说都是一样的。每个Actor的实例十分小,最多几百字节,所以单机几十万的Actor的实例很轻松。如果你写过golang代码,就会发现其实Actor在重量级上很像Goroutine。因为地位透明性,所以Actor零碎能够随便的横向扩大来应答并发,对于调用者来说,调用的Actor的地位就在本地,当然这也得益于Actor零碎弱小的路由零碎。 生命周期每个Actor实例都有本人的生命周期,就像C# java 中的GC机制一样,对于须要淘汰的Actor,零碎会销毁而后开释内存等资源来保证系统的持续性。其实在Actor零碎中,Actor的销毁齐全能够手动干涉,或者做到零碎自动化销毁。 容错说到Actor的容错,不得不说还是挺令人意外的。传统的编程形式都是在未来可能出现异常的中央去捕捉异样来保证系统的稳定性,这就是所谓的进攻式编程。然而进攻式编程也有本人的毛病,相似于事实,进攻的一方永远不能100%的进攻住所有未来可能呈现代码缺点的中央。比方在java代码中很多中央充斥着判断变量是否为nil,这些就属于进攻式编码最典型的案例。然而Actor模型的程序并不进行进攻式编程,而是遵循“任其解体”的哲学,让Actor的管理者们来解决这些解体问题。比方一个Actor解体之后,管理者能够抉择创立新的实例或者记录日志。每个Actor的解体或者异样信息都能够反馈到管理者那里,这就保障了Actor零碎在治理每个Actor实例的灵活性。 劣势天下无完满的语言,框架/模型亦是如此。Actor作为分布式下并发模型的一种,也有其劣势。 因为同一类型的Actor对象是扩散在多个宿主之中,所以取多个Actor的汇合是个软肋。比方在电商零碎中,商品作为一类Actor,查问一个商品的列表在少数状况下通过以下过程:首先依据查问条件筛选出一系列商品id,依据商品id别离取商品Actor列表(很可能会产生一个商品搜寻的服务,无论是用es或者其余搜索引擎)。如果量十分大的话,有产生网络风暴的危险(尽管几率十分小)。在实时性要求不是太高的状况下,其实也能够独立进去商品Actor的列表,利用MQ接管商品信息批改的信号来解决数据一致性的问题。在很多状况下基于Actor模型的分布式系统,缓存很有可能是过程内缓存,也就是说每个Actor其实都在过程内保留了本人的状态信息,业内通常把这种服务成为有状态服务。然而每个Actor又有本人的生命周期,会产生问题吗?呵呵,兴许吧。想想一下,还是拿商品作为例子, 如果环境是非Actor并发模型,商品的缓存能够利用LRU策略来淘汰非沉闷的商品缓存,来保障内存不会应用适量,如果是基于Actor模型的过程内缓存呢,每个actor其实就是缓存自身,就不那么容易利用LRU策略来保障内存使用量了,因为Actor的沉闷状态对于你来说是未知的。分布式事物问题,其实这是所有分布式模型都面临的问题,非因为Actor而存在。还是以商品Actor为例,增加一个商品的时候,商品Actor和统计商品的Actor(很多状况下的确被设计为两类Actor服务)须要保障事物的完整性,数据的一致性。在很多的状况下能够就义实时一致性用最终一致性来保障。每个Actor的mailBox有可能会呈现沉积或者满的状况,当这种状况产生,新音讯的解决形式是被摈弃还是期待呢,所以当设计一个Actor零碎的时候mailBox的设计须要留神。升华一下通过以上介绍,既然Actor对于地位是通明的,任何Actor对于其余Actor就如同在本地一样。基于这个个性咱们能够做很多事件了,以前传统的分布式系统,A服务器如果想和B服务器通信,要么RPC的调用(http调用不太罕用),要么通过MQ零碎。然而在Actor零碎中,服务器之间的通信都变的很优雅了,尽管实质上也属于RPC调用,然而对于编码者来说就如同在调用本地函数一样。其实当初比拟时髦的是Streaming形式。因为Actor零碎的执行模型是单线程,并且异步,所以但凡有资源竞争的相似性能都非常适合Actor模型,比方秒杀流动。基于以上的介绍,Actor模型在设计层面天生就反对了负载平衡,而且对于程度扩容反对的十分好。当然Actor的分布式系统也是须要服务注册核心的。尽管Actor是单线程执行模型,并不意味着每个Actor都须要占用一个线程,其实Actor上执行的工作就像Golang的goroutine一样,齐全能够是一个轻量级的货色,而且一个宿主上所有的Actor能够共享一个线程池,这就保障了在应用起码线程资源的状况下,最大量化业务代码。更多精彩文章 分布式大并发系列架构设计系列趣学算法和数据结构系列设计模式系列

September 22, 2020 · 1 min · jiezi

关于高并发:硬核干货抗住百万高并发的-6-个关键技术

作者:阿凡卢出处:http://www.cnblogs.com/luxiao... 一、什么是高并发 高并发(High Concurrency)是互联网分布式系统架构设计中必须思考的因素之一,它通常是指,通过设计保证系统可能同时并行处理很多申请。 高并发相干罕用的一些指标有响应工夫(Response Time),吞吐量(Throughput),每秒查问率QPS(Query Per Second),每秒事务处理量TPS(Transaction Per Second),并发用户数等。 响应工夫:系统对申请做出响应的工夫。 吞吐量:单位工夫内解决的申请数量。 QPS:每秒响应查问申请数。 TPS:每秒响应事务申请数。 并发用户数:同时承载失常应用零碎性能的用户数量。 二、晋升零碎的并发能力 互联网分布式架构设计,进步零碎并发能力的形式,方法论上次要有两种:垂直扩大(Scale Up)与程度扩大(Scale Out)。 (1)垂直扩大:晋升单机解决能力。垂直扩大的形式又有两种: 1)加强单机硬件性能,例如:减少CPU核数如32核,降级更好的网卡如万兆,降级更好的硬盘如SSD,裁减硬盘容量如2T,裁减零碎内存如128G; 2)晋升单机架构性能,例如:应用Cache来缩小IO次数,应用异步来减少单服务吞吐量,应用无锁数据结构来缩小响应工夫; (2)程度扩大:只有减少服务器数量,就能线性裁减零碎性能。程度扩大对系统架构设计是有要求的,如何在架构各层进行可程度扩大的设计是本文重点探讨的内容。 1、零碎集群化部署+负载平衡 (1)增加负载平衡层,将申请平均打到零碎层。 (2)零碎层采纳集群化多活部署,扛住初步的并发压力。 2、数据库分库分表+读写拆散+分布式数据库 (1)分库分表:程度拆分、垂直拆分(弊病太多如关联查问)。 (2)读写拆散:主库写,从库读(数据同步提早)。 (3)分布式数据库:TiDB(HTAP、兼容MySQL协定、程度扩大、分布式事务) 3、缓存 (1)本地缓存:本地磁盘或内存。 (2)分布式缓存:用缓存集群抗住大量的读申请。 (3)预缓存,多级缓存。 4、消息中间件 (1)零碎解耦,数据同步。 (2)申请异步化解决,实现削峰填谷的成果。 5、利用拆分(微服务) (1)按业务拆分、缩小耦合。 (2)分级部署,扩容缩容。 (3)利用资源隔离。 6、CDN(内容散发网络) (1)尽可能避开互联网上有可能影响数据传输速度和稳定性的瓶颈和环节, 使内容传输的更快更稳固。 (2)CDN可能实时地依据网络流量和各节点的链接,负载情况以及到用户的间隔和响应工夫等综合信息将用户的申请从新导向离用户最近的服务节点上。

September 9, 2020 · 1 min · jiezi

关于高并发:如何模拟超过-5-万的并发用户

作者:blazemeter起源:t.cn/ES7KBkW 步骤1 : 编写你的脚本步骤2 : 应用JMeter进行本地测试步骤3 : BlazeMeter沙箱测试步骤4 : 应用1个控制台和1个引擎来设置每个引擎用户的数量步骤5 : 装置并测试集群步骤6 : 应用 Master / Slave 个性来达成你的最大CC指标本文将从负载测试的角度,形容了做一次晦涩的5万用户并发测试须要做的事件. 你能够在本文的结尾局部看到探讨的记录. 疾速的步骤概要 编写你的脚本应用JMeter进行本地测试BlazeMeter沙箱测试应用一个控制台和一个引擎设置Users-per-Engine的数量设置并测试你的汇合 (1个控制台和10-14 引擎)应用 Master / Slave 个性来达成你的最大CC指标 步骤1 : 编写你的脚本开始之前,请确定从JMeter的Apache社区jmeter.apache.org 取得了最新的版本. 你也会要下载这些附加的插件 ,因为它们能够让你的工作更轻松. 有许多办法能够取得脚本: 应用 BlazeMeter 的 Chrome 扩大 来记录你的计划应用 JMeter HTTP(S) 测试脚本记录器 来设置一个代理,那样你就能够运行你的测试并记录下所有的货色从头开始全副手工构建(可能是性能/QA测试)如果你的脚本是一份记录的后果(像步骤1&2), 请牢记: 你须要扭转诸如Username & Password这样的特定参数,或者你兴许会想要设置一个CSV文件,有了外面的值每个用户就能够是不同的.为了实现诸如“增加到购物车”,“登录”还有其它这样的申请,你兴许要应用正则表达式,JSON门路提取器,XPath提取器,来提取诸如Token字符串,表单构建ID还有其它因素放弃你的脚本参数化,并应用配置元素,诸如默认HTTP申请,来使得在环境之间切换时你的工作更轻松.步骤2 : 应用JMeter进行本地测试在1个线程的1个迭代中应用查看后果树因素,调试样本,虚构样本还有关上的日志查看器(一些JMeter的谬误会在外面报告),来调试你的脚本. 遍历所有的场景(包含True 或者 False的回应) 来确保脚本行为确如预期… 在胜利应用一个线程测试之后——将其进步到10分钟10到20个线程持续测试: 如果你想要每个用户独立——是那样的么?有没有收到谬误?如果你在做一个注册过程,那就看看你的后盾 - 账户是不是照你的模板创立好了? 它们是不是独立的呢?从总结报告中,你能够看到对测试的统计 - 它们有点用么? (均匀响应工夫, 谬误, 每秒命中率)一旦你筹备好了脚本: 通过移除任何调试和虚构样本来清理脚本,并删除你的脚本侦听器如果你应用了侦听器(诸如 "将响应保留到一个文件"),请确保你没有应用任何门路! , 而如果他是一个侦听器或者一个CSV数据集配置——请确保你没有应用你在本地应用的门路 - 而只有文件名(就如同跟你的脚本在同一个文件夹)如果你应用了本人专有的JAR文件,请确保它也被上传了.如果你应用了超过一个线程组(不是默认的那个) - 请确保在将其上传到BlazeMeter之前设置了这个值.步骤3 : BlazeMeter沙箱测试如果那时你的第一个测试——你应该复习一下 这篇 无关如何在BlazeMeter中创立测试的文章. ...

August 28, 2020 · 2 min · jiezi

关于高并发:高并发的应对方案

高并发的应答计划 MQ消息中间件 高并发会用到的基础知识去深刻理解JUC 并多线程并发测试 怎么查看数据库同一时间最大的连贯数量 技术选型 docker装置

August 22, 2020 · 1 min · jiezi

关于高并发:记一次线上商城系统高并发的优化

起源:https://urlify.cn/jyYny2 对于线上零碎调优,它自身是个技术活,不仅须要很强的技术实战能力,很强的问题定位,问题辨认,问题排查能力,还须要很丰盛的调优能力。 本篇文章从实战角度,从问题辨认,问题定位,问题剖析,提出解决方案,施行解决方案,监控调优后的解决方案和调优后的察看等角度来与大家一起交换分享本次线上高并发调优整个闭环过程。 一、我的项目简要状况概述 该我的项目为基于SSM架构的商城类单体架构我的项目,其中有一个秒杀重磅模块,如下为当火线上环境的简要架构部署图,大抵形容一下: (1)我的项目为SSM架构 (2)服务器类别:1台负载平衡服务器(F5),3台使用程序服务器,1台计时器服务器,1台redis服务器,1台图片服服务器和1台基于Pass架构的Mysql主从服务器(微软云) (3)调用逻辑:下图为简要调用逻辑 二、何为单体架构我的项目 从架构倒退角度,软件我的项目经验了如下阶段的倒退: 1.单体架构:可了解为传统的前后端未分离的架构 2.垂直架构:可了解为前后端拆散架构 3.SOA架构:可了解为按服务类别,业务流量,服务间依赖关系等服务化的架构,如以前的单体架构ERP我的项目,划分为订单服务,洽购服务,物料服务和销售服务等 4.微服务:可了解为一个个小型的我的项目,如之前的ERP大型项目,划分为订单服务(订单我的项目),洽购服务(洽购我的项目),物料服务(物料我的项目)和销售服务(销售我的项目),以及服务之间调用 三、本SSM我的项目引发的线上问题 1.当秒杀的时候,cpu暴增 该零碎每天秒杀分为三个工夫端:10点,13点和20点,如下为秒杀的简要页面 图1 图2 图3 2.单台使用服务器cpu 3.单台使用服务器申请数 4.rdis连接数(info clients) 这个未保留截图,记得是600左右 connected_clients:600 5.mysql申请截图 四、排查过程及剖析 (一)排查思路 依据服务部署和我的项目架构,从如下几个方面排查: (1)使用服务器:排查内存,cpu,申请数等; (2)文件图片服务器:排查内存,cpu,申请数等; (3)计时器服务器:排查内存,cpu,申请数等; (4)redis服务器:排查内存,cpu,连接数等; (5)db服务器:排查内存,cpu,连接数等; (二)排查过程 在秒杀后30分钟内, 1.使用程序服务器cpu暴增,内存暴增,造成cpu和内存暴增的根本原因是申请数过高,单台使用服务器达到3000多; 2.redis申请超时 3.jdbc连贯超时 4.通过gc查看,发现24小时内,FullGC产生了152次 5.再看看堆栈,发现有一些线程阻塞和死锁 jstat -l pid,也能够通过VisualVM剖析 6.发现有2000多个线程申请有效资源 (三)造成本次零碎异样次要因素剖析 (1)在秒杀时,申请量过高,导致使用服务器负载过高; (2)redis连接池满,获取不到连贯,connot get a connection from thread pool ...

August 3, 2020 · 1 min · jiezi

Actor模型是解决高并发的终极解决方案

写在开始一般来说有两种策略用来在并发线程中进行通信:共享数据和消息传递。使用共享数据方式的并发编程面临的最大的一个问题就是数据条件竞争。处理各种锁的问题是让人十分头痛的一件事。 传统多数流行的语言并发是基于多线程之间的共享内存,使用同步方法防止写争夺,Actors使用消息模型,每个Actor在同一时间处理最多一个消息,可以发送消息给其他Actor,保证了单独写原则。从而巧妙避免了多线程写争夺。和共享数据方式相比,消息传递机制最大的优点就是不会产生数据竞争状态。实现消息传递有两种常见的类型:基于channel(golang为典型代表)的消息传递和基于Actor(erlang为代表)的消息传递。 Actor简介Actor模型(Actor model)首先是由Carl Hewitt在1973定义, 由Erlang OTP 推广,其 消息传递更加符合面向对象的原始意图。Actor属于并发组件模型,通过组件方式定义并发编程范式的高级阶段,避免使用者直接接触多线程并发或线程池等基础概念。Actor模型=数据+行为+消息。 Actor模型是一个通用的并发编程模型,而非某个语言或框架所有,几乎可以用在任何一门编程语言中,其中最典型的是erlang,在语言层面就提供了Actor模型的支持,杀手锏应用RabbitMQ 就是基于erlang开发的。 更加面向对象Actor类似面向对象编程(OO)中的对象,每个Actor实例封装了自己相关的状态,并且和其他Actor处于物理隔离状态。举个游戏玩家的例子,每个玩家在Actor系统中是Player 这个Actor的一个实例,每个player都有自己的属性,比如Id,昵称,攻击力等,体现到代码级别其实和我们OO的代码并无多大区别,在系统内存级别也是出现了多个OO的实例 class PlayerActor { public int Id { get; set; } public string Name { get; set; } }无锁在使用Java/C# 等语言进行并发编程时需要特别的关注锁和内存原子性等一系列线程问题,而Actor模型内部的状态由它自己维护即它内部数据只能由它自己修改(通过消息传递来进行状态修改),所以使用Actors模型进行并发编程可以很好地避免这些问题。Actor内部是以单线程的模式来执行的,类似于redis,所以Actor完全可以实现分布式锁类似的应用。 异步每个Actor都有一个专用的MailBox来接收消息,这也是Actor实现异步的基础。当一个Actor实例向另外一个Actor发消息的时候,并非直接调用Actor的方法,而是把消息传递到对应的MailBox里,就好像邮递员,并不是把邮件直接送到收信人手里,而是放进每家的邮箱,这样邮递员就可以快速的进行下一项工作。所以在Actor系统里,Actor发送一条消息是非常快的。 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vWwNZ5t0-1570964270753)(https://timgsa.baidu.com/timg...] 这样的设计主要优势就是解耦了Actor,数万个Actor并发的运行,每个actor都以自己的步调运行,且发送消息,接收消息都不会被阻塞。 隔离每个Actor的实例都维护这自己的状态,与其他Actor实例处于物理隔离状态,并非像 多线程+锁 模式那样基于共享数据。Actor通过消息的模式与其他Actor进行通信,与OO式的消息传递方式不同,Actor之间消息的传递是真正物理上的消息传递。 天生分布式每个Actor实例的位置透明,无论Actor地址是在本地还是在远程机器上对于代码来说都是一样的。每个Actor的实例非常小,最多几百字节,所以单机几十万的Actor的实例很轻松。如果你写过golang代码,就会发现其实Actor在重量级上很像Goroutine。由于位置透明性,所以Actor系统可以随意的横向扩展来应对并发,对于调用者来说,调用的Actor的位置就在本地,当然这也得益于Actor系统强大的路由系统。 生命周期每个Actor实例都有自己的生命周期,就像C# java 中的GC机制一样,对于需要淘汰的Actor,系统会销毁然后释放内存等资源来保证系统的持续性。其实在Actor系统中,Actor的销毁完全可以手动干预,或者做到系统自动化销毁。 容错说到Actor的容错,不得不说还是挺令人意外的。传统的编程方式都是在将来可能出现异常的地方去捕获异常来保证系统的稳定性,这就是所谓的防御式编程。但是防御式编程也有自己的缺点,类似于现实,防御的一方永远不能100%的防御住所有将来可能出现代码缺陷的地方。比如在java代码中很多地方充斥着判断变量是否为nil,这些就属于防御式编码最典型的案例。但是Actor模型的程序并不进行防御式编程,而是遵循“任其崩溃”的哲学,让Actor的管理者们来处理这些崩溃问题。比如一个Actor崩溃之后,管理者可以选择创建新的实例或者记录日志。每个Actor的崩溃或者异常信息都可以反馈到管理者那里,这就保证了Actor系统在管理每个Actor实例的灵活性。 劣势天下无完美的语言,框架/模型亦是如此。Actor作为分布式下并发模型的一种,也有其劣势。 由于同一类型的Actor对象是分散在多个宿主之中,所以取多个Actor的集合是个软肋。比如在电商系统中,商品作为一类Actor,查询一个商品的列表在多数情况下经过以下过程:首先根据查询条件筛选出一系列商品id,根据商品id分别取商品Actor列表(很可能会产生一个商品搜索的服务,无论是用es或者其他搜索引擎)。如果量非常大的话,有产生网络风暴的危险(虽然几率非常小)。在实时性要求不是太高的情况下,其实也可以独立出来商品Actor的列表,利用MQ接收商品信息修改的信号来处理数据一致性的问题。在很多情况下基于Actor模型的分布式系统,缓存很有可能是进程内缓存,也就是说每个Actor其实都在进程内保存了自己的状态信息,业内通常把这种服务成为有状态服务。但是每个Actor又有自己的生命周期,会产生问题吗?呵呵,也许吧。想想一下,还是拿商品作为例子, 如果环境是非Actor并发模型,商品的缓存可以利用LRU策略来淘汰非活跃的商品缓存,来保证内存不会使用过量,如果是基于Actor模型的进程内缓存呢,每个actor其实就是缓存本身,就不那么容易利用LRU策略来保证内存使用量了,因为Actor的活跃状态对于你来说是未知的。分布式事物问题,其实这是所有分布式模型都面临的问题,非由于Actor而存在。还是以商品Actor为例,添加一个商品的时候,商品Actor和统计商品的Actor(很多情况下确实被设计为两类Actor服务)需要保证事物的完整性,数据的一致性。在很多的情况下可以牺牲实时一致性用最终一致性来保证。每个Actor的mailBox有可能会出现堆积或者满的情况,当这种情况发生,新消息的处理方式是被抛弃还是等待呢,所以当设计一个Actor系统的时候mailBox的设计需要注意。升华一下通过以上介绍,既然Actor对于位置是透明的,任何Actor对于其他Actor就好像在本地一样。基于这个特性我们可以做很多事情了,以前传统的分布式系统,A服务器如果想和B服务器通信,要么RPC的调用(http调用不太常用),要么通过MQ系统。但是在Actor系统中,服务器之间的通信都变的很优雅了,虽然本质上也属于RPC调用,但是对于编码者来说就好像在调用本地函数一样。其实现在比较时兴的是Streaming方式。由于Actor系统的执行模型是单线程,并且异步,所以凡是有资源竞争的类似功能都非常适合Actor模型,比如秒杀活动。基于以上的介绍,Actor模型在设计层面天生就支持了负载均衡,而且对于水平扩容支持的非常好。当然Actor的分布式系统也是需要服务注册中心的。虽然Actor是单线程执行模型,并不意味着每个Actor都需要占用一个线程,其实Actor上执行的任务就像Golang的goroutine一样,完全可以是一个轻量级的东西,而且一个宿主上所有的Actor可以共享一个线程池,这就保证了在使用最少线程资源的情况下,最大量化业务代码。搜索公众号:架构师修行之路,领取福利,获取更多精彩内容

October 13, 2019 · 1 min · jiezi

我在极客时间的首次分享

文章首发于公众号 松花皮蛋的黑板报作者就职于京东,在稳定性保障、敏捷开发、高级JAVA、微服务架构有深入的理解 当时我将自己的文章投稿到InfoQ,然后总编辑郭总邮件邀请我来分享,于是有了这次值得纪念的演讲。欢迎朋友们前往极客时间App观看。 主题为:如何利用有效的资源抗住618大促流量 大概内容如下: 性能测试 性能优化 硬件优化可包括配置升级比如使用支持NIO的Tomcat版本代替低版本、集群水平扩展、摘取集群短板。 软件优化包括代码审查然后优化SQL和低性能代码。 扩展点 一、开发语言基础知识 数据结构中各种集合间的区别,包括适用场景、CRUD复杂度、线程安全性、有序性、快速失败特性。 并发当中的四种设计模式,包括Future模式、Master-Slave模式、保护暂停模式、不可变模式、生产消费者模式等。 IO当中的零拷贝技术、序列化和反序列化。 内存管理中的内存模型、垃圾回收策略。 使用连接池减少管理的开销。 二、程序设计 SOLID原则,具体为单一职责原则、开闭原则、里氏替换原则、接口隔离原则、依赖倒置原则。 设计模式,常见的有单例模式、工厂模式、策略模式、责任链模式。 接口设计,比如借鉴组合模式减少过多的重载。 避免过度设计,不要过早关注安全性、可配置性、可扩展性、可维护性、可继承性。 三、数据库 理解好索引和锁。 四、中间件 利用好缓存,但是要注意缓存和源数据同步的一致性问题。 利用好消息引擎,但是要注意幂等性、有序性、数据冗余复制的问题。 五、分层 理解好领域驱动设计,按业务划分限界上下文、消除隐匿数据依赖、明确定义依赖方向。 六、高可用 服务降级、服务限流、依赖熔断、队列、超时和重试机制、数据一致性。 文章来源:www.liangsonghua.me作者介绍:京东资深工程师-梁松华,在稳定性保障、敏捷开发、JAVA高级、微服务架构方面有深入的理解

October 1, 2019 · 1 min · jiezi

阿里云-ESSD-采用自研新一代存储网络协议打造超级高速

8月26日,阿里云透露,正投入自研数据存储“超级高速”,核心存储产品ESSD已率先采用这一最新的自研存储网络协议,并实现大规模商用,数据传输效率提高50%。 据了解,未来该协议还将继续演进,有望取代传统TCP网络协议。此前,谷歌、微软也曾先后发表论文试图突破瓶颈,但都未大规模应用。 随着AIoT时代的到来,所有数据都要求实时采集、传输、计算,传统 TCP 和 RDMA 网络都无法完美适配云时代的存储需求。 ESSD是业内首个百万级 IOPS 、百微秒延时云存储产品,相当于一个千万平米的巨型数据仓库,自带时速超过120公里的超级高速,仅需1秒就可以完成1部高清电影的传输和存储。 其优异的性能得益于阿里云的多项技术自研,底层架构基于自研大规模分布式存储系统盘古 2.0,存储芯片采用自研Aliflash SSD,并且依托自研网络协议 Luna 和增强型RDMA 数据传输协议,结合自研HPCC流控算法,深度优化TCP,大幅降低计算资源消耗及响应延时,使ESSD的数据传输效率可提高50%。 采用全新网络协议的ESSD已正式商用,目前服务数万企业,涵盖自动驾驶、工业物联网、AR/VR、证券交易、电商搜索等数据高并发领域。 “ESSD为企业数据存储和业务敏捷创新提供了新的可能,成为AIoT海量数据存储场景的标配。”阿里云智能存储产品资深总监Alex Chen表示。 阿里云拥有全球最丰富的云存储产品家族,总数据存储量达数十EB,凭借多层次防护、跨区域容灾等能力连续三年入选Gartner全球云存储魔力象限,并且被列为全球领导者地位。 本文作者:阿里云头条阅读原文 本文为云栖社区原创内容,未经允许不得转载。

August 27, 2019 · 1 min · jiezi

实战java高并发程序设计第五章并行模式与算法

单例模式112列表3 定义:一个类在系统中只产生一个实例 优点:对于频繁使用的对象,可以省略new的时间,对于重量级对象来说是一比客观的系统性能提升 内存使用频率低,减少GC次数,缩短GC停顿时间//饿汉式 类第一次使用时必定会创建单例对象public class Singleton { public static int STATUS=1; private Singleton(){ System.out.println("Singleton is create"); } private static Singleton instance = new Singleton(); public static Singleton getInstance() { return instance; } }//懒汉式 并发环境下,锁竞争激烈则性能较差public class LazySingleton { private LazySingleton() { System.out.println("LazySingleton is create"); } private static LazySingleton instance = null; public static synchronized LazySingleton getInstance() { if (instance == null) instance = new LazySingleton(); return instance; }}//静态内部类式 //集合上述两种优势public class StaticSingleton { public static int STATUS; private StaticSingleton(){ System.out.println("StaticSingleton is create"); } private static class SingletonHolder { private static StaticSingleton instance = new StaticSingleton(); } public static StaticSingleton getInstance() { return SingletonHolder.instance; }}注意:另外还有一种双重检查模式来创建单例,这种模式丑陋且复杂,甚至在低版本中不能保证正确性,不推荐使用不变模式定义:一个对象一旦被创建,内部状态永远不发生改变,故永远为线程安全的使用场景:对象创建后,内部状态和数据不再发生变化 对象需要被共享,被多线程频繁访问 public final class PCData { //父类不变,子类也必须不变,但无法保证这一点,故使用final private final int intData; //仅被赋值一次 public PCData(int d){ intData=d; } public PCData(String d){ intData=Integer.valueOf(d); } public int getData(){ return intData; } @Override public String toString(){ return "data:"+intData; }}注意:String 类也是不变模式,保证了在多线程下的性能生产者消费模式生产者消费模式:无锁实现Future模式并行流水线并行搜索并行排序并行算法:矩阵乘法网络NIO异步AIO

August 20, 2019 · 1 min · jiezi

实战java高并发程序设计第一章

1. 基本概念同步(Synchronous)和异步(Asynchronous)并发(Conncurrency)和并行(Parallelism)临界区阻塞(Blocking)与非阻塞(Non-Blocking)死锁(Deadlock)、饥饿(Starvation)和活锁(Livelock)同步(Synchronous)和异步(Asynchronous) 并发(Conncurrency)和并行(Parallelism) 临界区临界区:公共资源或共享数据,可被多个线程使用,但每次只能有一个线程使用,其他线程需等待 阻塞(Blocking)与非阻塞(Non-Blocking)阻塞:一个线程占用了临界区资源,其他线程就会在就必须在临界区等待,等待会导致线程挂起,这就是阻塞 非阻塞:没有一个线程可以妨碍其他线程执行,所有线程都会不断尝试向前 死锁(Deadlock)、饥饿(Starvation)和活锁(Livelock)死锁:A线程中占用a资源,并且尝试去获取b资源,同时B线程中占用着b资源,并且尝试去获取a资源,此时谁也无法进行下去,导致死锁 饥饿:资源竞争激烈时,某个线程长时间无法获取到资源,产生饥饿 活锁:两个线程竞争同一资源时相互谦让,导致两个线程一直在谦让而无法正常工作 2. 并发级别阻塞无饥饿(Starvation-Free)无障碍(Obstruction-Free)无锁(Lock-Free)无等待(Wait-Free)阻塞 使用synchronized关键字或重入锁等时,会阻塞其他线程获取临界区资源无饥饿(Starvation-Free) 使用公平锁时,先到先得,不会产生饥饿;非公平锁,由于竞争激烈,或者某些线程优先级高导致低优先级的线程有可能产生饥饿无障碍(Obstruction-Free) 无障碍是最弱的非阻塞调度,两个线程均可修改临界区数据,一旦检测到数据不安全,即对自己修改的数据进行回滚,确保数据安全,冲突严重时所有线程会不停回滚从而造成系统无法正常工作. 一般会配合"一致性标记"一起使用,操作前读取一致性标记,修改后再次读取此标记,若不一致则表示数据不安全无锁(Lock-Free) 无锁的并行都是无障碍的.无锁的并发必然有一个线程能够在有限步内完成操作离开临界区.一般都会包含无穷循环,且可能产生饥饿无等待(Wait-Free) 无等待在无所的基础上更进一步,要求所有线程必须在有限步内访问完临界区,这样就不会引起饥饿问题 读线程都是无等待的,写数据时,采用RCU(Read Copy Update)策略,先取得原数据副本,再修改副本数据,修改完后在合适的机会写回数据3.JMM内存模型JMM的技术点围绕多线程的原子性、可见性和有序性来建立的原子性(Atomicity) 32位虚拟机中操作long型变量是非原子性的,可能造成数据不安全public class MultiThreadLong { public volatile static long t=0; public static class ChangeT implements Runnable{ private long to; public ChangeT(long to){ this.to=to; } @Override public void run() { while(true){ MultiThreadLong.t=to; //赋值临界区的t Thread.yield(); //让出资源 } } } public static class ReadT implements Runnable{ @Override public void run() { while(true){ long tmp=MultiThreadLong.t; if(tmp!=111L && tmp!=-999L && tmp!=333L && tmp!=-444L) System.out.println(tmp); //打印非正常值 Thread.yield(); //让出资源 } } } public static void main(String[] args) { new Thread(new ChangeT(111L)).start(); new Thread(new ChangeT(-999L)).start(); new Thread(new ChangeT(333L)).start(); new Thread(new ChangeT(-444L)).start(); new Thread(new ReadT()).start(); //输出: //-4294966963 //4294966852 //-4294966963 }}可见性 可见性是指一个线程修改某一个共享变量的值时,其他线程是否能够立即知道这个修改 原因:缓存优化或者硬件优化问题(内存读写不会立即触发,而先进入一个硬件队列);指令重排和编辑器的优化 ...

August 7, 2019 · 1 min · jiezi

Java秒杀系统实战系列整体业务流程介绍与数据库设计

摘要: 本篇博文是“Java秒杀系统实战系列文章”的第三篇,本篇博文将主要介绍秒杀系统的整体业务流程,并根据相应的业务流程进行数据库设计,最终采用Mybatis逆向工程生成相应的实体类Entity、操作Sql的接口Mapper以及写动态Sql的配置文件Mapper.xml。 内容: 对于该秒杀系统的整体业务流程,相信机灵的小伙伴在看完第二篇博文的时候,就已经知道个大概了!因为在提供的源码数据库下载的链接中,Debug已经跟各位小伙伴介绍了该秒杀系统整体的业务流程,而且还以视频形式给各位小伙伴进行了展示!该源码数据库的下载链接如下:https://gitee.com/steadyjack/...  在本篇博文中Debug将继续花一点篇幅介绍介绍! 一图以概之,如下图所示为该秒杀系统整体的业务流程: 从该业务流程图中,可以看出,后端接口在接收前端的秒杀请求时,其核心处理逻辑为: (1)首先判断当前用户是否已经抢购过该商品了,如果否,则代表用户没有抢购过该商品,可以进入下一步的处理逻辑 (2)判断该商品可抢的剩余数量,即库存是否充足(即是否大于0),如果是,则进入下一步的处理逻辑 (3)扣减库存,并更新数据库的中对应抢购记录的库存(一般是减一操作),判断更新库存的数据库操作是否成功了,如果是,则创建用户秒杀成功的订单,并异步发送短信或者邮件通知信息通知用户 (4)以上的操作逻辑如果有任何一步是不满足条件的,则直接结束整个秒杀的流程,即秒杀失败! 如下图所示为后端处理“秒杀请求”时的核心处理逻辑: 综合这两个业务流程,下面进入“秒杀系统”的数据库设计环节,其中,主要包含以下几个表:商品信息表item、待秒杀信息表item_kill、秒杀成功记录表item_kill_success以及用户信息表user;当然,在实际的大型网站中,其所包含的数据库表远远不止于此!本系统暂且浓缩出其中核心的几张表! 如下图所示为该“秒杀系统”的数据库设计模型: 紧接着,是采用Mybatis的逆向工程生成这几个数据库表对应的实体类Entity、操作Sql的接口Mapper以及写动态Sql的配置文件Mapper.xml。如下图所示: 下面,贴出其中一个实体类以及相对应的Mapper接口和Mapper.xml代码,其他的,各位小伙伴可以点击链接:https://gitee.com/steadyjack/... 前往下载查看!首先是实体类ItemKill的源代码: import com.fasterxml.jackson.annotation.JsonFormat;import lombok.Data;import java.util.Date;@Datapublic class ItemKill { private Integer id; private Integer itemId; private Integer total; @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8") private Date startTime; @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8") private Date endTime; private Byte isActive; @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8") private Date createTime; private String itemName; //采用服务器时间控制是否可以进行抢购 private Integer canKill;}然后是ItemKillMapper接口的源代码: ...

July 16, 2019 · 2 min · jiezi

Java秒杀系统实战系列构建SpringBoot多模块项目

摘要:本篇博文是“Java秒杀系统实战系列文章”的第二篇,主要分享介绍如何采用IDEA,基于SpringBoot+SpringMVC+Mybatis+分布式中间件构建一个多模块的项目,即“秒杀系统”!。 内容:传统的基于IDEA构建SpringBoot的项目,是直接借助Spring Initializr插件进行构建,但是这种方式在大部分情况下,只能充当“单模块”的项目,并不能很好的做到“分工明确、职责清晰”的分层原则! 故而为了能更好的管理项目代码以及尽量做到“模块如名”,快速定位给定的类文件或者其他文件的位置,下面我们将基于IDEA、借助Maven构建多模块的项目,其中,其构建的思路如下图所示: ![图片上传中...] 详细的构建过程在本文就不赘述了!文末有提供源码的地址以及构建过程的视频教程!下面重点介绍一下跟“Java秒杀系统”相关的构建步骤。 (1)如下图所示为最终构建成功的项目的整体目录结构: 从该目录结构中可以看出,该项目为一个“聚合型项目”,其中,model模块依赖api模块,server模块依赖model模块,层层依赖!最终在server模块实现“大汇总”,即server模块为整个项目的核心关键所在,像什么“配置文件”、“入口启动类”啥的都在这个模块中! 而且,各个模块的职责是不一样的,分工也很明确,就像model模块,一般人看了就知道这里放的东西应该是跟mybatis或者跟数据库mysql相关的类文件与配置文件等等。 构建好相应的模块之后,就需要往相应的模块添加依赖,即只需要在pom.xml中加入相应的依赖即可,在这里就不贴出来了!(2)在这里主要贴一下server模块入口启动类MainApplication的代码,如下所示: @SpringBootApplication@ImportResource(value = {"classpath:spring/spring-jdbc.xml"})@MapperScan(basePackages = "com.debug.kill.model.mapper")@EnableSchedulingpublic class MainApplication extends SpringBootServletInitializer{ @Override protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) { return builder.sources(MainApplication.class); } public static void main(String[] args) { SpringApplication.run(MainApplication.class,args); }}其中,该启动类将加载配置文件spring-jdbc.xml(数据库链接信息的配置文件)! 构建完成之后,可以将整个项目采用外置的Tomcat跑起来,运行过程中,观察控制台Console的输出信息,如果没有报错信息,则代表整个项目的搭建是没有问题的!如果出现了问题,建议自己先研究一番并尝试去解决掉!如果仍旧不能解决,可以加文末提供的联系方式进行解决! (4)除此之外,为了让整个项目在前后端分离开发的情况下,前后端的接口交互更加规范(比如响应信息的规范等等),在这里我们采用了通用的一个状态码枚举类StatusCode 跟 一个通用的响应结果类BaseResponse,用于后端在返回响应信息给到前端时进行统一封装。 状态码枚举类StatusCode的源代码如下所示: public enum StatusCode { Success(0,"成功"), Fail(-1,"失败"), InvalidParams(201,"非法的参数!"), UserNotLogin(202,"用户没登录"), ; private Integer code; //状态码code private String msg; //状态码描述信息msg StatusCode(Integer code, String msg) { this.code = code; this.msg = msg; } public Integer getCode() { return code; } public void setCode(Integer code) { this.code = code; } public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; }}响应结果类BaseResponse的源代码如下所示: ...

July 16, 2019 · 2 min · jiezi

浅析基于-Serverless-的前后端一体化框架

概述Serverless 是一种“无服务器架构”模式,它无需关心程序运行环境、资源及数量,只需要将精力聚焦到业务逻辑上的技术。基于 Serverless 开发 web 应用,架构师总是试图把传统的解决方案移植到 Serverless 上,虽然可以做到既拥有 Serverless 新技术带来的红利,又能维持住传统开发模式的开发体验。但是,Serverless 技术带来的改变可能不止这些,可能是颠覆整个传统 web 应用开发模式的革命性技术。 开发模式业务应用的开发模式发展是从一体到分裂为前后端,再到前后端融合为一体过程。 注意:后面所说的后端特指后端业务逻辑。 1、早期,一体 没有前后端的概念,那时候的应用都是单机版,所有的业务逻辑都写一起,开发人员不需要关心网络请求,这个时期的工程师完全专注于业务代码的开发。随着业务规模的增长,也暴露了很多问题: 高并发问题高可用问题说明:业务应用升级困难等一些问题,不是本篇文章所关心,所以就不一一列举出来。 2、现在,分裂 前端 + 高可用高并发运维裹挟着的后端业务逻辑: 说明:现在 Serverless 技术已经出现有一段时间了,不但没有解决开发体验的问题,反而带来更多开发体验问题,所以,在这里我并没有突出 Serverless 技术。 解决的问题: 高并发。通过分布式部署和多级负载均衡等技术解决了业务的高并发问题高可用。通过主从架构等技术解决了业务的高可用问题解决一个问题,带来一堆问题: 分裂业务应用。为了解决高可用和高并发,业务应用引入了分布式架构,通过负载均衡和主从模式来保证高可用和高并发问题,但是这种解决方案对业务应用是侵入式的,从而导致原本高内聚一体化的应用分裂成前端和后端污染业务代码。与高可用、高并发和运维相关的逻辑与后端业务逻辑交织在一起,让后端技术门槛变高,导致需要多个后端工程师才能掌握所有后端技术增加联调成本。前后端的联调工作做日益繁重,成了工程开发效率提升的瓶颈。新功能和 BUG 需要前后端工程师配合才能完成,你如果是全栈开发工程师,你肯定深有体会,很多 BUG 一看就知道是前端问题,还是后端问题不匹配的前后端技术发展速度,前端技术发展迅猛,后端技术相对稳定,前端只能被动的去适配后端,让前端最新的技术在使用体验上大打折扣。最理想的方式是前后端通盘考量,整体发展,不要出现本来后端只需要优化一行代码的事,让前端写一百行代码来实现限制了代码抽象。因为实现的是同一个业务需求,所以前后端代码有高度的相关性,如果我们能在前后端代码之上抽象代码逻辑,肯定能有很大的作为。同时,代码的开发和维护也有质的提升,前后端分裂导致我们不得不局限在前端或者后端进行代码的抽象,抽象出来的代码可能是片面而重复的增加技术复杂度。前后端分裂,前后端工程师各自为营,形成各自的技术栈,包括语言、工具和理念,导致单个工程师维护整个业务应用变得极度困难,也让前后端工程师排斥彼此的技术栈,随着时间的推移,技术栈差异越来越大,一个项目,不管多小,至少两位工程师以上,全栈开发工程师另当别论增加运维成本。需要专门的运维工程师来运维,虽然,现在通过技术手段降低了运维的成本,但是目前运维成本依然很高,难度依然很大这也是为什么创业小公司喜欢全栈开发工程师,因为在创业早期,高可用和高并发的需求不是那么迫切,因而运维也相对简单,使用全栈开发工程师,不仅缩短了项目交付周期,而且也降低了公司的运营成本,这对创业小公司是至关重要的。 3、未来,融合回到到一体 前端 + 后端 + Serverless + 平台服务   =>  业务应用 + Serverless + 平台服务: 说明:共享逻辑是前后端的共享逻辑,在过去,由于前后端分裂,是很难做到前后端层面的代码抽象的,前后端融合后,让这件事变得简单自然。 带来困惑: 前后端分工合作,不是很好吗?在过去,将一个复杂的问题分解成多个简单的子问题,高并发和高可用没法做到不侵入业务应用,这种确实是一种很好的解法,也是没办法中的办法。前后端分工合作带来的成本问题,越发凸显。现在 Serverless 透明的解决了高并发和高可用问题,那么我们为什么还需要从技术维度来划分,我们不是更加推荐按业务维度来划分吗?后端依然很难,驾驭前后端的门槛依然很高?后端代码逻辑虽然没有了高并发和高可用的裹挟,还是会很难,比如 AI。我相信类似这种很难的业务,现在可能有,未来一定会有相关的开发工具包或者平台服务为我们解决,让这些很难的技术平民化。难的技术交给专业的人解决。找回初心: 回归业务,前后端一体化。随着 Serverless 技术的出现,解决了高可用、高并发和运维问题,作为工程师的我们是不是应该回头看看,找回初心:专注于业务代码。让原本在一起的后端业务代码与前端代码再次融合。因此,前后端一体化难道不是我们失去已久的应用开发终极解决方案吗?现状Serverless 已经做到了以下两点: 工程师只需要关心业务逻辑上的技术拥有接近于传统应用开发体验(解决历史遗留问题,可能还有些距离)传统应用框架,食之无味,弃之可惜: 目前,很多用户已经感知到了 Serverless 带来的高可用、高并发和免运维的好处,用户能够很自然的想到如果能将现有的开发框架移植到 Serverless 上,那就太好不过了。Serverless 平台很自然会提供现有框架的移植方案。解决的问题是将传统的解决方案移植到 Serverless 上,让用户在 Serverless 上拥有传统的开发体验应用框架找回初心: ...

July 2, 2019 · 1 min · jiezi

高并发架构的HTTP知识介绍

我们前面说过了 CDN的知识,也通过抓包分析了 TCP建立链接的过程。今天一起聊一聊应用层的协议 HTTP/HTTPS;这是应用工程师日常中接触最久的协议了。但是你真的了解他吗? 今天我们不讲 HTTP协议 的几种请求方式,主要介绍HTTP及HTTPS整个发送数据的过程。 消息结构还记得前面讲的 DNS 的过程吗?通过DNS我们拿到了服务端的IP地址,然后通过TCP协议,完成了浏览器与应用服务器的连接建立。HTTP协议是建立在TCP协议之上的(上层协议必然依赖下层协议),连接建立后,自然是开始通信。那么通信的格式是什么呢? 看上面这张图,HTTP的请求与响应格式基本如此。我们分开来说。 对于 请求消息 ,由三部分构成:请求行、请求头、请求的Body;所谓的请求行,就是:POST / HTTP/1.1 这部分内容。接下来的就是请求头,也就是我们常说的HTTP头;然后换行后紧接着的内容就是请求的Body,也就是正儿八经发送给应用的参数。 对于 响应消息 ,也是由三部分构成:状态行、响应头、响应的Body;关于响应行就是标记本次请求获得的结果是什么,这里主要有:20X、30X、40X、50X这几个范围的状态码,需要熟记。响应头里边重要的主要有跟缓存相关的东西,这部分内容会知道浏览器、CDN等缓存体的缓存行为,需要有一定的了解;最后的实体就是你请求的想要的结构,比如:HTML、Json等等。 传输过程消息构建后,如何发送进行传输呢?我们上面图片中看到的是字符串内容,HTTP本身是不能进行网络传输的,它必须依赖的底层的TCP协议建立的连接来发送数据。因此它实际上就是把这些构建好的字符串传给下层的TCP,至于TCP如何传输的可以看上篇文章,这里不展开了。 WebService 收到数据后会对数据进行处理然后交给应用服务器,应用服务器自然是将请求的Body作为输入,然后根据要求产生输出。输出的行为受到请求头中部分信息的控制,比如:格式(Content-Type)、编码(Accept-Charset)等。而产生的输出各个地方也会根据响应头进行处理。 看到这里大家有没有发现几个问题:HTTP依赖底层的TCP连接,也就是每个HTTP都需要进行三次握手,效率是不是会非常慢?这种方式总需要浏览器端主动发起链接,服务端想主动推送些什么很无能为力; 针对上面这些问题,HTTP2.0 协议也就诞生了,当然上面这些问题在 HTTP1.1 时代也有些解决方案。HTTP2.0 主要解决了协议头进行压缩,传输同样含义的内容,占用带宽更少速度更快;将上面的单向链接的方式改成二进制流的方式,服务端有能力主动推送数据;一个链接里边支持传输多种数据流。 关于 HTTP2.0 的内容不是文本主要想说的,大家可以自行了解下。接下来又到了 核心部分,关于 HTTPS 为什么安全、以及如何加密的解释。这部分内容算是面试的重要考点。 HTTPS为什么可靠现在大网站基本都适用了HTTPS协议,那么它跟HTTP是什么关系呢?它其实就是HTTP加上TLS(SSL)安全层,合在一起就叫 HTTPS。为什么有了这层处理数据就安全了呢? 很简单,要想安全就得加密。加密的方式现在无非就是:对称加密 与 非对称加密。 对称加密: 加密与解密都是使用相同的密钥,因此这种方式加密数据,密钥一定不能丢失。 非对称加密: 有两把密钥,私钥与公钥。使用私钥加密的数据必须使用公钥进行解密,反之依然。 安全的代价看起来 非对称加密 非常安全。不过对称加密的效率非常高。HTTPS正是综合使用这两种加密方式,让整个传输过程变得安全。接下来看看这个过程是如何完成的。 对称加密我们先来看看,如果HTTPS只使用 对称加密,能否满足安全的需要呢?由于这种情况只有一个密钥,服务端怎么把这个密钥交给客户端呢?线上传输肯定会泄漏。 所以单单有对称加密是不能满足需求。看来得换个路子。 非对称加密使用非对称加密的私钥加密数据,发给客户端。客户端用公钥解密就得到了数据。看起来好像没有什么问题。 但是这里有个问题,由于服务端发出来的数据是使用的私钥,由于公钥是公开,这相当于没有加密。大家都能够看到。并且服务端发出去的公钥这个过程也可能被串改啊,你怎么知道你收到的公钥就是服务器给你的呢?就跟现在很多诈骗公司一样,看起来有模有样,实则就是一皮包公司。 第三方公正为了解决上述问题,出现了一个所谓的 CA 机构,它怎么解决这个信任问题呢?它将服务器的公钥放到 CA证书 里边传给客户端(这里指浏览器),浏览器拿到后验证一下这个证书是否真实有效,因为CA机构是有限可追溯的。就跟你的护照一样,可辨别真伪,所以CA证书证明了有效,那么CA证书中携带的公钥自然也证明了自己的身份。 是不是看起来整个过程非常麻烦?没有办法为了安全,这点代价非常值得。这也是为什么我们常常说HTTPS的效率略低于HTTP的原因。 工作模式了解完上面的知识,我们来看看HTTPS到底是如何工作的? 客户端发起了一个https请求,包含版本信息,加密套件候选列表,压缩算法候选列表,随机数random_c,扩展字段等信息。这个过程此时是明文的。然后服务器会进行回复,根据客户端支持的算法信息、套件等,服务器选择一个告诉客户端,我们就用这个吧,同时也会返回一个随机数random_s,后面协商密钥有用。服务端响应客户端,这个响应中包含了证书的链接,用于交换密钥。客户端对收到的数据进行检查,先把证书给拉下来,然后检查各种指标,如果不合法,会看到浏览器提醒不安全。如果验证通过,就会生成一个 随机数字 Pre-master,并用证书公钥加密(非对称加密),发送给服务器。 此时的客户端已经有了生成证书的全部内容,它会计算协商的密钥(对称密钥),然后告诉服务端以后通信都采用协商的通信密钥和加密算法进行加密通信。紧接着会用协商的密钥加密一段数据发给服务端,看看是否能够正常。上面这步里边,客户端发送了三个请求。服务器先将收到的 Pre-master 用自己的私钥解密出来。然后验证客户端用对称加密发过来的数据,如果通过,则也会告知客户端后续的通信都采用协商的密钥与算法进行加密通信。并且服务端也会用对称加密生成一段加密信息给客户端让客户端试试(对称密钥)。客户端使用对称密钥正确完成解密。握手结束。开始使用对称密钥的方式进行数据传输。总结由于不让文章显得过于复杂,我只介绍了最简单的单向认证。这种安全性并不是最高,单日常中也足够了。 ...

June 24, 2019 · 1 min · jiezi

如何利用Java-NIO实现高性能高并发的http服务器

在学习Java NIO的过程中,我一直理解不了Java NIO是怎么用来实现高并发的服务器的,网上给出的例子里,基本上没有多少说到这一点的,Tomcat,Jetty这些的源码又太庞大了,导致我无从下手。 后来搜了下才发现,JDK自带了一个httpserver的实现,看了下代码,非常简洁,非常规范,一下子就让我搞懂了Java NIO是怎么实现高并发的了。 NIO是同步非阻塞模型,同步是指用NIO读取数据,需要你的线程一直在运行着,直到数据读写完毕。非阻塞是指监听通道的时候是非阻塞的,比如向通道询问有没有数据可读的时候,可以马上就返回有或者没有。如果有数据,你的线程就可以去处理读数据这部分的逻辑;如果没有数据,你的线程可以去忙其他的事情,这样子单个线程的处理能力就会高很多,不用总是等着数据。 在jdk的httpserver里,处理请求的过程大致如下图:

June 5, 2019 · 1 min · jiezi

从开源小白到-Apache-Member我的成长之路

我们走过的每一步路,都会留下印记,越坚实,越清晰。 近日,Apache 软件基金会(ASF)官方 Blog 宣布全球新增 40 位 Apache Member,张乎兴有幸成为其中一位。 目前,全球共有771位 ASF Member,中国仅13位。本文将分享作者从0基础的开源小白,一路走来的感触,希望把期间的经历分享出来,让更多的人看到,世界开源舞台的中国力量。只要有持续的付出,总会有所收获。 初次参与开源2014年,我加入阿里巴巴中间件团队,开始接手集团应用容器的维护工作。当时集团的应用容器绝大部分都是基于 JBoss,老旧且无人维护,另外有一小部分跑在 Jetty 和 Tomcat 之上,当时中间件团队维护了 Tomcat 的一个私有分支,最大的目标就是要统一所有集团的应用容器为 Tomcat。而在那之前,我从未接触过 Tomcat 的开发和运维,对我来说,挑战很大。然而,更大的挑战来自于团队大 leader 提出的,在当时看来几乎是无法实现的目标:成为 Apache Tomcat 的 committer。 要知道,作为 Apache 的核心项目之一,Tomcat 自1999年发布第一版以来,一直是开源届和 Apache 基金会的明星项目,至今仍然在应用容器领域市场占有率保持第一,历经20年发展,依旧热度不减。Tomcat 历经这么多年的发展,却从未出现过一位来自中国的 committer。而我们小团队只有4个人,根本没有任何开源的经验,也完全不知道从何做起。团队 leader 问我们,谁愿意挑战一下的时候,我也没有多想,就承担了下来。 就这样,我开始了自己的 Apache 开源之旅。 说实话,一开始,自己并没有太多的思路,于是开始反复浏览 Tomcat 官网,希望能够查询到一些有用的信息。所有的 Apache 项目几乎都会有新用户指南,介绍如何参与到此开源项目当中,Tomcat也不例外。很快,我从修复 bug 开始,第一个 patch 是修复一个Websocket 测试用例失败的问题,修复该问题涉及到了对代码的一些重构。 还记得当时提交之前我非常谨慎,和 leader 一起反复讨论了很多次,终于形成了一个比较满意的方案。提交给 Tomcat 社区之后,很快,社区便有了响应,并合并了我提交的补丁。第一次提交便获得了认可,心里很开心,紧张的情绪也缓解了。看到 Tomcat 的 release note 里面出现了自己的名字,真的非常开心。 Apache Roadshow China2015年10月,我有幸在北京参加了 Apache Roadshow China 的活动,算是第一次和Apache 基金会有了亲密接触。在大会上,亲眼目睹了时任 Apache 基金会主席的 Brett Porter、Apache 基金会副总裁 Niclas Hedhman 等大牛们的演讲,以及在 Panel Disussion 里面,和他们,以及几位来自中国的 Apache 成员,一起探讨社区领导的开发流程等。 ...

June 3, 2019 · 2 min · jiezi

当我们在谈论高并发的时候究竟在谈什么

什么是高并发?按照现在现在google搜索出来的结果,大部分人给出的定义都大同小异。 高并发是互联网分布式系统架构的性能指标之一,它通常是指单位时间内系统能够同时处理的请求数,简单点说,就是QPS(Queries per second)。那么我们在谈论高并发的时候,究竟在谈些什么东西呢? 高并发究竟是什么?这里先给出结论: 高并发的基本表现为单位时间内系统能够同时处理的请求数,高并发的核心是对CPU资源的有效压榨。 举个例子,如果我们开发了一个叫做MD5穷举的应用,每个请求都会携带一个md5加密字符串,最终系统返回穷举出所有的结果,并返回原始字符串。这个时候我们的应用场景或者说应用业务是属于CPU密集型而不是IO密集型。这个时候CPU一直在做有效计算,甚至可以把CPU利用率跑满,这时我们谈论高并发并没有任何意义。(当然,我们可以通过加机器也就是加CPU来提高并发能力,这个是一个正常猿都知道废话方案,谈论加机器没有什么意义,没有任何高并发是加机器解决不了,如果有,那说明你加的机器还不够多!????) 对于大多数互联网应用来说,CPU不是也不应该是系统的瓶颈,系统的大部分时间的状况都是CPU在等I/O (硬盘/内存/网络) 的读/写操作完成。 这个时候就可能有人会说,我看系统监控的时候,内存和网络都很正常,但是CPU利用率却跑满了这是为什么? 这是一个好问题,后文我会给出实际的例子,再次强调上文说的 '有效压榨' 这4个字,这4个字会围绕本文的全部内容!控制变量法万事万物都是互相联系的,当我们在谈论高并发的时候,系统的每个环节应该都是需要与之相匹配的。我们先来回顾一下一个经典C/S的HTTP请求流程。 如图中的序号所示:1 我们会经过DNS服务器的解析,请求到达负载均衡集群2 负载均衡服务器会根据配置的规则,想请求分摊到服务层。服务层也是我们的业务核心层,这里可能也会有一些PRC、MQ的一些调用等等3 再经过缓存层4 最后持久化数据5 返回数据给客户端 要达到高并发,我们需要 负载均衡、服务层、缓存层、持久层 都是高可用、高性能的,甚至在第5步,我们也可以通过 压缩静态文件、HTTP2推送静态文件、CND来做优化,这里的每一层我们都可以写几本书来谈优化。 本文主要讨论服务层这一块,即图红线圈出来的那部分。不再考虑讲述数据库、缓存相关的影响。高中的知识告诉我们,这个叫 控制变量法。 再谈并发网络编程模型的演变历史 并发问题一直是服务端编程中的重点和难点问题,为了优系统的并发量,从最初的Fork进程开始,到进程池/线程池,再到epool事件驱动(Nginx、node.js反人类回调),再到协程。从上中可以很明显的看出,整个演变的过程,就是对CPU有效性能压榨的过程。什么?不明显? 那我们再谈谈上下文切换在谈论上下文切换之前,我们再明确两个名词的概念。并行:两个事件同一时刻完成。并发:两个事件在同一时间段内交替发生,从宏观上看,两个事件都发生了。 线程是操作系统调度的最小单位,进程是资源分配的最小单位。由于CPU是串行的,因此对于单核CPU来说,同一时刻一定是只有一个线程在占用CPU资源的。因此,Linux作为一个多任务(进程)系统,会频繁的发生进程/线程切换。 在每个任务运行前,CPU都需要知道从哪里加载,从哪里运行,这些信息保存在CPU寄存器和操作系统的程序计数器里面,这两样东西就叫做 CPU上下文。进程是由内核来管理和调度的,进程的切换只能发生在内核态,因此 虚拟内存、栈、全局变量等用户空间的资源,以及内核堆栈、寄存器等内核空间的状态,就叫做 进程上下文。前面说过,线程是操作系统调度的最小单位。同时线程会共享父进程的虚拟内存和全局变量等资源,因此 父进程的资源加上线上自己的私有数据就叫做线程的上下文。 对于线程的上下文切换来说,如果是同一进程的线程,因为有资源共享,所以会比多进程间的切换消耗更少的资源。 现在就更容易解释了,进程和线程的切换,会产生CPU上下文切换和进程/线程上下文的切换。而这些上下文切换,都是会消耗额外的CPU的资源的。 进一步谈谈协程的上下文切换那么协程就不需要上下文切换了吗?需要,但是不会产生 CPU上下文切换和进程/线程上下文的切换,因为这些切换都是在同一个线程中,即用户态中的切换,你甚至可以简单的理解为,协程上下文之间的切换,就是移动了一下你程序里面的指针,CPU资源依旧属于当前线程。需要深刻理解的,可以再深入看看Go的GMP模型。最终的效果就是协程进一步压榨了CPU的有效利用率。 回到开始的那个问题这个时候就可能有人会说,我看系统监控的时候,内存和网络都很正常,但是CPU利用率却跑满了这是为什么?注意本篇文章在谈到CPU利用率的时候,一定会加上有效两字作为定语,CPU利用率跑满,很多时候其实是做了很多低效的计算。以"世界上最好的语言"为例,典型PHP-FPM的CGI模式,每一个HTTP请求:都会读取框架的数百个php文件,都会重新建立/释放一遍MYSQL/REIDS/MQ连接,都会重新动态解释编译执行PHP文件,都会在不同的php-fpm进程直接不停的切换切换再切换。 php的这种CGI运行模式,根本上就决定了它在高并发上的灾难性表现。 找到问题,往往比解决问题更难。当我们理解了当我们在谈论高并发究竟在谈什么 之后,我们会发现高并发和高性能并不是编程语言限制了你,限制你的只是你的思想。 找到问题,解决问题!当我们能有效压榨CPU性能之后,能达到什么样的效果? 下面我们看看 php+swoole的HTTP服务 与 Java高性能的异步框架netty的HTTP服务之间的性能差异对比。 性能对比前的准备swoole是什么Swoole是一个为PHP用C和C++编写的基于事件的高性能异步&协程并行网络通信引擎Netty是什么Netty是由JBOSS提供的一个java开源框架。 Netty提供异步的、事件驱动的网络应用程序框架和工具,用以快速开发高性能、高可靠性的网络服务器和客户端程序。单机能够达到的最大HTTP连接数是多少?回忆一下计算机网络的相关知识,Htpp协议是应用层协议,在传输层,每个HTTP请求都会进行三次握手,并建立一个TCP连接。每个TCP连接由 本地ip,本地端口,远端ip,远端端口,四个属性标识。TCP协议报文头如下(图片来自维基百科): 本地端口由16位组成,因此本地端口的最多数量为 2^16 = 65535个。远端端口由16位组成,因此远端端口的最多数量为 2^16 = 65535个。同时,在linux底层的网络编程模型中,每个TCP连接,操作系统都会维护一个File descriptor(fd)文件来与之对应,而fd的数量限制,可以由ulimt -n 命令查看和修改,测试之前我们可以执行命令: ulimit -n 65536修改这个限制为65535。 因此,在不考虑硬件资源限制的情况下,本地的最大HTTP连接数为: 65535 * 本地ip数 = 65535 个。远端的最大HTTP连接数为: 65535 * 远端ip数 = 无限制~~ 。 ...

June 1, 2019 · 2 min · jiezi

支付宝工程师如何搞定关系数据库的大脑查询优化器

摘要: 本文将深入了解OceanBase在查询优化器方面的设计思路和历经近十年时间提炼出的工程实践哲学。 前言查询优化器是关系数据库系统的核心模块,是数据库内核开发的重点和难点,也是衡量整个数据库系统成熟度的“试金石”。 查询优化理论诞生距今已有四十来年,学术界和工业界其实已经形成了一套比较完善的查询优化框架(System-R 的 Bottom-up 优化框架和 Volcano/Cascade 的 Top-down 优化框架),但围绕查询优化的核心难题始终没变——如何利用有限的系统资源尽可能为查询选择一个“好”的执行计划。 近年来,新的存储结构(如 LSM 存储结构)的出现和分布式数据库的流行进一步加大了查询优化的复杂性,本文章结合 OceanBase 数据库过去近十年时间的实践经验,与大家一起探讨查询优化在实际应用场景中的挑战和解决方案。 查询优化器简介SQL 是一种结构化查询语言,它只告诉数据库”想要什么”,但是它不会告诉数据库”如何获取”这个结果,这个"如何获取"的过程是由数据库的“大脑”查询优化器来决定的。在数据库系统中,一个查询通常会有很多种获取结果的方法,每一种获取的方法被称为一个"执行计划"。给定一个 SQL,查询优化器首先会枚举出等价的执行计划。 其次,查询优化器会根据统计信息和代价模型为每个执行计划计算一个“代价”,这里的代价通常是指执行计划的执行时间或者执行计划在执行时对系统资源(CPU + IO + NETWORK)的占用量。最后,查询优化器会在众多等价计划中选择一个"代价最小"的执行计划。下图展示了查询优化器的基本组件和执行流程。 查询优化器面临的挑战查询优化自从诞生以来一直是数据库的难点,它面临的挑战主要体现在以下三个方面: 挑战一:精准的统计信息和代价模型 统计信息和代价模型是查询优化器基础模块,它主要负责给执行计划计算代价。精准的统计信息和代价模型一直是数据库系统想要解决的难题,主要原因如下: 1、统计信息:在数据库系统中,统计信息搜集主要存在两个问题。首先,统计信息是通过采样搜集,所以必然存在采样误差。其次,统计信息搜集是有一定滞后性的,也就是说在优化一个 SQL 查询的时候,它使用的统计信息是系统前一个时刻的统计信息。 2、选择率计算和中间结果估计:选择率计算一直以来都是数据库系统的难点,学术界和工业界一直在研究能使选择率计算变得更加准确的方法,比如动态采样,多列直方图等计划,但是始终没有解决这个难题,比如连接谓词选择率的计算目前就没有很好的解决方法。 3、代价模型:目前主流的数据库系统基本都是使用静态的代价模型,比如静态的 buffer 命中率,静态的 IO RT,但是这些值都是随着系统的负载变化而变化的。如果想要一个非常精准的代价模型,就必须要使用动态的代价模型。 挑战二:海量的计划空间 复杂查询的计划空间是非常大的,在很多场景下,优化器甚至没办法枚举出所有等价的执行计划。下图展示了星型查询等价逻辑计划个数(不包含笛卡尔乘积的逻辑计划),而优化器真正的计划空间还得正交上算子物理实现,基于代价的改写和分布式计划优化。在如此海量的计划空间中,如何高效的枚举执行计划一直是查询优化器的难点。 挑战三:高效的计划管理机制 计划管理机制分成计划缓存机制和计划演进机制。 1、计划缓存机制:计划缓存根据是否参数化,优化一次/总是优化以及是否缓存可以划分成如下图所示的三种计划缓存方法。每个计划缓存方法都有各自的优缺点,不同的业务需求会选择不同的计划缓存方法。在蚂蚁/阿里很多高并发,低时延的业务场景下,就会选择参数化+优化一次+缓存的策略,那么就需要解决不同参数对应不同计划的问题(parametric query optimization),后面我们会详细讨论。 2、计划演进机制:计划演进是指对新生成计划进行验证,保证新计划不会造成性能回退。在数据库系统中, 新计划因为一些原因(比如统计信息刷新,schema版本升级)无时无刻都在才生,而优化器因为各种不精确的统计信息和代价模型始终是没办法百分百的保证新生成的计划永远都是最优的,所以就需要一个演进机制来保证新生成的计划不会造成性能回退。 OceanBase 查询优化器工程实践下面我们来看一下 OceanBase 根据自身的框架特点和业务模型如何解决查询优化器所面临的挑战。 从统计信息和代价模型的维度看,OceanBase 发明了基于 LSM-TREE 存储结构的基表访问路径选择。从计划空间的角度看,因为 OceanBase 原生就是一个分布式关系数据库系统,它必然要面临的一个问题就是分布式计划优化。从计划管理的角度看,OceanBase 有一整套完善的计划管理机制。 1.基于 LSM - TREE 的基表访问路径选择 基表访问路径选择方法是指优化器选择索引的方法,其本质是要评估每一个索引的代价并选择代价最小的索引来访问数据库中的表。对于一个索引路径,它的代价主要由两部分组成,扫描索引的代价和回表的代价(如果一个索引对于一个查询来说不需要回表,那么就没有回表的代价)。 通常来说,索引路径的代价取决于很多因素,比如扫描/回表的行数,投影的列数,谓词的个数等。为了简化我们的讨论,在下面的分析中,我们从行数这个维度来介绍这两部分的代价。 扫描索引的代价扫描索引的代价跟扫描的行数成正比,而扫描的行数则是由一部分查询的谓词来决定,这些谓词定义了索引扫描开始和结束位置。理论上来说扫描的行数越多,执行时间就会越久。扫描索引的代价是顺序 IO。 ...

May 31, 2019 · 2 min · jiezi

达摩院首席数据库科学家李飞飞云原生新战场我们如何把握先机

阿里妹导读:云计算大潮来袭,传统数据库市场正面临重新洗牌的情境,包括云数据库在内的一批新生力量崛起,动摇了传统数据库的垄断地位,而由云厂商主导的云原生数据库则将这种“改变”推向了高潮。云时代的数据库将面临怎样的变革?云原生数据库有哪些独特优势?在 DTCC 2019大会上,阿里巴巴副总裁 李飞飞博士就《下一代云原生数据库技术与趋势》进行了精彩分享。 李飞飞(花名:飞刀),阿里巴巴集团副总裁,高级研究员,达摩院首席数据库科学家,阿里云智能事业群数据库产品事业部负责人,ACM 杰出科学家。 大势所趋:云数据库市场份额增速迅猛如下图所示的是 Gartner 关于全球数据库市场份额的报告,该报告指出目前全球数据库市场份额大约为400亿美金,其中,中国数据库市场份额占比为3.7%,大约为14亿美金。 具体到数据库市场分布,传统五大数据库厂商 Oracle、Microsoft、IBM、SAP、Teradata 占比达到了80%,云数据库的份额占比接近10%,并且云数据库市场份额占比每年也在快速增长,因此, Oracle、MongoDB 等也在大力布局其在云数据库市场的竞争态势。 根据 DB-Engines 数据库市场分析显示,数据库系统正朝着多样化、多元化的方向发展,从传统的 TP 关系型数据库发展到今天的多源异构的数据库形态。目前,处于主流位置的还是大家耳熟能详的数据库系统,比如商业数据库 Oracle、SQL Server以及开源的 MySQL、PostgreSQL 等。而一些比较新的数据库系统,比如MongoDB、Redis 则开辟了一个新的赛道。数据库 License 的传统销售方式在逐渐走下坡路,而开源以及云上数据库 License 的流行程度却在不断提升。 数据库:云上应用关键的一环正如 AWS 创始人 Jeff Bezos 所说:“The real battle will be in databases”。因为云最早是从 IaaS 做起来的,从虚拟机、存储、网络,到现在如火如荼的语音识别、计算机视觉以及机器人等智能化应用,都是基于 IaaS 的,而数据库就是连接 IaaS 与智能化应用 SaaS 最为关键的一环。从数据产生、存储到消费的各个环节,数据库都至关重要。 数据库主要包括四大板块,即 OLTP、OLAP、NoSQL 以及数据库服务和管理类工具,也是云数据库厂商发力的四个方向。对于 OLTP 而言,技术发展已经历经了40年,而如今大家还在做的一件事情就是“加10元和减10元”,也就是所谓的事务处理。当数据量变得越来越大和读写冲突的原因,对数据进行在线实时分析的需求衍生出了 OLAP。由于需要 Scale out,而数据强一致性不能够得到保证,就有了NoSQL 。而最近又出现了一个新名词—— NewSQL,这是因为 NoSQL 也有所不足,故将传统 OLTP 的 ACID 保证与 NoSQL 的 Scale out 能力进行了整合,变成了NewSQL。 ...

May 29, 2019 · 2 min · jiezi

沙龙报名-探索新零售时代的数字化创新

互联网技术正在重塑新零售的消费场景,使顾客消费思维发生改变。消费习惯由价格时代转为价值时代。同时,体验需求得到升级。新零售在“人、货、场”数字化过程中,针对不同垂直领域需要不同的设计和思考,产出不同的商业要素解决方案划分。 此次沙龙活动以京东在零售业和社交电商方面的核心技术以及在“618”、“双11”大促时的成功解决方案为出发点,并结合京东云在新零售领域的诸多落地实践,详解新零售数字化创新的方方面面,同时聚焦监控运维的技术升级、企业如何依靠”京东能力“打造社交电商系统、区块链技术对零售行业的创造性影响等诸多方向与开发者们展开深入探讨。 活动详情 时间:2019年5月26日(周日)13:00—18:00 地点:上海·Hello Coffee(上海市长宁区延安西路918号1楼) 主题:从高并发架构到企业级区块链,探索新零售时代的数字化创新 点击“阅读原文”即可报名!** 活动日程 讲师信息 内容预告 主题一:京东数据库服务如何应对”大促活动“? 郭理靖 京东云产品研发部高级总监 数据库作为底层架构的中枢“神经”,其稳健、可靠以及高性能对业务支持意义重大。本次分享会: 1、从选择合适业务的数据库服务出发 ,详细解析常见的数据库服务性能问题 2、如何在业务高峰的场景下高性价比完成需求等技术详情,并配合京东电商具体的生产实践加以说明,让开发者做到更深入了解数据库选型的基本方法、高可用架构与性能优化的诸多模式等,并提供超大流量场景下系统保障的经验与技巧。 主题二:企业级监控系统设计与实践郑永宽 京东云产品研发部总监1、企业级监控系统的关键问题 2、大型监控系统设计思路与方法 3、京东云监控实践经验 主题三:社交电商SaaS产品技术架构赵金勇 京东云应用研发部社交电商负责人1、社交电商的概念 2、如何依托”京东能力“,帮助企业打造社交电商系统 3、京东电商云在社交电商领域的技术优势 4、明确微信小程序SaaS化,探究如何通过托管方式实现小程序SaaS化部署 5、深入探讨服务端如何通过域名泛解析来实现多租户部署 主题四:基于企业级区块链框架系统JD Chain的技术实践张作义 京东数科区块链产品创新负责人1、京东区块链整体技术架构解读 2、JD Chain区块链底层框架介绍 2、京东区块链产品创新应用概览 3、区块链在零售业应用案例分享 福利预警 京东云开发者社区用户专属福利! 我们为您的学习买单! ???????????? 报名并参加此次京东云技术沙龙,现场为大家抽取价值2400元由CNCF主办的“KubeCon/CloudNative开源大会”参会门票2张! 作为2019年最大规模的云原生和开源盛会,感兴趣的小伙伴千万不能错过哦! 立即点击“加入我们”报名吧!

May 16, 2019 · 1 min · jiezi

通俗易懂如何设计能支撑百万并发的数据库架构

1、引言相信看到这个标题,很多人的第一反应就是:对数据库进行分库分表啊!但是实际上,数据库层面的分库分表到底是用来干什么的,其不同的作用如何应对不同的场景,我觉得很多同学可能都没搞清楚。 本篇文章我们一起来学习一下,对于一个支撑日活百万用户的高并发系统,数据库架构应该如何设计呢? 本文的讨论和分享,将用一个创业公司的发展作为背景引入,方便大家理解。 (本文同步发布于:http://www.52im.net/thread-25...) 2、相关文章高性能数据库方面的文章: 《优秀后端架构师必会知识:史上最全MySQL大表优化方案总结》《阿里技术分享:深度揭秘阿里数据库技术方案的10年变迁史》 《阿里技术分享:阿里自研金融级数据库OceanBase的艰辛成长之路》 《腾讯TEG团队原创:基于MySQL的分布式数据库TDSQL十年锻造经验分享》 分布式架构方面的入门文章: 《腾讯资深架构师干货总结:一文读懂大型分布式系统设计的方方面面》《新手入门:零基础理解大型分布式架构的演进历史、技术原理、最佳实践》 《快速理解高性能HTTP服务端的负载均衡技术原理》 《一篇读懂分布式架构下的负载均衡技术:分类、原理、算法、常见方案等》 3、小型系统的典型数据库单机架构和明显的瓶颈假如我们现在是一个小创业公司,注册用户就 20 万,每天活跃用户就 1 万,每天单表数据量就 1000,然后高峰期每秒钟并发请求最多就 10。 天呐!就这种系统,随便找一个有几年工作经验的高级工程师,然后带几个年轻工程师,随便干干都可以做出来。 因为这样的系统,实际上主要就是在前期进行快速的业务功能开发,搞一个单块系统部署在一台服务器上,然后连接一个数据库就可以了。 接着大家就是不停地在一个工程里填充进去各种业务代码,尽快把公司的业务支撑起来。 如下图所示: 结果呢,没想到我们运气这么好,碰上个优秀的 CEO 带着我们走上了康庄大道! 公司业务发展迅猛,过了几个月,注册用户数达到了 2000 万!每天活跃用户数 100 万!每天单表新增数据量达到 50 万条!高峰期每秒请求量达到 1 万! 同时公司还顺带着融资了两轮,估值达到了惊人的几亿美金!一只朝气蓬勃的幼年独角兽的节奏! 好吧,现在大家感觉压力已经有点大了,为啥呢?因为每天单表新增 50 万条数据,一个月就多 1500 万条数据,一年下来单表会达到上亿条数据。 经过一段时间的运行,现在咱们单表已经两三千万条数据了,勉强还能支撑着。 但是,眼见着系统访问数据库的性能怎么越来越差呢,单表数据量越来越大,拖垮了一些复杂查询 SQL 的性能啊! 然后高峰期请求现在是每秒 1 万,咱们的系统在线上部署了 20 台机器,平均每台机器每秒支撑 500 请求,这个还能抗住,没啥大问题。但是数据库层面呢? 如果说此时你还是一台数据库服务器在支撑每秒上万的请求,负责任的告诉你,每次高峰期会出现下述问题: 1)你的数据库服务器的磁盘 IO、网络带宽、CPU 负载、内存消耗,都会达到非常高的情况,数据库所在服务器的整体负载会非常重,甚至都快不堪重负了; 2)高峰期时,本来你单表数据量就很大,SQL 性能就不太好,这时加上你的数据库服务器负载太高导致性能下降,就会发现你的 SQL 性能更差了; 3)最明显的一个感觉,就是你的系统在高峰期各个功能都运行的很慢,用户体验很差,点一个按钮可能要几十秒才出来结果; 4)如果你运气不太好,数据库服务器的配置不是特别的高的话,弄不好你还会经历数据库宕机的情况,因为负载太高对数据库压力太大了。 4、多台服务器分库支撑高并发读写首先我们先考虑第一个问题,数据库每秒上万的并发请求应该如何来支撑呢? 要搞清楚这个问题,先得明白一般数据库部署在什么配置的服务器上。通常来说,假如你用普通配置的服务器来部署数据库,那也起码是 16 核 32G 的机器配置。 ...

May 15, 2019 · 5 min · jiezi

高并发架构的TCP知识介绍

做为一个有追求的程序员,不能只满足增删改查,我们要对系统全方面无死角掌控。掌握了这些基本的网络知识后相信,一方面日常排错中会事半功倍,另一方面日常架构中不得不考虑的高并发问题,理解了这些底层协议也是会如虎添翼。 本文不会单纯给大家讲讲TCP三次握手、四次挥手就完事了。如果只是哪样的话,我直接贴几个连接就完事了。我希望把实际工作中的很多点能够串起来讲给大家。当然为了文章完整,我依然会从 三次握手 起头。 再说TCP状态变更过程不管是三次握手、还是四次挥手,他们都是完成了TCP不同状态的切换。进而影响各种数据的传输情况。下面从三次握手开始分析。 本文图片有部分来自网络,若有侵权,告知即焚三次握手来看看三次握手的图,估计大家看这图都快看吐了,不过为什么每次面试、回忆的时候还是想不起呢?我再来抄抄这过剩饭吧! 首先当服务端处于 listen 状态的时候,我们就可以再客户端发起监听了,此时客户端会处于 SYN_SENT 状态。服务端收到这个消息会返回一个 SYN 并且同时 ACK 客户端的请求,之后服务端便处于 SYN_RCVD 状态。这个时候客户端收到了服务端的 SYN&ACK,就会发送对服务端的 ACK,之后便处于 ESTABLISHED 状态。服务端收到了对自己的 ACK 后也会处于 ESTABLISHED 状态。 经常在面试中可能有人提问:为什么握手要3次,不是2次或者4次呢? 首先说4次握手,其实为了保证可靠性,这个握手次数可以一直循环下去;但是这没有一个终止就没有意义了。所以3次,保证了各方消息有来有回就足够了。当然这里可能有一种情况是,客户端发送的 ACK 在网络中被丢了。那怎么办? 其实大部分时候,我们连接建立完成就会立刻发送数据,所以如果服务端没有收到 ACK 没关系,当收到数据就会认为连接已经建立;如果连接建立后不立马传输数据,那么服务端认为连接没有建立成功会周期性重发 SYN&ACK 直到客户端确认成功。再说为什么2次握手不行呢?2次握手我们可以想象是没有三次握手最后的 ACK, 在实际中确实会出现客户端发送 ACK 服务端没有收到的情况(上面的情况一),那么这是否说明两次握手也是可行的呢?看下情况二,2次握手当服务端发送消息后,就认为建立成功,而恰巧此时又没有数据传输。这就会带来一种资源浪费的情况。比如:客户端可能由于延时发送了多个连接情况,当服务端每收到一个请求回复后就认为连接建立成功,但是这其中很多求情都是延时产生的重复连接,浪费了很多宝贵的资源。 因此综上所述,从资源节省、效率3次握手都是最合适的。话又回来三次握手的真实意义其实就是协商传输数据用的:序列号与窗口大小。 下面我们通过抓包再来看一下真实的情况是否如上所述。 20:33:26.583598 IP 192.168.0.102.58165 > 103.235.46.39.80: Flags [S], seq 621839080, win 65535, options [mss 1460,nop,wscale 6,nop,nop,TS val 1050275400 ecr 0,sackOK,eol], length 020:33:26.660754 IP 103.235.46.39.80 > 192.168.0.102.58165: Flags [S.], seq 1754967387, ack 621839081, win 8192, options [mss 1452,nop,wscale 5,nop,nop,nop,nop,nop,nop,nop,nop,nop,nop,nop,nop,sackOK,eol], length 020:33:26.660819 IP 192.168.0.102.58165 > 103.235.46.39.80: Flags [.], ack 1754967388, win 4096, length 0抓包: sudo tcpdump -n host www.baidu.com -S ...

May 7, 2019 · 2 min · jiezi

35条建议让你对Java-代码性能优化彻底理解

代码优化,一个很重要的课题。可能有些人觉得没用,一些细小的地方有什么好修改的,改与不改对于代码的运行效率有什么影响呢?这个问题我是这么考虑的,就像大海里面的鲸鱼一样,它吃一条小虾米有用吗?没用,但是,吃的小虾米一多之后,鲸鱼就被喂饱了。 代码优化也是一样,如果项目着眼于尽快无BUG上线,那么此时可以抓大放小,代码的细节可以不精打细磨;但是如果有足够的时间开发、维护代码,这时候就必须考虑每个可以优化的细节了,一个一个细小的优化点累积起来,对于代码的运行效率绝对是有提升的。 代码优化的目标是 减小代码的体积提高代码运行的效率代码优化细节 1、尽量指定类、方法的final修饰符带有final修饰符的类是不可派生的。在Java核心API中,有许多应用final的例子,例如java.lang.String,整个类都是final的。为类指定final修饰符可以让类不可以被继承,为方法指定final修饰符可以让方法不可以被重写。如果指定了一个类为final,则该类所有的方法都是final的。Java编译器会寻找机会内联所有的final方法,内联对于提升Java运行效率作用重大,具体参见Java运行期优化。此举能够使性能平均提高50%。 2、尽量重用对象特别是String对象的使用,出现字符串连接时应该使用StringBuilder/StringBuffer代替。由于Java虚拟机不仅要花时间生成对象,以后可能还需要花时间对这些对象进行垃圾回收和处理,因此,生成过多的对象将会给程序的性能带来很大的影响。 3、尽可能使用局部变量调用方法时传递的参数以及在调用中创建的临时变量都保存在栈中速度较快,其他变量,如静态变量、实例变量等,都在堆中创建,速度较慢。另外,栈中创建的变量,随着方法的运行结束,这些内容就没了,不需要额外的垃圾回收。 4、及时关闭流Java编程过程中,进行数据库连接、I/O流操作时务必小心,在使用完毕后,及时关闭以释放资源。因为对这些大对象的操作会造成系统大的开销,稍有不慎,将会导致严重的后果。 5、尽量减少对变量的重复计算明确一个概念,对方法的调用,即使方法中只有一句语句,也是有消耗的,包括创建栈帧、调用方法时保护现场、调用方法完毕时恢复现场等。所以例如下面的操作: for (int i = 0; i < list.size(); i++){...}建议替换为: for (int i = 0, int length = list.size(); i < length; i++){...}这样,在list.size()很大的时候,就减少了很多的消耗 6、尽量采用懒加载的策略,即在需要的时候才创建例如: String str = "aaa";if (i == 1){list.add(str);}建议替换为: if (i == 1){String str = "aaa";list.add(str);}慎用异常 异常对性能不利。抛出异常首先要创建一个新的对象,Throwable接口的构造函数调用名为fillInStackTrace()的本地同步方法,fillInStackTrace()方法检查堆栈,收集调用跟踪信息。只要有异常被抛出,Java虚拟机就必须调整调用堆栈,因为在处理过程中创建了一个新的对象。异常只能用于错误处理,不应该用来控制程序流程。 8、不要在循环中使用try…catch…,应该把其放在最外层除非不得已。如果毫无理由地这么写了,只要你的领导资深一点、有强迫症一点,八成就要骂你为什么写出这种垃圾代码来了。 9、如果能估计到待添加的内容长度,为底层以数组方式实现的集合、工具类指定初始长度比如ArrayList、LinkedLlist、StringBuilder、StringBuffer、HashMap、HashSet等等,以StringBuilder为例: (1)StringBuilder() // 默认分配16个字符的空间(2)StringBuilder(int size) // 默认分配size个字符的空间(3)StringBuilder(String str) // 默认分配16个字符+str.length()个字符空间可以通过类(这里指的不仅仅是上面的StringBuilder)的来设定它的初始化容量,这样可以明显地提升性能。比如StringBuilder吧,length表示当前的StringBuilder能保持的字符数量。因为当StringBuilder达到最大容量的时候,它会将自身容量增加到当前的2倍再加2,无论何时只要StringBuilder达到它的最大容量,它就不得不创建一个新的字符数组然后将旧的字符数组内容拷贝到新字符数组中—-这是十分耗费性能的一个操作。试想,如果能预估到字符数组中大概要存放5000个字符而不指定长度,最接近5000的2次幂是4096,每次扩容加的2不管,那么: (1)在4096 的基础上,再申请8194个大小的字符数组,加起来相当于一次申请了12290个大小的字符数组,如果一开始能指定5000个大小的字符数组,就节省了一倍以上的空间; (2)把原来的4096个字符拷贝到新的的字符数组中去。 这样,既浪费内存空间又降低代码运行效率。所以,给底层以数组实现的集合、工具类设置一个合理的初始化容量是错不了的,这会带来立竿见影的效果。但是,注意,像HashMap这种是以数组+链表实现的集合,别把初始大小和你估计的大小设置得一样,因为一个table上只连接一个对象的可能性几乎为0。初始大小建议设置为2的N次幂,如果能估计到有2000个元素,设置成new HashMap(128)、new HashMap(256)都可以。 10、当复制大量数据时,使用System.arraycopy()命令11、乘法和除法使用移位操作例如: for (val = 0; val < 100000; val += 5){a = val * 8;b = val / 2;}用移位操作可以极大地提高性能,因为在计算机底层,对位的操作是最方便、最快的,因此建议修改为: ...

April 29, 2019 · 3 min · jiezi

阿里云栖开发者沙龙PHP技术专场聊聊服务稳定性保障这些事

摘要:本文主要带大家了解服务稳定性的重要性和相关策略。策略大概分两部分,第一方面从架构层面介绍保障服务稳定性的常见策略(限流,降级,隔离,超时,重试和集群)。第二个方面是从流程方面(code review, 压测,灰度和监控)讲解怎么去保证稳定性。演讲嘉宾简介: 信海龙(花名沧龙),十余年的互联网开发经验,2013年加入阿里巴巴,深耕于电商、社区相关应用开发与架构。同时也是多个开源项目的开发者和维护者。代表开源作品,tclip,基于人脸识别的图片裁剪扩展。 本次直播视频精彩回顾,戳这里!直播回顾:https://yq.aliyun.com/live/965PPT分享:https://yq.aliyun.com/download/3530以下内容根据演讲嘉宾视频和PPT分享整理而成。 本次的分享主要围绕以下三个方面: 稳定性的重要性保障策略架构篇保障策略流程篇稳定性的重要性对很多企业来说服务稳定性非常重要,首先稳定性问题会对企业带来直接的经济损失。举例来说,亚马逊的“Prime Day”当天出现的一个故障,给亚马逊带来了高达9900万美元的损失。这一个故障损失就可能是其它小公司市值的几倍。所以服务稳定性对公司影响是特别大的。而对于个人来说,服务不稳定性会影响员工的绩效,甚至影响个人前程。 保障策略架构篇从架构层面保障稳定性,常见的策略包括限流,降级,隔离,超时,重试和集群等。 1.限流限流目的 限流的目的主要有两点,第一点是防止系统高负荷运行,第二点是有效利用服务器资源。为什么要做限流?假如不封锁请求,可能会导致服务器报警,如果平时服务器只能处理100个请求,突然多出两个请求服务器或许勉强能够处理,但突然多了500个请求的话,后面的400个请求只处在积压状态,等服务器处理到第500个请求的时候,用户等待时间就会过长,而且最后积压部分的请求可能根本就是无效的处理,因为用户早已流失。 限流算法 常见限流的算法包括漏桶算法和令牌桶算法。漏桶算法如下图,图中的例子有个小桶,桶下面有个孔,每流一滴水就可以认为是一个请求进去。滴水的速率是一样的,孔的高度也是固定的。漏桶算法能保证每个请求的负载时长,即确定每秒能处理的请求数量。 漏痛算法实现如下图,可以设定桶的高度是5个,每秒漏两个。执行效果中前面5次结果都是true,之后中间一次结果是false,这说明桶已经装满,之后又漏了两滴水,因为false的时候sleep了一秒,所以下面又有两个true出来。 令牌桶算法 如下图,令牌桶算法也是有一个桶,但是桶不漏,桶里面放了一些令牌,每来一个请求就在桶里拿一个令牌,如果没有令牌它就可以等待,令牌满了就不再往里面加令牌。这样方法基本上也可以达到一个限流的目的。令牌桶算法和漏桶算法的一个显著区别是漏桶算法在后端取请求量时,基本上漏的速率是一样的,但是令牌桶算法中后端部分可以有突发请求,如果桶满了,可以将桶里所有令牌都拿走。 下图是令牌桶算法lua代码实现部分,当然读者还可以使用Nginx,Java脚本或者php脚本来实现。 2.降级社区降级案例 一般情况下,系统上线之后总会遇到一些不稳定情况,比如redis挂掉,甚至后端数据库My SQL挂掉。当出现不稳定情况之后,系统如何保证继续提供这些服务。以社区案例为例,即便是My SQL挂掉,也要能够保证社区为用户提供基本的可读服务。其中一个策略是将一些热点数据,即用户经常浏览的信息或者最新的信息缓存起来,当后端服务不可用的时候,把这些数据展现给用户。大概流程如下图,数据存储部分后端会有一个脚本去分析Nginx里面的日志,然后去请求Vanish,Vanish再去请求后端,这样的话Vanish会有一个有效期,能够保证Vanish存进去的数据都是用户经常访问的一些数据。第二步,如何保证后端数据库挂掉的数据时候能迁过去?下图可以看到,Nginx中使用lua脚本进行实现,它会检测后端服务返回的一些状态,使用计数器计算失败次数,如果频繁的达到一定程度的失败次数,就切换到从Vanish获取数据,最后推送给用户。这样能保证即便是后端的数据库挂掉,甚至即便所有的php进程都挂掉的时候,社区也能给用户提供一些基本的服务。 降级目的 降级的目的比较简单,第一个是保障服务器基本可用,第二个是保障服务的核心服务可用。降级是怎么一个思路呢?一般降级的每个策略都是针对一个场景,预想特定场景下需要要解决什么问题;然后再梳理在这个场景下需要保留哪些核心基本服务;最后才选定技术方案,系统化的进行实现。简单讲就是先确定需要达到什么目的,再去了解是什么样的情况,最后制定策略或者计划。比如,系统会调用第三方服务,而第三方服务有可能挂掉,这是一种典型的场景。再比如,系统本身调用推荐服务,但是推荐服务也会挂掉,这种场景下不能够因为没有推荐数据就不显示数据,还是需要展示一些数据,这是一种基本的核心服务。每年的双11或者一些大型活动中基本都会存在降级。降级不仅仅是存在于资源故障场景下,资源不够用时也可能会需要降级,因为资源不够用需要关注重点。如大促活动中,需要先保证交易服务正常运行,其它消耗资源的服务(如对账)可以后续再去处理。 3.超时超时案例 社区对外提供接口服务,对方的反馈是接口服务较慢。接口部分流程是查一段数据,然后将数据反映过去,其问题点在于系统中超时时间设置过长。比如调用Memcache,但是Memcache已经挂掉,由于超时设置过长,数据需要等到超时时间结束以后再返回,导致接口一直在等待。那如何设置超时时间才合理?要注意超时时间并不是固定的值,而是需要针对整个业务,根据特定场景设置超时时间值。 如何设置超时时间 大体的思路如下图。第一步,识别业务需要的服务响应时间。比如,需要100毫秒去响应数据,之后统计业务里面可能需要调多少服务。第二步,统计服务日常的响应时间。第三步,分清主次,即分出哪些是核心服务。因为核心服务一旦失败,整个链路便不可用,所以可以对核心服务的时间设置的宽松一些。如果一些服务调不通,但又不影响整个链路,可以对它的时间设置的相对严格。 设置完超时之后需要验证,借助模拟手段封端口(如下图),模拟故障,然后检查数据返回时间是否在指定的时间内。 4.隔离隔离案例 下2013年左右,手机客户端开始逐渐升级起来,很多项目既有PC端也有客户端,所以同一个服务即要为PC端又要为客户端提供API接口。一旦遇到大型活动或者需要手机推送,服务会遇到不稳定情况,服务的不稳定会导致PC端也受影响,所以需要将服务进行物理隔离,从原先耦合到一块的服务器分到不同的机器组。隔离目的非常简单,要限制住不稳定因素导致的风险,停止传播。 隔离形式 隔离的常见形式包括几种。第一是秒杀场景,秒杀场景一个高并发的场景,可能带来的问题也比较多,在高并发场景下秒杀的时候,需要和一些正常的业务区分开来,不建议一台机器既提供秒杀也提供进程服务。另外,秒杀的时候会产生热点数据,如售卖数据。数据库更新比较频繁,从数据库层面也可以进行隔离,将热点部分和正常服务部分从资源上隔离。第二个场景是慢SQL隔离,一个资源隔离。一条慢SQL会导致整个服务不稳定。每请求一次线程,慢SQL会一直耗着当前线程,所以资源占用非常大。第三个场景是机房隔离。一般大公司都会做多机房部署,其目的就是确保稳定性。确保稳定性时不要做跨机房调用,否则耦合度会比较高,假如A调B,B挂掉,A服务也会受影响。一般确保稳定性都是做本机房的调用。而且本机房的调用性能也比较快。最后一个场景是进程隔离,因为进程比线程更加稳定。 5.集群对小公司而言,一台机器就提供一个服务,如果机器挂掉服务恢复就会成为一个问题。一般解决方法是做一个集群,从原来的一台机器提供服务变为可以用多台机器提供服务。集群的目的是为了解决单点的问题。集群的形式主要有主备,即同时只有一台机器提供整个服务,可以有一台或者多台提供备份,备份不仅要包含代码层面,整个服务运行所依赖的资源都要有备份。另外一个形式是主从。主是提供一个完整的服务,从是提供部分的服务。还有一种是多主,多主指的是每一台机器的决策是对等的,都会对外提供一些服务。随着集群形式的不同,对代码编写的并发性上有一定要求。主备只需要考虑单机的并发控制,主从是考虑同时提供服务的部分。比如加锁,主备上只要加一个本地的技能锁就可以,主从或者多主则需要加分布式锁。 保证策略流程篇保证稳定性策略的流程方面上分为下图中四个点,code review, 压测,灰度和监控。 1.Code reviewcode review目的是在项目上线前及时发现一些问题。经验比较丰富的人可以将经验进行分享。code review基本经过三个阶段。第一个阶段是头脑风暴式,一群开发人员围着代码做code review,虽然时间成本较高,效果也不太理想,但是这种方式也有好处,在前期可以将大家的意见进行整理,制定code review的规范。第二种code review形式是演讲式,专家事先把代码做一下review,整理一些点,然后进行分享。演讲式可以按照轮岗制,相对头脑风暴式大大节约了时间。目前常见的code review 形式是结对式,由一个或者两个专家结对,相互review,时间上比较灵活,也不需要占据会议室资源。 2.压测压测目的 压测的目的,第一是保证系统稳定性。在高并发的时候,检测系统是否稳定,因为一些问题在流量比较低的时候发现不了,只有在高并发的时候才能发现这个问题。第二是检测性能的抗压能力,检查系统能承受多大的QPS。 压测关注点 首先,压测机器和被压测服务在同一网段,尽量避免因为网络原因导致压测的结果不准确。第二点是关注服务器的负载,注意不要把服务器压到100%,服务器快要崩的时候,得到的值意义不大。应该是服务器负载达到60%~70%的时候,看QPS是多少。另外,压测并发数据是逐步递增的过程,到一个点的时候,并发数据越多代表QPS越低。最后,根据测试环境的压测结果估算线上的承载能力。估算的公式是线上QPS = 单机QPS 机器数 0.7。后面会乘以一个系数(0.7)是因为线上put上去的时候总会存在一些损耗。 ...

April 29, 2019 · 1 min · jiezi

从濒临解散到浴火重生OceanBase-这十年经历了什么

阿里妹导读:谈及国产自研数据库,就不得不提 OceanBase。与很多人想象不同的是,OceanBase 并非衔着金钥匙出生的宠儿。相反,它曾无人看好、困难重重,整个团队甚至数度濒临解散。从危在旦夕到浴火重生,OceanBase 这十年经历了什么?今天,我们一起了解它背后不为人知的故事。 OceanBase 是完全由阿里巴巴和蚂蚁金服自主研发、全球首个应用于金融核心业务的分布式关系数据库。OceanBase 的研发始于 2010 年 6 月,因为选择从零开始,研发之路从一开始就磨难重重,中途因为找不到愿意使用的业务,团队曾经濒临解散。 最终 OceanBase 还是跨越了死亡之谷,在蚂蚁金服实现了全面替代 Oracle,成功支撑了过去 5 年“双 11”蚂蚁金服全部核心业务的重压,创造了 25.6 万笔 / 秒支付峰值和 4200 万笔 / 秒请求数处理峰值这一业内全新的纪录。自 2017 年开始,OceanBase 开始走向外部商用,目前已经在数十家商业银行落地,其中包括南京银行、浙商银行、苏州银行、人保健康险等。OceanBase 帮助南京银行共同打造“鑫云 +”互金开放平台,实现贷款交易处理能力 10 倍提升,轻资产模式显著降低成本,从原有的 30~50 元 / 账户降低到上线后的 4 元 / 账户。日处理百万笔放款,平均处理时间小于 1 秒,让老百姓借钱更方便,真正实现了普惠金融。 站在现在这个时间点上顾盼今昔,蚂蚁金服高级研究员、OceanBase 创始人阳振坤认为,OceanBase 的成功其实有行业和时代的必然性。 时 机2009 年开始,大量新的非关系型数据库如雨后春笋般涌出,在整个数据库行业掀起了一场空前盛大的 NoSQL 革命,如今赫赫有名的 Redis、MongoDB 皆诞生于那一年。NoSQL 的拥护者们积极提倡使用非关系型的数据存储,从而获得丰富而随需应变的可伸缩性。这时候的关系数据库早已过了而立之年,在此期间虽然曾短暂爆发过一些所谓终结关系数据库的革命,但最终都失败了,丝毫没有动摇到关系数据库的主导地位。 但这一次似乎与以往不同,火热发展的云计算带来了对更大规模数据库的需求,而关系数据库的缺点则相应地被越来越多人诟病:不能够扩展、容量小、处理能力不够、成本又非常高。在当时的很多人看来,关系数据库的末日是真的要来了。2010 年,NoSQL 革命愈演愈烈,有行业专家发文直指“云计算时代属于 NoSQL,关系数据库已经日薄西山”。 那时阳振坤已经做了两年多的自研分布式系统,十分看好云计算系统的发展机会。同一年,阳振坤加入阿里巴巴,开始了分布式关系数据库 OceanBase 的研发。 数据库从诞生起已经有几十年的时间了,但基本上它的市场格局就没有多少变化,最早起来的几家厂商今天还是占据着统治地位。因为数据库非常难被替换,它处在整个产品或者产业链最底层的位置,替换风险很大,但收益相比起来却小得多。这也是为什么像 IBM、微软这样的后来者也无法取代 Oracle。这就导致了数据库变成了一个门槛极高、强者恒强的领域,后来者很难居上。前有 Oracle 挡道、后有 NoSQL 数据库追赶,在大部分人看来,那时候怎么也不会是自研关系数据库的好时机,但阳振坤却不这么想。 ...

April 26, 2019 · 2 min · jiezi

云原生时代来袭 下一代云数据库技术将走向何方?

全面云化的时代已经到来,面对一系列的新技术和挑战,数据库市场将面临怎样的变革?作为云服务提供商,如何帮助更多的企业级用户把握“云”潮,提供最高效、最具价值的数据库解决方案?日前,在阿里云峰会·北京站的数据库专场上,阿里巴巴集团副总裁、达摩院首席数据库科学家、阿⾥云智能事业群数据库产品事业部总负责⼈李飞飞针对下一代云原生数据库的技术与挑战进行了精彩分享。数据库发展与技术演进之路根据DB-Engine 2019年1月份的数据库市场趋势分析,关系型数据库依旧占据着最核心的市场份额。与此同时,数据库市场也在不断细分,图数据库、文档数据库以及NoSQL等数据库细分市场正在崛起。另一大趋势则是:以Oracle、DB2和Microsoft SQL Server三大巨头为代表的传统商业数据库所占据的市场份额不断下降,而开源和第三方数据库市场持续增长。数据库技术诞生至今,虽然已经历40多年的发展历程,但仍旧处于蓬勃发展的时期。如今,各大云计算厂商也达成了共识:数据库是连接IaaS和云上智能化应用的重要组成部分,因此从数据的产生、存储以及消费等各个环节,云产商都需要提升全链路的能力,进而满足用户连接IaaS和智能化应用的需求。随着数据库技术的不断发展,不仅出现了OLTP系统来实现事务的处理,可以实现对交易数据的实时记录;还出现了OLAP系统,借助OLAP系统可实现对于海量数据的实时分析。除此之外,还需要各类的数据库服务和管控工具支持核心的OLTP和OLAP系统。在此基础之上,针对半结构化数据和非结构化数据,NoSQL数据库解决方案应运而生。上世纪70年代末到80年代初,诞生了关系型数据库,产生了SQL查询语言以及OLTP交易处理系统。随着数据的爆发式增长以及复杂分析需求的出现,诞生了数据仓库,以及OLAP在线分析处理系统以及ETL等数据处理技术。技术发展到今天,图、文档以及时空、时序等多元异构数据量持续增长,因此也对应地出现了关系型数据库之外的NoSQL和NewSQL数据库系统。云原生时代 我们需要怎样的数据库?传统数据库往往采用单节点架构,而到了如今的云原生时代,云原生数据库通常会采用共享存储的架构方式。阿里云POLARDB所采用的就是共享存储架构,通过高速网络构建共享存储,在此之上实现存储与计算的分离,进而可以快速弹性扩容出多个计算节点。与此同时,POLARDB还可以根据用户的具体需求在存储和计算两个维度迅速地实现扩缩容。而对于用户而言,无需修改任何业务逻辑,就可以使用基于共享存储的POLARDB数据库,可以实现无侵入式迁移。除了云原生的共享存储技术之外,面对高并发、海量数据访问的挑战,也需要借助分布式架构来解决,比如为了应对每年的双11大促,阿里巴巴自身就需要去探索分布式架构。此外,针对数据的多模多态需求,阿里云也希望在用户侧提供不同查询接口,比如SQL。在存储侧,阿里云希望能够支持用户将数据存储到不同地方,并通过像SQL这样的统一接口实现对不同数据类型的统一查询访问。目前,阿里云所提供数据湖服务就是针对上述场景所演进来的云原生技术。正如前面所提到的OLTP和OLAP系统,传统的解决方案希望将读写冲突隔离开,让OLTP负责事务处理,让OLAP负责海量数据的分析任务。而在云原生时代,阿里云会借助新硬件所带来的技术红利,尽可能降低数据迁移的成本,将事务处理和数据分析整合在同一个引擎中,通过一套系统为用户无缝解决两种问题。阿里云目前已经服务了大量企业级客户,这些客户通过虚拟化、存储与计算分离来使用阿里云所提供的云资源池。因此,我们需要对于云上全部资源进行智能化监控和调配,做到快速响应,并且为用户提供最高质量的服务。而智能化的背后需要利用机器学习以及人工智能技术,从数据迁移、数据保护、弹性调度等各个维度实现自感知、自决策、自恢复以及自优化。在云原生时代,另外一个重要技术就是软硬件一体化设计。新硬件的发展为数据库系统带来了许多可不断利用的技术红利,比如RDMA网络、SSD、NVM、GPU、IPG加速等。阿里云的POLARDB共享存储就利用了RDMA网络,因此可以做到像本地节点一样快速访问远程数据库节点。对于很多云上的客户而言,可能也有金融级高可用的需求。利用高可用协议,阿里云数据库可以采用三副本架构,在本地可以实现数据库之间的无缝实时切换,在异地也可以满足不同用户对于灾备的需求,借助Binlog技术实现异地数据同步,实现金融级高可用。此外,云上用户对数据的安全性尤为看重,阿里云数据库服务从数据落盘开始,就提供了加密技术,确保全链路的数据安全。阿里云数据库服务:全域布局自主可控 兼具创新与商用价值阿里云数据库所提供的工具类产品包括数据备份、数据迁移、数据管理、混合云数据管理以及智能化诊断与优化系统等,可以帮助客户实现快速上云并且打造混合云解决方案。我们提供的核心引擎产品中,既有自主可控的自研产品,也有第三方以及开源的产品。希望通过商业数据库以及开源产品为用户提供丰富的选择,同时也希望将云计算的技术红利整合到自研数据库产品中去,进一步做深做透,真正地帮助客户解决应用第三方或者开源数据库所无法解决的痛点和问题。在OLTP方向,阿里云数据库所提供的核心产品是POLARDB以及其分布式版本POLARDB X。除此之外,阿里云也提供主流的MySQL、PostgreSQL、SQLServer以及针对于Oracle兼容的PPAS等一系列服务。针对OLAP系统,我们的核心产品是AnalyticDB、针对多源异构数据所提供的数据湖服务DLA,以及针对IoT场景的时序时空数据库TSDB。而在NoSQL领域,阿里云则提供了丰富的第三方数据库产品供客户选择,比如HBase、Redis、MongoDB以及阿里云自研的图数据库GDB等。阿里云数据库的管控平台与全链路监控服务为用户提供了智能化的全链路检测与分析,保障阿里云数据库能够为用户提供最高级别的Service Level。阿里云帮助客户打造了一条线上线下混合云数据存储的链路,从客户迁移上云开始,其可以选择阿里云DTS服务进行实时的数据上传和同步。数据上云之后,客户可选择POLARDB等云原生数据库产品进行存储,也可以借助DLA或者AnalyticDB等产品进行数据分析。而针对特定场景进行数据分析可以选用文档数据库、图数据库或者时序时空数据库等解决方案,可以借助DTS系统实现线上线下的数据同步备份,并且还可以借助HDM来进行混合云数据库管理。此外,阿里云数据库服务还提供了数据库管理套件,可以支撑用户对数据库进行管理和开发,使得管理和开发流程更为高效。这里主要为大家介绍两种云原生数据库产品,POLARDB和AnalyticDB。POLARDBPOLARDB利用RDMA网络实现了高效的共享分布式存储,借助于共享存储技术,多个计算节点中可以实现“一写多读”,并且可以根据客户的工作量需求,快速弹出多个只读节点来满足客户在高峰时刻对于计算的需求,同时也可以在存储节点上实现快速缩扩容。针对客户的应用场景以及其业务峰值峰谷的波动情况,POLARDB可以做到按量按需使用和计费,进而大大地提升了客户的数据库使用效率,节省了所需要成本。总体而言,POLARDB就是一个超级MySQL,后续POLARDB还会陆续推出兼容PostgreSQL和Oracle的版本。在一些场景下,用户需要面对高并发和海量数据访问的挑战,因此需要突破共享存储的容量上限。分布式架构的POLARDB X则利用Sharding Partition解决方案实现了存储容量的无限水平扩展。POLARDB X分布式版本也会在后续进行公测,欢迎大家试用。AnalyticDB对海量数据进行分析的时候,读写会产生一定的冲突。如果需要读取大量数据并进行分析将会极为复杂,因此推荐大家使用阿里云的实时交互式分析数据库系统AnalyticDB。AnalyticDB最核心的特点是具有支持高吞吐写入的能力,并且具有针对于行列存储所研发的存储引擎,因此可以实现实时的交互式分析。在海量数据以及高并发场景下,AnalyticDB在响应时间方面的表现都非常优秀。AnalyticDB兼容了MySQL生态,因此可以将MySQL里面的数据直接导入AnalyticDB中,可以实现对上百亿级别的数据的毫秒级查询以及百万TPS级别的写入。数据传输云服务DTS除了核心的云原生数据库产品,我们还有多款数据库工具型产品,比如数据传输云服务DTS。DTS所解决的痛点是云下客户上云所需要进行的数据传输问题,以及其在上云之后,云上与云下数据库或者从TP到AP系统之间进行实时的数据同步问题。利用DTS,用户可以实现快速、高效地增量数据同步,保证了实时数据一致。DTS还提供了数据订阅能力,可以通过不同的协议和接口来接入更多不同的数据源。数据库家族迎来新成员:图数据库GDB这里为大家介绍一款阿里云新的数据库产品——阿里云图数据库GDB,该产品目前正在阿里云官网进行公测。GDB是一种支持属性图模型,用于处理高度连接数据查询与存储的实时可靠的在线数据库,其利用了大量的云原生技术,比如存储与计算分离等。GDB支持标准的图查询语言,兼容Gremlin语法,这一点与市场主流的图数据库保持一致。GDB另外一个最核心的特点就是支持实时更新、支持OLTP级别的数据一致性,能够帮助大家在对海量的属性图进行分析存储时保证数据的一致性。GDB具有服务高可用、易于维护等云原生数据库产品的特性。比较典型的应用场景有社交网络、金融欺诈检测、实施推荐等,同时GDB还支持知识图谱等形态以及神经网络等。以赋能客户为本:降本提效 无忧发展阿里云数据库团队的目标是为客户提供企业级的云原生数据库服务,利用自身全域布局、自主可控的技术为企业客户提供快速数据上云、云上云下统一数据管理以及数据安全等服务。举例而言,阿里云数据库服务目前在杭州等城市支撑了像城市大脑这样复杂的应用,其既需要存储结构化数据也需要存储非结构数据,并且对于OLTP、OLAP、工具类产品等都提出了巨大的挑战,而借助阿里云AnalyticDB、POLARDB、DTS等利用了云原生技术的产品可以无缝地支撑城市大脑这样的复杂应用场景。以POLARDB为例,该产品于2018年8月份进行公测,2018年的年底进行商业化,到目前为止实现了在公有云平台上的快速增长。POLARDB呈现快速增长背后的核心原因就是阿里云真正地帮助客户解决自身的痛点问题,不是利用新技术来“造完锤子找钉子”,而是真正地“看到钉子再去造锤子”。POLARDB最为核心的特点就是云原生、分钟级别的弹性存储和计算、高性价比、灵活弹性的使用计费方式、高并发能力,可快速扩容多个只读节点、大容量支持、通过共享分布式存储做到了类似单机数据库的体验,对用户的业务逻辑无侵入,并且高度兼容MySQL。AnalyticDB则是一个实时交互式分析系统,无论是自制数据还是从大数据系统中获取的存储数据,都可以借助DTS工具将其迁移到AnalyticDB集群上,进行深入的商业分析、可视化以及交互式查询等。AnalyticDB能够支持上百个表的连接查询,可为客户提供毫秒级的查询服务。如今,无数用户正在阿里云上使用POLARDB和AnalyticDB这样的云原生数据库,云原生数据库也正在真正地改变客户在应用中所遇到的痛点,为他们带来更多的业务价值。总结而言,云原生时代出现了一系列的新技术和新挑战。面对这些挑战,需要对于数据库内核产品、管控平台以及数据库工具进行有机整合,才能够为客户提供最高效、最具有价值的解决方案。阿里云诚挚地邀请大家体验自身的产品和技术,希望能够与更多的客户一起合作,解决问题,也希望更多的开发者和生态合作伙伴能够基于阿里云的数据库服务和产品打造针对特定行业和领域的深度解决方案,使得云原生时代的数据库市场更加繁荣。本文作者:七幕阅读原文本文为云栖社区原创内容,未经允许不得转载。

April 19, 2019 · 1 min · jiezi

蚂蚁金服高级研究员阳振坤:为什么我们要选择自研数据库这条艰难之路

“如果大家当时能看见原来十年后OceanBase能长成这样,可能十年前OceanBase得到的支持会好很多。但是这种如果是不存在的,很多时候你要先证明自己。” 根据工信部数据显示,1998年,中国软件企业5000家,市场规模325亿;到了2018年底,中国软件企业3.78万家,收入规模超过6.3万亿元,营收增长了193.8倍。可在最核心的基础设施三大件芯片、操作系统和数据库上,过去我们并未取得商用意义上的重大突破。不过,相比芯片和操作系统,国内数据库领域的局面要略微乐观一些。除了传统的数据库厂商、数据服务商,互联网巨头、云计算厂商、硬件厂商、新兴的创业公司也越来越多地投入到数据库的研发中。而谈及国产自研数据库,就不得不提OceanBase。OceanBase是完全由阿里巴巴和蚂蚁金服自主研发、全球首个应用于金融核心业务的分布式关系数据库。OceanBase的研发始于2010年6月,因为选择从零开始,研发之路从一开始就磨难重重,中途因为找不到愿意使用的业务,团队曾经濒临解散。最终OceanBase还是跨越了死亡之谷,在蚂蚁金服实现了全面替代Oracle,成功支撑了过去5年“双11”蚂蚁金服全部核心业务的重压,创造了25.6万笔/秒支付峰值和4200万笔/秒请求数处理峰值这一业内全新的纪录。自2017年开始,OceanBase开始走向外部商用,目前已经在数十家商业银行落地,其中包括南京银行、浙商银行、苏州银行、人保健康险等。OceanBase帮助南京银行共同打造“鑫云+”互金开放平台,实现贷款交易处理能力10倍提升,轻资产模式显著降低成本,从原有的3050元/账户降低到上线后的4元/账户。日处理百万笔放款,平均处理时间小于1 秒,让老百姓借钱更方便,真正实现了普惠金融。站在现在这个时间点上顾盼今昔,蚂蚁金服高级研究员、OceanBase创始人阳振坤认为,OceanBase的成功其实有行业和时代的必然性。这是最坏的时代,也是最好的时代2009年开始,大量新的非关系型数据库如雨后春笋般涌出,在整个数据库行业掀起了一场空前盛大的NoSQL革命。这时候的关系数据库早已过了而立之年,在此期间虽然曾短暂爆发过一些所谓终结关系数据库的革命,但丝毫没有动摇到关系数据库的主导地位。但这一次似乎与以往不同,火热发展的云计算带来了对更大规模数据库的需求,而关系数据库的缺点则相应地被越来越多人诟病:不能够扩展、容量小、处理能力不够、成本又非常高。在当时的很多人看来,关系数据库的末日是真的要来了。那时阳振坤已经做了两年多的自研分布式系统,他十分看好云计算系统的发展机会。同一年,阳振坤加入阿里巴巴,开始了分布式关系数据库OceanBase的研发。数据库从诞生起已经有几十年的时间了,但基本上它的市场格局就没有多少变化,最早起来的几家厂商今天还是占据着统治地位。因为数据库非常难被替换,它处在整个产品或者产业链最底层的位置,替换风险很大,但收益相比起来却小得多。这也是为什么像IBM、微软这样的后来者也无法取代Oracle。这就导致了数据库变成了一个门槛极高、强者恒强的领域,后来者很难居上。前有Oracle挡道、后有NoSQL数据库追赶,在大部分人看来,那时候怎么也不会是自研关系数据库的好时机,但阳振坤却不这么想。加入阿里之后,阳振坤发现无论对淘宝还是支付宝,关系数据库都扮演着十分关键的角色,在使用上根本不可能摆脱。但已有的数据库,无论是商业数据库还是开源数据库,都有非常多的局限,远远无法满足如淘宝、支付宝这样的互联网和金融业务对高扩展、高并发、高可用和低成本的需求。单机数据库已经走到了尽头,下一步只能走向分布式,而分布式恰好是阳振坤所擅长的。如果能将分布式技术揉到数据库里面,解决单机数据库存在的各种问题,对当时整个互联网的基础设施都会是一个巨大的帮助和进步。阳振坤认为他们赶上了一个“天时地利人和”的好机会。“天时”指的是互联网的爆发式增长对数据库的高并发、大数据量提出了很大的需求,有了需求去推动就会容易得多;“地利”指的是阿里内部从淘宝到蚂蚁金服拥有大量需要使用数据库的场景,OceanBase可以从不是特别重要的应用场景开始尝试,一步步地将数据库做成关键系统;“人和”指的是当时单机数据库已经走到了尽头,下一步一定是走向分布式,而当时团队成员大多是研究分布式出身,做的就是自己最擅长的工作。用阳振坤的原话就是:“这是千载难逢的机会,我们一定要做,而且一定能做成。”一个不断“破格”的人“一个不断破格的人”,这是早前某次采访中记者对阳振坤的评价。1984年阳振坤考入北京大学数学系,硕士师从本系的张恭庆院士,后又转向计算机领域,博士师从计算机系的王选院士。需要强调的是,他修完大学课程只用了3年,硕士只用了一年多,成为王选院士博士生的时候他只有24岁。1995年其所在团队研究成果获国家科技进步一等奖(排名第四),1997年也就是他32岁那年被破格晋升为教授。在他人或许都安于现状之时,他却毅然选择了离校。个中原因也不复杂,他的工作更偏于工程,而在工业界有更多的机会,也能发挥更大的作用。2002年离开北大/方正的时候,阳振坤内心很清楚自己必须要做点不一样的事情。他先是加入联想研究院担任首席研究员,负责无线通信领域的研究;后来接触到分布式系统并看好其前景,在微软亚洲研究院、百度所从事的工作都属于分布式这个范畴,前者侧重研究,后者偏重工程实践。回想在北大的那些年,阳振坤觉得特别感激的是,学数学让他有了一个很好的数学基础,后来转到计算机系以后,碰到了王选老师,又打下了一个比较牢靠的计算机基础,这才有了他后来的今天。作为对阳振坤影响最大的人,恩师王选有两点让他至今受益:一是如何判断一件事情是否有价值,二是“顶天立地”的技术理念,“顶天”就是技术上要不断追求新突破,“立地”就是要把技术做成通用产品,让整个社会都能普遍使用。其实2010年去淘宝的时候,阳振坤根本不知道自己会做什么事情。加入淘宝之后,摆在他面前的有两个选择,一个是加入正在快速发展的淘宝业务团队,去主管技术,这是一条已经能看到很大的发展机会、相对轻松的道路;另一条是阳振坤后来自己选的,从头组建团队做一个技术平台,也就是今天我们看到的OceanBase数据库。从加入淘宝到选择做自研数据库,一共只花了两个星期的时间。这不是一个容易的选择,但阳振坤相信自己的判断:“2010年选这个项目的时候,我是觉得这件事情需要做。当时互联网迅速发展带来了对大数据量、高并发的需求,大家对传统单机数据库有很大的抱怨,觉得它既没有扩展能力,又没有高并发的能力,成本还非常高,但是互联网根本就离不开关系数据库。这件事情怎么看都是一件应该要做、需要做的事情。”阳振坤没有说出来的是,这件事到底有多难。那时候阿里巴巴刚开始要“去IOE”,几乎没人想着说要自己从头做一个数据库。传统关系数据库都是通过外部硬件来保证可用性,用便宜的PC机替换高端服务器之后,硬件更容易出故障了,如何保证数据库高可用?高可用和数据一致性如何同时保证?分布式系统怎么同时实现CAP的要求?几十年来这么多做数据库的厂商,国内国外基本没有人成功过。而且从公司的业务发展的角度,也不可能等你几年把数据库做出来,再去发展业务,更可行的做法是基于开源做出一些东西,让业务先往前走。因此OceanBase立项之初,除了阳振坤和他当时的直属领导,其他人对这个项目要么不关心,要么不赞成。从零开始自研分布式关系数据库并全面替换Oracle,在当时有多少人会相信这真的能做成呢?当时整个淘宝一共只有两三千人,而Oracle有十几万人,就算整个淘宝的人全部去做数据库,跟Oracle比起来也只是很小很小的一个比例。在阳振坤看来,如果一件事情几乎所有的人都认为它很重要、需要做,这件事情就已经不是创新了。当所有人都认为这件事情要做的时候,其实做这件事情的时机已经过去了一大半。作为最底层的基础软件设施,数据库需要很长时间的积累,不可能今年做,明年就能真正大规模地用起来。 虽然在2010年选择做数据库的时候,没有太多人看重和支持,对于团队来说这可能反而是一件好事。无人关注,反倒给了团队几年积累发展的时间。阳振坤不只要自研,还要把OceanBase定位成恩师王选所说的“顶天立地”的技术产品——走标准化的路,做一个通用的关系数据库产品,而不是一个仅仅在公司内部使用的产品。每个公司使用任何产品其实都只用了其中很小的一部分功能,如果只做满足公司自用需求的数据库,可能只需要投入十分之一、五分之一的人力物力时间。而要做成通用产品就意味着必须实现所有功能,这要困难得多,团队的投入、花费的精力和时间也要大好多倍。但也因为阳振坤最初的坚持,今天的OceanBase才得以走出蚂蚁金服,走进众多银行系统。不过这都是后话了。做数据库就像在黑暗中前行,守得住寂寞、担得了压力,甚至要有近乎偏执的性格才可能跨越死亡之谷,到达最终目的地。阳振坤团队中一位新人曾经向他表达过自己的困惑,当时这位新人入职三个月了,因为有太多东西要学,什么也没做出来,而跟他同时入职天猫的新员工才来了一个月,做的系统就已经在线上使用了。阳振坤当时给新人讲了一个故事,他说:“你过三年再看,没有人还记得那个同学三年前在天猫上把网页做了什么改版,可是三年以后你今天做的东西还会在生产系统中使用。”十年蛰伏,一飞冲天OceanBase的第一个客户来自淘宝收藏夹。当时的淘宝收藏夹正处于业务高速发展期,数据库的访问量飞快增长,面临着第二年服务器数量需要翻一倍甚至几倍的局面。业务方忙于寻找解决方案的时候,阳振坤主动找上门去提出了可以用OceanBase帮他们解决问题,把服务器数量降低一个数量级。四个月出Demo,八个月出试用版,一年后系统正式上线,淘宝收藏夹就这样成了第一个吃OceanBase螃蟹的业务,新数据库取得了非常好的效果。这时候是2011年,收藏夹项目成为了OceanBase第一个小小的里程碑。但在后续一年多的时间里,OceanBase团队一直在寻找更多业务,也确实有一些业务用了,却再也没有找到像淘宝收藏夹效果这么显著的业务。做数据库难度大、周期长,前几年的投入也许有那么一点点产出,但其实跟投入比几乎微不足道,团队面临的压力可想而知。数据库少不了人力投入,OceanBase团队从最早只有阳振坤一个人,后来发展到2012年已经有30多个人了。占了这么多人头,但在公司里却没有足够多、足够重要的业务,没能产生足够大的价值和效益。团队陷入了一个比较困难的时期,甚至数度濒临解散。当被问及“中间有没有想过这事如果没做成,怎么办?”,阳振坤回答得云淡风轻:“不是每件事都能做成,那太难了。如果每件事在做之前都想着它能不能做成,那最后做成的事就会很少。”在最困难也最危险的时候,团队迎来了一丝转机。2012年底,公司把OceanBase整个团队调到了支付宝。支付宝属于金融领域,面临的数据库挑战会比其他业务更大,这相当于给了OceanBase团队一次从头开始的机会。2013年夏天,支付宝也开始启动“去IOE”,并希望能够把Oracle数据库替换掉。阳振坤又一次主动出击,向当时的主管、也是现在蚂蚁金服的CTO程立自荐了OceanBase的解决方案。金融行业数据库,最怕的就是突发故障导致数据丢失,涉及到钱的事,多了少了都是不可接受的。为了解决高可用与主备库数据一致的矛盾,OceanBase将可用性做到了数据库系统内部,用一主两备或一主多备代替一主一备。主库到备库同步的时候不要求同步到每个备库,而是同步到包括主库在内的多数库(超过半数),也就是说总共三个库中如果有两个成功了,这个事务就成功了。如果任何一台机器出了问题,这个系统的可用性和数据一致性都是可以保证的。程立认可了阳振坤提出的方案,OceanBase团队开始埋头开发,第一个要攻克的目标是支付宝交易库。2014年双11,OceanBase迎来了第一次大考。大促开始前的凌晨,各个团队都在自己的作战室里热火朝天地准备。当时任蚂蚁金服董事长的彭蕾去了OceanBase团队的作战室,问大家:“有没有信心?”阳振坤跟彭蕾开了个玩笑说:“你看我们窗子都已经打开了,如果等会出问题,我们就准备从这跳下去。”在一开始的计划里,双11交易流量的1%会切给OceanBase,但因为当时的Oracle数据库系统支撑不了汹涌而来的巨大流量,最后OceanBase成功支撑了2014年双11中10%的交易流量。经过了双11的考验之后,OceanBase得到了更多的认可和支持。后来OceanBase团队获得了2015年蚂蚁金服的CEO大奖,这也是第一次由技术团队拿到这个奖。彭蕾希望借这个奖鼓励那些能够沉下心来、扎扎实实地把一项技术做好做扎实的技术人们。2015年春夏,支付宝交易库和支付库都换成了OceanBase;2016年,支付宝账务系统上线,这也标记着OceanBase真正在金融系统最核心最关键的领域站住了脚。从2017年开始,OceanBase开始走出支付宝、走出蚂蚁金服,在商业银行推广使用,最早的两家客户是浙商银行和南京银行。仅仅用了两年多的时间,OceanBase已经在人保健康险、常熟农商行、苏州银行、广东农信等数十家商业银行和保险机构上线。2017年10月,南京银行“鑫云+”互金开放平台正式发布,这是阿里云、蚂蚁金服合作整体输出的第一次尝试,通过“鑫云”+平台的建设,南京银行互金核心系统在交易处理能力、成本控制和对接效率都得到了极大的提升。南京银行传统的线下消费金融业务开展10年,余额100亿,而与互联网平台合作开展线上业务仅一年时间业务量已达到100亿。南京银行“鑫云+”平台上线后,业务快速增长,贷款交易处理能力全面升级,从原有的10万笔/天到上线后实现100万笔/天,对普惠金融起到了更有利的支撑。轻资产模式使得单账户管理成本约为传统IOE架构的1/5至1/10,从原有的3050元/账户降到了上线后的4元/账户。“鑫云+”平台的维护人员较传统银行业务系统约为1/5左右。以往合作时银行需要分别与各个互联网平台进行对接,自项目上线后,只需对接鑫云+一家平台即可实现多家互联网平台的对接,大大减少了重复建设,提高对接效率,同时也降低了中小银行以及互联网平台的对接成本。从濒临解散到浴火重生,OceanBase已经走了快十年,但在自研关系数据库这条漫漫长路上,OceanBase才仅仅走出了一小步。在阳振坤看来,OceanBase现在“开了很大的一朵花,但是结了很小的一个果”,虽然它已经向所有人证明了通用的分布式关系数据库是能够做成的,而且能真正应用在生产系统中,但今天OceanBase的应用还很有限,远远没有充分发挥它的价值。阳振坤告诉我们,OceanBase当初没有选择基于开源或已有的技术思路开发,而是选择走分布式自研这条路,虽然走得艰难,但做成之后就会成为不可替代的优势。过去这十来年正好是分布式系统发展的十来年,转型到分布式已经成为所有人都认可的一个选择。如今,以蚂蚁金服的OceanBase为代表的分布式关系数据库,不仅解决了关系数据库的扩展性问题,也极大地降低了关系数据库的成本,还提升了可用性。现在,兼容Oracle的工作是OceanBase的重中之重。OceanBase团队的目标是,用两年时间做到Oracle业务的平滑迁移,不需要修改一行代码、不需要业务做任何调整就能够将数据库迁移过来。在阳振坤看来,能够把最早的一些想法一些创新变成产品,真的是非常艰难甚至说过程中充满痛苦的一条道路。但是OceanBase做的所有事情其实还是从业务、从客户中出发,只有技术真的能够落到生产中去,落到用户中去才是真正有价值的,否则做得再好也只是一个空中楼阁。相信未来,OceanBase还会走得更快、更远。本文作者:华蒙阅读原文本文为云栖社区原创内容,未经允许不得转载。

April 17, 2019 · 1 min · jiezi

阿里云数据库自研产品亮相国际顶级会议ICDE 推动云原生数据库成为行业标准

4月9日,澳门当地时间下午4:00-5:30,阿里云在ICDE 2019举办了主题为“云时代的数据库”的专场分享研讨会。本次专场研讨会由阿里巴巴集团副总裁、高级研究员,阿里云智能数据库产品事业部负责人李飞飞(花名:飞刀)主持,五位学术界知名学者和教授受邀参加作为Panel Discussion的嘉宾,与现场近百位与会者进行了深入交流讨论。这五位教授分别是:Anastasia Aliamaki,Professor and ACM Fellow, EPFL;Ihab Ilyas, Professor and ACM Distinguished Scientist, Vice Chair of ACM SIGMOD, University of Waterloo;Guoliang Li, Professor, Tsinghua University;C Mohan,IBM Fellow,IEEE&ACM Fellow,IBM; Xiaofang Zhou, Professor & IEEE Fellow, University of Queensland;整场分享讨论会分为两部分。第一部分先由来自阿里巴巴集团、阿里云智能数据库产品事业部的吕漫漪、林亮、黄贵、乔红麟技术专家们分别介绍了阿里巴巴在POLARDB for MySQL, POLARDB X, AnalyticDB, 以及智能化自治数据库平台SDDP(Self-Driving Database Platform)等产品和技术的进展,以及如何依靠创新来帮助企业解决传统数据库业务场景中在数据处理方面面临的挑战,体现出阿里云智能数据库的技术领先性,以及品牌和文化,目前阿里云数据库在全球云数据市场上位列前三。第二部分由几位专家分别就云时代的数据库趋势和挑战发表了自己的见解,然后就与会学者关心的问题进行了深入探讨。其中,C Mohan博士提出,云时代下Serverless允许用户实现应用无需考虑软硬件配置,并且通过PaaS实现自动扩展,对数据库来说,自身健壮性是基础要求,另外还需要加强分布式负载的处理能力。目前面临一些挑战,例如公有云用户是一个私有环境,混合云方面还需要优秀的分布式OLTP DBMS, 内存/存储架构上还有很多工作可以做。除此之外,数据安全、数据管理方面都是需要考虑的问题。数据显示,中国84%以上的企业表示愿意接受云技术。针对目前面临的挑战,Anastasia Aliamaki 教授指出,一是数据多样性(关系型数据,非关系型数据)对于数据库处理数据是一个巨大挑战,需要构建一个智能的数据库来处理各种各样的负载,需要扩展SQL接口,code-generation提供了运行时构建相应底层数据的访问路径;二是 data cleaning是极其消耗资源的,包括数据从传统数据库迁移到云数据库的迁移工具(用户可以不关心如何迁移的细节问题)。对于用户来说,如果能让用户上述两点都能做到无感知应用,这无疑是云数据库的最大亮点。周晓方教授认为,从传统数据库迁移到云数据库是一个系统工程。为了提升用户体验的满意度,云数据库努力解决高并发、扩展等问题,用户从传统数据库迁移到云数据库不仅仅是一次迁移,也是一次自我调优的过程,可以构建生态系统,从不同的领域开展深入研究。Ihab Ilyas教授分享了在Data Cleaning and Integration to cloud领域的洞见和成果。他特别指出,迁移到云数据库问题不在云本身,用户通常选择他熟悉的产品。云数据库必须了解客户需求,解决客户问题。他说:“对于大数据工程师来说,算法的实现对他们不是噩梦,Hadoop版本却成为他们的噩梦。我们发现迁移这个事情已经在发生,但是我们需要更多关注这个过程本身,并且对过程敏感,能够带给用户无痛感的迁移。”李国良教授认为,云数据库最大的特点是不需要维护数据库,可以节约DBA成本,但是数据库是比操作系统还复杂的系统,需要迁移数据库设计的生态系统,并构建相应的APP。小公司业务应用简单容易上云,大公司因为业务太过复杂不太容易上云,云厂商需要解决大公司的应用迁移问题。最后,数据库领域的专家学者们强调可以借鉴云和大数据生态的演进发展,逐步把数据库技术带入机器学习中而不是强求打造一个“全能的”数据库。最后几位教授也对阿里巴巴在此领域的继续贡献充满期盼。本文作者:桐碧2018阅读原文本文为云栖社区原创内容,未经允许不得转载。

April 16, 2019 · 1 min · jiezi

Java并发之原子性、有序性、可见性

原子性 原子性指的是一个或者多个操作在 CPU 执行的过程中不被中断的特性线程切换 带来的原子性问题Java 并发程序都是基于多线程的,操作系统为了充分利用CPU的资源,将CPU分成若干个时间片,在多线程环境下,线程会被操作系统调度进行任务切换。为了直观的了解什么是原子性,我们看下下面哪些操作是原子性操作int count = 1; //1count++; //2int a = count; //3上面展示语句中,除了语句1是原子操作,其它两个语句都不是原子性操作,下面我们来分析一下语句2其实语句2在执行的时候,包含三个指令操作指令 1:首先,需要把变量 count 从内存加载到 CPU的寄存器指令 2:之后,在寄存器中执行 +1 操作;指令 3:最后,将结果写入内存对于上面的三条指令来说,如果线程 A 在指令 1 执行完后做线程切换,线程 A 和线程 B 按照下图的序列执行,那么我们会发现两个线程都执行了 count+=1 的操作,但是得到的结果不是我们期望的 2,而是 1。操作系统做任务切换,可以发生在任何一条CPU 指令执行完有序性 有序性指的是程序按照代码的先后顺序执行编译优化 带来的有序性问题为了性能优化,编译器和处理器会进行指令重排序,有时候会改变程序中语句的先后顺序,比如程序:a = 5; //1b = 20; //2c = a + b; //3编译器优化后可能变成b = 20; //1a = 5; //2c = a + b; //3在这个例子中,编译器调整了语句的顺序,但是不影响程序的最终结果synchronized(具有有序性、原子性、可见性)表示锁在同一时刻只能由一个线程进行获取,当锁被占用后,其他线程只能等待。在单例模式的实现上有一种双重检验锁定的方式(Double-checked Locking)public class Singleton { static Singleton instance; static Singleton getInstance(){ if (instance == null) { synchronized(Singleton.class) { if (instance == null) instance = new Singleton(); } } return instance; }}我们先看 instance = new Singleton() 的未被编译器优化的操作指令 1:分配一块内存 M;指令 2:在内存 M 上初始化 Singleton 对象;指令 3:然后 M 的地址赋值给 instance 变量。编译器优化后的操作指令指令 1:分配一块内存 M;指令 2:将 M 的地址赋值给 instance 变量;指令 3:然后在内存 M 上初始化 Singleton 对象。现在有A,B两个线程,我们假设线程A先执行getInstance()方法,当执行编译器优化后的操作指令2时(此时候未完成对象的初始化),这时候发生了线程切换,那么线程B进入,刚好执行到第一次判断 instance==null会发现instance不等于null了,所以直接返回instance,而此时的 instance 是没有初始化过的。现行的比较通用的做法就是采用静态内部类的方式来实现public class SingletonDemo { private SingletonDemo() { } private static class SingletonDemoHandler{ private static SingletonDemo instance = new SingletonDemo(); } public static SingletonDemo getInstance() { return SingletonDemoHandler.instance; }}可见性 可见性指的是当一个线程修改了共享变量后,其他线程能够立即得知这个修改缓存 导致的可见性问题首先我们来看一下Java内存模型(JMM)我们定义的所有变量都储存在主内存中每个线程都有自己独立的工作内存,里面保存该线程使用到的变量的副本(主内存中该变量的一份拷贝)线程对共享变量所有的操作都必须在自己的工作内存中进行,不能直接从主内存中读写(不能越级)不同线程之间也无法直接访问其他线程的工作内存中的变量,线程间变量值的传递需要通过主内存来进行。(同级不能相互访问)共享变量可见性的实现原理:线程1对共享变量的修改要被线程2及时看到的话,要经过如下步骤:把工作内存1中更新的变量值刷新到主内存把主内存中的变量的值更新到工作内存2中可以使用 synchronized 、volatile 、final 来保证可见性欢迎大家关注公众号:小白程序之路(whiteontheroad),第一时间获取最新信息!!!笔者博客地址:http:www.gulj.cn ...

March 27, 2019 · 1 min · jiezi

物联网高并发编程之P2P技术NAT穿越方案

物联网高并发编程之P2P技术NAT穿越方案更多物联网高并发编程知识请移步:https://www.yuque.com/shizhiy…内容概述P2P即点对点通信,或称为对等联网,与传统的服务器客户端模式(如下图“P2P结构模型”所示)有着明显的区别,在即时通讯方案中应用广泛(比如IM应用中的实时音视频通信、实时文件传输甚至文字聊天等)。P2P可以是一种通信模式、一种逻辑网络模型、一种技术、甚至一种理念。在P2P网络中(如右图所示),所有通信节点的地位都是对等的,每个节点都扮演着客户机和服务器双重角色,节点之间通过直接通信实现文件信息、处理器运算能力、存储空间等资源的共享。P2P网络具有分散性、可扩展性、健壮性等特点,这使得P2P技术在信息共享、即时通讯、协同工作、分布式计算、网络存储等领域都有广阔的应用。1经典的CS模式: P2P结构模型:NAT技术和P2P技术作为经典的两项网络技术,在现在的网络上有着广泛的应用,P2P主机位于NAT网关后面的情况屡见不鲜。NAT技术虽然在一定程度上解决了IPv4地址短缺的问题,在构建防火墙、保证网络安全方面都发挥了一定的作用,却破坏了端到端的网络通信。NAT阻碍主机进行P2P通信的主要原因是NAT不允许外网主机主动访问内网主机,但是P2P技术却要求通信双方都能主动发起访问,所以要在NAT网络环境中进行有效的P2P通信,就必须采用新的解决方案。P2P作为一项实用的技术,有很大的优化空间,并且相对于网络设备,基于P2P的应用程序在实现上更为灵活。所以为了兼容NAT,基于P2P的应用程序在开发的时候大多会根据自身特点加入一些穿越NAT的功能以解决上述问题。以下着重介绍几种常见的P2P穿越NAT方案。**反向链接技术一种特殊的P2P场景(通信双方中只有一方位于NAT设备之后)此种情况是所有P2P场景中最简单的,它使用一种被称为“反向链接技术”来解决这个问题。大致的原理如下所述。如图所示,客户端A位于NAT之后,它通过TCP端口1234连接到服务器的TCP端口1235上,NAT设备为这个连接重新分配了TCP端口62000。客户端B也通过TCP端口1234连接到服务器端口1235上。A和B从服务器处获知的对方的外网地址二元组{IP地址:端口号}分别为{138.76.29.7:1234}和{155.99.25.11:62000},它们在各自的本地端口上进行侦听。由于B 拥有外网IP地址,所以A要发起与B的通信,可以直接通过TCP连接到B。但如果B尝试通过TCP连接到A进行P2P通信,则会失败,原因是A位于NAT设备后,虽然B发出的TCP SYN请求能够到达NAT设备的端口62000,但NAT设备会拒绝这个连接请求。要想与Client A通信, B不是直接向A发起连接,而是通过服务器给A转发一个连接请求,反过来请求A连接到B(即进行反向链接),A在收到从服务器转发过来的请求以后,会主动向B发起一个TCP的连接请求,这样在NAT设备上就会建立起关于这个连接的相关表项,使A和B之间能够正常通信,从而建立起它们之间的TCP连接。基于UDP协议的P2P打洞技术原理概述UDP打洞技术是通过中间服务器的协助在各自的NAT网关上建立相关的表项,使P2P连接的双方发送的报文能够直接穿透对方的NAT网关,从而实现P2P客户端互连。如果两台位于NAT设备后面的P2P客户端希望在自己的NAT网关上打个洞,那么他们需要一个协助者——集中服务器,并且还需要一种用于打洞的Session建立机制。什么是集中服务器?集中服务器本质上是一台被设置在公网上的服务器,建立P2P的双方都可以直接访问到这台服务器。位于NAT网关后面的客户端A和B都可以与一台已知的集中服务器建立连接,并通过这台集中服务器了解对方的信息并中转各自的信息。同时集中服务器的另一个重要作用在于判断某个客户端是否在NAT网关之后。具体的方法是:一个客户端在集中服务器上登陆的时候,服务器记录下该客户端的两对地址二元组信息{IP地址:UDP端口},一对是该客户端与集中服务器进行通信的自身的IP地址和端口号,另一对是集中服务器记录下的由服务器“观察”到的该客户端实际与自己通信所使用的IP地址和端口号。我们可以把前一对地址二元组看作是客户端的内网IP地址和端口号,把后一对地址二元组看作是客户端的内网IP地址和端口号经过NAT转换后的外网IP地址和端口号。集中服务器可以从客户端的登陆消息中得到该客户端的内网相关信息,还可以通过登陆消息的IP头和UDP头得到该客户端的外网相关信息。如果该客户端不是位于NAT设备后面,那么采用上述方法得到的两对地址二元组信息是完全相同的。P2P的Session建立原理:假定客户端A要发起对客户端B的直接连接,具体的“打洞”过程如下:1)A最初不知道如何向客户端B发起连接,于是A向集中服务器发送消息,请求集中服务器帮助建立与客户端B的UDP连接。2)集中服务器将含有B的外网和内网的地址二元组发给A,同时,集中服务器将包含有A的外网和内网的地址二元组信息的消息也发给B。这样一来, A与B就都知道对方外网和内网的地址二元组信息了。3)当A收到由集中服务器发来的包含B的外网和内网的地址二元组信息后, A开始向B的地址二元组发送UDP数据包,并且A会自动锁定第一个给出响应的B的地址二元组。同理,当B收到由集中服务器发来的A的外网和内网地址二元组信息后,也会开始向A的外网和内网的地址二元组发送UDP数据包,并且自动锁定第一个得到A回应的地址二元组。由于A与B互相向对方发送UDP数据包的操作是异步的,所以A和B发送数据包的时间先后并没有时序要求。下面来看下这三者之间是如何进行UDP打洞的。在这我们分三种具体情景来讨论:第一种是最简单的一种情景,两个客户端都位于同一个NAT设备后面,即位于同一内网中;第二种是最普遍的一种情景,两个客户端分别位于不同的NAT设备后面,分属不同的内网;第三种是客户端位于两层NAT设备之后,通常最上层的NAT是由网络提供商提供的,第二层NAT是家用的NAT路由器之类的设备提供的。典型P2P情景1: 两客户端位于同一NAT设备后面这是最简单的一种情况(如图4所示):客户端A和B分别与集中服务器建立UDP连接,经过NAT转换后,A的公网端口被映射为62000,B的公网端口映射为62005。位于同一个NAT设备后的UDP打洞过程: 当A向集中服务器发出消息请求与B进行连接,集中服务器将B的外网地址二元组以及内网地址二元组发给A,同时把A的外网以及内网的地址二元组信息发给B。A和B发往对方公网地址二元组信息的UDP数据包不一定会被对方收到,这取决于当前的NAT设备是否支持不同端口之间的UDP数据包能否到达(即Hairpin转换特性),无论如何A与B发往对方内网的地址二元组信息的UDP数据包是一定可以到达的,内网数据包不需要路由,且速度更快。A与B推荐采用内网的地址二元组信息进行常规的P2P通信。假定NAT设备支持Hairpin转换,P2P双方也应忽略与内网地址二元组的连接,如果A 和B采用外网的地址二元组做为P2P通信的连接,这势必会造成数据包无谓地经过NAT设备,这是一种对资源的浪费。就目前的网络情况而言,应用程序在“打洞”的时候,最好还是把外网和内网的地址二元组都尝试一下。如果都能成功,优先以内网地址进行连接。什么是Hairpin技术?Hairpin技术又被称为Hairpin NAT、Loopback NAT或Hairpin Translation。Hairpin技术需要NAT网关支持,它能够让两台位于同一台NAT网关后面的主机,通过对方的公网地址和端口相互访问,NAT网关会根据一系列规则,将对内部主机发往其NAT公网IP地址的报文进行转换,并从私网接口发送给目标主机。目前有很多NAT设备不支持该技术,这种情况下,NAT网关在一些特定场合下将会阻断P2P穿越NAT的行为,打洞的尝试是无法成功的。好在现在已经有越来越多的NAT设备商开始加入到对该转换的支持中来。典型P2P情景2: 两客户端位于不同的NAT设备后面这是最普遍的一种情况(如图5所示):客户端A与B经由各自的NAT设备与集中服务器建立UDP连接, A与B的本地端口号均为4321,集中服务器的公网端口号为1234。在向外的会话中, A的外网IP被映射为155.99.25.11,外网端口为62000;B的外网IP被映射为138.76.29.7,外网端口为31000。如下所示:**客户端A——>本地IP:10.0.0.1,本地端口:4321,外网IP:155.99.25.11,外网端口:62000客户端B——>本地IP:10.1.1.3,本地端口:4321,外网IP:138.76.29.7,外网端口:31000位于不同NAT设备后的UDP打洞过程:在A向服务器发送的登陆消息中,包含有A的内网地址二元组信息,即10.0.0.1:4321;服务器会记录下A的内网地址二元组信息,同时会把自己观察到的A的外网地址二元组信息记录下来。同理,服务器也会记录下B的内网地址二元组信息和由服务器观察到的客户端B的外网地址二元组信息。无论A与B二者中的任何一方向服务器发送P2P连接请求,服务器都会将其记录下来的上述的外网和内网地址二元组发送给A或B。A和B分属不同的内网,它们的内网地址在外网中是没有路由的,所以发往各自内网地址的UDP数据包会发送到错误的主机或者根本不存在的主机上。当A的第一个消息发往B的外网地址(如图3所示),该消息途经A的NAT设备,并在该设备上生成一个会话表项,该会话的源地址二元组信息是{10.0.0.1:4321},和A与服务器建立连接的时候NAT生成的源地址二元组信息一样,但它的目的地址是B的外网地址。在A的NAT设备支持保留A的内网地址二元组信息的情况下,所有来自A的源地址二元组信息为{10.0.0.1:4321}的数据包都沿用A与集中服务器事先建立起来的会话,这些数据包的外网地址二元组信息均被映射为{155.99.25.11:62000}。A向B的外网地址发送消息的过程就是“打洞”的过程,从A的内网的角度来看应为从{10.0.0.1:4321}发往{138.76.29.7:31000},从A在其NAT设备上建立的会话来看,是从{155.99.25.11:62000}发到{138.76.29.7:31000}。如果A发给B的外网地址二元组的消息包在B向A发送消息包之前到达B的NAT设备,B的NAT设备会认为A发过来的消息是未经授权的外网消息,并丢弃该数据包。B发往A的消息包也会在B的NAT设备上建立一个{10.1.1.3:4321,155.99.25.11:62000}的会话(通常也会沿用B与集中服务器连接时建立的会话,只是该会话现在不仅接受由服务器发给B的消息,还可以接受从A的NAT设备{155.99.25.11:6200}发来的消息)。一旦A与B都向对方的NAT设备在外网上的地址二元组发送了数据包,就打开了A与B之间的“洞”,A与B向对方的外网地址发送数据,等效为向对方的客户端直接发送UDP数据包了。一旦应用程序确认已经可以通过往对方的外网地址发送数据包的方式让数据包到达NAT后面的目的应用程序,程序会自动停止继续发送用于“打洞”的数据包,转而开始真正的P2P数据传输。 典型P2P情景3: 两客户端位于两层(或多层)NAT设备之后此种情景最典型的部署情况就像这样:最上层的NAT设备通常是由网络提供商(ISP)提供,下层NAT设备是家用路由器。如图所示:假定NAT C是由ISP提供的NAT设备,NAT C提供将多个用户节点映射到有限的几个公网IP的服务,NAT A和NAT B作为NAT C的内网节点将把用户的内部网络接入NAT C的内网,用户的内部网络就可以经由NAT C访问公网了。从这种拓扑结构上来看,只有服务器与NAT C是真正拥有公网可路由IP地址的设备,而NAT A和NAT B所使用的公网IP地址,实际上是由ISP服务提供商设定的(相对于NAT C而言)内网地址(我们将这种由ISP提供的内网地址称之为“伪”公网地址)。同理,隶属于NAT A与NAT B的客户端,它们处于NAT A,NAT B的内网,以此类推,客户端可以放到到多层NAT设备后面。客户端A和客户端B发起对服务器S的连接的时候,就会依次在NAT A和NAT B上建立向外的Session,而NAT A、NAT B要联入公网的时候,会在NAT C上再建立向外的Session。现在假定客户端A和B希望通过UDP“打洞”完成两个客户端的P2P直连。最优化的路由策略是客户端A向客户端B的“伪公网”IP上发送数据包,即ISP服务提供商指定的内网IP,NAT B的“伪”公网地址二元组,{10.0.1.2:55000}。由于从服务器的角度只能观察到真正的公网地址,也就是NAT A,NAT B在NAT C建立session的真正的公网地址{155.99.25.11:62000}以及{155.99.25.11:62005},非常不幸的是客户端A与客户端B是无法通过服务器知道这些“伪”公网的地址,而且即使客户端A和B通过某种手段可以得到NAT A和NAT B的“伪”公网地址,我们仍然不建议采用上述的“最优化”的打洞方式,这是因为这些地址是由ISP服务提供商提供的或许会存在与客户端本身所在的内网地址重复的可能性(例如:NAT A的内网的IP地址域恰好与NAT A在NAT C的“伪”公网IP地址域重复,这样就会导致打洞数据包无法发出的问题)。因此客户端别无选择,只能使用由公网服务器观察到的A,B的公网地址二元组进行“打洞”操作,用于“打洞”的数据包将由NAT C进行转发。当客户端A向客户端B的公网地址二元组{155.99.25.11:62005}发送UDP数据包的时候,NAT A首先把数据包的源地址二元组由A的内网地址二元组{10.0.0.1:4321}转换为“伪”公网地址二元组{10.0.1.1:45000},现在数据包到了NAT C,NAT C应该可以识别出来该数据包是要发往自身转换过的公网地址二元组,如果NAT C可以给出“合理”响应的话,NAT C将把该数据包的源地址二元组改为{155.99.25.11:62000},目的地址二元组改为{10.0.1.2:55000},即NAT B的“伪”公网地址二元组,NAT B最后会将收到的数据包发往客户端B。同样,由B发往A的数据包也会经过类似的过程。目前也有很多NAT设备不支持类似这样的“Hairpin转换”,但是已经有越来越多的NAT设备商开始加入对该转换的支持中来。一个需要考虑的现实问题:UDP在空闲状态下的超时当然,从应用的角度上来说,在完成打洞过程的同时,还有一些技术问题需要解决,如UDP在空闲状态下的超时问题。由于UDP转换协议提供的“洞”不是绝对可靠的,多数NAT设备内部都有一个UDP转换的空闲状态计时器,如果在一段时间内没有UDP数据通信,NAT设备会关掉由“打洞”过程打出来的“洞”。如果P2P应用程序希望“洞”的存活时间不受NAT网关的限制,就最好在穿越NAT以后设定一个穿越的有效期。对于有效期目前没有标准值,它与NAT设备内部的配置有关,某些设备上最短的只有20秒左右。在这个有效期内,即使没有P2P数据包需要传输,应用程序为了维持该“洞”可以正常工作,也必须向对方发送“打洞”心跳包。这个心跳包是需要双方应用程序都发送的,只有一方发送不会维持另一方的Session正常工作。除了频繁发送“打洞”心跳包以外,还有一个方法就是在当前的“洞”超时之前,P2P客户端双方重新“打洞”,丢弃原有的“洞”,这也不失为一个有效的方法。基于TCP协议的P2P打洞技术详细建立穿越NAT设备的P2P的TCP连接只比UDP复杂一点点,TCP协议的”“打洞”从协议层来看是与UDP的“打洞”过程非常相似的。尽管如此,基于TCP协议的打洞至今为止还没有被很好的理解,这也造成了的对其提供支持的NAT设备不是很多。在NAT设备支持的前提下,基于TCP的“打洞”技术实际上与基于UDP的“打洞”技术一样快捷、可靠。实际上,只要NAT设备支持的话,基于TCP的P2P技术的健壮性将比基于UDP技术的更强一些,因为TCP协议的状态机给出了一种标准的方法来精确的获取某个TCP session的生命期,而UDP协议则无法做到这一点。**套接字和TCP端口的重用实现基于TCP协议的P2P打洞过程中,最主要的问题不是来自于TCP协议,而是来自于应用程序的API接口。这是由于标准的伯克利(Berkeley)套接字的API是围绕着构建客户端/服务器程序而设计的,API允许TCP流套接字通过调用connect()函数来建立向外的连接,或者通过listen()和accept函数接受来自外部的连接,但是,API不提供类似UDP那样的,同一个端口既可以向外连接,又能够接受来自外部的连接。而且更糟的是,TCP的套接字通常仅允许建立1对1的响应,即应用程序在将一个套接字绑定到本地的一个端口以后,任何试图将第二个套接字绑定到该端口的操作都会失败。为了让TCP“打洞”能够顺利工作,我们需要使用一个本地的TCP端口来监听来自外部的TCP连接,同时建立多个向外的TCP连接。幸运的是,所有的主流操作系统都能够支持特殊的TCP套接字参数,通常叫做“SO_REUSEADDR”,该参数允许应用程序将多个套接字绑定到本地的一个地址二元组(只要所有要绑定的套接字都设置了SO_REUSEADDR参数即可)。BSD系统引入了SO_REUSEPORT参数,该参数用于区分端口重用还是地址重用,在这样的系统里面,上述所有的参数必须都设置才行。打开P2P的TCP流假定客户端A希望建立与B的TCP连接。我们像通常一样假定A和B已经与公网上的已知服务器建立了TCP连接。服务器记录下来每个接入的客户端的公网和内网的地址二元组,如同为UDP服务的时候一样。从协议层来看,TCP“打洞”与UDP“打洞”是几乎完全相同的过程:客户端A使用其与服务器的连接向服务器发送请求,要求服务器协助其连接客户端B;服务器将B的公网和内网的TCP地址的二元组信息返回给A,同时,服务器将A的公网和内网的地址二元组也发送给B;客户端A和B使用连接服务器的端口异步地发起向对方的公网、内网地址二元组的TCP连接,同时监听各自的本地TCP端口是否有外部的连接联入;A和B开始等待向外的连接是否成功,检查是否有新连接联入。如果向外的连接由于某种网络错误而失败,如:“连接被重置”或者“节点无法访问”,客户端只需要延迟一小段时间(例如延迟一秒钟),然后重新发起连接即可,延迟的时间和重复连接的次数可以由应用程序编写者来确定;TCP连接建立起来以后,客户端之间应该开始鉴权操作,确保目前联入的连接就是所希望的连接。如果鉴权失败,客户端将关闭连接,并且继续等待新的连接联入。客户端通常采用“先入为主”的策略,只接受第一个通过鉴权操作的客户端,然后将进入P2P通信过程不再继续等待是否有新的连接联入。TCP打洞:与UDP不同的是,因为使用UDP协议的每个客户端只需要一个套接字即可完成与服务器的通信,而TCP客户端必须处理多个套接字绑定到同一个本地TCP端口的问题,如图7所示。现在来看实际中常见的一种情景,A与B分别位于不同的NAT设备后面,如图5所示,并且假定图中的端口号是TCP协议的端口号,而不是UDP的端口号。图中向外的连接代表A和B向对方的内网地址二元组发起的连接,这些连接或许会失败或者无法连接到对方。如同使用UDP协议进行“打洞”操作遇到的问题一样,TCP的“打洞”操作也会遇到内网的IP与“伪”公网IP重复造成连接失败或者错误连接之类的问题。客户端向彼此公网地址二元组发起连接的操作,会使得各自的NAT设备打开新的“洞”允许A与B的TCP数据通过。如果NAT设备支持TCP“打洞”操作的话,一个在客户端之间的基于TCP协议的流通道就会自动建立起来。如果A向B发送的第一个SYN包发到了B的NAT设备,而B在此前没有向A发送SYN包,B的NAT设备会丢弃这个包,这会引起A的“连接失败”或“无法连接”问题。而此时,由于A已经向B发送过SYN包,B发往A的SYN包将被看作是由A发往B的包的回应的一部分,所以B发往A的SYN包会顺利地通过A的NAT设备,到达A,从而建立起A与B的P2P连接。从应用程序的角度来看TCP“打洞”从应用程序的角度来看,在进行TCP“打洞”的时候都发生了什么呢?假定A首先向B发出SYN包,该包发往B的公网地址二元组,并且被B的NAT设备丢弃,但是B发往A的公网地址二元组的SYN包则通过A的NAT到达了A,然后,会发生以下的两种结果中的一种,具体是哪一种取决于操作系统对TCP协议的实现:(1)A的TCP实现会发现收到的SYN包就是其发起连接并希望联入的B的SYN包,通俗一点来说就是“说曹操,曹操到”的意思,本来A要去找B,结果B自己找上门来了。A的TCP协议栈因此会把B作为A向B发起连接connect的一部分,并认为连接已经成功。程序A调用的异步connect()函数将成功返回,A的listen()等待从外部联入的函数将没有任何反映。此时,B联入A的操作在A程序的内部被理解为A联入B连接成功,并且A开始使用这个连接与B开始P2P通信。由于收到的SYN包中不包含A需要的ACK数据,因此,A的TCP将用SYN-ACK包回应B的公网地址二元组,并且将使用先前A发向B的SYN包一样的序列号。一旦B的TCP收到由A发来的SYN-ACK包,则把自己的ACK包发给A,然后两端建立起TCP连接。简单的说,第一种,就是即使A发往B的SYN包被B的NAT丢弃了,但是由于B发往A的包到达了A。结果是,A认为自己连接成功了,B也认为自己连接成功了,不管是谁成功了,总之连接是已经建立起来了。(2)另外一种结果是,A的TCP实现没有像(1)中所讲的那么“智能”,它没有发现现在联入的B就是自己希望联入的。就好比在机场接人,明明遇到了自己想要接的人却不认识,误认为是其他的人,安排别人给接走了,后来才知道是自己错过了机会,但是无论如何,人已经接到了任务已经完成了。然后,A通过常规的listen()函数和accept()函数得到与B的连接,而由A发起的向B的公网地址二元组的连接会以失败告终。尽管A向B的连接失败,A仍然得到了B发起的向A的连接,等效于A与B之间已经联通,不管中间过程如何,A与B已经连接起来了,结果是A和B的基于TCP协议的P2P连接已经建立起来了。第一种结果适用于基于BSD的操作系统对于TCP的实现,而第二种结果更加普遍一些,多数Linux和Windows系统都会按照第二种结果来处理。总结在IP地址极度短缺的今天,NAT几乎已经是无所不在的一项技术了,以至于现在任何一项新技术都不得不考虑和NAT的兼容。作为当下应用最广泛的技术之一,P2P技术也必然要面对NAT这个障碍。打洞技术看起来是一项近似乎蛮干的技术,却不失为一种有效的技术手段。在集中服务器的帮助下,P2P的双方利用端口预测的技术在NAT网关上打出通道,从而实现NAT穿越,解决了NAT对于P2P的阻隔,为P2P技术在网络中更广泛的推广作出了非常大的贡献。

March 17, 2019 · 1 min · jiezi

罗辑思维在全链路压测方面的实践和工作笔记

业务的知名度越高,其背后技术团队承受的压力就越大。一旦出现技术问题,就有可能被放大,尤其是当服务的是对知识获取体验要求颇高的用户群体。提供知识服务的罗辑思维主张“省时间的获取知识”,那么其技术团队在技术实践方面是如何践行省时间的理念的呢?本文将还原罗辑思维技术团队在全链路压测上的构建过程,为您一探究竟。全链路压测知多少保障服务的可用性和稳定性是技术团队面临的首要任务,也是技术难题之一。例如,罗辑思维提供的是知识服务,服务的是在高铁、地铁和公交车等场所利用碎片时间进行学习,在凌晨、深夜都有可能打开App,以及分布在海外的全球用户。这就需要得到App提供7*24的稳定高性能的服务和体验。在实际生产环境中,用户的访问行为一旦发生,从CDN到接入层、前端应用、后端服务、缓存、存储、中间件整个链路都面临着不确定的流量,无论是公有云、专有云、混合云还是自建IDC,全局的瓶颈识别、业务整体容量摸底和规划都需要高仿真的全链路压测来检验。这里的不确定的流量指的是某个大促活动、常规高并发时间段以及其他规划外的场景引起的不规则、大小未知的流量。众所周知,应用的服务状态除了会受到自身稳定性的影响,还会受到流量等环境因素的影响,并且影响面会继续传递到上下游,哪怕一个环节出现一点误差,误差在上下游经过几层累积后会造成什么影响谁都无法确定。因此,在生产环境里建立起一套验证机制,来验证各个生产环节都是能经受住各类流量的访问,成为保障服务的可用性和稳定性的重中之重。最佳的验证方法就是让事件提前发生,即让真实的流量来访问生产环境,实现全方位的真实业务场景模拟,确保各个环节的性能、容量和稳定性均做到万无一失,这就是全链路压测的诞生背景,也是将性能测试进行全方位的升级,使其具备“预见能力”。可见,全链路压测做得好,遇到真实环境的流量,系统仅仅只是再经历一次已经被反复验证过的场景,再考一遍做“做过的考题”,不出问题在意料之中将成为可能。压测的核心要素实施完整的业务压测,路径很重要。要达成精准衡量业务承接能力的目标,业务压测就需要做到一样的线上环境、一样的用户规模、一样的业务场景、一样的业务量级和一样的流量来源,让系统提前进行“模拟考”,从而达到精准衡量业务模型实际处理能力的目标,其核心要素是:压测环境、压测基础数据、压测流量(模型、数据)、流量发起、掌控和问题定位。生产环境上基础数据基本分为两种方式,一种是数据库层面不需要做改造,直接基于基础表里的测试账户(相关的数据完整性也要具备)进行,压测之后将相关的测试产生的流水数据清除(清除的方式可以固化SQL脚本或者落在系统上);另一种就是压测流量单独打标(如单独定义的Header),然后业务处理过程中识别这个标并传递下去,包括异步消息和中间件,最终落到数据库的影子表或者影子库中。这种方式详见阿里的全链路压测实践,我们也是选用了这种方式。此外,生产环境的压测尽量在业务低峰期进行从而避免影响生产的业务。全链路压测的构建过程目前,罗辑思维已经提供了得到APP、少年得到、和微信公众号得到里的得到商城等多个流量入口。每一年,罗辑思维都会举办跨年演讲,第一年是在优酷,有200多万人的在线观看,第二年是在深圳卫视合作直播,并在优酷等视频网站同步,直播过程中当二维码放出来的时候,我们就遇到了大量的用户请求,在同一时刻。这就意味着,如果没有对这个期间的流量做好准确的预估,评估好性能瓶颈,那么在直播过程中就有可能出现大面积服务中断。对整体系统进行全链路压测,从而有效保障每个流量入口的服务稳定性成为技术团队的当务之急。因此,我们在2017年,计划引入全链路压测。期间,也对系统做了服务化改造,对于服务化改造的内容之前在媒体上传播过,这次我们主要是讲下保障服务稳定性的核心 - 全链路压测。大致的构建过程如下:2017年10月启动全链路压测项目,完成商城的首页、详情、购物车、下单的改造方案设计、落地、基础数据准备和接入,以及得到APP首页和核心功能相关的所有读接口接入,并在进行了得到APP的第一次读接口的全链路压测。2017年11月商城核心业务和活动形态相对稳定,进入全面的压测&攻坚提升期,同时拉新、下单和支付改造开始,得到APP开始进入写改造,活动形态初具雏形,读写覆盖范围已经覆盖首页、听书、订阅、已购、拉新、知识账本,并启动用户中心侧用户部分的底层改造,包括短信等三方的业务挡板开发。2017年12月商城进行最后的支付改造补齐,然后是全链路压测&优化的整体迭代;得到APP全链路压测接入范围继续增加,覆盖全部首页范围和下侧5个tab,同时开始部分模块组合压测和性能调优的迭代;完成底层支付和用户中心配合改造,以及支付和短信所有外部调用的挡板改造;行了多轮次的全链路形态完整压测,并从中发现发现,给予问题定位,提升体系稳定性;梳理对焦了风险识别、预案和值班等等。经过3个多月的集中实施,我们将全链路压测接入链路174个,创建44个场景,压测消耗VUM1.2亿,发现各类问题200多个。如果说2017年全链路压测的设施在罗辑是从0到1,那么2018年就是从1到N了。从2018年开始,全链路压测进入比较成熟的阶段,我们的测试团队基于PTS和之前的经验,迅速地将全链路压测应用于日常活动和跨年活动中,并应用于新推出的业务「少年得到」上。目前,全链路压测已经成为保障业务稳定性的核心基础设施之一。全链路压测的构建与其说是一个项目,不如说是一项工程。仅凭我们自己的技术积累和人员配置是无法完成的,在此特别感谢阿里云PTS及其他技术团队提供的支持,帮助我们将全链路压测在罗辑思维进行落地。下面我们将落地过程中积累的经验以工作笔记的方式分享给大家。工作笔记A. 流量模型的确定:流量较大的时候可以通过日志和监控快速确定。但是往往可能日常的峰值没有那么高,但是要应对的一个活动却有很大的流量,有个方法是可以基于业务峰值的一个时间段内统计各接口的峰值,最后拼装成压测的流量模型。B. 脏数据的问题:无论是通过生产环境改造识别压测流量的方式还是在生产环境使用测试帐号的方式,都有可能出现产生脏数据的问题,最好的办法是:在仿真环境或者性能环境多校验多测试:有个仿真环境非常重要,很多问题的跟进、复现和debug不需要再上生产环境,降低风险。有多重机制保障:比如对了压测流量单独打标还需要UID有较强的区分度,关键数据及时做好备份等等。C. 监控:由于是全链路压测,目的就是全面的识别和发现问题,所以要求监控的覆盖度很高。从网络接入到数据库,从网络4层到7层和业务的,随着压测的深入,你会发现监控总是不够用。D. 压测的扩展:比如我们会用压测进行一些技术选型的比对,这个时候要确保是同样的流量模型和量级,可以通过全链路压测测试自动扩容或者是预案性质的手工扩容的速度和稳定性。在全链路压测的后期,也要进行重要的比如限流能力的检验和各种故障影响的实际检验和预案的演练。E. 网络接入:如果网络接入的节点较多,可以分别做一些DIS再压测,逐个确定能力和排除问题,然后整体enable之后再一起压测确定整体的设置和搭配上是否有能力对不齐的情况。比如,网络上使用了CDN动态加速、WAF、高防、SLB等等,如果整体压测效果不理想的时候建议屏蔽掉一些环节进行压测,收敛问题,常见的比如WAF和SLB之间的会话保持可能带来负载不匀的问题。当然这些产品本身的容量和规格也是需要压测和验证的,如SLB的CPS、QPS、带宽、连接数都有可能成为瓶颈,通过在PTS的压测场景中集成相关SLB监控可以方便地一站式查看,结合压测也可以更好的选择成本最低的使用方式。另外负载不匀除了前面说的网络接入这块的,内部做硬负载的Nginx的负载也有可能出现不匀的现象,特别是在高并发流量下有可能出现,这也是全链路、高仿真压测的价值。特别是一些重要活动的压测,建议要做一下业务中会真实出现的流量脉冲的情况。阿里云PTS是具备这个能力的,可以在逐级递增满足容量的背景下再观察下峰值脉冲的系统表现,比如验证限流能力,以及看看峰值脉冲会不会被识别为DDOS。F. 参数调优:压测之后肯定会发现大量的参数设置不合理,我们的调优主要涉及到了这些:内核网络参数调整(比如快速回收连接)、Nginx的常见参数调优、PHP-FPM的参数调整等等,这些网上相关的资料非常多。G. 缓存和数据库:重要业务是否有缓存;Redis CPU过高可以重点看下是否有模糊匹配、短连接的不合理使用、高时间复杂度的指令的使用、实时或准实时持久化操作等等。同时,可以考虑升级Redis到集群版,另外对于热点数据考虑Local Cache的优化机制(活动形态由于K-V很少,适合考虑Local Cache);重要数据库随着压测的进行和问题的发现,可能会有索引不全的情况;H. Mock服务:一般在短信下发、支付环节上会依赖第三方,压测涉及到这里的时候一般需要做一些特殊处理,比如搭建单独的Mock服务,然后将压测流量路由过来。这个Mock服务涉及了第三方服务的模拟,所以需要尽量真实,比如模拟的延迟要接近真正的三方服务。当然这个Mock服务很可能会出现瓶颈,要确保其容量和高并发下的接口延时的稳定性,毕竟一些第三方支付和短信接口的容量、限流和稳定性都是比较好的。I. 压测时系统的CPU阈值和业务SLA我们的经验是CPU的建议阈值在50到70%之间,主要是考虑到容器的环境的因素。然后由于是互联网性质的业务,所以响应时间也是将1秒作为上限,同时压测的时候也会进行同步的手工体感的实际测试检查体验。(详细的指标的解读和阈值可以点击阅读原文)J. 其他限流即使生效了,也需要在主要客户端版本上的check是否限流之后的提示和体验是否符合预期,这个很重要;全链路压测主要覆盖核心的各种接口,除此以外的接口也要有一定的保护机制,比如有默认的限流阈值,确保不会出现非核心接口由于预期外流量或者评估不足的流量导致核心系统容量不足(如果是Java技术栈可以了解下开源的Sentinel或者阿里云上免费的限流工具 AHAS)核心的应用在物理机层面要分开部署;省时间的技术理念目前,全链路压测已成为罗辑思维的核心技术设施之一,大幅提升了业务的稳定性。借助阿里云PTS,全链路压测的自动化程度得以进一步提高,加速了构建进程、降低了人力投入。我们非常关注技术团队的效率和专注度,不仅是全链路压测体系的构建,还包括其他很多业务层面的系统建设,我们都借助了合作伙伴的技术力量,在可控时间内支撑起业务的快速发展。当业务跑的快的时候,技术建设的路径和方式,是团队的基础调性决定的。本文作者:中间件小哥阅读原文本文为云栖社区原创内容,未经允许不得转载。

February 22, 2019 · 1 min · jiezi

Python高级语法之:一篇文章了解yield与Generator生成器

Python高级语法中,由一个yield关键词生成的generator生成器,是精髓中的精髓。它虽然比装饰器、魔法方法更难懂,但是它强大到我们难以想象的地步:小到简单的for loop循环,大到代替多线程做服务器的高并发处理,都可以基于yield来实现。理解yield:代替return的yield简单来说,yield是代替return的另一种方案:return就像人只有一辈子,一个函数一旦return,它的生命就结束了yield就像有“第二人生”、“第三人生”甚至轮回转世一样,函数不但能返回值,“重生”以后还能再接着“上辈子”的记忆继续返回值我的定义:yield在循环中代替return,每次循环返回一次值,而不是全部循环完了才返回值。yield怎么念?return我们念“返回xx值”,我建议:yield可以更形象的念为"呕吐出xx值“,每次呕一点。一般我们进行循环迭代的时候,都必须等待循环结束后才return结果。数量小的时候还行,但是如果循环次数上百万?上亿?我们要等多久?如果循环中不涉及I/O还行,但是如果涉及I/O堵塞,一个堵几秒,后边几百万个客户等着呢,银行柜台还能不能下班了?所以这里肯定是要并行处理的。除了传统的多线程多进程外,我们还可以选择Generator生成器,也就是由yield代替return,每次循环都返回值,而不是全部循环完了才返回结果。这样做的好处就是——极大的节省了内存。如果用return,那么循环中的所有数据都要不断累计到内存里直到循环结束,这个不友好。而yield则是一次一次的返回结果,就不会在内存里累加了。所以数据量越大,优势就越明显。有多明显?如果做一百万的简单数字计算,普通的for loop return会增加300MB+的内存占用!而用yield一次一次返回,增加的内存占用几乎为0MB!yield的位置既然yield不是全部循环完了再返回,而是循环中每次都返回,所以位置自然不是在for loop之后,而是在loop之中。先来看一般的for loop返回:def square(numbers): result = [] for n in numbers: result.append( n2 ) return result #在for之外再来看看yield怎么做:def square(numbers): for n in numbers: yield n2 #在for之中可以看到,yield在for loop之中,且函数完全不需要写return返回。这时候如果你print( square([1,2,3]) )得到的就不是直接的结果,而是一个<generator object>。如果要使用,就必须一次一次的next(…)来获取下一个值:>>> results = square( [1,2,3] )>>> next( result )1>>> next( result )4>>> next( result )9>>> next( result )ERROR: StopIteration这个时候更简单的做法是:for r in results: print( r )因为in这个关键词自动在后台为我们调用生成器的next(..)函数什么是generator生成器?只要我们在一个函数中用了yield关键字,函数就会返回一个<generator object>生成器对象,两者是相辅相成的。有了这个对象后,我们就可以使用一系列的操作来控制这个循环结果了,比如next(..)获取下一个迭代的结果。yield和generator的关系,简单来说就是一个起因一个结果:只要写上yield, 其所在的函数就立马变成一个<generator object>对象。xrange:用生成器实现的rangePython中我们使用range()函数生成数列非常常用。而xrange()的使用方法、效果几乎一模一样,唯一不同的就是——xrange()返回的是生成器,而不是直接的结果。如果数据量大时,xrange()能极大的减小内存占用,带来卓越的性能提升。当然,几百、几千的数量级,就直接用range好了。多重yield有时候我们可能会在一个函数中、或者一个for loop中看到多个yield,这有点不太好理解。但其实很简单!一般情况下,我们写的:for n in [1,2,3]: yield n2实际上它的本质是生成了这个东西:yield 12yield 22yield 32也就是说,不用for loop,我们自己手写一个一个的yield,效果也是一样的。你每次调用一次next(..),就得到一个yield后面的值。然后三个yield的第一个就会被划掉,剩两个。再调用一次,再划掉一个,就剩一个。直到一个都不剩,next(..)就返回异常。一旦了解这个本质,我们就能理解一个函数里写多个yield是什么意思了。更深入理解yield:作为暂停符的yield从多重yield延伸,我们可以开始更进一步了解yield到底做了些什么了。现在,我们不把yield看作是return的替代品了,而是把它看作是一个suspense暂停符。即每次程序遇到yield,都会暂停。当你调用next(..)时候,它再resume继续。比如我们改一下上面的程序:def func(): yield 12 print(‘Hi, Im A!’) yield 22 print(‘Hi, Im B!’) yield 32 print(‘Hi, Im C!’)然后我们调用这个小函数,来看看yield产生的实际效果是什么:>>> f = func()>>> f<generator object func at 0x10d36c840>>>> next( f )1>>> next( f )Hi, Im A!4>>> next( f )Hi, Im B!9>>> next( f )Hi, Im C!ERROR: StopIteration从这里我们可以看到:第一次调用生成器的时候,yield之后的打印没有执行。因为程序yield这里暂停了第二次调用生成器的时候,第一个yield之后的语句执行了,并且再次暂停在第二个yield第三次调用生成器的时候,卡在了第三个yield。第四次调用生成器的时候,最后一个yield以下的内容还是执行了,但是因为没有找到第四个yield,所以报错。所以到了这里,如果我们能理解yield作为暂停符的作用,就可以非常灵活的用起来了。yield from与sub-generator子生成器yield from是Python 3.3开始引入的新特性。它主要作用就是:当我需要在一个生成器函数中使用另一个生成器时,可以用yield from来简化语句。举例,正常情况下我们可能有这么两个生成器,第二个调用第一个:def gen1(): yield 11 yield 22 yield 33def gen2(): for g in gen1(): yield g yield 44 yield 55 yield 66可以看到,我们在gen2()这个生成器中调用了gen1()的结果,并把每次获取到的结果yield转发出去,当成自己的yield出来的值。我们把这种一个生成器中调用的另一个生成器叫做sub-generator子生成器,而这个子生成器由yield from关键字生成。由于sub-generator子生成器很常用,所以Python引入了新的语法来简化这个代码:yield from。上面gen2()的代码可以简化为:def gen2(): yield from gen1() yield 44 yield 55 yield 66这样看起来是不是更"pythonic"了呢?:)所以只要记住:yield from只是把别人呕吐出来的值,直接当成自己的值呕吐出去。递归+yield能产生什么?一般我们只是二选一:要不然递归,要不然for循环中yield。有时候yield就可以解决递归的问题,但是有时候光用yield并不能解决,还是要用递归。那么怎么既用到递归,又用到yield生成器呢?参考:Recursion using yielddef func(n): result = n2 yield result if n < 100: yield from func( result )for x in func(100): print( x )上面代码的逻辑是:如果n小于100,那么每次调用next(..)的时候,都得到n的乘方。下次next,会继续对之前的结果进行乘方,直到结果超过100为止。我们看到代码里利用了yield from子生成器。因为yield出的值不是直接由变量来,而是由“另一个”函数得来了。 ...

February 19, 2019 · 1 min · jiezi

物联网高并发编程之网络编程中的线程模型

如需了解更多物联网网络编程知识请点击:物联网云端开发武器库物联网高并发编程之网络编程中的线程模型值得说明的是,具体选择线程还是进程,更多是与平台及编程语言相关。例如 C 语言使用线程和进程都可以(例如 Nginx 使用进程,Memcached 使用线程),Java 语言一般使用线程(例如 Netty),为了描述方便,下面都使用线程来进行描述。线程模型1:传统阻塞 I/O 服务模型特点:1)采用阻塞式 I/O 模型获取输入数据;2)每个连接都需要独立的线程完成数据输入,业务处理,数据返回的完整操作。存在问题:1)当并发数较大时,需要创建大量线程来处理连接,系统资源占用较大;2)连接建立后,如果当前线程暂时没有数据可读,则线程就阻塞在 Read 操作上,造成线程资源浪费。线程模型2:Reactor 模式基本介绍针对传统阻塞 I/O 服务模型的 2 个缺点,比较常见的有如下解决方案: 1)基于 I/O 复用模型:多个连接共用一个阻塞对象,应用程序只需要在一个阻塞对象上等待,无需阻塞等待所有连接。当某条连接有新的数据可以处理时,操作系统通知应用程序,线程从阻塞状态返回,开始进行业务处理;2)基于线程池复用线程资源:不必再为每个连接创建线程,将连接完成后的业务处理任务分配给线程进行处理,一个线程可以处理多个连接的业务。Reactor 模式,是指通过一个或多个输入同时传递给服务处理器的服务请求的事件驱动处理模式。 服务端程序处理传入多路请求,并将它们同步分派给请求对应的处理线程,Reactor 模式也叫 Dispatcher 模式。即 I/O 多了复用统一监听事件,收到事件后分发(Dispatch 给某进程),是编写高性能网络服务器的必备技术之一。Reactor 模式中有 2 个关键组成:1)Reactor:Reactor 在一个单独的线程中运行,负责监听和分发事件,分发给适当的处理程序来对 IO 事件做出反应。 它就像公司的电话接线员,它接听来自客户的电话并将线路转移到适当的联系人;2)Handlers:处理程序执行 I/O 事件要完成的实际事件,类似于客户想要与之交谈的公司中的实际官员。Reactor 通过调度适当的处理程序来响应 I/O 事件,处理程序执行非阻塞操作。根据 Reactor 的数量和处理资源池线程的数量不同,有 3 种典型的实现:1)单 Reactor 单线程;2)单 Reactor 多线程;3)主从 Reactor 多线程。单 Reactor 单线程其中,Select 是前面 I/O 复用模型介绍的标准网络编程 API,可以实现应用程序通过一个阻塞对象监听多路连接请求,其他方案示意图类似。方案说明:1)Reactor 对象通过 Select 监控客户端请求事件,收到事件后通过 Dispatch 进行分发;2)如果是建立连接请求事件,则由 Acceptor 通过 Accept 处理连接请求,然后创建一个 Handler 对象处理连接完成后的后续业务处理;3)如果不是建立连接事件,则 Reactor 会分发调用连接对应的 Handler 来响应;4)Handler 会完成 Read→业务处理→Send 的完整业务流程。优点:模型简单,没有多线程、进程通信、竞争的问题,全部都在一个线程中完成。缺点:性能问题,只有一个线程,无法完全发挥多核 CPU 的性能。Handler 在处理某个连接上的业务时,整个进程无法处理其他连接事件,很容易导致性能瓶颈。可靠性问题,线程意外跑飞,或者进入死循环,会导致整个系统通信模块不可用,不能接收和处理外部消息,造成节点故障。使用场景:客户端的数量有限,业务处理非常快速,比如 Redis,业务处理的时间复杂度 O(1)。单 Reactor 多线程方案说明:1)Reactor 对象通过 Select 监控客户端请求事件,收到事件后通过 Dispatch 进行分发;2)如果是建立连接请求事件,则由 Acceptor 通过 Accept 处理连接请求,然后创建一个 Handler 对象处理连接完成后续的各种事件;3)如果不是建立连接事件,则 Reactor 会分发调用连接对应的 Handler 来响应;4)Handler 只负责响应事件,不做具体业务处理,通过 Read 读取数据后,会分发给后面的 Worker 线程池进行业务处理;5)Worker 线程池会分配独立的线程完成真正的业务处理,将响应结果发给 Handler 进行处理;6)Handler 收到响应结果后通过 Send 将响应结果返回给 Client。优点:可以充分利用多核 CPU 的处理能力。缺点:多线程数据共享和访问比较复杂;Reactor 承担所有事件的监听和响应,在单线程中运行,高并发场景下容易成为性能瓶颈。主从 Reactor 多线程针对单 Reactor 多线程模型中,Reactor 在单线程中运行,高并发场景下容易成为性能瓶颈,可以让 Reactor 在多线程中运行。方案说明:1)Reactor 主线程 MainReactor 对象通过 Select 监控建立连接事件,收到事件后通过 Acceptor 接收,处理建立连接事件;2)Acceptor 处理建立连接事件后,MainReactor 将连接分配 Reactor 子线程给 SubReactor 进行处理;3)SubReactor 将连接加入连接队列进行监听,并创建一个 Handler 用于处理各种连接事件;4)当有新的事件发生时,SubReactor 会调用连接对应的 Handler 进行响应;5)Handler 通过 Read 读取数据后,会分发给后面的 Worker 线程池进行业务处理;6)Worker 线程池会分配独立的线程完成真正的业务处理,如何将响应结果发给 Handler 进行处理;7)Handler 收到响应结果后通过 Send 将响应结果返回给 Client。优点:父线程与子线程的数据交互简单职责明确,父线程只需要接收新连接,子线程完成后续的业务处理。父线程与子线程的数据交互简单,Reactor 主线程只需要把新连接传给子线程,子线程无需返回数据。这种模型在许多项目中广泛使用,包括 Nginx 主从 Reactor 多进程模型,Memcached 主从多线程,Netty 主从多线程模型的支持。小结3 种模式可以用个比喻来理解:(餐厅常常雇佣接待员负责迎接顾客,当顾客入坐后,侍应生专门为这张桌子服务)1)单 Reactor 单线程,接待员和侍应生是同一个人,全程为顾客服务;2)单 Reactor 多线程,1 个接待员,多个侍应生,接待员只负责接待;3)主从 Reactor 多线程,多个接待员,多个侍应生。Reactor 模式具有如下的优点:1)响应快,不必为单个同步时间所阻塞,虽然 Reactor 本身依然是同步的;2)编程相对简单,可以最大程度的避免复杂的多线程及同步问题,并且避免了多线程/进程的切换开销;3)可扩展性,可以方便的通过增加 Reactor 实例个数来充分利用 CPU 资源;4)可复用性,Reactor 模型本身与具体事件处理逻辑无关,具有很高的复用性。线程模型2:Proactor 模型在 Reactor 模式中,Reactor 等待某个事件或者可应用或者操作的状态发生(比如文件描述符可读写,或者是 Socket 可读写)。然后把这个事件传给事先注册的 Handler(事件处理函数或者回调函数),由后者来做实际的读写操作。其中的读写操作都需要应用程序同步操作,所以 Reactor 是非阻塞同步网络模型。如果把 I/O 操作改为异步,即交给操作系统来完成就能进一步提升性能,这就是异步网络模型 Proactor。Proactor 是和异步 I/O 相关的,详细方案如下:1)Proactor Initiator 创建 Proactor 和 Handler 对象,并将 Proactor 和 Handler 都通过 AsyOptProcessor(Asynchronous Operation Processor)注册到内核;2)AsyOptProcessor 处理注册请求,并处理 I/O 操作;3)AsyOptProcessor 完成 I/O 操作后通知 Proactor;4)Proactor 根据不同的事件类型回调不同的 Handler 进行业务处理;5)Handler 完成业务处理。可以看出 Proactor 和 Reactor 的区别:1)Reactor 是在事件发生时就通知事先注册的事件(读写在应用程序线程中处理完成);2)Proactor 是在事件发生时基于异步 I/O 完成读写操作(由内核完成),待 I/O 操作完成后才回调应用程序的处理器来进行业务处理。理论上 Proactor 比 Reactor 效率更高,异步 I/O 更加充分发挥 DMA(Direct Memory Access,直接内存存取)的优势。但是Proactor有如下缺点: 1)编程复杂性,由于异步操作流程的事件的初始化和事件完成在时间和空间上都是相互分离的,因此开发异步应用程序更加复杂。应用程序还可能因为反向的流控而变得更加难以 Debug;2)内存使用,缓冲区在读或写操作的时间段内必须保持住,可能造成持续的不确定性,并且每个并发操作都要求有独立的缓存,相比 Reactor 模式,在 Socket 已经准备好读或写前,是不要求开辟缓存的;3)操作系统支持,Windows 下通过 IOCP 实现了真正的异步 I/O,而在 Linux 系统下,Linux 2.6 才引入,目前异步 I/O 还不完善。因此在 Linux 下实现高并发网络编程都是以 Reactor 模型为主。参考:http://www.52im.net/forum.php ...

February 11, 2019 · 2 min · jiezi

高并发接口设计思路

并发队列的选择Java的并发包提供了三个常用的并发队列实现,分别是:ArrayBlockingQueue、ConcurrentLinkedQueue 和 LinkedBlockingQueue 。ArrayBlockingQueue是初始容量固定的阻塞队列,我们可以用来作为数据库模块成功竞拍的队列,比如有10个商品,那么我们就设定一个10大小的数组队列。ConcurrentLinkedQueue使用的是CAS原语无锁队列实现,是一个异步队列,入队的速度很快,出队进行了加锁,性能稍慢。LinkedBlockingQueue也是阻塞的队列,入队和出队都用了加锁,当队空的时候线程会暂时阻塞。在请求预处理阶段,由于我们的系统入队需求要远大于出队需求,一般不会出现队空的情况,所以我们可以选择ConcurrentLinkedQueue来作为我们的请求队列实现请求接口的合理设计一个秒杀或者抢购页面,通常分为2个部分,一个是静态的HTML等内容,另一个就是参与秒杀的Web后台请求接口。通常静态HTML等内容,是通过CDN的部署,一般压力不大,核心瓶颈实际上在后台请求接口上。这个后端接口,必须能够支持高并发请求,同时,非常重要的一点,必须尽可能“快”,在最短的时间里返回用户的请求结果。为了实现尽可能快这一点,接口的后端存储使用内存级别的操作会更好一点。仍然直接面向MySQL之类的存储是不合适的,如果有这种复杂业务的需求,都建议采用异步写入。当然,也有一些秒杀和抢购采用“滞后反馈”,就是说秒杀当下不知道结果,一段时间后才可以从页面中看到用户是否秒杀成功。但是,这种属于“偷懒”行为,同时给用户的体验也不好,容易被用户认为是“暗箱操作”。高并发下的数据安全我们知道在多线程写入同一个文件的时候,会存现“线程安全”的问题(多个线程同时运行同一段代码,如果每次运行结果和单线程运行的结果是一样的,结果和预期相同,就是线程安全的)。如果是MySQL数据库,可以使用它自带的锁机制很好的解决问题,但是,在大规模并发的场景中,是不推荐使用MySQL的。秒杀和抢购的场景中,还有另外一个问题,就是“超发”,如果在这方面控制不慎,会产生发送过多的情况。我们也曾经听说过,某些电商搞抢购活动,买家成功拍下后,商家却不承认订单有效,拒绝发货。这里的问题,也许并不一定是商家奸诈,而是系统技术层面存在超发风险导致的。超发的原因假设某个抢购场景中,我们一共只有100个商品,在最后一刻,我们已经消耗了99个商品,仅剩最后一个。这个时候,系统发来多个并发请求,这批请求读取到的商品余量都是99个,然后都通过了这一个余量判断,最终导致超发。(同文章前面说的场景)在上面的这个图中,就导致了并发用户B也“抢购成功”,多让一个人获得了商品。这种场景,在高并发的情况下非常容易出现。悲观锁思路解决线程安全的思路很多,可以从“悲观锁”的方向开始讨论。悲观锁,也就是在修改数据的时候,采用锁定状态,排斥外部请求的修改。遇到加锁的状态,就必须等待。虽然上述的方案的确解决了线程安全的问题,但是,别忘记,我们的场景是“高并发”。也就是说,会很多这样的修改请求,每个请求都需要等待“锁”,某些线程可能永远都没有机会抢到这个“锁”,这种请求就会死在那里。同时,这种请求会很多,瞬间增大系统的平均响应时间,结果是可用连接数被耗尽,系统陷入异常。FIFO队列思路那好,那么我们稍微修改一下上面的场景,我们直接将请求放入队列中的,采用FIFO(First Input First Output,先进先出),这样的话,我们就不会导致某些请求永远获取不到锁。看到这里,是不是有点强行将多线程变成单线程的感觉哈。然后,我们现在解决了锁的问题,全部请求采用“先进先出”的队列方式来处理。那么新的问题来了,高并发的场景下,因为请求很多,很可能一瞬间将队列内存“撑爆”,然后系统又陷入到了异常状态。或者设计一个极大的内存队列,也是一种方案,但是,系统处理完一个队列内请求的速度根本无法和疯狂涌入队列中的数目相比。也就是说,队列内的请求会越积累越多,最终Web系统平均响应时候还是会大幅下降,系统还是陷入异常。乐观锁思路这个时候,我们就可以讨论一下“乐观锁”的思路了。乐观锁,是相对于“悲观锁”采用更为宽松的加锁机制,大都是采用带版本号(Version)更新。实现就是,这个数据所有请求都有资格去修改,但会获得一个该数据的版本号,只有版本号符合的才能更新成功,其他的返回抢购失败。这样的话,我们就不需要考虑队列的问题,不过,它会增大CPU的计算开销。但是,综合来说,这是一个比较好的解决方案。有很多软件和服务都“乐观锁”功能的支持,例如Redis中的watch就是其中之一。通过这个实现,我们保证了数据的安全。

January 15, 2019 · 1 min · jiezi

阿里云护航罗振宇2018“时间的朋友”跨年演讲,与千万观众一起跨年

摘要: “时间的朋友”跨年演讲背后的技术支撑。2018年12月31日20:30分,以“时间的朋友”为主题的罗振宇2018跨年演讲在深圳正式召开,同时通过深圳卫视、优酷等平台进行全球直播。作为年度总结式演讲开创者,2018年跨年演讲与以往跨年演讲一样,依旧保持着超高的人气,据现场统计演讲现场共吸引了7884名观众出席。罗辑思维自有平台“得到APP”,承载了演讲期间来自现场、网络及电视端用户持续不断的大流量、高并发访问,保障用户获得了稳定、流畅的使用体验。在这场知识盛宴的背后,罗辑思维技术团队与阿里云携手作战四余月,期间共同探讨、落实了多个提升和优化平台处理能力的技术方案,并通过多轮现网平台压测积累了多个应急处理措施。历届“时间的朋友”跨年演讲都吸引了大量现场和线上观众观看及互动,是典型的大流量、高并发、极端峰值的业务应用场景。随着演讲过程中罗振宇不断抛出新观点、新认知和新活动,海量用户也随之产生庞大的访问请求,这需要得到APP和生活作风电商平台能够稳定、流畅的将请求正确处理完成,使用户获得最好的使用体验。在项目筹备之初,双方技术团队就如何定制化的满足整场演讲高并发、大流量需求进行了深入探讨,阿里云技术团队将多年来支持阿里巴巴多年“双十一大促”活动所积累下来的产品技术及活动经验分享给罗辑思维技术团队。以优化系统处理能力提升为例,罗辑思维使用了阿里云全链路压测产品,在跨年演讲活动备战期间,双方团队协同使用了缓升、激增、脉冲等多种压测手段,模拟跨年演讲期间可能出现的各种对系统处理能力的需求。经过几十轮压测,多次模拟数百万用户集中访问平台等场景,实现了对平台各功能模块进行迭代式的摸底、扩容和验证的闭环过程,保障跨年演讲活动期间平台具备足够的计算、网络和存储等能力。阿里云技术服务团队全程参与到跨年演讲活动中,与罗辑思维技术团队一起进行现场护航工作,负责监控平台基础资源的运行指标、给出优化建议并在突发情况下给予全力支持。为了保证跨年演讲工作的技术投入具备最佳性价比,罗辑思维技术团队大量使用阿里云弹性计算、弹性带宽等产品技术。在跨年演讲活动准备期间,根据压测的结果罗辑思维技术团队扩缩容计算资源(ECS、RDS、Redis等)和带宽资源(SLB、EIP、NATGW等)。根据跨年活动的用户量预估和压测结果等数据,确定跨年演讲活动期间最具性价比的计算及带宽等云计算资源用量;同时,得到APP及生活作风平台经过17年和18年不断的技术架构进化,做到了弹性处理架构,使平台在活动期间可以做到秒级扩容云计算资源,进而秒级提升系统处理能力,最终保证了整场跨年演讲的顺利举行。同时在演讲中,罗振宇也提到了阿里的设计人工智能“鹿班”——让天下没有难撸的banner。刚刚过去的2018年双十一期间,阿里巴巴设计了5亿张广告图,那背后可能是人工设计师吗?当然不是,它是一个人工智能,叫做“鹿班”的系统。平均每秒可以设计8000张海报。使用了这个系统之后,首页Banner的点击率,提升了100%多,效果那是相当惊人。这只是阿里巴巴这样的公司,它庞大的智能后台系统的冰山一角。阿里云作为全球前三名、亚洲最大的云计算服务商,国内市场份额超第2-5名总和,在云计算、大数据及人工智能等领域拥有领先的技术优势,在全球19个地域开放了53个可用区,建设1500多个CDN节点,带宽储备120多T,为全球数十亿用户提供可靠的计算支持。2018年俄罗斯足球世界杯期间,为优酷、CNTV、CCTV5提供了技术及服务,承担了全网70%的世界杯流量。此外,阿里云还为全球客户部署超过200个飞天数据中心,通过底层统一的飞天操作系统,为客户提供全球独有的混合云体验。本文作者:阿里云头条阅读原文本文为云栖社区原创内容,未经允许不得转载。

January 4, 2019 · 1 min · jiezi

阿里云时空数据库引擎HBase Ganos上线,场景、功能、优势全解析

摘要: 2018年12月18日,伴随阿里云HBase全新发布X-Pack全托管NoSQL数据库平台,HBase Ganos时空数据库引擎正式上线。HBase Ganos以阿里云飞天操作系统为强大底座,结合云HBase新一代KV、时序、时空、图多模数据综合处理能力以及云上Spark大数据分析计算服务,为迎接在线时空全量大数据应用构筑PaaS(Platform-as-a-Service)平台能力。随着全球卫星导航定位系统、传感网、移动互联网、IoT等技术的快速发展,越来越多的终端设备连接至网络,由此产生了大规模的时空位置信息,如车辆轨迹、个人轨迹、群体活动、可穿戴设备时空位置等。这些数据具有动态变化(数据写入频繁)、时空多维、规模巨大、价值随时间推移而衰减、空间搜索和时序查询相结合等特征,这对传统数据库带来了新的挑战。2018年12月13日,伴随阿里云HBase全新发布X-Pack全托管NoSQL数据库平台,HBase Ganos时空数据库引擎正式上线。Ganos取名于大地女神盖亚(Gaea)和时间之神柯罗诺斯(Chronos),代表着“时空” 结合。HBase Ganos以阿里云飞天操作系统为强大底座,结合云HBase新一代KV、时序、时空、图多模数据综合处理能力以及云上Spark大数据分析计算服务,为迎接在线时空全量大数据应用构筑PaaS(Platform-as-a-Service)平台能力。1、适用场景举例互联网出行互联网出行涉及到运力的调度、拼车、供需预测、热力图等业务。以供需预测为例,基于对历史轨迹数据的分析,并结合实时订单数据,预测当前订单密集区域的分布,提高接单概率并减少司机空驶时间。这背后涉及到大量时空型数据和业务信息的快速读取,并结合业务算法进行预测,利用HBase Ganos可有力支持该业务场景。IoTIoT行业产生的数据兼具时序和空间特征。以车联网为例,海量的车辆终端在不断地产生轨迹数据,轨迹数据包含了时间和空间位置。利用HBase Ganos,实时监测车辆的行驶轨迹、是否偏航、是否进入某个限制区域等。除了实时监控外,还可以进行实时时空查询,如查询某段时间的轨迹,某段时间进入该区域的车辆等。结合大数据分析框架(如Spark)还可以进行穿越分析、区域分布热力图等。智慧物流与外卖递送在物流与外卖等领域,需要实时监控车辆、骑手的位置,以便进行可靠的时间预测等服务。车辆和骑手的位置需要实时上报,云端需要处理高并发写入并进行实时路径规划、偏航监测等计算,背后都需要大量的时空计算。 传感网与实时GIS在环保、气象、水利、航空监测等领域,需要通过各种传感器获取天、空、地、海不同地理现象、事件、要素的全生命周期多尺度监测指标,比如污染监测、水位监测、降雨量监测、航标监测等。HBase Ganos可以为构建实时GIS大数据应用提供稳定、可靠、弹性、免运维的PaaS服务,为地理国情常态化监测和智慧城市建设提供基础平台。2、HBase Ganos主要功能与特性PB级时空数据存储与高并发写入基于阿里云HBase存储计算分离和完全分布式系统架构, Ganos引擎可支撑TB-PB级时空数据的存储与管理需求,且存储节点可弹性扩展。针对GNSS、传感网、移动APP等千万甚至上亿终端的数据采集,HBase Ganos在提供高效时空索引的同时,结合HBase LSM模型,可满足高并发数据写入需求,其中一个最小的HBase Ganos集群节点写入速度可达到数十万QPS,数据规模可达千亿记录级别。遵循OpenGIS标准规范,支持多种空间数据类型与访问接口引擎遵循OpenGIS标准规范,支持完备的时空点、线、面等常用数据结构,这些数据结构可对应于现实中的POI兴趣点、道路与车辆轨迹、地理围栏等。常见的地理围栏判断、轨迹数据查询与计算、空间搜索等均可完美支持。接口层面上,提供了多种访问方式,包括基于GeoTools API的访问、支持GeoJson作为时空数据结构的REST API、以及即将推出的GeoSQL支持,可最大程度兼容不同用户需求。高效的时空索引与算法分析包引擎以Z-Order、Hilbert等空间填充曲线为基础,支持二维和三维时空索引,百亿量级的时空条件查询可到秒级,完全能够满足海量时空数据的在线处理业务需求。此外,针对常用的时空分析场景,引擎在HBase中内嵌了轨迹抽稀、轨迹相似度计算、密度图等分析算法包,可充分利用HBase协处理器等技术带来的并行优势,加快查询性能、减轻业务层代码量。结合流式计算引擎支撑实时大数据处理为了满足对实时数据分析计算需求,HBase Ganos流数据处理框架基于Lambda架构设计开发,融合了不可变性、复杂性隔离和读写分离等一系原则,具备低延时、高容错、易于扩展等特性。数据接入层面,支持Kafka等消息中间件的实时接入,将基于事件的数据流直接转换到内部数据源。数据分析层面,与Spark Streaming或Flink流数据引擎无缝集成,具备了实时地在任意大数据集上进行数据流查询分析的能力,帮助用户随时随地快速准确地应对复杂的实时数据处理场景。3、云上大规模时空数据处理的优势K-V、时序、时空、图多模型(Multi-Model)助力综合业务场景建模对于互联网和政企客户而言,时空场景虽然是一种重要业务类型,但要支撑好复杂业务系统开发,更多时候需要具备多模型支撑能力。针对这类业务系统,阿里云HBase X-Pack提供了强大的多模式处理能力,不仅支持时空,还支持K-V、时序和图模型等,每一类模型都内置有丰富数据处理能力。Ganos作为其中的时空数据引擎,能够与其他引擎结合,做到开箱即用,满足用户多维度的查询分析需求,让业务开发效率大幅提升。冷热混合存储,助你不改代码,1/3成本轻松搞定冷数据处理时空大数据应用场景下,存储成本占比往往是大头,把存储成本降下来,整体成本才能下降。针对时空数据的价值随时间而衰减的特性,提供了将访问量极少,访问延迟要求不高的历史数据按规则(比如一个月之前的数据)自动转储到阿里云OSS冷存储介质中,其存储成本可下降为高效云盘的1/3,写入性能与云盘相当,并能保证数据随时可读,从而降低存储成本,基本不用改代码就获得了低成本存储能力。全托管,全面解放运维,为业务稳定保驾护航大数据应用往往涉及组件多、系统庞杂、开源与自研混合,因此维护升级困难,稳定性风险极高。云HBase Ganos提供的全托管服务相比其他的半托管服务以及用户自建存在天然的优势。依托持续8年在内核和管控平台的研发,以及大量配套的监控工具、跨可用区、跨域容灾多活方案,Ganos的底层核心阿里云HBase提供目前业界最高的4个9的可用性(双集群),11个9的可靠性的高SLA的支持,满足众多政企客户对平台高可用、稳定性的诉求。4、HBase Ganos实操使用途径Ganos时空引擎包含SQL版和NoSQL版,此次发布的HBase Ganos为NoSQL版,主要服务于在线全量时空大数据应用。引擎包含在HBase 2.0版本中,用户在购买云上HBase数据库服务时,可以选择Ganos作为其时空引擎。Ganos引擎本身并不额外收费,这对于需要使用GIS或时空大数据功能的用户而言,将大幅降低应用和开发成本。Ganos将逐步沉淀基础时空云计算能力到云计算基础平台,赋能ISV厂商,推动时空云计算作为数字化转型的基础引擎普惠到更多客户。本文作者:ganos阅读原文本文为云栖社区原创内容,未经允许不得转载。

December 21, 2018 · 1 min · jiezi

毫秒级从百亿大表任意维度筛选数据,是怎么做到的...

1、业务背景随着闲鱼业务的发展,用户规模达到数亿级,用户维度的数据指标,达到上百个之多。如何从亿级别的数据中,快速筛选出符合期望的用户人群,进行精细化人群运营,是技术需要解决的问题。业界的很多方案常常需要分钟级甚至小时级才能生成查询结果。本文提供了一种解决大数据场景下的高效数据筛选、统计和分析方法,从亿级别数据中,任意组合查询条件,筛选需要的数据,做到毫秒级返回。2、技术选型分析从技术角度分析,我们这个业务场景有如下特点:需要支持任意维度的组合(and/or)嵌套查询,且要求低延迟;数据规模大,至少亿级别,且需要支持不断扩展;单条数据指标维度多,至少上百,且需要支持不断增加;综合分析,这是一个典型的OLAP场景。2.1 OLTP与OLAP下面简单对比下OLTP和OLAP: OLTPOLAP定义联机事务处理联机分析处理应用场景日常业务操作分析决策,报表统计事务要求需要支持事务无需事务支持常用数据操作读/写高并发查询为主,并发要求相对低实时性要求高要求不严格DB大小MB-GBGB-TB数据行数几万到几百万亿级别甚至几十亿上百亿数据列数几十至上百列百级别至千级别最常见的数据库,如MySql、Oracle等,都采用行式存储,比较适合OLTP。如果用MySql等行数据库来实现OLAP,一般都会碰到两个瓶颈:数据量瓶颈:mysql比较适合的数据量级是百万级,再多的话,查询和写入性能会明显下降。因此,一般会采用分库分表的方式,把数据规模控制在百万级。查询效率瓶颈:mysql对于常用的条件查询,需要单独建立索引或组合索引。非索引字段的查询需要扫描全表,性能下降明显。综上分析,我们的应用场景,并不适合采用行存储数据库,因此我们重点考虑列存数据库。2.2 行式存储与列式存储下面简单对比一下行式存储与列式存储的特点: 行式存储列式存储存储特点同一行数据一起存储同一列数据一起存储读取优点一次性读取整行数据快捷读取单列数据快捷读取缺点单列读取,也需要读取整行数据整行查询,需要重组数据数据更新特点INSERT/UPDATE比较方便INSERT/UPDATE比较麻烦索引需要单独对查询列建索引每一列都可以作为索引压缩特点同一行数据差异大,压缩比率低一列数据由于相似性,压缩比率高行存适合近线数据分析,比如要求查询表中某几条符合条件的记录的所有字段的场景。列存适合用于数据的统计分析。考虑如下场景:一个用于存放用户的表中有20个字段,而我们要统计用户年龄的平均值,如果是行存,则要全表扫描,遍历所有行。但如果是列存,数据库只要定位到年龄这一列,然后只扫描这一列的数据就可以得到所有的年龄,计算平均值,性能上相比行存理论上就会快20倍。而在列存数据库中,比较常见的是HBase。HBase应用的核心设计重点是rowkey的设计,一般要把常用的筛选条件,组合设计到rowkey中,通过rowkey的get(单条记录)或者scan(范围)查询。因此HBase比较适合有限查询条件下的非结构化数据存储。而我们的场景,由于所有字段都需要作为筛选条件,所以本质上还是需要结构化存储,且要求查询低延迟,因此也无法使用HBase。我们综合考虑集团内多款列式存储的DB产品(ADS/PostgreSQL/HBase/HybridDB),综合评估读写性能、稳定性、语法完备程度及开发和部署成本,我们选择了HybridDB for MySQL计算规格来构建人群圈选引擎。2.3 HybridDB for MySQL计算规格介绍HybridDB for MySQL计算规格对我们的这个场景而言,核心能力主要有:任意维度智能组合索引(使用方无需单独自建索引)百亿大表查询毫秒级响应MySql BI生态兼容,完备SQL支持空间检索、全文检索、复杂数据类型(多值列、JSON)支持那么,HybridDB for MySQL计算规格是如何做到大数据场景下的任意维度组合查询的毫秒级响应的呢?首先是HybridDB的高性能列式存储引擎,内置于存储的谓词计算能力,可以利用各种统计信息快速跳过数据块实现快速筛选;第二是HybridDB的智能索引技术,在大宽表上一键自动全索引并根据列索引智能组合出各种谓词条件进行过滤;第三是高性能MPP+DAG的融合计算引擎,兼顾高并发和高吞吐两种模式实现了基于pipeline的高性能向量计算,并且计算引擎和存储紧密配合,让计算更快;第四是HybridDB支持各种数据建模技术例如星型模型、雪花模型、聚集排序等,业务适度数据建模可以实现更好的性能指标。综合来说,HybridDB for MySQL计算规格是以SQL为中心的多功能在线实时仓库系统,很适合我们的业务场景,因此我们在此之上构建了我们的人群圈选底层引擎。3、业务实现在搭建了人群圈选引擎之后,我们重点改造了我们的消息推送系统,作为人群精细化运营的一个重要落地点。3.1 闲鱼消息推送简介消息推送(PUSH)是信息触达用户最快捷的手段。闲鱼比较常用的PUSH方式,是先离线计算好PUSH人群、准备好对应PUSH文案,然后在第二天指定的时间推送。一般都是周期性的PUSH任务。但是临时性的、需要立刻发送、紧急的PUSH任务,就需要BI同学介入,每个PUSH任务平均约需要占用BI同学半天的开发时间,且操作上也比较麻烦。本次我们把人群圈选系统与原有的PUSH系统打通,极大地改善了此类PUSH的准备数据以及发送的效率,解放了开发资源。3.2 系统架构离线数据层:用户维度数据,分散在各个业务系统的离线表中。我们通过离线T+1定时任务,把数据汇总导入到实时计算层的用户大宽表中。实时计算层:根据人群的筛选条件,从用户大宽表中,查询符合的用户数量和用户ID列表,为应用系统提供服务。人群圈选前台系统:提供可视化的操作界面。运营同学选择筛选条件,保存为人群,用于分析或者发送PUSH。每一个人群,对应一个SQL存储。类似于:select count(*) from user_big_table where column1> 1 and column2 in (‘a’,‘b’) and ( column31=1 or column32=2)同时,SQL可以支持任意字段的多层and/or嵌套组合。用SQL保存人群的方式,当用户表中的数据变更时,可以随时执行SQL,获取最新的人群用户,来更新人群。闲鱼PUSH系统:从人群圈选前台系统中获取人群对应的where条件,再从实时计算层,分页获取用户列表,给用户发送PUSH。在实现过程中,我们重点解决了分页查询的性能问题。分页查询性能优化方案:在分页时,当人群的规模很大(千万级别)时,页码越往后,查询的性能会有明显下降。因此,我们采用把人群数据增加行号、导出到MySql的方式,来提升性能。表结构如下:批次号人群ID行号用户ID100111123100112234100113345100114456批次号:人群每导出一次,就新加一个批次号,批次号为时间戳,递增。行号:从1开始递增,每一个批次号对应的行号都是从1到N。我们为"人群ID"+“批次号”+“行号"建组合索引,分页查询时,用索引查询的方式替换分页的方式,从而保证大页码时的查询效率。另外,为此额外付出的导出数据的开销,得益于HybridDB强大的数据导出能力,数据量在万级别至百万级别,耗时在秒级至几十秒级别。综合权衡之后,采用了本方案。4、PUSH系统改造收益人群圈选系统为闲鱼精细化用户运营提供了强有力的底层能力支撑。同时,圈选人群,也可以应用到其他的业务场景,比如首页焦点图定投等需要分层用户运营的场景,为闲鱼业务提供了很大的优化空间。本文实现了海量多维度数据中组合查询的秒级返回结果,是一种OLAP场景下的通用技术实现方案。同时介绍了用该技术方案改造原有业务系统的一个应用案例,取得了很好的业务结果,可供类似需求或场景的参考。5、未来人群圈选引擎中的用户数据,我们目前是T+1导入的。这是考虑到人群相关的指标,变化频率不是很快,且很多指标(比如用户标签)都是离线T+1计算的,因此T+1的数据更新频度是可以接受的。后续我们又基于HybridDB构建了更为强大的商品圈选引擎。闲鱼商品数据相比用户数据,变化更快。一方面用户随时会更新自己的商品,另一方面,由于闲鱼商品单库存(售出即下架)的特性,以及其他原因,商品状态会随时变更。因此我们的选品引擎,应该尽快感知到这些数据的变化,并在投放层面做出实时调整。我们基于HybridDB(存储)和实时计算引擎,构建了更为强大的“马赫”实时选品系统。本文作者:闲鱼技术-才思阅读原文本文为云栖社区原创内容,未经允许不得转载。

November 28, 2018 · 1 min · jiezi