《大型网站系统与Java中间件实践》读书笔记

分布式系统的基础知识

  • 阿姆达尔定律
  • 多线程交互模式
    • 互不通讯,没有交集,各自执行各自的任务和逻辑
    • 基于共享容器(如队列)协同的多线程模式->生产者-消费者->队列
    • 经过事件协同的多线程模式->如B线程须要等到某个状态或事件发生后才能继续工做,而这个状态改变或者事件产生和A线程相关
      • 避免死锁
  • 网络通讯基础知识
    • OSI、TCP/IP
    • 网络IO实现方式
      • BIO
      • NIO->Reactor模式
      • AIO->Proactor模式
负载均衡
  • 硬件负载均衡
  • LVS等软件的负载均衡
  • 名称服务
  • 规则服务器
  • Master-Worker
小结
1.一致性hash的算法思路(游戏服务器进程/线程设计思路)(扩容/缩容平滑)/环形消息队列disruptor
2.缓存服务器扩容或者缩容要尽可能平滑
3.消息中间件-MOM:Message-Oriented middleware,is software infrastructure focused on sending and receiving messages between distributed system.
4.两个常被说起的好处:异步和解耦.
5.CountDownLatch不能循环使用,而CyclicBarrier能够循环使用(从名字上看,【循环_cyclic】屏障)

大型网站及其架构演进过程

海量数据+高并发访问量+复杂的业务和系统
  • 数据库与应用分离
  • 应用服务器走向集群(引入负载均衡器)
session问题
  • Session Sticky
  • Session Replication
  • Session数据集中存储
  • Cookie Based
读写分离
  • 引入读库->搜索引擎(站内搜索功能)->Search Cluster
  • 专库专用->数据垂直拆分
  • 数据水平拆分
加快数据读取
  • 缓存->页面缓存->Cache Cluster
分布式存储系统
  • Distributed Storage
拆分应用
  • 走服务化的路
消息中间件

中间件

特定中间件是解决特定场景问题的组件,它可以让软件开发人员专一于本身应用的开发
  • 远程过程调用和对象访问中间件->解决分布式环境下应用的互相访问问题
  • 消息中间件:解决应用之间的消息传递、解耦、异步的问题
  • 数据访问中间件:解决应用访问数据库的共性问题的组件
构建Java中间件的基础知识
  • JVM/GC/内存堆布局
  • 并发
    • 线程池/synchronized/ReentrantLock/volatile(可见性与操做互斥是两件事情)/Atomics/wait、nofity、nofifyAll/CountDownLatch/
    • CyclicBarrier/Semaphore/Exchanger/Future、FutureTask/
    • 并发容器:CopyOnWrite、Concurrent
  • 动态代理
  • 反射
  • 网络通讯选择
    • BIO/NIO/AIO
    • MINA、Netty
    • 协议制定
中间件范畴
  • 应用的拆分
  • 服务的拆分
  • 数据的拆分
  • 应用的解耦

服务框架

运行期服务框架与应用和容器的关系
  • 直接或者间接依赖的jar致使应用里同一个jar包有不一样的版本->产生冲突
  • 将服务框架自身用到的类和应用用到的类都控制在User-Defined Class Loader级别->实现相互间隔离
  • web容器对于多个web应用的处理以及osgi对于不一样的bundle的处理都采用了相似的方法
服务调用者与服务提供者之间通信方式的选择
  • 调用者[服务框架]
    • -->服务注册查找中心
      • --->服务提供者[服务框架]
  • 并非每次调用远程服务前都经过服务注册查找中心来查找可用地址->而是把地址缓存在调用者本地->当有变化时主动从服务注册查找中心发起通知->告诉调用者可用的服务提供者列表的变化
    • 客户端拿到可用的服务提供者的地址列表后->如何为当次的调用进行选择就是路由要解决的问题->随机、轮训、权重(通常指动态权重)
引入基于接口、方法、参数的路由
  • 由于通常状况下,一个集群会提供多个服务,每一个服务又有多个方法.->须要细粒度的控制服务路由
  • 如服务提供者有两个服务-接口A和接口B->接口A中的某个方法1执行比较耗时->影响总体性能->排队
    • 隔离资源,使得快慢不一样、重要级别不一样的方法之间互不影响
服务调用端的流控处理
  • 控制到服务提供者的请求的流量
序列化与反序列化处理
  • 具体制定通讯协议时->版本号、可扩展(扩展性、向后兼容性)属性以及发起方支持能力的介绍(如是否支持压缩等)
网络通讯实现选择
  • 同步方式进行远程调用使用nio
  • io线程/数据队列/通讯对象队列/定时任务
    • 请求线程发送数据->进入数据队列->生成通讯对象(阻塞请求线程)->加入通讯对象队列->请求线程等待(通讯对象用于唤醒请求线程)
    • 数据队列->io线程->socket链接->进行数据收发(须要发送的数据进入数据队列后,这样请求线程就不须要直接和socket链接打交道->复用socket链接)
    • 若是远程调用超时前有执行结果返回->io线程会通知通讯对象->通讯对象结束请求线程等待->结果传送给请求线程
    • 定时任务用于负责检查通讯对象队列中的哪些通讯对象已经超时->而后这些通讯对象会通知请求线程已经超时
  • 支持多种异步服务调用方式
    • OneWay->只管发送请求而不关心结果的方式->只须要把发送的数据放入数据队列便可
    • CallBack->请求发送方发送请求后会继续执行本身的操做->等对方有响应时进行一个回调
      • 请求方设置回调->加入回调对象队列
      • 收到服务提供者的返回后->io线程会通知回调对象->执行回调方法
      • 对于超时->定时任务的方式->若是没有返回->也须要执行回调对象的方法->告知已超时没有结果
      • 若是不引入新的线程->那么回调的执行要么是io线程中要么是在定时任务的线程中
      • [建议用新的线程执行回调->不要由于回调自己的代码执行时间久等问题影响了io线程或者定时任务].
    • Future->请求线程经过future来获取通讯结果并直接控制超时->同上Future对象队列 io线程依然是从数据队列中获得数据再进行通讯->获得结果后会把它传给Future
    • 可靠异步->要保证异步请求可以在远程被执行->消息中间件来完成这个保证
    • 使用Future方式对远程服务调用的优化
      • 一个请求中调用多个远程服务的状况
      • 按照调用顺序把服务的请求发送给服务A,服务B,服务C->请求发送过去并不直接等待执行结果
      • 而是直到服务C的请求也发出去后再来统一等待服务A、服务B和服务C的执行结果->而后再接着进行本地的数据处理
      • 前提:所调用服务A、B、C之间并无相互的依赖关系
      • 即并行调用优化->由于Future方式的支持
    • 反序列化线程:通常是使用io线程,不过这样会影响io线程的工做效率;另外一种方式是把反序列化工做从io线程转移到其余线程去作
服务提供端的设计与实现
  • 服务端的工做
    • 本地服务的注册管理
    • 根据进来的请求定位服务并执行
  • IO线程通讯处理->反序列化的工做取决于具体实现,io线程或者工做线程中进行的方式都有
    • 获得反序列化的消息并定位服务后->调用服务通常在非io线程进行(工做线程)
  • 执行不一样服务的线程池隔离
    • 服务提供端,工做线程池不止一个,而是多个,定位到服务后,根据服务名称、方法、参数来肯定具体执行服务调用的是哪一个线程池.->这样,不一样线程池之间是隔离的->不会出现争夺线程资源的状况.
  • 整个服务框架的功能分为服务调用者和服务提供者两方面,此外像序列化、协议、通讯等是公用的功能.在具体实现上,是把这些功能都放在一块儿造成一个完成的服务框架->而不是分为服务调用者框架和服务提供者框架
  • 服务框架必须作到模块化且可配置->模块可替换->并留有必定的扩展点来扩展原有功能
服务升级
  • 接口不变->内部的服务实现有变化->比较简单,采用灰度发布的方式验证而后所有发布就能够了
  • 接口中增长方法->也比较简单,直接增长方法便可->须要使用新方法的调用者就使用新方法,原来的调用者继续使用原来的方法便可
  • 接口的某些方法修改调用的参数列表
    • 对使用原来方法的代码都进行修改->不太可行->由于要求咱们同时发布多个系统
    • 版本号解决->经常使用的方式->使用老方法的系统继续调用原来版本的服务,而须要使用新方法的系统则使用新版本的服务
    • 在设计方法上考虑参数的扩展性->可行,不太好->因参数列表可扩展通常就觉得是采用相似map的方式来传递参数->不直观,而且对参数的校验会比较复杂
实战中的优化
  • 服务的拆分
    • 要拆分的服务是须要为多方提供公共功能
  • 服务的粒度
    • 须要根据业务的实际状况来划分服务
  • 优雅和实用的平衡
    • 多调用一次就比以前多了一次网络->一些功能直接在服务调用者的机器上实现会更加合适、经济
    • 如服务调用者直接读缓存->大部分对数据的请求直接走一次缓存就能够,只有少部分没有命中缓存的数据读取须要走服务提供者->而后再到数据库进行读取并插入缓存
  • 分布式环境的请求合并
    • 分布式锁-->额外开销->另一个思路->在服务调用端不是把请求随机分发给服务提供者,而是根据必定的规则把一样的请求发送到同一个服务提供者上->减小复杂性.
服务治理
  • 服务信息、服务质量、服务容量、服务依赖、服务分布、服务统计、服务元数据、服务查询、服务报表、服务监视、服务上下线、服务路由、服务限流降级、服务归组、服务线程池管理、机房规则、服务受权、
  • ESB
    • Enterprise Service Bus:企业服务总线

数据访问层

数据库减压思路
  • 优化应用,看看是否有没必要要的压力给了数据库(应用优化)
  • 看看有没有办法能够下降对数据库的压力,例如引入缓存、加搜索引擎等
  • 把数据库的数据和访问分到多台数据库上,分开支持->核心思路和逻辑
    • 数据拆分->垂直拆分->水平拆分
单机变为多机后,事务如何处理
  • 分布式事务->XA/DTP-->AP(Application Program)->RM(Resource Manager)->TM(Transaction Manager)
    • 两阶段提交->Prepare->Commit
    • Prepare阶段有一个节点资源失败则Rollback
  • 大型网站一致性的基础理论-CAP/BASE
    • CAP:Consitency,一致性、Availability,可用行、Partition-Tolerance,分区容忍性
    • 分布式系统中不能同时知足上面三项,CA、AP、CP
    • 分布式系统中,咱们通常选择增强可用性和分区容忍性而牺牲一致性->首先先知足A和P,而后看如何解决C的问题.
    • BASE模型
      • Basically Available,基本可用,容许分区失败
      • Soft state,软状态,接受一段时间的状态不一样步
      • Eventually consistent,最终一致,保证最终数据的状态是一致的
      • 对于C,咱们采用的方式和策略就是保持最终一致,也就是不保证数据发生变化后全部节点马上一致,可是保证他们最终是一致的。在大型网站中,为了更好的保持扩展性和可用性,通常都不会选择强一致性,而是采用最终一致的策略来实现.
  • 比两阶段提交更轻量一些的Paxos协议
    • 核心原则:少数服从多数
  • 集群内数据一致性的算法实例
    • Quorum、Vector Clock算法
  • 从工程上说,若是可以避免分布式事务的引入,那么仍是避免为好;若是必定要引入分布式事务,那么能够考虑最终一致的方法,而不是追求强一致。并且从实现上来讲,咱们是经过补偿的机制不断重试,让以前由于异常而没有进行到底的操做继续进行而不是回滚。若是仍是不能知足需求,那么基于Paxos的算法会是一个不错的选择.
多机的Sequence问题与处理
  • 惟一性
  • 连续性
    • 提供一个实现方案:把全部id集中放到一个地方进行管理,每台机器使用时都从这个id生成器上取
跨库查询
数据访问层的设计与实现
  • 对外提供数据访问层的方式
    • 为用户提供专有API->不推荐->没有通用性
    • 通用方式->jdbc->数据层自身能够做为一个jdbc的实现,即暴露出jdbc的接口给应用
    • 基于orm或者类orm接口的方式
  • 按照数据层流程的顺序看数据层设计
    • SQL解析
      • 经过SQL解析能够获得SQL中的关键信息,如代表、字段、where条件等;而在数据层中,一个很重要的事情是根据执行的SQL获得被操做的表,根据参数及规则来肯定目标数据库链接
    • 规则处理阶段
      • 用固定哈希算法做为规则_分库分表
    • 一致性 hash
      • [把节点对应的哈希值变为了一个范围],而再也不是离散的.在一致性哈希中,咱们会把整个哈希值的范围定义的很是大,而后把这个范围分配给现有的节点
      • 虚拟节点对一致性hash的改进
        • 解决增长或减小节点时负载不均衡的问题
    • 映射表与规则自定义方式
      • 经过比较复杂的函数计算来解决数据访问的规则问题
    • 为何要改写SQL
      • 多库多表->修改表名->跨库计算平均值等
    • 如何选择数据源
      • Master/Slave
      • 根据当前SQL的特色(读、写)、是否在事务中以及各个库的权重规则,计算获得此次SQL请求要访问的数据库
    • 执行SQL和结果处理阶段
      • 对异常的处理和判断
  • 实战
    • 复杂的链接管理
    • 三层数据源的支持和选择
      • DataSouce/AtomDataSource/groupDataSource
    • 独立部署的数据访问层实现方式
      • jar包方式
      • proxy方式
        • 数据库协议
        • 私有协议
    • 读写分离的挑战和应对
      • 数据结果相同,多从库对应一主库的场景
        • 应用经过数据层访问数据库,经过消息系统就数据库的更新送出消息通知->数据库同步服务器得到消息通知后会进行数据的复制工做.分库规则配置则负责在读数据及数据同步服务器更新分库时让数据层知道分库规则->数据同步服务器和DB主库的交互主要是根据被修改或新增的数据主键来获取内容,采用的是行复制的形式
        • 比较优雅的方式是基于数据库的日志来进行数据的复制
      • 主/备库分库方式不一样的数据复制
        • 非对称复制->控制数据分发->如主库按照买家id分库,而备库按照卖家id进行分库
      • 引入数据变动平台
        • 不少其余场景也会关心数据的变动,除了复制到其余数据库,例如缓存的失效等->能够考虑构建一个通用的平台来管理和控制数据变动->
        • 引入Extractor和Applier->Extractor负责把数据源变动的信息加入到数据分发平台中,而Applier的做用是把这些变动应用到相应的目标上->中间的数据分发平台中是由多个管道组成->进入到数据分发平台的变动信息就是标准化、结构化的数据了-
        • 如何作到数据平滑迁移
          • 最大挑战是,在迁移的过程当中又会有数据的变化(由于不少应用不能接受长时间的停机)->能够考虑的方案是在开始进行数据迁移时记录增量的日志,在迁移结束后,再对增量的变化进行处理.->在最后,能够要把要被迁移的数据的写暂停,保证增量日志都处理完毕后,再切换规则,放开全部的写,完成迁移工做

消息中间件

消息中间件对应用的解耦
  • 如登录系统负责向消息中间件发送消息,而其余的系统则向消息中间件来订阅这个消息,而后完成本身的工做.
  • 经过消息中间件解耦,登录系统就不用关心到底有多少个系统须要知晓登录成功这件事了,而不用关心如何通知它们,只须要把登录成功这件事转化为一个消息发送到消息中间件就能够了
  • landon:和事件解耦同样,如游戏中玩家升级抛出一个事件,其余子系统只须要监听该事件便可,而没必要升级直接调用各个子系统
  • 登录成功时须要向消息中间件发送一个消息,那么[必须保证这个消息发送到了消息中间件],不然依赖这个消息的系统就没法工做了
互联网时代的消息中间件
  • JMS:Java Message Service->规范->Hornetq,ActiveMQ等产品是这个规范的实现
  • 如何解决消息发送一致性
    • 消息发送一致性的定义:产生消息的业务动做与消息发送的一致,即若是业务操做成功了,那么由这个操做产生的消息必定要发送出去,不然就丢失消息了;而另外一方面,若是这个业务行为没有发生或者失败,那么就不该该把消息发出去.
    • JMS消息模型-Queue/Topic_支持XA协议(两阶段提交)->会引入分布式事务->存在一些限制且成本相对较高
    • 一致性方案的正向流程
      • (1) 业务处理应用首先把消息发给消息中间件,标记消息的状态为待处理.
      • (2) 消息中间件收到消息后,把消息存储在消息存储中,并不投递该消息.
      • (3)消息中间件返回消息处理的结果,仅是入库的结果,结果是成功或者失败.
      • (4)业务方收到消息中间件返回的结果并进行处理:
        • a) 若是收到的结果是失败,那么就放弃业务处理,结束
        • b) 若是收到的结果是成功,则进行业务自身的操做
      • (5)业务操做完成,把业务操做的结果发送给消息中间件
      • (6)消息中间件收到业务操做结果,根据结果进行处理
        • a) 若是业务失败,则删除消息存储中的消息,结束
        • b)若是业务成功,则更新消息存储中的消息状态为可发送,而且进行调度,进行消息的投递
    • 须要注意各类步骤中可能出现的异常状况
    • 最终一致性方案的补偿流程:
      • (1)消息中间件询问状态为待处理的消息对应业务操做结果
      • (2)应用即消息发布者对业务操做检查操做结果
      • (3)发送业务处理结果给消息中间件
      • 4)消息中间件更新消息状态,业务成功,消息状态为待发送;业务失败则消息删除
  • 如何解决消息中间件与使用者的强依赖问题
    • 把消息中间件所须要的消息表与业务数据表放到同一个业务数据库->业务操做和写入消息做为一个本地事务完成,而后再通知消息中间件有消息能够发送->解决一致性->也能够消息中间件定时去轮询业务数据库找到须要发送的消息,取出内容后进行发送
    • 须要业务本身的数据库承载消息数据/须要让消息中间件去访问业务数据库/须要业务操做的对象是一个数据库
    • 消息中间件再也不直接与业务数据库打交道->将业务操做、写入消息,轮询消息等所有放到业务应用
    • 加一个本地磁盘做为一个消息存储
  • 消息模型对消息接收的影响
    • JMS Queue模型:
      • 应用1和应用2发送消息到JMS服务器,这些消息根据到达的顺序造成一个队列->应用3和应用4进行消息的消费;若是Queue里面的消息被一个应用处理了,那么链接到JMS Queue上的另外一个应用是收不到这个消息的->即链接到这个JMS Queue上的应用共同消费了全部的消息->消息从发送端发送出来时不能肯定最终会被哪一个应用消费,可是能够明确的是只有一个应用会去消费这条消息->Peer To Peer方式(PTP)
    • JMS Topic模型:
      • 和Queue模型的最大区别在于消息接收的部分,在该模型中,接收消息的应用3和应用4是能够独立收到全部到达Topic的消息的->Pub/Sub方式
    • JMS中客户端链接的处理和带来的限制
      • JMS中每一个Connection都有一个惟一的clientId,用于标识链接的惟一性
      • 应用3和JMS服务器创建了两个链接,应用4和JMS服务器创建了一个链接->能够看到这三个链接所接收的消息是彻底不一样,每一个链接收到的消息条数以及收到消息的顺序则不是固定的.->另外每一个链接都会收到全部发送到Topic的消息.
    • 咱们须要什么样的消息模型
      • 消息发送方和接收方都是集群/同一个消息的接收方可能有多个集群进行消息的处理/不一样集群对于同一条消息的处理不能相互干扰
      • 如8条消息和两个集群,每一个集群刚好有两台机器->那么须要这两个集群的机器分别处理掉全部8条消息->不能遗漏也不能重复
      • 引入ClusterId,用这个Id来标识不一样的集群,而集群内的各个应用实例的链接使用一样的ClusterId->把Topic模型和Queue模型的特色结合起来使用
  • 消息订阅者订阅消息的方式
    • 做为消息中间件,提供对于消息的可靠保证是很是重要的事情->一些场景中一些下游系统彻底经过消息中间件进行自身任务的驱动
  • 持久订阅、非持久订阅
    • 非持久订阅:消息接收者应用启动时,就创建了订阅关系->能够收到消息->若是消息接收者应用结束了,那么消息订阅关系也就不存在了->这时的消息是不会为消息接收者保留的.
    • 持久订阅:消息订阅关系一旦创建除非应用显示地取消订阅关系不然这个订阅关系将一直存在即便消息接收者应用中止->这个消息也会保留,等待下次应用启动后再投递给消息接收者.
  • 保证消息可靠性
    • 消息从发送端应用到接收端应用,中间有三个阶段须要保证可靠,分别是:[消息发送者把消息发送到消息中间件];[消息中间件把消息存入消息存储];[消息中间件把消息投递给消息接收者]
    • 要保证这三个阶段均可靠,才能保证最终消息的可靠
      • 消息发送端可靠的保证->注意异对异常的处理->可能出现的问题是在不注意的状况下吃掉了异常->从而致使错误的判断结果
      • 消息存储的可靠性保证
        • 持久存储部分的代码彻底自主实现
        • 利用现有的存储系统实现
          • 实现基于文件的消息存储
          • 采用数据库做为消息存储
          • 基于双机内存的消息存储
      • 消息中间件自身扩容
        • 让消息的发送者和消息的订阅者可以感知到有新的消息中间件机器加入到了机器->软负载中心
      • 消息存储的扩容处理
        • 服务端主动调度安排投递
    • 消息投递的可靠性保证
      • 消息接收者在处理消息的过程当中对于异常的处理->千万不要吃掉异常后确认消息处理成功
      • 投递处理优化:
        • 投递是必定要采用多线程处理
        • 单机多订阅者共享链接->消息只发送一次
  • 订阅者视角的消息重复的产生和应对
    • 分布式事务,复杂
    • 幂等操做->对于消息接收端->采用一样的输入屡次调用处理函数会获得一样的结果
    • JMS的消息确认方式与消息重复的关系
      • AUTOACKNOWLEDGE/CLIENTACKNOWLEDGE/DUPSOKACKNOWLEDGE
  • 消息投递的其余属性支持
    • 消息优先级
    • 订阅者消息处理顺序和分级订阅
    • 自定义属性
    • 局部顺序
  • 保证顺序的消息队列设计
    • 接收端的设计从原来的Push模式变为了Pull模式

软负载中心与集中配置管理

  • 软负载中心两个最基础的职责
    • 聚合地址信息
    • 生命周期感知->须要能对服务的上下线自动感知,而且根据这个变化去更新服务地址数据
  • 软负载中心的结构
    • 软负载中心的服务端->负责感知提供服务的机器是否在线,聚合提供者的机器信息并负责把数据传给使用数据的应用
    • 软负载中心的客户端
      • 服务提供者->把服务器提供者提供服务的具体信息主动传给服务端->而且随着提供服务的变化去更新数据
      • 服务器使用者->向服务端告知本身所须要的数据并负责去更新数据,还要进行本地的数据缓存
    • 软负载中心三部分重要的数据->聚合数据、订阅关系、链接数据
  • 内容聚合功能的设计
    • 保证数据正确性
    • 高效聚合数据
      • 并发下的数据正确性的保证
      • 数据更新、删除的[顺序]保证
      • 大量数据同时插入、更新时的性能保证
        • 根据key进行分线程的处理->保证一样key的数据是在同一个线程中处理->顺序任务队列
  • 解决服务上下线的感知
    • 经过客户端与服务端的链接感知
      • 长链接的心跳或数据的发布来判断服务发布者是否还在线->若是好久没有心跳或数据的发布,则断定为不在线;那么就取出这个发布者发布的数据->而对于新上线的发布者,经过链接创建和数据发布就实现了上线的通知
      • 当负载中心的自身的负载很高时,可能产生误判,如软负载中心压力很大,处理请求变慢,心跳数据来不及处理->会觉得心跳超时而判断服务不在线,认为服务不可用而且把信息通知给服务调用者,这会致使本来可用的服务被下线了
      • 另外的问题,若是服务发布者到软负载中心的网络链路有问题而服务发布者到服务使用者的链路没问题,也会形成感知的问题->由于软负载中心属于旁路
        • 解决:软负载中心客户端增长逻辑,当收到软负载中心通知的应用下线数据时,须要服务调用者进行验证才能接收这个通知
    • 经过对于发布数据中提供的地址端口进行链接的检查
      • 须要服务调用者进行最终确认,由于在系统中进行的实际业务调用通讯是在服务调用者和服务提供者之间
  • 软负载中心的数据分发的特色和设计
    • 数据分发与消息订阅的区别
      • 消息中间件须要保证消息不丢失->每条消息都应该送到相关订阅者->而软负载中心只须要保证最新数据送到相关的订阅者->不须要保证每次的数据变化都能让最终订阅者感知
      • 消息中间件中同一个集群中的不一样机器是分享全部消息的,由于该消息只要同一集群中的一台机器去处理了就行->而软负载中心则不一样,由于其维护的是你们都须要用的服务数据->因此须要把这数据分发给全部的机器
    • 提高数据分发性能须要注意的问题
      • 数据压缩->CPU换带宽
      • 全量与增量的选择->建议刚开始的实现中采用简单的方式,即传送全量数据,当全量数据很大时就须要考虑采用增量传送的方式实现.
    • 针对服务化的特性支持
      • 软负载数据分组
        • 根据环境进行划分
        • 分优先级的隔离
      • 提供自动感知之外的上下线开关
        • 优雅的中止应用
          • 咱们应该先从服务列表中去掉这个机器->等待当时正在执行的服务器结束,而后再中止应用->经过指令直接从软负载中心使机器下线
        • 保持应用场景,用于排错
          • 遇到服务的问题时,能够把出问题的服务留下一台进行故障定位和场景分析->此时须要把这台机器从服务列表中拿下来,以避免有新的请求进来形成服务的失败,这也是须要软负载中心直接使服务下线的一个场景.
      • 维护管理路由规则
        • 对不一样特性的数据进行拆分
  • 从单机到集群
    • 数据管理问题/链接管理问题
    • 数据统一管理
      • 数据聚合放在一个地方->软负载中心集群,无状态->对于数据发布者和订阅者来讲,选择软负载中心集群中的任何一个机器链接皆可
      • 把软负载中心集群中的机器的职责分开,即把聚合数据的任务和推送数据的任务分到专门的机器上处理->将软负载中心集群中有一台机器为软负载中心数据聚合,另外一台机器为软负载中心数据推送->发布者和订阅者的链接是分开管理的->为了提高性能,在软负载中心负责数据推送的机器上是能够对聚合数据作缓存
    • 数据对等管理方案
      • 将数据分散在各个软负载中心的节点上而且把本身节点管理的数据分发到其余节点上,从而保证每一个节点都有整个集群的所有数据而且这些节点的角色是对等的->使用软负载中心的数据发布者和数据订阅者只须要去链接软负载中心集群中的任何一台机器就能够->软负载中心集群内部,各个节点之间会进行数据的同步
        • 批量处理同步->合并变化,同步一次
        • 若是节点较多,同步量会较大->对集群内的节点进行指责划分
        • 若是集群管理的整体数据不少,超过了单机限制->则须要对数据进行分组处理->让每一个节点管理一部分数据->即用UI规则对数据进行相似分库分表的操做->则数据订阅者可能就须要链接多个数据分发节点了
  • 集中配置管理中心
    • 集中配置管理中心结构
      • 准备的持久存储来保存持久数据(Master-Slave)->通常采用关系型数据库->经过两个节点的主备来解决持久数据安全的问题.
      • 集中配置管理中心集群这层由多个集中配置管理中心节点组成->对等->均可以提供数据给应用端等->互不依赖
      • 集中配置管理中心的单个节点->部署了一个nginx和一个web应用->其中web应用主要负责完成相关的程序逻辑如数据库的相关操做以及根据ip等的分组操做,即整个应用的逻辑放在了web应用中;单机的本地文件Local File则是为了容灾和提高性能,客户端进行数据获取的时候,最后都是从nginx直接获取本地文件并把数据返回给请求端
    • 集中配置管理中心的使用分为了如下两部分
      • 提供给应用使用的客户端->主要是业务应用经过客户端去获取配置信息和数据,用于数据的读取
      • 为控制台或者控制脚本提供管理SDK
        • 包括了对数据的读写,经过管理SDK能够进行配置数据的更改
    • 客户端实现和容灾策略
      • 客户端经过http协议与集中配置管理中心进行通讯
        • 经过轮询获取最新数据_普通轮询
        • 改进使用长轮询,Long Polling->若是没有数据,长轮询会等待;若是等待数据,马上返回;若是一直没有数据则等到超时后返回,继续创建链接,而普通轮询就直接返回了->是HTTP普通轮询和Socket长链接方式的折中-
      • 容灾
        • 数据缓存
        • 数据快照
        • 本地配置
        • 文件格式->若是是二进制数据格式,那么就没有对应的工具是没法对配置进行修改->若是客户端容灾退化到一个单机应用就会须要直接修改配置内容和数据->那么文本格式的限制就很是重要和关键了
    • 服务端实现和容灾策略
      • Nginx+Web应用->和逻辑相关的部分在Web应用上实现,Nginx用于请求的处理和最后结果的返回,而供返回的数据的都在本地文件系统中
      • 和数据库的数据同步
    • 数据库策略
      • 数据库在设计时须要支持配置的版本管理,即随着配置内容的更改,老的版本是须要保留的,为了方便进行配置变动的对比和回滚->而数据库自己须要主备进行数据的容灾考虑

构建大型网站的其余要素

  • 加速静态内容访问速度的CDN
    • CDN源站/CDN节点
    • 网络缓存技术
    • 几个关键技术
      • 全局调度->须要根据用户地域、接入运营商以及CDN机房的负载状况去调度
      • 缓存技术->提高命中率
      • 内容分发
      • 带宽优化
  • 大型网站的存储支持
    • 基本上就是在解决存储和计算的问题
    • 关系型数据库
    • 分布式文件系统
      • 图片、大文本存储->使用数据库不合适
      • NAS网络存储设备(Network Attached Storage),其自己的IO吞吐性能以及扩展性在大型网站中会出现比较明显不足
      • 分布式文件系统具体产品
        • 开源的淘宝的TFS
        • 不开源的Google#GFS,Goole File System->GFS Client(负责从Master获取要操做的文件在ChunkServer中的具体地址,而后直接和ChunkServer通讯,获取数据或者进行数据的写入、更新)/GFS Master(维护全部的文件系统元数据、控制整个系统范围内的一些活动、与ChunkServer之间经过周期性的心跳进行通讯,检测对方是否在线)/GFS chunkserver(Data Node,文件存储的地方)
        • 主要解决了单机文件存储容量及安全性的问题,把多台廉价pC组成一个大的分布式的看起来像文件系统的集群
        • [HDFS,采用Java的类GFS的实现]
      • NoSQL
        • No SQL/Not Only SQL
        • 基本上处于分布式文件系统和SQL关系型数据库之间的系统都被归为NOSQL的范畴
        • 数据模型
          • Key-Value,没办法进行高效的范围查询
          • Ordered Key-Value,Key是有序的->可解决基于Key的范围查询的效率问题,不过在这个模型中,Value自己的内容和结构是由应用来负责解析和存储的->若是在多个应用中去使用则并不直观也不方便
          • BigTable
            • Google的结构化数据的分布式存储系统->Value是由多个Column Family组成
          • Document,Full-Text Search
            • 能够在Value中任意自定义复杂的Scheme/对索引方面的支持
          • Graph
            • 支持图结构的数据类型
        • 系统结构
          • [HBase]->借鉴Google BigTable的一个Java版本的开源实现
            • 存储到HBase的数据是经过HRegionServer来管理的,每一个HRegionServer管理了多个HRegion,每一个Region管理具体的数据->HMaster是管理全部HRegionServer的节点,是一个中心控制的结构
          • Amazon#Dynamo结构
            • 采用了一致性哈希进行管理
            • [Cassandra]是一个开源的相似Dynamo的实现
  • 缓存系统
    • 非持久的存储,是为了加速应用对数据的读取
    • Redis和Memcache是两个使用很普遍的开源缓存系统->Redis已经有了对于集群的支持,也能够作单机的应用来使用;而memcache自己仍是一个单机的应用,在使用时->集群->常见的是采用一致性哈希的方式
    • 使用缓存的场景
      • 使用缓存来下降对底层存储的读压力,须要注意缓存和数据存储中数据一致性问题
      • 应用 <---> 缓存 <---> 存储
        • 这种方式,应用是不直接操做存储,存储由缓存控制;对于缓存来讲,须要保证数据写入缓存后可以存入存储中,因此缓存自己的逻辑会复杂些,须要有不少操做日志及故障恢复等
      • 另外一种方式,应用直接与缓存和存储进行交互。通常的作法是应用在写数据时更新存储,而后失效缓存数据;而在读数据时首先读缓存,若是缓存中没有数据,那么再去读存储,而且把数据写入缓存
      • 第三种方式,对于全数据缓存比较合适,即当存储的数据变化时,直接从存储去同步数据到缓存中,以更新缓存数据
      • 另外一个重要场景是对于Web应用的页面渲染内容的缓存
        • 具体的实现技术为ESI(Edge Side Includes)->经过在返回的页面中加上特殊的标签,而后根据标签的内容去用缓存进行填充的一个过程.
        • 处理ESI标签的具体工做能够放在Java的应用容器中作,也能够放在Java应用容器前置的服务器作
  • 搜索系统
    • 站内搜索->网站的数据量和访问量很小时,一些数据的查询能够直接用数据库的Like操做来实现->实现效率低->不智能
    • 爬虫问题->根据数据变化来更新索引
    • 倒排索引
    • 查询预处理
    • 相关度计算
  • 数据计算支撑
    • 离线计算、实时计算
    • 离线计算
      • 把业务数据从在线存储中移动到离线存储中,而后进行数据处理的过程
      • Google MapReduce模型
        • Map阶段:根据设定的规则把总体数据集映射给不一样的Worker来处理而且生成各自的处理结果
        • Reduce阶段:对前面处理过的数据进行聚合,造成最后的结果
        • [Hadoop]是MapReduce的一个开源实现.Hadoop使用HDFS进行数据存储,而[Spark]则提供了基于内存的集群计算的支持
    • 在线计算
      • 流式计算->Storm
  • 发布系统
    • 分发应用->须要提供自动高效而且容易操做的机制来把通过测试的程序包分发到线上应用->通常采用Web的操做方式
      • 发布控制台->多机房->发布服务器
    • 启动校验
      • 应用重启启动后,须要进行校验从而完成这台应用服务器上的应用发布->对应用的校验一般是应用自身提供一个检测脚本或者页面,发布系统执行这个脚本或者访问页面后来判断返回的结果
      • 中止应用时,须要优雅的关闭->须要在关闭应用前把这个应用从负载均衡或者软负载中心上移除
    • 灰度发布
      • 会对新应用进行分批发布,逐步扩大新应用在整个集群中的比例直至最后所有完成->这里讲的灰度发布主要是针对新应用在用户体验方面彻底感知不到的更新.
    • 产品改版Beta
      • 提供新旧应用的共存
  • 应用监控系统
    • 可以及时了解应用的运行情况并可以进行相应的控制
    • 监视和控制量部分
      • 数据监视维度
        • 系统数据和应用自身的数据->系统数据指的就是当前应用运行的系统环境的信息,如CPU使用率、内存使用状况、交换分区使用状况、当前系统负载、IO状况等;而应用自身的数据,则是不一样应用有不一样的数据,通常会是调用次数、成功率、响应时间、异常数量等维度的数据
      • 数据记录方式
        • 系统自身的数据已经被记录到了本地磁盘,应用的数据通常也是存放在应用自身的目录中,便于采集->也有直接把应用日志经过网络发送到采集服务器的状况,能够减轻本地写日志的压力
        • 对于应用数据的记录,会考虑用定时统计的方式记录一些量很大的信息.如对于一个提供服务的应用,在没有特别需求时,并不直接记录每次调用的信息,而是会记录一段时间如5s或者一个间隔时间内的总调用次数、总响应时间这样写信息,而对于异常信息则每次都会予以记录;采用统计的方式是为了减少记录的大小以及对本地磁盘的写入压力
      • 数据采集的方式
        • 采集方式有应用服务器主动对同给监控中心以及等待监控中心来拉取两种方式->前者控制权在应用服务器上,可能出现的问题是应用服务器推送的压力超过采集的中心服务器的能力,会形成重试等额外开销而且须要应用服务器上的推送程序控制重试逻辑和当前传送位置等信息->后者把复杂性都放在中心采集服务器上处理,使得应用服务器中支持数据采集的部分变的简单
        • 展示与告警
          • 提供图表的形式能够提供Web页面的展现->经过手机应用来接收报警->比短信方式好
        • 控制
          • 应用启动后在运行期对于应用的行为改变->对于应用的运维,最低的要求是出现问题时能够经过重启应用解决,可是咱们仍是须要更加精细化的控制应用-下降和一些切换。降级是咱们遇到大量请求且不能扩容的状况时所进行的功能限制的行为->而切换更多的是当依赖的下层系统出现故障而且须要手工进行切换时的一个管理,这些控制通常都是经过开关,参数设置来完成
  • 依赖管理系统
    • 随着网站功能增多,应用的个数迅速增长,应用之间的关系也会愈来愈复杂,理清这些依赖关系并可以管理这些依赖会很是重要
    • 一个应用在完成某个功能时到底须要依赖哪些外部系统、这些依赖中哪些是必要依赖,强依赖(登录验证用户名和密码),哪些是有了更好没有也能够的依赖,弱依赖(如记录登录时间和ip等)
    • 动态检测和静态检测->动态检测的主要检查方式是模拟被调用系统不可用和响应慢的两种状况
      • Google#Dapper,A Large-Scale Distributed Systems Tracing Infrastructure->traceId,index->造成一个调用时序图
  • 多机房问题分析
    • 同城机房和异地机房
      • 同城多个机房中,对于重要的应用系统,会在不止一个机房中部署;而对于数据库系统,则会把主备放在不一样机房->尽可能避免没必要要的跨机房的内部系统调用
      • 为了数据安全,把产生的业务数据都同步到异地的机房->把一些对数据延迟不敏感的系统部署到异地,如只读系统.
  • 系统容量规划
    • 咱们应该知道的信息就是整个系统的容量以及运行时所处的水位->咱们把某个应用系统集群可以提供的并发能力和当前的压力比做一个水桶的容量和水位->那么准确知道各个系统的容量和当前高峰时的水位是一件很重要的事情->由于咱们仍是但愿优先经过扩大容量来支持更多的请求而不是首选降级的方案.
    • 考虑过去的增加状况并结合人为的判断
      • 弄清楚当前系统高峰期的水位
      • 弄清楚当前各个系统的容量
        • 经过测试->压力测试
      • 设置警惕值,高峰水位搞过警惕值就增长容量,保持高峰的水位是低于警惕值的
  • 内部私有云

总结:

能够将一些设计思路、方法等应用到游戏服务器总体架构设计当中(主要是登录、支付等http服务).nginx

相关文章
相关标签/搜索