zz《可伸缩服务架构 框架与中间件》综合

  

第1章 如何设计一款永不重复的高性能分布式发号器

1. 为什么不直接采用UUID?

虽然UUID能够保证唯一性,但无法满足业务系统需要的很多其他特性,比如时间粗略有序性、可反解和可制造性(说人话,就是分布式ID需要体现根据时间递增的特点,并且从ID串中能解析出一定的业务含义),同时UUID比较长,占空间大,性能较差。

2. 那基于数据库来实现呢?

即通过调整自增字段或者数据库sequence的步长来确保跨数据库的ID的唯一性,但这种方案强依赖于数据库。

实现方案,可见我:重构 - 分布式ID设计方案

3. 分布式ID的基本需求

(1)全局唯一

分布式系统保证唯一的一个悲观策略是使用锁或者分布式锁,但是这样会大大降低性能。因此利用时间的有序性,并且在时间的某个单元下采用自增序列,来达到全局唯一。

(2)粗略有序

UUID最大的问题是无序。

(3)可反解

即能从ID串能看出一定的业务含义,比如什么时候产生的,跟哪些业务功能模块相关的。

(4)可制造

不依赖发号器(如果系统崩了),也能通过一定的规律来手工处理数据。

(5)高性能

产生一个新业务,就要生成一个新ID,所以对性能要求非常高。ID的生成取决于网络I/O和CPU的性能,网络I/O一般不是瓶颈。

(6)高可用

发号器应该是满足HA的集群,同时拥有重试机制。在极端情况下,还要有本地的容错方案。

4. 如何保证性能需求?

在项目初期提出性能需求,在项目进行中做性能测试来验证。

5. 获取分布式ID的几种方法

(1)REST方法

提供一个HTTP接口来获取

(2)RPC服务化方法

服务化模式通过Dubbo导出RPC服务

(3)嵌入方法

将发号器服务嵌入到业务项目中,并且提供JVM进程内的本地服务

第2章 可灵活扩展的消息队列框架的设计与实现

1. 背景介绍

消息队列多应用于异步处理、模块之间的解耦和高并发系统的削峰等场景中。

2. 线程模型

(1)同步线程模型

客户端为每个消费者流使用一个线程,每个线程负责从Kafka队列里消费消息,并且在同一个线程里处理业务。

(2)异步线程模型

客户端为每个消费者流使用一个线程,每个线程负责从Kafka队列里消费消息,并且传递消费得到的消息到后端的异步线程池中,在异步线程池中处理业务。

而后端的异步业务线程池又可细分为:

1> 所有消费者流共享线程池

此种模式可以创建更少的线程池对象,节省些许内存

2> 每个流独享线程池

使用不同的异步业务线程池来处理不同的流里面的消息,互相隔离、互相独立、不互相影响。比如,区分普通用户的消息,和VIP用户的消息,这样可以在不同业务线程池中来处理。

3. 异常处理

对于在消息处理过程中产生的业务异常,当前在业务处理的上层捕捉了Throwable,在专用的错误恢复日志中记录了出错的信息,后续可根据错误恢复日志人工处理错误消息,也可重做或者清洗数据。也可考虑采用Listener体系,对异常处理采用监听者模式来实现异常处理器的可插拔等。

4. 优雅关机

通过注册JVM退出钩子进行优雅关机。

=========================继续看下篇==========================

=================================请看上篇======================================

第3章 轻量级的数据库分库分表架构与框架

1. 分库分表的定义

(1)垂直拆分

根据业务的维度,将原来的一个库(表)拆分为多个库(表),每个库(表)与原有的结构不同。

这种方式除了用于分解单库单表的压力,也用于实现冷热分离。

1》在Mysql中,冷数据查询较多,更新较少,适合用MyISAM引擎,而热数据更新比较频繁,适合使用InnoDB存储引擎

2》对读多写少的冷数据可配置更多的从库来化解大量查询请求的压力对于热数据,可以使用多个主库构建分库分表的结构

3》对于一些特殊的活跃数据或者热点数据,也可以考虑使用Memcache、Redis之类的缓存,等累计到一定的量后再更新数据库。例如,在记录微博点赞数量的业务中,点赞数量被存储在缓存中,每增加1000个点赞,才写一次数据。

(2)水平拆分

根据分片(sharding)算法,将一个库(表)拆分为多个库(表),每个库(表)依旧保留原有的结构。

切分的维度有:

1》按哈希切片

好处是数据切片比较均匀,对数据压力分散的效果较好,缺点是数据分散后,对于查询需求需要进行聚合处理

2》按时间切片

适用于有明显时间特点的数据

2. 分库分表的三种实现方案

(1)客户端分片

使用分库分表的数据库的应用层直接操作分片逻辑,分片规则需要在同一个应用的多个节点间进行同步,每个应用层都嵌入一个操作切片的逻辑实现(分片规则)。一般是Jar包的形式。

(2)代理分片

在应用层和数据库层增加一个代理层,把分片的路由规则配置在代理层,代理层对外提供与JDBC兼容的接口给应用层。我们公司正在用这样的框架:Mycat。

(3)支持事务的分布式数据库

将可伸缩的特点和分布式事务的实现包装到分布式数据库内部实现,对使用者透明。例如:OceanBase、TiDB等产品。

3. 分布式事务

解决方案有三种:两阶段提交协议、最大努力保证模式、事务补偿机制。

第4章 缓存的本质和缓存使用的优秀实践

通过缓存和消息队列来化解海量的读请求和写请求对后端数据库服务造成的压力。

1. 伪共享

是指在多个线程同时读写同一个缓存行的不同变量时,尽管这些变量之间没有任何关系,但是在多个线程之间仍然需要同步,从而导致性能下降的情况。

2. JVM对象的内存模型

1》所有的Java对象都有8字节的对象头,前4个字节用来保存对象的哈希码和锁的状态,前3个字节用来存储哈希码,最后一个字节用来存储锁的状态,一旦对象上锁,这4个字节都会被拿出对象外,并用指针进行链接;剩下的4个字节用来存储对象所属类的引用。每个对象的大小都会对齐到8字节的倍数,不够8字节的部分需要填充。

2》为了保证效率,Java编译器在编译Java对象时,会通过字段类型对Java对象的字段进行排序。在任意字段之间通过填充长整型的变量,把热点变量隔离在不同的缓存行中,通过减少伪同步,在多核心CPU中能够极大地提高效率。

3》著名的无锁队列Disruptor通过解决伪共享的问题来提高效率,它通过在RingBuffer的游标和BatchEventProcessor的序列变量之后填充变量,并使之与64字节大小的缓存行对齐,来解决伪竞争的问题。

3. 常用的分布式缓存:Redis、Memcached、Tair

https://www.cnblogs.com/chenyangjava/p/8986383.html

阿里P8架构师谈:分布式缓存的应用场景、选型比较、问题和挑战

高并发环境下,例如典型的淘宝双11秒杀,几分钟内上亿的用户涌入淘宝,这个时候如果访问不加拦截,让大量的读写请求涌向数据库,由于磁盘的处理速度与内存显然不在一个量级,服务器马上就要宕机。

作者:优知学院来源:今日头条|2018-08-07 10:04
 

高并发环境下,例如典型的淘宝双11秒杀,几分钟内上亿的用户涌入淘宝,这个时候如果访问不加拦截,让大量的读写请求涌向数据库,由于磁盘的处理速度与内存显然不在一个量级,服务器马上就要宕机。从减轻数据库的压力和提高系统响应速度两个角度来考虑,都会在数据库之前加一层缓存,访问压力越大的,在缓存之前就开始CDN拦截图片等访问请求。

并且由于最早的单台机器的内存资源以及承载能力有限,如果大量使用本地缓存,也会使相同的数据被不同的节点存储多份,对内存资源造成较大的浪费,因此,才催生出了分布式缓存。

 

分布式缓存应用场景

  1. 页面缓存.用来缓存Web 页面的内容片段,包括HTML、CSS 和图片等;
  2. 应用对象缓存.缓存系统作为ORM 框架的二级缓存对外提供服务,目的是减轻数据库的负载压力,加速应用访问;
  3. 解决分布式Web部署的session同步问题,状态缓存.缓存包括Session 会话状态及应用横向扩展时的状态数据等,这类数据一般是难以恢复的,对可用性要求较高,多应用于高可用集群。
  4. 并行处理.通常涉及大量中间计算结果需要共享;
  5. 云计算领域提供分布式缓存服务。

分布式缓存比较:Memcache VS Redis

1、Redis不仅仅支持简单的k/v类型的数据,同时还提供list,set,zset,hash等数据结构的存储。而memcache只支持简单数据类型,需要客户端自己处理复杂对象

2、Redis支持数据的持久化,可以将内存中的数据保持在磁盘中,重启的时候可以再次加载进行使用(PS:持久化在rdb、aof)。

3、由于Memcache没有持久化机制,因此宕机所有缓存数据失效。Redis配置为持久化,宕机重启后,将自动加载宕机时刻的数据到缓存系统中。具有更好的灾备机制。

4、Memcache可以使用Magent在客户端进行一致性hash做分布式。Redis支持在服务器端做分布式(PS:Twemproxy/Codis/Redis-cluster多种分布式实现方式)

5、Memcached的简单限制就是键(key)和Value的限制。***键长为250个字符。可以接受的储存数据不能超过1MB(可修改配置文件变大),因为这是典型slab 的***值,不适合虚拟机使用。而Redis的Key长度支持到512k。

6、Redis使用的是单线程模型,保证了数据按顺序提交。Memcache需要使用cas保证数据一致性。CAS(Check and Set)是一个确保并发一致性的机制,属于“乐观锁”范畴;原理很简单:拿版本号,操作,对比版本号,如果一致就操作,不一致就放弃任何操作

cpu利用。由于Redis只使用单核,而Memcached可以使用多核,所以平均每一个核上Redis在存储小数据时比Memcached性能更 高。而在100k以上的数据中,Memcached性能要高于Redis 。

7、memcache内存管理:使用Slab Allocation。原理相当简单,预先分配一系列大小固定的组,然后根据数据大小选择最合适的块存储。避免了内存碎片。(缺点:不能变长,浪费了一定空间)memcached默认情况下下一个slab的***值为前一个的1.25倍。

8、redis内存管理: Redis通过定义一个数组来记录所有的内存分配情况, Redis采用的是包装的malloc/free,相较于Memcached的内存 管理方法来说,要简单很多。由于malloc 首先以链表的方式搜索已管理的内存中可用的空间分配,导致内存碎片比较多。

分布式缓存选型总结

其实对于企业选型Memcache、Redis而言,更多还是应该看业务使用场景(因为Memcache、Redis两者都具有足够高的性能和稳定性)。假若业务场景需要用到持久化缓存功能、或者支持多种数据结构的缓存功能,那么Redis则是***选择。

(PS:Redis集群解决方式也优于Memcache,Memcache在客户端一致性hash的集群解决方案,Redis采用无中心的服务器端集群解决方案)

综上所述:为了让缓存系统能够支持更多的业务场景,选择Redis会更优。

分布式缓存的常见问题和挑战

1.缓存雪崩

缓存雪崩我们可以简单的理解为:由于原有缓存失效,新缓存未到期间(例如:我们设置缓存时采用了相同的过期时间,在同一时刻出现大面积的缓存过期),所有原本应该访问缓存的请求都去查询数据库了,而对数据库CPU和内存造成巨大压力,严重的会造成数据库宕机。从而形成一系列连锁反应,造成整个系统崩溃。

2.缓存穿透

缓存穿透是指用户查询数据,在数据库没有,自然在缓存中也不会有。这样就导致用户查询的时候,在缓存中找不到,每次都要去数据库再查询一遍,然后返回空(相当于进行了两次无用的查询)。这样请求就绕过缓存直接查数据库,这也是经常提的缓存***率问题。

3.缓存预热

缓存预热这个应该是一个比较常见的概念,相信很多小伙伴都应该可以很容易的理解,缓存预热就是系统上线后,将相关的缓存数据直接加载到缓存系统。这样就可以避免在用户请求的时候,先查询数据库,然后再将数据缓存的问题!用户直接查询事先被预热的缓存数据!

4.缓存更新

除了缓存服务器自带的缓存失效策略之外,我们还可以根据具体的业务需求进行自定义的缓存淘汰,常见的策略有两种:

(1)定时去清理过期的缓存;

(2)当有用户请求过来时,再判断这个请求所用到的缓存是否过期,过期的话就去底层系统得到新数据并更新缓存。

两者各有优劣,***种的缺点是维护大量缓存的key是比较麻烦的,第二种的缺点就是每次用户请求过来都要判断缓存失效,逻辑相对比较复杂!具体用哪种方案,大家可以根据自己的应用场景来权衡。

5.缓存降级

当访问量剧增、服务出现问题(如响应时间慢或不响应)或非核心服务影响到核心流程的性能时,仍然需要保证服务还是可用的,即使是有损服务。系统可以根据一些关键数据进行自动降级,也可以配置开关实现人工降级。

降级的最终目的是保证核心服务可用,即使是有损的。而且有些服务是无法降级的(如加入购物车、结算)。

在进行降级之前要对系统进行梳理,看看系统是不是可以丢卒保帅;从而梳理出哪些必须誓死保护,哪些可降级;比如可以参考日志级别设置预案:

(1)一般:比如有些服务偶尔因为网络抖动或者服务正在上线而超时,可以自动降级;

(2)警告:有些服务在一段时间内成功率有波动(如在95~100%之间),可以自动降级或人工降级,并发送告警;

(3)错误:比如可用率低于90%,或者数据库连接池被打爆了,或者访问量突然猛增到系统能承受的***阀值,此时可以根据情况自动降级或者人工降级;

(4)严重错误:比如因为特殊原因数据错误了,此时需要紧急人工降级。

 
   

还是《吊打面试官》系列讲得好

4. 缓存穿透、缓存并发和缓存雪崩

(1)缓存穿透

指的是使用不存在的key进行大量的高并发查询,这导致缓存无法命中,每次请求都要穿透到后端数据库系统进行查询,使数据库压力过大,甚至使数据库服务被压死。

通常将空值缓存起来,再次接收到同样的查询请求时,若命中缓存并且值为空,就会直接返回,不会透传到数据库,避免缓存穿透。

但有时恶意攻击者会猜到这种方案,每次都会使用不同的参数来查询,这是需要我们对输入的参数进行过滤。

(2)缓存并发

通常发生在高并发的场景下,当一个缓存key过期时,因为访问这个缓存key的请求量过大,多个请求同时发现缓存过期,因此多个请求会同时访问数据库来查询最新数据,并且会写缓存,这样会造成应用和数据库的负载增加,性能降低。

有3种方式解决:

1》分布式锁

2》本地锁

3》软过期

就是不使用缓存服务提供的过期时间,而是业务层在数据中存储过期时间信息,由业务程序判断是否过期并更新。

(3)缓存雪崩

指缓存服务器重启或者大量缓存集中在某一个时间段内失效,给后端数据库造成瞬时的负载升高的压力。

通常是对不同的数据使用不同的失效时间。

5. 缓存设计的优秀实践(好好看看)

(1)核心业务和非核心业务使用不同的缓存实例,从物理上进行隔离。

(2)缓存的超时时间的设置是很重要的。给个数字,希望是100毫秒以内。

(3)所有的缓存实例都需要添加监控,需要对慢查询、大对象、内存使用情况做可靠的监控。

(4)通过规范来限制各个应用使用的key有唯一的前缀。

(5)任何缓存的key都必须设定缓存失效时间,且失效时间不能集中在某一点。

(6)通常情况下,读的顺序是先缓存,后数据库;写的顺序是先数据库,后缓存。

(7)在使用缓存时,一定要有降级处理,尤其是对关键的业务环节。常见的方案是在数据库层次预留足够的容量。

(8)如果有大量的键值对要保存,则请使用字符串键的数据库类型,并对每个键都设置过期时间,请不要在哈希键内部存储一个没有边界的集合数据。

====================================继续看下篇===============================================

==================================请看上篇========================================

第5章 大数据利器之Elasticsearch

1. Lucene

底层是基于Lucene实现的,其基于倒排表的设计原理,采用了分段的存储模式,使它在读写时几乎完全避免了锁的出现,大大提升了读写性能

2. 分段存储

(1)在删除文件时,Lucene在索引文件下新增了一个.del的文件,用于专门存储被删除的数据id。

(2)为了提升写的性能,Lucene并没有每新增一条数据就增加一个段,而是采用延迟写的策略,每当有新增的数据时,就将其先写入内存中,然后批量写入磁盘中。

所以Lucene或Elasticsearch并不能称为实时的搜索引擎,只能被称为准实时的搜索引擎。

3. 段合并策略

当索引中段的数量太多时,不仅会严重消耗服务器的资源,还会影响检索的性能。所以必须定期进行段合并操作。

Lucene段合并的思路:根据段的大小先将段进行分组,再将属于同一组的段进行合并。Lucene会在段的大小达到一定的规模,或者段里面的数据量达到一定条数时,不会再进行合并。

4. Lucene相似度打分

最经典的两个文本相似度算法:基于向量空间模型的算法和基于概率的算法(BM25)

https://learnku.com/articles/40400

3C 和脑裂

①共识性(Consensus)
共识性是分布式系统中最基础也最主要的一个组件,在分布式系统中的所有节点必须对给定的数据或者节点的状态达成共识。

虽然现在有很成熟的共识算法如 Raft、Paxos 等,也有比较成熟的开源软件如 Zookeeper。

但是 Elasticsearch 并没有使用它们,而是自己实现共识系统 zen discovery。

*Elasticsearch 之父 Shay Banon 解释了其中主要的原因:**“zen discovery 是 Elasticsearch 的一个核心的基础组件,zen discovery 不仅能够实现共识系统的选择工作,还能够很方便地监控集群的读写状态是否健康。当然,我们也不保证其后期会使用 Zookeeper 代替现在的 zen discovery”。*

zen discovery 模块以 “八卦传播”(Gossip)的形式实现了单播(Unicat):单播不同于多播(Multicast)和广播(Broadcast)。节点间的通信方式是一对一的。

②并发(Concurrency)
Elasticsearch 是一个分布式系统。写请求在发送到主分片时,同时会以并行的形式发送到备份分片,但是这些请求的送达时间可能是无序的。

在这种情况下,Elasticsearch 用乐观并发控制(Optimistic Concurrency Control)来保证新版本的数据不会被旧版本的数据覆盖。

乐观并发控制是一种乐观锁,另一种常用的乐观锁即多版本并发控制(Multi-Version Concurrency Control)。

它们的主要区别如下:

乐观并发控制(OCC):是一种用来解决写 - 写冲突的无锁并发控制,认为事务间的竞争不激烈时,就先进行修改,在提交事务前检查数据有没有变化,如果没有就提交,如果有就放弃并重试。乐观并发控制类似于自选锁,适用于低数据竞争且写冲突比较少的环境。

多版本并发控制(MVCC):是一种用来解决读 - 写冲突的无所并发控制,也就是为事务分配单向增长的时间戳,为每一个修改保存一个版本,版本与事务时间戳关联,读操作只读该事务开始前的数据库的快照。

这样在读操作不用阻塞操作且写操作不用阻塞读操作的同时,避免了脏读和不可重复读。

③一致性(Consistency)
Elasticsearch 集群保证写一致性的方式是在写入前先检查有多少个分片可供写入,如果达到写入条件,则进行写操作,否则,Elasticsearch 会等待更多的分片出现,默认为一分钟。

有如下三种设置来判断是否允许写操作:

One:只要主分片可用,就可以进行写操作。

All:只有当主分片和所有副本都可用时,才允许写操作。

Quorum(k-wu-wo/reng,法定人数):是 Elasticsearch 的默认选项。当有大部分的分片可用时才允许写操作。其中,对 “大部分” 的计算公式为 int ((primary+number_of_replicas)/2)+1。

Elasticsearch 集群保证读写一致性的方式是,为了保证搜索请求的返回结果是最新版本的文档,备份可以被设置为 Sync(默认值),写操作在主分片和备份分片同时完成后才会返回写请求的结果。

这样,无论搜索请求至哪个分片都会返回最新的文档。但是如果我们的应用对写要求很高,就可以通过设置 replication=async 来提升写的效率,如果设置 replication=async,则只要主分片的写完成,就会返回写成功。

④脑裂
在 Elasticsearch 集群中主节点通过 Ping 命令来检查集群中的其他节点是否处于可用状态,同时非主节点也会通过 Ping 来检查主节点是否处于可用状态。

当集群网络不稳定时,有可能会发生一个节点 Ping 不通 Master 节点,则会认为 Master 节点发生了故障,然后重新选出一个 Master 节点,这就会导致在一个集群内出现多个 Master 节点。

当在一个集群中有多个 Master 节点时,就有可能会导致数据丢失。我们称这种现象为脑裂。

————————————————

 
   

5. 3C和脑裂

(1)共识性(Consensus)

在分布式系统中的所有节点必须对给定的数据或者节点的状态达成共识。

(2)并发(Concurrency)

写请求在发送到主分片时,同时会以并行的形式发送到备份分片,但是这些请求的送达时间可能是无序的。在这种情况下,Elasticsearch用乐观并发控制来保证新版本的数据不会被旧版本的数据覆盖。

(3)一致性(Consistency)

Elasticsearch集群保证写一致性的方式是在写入前先检查有多少个分片可供写入,如果达到写入条件,则进行写操作,否则,Elasticsearch会等待更多的分片出现,默认为一分钟。

(4)脑裂

在Elasticsearch集群中主节点通过ping命令来检查集群中的其他节点是否处于可用状态,同时非主节点也会通过ping命令来检查主节点是否处于可用状态。

6. 性能调优

(1)写优化

批量提交、优化存储设备、合理使用段合并、减少refresh的次数(为了提高写的性能,采用的是延迟写入的策略,即将要写入的数据先写到内存中,当延时超过1秒(默认)时,会触发一次refresh,refresh会把内存中的数据以段的形式刷新到操作系统的文件缓存系统中)、减少flush的次数、减少副本的数量

(2)读优化

避免大结果集和深翻、选择合适的路由、SearchType、定期删除

7. Elasticsearch堆内存的分配

(1)最好不要超过物理内存的50%

(2)堆内存的大小最好不要超过32GB

Java使用内存指针压缩技术来解决这个问题。

第6章 全面揭秘分布式定时任务

1. 怎么让某一个定时任务在一个触发时刻上仅有一台服务器在执行?

(1)只在一台服务器上执行

但这样有明显缺陷:单点风险和资源分布不均衡。

(2)通过配置参数分散运行

比如创建一个配置项,指定该台服务器需要执行的定时任务类名,这样可以在部署服务时手动分散定时任务到不同的服务器上。

(3)通过全局“锁”互斥执行

可使用ZooKeeper、Redis或者数据库(依靠唯一索引)等方式。

2. TBSchedule

(1)简介

是阿里开源的分布式调度框架,可以使批量的动态变化的任务被动态地分配到多个机器的JVM中并行执行,而且有失效转移等优点。

(2) TBSchedule的动态可伸缩原理

TBSchedule在硬件主机与工作处理器之间又抽象出一个调度服务器层IStrategyTask,通过这个调度服务器层完成解耦。工作处理器ScheduleProcessor是真正负责具体任务处理的,工作处理器内部以多线程的方式并行进行任务处理,与调度服务器是一对一的关系。调度服务器依赖当前可以使用的主机数量进行自定义分配或均匀分配。

(3) TBSchedule的分片机制

TBSchedule可以将批量任务进行分片,分片的逻辑由 TBSchedule使用方根据具体业务自行定义,一个分片对应一个调度处理器,但一个调度处理器可对应多个任务分片,进而由调度处理器对应的工作处理器进行并发处理。

3. Elastic-Job

(1)简介

Elastic-Job是当当网开源的分布式调度解决方案,支持任务分片功能,能充分利用资源。

(2)Elastic-Job分为Elastic-Job-Lite和Elastic-Job-Cloud组成

Elastic-Job-Lite被定位为轻量级无中心化解决方案,通过Jar包的形式提供分布式任务的协调服务。而Elastic-Job-Cloud使用Mesos+Docker的解决方案,额外提供资源治理、应用分发及进程隔离等服务。

(3)任务分片

1》目的在于将一个任务分散到不同的机器上执行,既可以解决单机计算能力有限的问题,也能减少部分任务失败对整体系统的影响。开发者需要自行处理分片项与真实数据的对应关系,同时要注意任务失败重试的幂等性。

2》通常将分片项设置为大于服务器的数量,最好是大于服务器倍数的数量,作业将会合理地利用分布式资源,动态地分配分片项。

=============================继续看下篇=======================================

=========================请看上篇================================

第7章 RPC服务的发展历程和对比分析

1. RPC采用客户端/服务端模式,请求程序就是一个客户端,服务提供程序就是一个服务端。

RPC是构建在语言级之上的,是跨语言的,它在OSI七层模型中介于会话层和表示层之间。

2. RPC实现透明的远程过程调用的重点

就是创建客户存根(client stub),存根(stub)就像代理(agent)模式里的代理(agent),在生成代理代码后,代理的代码就能与远程服务端通信了,通信的过程都由RPC框架实现,而调用者就像调用本地代码一样方便。在客户端看来,存根函数就像普通的本地函数一样,但实际上包含了通过网络发送和接收消息的代码。

(1)客户端存根的方法会将参数打包并封装成一个或多个网络消息体并发送到服务端。将参数封装到网络消息中的过程被称为编码(encode),它会将所有数据序列化为字节数组格式。

(2)服务端存根(server stub)接收客户端发送的消息,并对参数消息进行编码(decode),通常它会将参数从标准的网络格式转换成特定的语言格式。

(3)服务端存根在将该返回值进行编码并序列化后,通过一个或多个网络消息发送给客户端。

3. 在使用RMI的JVM中,Java支持两种操作:标记脏数据和清理。当对象仍在使用时,本地JVM会定期向服务器的JVM发送一个标记脏数据的调用。标记脏数据基于服务器给定的时间间隔定期重新发送心跳信息。当客户端没有更多的本地引用远程对象时,会发送一个清理的调用给服务器。

第8章 Dubbo实战及源码分析

1. Dubbo的三大类配置

(1)服务发现类:表示该配置项用于服务的注册与发现,目的是让消费者找到提供者。

(2)服务治理类:表示该配置项用于治理服务间的关系,或为开发测试提供便利条件。

(3)性能调优类:表示该配置项用于调优性能,不同的选项会对性能产生不同的影响。

2. 由服务提供者设置超时,因为服务提供者更清楚一个方法需要执行多久。

3.

(1)如果服务需要预热的时机,比如初始化缓存、等待相关资源就位等,就可以使用delay属性进行服务延迟暴露。

(2)如果一个服务的并发量过大,超出了服务器的承受能力 --- 使用executes属性控制并发。

(3)客户端进行并发控制,通过actives属性限制。

4. 服务隔离

是为了在系统发生故障时限定传播范围和影响范围,从而保证只有出问题的服务不可用,其他服务还是正常的。

隔离一般有线程隔离、进程隔离、读写隔离、集群隔离和机房隔离,而Dubbo还提供了分组隔离,即使用group属性分组。

5. Dubbo中的异步调用时基于NIO的非阻塞机制实现的。

在远程调用的过程中如果出现异常或者需要回调,则可以使用Dubbo的事件通知机制。

6. Dubbo支持的协议

Dubbo协议 --- 通信数据包小、并发高的服务

Hessian协议 --- 传输数据大且提供者比消费者数量多的服务

HTTP或Hessian协议 --- 对于外部与内部进行通信的场景,若想要穿透防火墙的限制

7. Dubbo协议的使用注意事项

(1)在实际情况下消费者的数据比提供者的数量更多

(2)不能传大的数据包

(3)推荐使用异步单一长连接方式

长连接可以减少连接握手验证等,并且使用异步I/O,可以复用线程池,防止出现C10K问题。

8. Dubbo的I/O线程模型

Dubbo的服务提供者主要有两种线程池类型:一种是I/O处理线程池;另一种是业务调度线程池。

Dubbo默认配置无限制大小的CachedThreadPool线程池,这意味着它对所有服务的请求都不会拒绝,但是Dubbo限制了I/O线程数,默认是核数+1。

9. 服务熔断与服务降级

(1)服务熔断

是一种保护措施,一般用于防止在软件系统中由于某些原因使服务出现了过载现象,从而造成整个系统发生故障。

(2)服务降级

是在服务器压力剧增的情况下,根据当前的业务情况及流量对一些服务和页面有策略地进行降级,以释放服务器资源并保证核心任务的正常运行。

10. Dubbo是通过JDK的ShutdownHook来完成优雅停机的。

11. 线上问题排查(此处敲敲黑板)

(1)发现问题 

1》系统层面的监控

对系统的CPU利用率、系统负载、内存使用情况、网络I/O负载、磁盘负载、I/O等待、交换区的使用、线程数及打开的文件句柄数等

2》应用层面的监控

对服务接口的响应时间、吞吐量、调用频次、接口成功率及接口的波动率

3》资源层的监控

对数据库(负载、慢SQL、连接数)、缓存(连接数、占用内存、吞吐量、响应时间)和消息队列(响应时间、吞吐量、负载、积压情况)的监控

(2)定位问题

考虑如下问题:

1》问题系统最近是否进行了上线?

2》依赖的基础平台和资源是否进行了上线或者升级?

3》依赖的系统最近是否进行了上线?

4》运营人员是否在系统里做过运营变更?

5》网络是否有波动?

6》最近的业务是否上量?

7》服务的使用方是否有促销活动?

(3)解决问题

每个系统都会对各种严重情况设计止损和降级开关,因此在发生严重问题时先使用止损策略,在恢复问题后再定位和解决问题。解决问题要以定位问题为基础,必须清晰地定位问题产生的根本原因,再提出解决问题的有效方案,切记在没有明确原因之前,不要使用各种可能的方法来尝试修复问题,这样可能导致还没有解决这个问题又引出另一个问题。

(4)消除影响

首先,找运维看日志。

其次,保存现场并恢复现场。在JVM中保存现场快照通常包括保存当前运行线程的快照和保存JVM内存堆栈快照。

12. SPI(Service Provider Interface)

是Java提供的一种服务加载方式,可以避免在Java代码中写死服务提供者,而是通过SPI服务加载机制进行服务的注册和发现,实现多个模块的解耦。

第9章 高性能网络中间件

1. 在OSI中,每一层都使用下一层的协议和服务。每一层的数据包都包含自己的一个包头,这个包头包含本层定义的典型信息如IP地址、TCP端口号,以及本层服务需要的信息如IP分片信息、TCP的流量控制信息等。

因为TCP层的数据包是在IP层的数据包内增加了自己的TCP头,所以在IP层看来,任意一个TCP包都是一个普通的IP包。

2. TCP的核心还是接收序号和确认序号,通过它们可以在中途有数据丢失的情况下,通过返回ACK的序号进行重发。

3. 在应用层进行失效转移、降级、备份方案、重试等操作。
————————————————

原文地址:https://www.cnblogs.com/cx2016/p/12018214.html