• [技术干货] 合理设置servicecomb.rest.server.thread-count的值
    很对开发者测试的时候,发现CSE SDK的CPU占用率很多时候只能够到1CPU,这是因为servicecomb.rest.server.thread-count默认值为1。 官网文档将这个配置项含义解释为“服务端线程数”(参考: https://docs.servicecomb.io/java-chassis/zh_CN/build-provider/protocol/rest-over-vertx.html),这个解释其实不怎么准确,容易和event-loop线程数混淆,开发者理解不了,而且提升这个值,CPU的利用率确实会增加,但是通过jstack查看线程数,却不会增加。 servicecomb.rest.server.thread-count实际上的含义是Vertical的一个概念: vertical instances count。 他是一个非常抽象的概念, 涉及到资源分配和线程绑定关系。比如当值为1的时候,服务端每收到一个请求,就会将请求丢到一个event-loop线程去执行,然后这个绑定关系就确定下来了,不会发生改变。 单纯的从服务端来看,缺省值为1,而event-loop线程是2 * CPU个,无法修改,那么实际上被使用的event-loop线程只有1个,其他线程都是永远空缺着的。 vertical instances count和操作系统资源是紧密绑定的,对于服务端而言,CPU核数就是资源,因此servicecomb.rest.server.thread-count大于CPU核数就显得毫无意义了。但是操作系统还有其他的资源和处理,一个进程里面还会处理客户端网络调用, servicecomb.rest.client.thread-count增加,会分配更多的连接资源(句柄)。客户端的一个vertical instance和服务端一样,绑定到一个event-loop线程执行,不会改变。 Vertx就是利用了极小的线程池(2 * CPU),处理大量的并发请求(网络适配器、CPU、磁盘等)。着也就理解了为什么通常event-loop线程数固定不变,但是单纯看一个点,存在线程池冗余的情况。 总体来看,线程池是被合理的利用到其他地方了。 
  • [技术干货] CSE JAVA SDK如何配置业务处理线程池
    CSE JAVA SDK的线程池模型比较复杂,对于tomcat场景,以及Edge Service的vert.x场景,都有一个业务处理线程池(Edge Service的场景默认在reactive模式,是没有业务线程池的)。CSE设置的默认线程池的线程个数为2 * CPU个数。如果部分服务处理比较慢(比如评价时延>50ms),那么建议要设置一个较大的业务线程池,以提升吞吐量。如果所有接口都处理的很快,则不需要设置非常大的业务线程池,过多线程反而会因为线程调度,增加处理时延。如果一个微服务,有少量的几个接口处理非常耗时,需要考虑将这些接口放到独立的线程池执行(线程池隔离),防止访问慢的接口,影响访问快的接口。     线程池配置 (可以适当的抽时间学习下线程模型: https://bbs.huaweicloud.com/blogs/0a1a862f412611e89fc57ca23e93a89f)        通过配置项servicecomb.executors.default给业务接口制定执行线程池。配置项的的缺省值为servicecomb.executor.groupThreadPool,这个值是Bean的ID,CSE缺省的几个Bean ID如下:  <bean id="cse.executor.groupThreadPool" class="org.apache.servicecomb.core.executor.FixedThreadExecutor"/>   <alias name="cse.executor.groupThreadPool" alias="cse.executor.default"/>   <alias name="cse.executor.groupThreadPool" alias="servicecomb.executor.groupThreadPool"/>   <bean id="cse.executor.reactive" class="org.apache.servicecomb.core.executor.ReactiveExecutor"/>   <alias name="cse.executor.reactive" alias="servicecomb.executor.reactive"/>  2.       通过servicecomb.executors.Provider.[servicename.schemaid.operationId]给某个具体的接口指定不同的线程池。
  • [技术干货] MongoDB 网络通信和线程池处理初步
  • [技术干货] 叶子解密MapReduce(上集)
           作者叶子是一位90后,天秤座,大数据&人工智能美女讲师。钟情创作,相信一切感受皆是生命的体验,坚持在朴素的文字中寻找生活的出路。是一位集才气于一身的大美女。【技术分享】我学习大数据有这样一个感悟:IT的智慧体现在算法上,而算法的灵感就来源于生活。什么意思,我们一起来看看MapReduce做离线计算到底是怎么一回事儿!要理解这个过程,我们先请出一个秘密武器:大家都玩过扑克牌吧?没有玩过至少见过吧?现在有两幅扑克牌无序的堆成一摞放在桌子上。四个人玩牌,玩牌之前呢,他们要看看每一种花色的牌是否完整?请问大家,用什么方式最快?——正确答案:把牌分成4摞,每个人拿一摞,拿到牌之后每个人把自己手里的牌按照红黑花梅的花色分堆,大家都分好之后再按照花色把四个人手中的牌整合到一起。红桃一摞,黑桃一摞,以此类推。到此为止,我们就已经成功的将两幅混乱的扑克牌按花色分成四摞了。然后呢,每个人去这四摞牌里面拿到一种花色,从最小排到最大,去清点自己手里的花色是否完整!哈哈,其实这就是一个离线计算的过程。离线计算的过程分为两个阶段,首先是Map的阶段,就是我们最开始的时候把混乱的扑克牌分成4摞,每个人去拿一摞将其一张一张的分开的过程就是Map。分开之后,我们检查花色,并且按照花色将其分堆,这就是一个Partition的过程。Partition完成之后呢?我们每个人的手上保存了4摞分好了花色的牌,这时候就进入了我们的Reduce阶段,进入Reduce阶段之后做些什么呢?首先要把每个人手里分好的牌按照花色整合到一起,然后再对整合好的每一摞牌进行检查。最后分别输出检查结果。我们具体拿一些数据来分析一下这个过程。OK,看这里,input是我们要处理的数据,这是一个文本文档。我们需要做什么呢?我们把它送到MapReduce模块里边,需要得到如右边所示的结果。统计出来每个单词出现的词数。MapReduce怎么做呢?既然是集群模式,我肯定要把任务分配给不同的服务器对吧?假设我现在把文件分成3块(文件怎么划分呢?),文件分成几个块,就会在集群当中启动几个Map进程。每个进程拿到一块数据,接下来它会怎么做呢?——正确答案:Map要对拿到的数据做一个处理,然后每个Map把处理结果交给Reduce,让Reduce去做一个统计,对吧?那么,Map先会将一行数据按空格键切分,变成若干个单词,然后单词每出现一次,计数为1,用一个键值对装起来。图片解析奉上~~~计算好之后呢Map还要做一件事情,在数据量很多的情况下,我们一般还会让Map做一个单词的统计。因为如果不做单词统计的话,它就会把这一堆<hello,1>,<world,1>,直接交给Reduce,所有Map的统计结果都要通过网络传输给特定的起Reduce进程的服务器,这样子的话会造成不同服务器之间数据传输的量很大。所以Map会把每一个单词出现了多少次统计出来,比如hello出现了1次,world出现了2次这样子。最后每一个Map都把自己手头的任务计算完毕了,就把结果存储起来,接下来就轮到Reduce上场啦!在Map存储结果的过程当中,Reduce就会被启动,Reduce启动以后会去拿Map处理的结果,当有多个Reduce的时候,Reduce不会把全部的结果都读取回来,而是只读取属于自己的分区,即分区Partition。什么叫做分区呢?分区就是对Map计算的结果做的一个分类,有多少Reduce,我就把结果分成多少类,也就是多少个分区。这是通过一个算法来完成的,这个算法是怎么回事,我们后面会做一个细节补充。我们先以4个Reduce为例(一般情况下Reduce的个数少于Map的个数),假设每一个不同的单词刚刚好对应一个不同的分区。那么Reduce拿到的数据就是我们图中所示的样子。Reduce对多个Map的计算结果做了一个整合,它还是一个一个的键值对,键是我们的单词,但是值呢变成了由每一个Map提交的计算结果组成的集合。Reduce要做的就是把这些集合里面的值加起来,得到最后的结果,也就是在整个文档里面,每个单词出现了多少次。这样说起来是不是很简单呢?因为还有很多的细节我留在了<下集>中跟大家补充哦!大家是否对MapReduce的具体执行流程开始感兴趣啦?个人订阅号“叶子的浅浅时光”,集文学作品和技术文章于一体。读者叹之:才学傲须眉,妙笔若行云。欲知详情如何,请听下回分解! 
  • [技术干货] 一次MySQL5.7线上故障分析
    MySQL 5.7 GA(wwwqsfanlicom)已经有很长一段时间了,经过测试评估,在5.7.16版本release之后,我们开始在生产线上规模部署,一个多月相安无事,心中窃喜,在部署了大约200+实例之后,天有不测风云,故障开始接二连三。某日,一个从库报OOM。该实例的innodb_buffer_pool_size = 40G,而系统内存是64G,怎么就能OOM了呢,先下线该实例,再看情况:1、机器内存: 基本快要跪了   total    used    free    shared    buffers    cached Mem:    65808000    65492564    315436    0    1648    247284 -/+ buffers/cache: 65243632    564368 Swap:    2088952    2087852    11002、看谁是元凶:确认是mysqldPID    USER    PR    NI    VIRT    RES    SHR S %CPU %MEM    TIME+  COMMAND     28086 mysql    20    0 87.3g    61g 5192 S 21.2 97.7  28226:23 mysqld3、看MySQL监控:com_delete/innodb_rows_deleted : 29/9340 com_update/innodb_rows_updated : 155/9740 com_select/innodb_rows_selected : 299/39400该业务正常情况下是不可能出现这么大的innodb_rows值的,先stop slave,com_delete/com_update/com_select的值变0,但是innodb_rows值并没有降低,什么!!!现在已经没有任何写入了,怎么可能,诡异了。4、再看看LSN: 还在涨?5、再看看是否有事务在回滚(wwwqsfanlicom):确实有线程在rollback,但是该实例也没有什么大事务,stop slave都已经好久了,为什么还在回滚呢?6、pstack $mysqldpid看看线程都在干什么:从下图能看出都是在compress_gtid_table()里,难道和gtid compress有关系?**一段关于gtid compress的介绍: MySQL 5.7中新增了一个mysql.gtid_executed表,用于记录当前执行过的gtid,在binlog开启的情况下,当binlog retation的时候会唤醒一个内部线程对这个表的数据进行压缩合并。7、看下mysql.gtid_executed表的情况:果然是没有压缩,看来是在压缩这个表数据的时候出错了,然后产生了回滚操作。8、经过对比,最后确认是这个参数引起的:我们为了防止有DBA不小心在从库上执行SQL,给gtid_mode=on的复制模式留下隐患,将super_read_only设置成了on,为此,还特意修改了MHA的源码,以便检测和支持这个设置,结果人算不如天算,踩上了这个坑。将super_read_only 设置成0,当binlog retation后可以看到mysql.gtid_executed的compress恢复正常了,innodb_rows也正常了:
  • [热门活动] 关于ProtectionX新型挖矿病毒的预警
    一、概要近期,出现一种新型挖矿病毒(ProtectionX),该病毒利用永恒之蓝安全漏洞,首次发现具备自我保护功能,与普通挖矿病毒有很大差异。感染主机主要是被用于挖矿,多表现为异常卡顿,严重影响主机性能和业务正常运行。该病毒使用了驱动保护,无法通过任务管理器中止病毒进程,查杀难度较高。请涉及威胁的租户根据自身业务情况,采取防护措施。二、威胁级别威胁级别:【严重】(说明:威胁级别共四级:一般、重要、严重、紧急。)三、影响范围开启了445端口SMB网络共享协议,未及时更新系统补丁或未采取终端安全防护措施的Windows系统(包括个人版和服务器版)。四、排查和处置方法排查方法:1.检查系统是否安装了最近系统漏洞补丁包;2.检查系统是否开启了445端口的SMB网络共享协议或者不必要的系统服务端口;3.检查内网是否有主机访问pool.minexmr.com:7777;4.检查系统是否存在异常运行的1sass.exe、wuauc1t.exe和win1ogon.exe进程;5.检查系统是否存在注册表项HKLM\software\microsoft\windows\CurrentVersion\Run\Registered font。处置方案:1.隔离感染主机:已中毒计算机尽快隔离,关闭所有网络连接,禁用网卡;2.切断传播途径:关闭潜在终端的SMB 445等网络共享端口,关闭异常的外联访问;3.查找攻击源:手工抓包分析或借助态势感知类产品分析,确认全网感染数量;4.查杀病毒:可使用以下工具进行查杀(http://edr.sangfor.com.cn/tool/SfabAntiBot.zip),或者可使用华为云防病毒工具进行查杀(推荐使用华为云市场软件);5.强制删除病毒程序:可以尝试使用PC Hunter强制删除并结束进程1sass.exe、wuauc1t.exe、win1ogon.exe;6.删除注册表项:HKLM\software\microsoft\windows\CurrentVersion\Run\Registered font;7.设置复杂密码:如果主机账号使用简单密码,建议重置为高强度的密码。五、安全建议1. 不从不明网站下载相关的软件,不要点击来源不明的邮件以及附件;2. 及时给电脑打补丁,修复漏洞;3. 修改密码:设置主机账号密码为高强度的密码;4. 对重要的数据文件定期进行非本地备份;5. 安装专业的第三方反病毒软件(推荐使用华为云市场软件);6. 关闭不必要的文件共享权限以及关闭不必要的端口,如: 135、139、445等。注意:修复漏洞前请将资料备份,并进行充分测试。
  • [技术干货] Web之旅-前端篇
    目录前言... 1从一次页面请求说起... 3浏览器运行机制... 3浏览器是多进程的... 3浏览器内核是多线程的... 4HTTP缓存... 6强制缓存... 7对比缓存... 7运行流程... 8DNS域名解析... 10TCP连接... 12HTTP请求与响应... 12页面渲染... 15关闭TCP连接... 16结束语... 17 前言            经常听到有人说,前端很简单,就是画画界面。好像也不无道理,只是还没有真正的去了解它,就像跑步一样,也确实很简单,但是你百米跑进十秒试试。只有对每一个动作都充分了解,才能把速度提上去,而且每进步一秒,都要付出更大的努力。简单的事情做到极致,就不简单了。其实前端并非想象中的简单,也有自己的一套庞大生态体系。下面是一张最新的前端知识体系图,长路漫漫,还需要潜心修炼才行。本文旨在带领大家进入前端的世界,不会讲解具体的HTML/CSS/JS的语法,也不涉及前端框架,而是帮助大家了解前端的运行机制,准确的说是浏览器页面加载的机制。只有充分的了解了前端,才能更好的优化我们的Web系统。对前端不感兴趣也没关系,就当是了解一下,后续还有《Web之旅-后端篇》带给大家。本文中部分内容参考《WebKit技术内幕》一书,感兴趣的童鞋可以研究一下。从一次页面请求说起先考大家一个问题:从打开一个URL到看到页面,中间都经历了什么?输入URL,加载资源,显示页面?没毛病,不过从前端角度来说,一般会经历下面几个过程:1)      在浏览器中输入URL并回车(当然,也可以直接点击URL超链接打开)2)      浏览器查找当前URL是否存在缓存,并比较缓存是否过期3)      DNS解析URL获取对应IP4)      根据IP建立TCP连接(三次握手)5)      发送HTTP请求6)      服务器处理请求,浏览器接收HTTP响应7)      渲染页面8)      关闭TCP连接(四次挥手)那么这里的每一步又是怎么实现的呢?作为一名IT工作者,可能有些过程大家已经耳濡目染了,那就可以选择性的跳过啦。如果还不太了解,那也没关系,下面就开始我们的Web之旅-前端篇。首先第一步,输入URL?不不不,是打开浏览器。是的,你没看错,作为此次Web之旅的主办单位及赞助商,在发车之前,浏览器的运行机制,确定不先了解一下么。浏览器运行机制首先申明,下面要讲述的是Chrominum浏览器的架构模型,不排除个别浏览器特立独行哦。浏览器是多进程的            当我们启动浏览器的时候,默认就启动了一个进程(进程和线程的概念就不再科普了),作为主进程,并从系统中获取资源(CPU、内存)。每当我们打开一个tab页签,就会另起一个新的进程(某些情况下多个tab会合并进程)。那么浏览器到底包含哪些进程呢?下面列举了一些主要进程:1)      Browser进程:浏览器的主进程(负责协调、主控),只有一个。a.      负责浏览器界面显示,与用户交互。如前进,后退等b.      负责各个页面的管理,创建和销毁其他进程c.       将Renderer进程得到的内存中的Bitmap,绘制到用户界面上d.      网络资源的管理,下载等2)      第三方插件进程:每种类型的插件对应一个进程,仅当使用该插件时才创建3)      GPU进程:最多一个,用于3D绘制等4)      浏览器内核(渲染进程)(Renderer进程,内部是多线程的):默认每个Tab页面一个进程,互不影响。主要用来进行页面渲染、脚本执行以及事件的处理那么Browser进程和浏览器内核(Renderer进程)的通信过程是怎样的呢?引用Chromium浏览器的代码层次结构图:从WebKit接口层到用户界面的路径1)      Browser进程收到用户请求,首先需要获取页面内容(比如通过网络下载资源),随后将该任务通过RendererHost接口传递给Render进程。2)      Renderer进程的Renderer接口收到消息,简单解释后,调用相应的WebKit接口层,进行渲染,并将Webkit处理后的结果发送回去3)      Browser进程接收到结果并将结果绘制出来作为前端开发,我们需要重点关注一下浏览器内核进程,也是渲染页面所需要的。浏览器内核是多线程的            每一个tab页面可以看作是一个浏览器内核进程,内核进程是多线程的,主要包含下面几大类线程:1)      GUI渲染线程a.      负责渲染浏览器界面,解析HTML(SVG/XHTML)和CSS,构建DOM树和Render树,并进行布局和绘制等操作。b.      当界面需要重绘(Repaint)或由于某种操作引发重排(Reflow)时,该线程就会执行c.       注意,GUI渲染线程与JS引擎线程是互斥的,当JS引擎执行时GUI线程会被挂起,GUI更新会被保存在一个队列中等到JS引擎空闲时立即被执行。2)      JS引擎线程a.      也称为JS内核,负责处理Javascript脚本程序。(例如V8引擎)b.      JS引擎线程负责解析Javascript脚本,执行代码。c.       JS引擎一直等待着任务队列中任务的到来,然后加以处理,一个Tab页(Renderer进程)中无论什么时候都只有一个JS线程在运行JS程序d.      同样注意,GUI渲染线程与JS引擎线程是互斥的,所以如果JS执行的时间过长,这样就会造成页面的渲染不连贯,导致页面渲染阻塞。3)      事件触发线程a.      归属于浏览器而不是JS引擎,用来控制事件循环(可以理解,JS引擎自己都忙不过来,需要浏览器另开线程协助)b.      当JS引擎执行代码块如setTimeOut时(也可来自浏览器内核的其他线程,如鼠标点击、AJAX异步请求等),会将对应任务添加到事件线程中c.       当对应的事件符合触发条件被触发时,该线程会把事件添加到待处理队列的队尾,等待JS引擎的处理d.      注意,由于JS的单线程关系,所以这些待处理队列中的事件都得排队等待JS引擎处理(当JS引擎空闲时才会去执行)4)      定时触发器线程a.      传说中的setInterval与setTimeout所在线程b.      浏览器定时计数器并不是由JS引擎计数的,因为JS引擎是单线程的,如果处于阻塞线程状态就会影响计时的准确性。因此通过单独线程来计时并触发定时任务(计时完毕后,添加到事件队列中,等待JS引擎空闲后执行)c.       注意,W3C在HTML标准中规定,要求setTimeout中低于4ms的时间间隔算为4ms。5)      异步http请求线程a.      XMLHttpRequest在连接后是通过浏览器新开一个线程进行异步请求b.      当检测到状态变更时,如果设置有回调函数,异步线程就产生状态变更事件放到JS引擎的处理队列中等待处理。c.       在发起了一个异步请求时,http请求线程则负责去请求服务器,有了响应以后,事件触发线程再把回调函数放到事件队列当中。d.      大部分浏览器支持6个左右的并发请求(并发数是针对同一域名的),超过限制数目的请求会被阻塞。浏览器这块先介绍到这里,至于浏览器内核(渲染进程)具体是怎么工作的,各线程之间是怎么配合的,我们到页面渲染那一章节再详细介绍。好了,参观完浏览器,现在是不是可以出发了呢?还不行,下面我们要验票了,已经参加过web之旅的童鞋可能就不需要再次参加了。HTTP缓存            HTTP缓存有多种规则,根据是否需要向服务器发起请求,可以分为两大类:强制缓存和对比缓存。强制缓存如果生效,不再需要和服务器发生交互,而对比缓存不管是否生效,都需要与服务端发生交互。两类缓存规则可以同时存在,而强制缓存优先级高于对比缓存,也就是说,当执行强制缓存的规则时,如果缓存生效,直接使用缓存,不再执行对比缓存规则。和缓存规则相关的配置信息均存在于Http报文的请求头里。强制缓存强制缓存规则主要通过Cache-Control字段进行描述,而早先的HTTP1.0中使用的是Expires字段,以及古老的IE中支持的Pragma字段(这里就不细说了),为了保证兼容性,这些旧的字段还是会用到的。1)      Expires:值为服务端返回的到期时间,是一个绝对时间。浏览器检查当前时间,如果还没到失效时间就直接使用缓存文件。估计大家已经想到了一个问题:当服务器时间与客户端时间不一致时,这种方式就会产生误差。所以从HTTP1.1版开始,使用Cache-Control来代替Expires。2)      Cache-Control:用于定义所有的缓存机制都必须遵循的缓存指示,这些指示是一些特定的指令,包括public、private、no-cache、no-store、max-age、s-maxage以及must-revalidate等。其中的max-age保存一个相对时间。例如Cache-Control: max-age = 484200,表示浏览器收到文件后,缓存在484200s内均有效。当同时设置了上述三种规则时(包括Pragma),浏览器按照Cache-Control、Expires、Pragma的顺序进行覆盖处理。对比缓存强制缓存决定客户端是否向服务器发送请求,缓存判断生效,则直接从本地缓存读取数据,缓存失效,则需要向服务器发送请求。那么这时的资源是否真的已经发生了变化呢?如果没有变化,再把数据重传一遍岂不是会浪费带宽和时间。为了让客户端与服务器之间能实现缓存文件是否更新的验证,提升缓存的复用率,HTTP1.1新增了两种规则:Last-Modified以及ETag。一个是对比最后更新时间,一个是对比内容变化(如MD5值)。1)      Last-Modifie:服务器将资源传递给客户端时,会将资源最后更改的时间以“Last-Modified: GMT”的形式加在响应头上一起返回给客户端。客户端会为资源标记上该信息,下次再次请求时,会把该信息附带在请求报文中一并带给服务器去做检查,若传递的时间值与服务器上该资源最终修改时间一致,则说明该资源没有被修改过,返回304状态码知会客户端直接使用本地缓存即可。否则以常规GET 200回包形式将新的资源(包括新的Last-Modified时间)返回给客户端。相关报文头字段:If-Modified-Since 和 If-Unmodified-Since。可能细心的小伙伴又要问了,如果只是更新了时间,内容没有发生变化呢?为了解决这个困扰,HTTP1.1还增加了ETag规则。2)      Etag:服务器会通过某种算法,给资源计算出一个唯一标志符(比如md5值),在把资源返回给客户端的时候,会在响应头加上“ETag: 唯一标识符”一起返回给客户端。客户端保留该 ETag 字段,并在下一次请求时将其一并带给服务器。服务器只需要比较客户端传来的ETag跟自己服务器上该资源的ETag是否一致,就能很好地判断资源相对客户端而言是否被修改过了。如果服务器判断ETag是一致的,直接返回304状态码知会客户端直接使用本地缓存即可。否则以常规GET 200回包形式将新的资源(当然也包括了新的ETag)发给客户端相关报文头字段:If-None-Match 和 If-Match3)      如果同时指定了Last-Modifie和ETag字段,那么需要同时校验这两个规则。运行流程讲了这么多概念,是不是有点大脑缺氧呢,这到底是怎样一个运行流程呢?下面就通过一张图来回顾一下HTTP的缓存机制:            到这里,大家对HTTP的缓存机制应该有了清晰的认识了吧,下面我们放松一下,探讨一个比较有趣的话题:你知道浏览器里的回车(转到)和刷新(F5)有什么区别么?对,按键不同,还有呢?简单的说:1)      回车保证了原有的缓存规则不变,即在 强制缓存有效的时候,是不会去请求服务器的,打开调试看到的请求也只是伪造的,比如 谷歌浏览器可能显示 200(from cached),其实并没有发起实际的请求,而是直接读取了本地缓存。2)      F5则会跳过 强制缓存,只有Last-Modified/ETag起作用,如果服务器比较后返回 304,再去本地缓存中读取,返回200则使用服务器返回的资源。但是浏览器依然会保留之前的一些变量的值,所以可能会造成页面刷新后,网页出现错误,甚至打不开的情况。3)      那么如果想完全抛弃本地缓存怎么办?Ctrl+F5!此时请求头会发送 Cache-Control: no-cache,告诉浏览器,我不要缓存中的文件了,直接从服务器获取文件,此时缓存完全失效。是不是很有意思呢,一般人我都不告诉他的。好了,那些已经参加过Web前端之旅的乘客,你已经被浏览器的强制缓存检测到了,请前往浏览器工作室进行页面的渲染。剩下的乘客请前往候车室,并耐心在座位等待。什么?为什么还不出发?因为导游要确定一下行程,你说去www.webzhilv.com ,除非是老司机,要不怎么知道具体在哪呢?没关系,我们正在查询线路网系统:DNS域名解析。DNS域名解析            关于域名解析,想必大部分童鞋都有所了解的,我们在地址栏输入的域名并不是最后资源所在的真实位置,域名只是与IP地址的一个映射。网络服务器的IP地址那么多,我们不可能去记一串串的数字,因此域名就产生了,域名解析的过程实际是将域名还原为IP地址的过程。            域名解析的过程,大体会经历下面几个步骤:1)      浏览器检查自身的缓存中有没有这个域名对应的解析过的IP地址,如果有,就使用这个IP。浏览器域名缓存在数量和时间(TTL)上都是有限制的,合理的设置才能保证域名的正常解析。小窍门:在浏览器中输入chrome://net-internals/#dns可以查看当前缓存的域名信息哦。2)      查找操作系统缓存即本地hosts文件,如果hosts文件中有相应的域名映射记录,则使用对应的IP地址。温馨提示:要当心恶意程序修改你的hosts文件来把特定的域名解析到它指定的IP地址上,导致这些域名被劫持。3)      当前面步骤都无法完成域名解析时,就要请求本地域名解析器(LDNS)来解析了。那么怎么知道域名服务器在哪呢?这个就是操作系统网络配置中的DNS服务器地址了。4)      如果LDNS仍然没有解析到,就直接到Root Service域名解析器请求解析。5)      根域名服务器返回给本地域名服务器一个所查询余的主域名服务器(gTLDServer)地址。gTLD是国际顶级域名服务器,如:.com/.cn/.org等,全球只有13台左右。6)      本地域名服务器再向gTLD服务器发送请求。7)      接收请求的gTLD服务器查找并返回此域名对应的Name Server域名服务器的地址,这个Name Server通常就是你注册的域名服务器(如你的域名供应商)。8)      Name Server域名服务器会查询存储的域名和IP的映射关系表,正常情况下都根据域名得到目标IP记录,连同一个TTL值返回给DNS Server域名服务器。9)      返回该域名对应的IP和TTL值,本地域名服务器会缓存这个域名和IP的对应关系,缓存的时间有TTL值控制。10)  把解析的结果返回给用户,用户根据TTL值缓存在浏览器缓存中,域名解析过程结束。经过这么一系列查询,总算知道目的地在哪了,可以出发了吧。什么?还不行?奔溃中...为了大家安全着想,我们需要先向目的地发信息确认一下,路线是否畅通,是否有工作人员接站,要不然就有去无回了。先来个三次握手(TCP连接)吧。TCP连接在获取到服务器的IP地址后,便会开始建立一次连接,这是由TCP协议完成的,主要通过三次握手的方式建立连接:1)      第一次握手: 建立连接时,客户端发送SYN包(syn=i)到服务器,并进入SYN_SENT状态,等待服务器确认。2)      第二次握手: 服务器收到SYN包,必须确认客户的SYN(ack=i+1),同时自己也发送一个SYN包(syn=j),即SYN+ACK包,此时服务器进入SYN_RECV状态。3)      第三次握手: 客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=j+1),此包发送完毕,客户端和服务器进入ESTABLISHED(TCP连接成功)状态,完成三次握手。经过三次握手之后,我们就可以放心的出发了。NO NO NO,还要稍等片刻(估计大家已经崩溃了吧),先检查下火车头及车厢是否准备就绪(也就是封装HTTP消息头和消息体)。HTTP请求与响应            先来科普一下:HTTP协议是Hyper Text Transfer Protocol(超文本传输协议)的缩写,是用于从万维网(WWW)服务器传输超文本到本地浏览器的传送协议。HTTP基于TCP/IP通信协议来传递数据(HTML 文件, 图片文件,查询结果等),是一个属于应用层的面向对象的协议。            HTTP请求报文由三部分组成:请求行、请求报文头、请求报文体。下面是一个实际的请求报文:            HTTP请求行包括请求方法、请求URL和HTTP协议及版本。1)      GET和POST是最常见的HTTP请求方法,除此以外还包括DELETE、HEAD、OPTIONS、PUT、TRACE。2)      请求URL和报文头里的HOST属性拼接组成完整的请求URL。3)      当前使用的协议是HTTP/1.1版本,在此之前还有HTTP/0.9和HTTP/1.0。那么HTTP/1.1和HTTP/1.0有哪些区别呢?a.      HTTP/1.1则默认支持长连接,HTTP/1.0中每对请求/ 响应都使用一个新的连接。b.      HTTP/1.1在请求消息头多一个Host域,HTTP/1.0则没有这个域。c.       HTTP/1.1增加了带宽优化的一些字段:比如range头域、100响应码、Content-Encoding等,具体细节在后续的优化篇中再详细介绍。d.      HTTP1.1增加了OPTIONS、PUT、 DELETE、TRACE、CONNECT请求方法。e.      HTTP/1.1中新增了24个状态响应码。HTTP请求报文头,以明文的字符串格式传送,是以冒号分隔的键/值对,如:Cache-Control: no-cache,每一个消息头最后以回车符(CR)和换行符(LF)结尾。HTTP消息头结束后,会用一个空白的字段来标识,这样就会出现两个连续的CR-LF。结构大体如下图所示:我们前面在介绍HTTP缓存时提到的Cache-Control字段,就位于请求报文头里。此外常见的HTTP请求报文头属性还有Accept、Cookie、Referer、Connection、Host、Content-Type等等。详细的报文头属性这里就不再介绍了,感兴趣的童鞋可自行查阅。Content-Type用来表示请求体数据的媒体类型信息,常见类型有application/json、text/xml以及application/x-www-form-urlencoded。浏览器负责发送HTTP请求,同时也接收返回的HTTP响应信息。HTTP响应报文同样由三部分组成:响应行、响应头和响应体。下面是一个实际的HTTP响应报文:响应行包含协议版本信息以及状态码和状态描述信息。HTTP响应状态码由5段组成:1)      1xx 消息,一般是告诉客户端,请求已经收到了,正在处理,别急...2)      2xx 处理成功,一般表示:请求收到、我明白你要的、请求已受理、已经处理完成等信息.3)      3xx 重定向到其它地方,它让客户端再发起一个请求以完成整个处理。4)      4xx 处理发生错误,责任在客户端,如客户端的请求一个不存在的资源,客户端未被授权,禁止访问等。5)      5xx 处理发生错误,责任在服务端,如服务端抛出异常,路由出错,HTTP版本不支持等。响应头结构和请求头一样,常见报文头属性有:Cache-Control、ETag、Location、Set-Cookie、Content-Type等。响应体则是服务器返回的数据了,可能是一个HTML页面,一个图片或者一段JSON数据。HTTP请求报文生成后,总算是可以出发了,祝大家一路顺风!HTTP请求发出之后,就交由指定的后台服务来进行处理(Web服务器或者反向代理服务器等),具体的处理过程,我们会在后续的《Web之旅-后端篇》中再详细介绍。终于来到了大家最感兴趣的环节,估计看了前面那么多繁琐的步骤,已经脑阔疼了吧。建议休息五分钟,然后再来看下浏览器获取到HTML页面后,是怎么一步步把界面渲染出来的。页面渲染在前面浏览器运行机制章节,我们有提到,浏览器是多进程的,每打开一个tab页就会创建一个新的进程,也就是渲染进程。渲染进程又是多线程的,各个线程之间相互协作,共同完成页面的渲染工作。那么各个线程是怎样的一个分工呢?简单来讲,GUI渲染线程按照从上到下的顺序解析HTML文件,当遇到任何样式(link、style)与脚本(script)时,就会调用异步请求线程去加载资源。加载到的CSS交给GUI渲染线程继续处理,JS文件则交由JS引擎线程去解析执行。当JS引擎线程解析遇到事件(如Ajax异步请求)时,提交任务给事件触发线程,由事件触发线程判断触发条件是否满足,满足时再交由JS引擎线程处理。对于setInterval和setTimeout方法,定时计数交给定时触发器线程处理,计时完毕后再交由JS引擎处理。从页面的渲染角度来讲,大概可以划分成以下几个过程:1)      解析HTML/SVG/XHTML,构造DOM树。Webkit中有三个C++的类来处理这三类文档,解析后会产生一个DOM树。2)      解析CSS构造CSS规则树。3)      解析JS,通过DOM API和CSSOM API 来操作DOM树和CSS规则树4)      通过DOM树和CSS规则树构造Render树,计算各元素尺寸和位置,即Layout/Reflow的过程。DOM树与HTML一一对应,而Render树会忽略诸如head、display:none的元素,并且附加CSS规则到Render树的每个节点(Element)上。5)      绘制Render树(Paint),绘制页面像素信息6)      浏览器会将各层的信息发送给GPU,GPU会将各层合成(composite),显示在屏幕上。整个解析过程,默认采用同步的方式进行。当解析到外部资源(CSS、JS)时,浏览器会启用异步请求线程去加载资源。如果是CSS资源,资源加载不会阻塞DOM树的解析,但是要在CSS加载完成后,才能构造Render树。如果CSS加载不阻塞render树渲染的话,那么当CSS加载完之后,Render树可能又得Repaint或者Reflow了,这就造成了一些没有必要的损耗。什么是Repaint和Reflow呢?Repaint(重绘):部分节点需要更新,但不改变其他集合形状。如改变某个元素的颜色是,就会发生重绘。Reflow(重排):Render树的部分更新,如尺寸变化,就会发生重排。为提高页面的加载速度,我们要尽量的避免页面的Repaint和Reflow操作。如果解析遇到外部JS资源,资源加载将阻塞DOM树的解析,直到JS解析完成后,再继续往下解析。这是因为JS是可操纵DOM的,如果在修改这些元素属性同时渲染界面(即JS引擎线程和GUI线程同时运行),那么渲染线程前后获得的元素数据就可能不一致了。因此为了防止渲染出现不可预期的结果,浏览器设置GUI渲染线程与JS引擎线程为互斥的关系,当JS引擎执行时GUI线程会被挂起,GUI更新则会被保存在一个队列中等到JS引擎线程空闲时立即被执行。所以如果JS执行时间过长,就会阻塞整个页面的渲染,我们要尽量避免这种情况,比如把JS脚本应用放到HTML的最后(</body>之前)。那如果耗时操作必不可少呢,别担心,JS里有一个叫Worker的类,可以帮助我们实现类似多线程的操作,感兴趣的童鞋可以去了解下。当然,JS也可以采用异步加载的方式,如HTML5在<script>增加的async和defer属性,可以让浏览器异步去下载并执行JS,而不阻塞页面的渲染。异步加载原理基本上都是向DOM中写入script或者通过eval函数执行JS代码,可以把它放在匿名函数中执行,也可以在onload中执行,也可以通过XHR注入实现,也可以创建一个iframe元素,然后在iframe中执行**JS代码。但是异步执行需要自行判断指定JS文件是否会对页面的渲染造成影响,否则会出现渲染异常的情况。这次旅程差不多到这里就要结束了,是时候挥手告别了。关闭TCP连接            建立TCP连接需要三次握手,关闭TCP连接则需要四次挥手。 第一次挥手是浏览器发完数据后,发送FIN请求断开连接。第二次挥手是服务器发送ACK表示同意,如果在这一次服务器也发送FIN请求断开连接似乎也没有不妥,但考虑到服务器可能还有数据要发送,所以服务器发送FIN放在了第三次挥手中。最后浏览器返回ACK表示同意,也就是第四次挥手。结束语            首先恭喜大家完成了此次的web前端之旅,通过这次旅程,大家应该对前端的整个加载机制有了初步的了解,如果还意犹未尽,可以参考《WebKit技术内幕》一书进行深入学习和研究。            同时我们也看到了,在整个页面的加载机制中,有很多复杂繁琐的步骤。而浏览器也已经做了很多的优化工作,那么对于前端开发来说,如何才能快速的获取和展示出我们想要的数据呢?下一篇文章《Web之旅-前端优化篇》,将针对每一个步骤,阐述如何进行优化,以达到极致体验。敬请期待。
  • [技术干货] FusionSphere 物理CPU与VCPU的关系梳理总结
    背景说明:在项目和培训中多次被问题FusionSphere物理CPU和vCPU的对应或分配关系,一个物理CPU能虚拟出多少个vCPU,一个vCPU的主频是多少等问题。设置了CPU预留、份额与限制之后又是什么情况。看过之前的一些讨论,也没有定论,本着实践是检验整理的唯一标准,本文通过实验,并对照相关文档来梳理这些问题,希望能让大家有更清楚的理解。1. 系统可用的VCPU总数计算服务器CPU信息:1台R2288H V3,2个CPU, 10 核,超线程为2。总共2x10x2= 40个thread,每个Thread 2.3GHz。Haswell EP CPU 02311CDJ BC1M12CPU X86 series,2300**z,1.8V,64bit,105000mW,Haswell EP Xeon E5-2650 v3,10Core,with heatsink 2 2服务器BMC管理界面上查看 CPU信息 Intel官网看到E5-2650的信息http://ark.intel.com/products/81705/Intel-Xeon-Processor-E5-2650-v3-25M-Cache-2_30-GHz在主机上部署FusionCompute R5C00, 登录CNA主机运行xentop命令查看CPU信息CPUs:40 @ 2294 **z,主频总容量为40 x 2.294 GHz = 91.76 GHz。Domain 0默认配置2个VCPU,占用2 x 2.294 = 4.588 GHz用户可用的主频总容量 = 91.76 - 4.588 = 87.172 GHzFC portal上查看CPU信息,总容量87.17GHz正好等于系统总容量减去Domain 0占用的容量。结论1: 系统可用的vCPU总数(逻辑处理器) = Socket数(CPU个数)x Core数(内核)x Thread数(超线程)1个VCPU = 1个超线程Thread。如下图: CPU QoS如图所示,CPU预留容量为4.59GHz,可用容量为82.58GHz,说明除了VRM01的2个VCPU预留容量4588**z之外的VCPU主频均是可用的,尽管该环境已创建了7台4 VCPU的VM,还可以创建更多VM,这些VM的VCPU总数可以远远超过当前系统显示可用的38个VCPU。在不对VRM01的VCPU进行限制的情况下,将VCPU份额自定义为128000,显示可使用的CPU数为38,说明如果需要的话VRM01可以占用该主机上的除了Domain 0之外的所有VCPU(Domain 0占用了2个VCPU)。2. 虚拟机VCPU的分配与调度对虚拟机来说,不直接感知物理CPU,虚拟机的计算单元通过vCPU对象来呈现。虚拟机只看到VMM呈现给它的vCPU。在VMM中,每个vCPU对应一个VMCS(Virtual-Machine Control Structure)结构,当VCPU被从物理CPU上切换下来的时候,其运行上下文会被保存在其对应的VMCS结构中;当VCPU被切换到PCPU上运行时,其运行上下文会从对应的VMCS结构中导入到物理CPU上。通过这种方式,实现各vCPU之间的独立运行。从虚拟机系统的结构与功能划分可以看出,客户操作系统与虚拟机监视器共同构成了虚拟机系统的两级调度框架,如图所示是一个多核环境下虚拟机系统的两级调度框架。客户操作系统负责第2 级调度,即线程或进程在vCPU 上的调度(将核心线程映射到相应的VCPU上)。虚拟机监视器负责第1 级调度, 即vCPU在物理处理单元上的调度。两级调度的调度策略和机制不存在依赖关系。vCPU调度器负责物理处理器资源在各个虚拟机之间的分配与调度,本质上即把各个虚拟机中的vCPU按照一定的策略和机制调度在物理处理单元上可以采用任意的策略来分配物理资源, 满足虚拟机的不同需求。vCPU可以调度在一个或多个物理处理单元执行(分时复用或空间复用物理处理单元), 也可以与物理处理单元建立一对一固定的映射关系(限制访问指定的物理处理单元)。3. CPU QoS说明Hypervisor层根据分时复用的原理实现对VCPU的调度,CPU QoS的原理是定期给各VCPU分配运行时间片,并对各VCPU运行的时间进行记账,对于消耗完时间片的虚拟CPU将被限制运行,直到获得时间片。以此控制虚拟机获得物理计算资源的比例。以上分配时间片和记账的时间周期很短,对虚拟机用户来说会感觉一直在运行。CPU预留定义了分配给该VM的最少CPU资源。CPU限制定义了分配虚拟机占用CPU资源的上限。CPU份额定义多个虚拟机在竞争CPU资源的时候按比例分配。CPU份额只在各虚拟机竞争计算资源时发挥作用,如果没有竞争,有需求的虚拟机可以独占主机的物理CPU资源。如果虚拟机根据份额值计算出来的计算能力小于虚拟机预留值,调度算法会优先按照虚拟机预留值分配给虚拟机,对于预留值超出按份额分配的计算资源的部分,调度算法会从主机上其他虚拟机的CPU上按各自的份额比例扣除。如果虚拟机根据份额值计算出来的计算能力大于虚拟机预留值,那么虚拟机的计算能力会以份额值计算为准。以一台主频为2800**z的单核物理机为例,如果满负载运行3台单VCPU的虚拟机A、B、C,分配情况如下。结论2:由于采用分时复用的方式,在不做VCPU预留的条件下,系统可分配给VM的VCPU总数远远大于实际可提供的VCPU数目(具体能创建多少额外的VCPU依赖于物理CPU的性能和VCPU的使用情率),在出现资源争用的时根据CPU QoS中的预留和份额来分配资源。
  • [分享交流] 一线互联网常见的 14 个 Java 面试题,你颤抖了吗程序员
    跳槽不算频繁,但参加过不少面试(电话面试、face to face 面试),面过大 / 小公司、互联网 / 传统软件公司,面糊过(眼高手低,缺乏实战经验,挂掉),也面过人,所幸未因失败而气馁,在此过程中不断查缺补漏,养成了踏实、追本溯源、持续改进的习惯,特此将自己经历过、构思过的一些面试题记录下来,如果答案有问题,欢迎拍砖讨论,希望能对找工作或者感兴趣的同学有所帮助,陆续整理中。1. synchronized 和 reentrantlock 异同相同点都实现了多线程同步和内存可见性语义都是可重入锁不同点实现机制不同 synchronized 通过 java 对象头锁标记和 Monitor 对象实现 reentrantlock 通过CAS、ASQ(AbstractQueuedSynchronizer)和 locksupport(用于阻塞和解除阻塞)实现synchronized 依赖 jvm 内存模型保证包含共享变量的多线程内存可见性 reentrantlock 通过 ASQ 的volatile state 保证包含共享变量的多线程内存可见性使用方式不同 synchronized 可以修饰实例方法(锁住实例对象)、静态方法(锁住类对象)、代码块(显示指定锁对象)reentrantlock 显示调用 trylock()/lock() 方法,需要在 finally 块中释放锁功能丰富程度不同 reentrantlock提供有限时间等候锁(设置过期时间)、可中断锁(lockInterruptibly)、condition(提供 await、signal等方法)等丰富语义 reentrantlock 提供公平锁和非公平锁实现 synchronized不可设置等待时间、不可被中断(interrupted)2. concurrenthashmap 为何读不用加锁jdk1.71)HashEntry 中的 key、hash、next 均为 final 型,只能表头**、删除结点2)HashEntry 类的 value 域被声明为 volatile 型3)不允许用 null 作为键和值,当读线程读到某个 HashEntry 的 value 域的值为 null时,便知道产生了冲突——发生了重排序现象(put 设置新 value 对象的字节码指令重排序),需要加锁后重新读入这个 value 值4)volatile 变量 count 协调读写线程之间的内存可见性,写操作后修改 count,读操作先读 count,根据happen-before 传递性原则写操作的修改读操作能够看到jdk1.81)Node 的 val 和 next 均为 volatile 型2)tabAt 和 casTabAt 对应的 unsafe 操作实现了 volatile 语义3. ContextClassLoader(线程上下文类加载器)的作用越过类加载器的双亲委派机制去加载类,如 serviceloader 实现使用线程上下文类加载器加载类,要注意保证多个需要通信的线程间的类加载器应该是同一个,防止因为不同的类加载器导致类型转换异常(ClassCastException)4. tomcat 类加载机制不同应用使用不同的 webapp 类加载器,实现应用隔离的效果,webapp 类加载器下面是 jsp 类加载器不同应用共享的 jar 包可以放到 Shared 类加载器 /shared 目录下5. osgi 类加载机制osgi 类加载模型是网状的,可以在模块(Bundle)间互相委托osgi 实现模块化热部署的关键是自定义类加载器机制的实现,每个 Bundle 都有一个自己的类加载器,当需要更换一个 Bundle时,就把 Bundle 连同类加载器一起换掉以实现代码的热替换当收到类加载请求时,osgi 将按照下面的顺序进行类搜索:1)将以 java.* 开头的类委派给父类加载器加载2)否则,将委派列表名单(配置文件 org.osgi.framework.bootdelegation 中定义)内的类委派给父类加载器加载3)否则,检查是否在 Import-Package 中声明,如果是,则委派给 Export 这个类的 Bundle 的类加载器加载4)否则,检查是否在 Require-Bundle 中声明,如果是,则将类加载请求委托给 required bundle 的类加载器5)否则,查找当前 Bundle 的 ClassPath,使用自己的类加载器加载6)否则,查找类是否在自己的 Fragment Bundle 中,如果在,则委派给 Fragment Bundle 的类加载器加载7)否则,查找 Dynamic Import-Package(Dynamic Import 只有在真正用到此 Package的时候才进行加载)的 Bundle,委派给对应 Bundle 的类加载器加载8)否则,类查找失败6. 如何结束一个一直运行的线程使用退出标志,这个 flag 变量要多线程可见使用 interrupt,结合 isInterrupted() 使用7. threadlocal 使用场景及问题threadlocal 并不能解决多线程共享变量的问题,同一个 threadlocal 所包含的对象,在不同的 thread中有不同的副本,互不干扰用于存放线程上下文变量,方便同一线程对变量的前后多次读取,如事务、数据库 connection 连接,在 web 编程中使用的更多问题: 注意线程池场景使用 threadlocal,因为实际变量值存放在了 thread 的 threadlocalmap类型变量中,如果该值没有 remove,也没有先 set 的话,可能会得到以前的旧值问题: 注意线程池场景下的内存泄露,虽然 threadlocal 的 get/set 会清除 key(key 为 threadlocal的弱引用,value 是强引用,导致 value 不释放)为 null 的 entry,但是最好 remove8. 线程池从启动到工作的流程刚创建时,里面没有线程调用 execute() 添加任务时:1)如果正在运行的线程数量小于核心参数 corePoolSize,继续创建线程运行这个任务2)否则,如果正在运行的线程数量大于或等于 corePoolSize,将任务加入到阻塞队列中3)否则,如果队列已满,同时正在运行的线程数量小于核心参数 maximumPoolSize,继续创建线程运行这个任务4)否则,如果队列已满,同时正在运行的线程数量大于或等于 maximumPoolSize,根据设置的拒绝策略处理5)完成一个任务,继续取下一个任务处理6)没有任务继续处理,线程被中断或者线程池被关闭时,线程退出执行,如果线程池被关闭,线程结束7)否则,判断线程池正在运行的线程数量是否大于核心线程数,如果是,线程结束,否则线程阻塞。因此线程池任务全部执行完成后,继续留存的线程池大小为corePoolSize8)本文所列出的 14 个 Java面试题只是我所遭遇的面试中的一部分,其他的面试题我也会陆续整理出来,说到这里另外顺便给大家推荐一个架构交流学习群:650385180,里面会分享一些资深架构师录制的视频录像:有Spring,MyBatis,Netty 源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化这些成为架构师必备的知识体系。还能领取免费的学习资源,相信对于已经工作和遇到技术瓶颈的码友,在这个群里会有你需要的内容。9. 阻塞队列 BlockingQueue take 和 poll 区别poll(time):取走 BlockingQueue 里排在首位的对象, 若不能立即取出,则可以等 time参数规定的时间,取不到时返回 nulltake():取走 BlockingQueue 里排在首位的对象,若 BlockingQueue 为空,阻塞直到BlockingQueue 有新的对象被加入10. 如何从 FutureTask 不阻塞获取结果get(long timeout,TimeUnit unit),超时则返回轮询,先通过 isDone()判断是否结束,然后调用 get()11. blockingqueue 如果存放了比较关键的数据,系统宕机该如何处理开放性问题,欢迎讨论将队列持久化,比较麻烦,需要将生产数据持久化到磁盘,持久化成功才返回,消费者线程从磁盘加载数据到内存阻塞队列中,维护消费offset,启动时,根据消费 offset 从磁盘加载数据加入消息队列,保证消息不丢失,生成序列号,消费幂等,根据消费进程决定系统重启后的生产状态12. NIO 与传统 I/O 的区别节约线程,NIO 由原来的每个线程都需要阻塞读写变成了由单线程(即 Selector)负责处理多个 channel注册(register)的兴趣事件(SelectionKey)集合(底层借助操作系统提供的 epoll()),netty bossgroup 处理 accept 连接(没看明白为什么 bossgroup 设置多个 thread的必要性),workergroup 处理具体业务流程和数据读写NIO 提供非阻塞操作传统 I/O 以流的方式处理数据,而 NIO 以块的方式处理数据,NIO 提供 bytebuffer,分为堆内和堆外缓冲区,读写时均先放到该缓冲区中,然后由内核通过 channel传输到对端,堆外缓冲区不走内核,提升了性能13. list 中存放可重复字符串,如何删除某个字符串调用 iterator 相关方法删除倒删,防止正序删除导致的数组重排,index 跳过数组元素问题14. 有哪些 GC ROOTS(跟日常开发比较相关的是和此相关的内存泄露)所有 Java 线程当前活跃的栈帧里指向 GC 堆里的对象的引用,因此用不到的对象及时置 null,提升内存回收效率静态变量引用的对象,因此减少静态变量特别是静态集合变量的大小,集合存放的对象覆写 euqls()和 hashcode(),防止持续增长本地方法 JNI 引用的对象方法区中的常量引用的对象,因此减少在长字符串上调用 String.intern()classloader 加载的 class 对象,因此自定义 classloader 无效时及时置 null并且注意类加载器加载对象之间的隔离jvm 里的一些静态数据结构里指向 GC 堆里的对象的引用…本文转自茶轴的青春博客51CTO博客,如需转载,请自行联系原作者。原文链接
  • [技术干货] 默认端口20051被占用
    现象描述集群安装过程或执行DBService服务重启操作时,DBService服务启动失败,打印的错误日志中出现20051端口被占用等信息。 可能原因 [*]DBService使用的默认端口20051被其他进程占用。 [*]DBService进程没有停止成功,使用的端口未释放。 定位思路 [*]检查Manager管理界面显示的错误日志中是否存在如下内容:“already exists for port:20051”。 [*]恢复该故障大约需要10分钟。 处理步骤 [*]使用PuTTY工具,以root用户登录安装DBService报错的节点主机,执行netstat -nap | grep 20051命令查看使用端口的进程。 [*]使用kill命令强制终止使用20051端口的进程。 [*]约2分钟后,再次执行netstat -nap | grep 20051命令,查看是否还有进程占用该端口。 [*]是,执行步骤4 [*]否,执行步骤5 [*]确认占用该端口进程所属的服务,并修改为其他端口。 说明:20051端口为DBservice专用端口,其他进程不可以使用。 [*]分别在“/tmp”和“/var/run/FusionInsight-DBService”目录下执行find . -name '*20051*'命令,将搜索到的文件全部删除。 [*]登录FusionInsight Manager,单击“服务管理”,重启DBService服务。
  • [技术干货] MapReduce出现Map或者Reduce内存溢出进程被终止
    现象描述在Yarn上运行MapReduce任务,在任务执行过程中出现一些Map或者Reduce被中止的情况,出现“Task Failed”时在jobhistoryserver的原生WebUI界面显示如下日志信息:Container [pid=143419,containerID=container_1432208368012_0016_01_000066] is running beyond virtual memory limits. Current usage: 769.8 MB of 1 GB physical memory used; 3.8 GB of 2.5 GB virtual memory used. Killing container. 可能原因从日志显示这个“container killed”直接原因是虚拟内存使用超过了限定值,YARN的NodeManager监控到内存使用超过阈值,强制终止该container进程。 定位思路 [*]检查任务执行失败的直接原因是物理内存溢出还是虚拟内存溢出。 [*]检查失败的Map或Reduce的物理内存配置及虚拟内存的比率配置,需要检查的配置项包括“mapreduce.map.memory.mb”、“mapreduce.reduce.memory.mb” 及“yarn.nodemanager.vmem-pmem-ratio”。 处理步骤 [*]如果是物理内存溢出,则根据实际报错日志中显示需要的物理内存值,调整配置项“mapreduce.map.memory.mb”、“mapreduce.reduce.memory.mb”到合适的值 [*]如果是虚拟内存溢出,则根据实际报错日志中显示需要的虚拟内存值,按照以下公式:“yarn.nodemanager.vmem-pmem-ratio > 实际使用的虚拟内存值/map或者reduce的物理内存”进行调整比率
  • [介绍/入门] 打造一个企业级应用的微服务开发框架(下)---ServiceComb通信处理详解
    本帖最后由 大脸猫爱吃鱼 于 2018-4-17 10:06 编辑 【作者】文/华为云微服务引擎团队 【摘要】近年来越来越多的企业开始实践微服务,本文分为上下两篇介绍微服务框架ServiceComb如何帮助企业应用进行微服务化,实现快速交付,并可靠地运行在云端。下篇介绍ServiceComb的通信处理设计。 【参考】打造一个企业级应用的微服务开发框架(上)---从服务注册中心到服务管理中心 近年来越来越多的企业开始实践微服务,而微服务在企业应用落地的过程,面临着微服务开发框架的选型,无论是自研还是选择第三方框架都不得不考虑的问题包括:微服务框架是否具备高可靠性,任何时间不能中断业务;微服务框架是否能够实现高速通信性能,保证业务从单体架构向微服务架构切换时,性能下降不会太多。本文从服务管理中心、通信处理两个模块来介绍华为开源微服务框架SeviceComb如何帮助企业应用快速具备高性能的通信能力以及高可靠的服务管理能力。下篇将详细介绍ServiceComb的通信处理。 ServiceComb通信处理详解1 整体介绍ServiceComb的底层通信框架依赖Vert.x.vertx标准工作模式为高性能的reactive模式,其工作方式如下图所示:13913图9 reactive模式工作方式业务逻辑直接在eventloop中执行,整个业务流程中没有线程切换,所有的等待逻辑都是异步的,只要有任务,则不会让线程停下来,充分、有效地利用系统资源。vertx生态中包含了业界常用各种组件的reactive封装,包括jdbc、zookeeper、各种mq等等。但是reactive模式对业务的要求相当高,业务主流程中不允许有任何的阻塞行为。因此,为了简化上层业务逻辑,方便开发人员的使用,在Vertx之上提供同步模式的开发接口还是必不可少的,例如: [*]各种安全加固的组件,只提供了同步工作模式,比如redis、zookeeper等等 [*]一些存量代码工作于同步模式,需要低成本迁移 [*]开发人员技能不足以控制reactive逻辑 所以ServiceComb底层基于vertx,但在vertx之上进行了进一步封装,同时支持reactive及同步模式。工作于Reactive模式时,利用Vertx原生的能力,不必做什么额外的优化,仅需要注意不要在业务代码中阻塞整个进程。而同步模式则会遭遇各种并发性能问题。,本文描述同步模式下的各种问题以及解决方案。RESTful流程中,连接由vertx管理,当前没有特别的优化,所以本文中,连接都是指highway流程中的tcp连接。 2 同步模式下的整体线程模型13914图10 同步模式下的整体线程模型 [*]一个微服务进程中,为transport创建了一个独立的vertx实例 [*]Eventloop是vertx中的网络、任务线程 [*]一个vertx实例默认的Eventloop数为:2 * Runtime.getRuntime().availableProcessors() 3 服务消费者端在服务消费者端,主要需要处理的问题是如何更加高效地把请求推送到服务提供者上去,然后拿到服务提供者的返回信息。所以在这一端我们主要关注“如何更高效的发送数据”这个话题。 3.1 单连接模型最简单的单连接模型13916图11 最简单的单连接模型 从模型图中,我们可以看到,所有的consumer线程,如果向同一个目标发送数据,必然产生资源竞争,此时实际的处理如下: [*]Connection.send内部直接调用Vertx的socket.write(buf),是必然加锁互斥的。 这必然导致大量并发时,大多数consumer线程都无法及时地发送自己的数据。 [*]Socket.write内部会调用netty的channel.write,此时会判断出执行线程不是eventloop线程,所以会创建出一个任务并加入到eventloop任务队列中,如果eventloop线程当前在睡眠态,则立即唤醒eventloop线程,异步执行任务。 这导致频繁的任务下发及线程唤醒,无谓地增加cpu占用,降低性能。优化的单连接模型13917图12 优化的单连接模型 在优化模型中: [*]每个TcpClientConnection额外配备一个CAS消息队列 [*]Connection.send不再直接调用vertx的write方法,而是: 所有消息保存到CAS队列中,减少入队竞争 通过原子变量判定,只有入队前CAS队列为空,才向eventloop下发write任务,唤醒eventloop线程 在eventloop中处理write任务时,将多个请求数据包装为composite buffer,批量发送,减少进入os内核的次数,提高tcp发送效率。代码参见: https://github.com/ServiceComb/ServiceComb-Java-Chassis/blob/master/foundations/foundation-vertx/src/main/java/io/servicecomb/foundation/vertx/client/tcp/TcpClientConnection.java[code]io.servicecomb.foundation.vertx.client.tcp.TcpClientConnection.packageQueue io.servicecomb.foundation.vertx.client.tcp.TcpClientConnection.send(AbstractTcpClientPackage,long, TcpResponseCallback)[/code]https://github.com/ServiceComb/ServiceComb-Java-Chassis/blob/master/foundations/foundation-vertx/src/main/java/io/servicecomb/foundation/vertx/tcp/TcpConnection.java[code]io.servicecomb.foundation.vertx.tcp.TcpConnection.write(ByteBuf) io.servicecomb.foundation.vertx.tcp.TcpConnection.writeInContext()[/code]进行此项优化后,在同一环境下测试2组数据,可以看到性能有明显提升(不同硬件的测试环境,数据可能差异巨大,不具备比较意义):TPSLatency(ms)CPUTPS提升比例时延提升比例ConsumerProducer(新-旧)/旧(旧-新)/新优化前819861.22290%290%77.31%43.61%优化后1453690.688270%270%表6 单连接模型优化前后性能对比3.2 多连接模型在单连接场景下进行相应的优化后,我们发现其实还有更多的优化空间。因为在大多数场景中,实际机器配置足够高,比如多核、万兆网络连接、网卡支持RSS特性等。此时,需要允许一对consumer与producer之间建立多条连接来充分发挥硬件的性能。13918图13 多连接模型 [*]允许配置多个eventloop线程 在microservice.yaml中进行以下配置:[code]cse: highway: client: thread-count: 线程数 server: thread-count: 线程数[/code] [*]Consumer线程与eventloop线程建立均衡的绑定关系,进一步降低consumer线程的竞争概率。 代码参见:https://github.com/ServiceComb/ServiceComb-Java-Chassis/blob/master/foundations/foundation-vertx/src/main/java/io/servicecomb/foundation/vertx/client/ClientPoolManager.java[code]io.servicecomb.foundation.vertx.client.ClientPoolManager.findThreadBindClientPool()[/code]优化后的性能对比:TPSLatency (ms)CPUTPS提升比例时延提升比例ConsumerProducer(新-旧)/旧(旧-新)/新简单单连接*105434420.9192305%1766%72.81%42.11%CAS单连接*109391170.5321960%1758%表7 多连接下线程模型优化前后性能对比 每请求大小为1KB,可以看到万兆网的带宽接近吃满了,可以充分利用硬件性能。(该测试环境,网卡支持RSS特性。)4 服务提供者端不同于服务消费者,服务提供者主要的工作模式就是等待消费者的请求,然后处理后返回应答的信息。所以在这一端,我们更加关注“如何高效的接收和处理数据”这件事情。同步模式下,业务逻辑和IO逻辑分开,且根据“隔离仓”原则,为了保证整个系统更加稳定和高效地运行,业务逻辑本身也需要在不同隔离的区域内运行。而这些区域,就是线程池。所以构建服务提供者,就需要对线程池进行精细的管理。下面是针对线程池的各种管理方式。4.1 单线程池(ThreadPoolExecutor)下图表示的是将业务逻辑用单独的线程池实现的方式。在这种方式下,IO仍然采用异步模式,所有接到的请求放入队列中等待处理。在同一个线程池内的线程消费这个队列并进行业务处理。13919图14 单线程池实现方式在这种方式下,有以下瓶颈点: [*]所有的eventloop向同一个BlockingQueue中提交任务 [*]线程池中所有线程从同一个BlockingQueue中抢任务执行 ServiceComb默认不使用这种线程池。4.2 多线程池(ThreadPoolExecutor)为规避线程池中Queue带来的瓶颈点,我们可以使用一个Executor将多个真正的Executor包起来。13920图15 多线程池实现方式 [*]Eventloop线程与线程池建立均衡的绑定关系,降低锁冲突概率 [*]相当于将线程分组,不同线程从不同Queue中抢任务,降低冲突概率 ServiceComb默认所有请求使用同一个线程池实例:io.servicecomb.core.executor.FixedThreadExecutorFixedThreadExecutor内部默认创建2个真正的线程池,每个池中有CPU数目的线程,可以通过配置修改默认值:[code]servicecomb: executor: default: group: 内部真正线程池的数目 thread-per-group: 每个线程池中的线程数[/code]代码参见:https://github.com/ServiceComb/ServiceComb-Java-Chassis/blob/master/core/src/main/java/io/servicecomb/core/executor/FixedThreadExecutor.java4.3 隔离仓业务接口的处理速度有快有慢,如果所有的请求统一在同一个Executor中进行处理,则可能每个线程都在处理慢速请求,导致其他请求在Queue中排队。此时,可以根据业务特征,事先做好规划,将不同的业务处理按照一定的方式进行分组,每个组用不同的线程池,以达到隔离的目的。13921图16 隔离仓 隔离仓的实现依托到ServiceComb灵活的线程池策略,具体在下一节进行描述。4.4 灵活的线程池策略ServiceComb微服务的概念模型如下:13922图17 ServiceComb微服务概念模型可以针对这3个层次进行线程池的配置,operation与线程池之间的对应关系,在启动阶段既完成绑定。operation与线程池之间的绑定按以下逻辑进行: [*]查看配置项cse.executors.Provider.[schemaId].[operationId]是否有值 如果有值,则将值作为beanId从spring中获取bean实例,该实例即是一个Executor 如果没有值,则继续尝试下一步 [*]使用相同的方式,查看配置项cse.executors.Provider.[schemaId]是否有值 [*]使用相同的方式,查看配置项cse.executors.default是否有值 [*]以”cse.executor.groupThreadPool”作为beanId,获取线程池(系统内置的FixedThreadExecutor) 代码参见:https://github.com/ServiceComb/ServiceComb-Java-Chassis/blob/master/core/src/main/java/io/servicecomb/core/executor/ExecutorManager.java 按以上策略,用户如果需要创建自定义的线程池,需要按以下步骤执行: [*]实现java.util.concurrent.Executor接口 [*]将实现类定义为一个bean [*]在microservice.yaml中将线程池与对应的业务进行绑定 4.5 线程池模型总结如上一节所述,在默认多线程池的基础上,CSE提供了更为灵活的线程池配置。“隔离仓”模式的核心价值是实现不同业务之间的相互隔离,从而让一个业务的故障不要影响其他业务。这一点在CSE中可以通过对线程池的配置实现。例如,可以为不同的operation配置各自独立的线程池。 另外,灵活性也带来了一定的危险性。要避免将线程池配置为前面提到的“单业务线程池”模式,从而为整个系统引入瓶颈点。 ServiceComb除了在华为云微服务引擎商用之外,也于2017年12月全票通过进入Apache孵化器。欢迎感兴趣的读者前往开源社区和我们讨论切磋,希望此文可以给正在进行微服务方案实施的读者们一些启发。
  • MongoDB 副本集初始化背后的过程
    本帖最后由 danglf 于 2018-3-23 15:18 编辑副本集方式是MongoDB最常用的部署方式,副本集使用之前必须先初始化, 副本集初始化是通过执行replSetInitiate完成的,本文主要分析副本集收到该命令后背后的逻辑(本文基于MongoDB3.2.18分析)。1.1 入口 当用户执行replSetInitiate 命令后, 最终会调用到CmdReplSetInitiate类的 run 方法,CmdReplSetInitiate 在Replset_commands.cpp里面定义。对于Mongod 启动后, 用户执行各种命令和查询操作, 是如何运行起来的,及command内部原理和体系结构 ,后续会写一篇专门的文章去分析。代码如下: 13173 13174 总体流程如下1 校验mongod的配置文件是不是配置配置副本集参数。2 对于副本集配置参数进行补齐3 核心语句, 调用getGlobalReplicationCoordinator()->processReplSetInitiat, 进入复制集初始化流程。1.2 主体流程: 1 ) 判断当前状态是不是可以进行初始化,必须是副本集之前没有初始化过才可以,否则报错 2) 构建内部初始化需要的配置数据, 并判断初始化副本集中的副本集名称和配置文件中的是不是一致,初次以外还有其他的逻辑校验 3 ) 判断当前实例状态, 发起选举,选出主 备节点 4)启动所有后台线程: a)后台拉取主节点oplog线程 b) 向主节点同步自己复制状态的线程 c) 定时快照线程。 相关流程对应代码如下 1)状态判断 replication_coordinator_impl.cpp –>processReplSetInitiate 13175 2) 部分参数校验 13176 3) 发起选举 processReplSetInitiate->_finishReplSetInitiate->_performPostMemberStateUpdateAction->_startElectSelfV1 4) 启动后台线程: 13177 以上就是replSetInitiate 命令后,mongod 内部的大致流程, 分析比较粗, 实际上每一个流程,如选举的内部机制, 初始化同步过程, 增量同步过程, 备节点如何应用oplog,每一个都可以作为专项来分析,后面会持续进行, 文中如有不正确的地方,也欢迎指出并讨论。
  • [行业前沿] 业务进程spring版本是使用的3系列,怎么解决冲突
    业务进程spring版本是使用的3系列,由于一些原因没办法升级到4,怎么解决与客户端包依赖spring4的冲突。
  • [技术干货] MySQL半同步复制演进
    1 介绍MySQL 提供了异步复制,主库并不关心备库是否收到日志,从而可能导致较多的数据丢失。从MySQL5.5开始引入了一种半同步复制功能,该功能可以确保主服务器和至少一台从服务器之间的数据一致性和冗余,从而可以减少数据的丢失。1.1 异步复制 master将binlog event发送给slave后,不去确认slave是否已经收到就返回成功给客户端。80301.2 半同步复制 master将binlog event发送给slave后,确认slave已经收到时才返回成功给客户端。 80312 半同步复制演进2.1 减少LOCK_log锁冲突在MySQL5.7.2版本中重构DUMP线程减小了LOCK_log锁冲突。 在之前版本中,写binlog的设计为先持有LOCK_log锁(锁住了整个binlog文件),写完后释放锁,再发送binlog更新信号。DUMP线程在读取binlog内容时,也要先获取到LOCK_log锁,然后读取binlog内容,然后释放锁。这样当写binlog的线程和DUMP线程在处理同一个binlog时,就会有较严重的冲突。 在MySQL5.7中调整了这块的设计。因为写binlog时是追加写入,DUMP线程只是读取,这样可以用一个变量(binlog_end)记录binlog文件末尾位置,每次追加写入后更新下这个变量就可以(用lock_binlog_end锁保护变量binlog_end),DUMP线程在读取时只要不超过标记的binlog末尾位置就可以。只需要在读取到末尾位置时,获取下lock_binlog_end锁,获取下最新的末尾位置。2.2 半同步复制等待点调整 在MySQL5.6版本中,master将每个事物写入binlog传递到slave刷新到磁盘(relay log),同时主库提交事务。master等待slave 反馈收到relay log,只有收到ACK后master才将commit OK结果反馈给客户端。 这样主库返回给客户端commit ok前,可能事物已经真正提交了,但是从库还没有收到日志。这时其他客户端已经可以看到了提交的事物,这时如果主库故障,主备切换后,其他客户端发现刚才提交的事物又消失了。 为了解决这个问题MySQL在5.7.2中有了一个解决方案” transaction loss-less”.具体逻辑为master将每个事务写入binlog , 传递到slave刷新到磁盘(relay log)。master等待slave反馈接收到relay log的ack之后,再提交事务并且返回commit OK结果给客户端。即使主库crash,所有在主库上已经提交的事务都能保证已经同步到slave的relay log中。同时引入了参数rpl_semi_sync_master_wait_point来控制使用的策略,默认值为AFTER_SYNC,即5.7的改进做法,还有一个可选值为AFTER_COMMIT,即5.6中的策略。MySQL5.6处理逻辑图:8032MySQL5.7.2处理逻辑图:80332.3 独立线程处理ACKMySQL5.6的semi sync 受限于dump thread,原因是dump thread 承担了两份不同且又十分频繁的任务:传送binlog给slave ,还需要等待slave反馈信息,而且这两个任务是串行的,dump thread 必须等待 slave 返回之后才会传送下一个 events 事务。dump thread 已然成为整个半同步提高性能的瓶颈。在高并发业务场景下,这样的机制会影响数据库整体的TPS 。8034为了解决上述问题,在5.7.4版本的semi sync框架中,独立出一个 ack collector thread ,专门用于接收slave的反馈信息。这样master 上有两个线程独立工作,可以同时发送binlog 到slave ,和接收slave的反馈。80352.4 master支持等待多个slave在MySQL5.6中当半同步开启时,master只是保证一个slave接收到日志,这样当在一主多从的场景下,master和接收到最新日志的slave都挂掉了,那就会丢失数据。这样增加多个备库也不会提高HA的可用性。在MySQL5.7.3中新增了参数rpl_semi_sync_master_wait_slave_count,可以用来控制master需要保证多少个slave接收到日志。
总条数:813 到第 页
上滑加载中