Category Archives: Web

微服务化之缓存的设计

本文章为《互联网高并发微服务化架构实践》系列课程的第五篇

前四篇为:

微服务化的基石——持续集成

微服务的接入层设计与动静资源隔离

微服务化的数据库设计与读写分离

微服务化之无状态化与容器化

在高并发场景下,需要通过缓存来减少数据库的压力,使得大量的访问进来能够命中缓存,只有少量的需要到数据库层。由于缓存基于内存,可支持的并发量远远大于基于硬盘的数据库。所以对于高并发设计,缓存的设计时必不可少的一环。

一、为什么要使用缓存

为什么要使用缓存呢?源于人类的一个梦想,就是多快好省的建设社会主义。

多快好省?很多客户都这么要求,但是作为具体做技术的你,当然知道,好就不能快,多就没法省。

可是没办法,客户都这样要求:

这个能不能便宜一点,你咋这么贵呀,你看人家都很便宜的。(您好,这种打折的房间比较靠里,是不能面向大海的)

你们的性能怎么这么差啊,用你这个系统跑的这么慢,你看人家广告中说速度能达到多少多少。(您好,你如果买一个顶配的,我们也是有这种性能的)

你们服务不行啊,你就不能彬彬有礼,穿着整齐,送点水果瓜子啥的?(您好,我们兰州拉面馆没有这项服务,可以去对面的俏江南看一下)

这么贵的菜,一盘就这么一点点,都吃不饱,就不能上一大盘么。(您好,对面的兰州拉面10块钱一大碗)

怎么办呢?劳动人民还是很有智慧的,就是聚焦核心需求,让最最核心的部分享用好和快,而非核心的部门就多和省就可以了。

你可以大部分时间住在公司旁边的出租屋里面,但是出去度假的一个星期,选一个面朝大海,春暖花开的五星级酒店。

你可以大部分时间都挤地铁,挤公交,跋涉2个小时从北五环到南五环,但是有急事的时候,你可以打车,想旅游的时候,可以租车。

你可以大部分时间都吃普通的餐馆,而朋友来了,就去高级饭店里面搓一顿。

在计算机世界也是这样样子的,如图所示。

越是快的设备,存储量越小,越贵,而越是慢的设备,存储量越大,越便宜。

对于一家电商来讲,我们既希望存储越来越多的数据,因为数据将来就是资产,就是财富,只有有了数据,我们才知道用户需要什么,同时又希望当我想访问这些数据的时候,能够快速的得到,双十一拼的就是速度和用户体验,要让用户有流畅的感觉。

所以我们要讲大量的数据都保存下来,放在便宜的存储里面,同时将经常访问的,放在贵的,小的存储里面,当然贵的快的往往比较资源有限,因而不能长时间被某些数据长期霸占,所以要大家轮着用,所以叫缓存,也就是暂时存着。

二、都有哪些类型的缓存

当一个应用刚开始的时候,架构比较简单,往往就是一个Tomcat,后面跟着一个数据库。

简单的应用,并发量不大的时候,当然没有问题。

然而数据库相当于我们应用的中军大帐,是我们整个架构中最最关键的一部分,也是最不能挂,也最不能会被攻破的一部分,因而所有对数据库的访问都需要一道屏障来进行保护,常用的就是缓存。

我们以Tomcat为分界线,之外我们称为接入层,接入层当然应该有缓存,还有CDN,这个在这篇文章中有详细的描述,微服务的接入层设计与动静资源隔离

Tomcat之后,我们称为应用层,应用层也应该有缓存,这是我们这一节讨论的重点。

最简单的方式就是Tomcat里面有一层缓存,常称为本地缓存LocalCache。

这类的缓存常见的有Ehcache和Guava Cache,由于这类缓存在Tomcat本地,因而访问速度是非常快的。

但是本地缓存有个比较大的缺点,就是缓存是放在JVM里面的,会面临Full GC的问题,一旦出现了FullGC,就会对应用的性能和相应时间产生影响,当然也可以尝试jemalloc的分配方式。

还有一种方式,就是在Tomcat和Mysql中间加了一层Cache,我们常称为分布式缓存。

分布式缓存常见的有Memcached和Redis,两者各有优缺点。

Memcached适合做简单的key-value存储,内存使用率比较高,而且由于是多核处理,对于比较大的数据,性能较好。

但是缺点也比较明显,Memcached严格来讲没有集群机制,横向扩展完全靠客户端来实现。另外Memcached无法持久化,一旦挂了数据就都丢失了,如果想实现高可用,也是需要客户端进行双写才可以。

所以可以看出Memcached真的是设计出来,简简单单为了做一个缓存的。

Redis的数据结构就丰富的多了,单线程的处理所有的请求,对于比较大的数据,性能稍微差一点。

Redis提供持久化的功能,包括RDB的全量持久化,或者AOF的增量持久化,从而使得Redis挂了,数据是有机会恢复的。

Redis提供成熟的主备同步,故障切换的功能,从而保证了高可用性。

所以很多地方管Redis称为内存数据库,因为他的一些特性已经有了数据库的影子。

这也是很多人愿意用Redis的原因,集合了缓存和数据库的优势,但是往往会滥用这些优势,从而忽略了架构层面的设计,使得Redis集群有很大的风险。

很多情况下,会将Redis当做数据库使用,开启持久化和主备同步机制,以为就可以高枕无忧了。

然而Redis的持久化机制,全量持久化则往往需要额外较大的内存,而在高并发场景下,内存本来就很紧张,如果造成swap,就会影响性能。增量持久化也涉及到写磁盘和fsync,也是会拖慢处理的速度,在平时还好,如果高并发场景下,仍然会影响吞吐量。

所以在架构设计角度,缓存就是缓存,要意识到数据会随时丢失的,要意识到缓存的存着的目的是拦截到数据库的请求。如果为了保证缓存的数据不丢失,从而影响了缓存的吞吐量,甚至稳定性,让缓存响应不过来,甚至挂掉,所有的请求击穿到数据库,就是更加严重的事情了。

如果非常需要进行持久化,可以考虑使用levelDB此类的,对于随机写入性能较好的key-value持久化存储,这样只有部分的确需要持久化的数据,才进行持久化,而非无论什么数据,通通往Redis里面扔,同时统一开启了持久化。

三、基于缓存的架构设计要点

所以基于缓存的设计:

1、多层次

这样某一层的缓存挂了,还有另一层可以撑着,等待缓存的修复,例如分布式缓存因为某种原因挂了,因为持久化的原因,同步机制的原因,内存过大的原因等,修复需要一段时间,在这段时间内,至少本地缓存可以抗一阵,不至于一下子就击穿数据库。而且对于特别特别热的数据,热到导致集中式的缓存处理不过来,网卡也被打满的情况,由于本地缓存不需要远程调用,也是分布在应用层的,可以缓解这种问题。

2、分场景

到底要解决什么问题,可以选择不同的缓存。是要存储大的无格式的数据,还是要存储小的有格式的数据,还是要存储一定需要持久化的数据。具体的场景下一节详细谈。

3、要分片

使得每一个缓存实例都不大,但是实例数目比较多,这样一方面可以实现负载均衡,防止单个实例称为瓶颈或者热点,另一方面如果一个实例挂了,影响面会小很多,高可用性大大增强。分片的机制可以在客户端实现,可以使用中间件实现,也可以使用Redis的Cluster的方式,分片的算法往往都是哈希取模,或者一致性哈希。

四、缓存的使用场景

当你的应用扛不住,知道要使用缓存了,应该怎么做呢?

场景1:和数据库中的数据结构保持一致,原样缓存

这种场景是最常见的场景,也是很多架构使用缓存的适合,最先涉及到的场景。

基本就是数据库里面啥样,我缓存也啥样,数据库里面有商品信息,缓存里面也放商品信息,唯一不同的是,数据库里面是全量的商品信息,缓存里面是最热的商品信息。

每当应用要查询商品信息的时候,先查缓存,缓存没有就查数据库,查出来的结果放入缓存,从而下次就查到了。

这个是缓存最最经典的更新流程。这种方式简单,直观,很多缓存的库都默认支持这种方式。

场景2:列表排序分页场景的缓存

有时候我们需要获得一些列表数据,并对这些数据进行排序和分页。

例如我们想获取点赞最多的评论,或者最新的评论,然后列出来,一页一页的翻下去。

在这种情况下,缓存里面的数据结构和数据库里面完全不一样。

如果完全使用数据库进行实现,则按照某种条件将所有的行查询出来,然后按照某个字段进行排序,然后进行分页,一页一页的展示。

但是当数据量比较大的时候,这种方式往往成为瓶颈,首先涉及的数据库行数比较多,而且排序也是个很慢的活,尽管可能有索引,分页也是翻页到最后,越是慢。

在缓存里面,就没必要每行一个key了,而是可以使用Redis的列表方式进行存储,当然列表的长短是有限制的,肯定放不下数据库里面这么多,但是大家会发现其实对于所有的列表,用户往往没有耐心看个十页八页的,例如百度上搜个东西,也是有排序和分页的,但是你每次都往后翻了吗,每页就十条,就算是十页,或者一百页,也就一千条数据,如果保持ID的话,完全放的下。

如果已经排好序,放在Redis里面,那取出列表,翻页就非常快了。

可以后台有一个线程,异步的初始化和刷新缓存,在缓存里面保存一个时间戳,当有更新的时候,刷新时间戳,异步任务发现时间戳改变了,就刷新缓存。

场景3:计数缓存

计数对于数据库来讲,是一个非常繁重的工作,需要查询大量的行,最后得出计数的结论,当数据改变的时候,需要重新刷一遍,非常影响性能。

因此可以有一个计数服务,后端是一个缓存,将计数作为结果放在缓存里面,当数据有改变的时候,调用计数服务增加或者减少计数,而非通过异步数据库count来更新缓存。

计数服务可以使用Redis进行单个计数,或者hash表进行批量计数

场景4:重构维度缓存

有时候数据库里面保持的数据的维度是为了写入方便,而非为了查询方便的,然而同时查询过程,也需要处理高并发,因而需要为了查询方便,将数据重新以另一个维度存储一遍,或者说将多给数据库的内容聚合一下,再存储一遍,从而不用每次查询的时候都重新聚合,如果还是放在数据库,比较难维护,放在缓存就好一些。

例如一个商品的所有的帖子和帖子的用户,以及一个用户发表过的所有的帖子就是属于两个维度。

这需要写入一个维度的时候,同时异步通知,更新缓存中的另一个维度。

在这种场景下,数据量相对比较大,因而单纯用内存缓存memcached或者redis难以支撑,往往会选择使用levelDB进行存储,如果levelDB的性能跟不上,可以考虑在levelDB之前,再来一层memcached。

场景5:较大的详情内容数据缓存

对于评论的详情,或者帖子的详细内容,属于非结构化的,而且内容比较大,因而使用memcached比较好。

五、缓存三大矛盾问题

1、缓存实时性和一致性问题:当有了写入后咋办?

虽然使用了缓存,大家心里都有一个预期,就是实时性和一致性得不到完全的保证,毕竟数据保存了多份,数据库一份,缓存中一份,当数据库中因写入而产生了新的数据,往往缓存是不会和数据库操作放在一个事务里面的,如何将新的数据更新到缓存里面,什么时候更新到缓存里面,不同的策略不一样。

从用户体验角度,当然是越实时越好,用户体验越流畅,完全从这个角度出发,就应该有了写入,马上废弃缓存,触发一次数据库的读取,从而更新缓存。但是这和第三个问题,高并发就矛盾了,如果所有的都实时从数据库里面读取,高并发场景下,数据库往往受不了。

2、缓存的穿透问题:当没有读到咋办?

为什么会出现缓存读取不到的情况呢?

第一:可能读取的是冷数据,原来从来没有访问过,所以需要到数据库里面查询一下,然后放入缓存,再返回给客户。

第二:可能数据因为有了写入,被实时的从缓存中删除了,就如第一个问题中描述的那样,为了保证实时性,当数据库中的数据更新了之后,马上删除缓存中的数据,导致这个时候的读取读不到,需要到数据库里面查询后,放入缓存,再返回给客户。

第三:可能是缓存实效了,每个缓存数据都会有实效时间,过了一段时间没有被访问,就会失效,这个时候数据就访问不到了,需要访问数据库后,再放入缓存。

第四:数据被换出,由于缓存内存是有限的,当使用快满了的时候,就会使用类似LRU策略,将不经常使用的数据换出,所以也要访问数据库。

第五:后端确实也没有,应用访问缓存没有,于是查询数据库,结果数据库里面也没有,只好返回客户为空,但是尴尬的是,每次出现这种情况的时候,都会面临着一次数据库的访问,纯属浪费资源,常用的方法是,讲这个key对应的结果为空的事实也进行缓存,这样缓存可以命中,但是命中后告诉客户端没有,减少了数据库的压力。

无论哪种原因导致的读取缓存读不到的情况,该怎么办?是个策略问题。

一种是同步访问数据库后,放入缓存,再返回给客户,这样实时性最好,但是给数据库的压力也最大。

另一种方式就是异步的访问数据库,暂且返回客户一个fallback值,然后同时触发一个异步更新,这样下次就有了,这样数据库压力小很多,但是用户就访问不到实时的数据了。

3、缓存对数据库高并发访问:都来访问数据库咋办?

我们本来使用缓存,是来拦截直接访问数据库请求的,从而保证数据库大本营永远处于健康的状态。但是如果一遇到不命中,就访问数据库的话,平时没有什么问题,但是大促情况下,数据库是受不了的。

一种情况是多个客户端,并发状态下,都不命中了,于是并发的都来访问数据库,其实只需要访问一次就好,这种情况可以通过加锁,只有一个到后端来实现。

另外就是即便采取了上述的策略,依然并发量非常大,后端的数据库依然受不了,则需要通过降低实时性,将缓存拦在数据库前面,暂且撑住,来解决。

六、解决缓存三大矛盾的刷新策略

1、实时策略

所谓的实时策略,是平时缓存使用的最常用的策略,也是保持实时性最好的策略。

读取的过程,应用程序先从cache取数据,没有得到,则从数据库中取数据,成功后,放到缓存中。如果命中,应用程序从cache中取数据,取到后返回。

写入的过程,把数据存到数据库中,成功后,再让缓存失效,失效后下次读取的时候,会被写入缓存。那为什么不直接写缓存呢?因为如果两个线程同时更新数据库,一个将数据库改为10,一个将数据库改为20,数据库有自己的事务机制,可以保证如果20是后提交的,数据库里面改为20,但是回过头来写入缓存的时候就没有事务了,如果改为20的线程先更新缓存,改为10的线程后更新缓存,于是就会长时间出现缓存中是10,但是数据库中是20的现象。

这种方式实时性好,用户体验好,是默认应该使用的策略。

2、异步策略

所谓异步策略,就是当读取的时候读不到的时候,不直接访问数据库,而是返回一个fallback数据,然后往消息队列里面放入一个数据加载的事件,在背后有一个任务,收到事件后,会异步的读取数据库,由于有队列的作用,可以实现消峰,缓冲对数据库的访问,甚至可以将多个队列中的任务合并请求,合并更新缓存,提高了效率。

当更新的时候,异步策略总是先更新数据库和缓存中的一个,然后异步的更新另一个。

一是先更新数据库,然后异步更新缓存。当数据库更新后,同样生成一个异步消息,放入消息队列中,等待背后的任务通过消息进行缓存更新,同样可以实现消峰和任务合并。缺点就是实时性比较差,估计要过一段时间才能看到更新,好处是数据持久性可以得到保证。

一是先更新缓存,然后异步更新数据库。这种方式读取和写入都用缓存,将缓存完全挡在了数据库的前面,把缓存当成了数据库在用。所以一般会使用有持久化机制和主备的redis,但是仍然不能保证缓存不丢数据,所以这种情况适用于并发量大,但是数据没有那么关键的情况,好处是实时性好。

在实时策略扛不住大促的时候,可以根据场景,切换到上面的两种模式的一个,算是降级策略。

3、定时策略

如果并发量实在太大,数据量也大的情况,异步都难以满足,可以降级为定时刷新的策略,这种情况下,应用只访问缓存,不访问数据库,更新频率也不高,而且用户要求也不高,例如详情,评论等。

这种情况下,由于数据量比较大,建议将一整块数据拆分成几部分进行缓存,而且区分更新频繁的和不频繁的,这样不用每次更新的时候,所有的都更新,只更新一部分。并且缓存的时候,可以进行数据的预整合,因为实时性不高,读取预整合的数据更快。

有关缓存就说到这里,下一节讲分布式事务。

from:https://mp.weixin.qq.com/s/-9wHpKGf7aJSbtShpCcoVg

HTTP协议

我们知道目前很多应用系统中的内容传输协议采用的HTTP协议,因此不管你是前端人员、后端人员、运维人员,甚至是管理人员,都需要掌握HTTP知识!!

HTTP发展历史

HTTP/0.9  

该版本只有一个命令GET;没有HEADER等描述数据的信息; 服务器发送完毕,就关闭TCP连接。

HTTP/1.0  

该版本增加了很多命令;增加status code 和header;多字符集支持、多部分发送、权限、缓存等。

HTTP/1.1   

该版本增加了持久连接Pipeline,增加host和其他一些命令。持久连接会在HTTP特性中介绍;如果没有pipeline,那么Web服务器就需要串行处理请求,而有了pipeline,Web服务器就并行处理请求;而增加host实现了一台物理设备可以运行多个web服务。

HTTP/2.0   

所有数据以二进制传输,之前版本使用字符串进行传输;同一个连接 里面发送多个请求不再需要按照顺序来;头信息压缩以及推送等提高效率的功能。

HTTP三次握手

为什么要三次握手?因为网络是有可能延迟的,当客户端没有收到服务端的确认包,如果没有第三次握手,那么服务端不知道上次传输是不是被客户端正常接收了,如果没有接收,服务端的这个端口也是打开的,这就比较浪费资源。

HTTP报文

HTTP报文分为请求报文响应报文,请求报文和响应报文分为起始行、首部(header)和主体(body),请求报文的首部包括三部分,分别是HTTP方法、资源目录和协议,而响应报文的首部包括协议版本、状态码和状态吗对应的意思,比如200状态的意思是ok。需要注意的是:HTTP header和HTTP body之间以一行分隔。

HTTP方法  

HTTP方法定义对资源的操作,常用的有GET、POST等,这就就不详细展开了。

HTTP Code  

HTTP Code用于定义服务器对请求的处理结果,各个区间的code有不用的语义。1xx  表示信息响应类,表示接收到请求并且继续处理;2xx 表示成功;3xx 表示重定向;4xx 表示客户端出错;5xx 表示服务器出错。

HTTP特性

跨域请求   

同源策略,也就是说当两个请求的URL的协议、host和端口都相同的情况下,我们才认为这两个请求是同域的即同源,而只要协议、host和端口只要有一项是不同的,我们就认为是不同源的,即跨域,例如:

http://www.mukedada.com:80

http://www.mukedada.com:8080

上述两个请求就是跨域请求。需要注意的是跨域请求不是说浏览器限制了发起跨站请求,浏览器只是将返回结果拦截下来,最好的例子就是CSRF跨站脚本攻击。如果我们想让浏览器放行返回结果,则通过以下方法:

  1. 服务端设置Access-Control-Allow-Origin参数为允许,例如’Access-Control-Allow-Origin’ : ‘*’
  2. <link>、<img>和<script>三标签中的请求是允许跨域的,这也是JSONP的跨域做法。
Cache  Control 

对于静态资源,比如说image、js等,它们是不会经常方式变更的,而且它们的容量比较大,如果我们每次访问都要从服务器从获取相应数据,那么性能就会变得比较差,因此HTTP协议定义一些和缓存相关的参数。

可缓存性,表示在哪些地方可以缓存,比如说客户端浏览器、代理服务器等,它有三个常用的参数:public、private、no-cache。public 表明响应可以被任何对象缓存,包括发送请求的客户端浏览器、代理服务器等等;private 表示响应只能被单个用户缓存,不能作为共享缓存,即代理服务器不能缓存它;no-cache表明强制所有缓存了该响应的缓存用户,在使用已存储的缓存之前,发送带验证器的请求到源始服务器。

到期,max-age=<seconds>,设置缓存存储的最大周期,超过这个时间缓存就被认为过期。s-maxage=<seconds> 它的作用域仅在共享缓存(比如各个代理)。max-stale=<seconds> 表明客户端愿意接收一个已过期的资源。

验证,must-revalidate,缓存必须在使用之前验证旧资源的状态,并且不可使用过期资源。proxy-revalidate,与must-revalidate作用相同,但它仅适用于共享缓存(例如代理),并被私有缓存忽略。

其他。no-store,客户端和代理服务器不存储任何缓存,而是直接从服务器获取内容。no-transform:不得对资源进行转换或转变。Content-Encoding, Content-Range, Content-Type等HTTP头不能由代理修改。例如,非透明代理可以对图像格式进行转换,以便节省缓存空间或者减少缓慢链路上的流量。 no-transform指令不允许这样做。

Cookie   

服务端通过Set-Cookie将相关数据保存到浏览器中,而这些相关数据就是Cookie,那么,下次在同域的请求中就会带上这些Cookie,Cookie是键值对,可以设置多个。Cookie中通过max-age和expires设置过期时间,Secure值在https的时候发送,HttpOnly无法通过document.cookie访问。具体可以参考Session 和 Cookie

资源验证  

在Cache Control中我们介绍当设置no-cache参数时,表明每次请求都要到服务器验证,验证结果表明可以读取本地缓存才可以从本地读取缓存。只有到数据发生修改时,我们才需要从服务端读取最新数据,否则从本地读取缓存。此时,判断数据是否发生修改就变得尤为重要,通常我们采用Last-Modfied和Etag两个验证头来验证数据是否发生修改。其中Last-Modifed 通常配合If-Modified-Since或者If-UnModified-Since使用,而Etag 通常配合If-Match或者If-Non-Match使用。为了帮助大家理解,我举一个栗子。假设我们访问mudedada.com返回头信息包含:

Last-Modified:888

Etag:123

下一次访问mukedada.com的请求头中就会包含:

If-Modified-Since:888

If-Non-Match:123

服务器会比较请求头中的Last-Modified、Etag 和服务器中的对应值是否相同,如果不相同则重新获取,否则从本地缓存中获取。

长连接   

我们知道一个HTTP需要创建一个TCP连接,完成之后就关闭TCP连接,这个成本比较高(因为创建一个TCP连接需要通过三次握手),所以在HTTP/1.1开始支持长连接,请求头标识是Connection:keep-alive。如下图所示,同一个Connection ID表示同一个连接。需要注意的是同一个连接只能是同域请求。

数据协商   

数据协商指的是客户端向服务端发送请求时,客户端会声明它希望服务端返回个格式是什么?服务端根据客户端的声明来判断返回什么要的数据。其中客户端通过Accept、Accept-Encoding等参数进行设置,而服务端通过Content-Type等参数进行设置。

客户端相关参数

  1. Accept指定返回数据类型;
  2. Accept-Encoding指定服务端的数据压缩方式,目前服务端的压缩算法有gzip, deflate, br等;
  3. Accept-Language指定返回数据的语言,例如 Accept-Language:  zh-CN,zh;q=0.9,en;q=0.8,其中q表示的是权重,也就是说浏览器更希望服务器返回的是中文;
  4. User-Agent表示浏览器的相关信息,它能区分是移动端浏览器还是PC端浏览器,从而返回特定的页面。

服务端相关参数:

  1. Content-Type指的是服务端返回的数据类型;
  2. Content-Encoding对应客户端的Accept-Encoding,指的是数据压缩方式;
  3. Content-Language服务端语言。

from:https://mp.weixin.qq.com/s/vRQ2zuKxyLaBxcm9lolL7w

从零开始搭建创业公司后台技术栈

有点眼晕,以上只是我们会用到的一些语言的合集,而且只是语言层面的一部分,就整个后台技术栈来说,这只是一个开始,从语言开始,还有很多很多的内容。今天要说的后台是大后台的概念,放在服务器上的东西都属于后台的东西,比如使用的框架,语言,数据库,服务,操作系统等等,整个后台技术栈我的理解包括4个层面的内容:

  • 语言: 用了哪些开发语言,如:c++/java/go/php/python/ruby等等;
  • 组件:用了哪些组件,如:MQ组件,数据库组件等等;
  • 流程:怎样的流程和规范,如:开发流程,项目流程,发布流程,监控告警流程,代码规范等等;
  • 系统:系统化建设,上面的流程需要有系统来保证,如:规范发布流程的发布系统,代码管理系统等等;

结合以上的的4个层面的内容,整个后台技术栈的结构如图2所示:

[图2 后台技术栈结构]

以上的这些内容都需要我们从零开始搭建,在创业公司,没有大公司那些完善的基础设施,需要我们从开源界,从云服务商甚至有些需要自己去组合,去拼装,去开发一个适合自己的组件或系统以达成我们的目标。咱们一个个系统和组件的做选型,最终形成我们的后台技术栈。

一、各系统组件选型

1、项目管理/Bug管理/问题管理

项目管理软件是整个业务的需求,问题,流程等等的集中地,大家的跨部门沟通协同大多依赖于项目管理工具。有一些 SAAS 的项目管理服务可以使用,但是很多时间不满足需求,此时我们可以选择一些开源的项目,这些项目本身有一定的定制能力,有丰富的插件可以使用,一般的创业公司需求基本上都能得到满足,常用的项目如下:

  • Redmine: 用 Ruby 开发的,有较多的插件可以使用,能自定义字段,集成了项目管理,BUG 问题跟踪,WIKI 等功能,不过好多插件 N 年没有更新了;
  • Phabricator: 用 PHP 开发的,facebook 之前的内部工具,开发这工具的哥们离职后自己搞了一个公司专门做这个软件,集成了代码托管, Code Review,任务管理,文档管理,问题跟踪等功能,强烈推荐较敏捷的团队使用;
  • Jira:用 Java 开发的,有用户故事,task 拆分,燃尽图等等,可以做项目管理,也可以应用于跨部门沟通场景,较强大;
  • 悟空CRM :这个不是项目管理,这个是客户管理,之所以在这里提出来,是因为在 To B 的创业公司里面,往往是以客户为核心来做事情的,可以将项目管理和问题跟进的在悟空 CRM 上面来做,他的开源版本已经基本实现了 CR< 的核心 功能,还带有一个任务管理功能,用于问题跟进,不过用这个的话,还是需要另一个项目管理的软件协助,顺便说一嘴,这个系统的代码写得很难维护,只能适用于客户规模小(1万以内)时。

2、DNS

DNS 是一个很通用的服务,创业公司基本上选择一个合适的云厂商就行了,国内主要是两家:

  • 阿里万网:阿里 2014 年收购了万网,整合了其域名服务,最终形成了现在的阿里万网,其中就包含 DNS 这块的服务;
  • 腾讯 DNSPod: 腾讯 2012 年以 4000 万收购 DNSPod 100% 股份,主要提供域名解析和一些防护功能;

如果你的业务是在国内,主要就是这两家,选 一个就好,像今日头条这样的企业用的也是 DNSPod 的服务,除非一些特殊的原因才需要自建,比如一些 CDN 厂商,或者对区域有特殊限制的。要实惠一点用阿里最便宜的基础版就好了,要成功率高一些,还是用DNSPod 的贵的那种。

在国外还是选择亚马逊吧,阿里的 DNS 服务只有在日本和美国有节点,东南亚最近才开始部点, DNSPod 也只有美国和日本,像一些出海的企业,其选择的云服务基本都是亚马逊。

如果是线上产品,DNS 强烈建议用付费版,阿里的那几十块钱的付费版基本可以满足需求。如果还需要一些按省份或按区域调试的逻辑,则需要加钱,一年也就几百块,省钱省力。

如果是国外,优先选择亚马逊,如果需要国内外互通并且有自己的 APP 的话,建议还是自己实现一些容灾逻辑或者智能调度,因为没有一个现成的 DNS 服务能同时较好的满足国内外场景,或者用多个域名,不同的域名走不同的 DNS 。

3、LB(负载均衡)

LB(负载均衡)是一个通用服务,一般云厂商的 LB 服务基本都会如下功能:

  • 支持四层协议请求(包括 TCP、UDP 协议);
  • 支持七层协议请求(包括 HTTP、HTTPS 协议);
  • 集中化的证书管理系统支持 HTTPS 协议;
  • 健康检查;

如果你线上的服务机器都是用的云服务,并且是在同一个云服务商的话,可以直接使用云服务商提供的 LB 服务,如阿里云的 SLB,腾讯云的 CLB, 亚马逊 的 ELB 等等。如果是自建机房基本都是 LVS + Nginx。

4、CDN

CDN 现在已经是一个很红很红的市场,基本上只能挣一些辛苦钱,都是贴着成本在卖。国内以网宿为龙头,他们家占据整个国内市场份额的40%以上,后面就是腾讯,阿里。网宿有很大一部分是因为直播的兴起而崛起。

国外,Amazon 和 Akamai 合起来占比大概在 50%,曾经的国际市场老大 Akamai 拥有全球超一半的份额,在 Amazon CDN入局后,份额跌去了将近 20%,众多中小企业都转向后者,Akamai 也是无能为力。

国内出海的 CDN 厂商,更多的是为国内的出海企业服务,三家大一点的 CDN 服务商里面也就网宿的节点多一些,但是也多不了多少。阿里和腾讯还处于前期阶段,仅少部分国家有节点。

就创业公司来说,CDN 用腾讯云或阿里云即可,其相关系统较完善,能轻松接入,网宿在系统支持层面相对较弱一些,而且还贵一些。并且,当流量上来后,CDN 不能只用一家,需要用多家,不同的 CDN 在全国的节点覆盖不一样,而且针对不同的客户云厂商内部有些区分客户集群,并不是全节点覆盖(但有些云厂商说自己是全网节点),除了节点覆盖的问题,多 CDN 也在一定程度上起到容灾的作用。

5、RPC框架

维基百科对 RPC 的定义是:远程过程调用(Remote Procedure Call,RPC)是一个计算机通信协议。该协议允许运行于一台计算机的程序调用另一台计算机的子程序,而程序员无需额外地为这个交互作用编程。

通俗来讲,一个完整的RPC调用过程,就是 Server 端实现了一个函数,客户端使用 RPC 框架提供的接口,调用这个函数的实现,并获取返回值的过程。

业界 RPC 框架大致分为两大流派,一种侧重跨语言调用,另一种是偏重服务治理。

跨语言调用型的 RPC 框架有 Thrift、gRPC、Hessian、Hprose 等。这类 RPC 框架侧重于服务的跨语言调用,能够支持大部分的语言进行语言无关的调用,非常适合多语言调用场景。但这类框架没有服务发现相关机制,实际使用时需要代理层进行请求转发和负载均衡策略控制。

其中,gRPC 是 Google 开发的高性能、通用的开源 RPC 框架,其由 Google 主要面向移动应用开发并基于 HTTP/2 协议标准而设计,基于 ProtoBuf(Protocol Buffers) 序列化协议开发,且支持众多开发语言。本身它不是分布式的,所以要实现框架的功能需要进一步的开发。

Hprose(High Performance Remote Object Service Engine) 是一个 MIT 开源许可的新型轻量级跨语言跨平台的面向对象的高性能远程动态通讯中间件。

服务治理型的 RPC 框架的特点是功能丰富,提供高性能的远程调用、服务发现及服务治理能力,适用于大型服务的服务解耦及服务治理,对于特定语言(Java)的项目可以实现透明化接入。缺点是语言耦合度较高,跨语言支持难度较大。国内常见的冶理型 RPC 框架如下:

  • Dubbo: Dubbo 是阿里巴巴公司开源的一个 Java 高性能优秀的服务框架,使得应用可通过高性能的 RPC 实现服务的输出和输入功能,可以和 Spring 框架无缝集成。当年在淘宝内部,Dubbo 由于跟淘宝另一个类似的框架 HSF 有竞争关系,导致 Dubbo 团队解散,最近又活过来了,有专职同学投入。
  • DubboX: DubboX 是由当当在基于 Dubbo 框架扩展的一个 RPC 框架,支持 REST 风格的远程调用、Kryo/FST 序列化,增加了一些新的feature。
  • Motan: Motan 是新浪微博开源的一个 Java 框架。它诞生的比较晚,起于 2013 年,2016 年 5 月开源。Motan 在微博平台中已经广泛应用,每天为数百个服务完成近千亿次的调用。
  • rpcx: rpcx 是一个类似阿里巴巴 Dubbo 和微博  Motan 的分布式的 RPC 服务框架,基于 Golang net/rpc 实现。但是 rpcx 基本只有一个人在维护,没有完善的社区,使用前要慎重,之前做 Golang 的 RPC 选型时也有考虑这个,最终还是放弃了,选择了 gRPC,如果想自己自研一个 RPC 框架,可以参考学习一下。

6、名字发现/服务发现

名字发现和服务发现分为两种模式,一个是客户端发现模式,一种是服务端发现模式。

框架中常用的服务发现是客户端发现模式。

所谓服务端发现模式是指客户端通过一个负载均衡器向服务发送请求,负载均衡器查询服务注册表并把请求路由到一台可用的服务实例上。现在常用的负载均衡器都是此类模式,常用于微服务中。

所有的名字发现和服务发现都要依赖于一个可用性非常高的服务注册表,业界常用的服务注册表有如下三个:

  • etcd,一个高可用、分布式、一致性、key-value方式的存储,被用在分享配置和服务发现中。两个著名的项目使用了它:k8s和Cloud Foundry。
  • consul,一个发现和配置服务的工具,为客户端注册和发现服务提供了API,Consul还可以通过执行健康检查决定服务的可用性。
  • Apache Zookeeper,是一个广泛使用、高性能的针对分布式应用的协调服务。Apache Zookeeper本来是 Hadoop 的子工程,现在已经是顶级工程了。

除此之外也可以自己实现服务实现,或者用 Redis 也行,只是需要自己实现高可用性。

7、关系数据库

关系数据库分为两种,一种是传统关系数据,如 Oracle, MySQL,Maria, DB2,PostgreSQL 等等,另一种是 NewSQL,即至少要满足以下五点的新型关系数据库:

  1. 完整地支持SQL,支持JOIN / GROUP BY /子查询等复杂SQL查询;
  2. 支持传统数据标配的 ACID 事务,支持强隔离级别。
  3. 具有弹性伸缩的能力,扩容缩容对于业务层完全透明。
  4. 真正的高可用,异地多活、故障恢复的过程不需要人为的接入,系统能够自动地容灾和进行强一致的数据恢复。
  5. 具备一定的大数据分析能力

传统关系数据库用得最多的是 MySQL,成熟,稳定,一些基本的需求都能满足,在一定数据量级之前基本单机传统数据库都可以搞定,而且现在较多的开源系统都是基于 MySQL,开箱即用,再加上主从同步和前端缓存,百万 pv 的应用都可以搞定了。不过 CentOS 7 已经放弃了 MySQL,而改使用 MariaDB。MariaDB 数据库管理系统是 MySQ L的一个分支,主要由开源社区在维护,采用GPL 授权许可。开发这个分支的原因之一是:甲骨文公司收购了 MySQL 后,有将 MySQ L闭源的潜在风险,因此社区采用分支的方式来避开这个风险。

在 Google 发布了  F1: A Distributed SQL Database That Scales 和  Spanner: Google’s Globally-Distributed Databasa 之后,业界开始流行起 NewSQL。于是有了 CockroachDB,于是有了 奇叔公司的 TiDB。国内已经有比较多的公司使用 TiDB,之前在创业公司时在大数据分析时已经开始应用 TiDB,当时应用的主要原因是 MySQL 要使用分库分表,逻辑开发比较复杂,扩展性不够。

8、NoSQL

NoSQL 顾名思义就是 Not-Only SQL,也有人说是 No – SQL, 个人偏向于Not – Only SQL,它并不是用来替代关系库,而是作为关系型数据库的补充而存在。

常见 NoSQL 有4个类型:

  1. 键值,适用于内容缓存,适合混合工作负载并发高扩展要求大的数据集,其优点是简单,查询速度快,缺点是缺少结构化数据,常见的有 Redis, Memcache, BerkeleyDB 和 Voldemort 等等;
  2. 列式,以列簇式存储,将同一列数据存在一起,常见于分布式的文件系统,其中以 Hbase,Cassandra 为代表。Cassandra 多用于写多读少的场景,国内用得比较多的有 360,大概 1500 台机器的集群,国外大规模使用的公司比较多,如 Ebay,Instagram,Apple 和沃尔玛等等;
  3. 文档,数据存储方案非常适用承载大量不相关且结构差别很大的复杂信息。性能介于 kv 和关系数据库之间,它的灵感来于 lotus notes,常见的有 MongoDB,CouchDB 等等;
  4. 图形,图形数据库擅长处理任何涉及关系的状况。社交网络,推荐系统等。专注于构建关系图谱,需要对整个图做计算才能得出结果,不容易做分布式的集群方案,常见的有 Neo4J,InfoGrid 等。

除了以上4种类型,还有一些特种的数据库,如对象数据库,XML 数据库,这些都有针对性对某些存储类型做了优化的数据库。

在实际应用场景中,何时使用关系数据库,何时使用 NoSQL,使用哪种类型的数据库,这是我们在做架构选型时一个非常重要的考量,甚至会影响整个架构的方案。

9、消息中间件

消息中间件在后台系统中是必不可少的一个组件,一般我们会在以下场景中使用消息中间件:

  • 异步处理:异步处理是使用消息中间件的一个主要原因,在工作中最常见的异步场景有用户注册成功后需要发送注册成功邮件、缓存过期时先返回老的数据,然后异步更新缓存、异步写日志等等;通过异步处理,可以减少主流程的等待响应时间,让非主流程或者非重要业务通过消息中间件做集中的异步处理。
  • 系统解耦:比如在电商系统中,当用户成功支付完成订单后,需要将支付结果给通知ERP系统、发票系统、WMS、推荐系统、搜索系统、风控系统等进行业务处理;这些业务处理不需要实时处理、不需要强一致,只需要最终一致性即可,因此可以通过消息中间件进行系统解耦。通过这种系统解耦还可以应对未来不明确的系统需求。
  • 削峰填谷:当系统遇到大流量时,监控图上会看到一个一个的山峰样的流量图,通过使用消息中间件将大流量的请求放入队列,通过消费者程序将队列中的处理请求慢慢消化,达到消峰填谷的效果。最典型的场景是秒杀系统,在电商的秒杀系统中下单服务往往会是系统的瓶颈,因为下单需要对库存等做数据库操作,需要保证强一致性,此时使用消息中间件进行下单排队和流控,让下单服务慢慢把队列中的单处理完,保护下单服务,以达到削峰填谷的作用。

业界消息中间件是一个非常通用的东西,大家在做选型时有使用开源的,也有自己造轮子的,甚至有直接用 MySQL 或 Redis 做队列的,关键看是否满足你的需求,如果是使用开源的项目,以下的表格在选型时可以参考:

[图3]

以上图的纬度为:名字 成熟度所属社区/公司 文档 授权方式 开发语言支持的协议 客户端支持的语言 性能 持久化 事务 集群 负载均衡 管理界面 部署方式 评价

10 、代 码管理

代码是互联网创业公司的命脉之一,代码管理很重要,常见的考量点包括两块:

  • 安全和权限管理,将代码放到内网并且对于关系公司命脉的核心代码做严格的代码控制和机器的物理隔离;
  • 代码管理工具,Git 作为代码管理的不二之选,你值得拥有。Gitlab 是当今最火的开源 Git 托管服务端,没有之一,虽然有企业版,但是其社区版基本能满足我们大部分需求,结合 Gerrit 做 Code review,基本就完美了。当然 Gitlab 也有代码对比,但没Gerrit 直观。Gerrit 比 Gitlab 提供了更好的代码检查界面与主线管理体验,更适合在对代码质量有高要求的文化下使用。

11、持续集成

持续集成简,称 CI(continuous integration), 是一种软件开发实践,即团队开发成员经常集成他们的工作,每天可能会发生多次集成。每次集成都通过自动化的构建(包括编译,发布,自动化测试)来验证,从而尽早地发现集成错误。持续集成为研发流程提供了代码分支管理/比对、编译、检查、发布物输出等基础工作,为测试的覆盖率版本编译、生成等提供统一支持。

业界免费的持续集成工具中系统我们有如下一些选择:

  • Jenkins:Jjava写的 有强大的插件机制,MIT协议开源 (免费,定制化程度高,它可以在多台机器上进行分布式地构建和负载测试)。Jenkins可以算是无所不能,基本没有 Jenkins 做不了的,无论从小型团队到大型团队 Jenkins 都可以搞定。 不过如果要大规模使用,还是需要有人力来学习和维护。
  • TeamCity: TeamCity与Jenkins相比使用更加友好,也是一个高度可定制化的平台。但是用的人多了,TeamCity就要收费了。
  • Strider: Strider 是一个开源的持续集成和部署平台,使用 Node.js 实现,存储使用的是 MongoDB,BSD 许可证,概念上类似 Travis 和Jenkins。
  • GitLabCI:从GitLab8.0开始,GitLab CI 就已经集成在 GitLab,我们只要在项目中添加一个 .gitlab-ci.yml 文件,然后添加一个Runner,即可进行持续集成。并且 Gitlab 与 Docker 有着非常好的相互协作的能力。免费版与付费版本不同可以参见这里: https://about.gitlab.com/products/feature-comparison/
  • Travis:Travis 和 Github 强关联;闭源代码使用 SaaS 还需考虑安全问题; 不可定制;开源项目免费,其它收费;
  • Go: Go是ThoughtWorks公司最新的Cruise Control的化身。除了 ThoughtWorks 提供的商业支持,Go是免费的。它适用于Windows,Mac和各种Linux发行版。

12、日志系统

日志系统一般包括打日志,采集,中转,收集,存储,分析,呈现,搜索还有分发等。一些特殊的如染色,全链条跟踪或者监控都可能需要依赖于日志系统实现。日志系统的建设不仅仅是工具的建设,还有规范和组件的建设,最好一些基本的日志在框架和组件层面加就行了,比如全链接跟踪之类的。

对于常规日志系统ELK能满足大部分的需求,ELK 包括如下组件:

  • ElasticSearch 是个开源分布式搜索引擎,它的特点有:分布式,零配置,自动发现,索引自动分片,索引副本机制,restful风格接口,多数据源,自动搜索负载等。
  • Logstash 是一个完全开源的工具,它可以对你的日志进行收集、分析,并将其存储供以后使用。
  • Kibana 是一个开源和免费的工具,它可以为 Logstash 和 ElasticSearch 提供的日志分析友好的 Web 界面,可以帮助汇总、分析和搜索重要数据日志。

Filebeat 已经完全替代了 Logstash-Forwarder 成为新一代的日志采集器,同时鉴于它轻量、安全等特点,越来越多人开始使用它。

因为免费的 ELK 没有任何安全机制,所以这里使用了 Nginx 作反向代理,避免用户直接访问 Kibana 服务器。加上配置 Nginx 实现简单的用户认证,一定程度上提高安全性。另外,Nginx 本身具有负载均衡的作用,能够提高系统访问性能。ELK 架构如图4所示:

[图4] ELK 流程图

对于有实时计算的需求,可以使用 Flume+Kafka+Storm+MySQL方案,一 般架构如图5所示:

[图5] 实时分析系统架构图

其中:

  • Flume 是一个分布式、可靠、和高可用的海量日志采集、聚合和传输的日志收集系统,支持在日志系统中定制各类数据发送方,用于收集数据;同时,Flume 提供对数据进行简单处理,并写到各种数据接受方(可定制)的能力。
  • Kafka 是由 Apache 软件基金会开发的一个开源流处理平台,由 Scala 和 Java 编写。其本质上是一个“按照分布式事务日志架构的大规模发布/订阅消息队列”,它以可水平扩展和高吞吐率而被广泛使用。

Kafka 追求的是高吞吐量、高负载,Flume 追求的是数据的多样性,二者结合起来简直完美。

13、监控系统

监控系统只包含与后台相关的,这里主要是两块,一个是操作系统层的监控,比如机器负载,IO,网络流量,CPU,内存等操作系统指标的监控。另一个是服务质量和业务质量的监控,比如服务的可用性,成功率,失败率,容量,QPS 等等。常见业务的监控系统先有操作系统层面的监控(这部分较成熟),然后扩展出其它监控,如 zabbix,小米的 open-falcon,也有一出来就是两者都支持的,如 prometheu s。如果对业务监控要求比较高一些,在创业选型中建议可以优先考虑 prometheus。这里有一个有趣的分布,如图6所示

[图6 监控系统分布]

亚洲区域使用 zabbix 较多,而美洲和欧洲,以及澳大利亚使用 prometheus 居多,换句话说,英文国家地区(发达国家?)使用prometheus 较多。

Prometheus 是由 SoundCloud 开发的开源监控报警系统和时序列数据库( TSDB )。Prometheus 使用 Go 语言开发,是 Google BorgMon 监控系统的开源版本。相对于其它监控系统使用的 push 数据的方式,prometheus 使用的是 pull 的方式,其架构如图7所示:

[图7] prometheus架构图

如上图所示,prometheus 包含的主要组件如下:

  • Prometheus Server 主要负责数据采集和存储,提供 PromQL 查询语言的支持。Server 通过配置文件、文本文件、Zookeeper、Consul、DNS SRV Lookup等方式指定抓取目标。根据这些目标会,Server 定时去抓取 metric s数据,每个抓取目标需要暴露一个 http 服务的接口给它定时抓取。
  • 客户端SDK:官方提供的客户端类库有 go、java、scala、python、ruby,其他还有很多第三方开发的类库,支持 nodejs、php、erlang 等。
  • Push Gateway 支持临时性 Job 主动推送指标的中间网关。
  • Exporter Exporter 是Prometheus的一类数据采集组件的总称。它负责从目标处搜集数据,并将其转化为 Prometheus 支持的格式。与传统的数据采集组件不同的是,它并不向中央服务器发送数据,而是等待中央服务器主动前来抓取。Prometheus提供多种类型的 Exporter 用于采集各种不同服务的运行状态。目前支持的有数据库、硬件、消息中间件、存储系统、HTTP服务器、JMX等。
  • alertmanager:是一个单独的服务,可以支持 Prometheus 的查询语句,提供十分灵活的报警方式。
  • Prometheus HTTP API的查询方式,自定义所需要的输出。
  • Grafana 是一套开源的分析监视平台,支持 Graphite, InfluxDB, OpenTSDB, Prometheus, Elasticsearch, CloudWatch 等数据源,其 UI 非常漂亮且高度定制化。

创业公司选择 Prometheus + Grafana 的方案,再加上统一的服务框架(如 gRPC ),可以满足大部分中小团队的监控需求。

14、配置系统

随着程序功能的日益复杂,程序的配置日益增多:各种功能的开关、降级开关,灰度开关,参数的配置、服务器的地址、数据库配置等等,除此之外,对后台程序配置的要求也越来越高:配置修改后实时生效,灰度发布,分环境、分用户,分集群管理配置,完善的权限、审核机制等等,在这样的大环境下,传统的通过配置文件、数据库等方式已经越来越无法满足开发人员对配置管理的需求,业界有如下两种方案:

  • 基于 zk 和 etcd,支持界面和 api ,用数据库来保存版本历史,预案,走审核流程,最后下发到 zk 或 etcd 这种有推送能力的存储里(服务注册本身也是用 zk 或 etcd,选型就一块了)。客户端都直接和 zk 或 etcd 打交道。至于灰度发布,各家不同,有一种实现是同时发布一个需要灰度的 IP 列表,客户端监听到配置节点变化时,对比一下自己是否属于该列表。PHP 这种无状态的语言和其他 zk/etcd 不支持的语言,只好自己在客户端的机器上起一个 Agent 来监听变化,再写到配置文件或共享内存,如 360 的 Qconf。
  • 基于运维自动化的配置文件的推送,审核流程,配置数据管理和方案一类似,下发时生成配置文件,基于运维自动化工具如Puppet,Ansible 推送到每个客户端,而应用则定时重新读取这个外部的配置文件,灰度发布在下发配置时指定IP列表。

创业公司前期不需要这种复杂,直接上 zk,弄一个界面管理 zk 的内容,记录一下所有人的操作日志,程序直连 zk,或者或者用Qconf 等基于 zk 优化后的方案。

15、发布系统/部署系统

从软件生产的层面看,代码到最终服务的典型流程如图8所示:

[图8 流程图]

从上图中可以看出,从开发人员写下代码到服务最终用户是一个漫长过程,整体可以分成三个阶段:

  • 从代码(Code)到成品库(Artifact)这个阶段主要对开发人员的代码做持续构建并把构建产生的制品集中管理,是为部署系统准备输入内容的阶段。
  • 从制品到可运行服务 这个阶段主要完成制品部署到指定环境,是部署系统的最基本工作内容。
  • 从开发环境到最终生产环境 这个阶段主要完成一次变更在不同环境的迁移,是部署系统上线最终服务的核心能力。

发布系统集成了制品管理,发布流程,权限控制,线上环境版本变更,灰度发布,线上服务回滚等几方面的内容,是开发人员工作结晶最终呈现的重要通道。开源的项目中没有完全满足的项目,如果只是 Web 类项目,Walle、Piplin 都是可用的,但是功能不太满足,创业初期可以集成 Jenkins + Gitlab + Walle (可以考虑两天时间完善一下),以上方案基本包括 制品管理,发布流程,权限控制,线上环境版本变更,灰度发布(需要自己实现),线上服务回滚等功能。

16、跳板机

跳板机面对的是需求是要有一种能满足角色管理与授权审批、信息资源访问控制、操作记录和审计、系统变更和维护控制要求,并生成一些统计报表配合管理规范来不断提升IT内控的合规性,能对运维人员操作行为的进行控制和审计,对误操作、违规操作导致的操作事故,快速定位原因和责任人。其功能模块一般包括:帐户管理、认证管理、授权管理、审计管理等等

开源项目中,Jumpserver 能够实现跳板机常见需求,如授权、用户管理、服务器基本信息记录等,同时又可批量执行脚本等功能;其中录像回放、命令搜索、实时监控等特点,又能帮助运维人员回溯操作历史,方便查找操作痕迹,便于管理其他人员对服务器的操作控制。

17、机器管理

机器管理的工具选择的考量可以包含以下三个方面:

  1. 是否简单,是否需要每台机器部署agent(客户端)
  2. 语言的选择(puppet/chef vsansible/saltstack)开源技术,不看官网不足以熟练,不懂源码不足以精通;Puppet、Chef基于Ruby开发,ansible、saltstack基于python开发的
  3. 速度的选择(ansiblevssaltstack) ansible基于SSH协议传输数据,Saltstack使用消息队列zeroMQ传输数据;大规模并发的能力对于几十台-200台规模的兄弟来讲,ansible的性能也可接受,如果一次操作上千台,用salt好一些。

如图9所示:

[图9 机器管理软件对比]

一般创业公司选择 Ansible 能解决大部问题,其简单,不需要安装额外的客户端,可以从命令行来运行,不需要使用配置文件。至于比较复杂的任务,Ansible 配置通过名为 Playbook 的配置文件中的 YAML 语法来加以处理。Playbook 还可以使用模板来扩展其功能。

二、创业公司的选择

1、选择合适的语言

  • 选择团队熟悉的/能掌控的,创业公司人少事多,无太多冗余让研发团队熟悉新的语言,能快速上手,能快速出活,出了问题能快速解决的问题的语言才是好的选择。
  • 选择更现代一些的,这里的现代是指语言本身已经完成一些之前需要特殊处理的特性,比如内存管理,线程等等。
  • 选择开源轮子多的或者社区活跃度高的,这个原则是为了保证在开发过程中减少投入,有稳定可靠的轮子可以使用,遇到问题可以在网上快速搜索到答案。
  • 选择好招人的 一门合适的语言会让创业团队减少招聘的成本,快速招到合适的人。
  • 选择能让人有兴趣的 与上面一点相关,让人感兴趣,在后面留人时有用。

2、选择合适的组件和云服务商

  • 选择靠谱的云服务商;
  • 选择云服务商的组件;
  • 选择成熟的开源组件,而不是最新出的组件;
  • 选择采用在一线互联网公司落地并且开源的,且在社区内形成良好口碑的产品;
  • 开源社区活跃度;

选择靠谱的云服务商,其实这是一个伪命题,因为哪个服务商都不靠谱,他们所承诺的那些可用性问题基本上都会在你的身上发生,这里我们还是需要自己做一些工作,比如多服务商备份,如用CDN,你一定不要只选一家,至少选两家,一个是灾备,保持后台切换的能力,另一个是多点覆盖,不同的服务商在CDN节点上的资源是不一样的。

选择了云服务商以后,就会有很多的产品你可以选择了,比较存储,队列这些都会有现成的产品,这个时候就纠结了,是用呢?还是自己在云主机上搭呢?在这里我的建议是前期先用云服务商的,大了后再自己搞,这样会少掉很多运维的事情,但是这里要多了解一下云服务商的组件特性以及一些坑,比如他们内网会经常断开,他们升级也会闪断,所以在业务侧要做好容错和规避。

关于开源组件,尽可能选择成熟的,成熟的组件经历了时间的考验,基本不会出大的问题,并且有成套的配套工具,出了问题在网上也可以很快的找到答案,你所遇到的坑基本上都有人踩过了。

3、制定流程和规范

  • 制定开发的规范,代码及代码分支管理规范,关键性代码仅少数人有权限;
  • 制定发布流程规范,从发布系统落地;
  • 制定运维规范;
  • 制定数据库操作规范,收拢数据库操作权限;
  • 制定告警处理流程,做到告警有人看有人处理;
  • 制定汇报机制,晨会/周报;

4、自研和选型合适的辅助系统

所有的流程和规范都需要用系统来固化,否则就是空中楼阁,如何选择这些系统呢?参照上个章节咱们那些开源的,对比一下选择的语言,组件之类的,选择一个最合适的即可。

比如项目管理的,看下自己是什么类型的公司,开发的节奏是怎样的,瀑布,敏捷的 按项目划分,还是按客户划分等等,平时是按项目组织还是按任务组织等等

比如日志系统,之前是打的文本,那么上一个elk,规范化一些日志组件,基本上很长一段时间内不用考虑日志系统的问题,最多拆分一下或者扩容一下。等到组织大了,自己搞一个日志系统。

比如代码管理,项目管理系统这些都放内网,安全,在互联网公司来说,属于命脉了,命脉的东西还是放在别人拿不到或很难拿到的地方会比较靠谱一些。

5、选择过程中需要思考的问题

技术栈的选择有点像做出了某种承诺,在一定的时间内这种承诺没法改变,于是我们需要在选择的时候有一些思考。

看前面内容,有一个词出现了三次,合适,选择是合适的,不是最好,也不是最新,是最合适,适合是针对当下,这种选择是最合适的吗?比如用 Go 这条线的东西,技术比较新,业界组件储备够吗?组织内的人员储备够吗?学习成本多少?写出来的东西能满足业务性能要求吗?能满足时间要求吗?

向未来看一眼,在一年到三年内,我们需要做出改变吗?技术栈要做根本性的改变吗?如果组织发展很快,在 200 人,500 人时,现有的技术栈是否需要大动?

创业过程中需要考虑成本,这里的成本不仅仅是花费多少钱,付出多少工资,有时更重要的是时间成本,很多业务在创业时大家拼的就是时间,就是一个时间窗,过了就没你什么事儿了。

三、基于云的创业公司后台技术架构

结合上面内容的考量,在对一个个系统和组件的做选型之后,以云服务为基础,一个创业公司的后台技术架构如图10所示:

[图10 后台技术架构]

参考资料

http://database.51cto.com/art/201109/291781.htm

https://zh.wikipedia.org/wiki/Kafka

https://prometheus.io/docs/introduction/overview/

http://deadline.top/2016/11/23/配置中心那点事/

http://blog.fit2cloud.com/2016/01/26/deployment-system.html

from:http://www.phppan.com/2018/04/svr-stack/

HTTP协议冷知识大全

如果不用HTTPS,HTTP协议如何安全的传输密码信息?

HTTP协议是纯文本协议,没有任何加密措施。通过HTTP协议传输的数据都可以在网络上被完全监听。如果用户登陆时将用户名和密码直接明文通过HTTP协议传输过去了,那么密码可能会被黑客窃取。
一种方法是使用非对称加密。GET登陆页面时,将公钥以Javascript变量的形式暴露给浏览器。然后用公钥对用户的密码加密后,再将密码密文、用户名和公钥一起发送给服务器。服务器会提前存储公钥和私钥的映射信息,通过客户端发过来的公钥就可以查出对应的私钥,然后对密码密文进行解密就可以还原出密码的明文。
为了加强公钥私钥的安全性,服务器应该动态生成公钥私钥对,并且使用后立即销毁。但是动态生成又是非常耗费计算资源的,所以一般服务器会选择Pool方法提供有限数量的公钥私钥对池,然后每隔一段时间刷新一次Pool。

640文件路径攻击

很多操作系统都会使用..符号表示上层目录。如果黑客在URL的路径里面使用..符号引用上层目录,而服务器没有做好防范的话就有可能导致黑客可以直接访问权限之外的文件。比如使用多级..符号就可以引用到根目录,进一步就可以访问任意文件。
所以很多服务器都禁止在URL路径里出现..符号以避免被攻击。
文件路径攻击也是很多黑客非常喜爱使用的攻击方法之一。如果你的服务器有一定的访问量,打开你的nginx日志,你就会偶尔发现有一些奇怪的URL里面有一堆..符号,这种URL的出现就表示网络上的黑客正在尝试攻击你的服务器。

DNS欺骗

HTTP协议严重依赖于DNS域名解析。任意一个域名类网址的访问都需要经过域名解析的过程得到目标服务的IP地址才能成功继续下去。
如果掌管DNS服务的运营商作恶将域名解析到不正确的IP,指向一个钓鱼的网页服务。用户如果没有觉察,就可能会将自己的敏感信息提交给冒牌的服务器。

642谨慎使用外部的HTTP代理

HTTP代理作为客户端到服务器之间的中间路由节点,它起到传话人和翻译官的角色。
如果这个翻译官不靠谱的话,客户端是会拿到错误的返回数据的。它同DNS欺骗一样,是可以对客户端进行钓鱼攻击的。
如果这个翻译官口风不严的话,它可能会将它听到的敏感信息泄露给别人

643413 Request Entity Too Large

客户端上传图片太大超过服务器限制时,服务器返回413错误。

414 Request-URI Too Long

客户端访问的URI太长,超出了服务器允许限制,服务器返回414错误。

202 Accepted

常用于异步请求。客户端发送请求到服务器,服务器立即返回一个202 Accepted表示已经成功接收到客户端的请求。
后面怎么处理由服务器自己决定,一般服务器会给客户端预留一个可以查询处理状态的接口,客户端可以选择轮训该接口来知道请求的处理进度和结果。

POST提交数据的方式

application/x-www-form-urlencoded

提交数据表单时经常使用,Body内部存放的是转码后的键值对。

application/json

提交结构化表单时使用,Body内部存放的是JSON字符串。ElasticSearch的查询协议使用的是这种方式。

multipart/form-data

上传文件时经常使用。这种格式比较复杂,它是为了支持多文件上传混合表单数据而设计的一种特殊的格式。

用户填充了表单设置了待上传的文件,点击Submit,传输数据大致如下

Cookie

浏览器请求的Cookie中往往会携带敏感信息。服务器一般会将当前用户的会话ID存在cookie里,会话的具体内容存在服务器端,会话的内容很敏感。

浏览器请求时会携带Cookie信息,服务器根据Cookie信息中的会话ID找到对应的会话内容。会话内容里可能存储了用户的权限信息,拿到这部分权限信息后就可能随意控制修改用户的数据。

644因为HTTP协议的不安全性,请求数据包很容易被窃听,Cookie中的会话信息很容易被盗。解决方案之一就是在会话中记录用户的终端信息和IP地址信息,如果这些信息突然发生改变,需要强制用户重新认证。

不过高级的黑客是可以伪造出和用户真实请求一摸一样的数据包的。最彻底的解决方案还是采用HTTPS协议。

普通的Cookie信息可以通过Javascript脚本获取到。如果黑客通过某种方式在网页中植入不安全的脚本,将用户的Cookie拿到然后发送到远程的第三方服务器中,那么Cookie中的信息就被泄露了。

Cookie的两个重要属性

被标记为Secure的Cookie信息在HTTP请求中不会被传送,它只会在HTTPS请求中传送,避免数据被泄露。

被标记为HttpOnly的Cookie信息是无法通过Javascript API获取到的,它只会在请求中传送。这样可以避免黑客通过网页脚本方式窃取Cookie中的敏感信息。

Cookie(甜点)如此好吃,黑客们总想通过Cookie做各种文章。

645CSRF(Cross-Site Request Forgery)

CSRF跨站请求伪造有很多别名,比如One-Click Attack(一键攻击),比如Session Riding(搭便车攻击)

假设在在一个社区博客网站中,删除个人的文章只需要一个URL就可以,Cookie中的会话权限信息会自动附加到请求上。

那么当别人伪造了一个上面的链接地址诱惑你去点击,比如通过站内信件、私聊、博客评论、图片链接或者在别的什么网站上随机制造的一个链接。你不经意点了一下,就丢了你的文章。所以它被称为一键攻击。因为这是借用了你当前登陆的会话信息来搞事,所以也被称为搭便车攻击。

如果在一个金融系统中,转账要是也可以通过一个简单的URL进行的话,那这种危险就非同小可。

646这就要求修改性的操作务必不得使用简单的GET请求进行处理。但是即使这种情况下你改成了POST请求,黑客依然有办法伪造请求,那就是通过iframe。

黑客在别的什么网站上伪造了一个POST表单,诱惑你去submit。如果只是普通的内嵌进HTML网页的表单,用户提交时会出现跨域问题。因为当前网站的域名和表单提交的目标域名不一致。但是如果通过iframe来内嵌表单,则可以绕过跨域的问题,而用户却完全没有任何觉察。

为了防范CSRF攻击,聪明的网站的POST表单里都会带上CSRF_TOKEN这个隐藏字段。CSRF_TOKEN是根据用户的会话信息生成的。当表单提交时,会将token和用户的会话信息做比对。如果匹配就是有效的提交请求。

黑客必须拿到CSRF_TOKEN才可以借用用户的会话信息实施CSRF攻击,但是CSRF_TOKEN又必须由用户的会话信息才可以生成。黑客没有用户的会话信息,从而无法实施CSRF攻击。

XSS(Cross Site Scripting)

如果黑客可以在你的网页中植入任意Javascript脚本,那他就可以随意鱼肉你的账户。通过Javascript可以获取Cookie的信息,可以借用你的会话去调用一些隐秘的API,而这一些行为都是在偷偷的进行,你根本完全不知道。

这类攻击在一些UGC网站中非常常见,常见的博客类网站就是UGC网站,用户可以通过编辑内容来生成网页。

黑客也是用户。他可以编辑一段Javascript脚本作为内容提交上去。如果服务器没有做好防范,这段脚本就会在生成的网页中运行起来。当其它用户在登陆的状态下来浏览这个网页的时候,就悲剧了。

防范XSS一般是通过对输出的内容进行内容替换做到的。在HTML页面中不同的位置会有不同的内容替换规则。
比较常见的是使用HTML entity编码将HTML标签之间的内容中的一些特殊的字符进行转码。

还有些UGC内容在HTML标签的属性中、Javascript的变量中、URL、css代码中,他们转码的规则并不一样,具体方法可以去Google相关文档。

跨域

跨域是个很头痛的问题。

当你有多个后端服务,但是只有一个前端的时候,你想做前后端分离,就会遇到跨域问题。你发现你的前端js调用后端服务时控制台告诉你不ok。然后只好把这些服务都挂在了同一个nginx域名下面,通过url前缀区分。

647这时候你会想,跨域太TM讨厌了。既然跨域这么讨厌,那为什么浏览器非要限制跨域呢?

还是安全原因。

让我们回到上文的搭便车攻击(Session Riding),也就是骑着别人的会话来搞事情。

假设现在你的浏览器开了一个站点A,登陆了进去,于是cookie便记录了会话id。
然后你又不小心开了另一个站点B,这个站点页面一打开就开始执行一些恶意代码。这些代码的逻辑是调用站点A的API来获取站点A的数据,因为可以骑着(Ride)站点A的会话cookie。而这些数据正好是用户私密性的。于是用户在站点A上的私有信息就被站点B上的代码窃走了。这就是跨域的风险。

但是有时候我们又希望共享数据给不同的站点,该怎么办呢?

答案是JSONP & CORS

JSONP(JSON Padding)

JSONP通过HTML的script标记实现了跨域共享数据的方式。JSON通过在网页里定义一个回调方法,然后在页面上插入一个动态script标签,指向目标调用地址。服务器会返回一段javascript代码,一般是some_callback(data)这种形式的回调。该段代码会在浏览器里自动执行,于是网页就得到了跨域服务器返回的数据。

因为JSONP是不携带cookie信息的,所以能有效避免搭便车攻击。JSONP是否可以获取到数据还需要服务器对这种调用提供显示支持,服务器必须将数据以javascript代码的形式返回才可以传递给浏览器。

CORS(Cross-Origin Resource Sharing)

JSONP的不足在于它只能发送GET请求,并且不能携带cookie。而CORS则可以发送任意类型的请求,可以选择性携带cookie。

CORS是通过Ajax发送的跨域请求技术。CORS的请求分为两种,一种是简单请求,一种是复杂请求。简单请求就是头部很少很简单的GET/HEAD/POST请求。复杂请求就是非简单请求。

浏览器发现Ajax的请求是跨域的,就会在请求头添加一个Origin参数,指明当前请求的发起站点来源。服务器根据Origin参数来决定是否授权。

如果是简单请求,Ajax直接请求服务器。服务器会当成普通的请求直接返回内容,不同的是还会在响应头部添加几个重要的头部,其中最重要的头部是Access-Control-Allow-Origin: http://example.com

浏览器如果在响应中没有读到这个头部,就会通知Ajax请求失败。虽然服务器返回了数据,浏览器也不让脚本读到数据,这就保证了跨域的安全。服务器就是通过请求的Origin参数来决定要不要响应Access-Control-Allow-Origin头部来决定是否允许指定网站的跨域请求。

如果是复杂请求,要走一个预检的流程。预检就是浏览器先向服务器发送一个Method为Options的请求,如果服务器允许跨域请求,浏览器再发起这个Ajax请求。所以CORS的复杂请求会比简单请求额外耗费一个TTL的时间。

CORS的细节请参见大神阮一峰的博文《跨域资源共享CORS详解》

from:https://mp.weixin.qq.com/s/aekcsgLG6jZw3LeF3R9ssQ

Amazon’s AWS

原文链接:A Beginner’s Guide To Scaling To 11 Million+ Users On Amazon’s AWS

译者:杰微刊–汪建

 

一个系统从一个用户到多于1100万用户访问,你将如何对你的系统进行扩展?Amazon的web服务解决方案架构师乔尔?威廉姆斯就此话题给出了一个精彩的演讲:2015扩展你的第一个一千万用户。
如果你是一个拥有较丰富的AWS使用经验的用户,这个演讲将不太适合你,但如果你作为一个刚接触云、刚接触AWS的新用户,或者你还没有跟上Amazon源源不断对外发布的AWS新特性,它将是一个很好的入门资料。
正如大家所期望的,这个演讲讨论Amazon服务如何针对问题提出先进且主流的解决方案,Amazon平台总是令人印象深刻且拥有指导性。对于如何把所有产品组合在一起Amazon做了大量工作去提取出用户需要的是什么,并且确保Amazon对于每个用户的需求都拥有一个产品能满足这部分的需求。
演讲的一些有趣的要点:

1、一般刚开始时使用SQL而在必要时刻转向NoSQL。
2、一致的观点是通过引入组件去解耦系统,使用组件便于扩展并且组件故障不会影响到其他模块。组件便于使系统分层和构建微服务。
3、只把区别于已有任务的部分作为你的业务逻辑,不要重复发明轮子。
4、可伸缩性和冗余性不是两个互相独立的概念,你经常要将两个概念同时放在一起考虑。
5、没有提及成本,成为AWS解决方案被批评的一个主要方面。

 

基本情况
AWS覆盖全世界12个国家区域

1. 每个区域都对应着世界上的一个物理位置,每个位置都有弹性计算云提供多个可用区域(Availability Zones),这些区域包含北美、南美、欧洲、中东、非洲、亚太等地区。
2. 每个可用区域(AZ)实质上是单个数据中心,尽管它可由多个数据中心构造。
3. 每个可用区域都拥有很强的隔离性,他们各自拥有独立的电源和网络。
4. 可用区域之间只能通过低延迟网络互相连接,它们可以相距5或15英里,但网络的速度相当快以至于你的应用程序像在同一个数据中心。
5. 每个区域至少有2个可用区域,可用区域总共有32个。
6. 借助若干可用区域即可构建一个高可用的架构供你的应用使用。
7. 在即将到来的2016年将会增加至少9个可用区域和4个区域。

 

AWS在世界上拥有53个边缘位置
1. 这些边缘位置被用于Amazon的内容分发网络CDN、Route53、CloudFront以及Amazon的DNS管理服务器。
2. 边缘位置使用户可以在世界的任何角落低延迟地访问网页。
构建块服务
1. AWS已经使用多个可用区域构架了大量服务供使用,这些服务内部拥有高可用性和容错性。以下是可供使用的服务列表。
2. 你可以在你的应用中直接使用这些服务,它们是收费的,但使用它们你可以不必自己考虑高可用性。
3. 每个可用区域都提供很多服务,包括CloudFront, Route 53, S3, DynamoDB, 弹性负载均衡, EFS, Lambda, SQS, SNS, SES, SWF。
4. 即使这些服务只存在于一个单一的可用区域,通过使用这些服务任然可以构建一个高可用架构。
一个用户
在这种情况下,你是作为仅有的用户,你仅仅只想让web应用跑起来。
你的架构看起来像下面几点:

1. 运行在单独的实例上,可能是t2.micro型。实例类型包括了CPU、内存、存储和网络的不同组合,通过选择这些不同实例类型组成一个适合你的web应用的资源。
2. 在单独的实例上运行整个web栈,例如web应用程序、数据库以及各种管理系统等。
3. 使用Amazon的Route53作为DNS服务。
4. 在此实例上添加一个的弹性IP。
5. 在一段时间内运行的良好。
纵向扩展
1、你需要一个更大的容器放置你的应用,最简单的扩展方法是选择一个更大的实例类型,例如c4.8xlarge或者m3.2xlarge。
2、这种方法称为纵向扩展。
3、需要做的仅仅是选择一个新型实例取代原来的实例,应用跑起来即可以更加强大。
4、提供多种不同的硬件配置混搭选择,可以选择一个244G内存的系统(2TB的RAM即将到来),也可以选择40个CPU内核的系统,可以组成I/0密集型实例、CPU密集型实例以及高存储型实例。
5、Amazon的一些服务使用可配置的IOPS选项来保证性能,你可以使用小一点的实例去跑你的应用,对于需要扩展的服务独立使用Amazon的可扩展服务,例如DynamoDB。
6、纵向扩展有一个很大的问题:它不具备failover功能,同时也不具备冗余性。就像把所有鸡蛋都放在同一个篮子里,一旦实例发生故障你的web也会宕掉。
7、一个单独的实例最终能做到的也就这些,想要更加强大需要其他的措施。
10+用户
将单个主机分为多个主机
1. Web应用使用一台主机。
2. 数据库使用一台主机,你可以在上面跑任意数据库,只要负责数据库的管理。
3. 将主机分离成多个主机可以让web应用和数据库各自独立对自己进行扩展,例如在某种情况下可能你需要的数据库比web应用更大的规模。
或者你可以不自己搭建数据库转而使用Amazon的数据库服务
1. 你是一个DBA吗?你真的想要担心数据备份的问题吗?担心高可用?担心数据库补丁?担心操作系统?
2. 使用Amazon数据库服务有一大优势,你只要简单一点击即可完成多可用区域的数据库的安装,而且你不必担心这些可用区域之间的数据备份或其他类似的事情,这些数据库具备高可用性高可靠性。
正如你所想,Amazon有几种类型的完全托管数据库服务供出售:
1. Amazon RDS(Relational Database Service),可供选择的数据库类型相当多,包括Microsoft SQL Server, Oracle, MySQL, PostgreSQL, MariaDB, Amazon Aurora.
2. Amazon DynamoDB,一个NoSQL数据库。
3. Amazon Redshift,一个PB级的数据仓库系统。
更多Amazon 特性
1. 拥有自动扩展存储到64TB的能力,你不再需要限定你的数据存储。
2. 多大15个读副本。
3. 持续增量备份到S3。
4. 多达6路备份到3个可用区域,有助于处理故障。
5. MySQL兼容。
用SQL数据库取代NoSQL数据库
1. 建议使用SQL数据库。
2. SQL数据库相关技术完善。
3. 存在大量开源源码、社区、支持团队、书籍和工具。
4. 千万用户级别系统的还不足以拖垮SQL数据库,除非你的数据非常巨大。
5. 具有清晰的扩展模式。
什么时候你才需要使用NoSQL数据库
1. 如果你一年需要存储超过5TB的数据,或者你有一个令人难以置信的数据密集任务。
2. 你的应用具有超低延迟需求。
3. 你的应用需要一个非常高的吞吐量,需要在数据的读写I/O上进行优化。
4. 你的应用没有任何关系型数据。
100+用户
在web层进行主机分离。
使用Amazon RDS存储数据,它把数据库的所有工作都揽下了。
上面两点做好即可。

 

1000+用户
现在你构建的应用存在可用性问题,你的web应用将会宕掉如果你web服务的主机宕掉了。
你需要在另外一个可用区域上搭建另外一个web实例,由于可用区域之间拥有毫秒级别的低延迟,他们看起来就像互相挨着。
同样,你需要在另外一个可用区域上搭建一个RDS数据库slave,组成主备数据库,一旦主数据库发生故障你的web应用将会自动切换到slave备数据库。由于你的应用总是使用相同的端,failover不会带给应用任何改变。
在两个可用区域中分布着两个web主机实例,使用弹性负载均衡器(ELB)将用户访问分流到两个web主机实例。
弹性负载均衡器(ELB)
1. ELB是一个高可用的负载均衡器,它存在于所有的可用区域中,对于你的应用来说它是一个DNS服务,只需要把他放到Route53即可,它就会在你的web主机实例中进行负载分发。
2. ELB有健康检查机制,这个机制保证流量不会分发到宕掉的主机上。
3. 不用采取任何措施即可完成扩展,当它发现额外流量时它将在后台通过横向和纵向扩展,随着你的应用不断扩展,它也会自动不断扩展,而且这些都是系统自动完成的,你不必对ELB做任何管理。
10000到100000用户
前面例子中说到ELB后面挂载两个web主机实例,而实际上你可以在ELB后面挂载上千个主机实例,这就叫横向扩展。
添加更多的读副本到数据库中,或者添加到RDS中,但需要保持副本的同步。
通过转移一些流量到web层服务器减轻web应用的压力,例如从你的web应用中将静态内容抽离出来放到Amazon S3和Amazon CloudFront上,CloudFront是Amazon的CDN,它会将你的静态内容保存在全世界的53个边缘地区,通过这些措施提高性能和效率。
Amazon S3是一个对象仓库。
1. 它不像EBS,它不是搭载在EC2实例上的存储设备,它是一个对象存储而不是块存储。
2. 对于静态内容如JavaScript、css、图片、视频等存放在Amazon S3上再合适不过,这些内容没必要放到EC2实例上。
3. 高耐用性,11个9的可靠性。
4. 无限制的可扩展,只要你想可以往里面扔尽可能多的数据,用户在S3上存储了PB级别的数据。
5. 支持最大5TB的对象存储。
6. 支持加密,你可以使用Amazon的加密,或者你自己的加密,又或者第三方的加密服务。
Amazon CloudFront对你的内容提供缓存
1. 它将内容缓存在边缘地区以便供你的用户低延迟访问。
2. 如果没有CDN,将导致你的用户更高延迟地访问你的内容,你的服务器也会因为处理web层的请求而处于更高的负载。
3. 例如有个客户需要应对60Gbps的访问流量,CloudFront将一切都处理了,web层甚至都不知道有这么大的访问流量存在。
你还可以通过转移session状态减轻你的web层的负载
1. 将session状态保存到ElastiCache或DynamoDB。
2. 这个方法也让你的系统在未来可以自动扩展。
你也可以将数据库的一些数据缓存在ElastiCache减轻应用负载
数据库没有必要处理所有获取数据的请求,缓存服务可以处理这些请求从而让宝贵的数据库资源处理更加重要的操作。
Amazon DynamoDB——全托管的NoSQL数据库
1. 根据你自己想要的吞吐量,定制你想要的读写性能。
2. 支持高性能。
3. 具备分布式和容错性,它部署在多个可用区域中。
4. 它以kv结构存储,且支持JSON格式。
5. 支持最大400k大的文件。
Amazon Elasticache ——全托管的Memcached或Redis
1. 维护管理一个memcached集群并不会让你赚更多的钱,所以让Amazon来做。
2. Elasticache集群会自动帮你扩展,它是一个具备自我修复特性的基础设施,如果某些节点宕掉了其它的新节点即会自动启动。
你也可以转移动态内容到CloudFront减轻负载
众所周知CloudFront能处理静态内容,例如文件,但除此之外它还还能处理某些动态内容,这个话题不再进行深入的探讨,可以看看这个链接。
自动扩展
对于黑色星期五,假如你不用做任何扩展就足够处理这些峰值流量,那么你是在浪费钱。如果需求和计算能力相匹配自然是最好的,而这由自动扩展帮你实现,它会自动调整计算集群的大小。
作为用户,你可以决定集群的最小实例数和最大实例数,通过实例池中设置最小和最大实例数即可。
云监控是一种嵌入应用的管理服务
1. 云监控的事件触发扩展。
2. 你准备扩展CPU的数量吗?你准备优化延迟吗?准备扩展带宽吗?
3. 你也可以自定义一些指标到云监控上,如果你想要指定应用针对某些指标自动扩展,只需将这些指标放到云监控上,告诉根据云监控根据这些指标分别扩展哪些资源。
500000+用户
前面的配置可以自动扩展群组添加到web层,在两个可用区域里自动扩展群组,也可以在三个可用区域里扩展,在不同可用区域中的多实例模式不经可以确保可扩展性,同时也保证了可用性。
论题中的案例每个可用区域只有3个web层实例,其实它可以扩展成上千个实例,而你可以设置实例池中最小实例数为10最大实例数为1000。
ElastiCache用于承担数据库中热点数据的读负载。
DynamoDB用于Session数据的负载。
你需要增加监控、指标以及日志。
1. 主机级别指标,查看自动扩展的集群中的某一CPU参数,快速定位到问题的位置。
2. 整体级别指标,查看弹性负载均衡的指标判断整个实例集群的整体性能。
3. 日志分析,使用CloudWatch日志查看应用有什么问题,可以使用CloudTrail对这些日志进行分析管理。
4. 外部站点监控,使用第三方服务例如New Relic或Pingdom监控作为终端用户看到了什么情况。
你需要知道你的用户的反馈,他们是不是访问延迟很慢,他们在访问你的web应用时是不是出现了错误。
从你的系统结构中尽可能多地排出性能指标,这有助于自动扩展的决策,你可不希望你的系统CPU使用率才20%。
自动化运维
随着基础设施越来越大,它扩展到了上千个实例,我们有读副本,我们有水平横线扩展,对于这些我们需要一些自动化运维措施去对他们进行管理,我们可不希望对着每个实例一个一个单独地管理。
动化运维工具分为两个层级
1. DIY层,包括Amazon EC2和AWS CloudFormation。
2. 更高层次的服务,包括AWS Elastic Beanstalk和AWS OpsWorks。
AWS Elastic Beanstalk,为你的应用自动管理基础设施,很方便。
AWS OpsWorks,应用程序管理服务,用于部署和操作不同形态规模的应用程序,它还能做到持续集成。
AWS CloudFormation
1. 提供了最大的灵活性,它提供了你的应用栈的模板,它可以构建你的整个应用栈,或者仅仅是应用栈中的某个组件。
2. 如果你要更新你的应用栈你只要更新CloudFormation模板,它将更新你的整个应用。
3. 它拥有大量的控制,但缺乏便利性。
AWS CodeDeploy,部署你的程序到整个EC2实例集群
1. 可以部署一到上千个实例。
2. Code Deploy可以指向一个自动扩展配置。
3. 可连同Chef和Puppet一起使用。
解耦基础设施
使用SOA/微服务,从你的应用抽离出不同服务,就像前面你将web层与数据库层分离出来那样,再分别创建这些服务。
这些独立出来的服务就可以根据自己需要扩展,它给你系统的扩展带来了灵活性,同时也保证了高可用性。
SOA是Amazon搭建架构关键的组成部分。
松耦合解放了你
1. 你可以对某些服务单独地扩展和让它失效。
2. 如果一个工作节点从SQS拉取数据失败,没有没关系?没有,只要重启另外一个工作节点即可,所有操作都有可能发生故障,所以一定要搭建一个可以处理故障的架构,提供failover功能。
3. 将所有模块设置成黑盒。
4. 把交互设计成松耦合方式。
5. 优先考虑内置了冗余性和可扩展性的服务,而不是靠自己构建实现。
不要重复发明轮子
只需把你区别于已有任务的部分作为你的业务逻辑。
Amazon的很多服务本身具备容错能力,因为他们跨多个可用区域,例如:队列、邮件、转码、搜索、数据库、监控、性能指标采集、日志处理、计算等服务,没有必要自己搭建。
SQS:队列服务
1. Amazon提供的第一个服务。
2. 它是跨可用区域的所以拥有容错性。
3. 它具备可扩展性、安全性、简单性。
4. 队列可以帮助你的基础设施上的不同组件之间传递消息。
5. 以图片管理系统为例,图片收集系统和图片处理系统是两个不同的系统,他们各自都可以独立地扩展,他们之间具备松耦合特性,摄取照片然后扔进队列里面,图片处理系统可以拉取队列里面的图片再对其进行其他处理。
AWS Lambda,用于代码部署和服务管理。
1. 提供解耦你的应用程序的工具。
2. 在前面图片系统的例子中,Lambda可以响应S3的事件,就像S3中某文件被增加时Lambda相关函数会被自动触发去处理一些逻辑。
3. 已经在EC2上集成,供应用扩展。
百万级别用户
当用户数量达到百万级别时,这就要求前面提到的所有方案都要综合考虑。
1. 扩展多为可用区域。
2. 在所有层之间使用弹性负载均衡,不仅在web层使用,而且还要在应用层、数据层以及应用包含的其他所有层都必须使用弹性负载均衡。
3. 自动伸缩能力。
4. 面向服务的架构体系。
5. 巧妙使用S3和CloudFront部署一部分内容。
6. 在数据库前面引入缓存。
7. 将涉及状态的对象移除出Web层。
使用Amazon SES发送邮件。
使用CloudWatch监控。

 

千万级别用户
当我们的系统变得越来越大,我们会在数据层遇到一些问题,你可能会遇到竞争写主库的数据库问题,这也就意味着你最多只能发送这么多写流量到一台服务器上。
你如何解决此问题?
1. Federation,根据你的应用功能把数据库分成多个库。
2. Sharding,分表分片,使用多个服务器分片。
3. 把部分数据迁移到其他类型的数据库上,例如NoSQL、graph等。
Federation——根据应用功能切分成多个库
1. 例如,创建一个论坛数据库、一个用户数据库、一个产品数据库,你可能之前就是一个数据库包含这所有类型的数据,所以现在要将他们拆分开。
2. 按照功能分离出来的数据库可以各自独立进行扩展。
3. 缺点:不能做跨数据库查询。
Sharding——将数据分割到多主机上
1. 应用层变得更加复杂,扩展能力更强。
2. 例如,对于用户数据库,三分之一的用户被发送到一个分片上,三分之一发到另一个分片上,最后三分之一发到第三个分片。
将数据迁移到其他类型的数据库上
1. 考虑NoSQL数据库。
2. 如果你的数据不要求复杂的join操作,比如说排行榜,日志数据,临时数据,热表,元数据/查找表等等,满足这些情况可以考虑迁移到NoSQL数据库上。
3. 这意味着他们可以各自单独扩展。
11000000用户
扩展是一个迭代的过程,当你的系统变得越来越大,你总有更多的事情需要你解决。
调整你的应用架构。
更多的SOA特性和功能。
从多可用区域到多区域。
自定义解决方案去解决你的特定问题,当用户量到达十亿级别时自定义解决方案是必要的。
深入分析你的整个应用栈。
回顾
使用多可用区域的基础设施提升可靠性。
使用自带扩展能力的服务,比如ELB,S3,SQS,SNS,DynamoDB等等。
每一层级都建立冗余,可扩展性和冗余性不是两个分开单独的概念,经常需要同时考虑两者。
刚开始使用传统关系型数据库。
在你的基础设施的里面和外面都考虑缓冲数据。
在你的基础设施中使用自动化工具。
确保你的应用有良好的指标采样、系统监控、日志记录,确保收集你的用户访问你的应用过程中产生的问题。
将各个层分拆成独立的SOA服务,让这些服务能保持最大的独立性,可以各自进行扩展,及时发生故障也不波及其他。
一旦做了足够的准备及可使用自动扩展功能。
不重复发明轮子,尽量使用托管服务而不是自己构建,除非非要不可。
必要的情况下转向NoSQL数据库。

参考资料
On HackerNews / On Reddit

http://aws.amazon.com/documentation

http://aws.amazon.com/architecture

http://aws.amazon.com/start-ups

http://aws.amazon.com/free

From:http://www.jfh.com/jfperiodical/article/1242