分布式寻址,

    1.不重启客户端就能动态地变更服务节点。
    2.实现优雅关闭的功能。
    3.主动探测,心跳模式。
    4.要控制一组注册中心管理的服务集群的规模。
    5.扩容注册中心节点。
    6.规范一下对于注册中心的使用方式,如果只是变更某一个节点,那么只需要通知这个节点的变更信息即可。
    7.如果是自建的注册中心,你也可以在其中加入一些保护策略,比如说如果通知的消息量达到某一个阈值就停止变更通知。

    分布式组件慢请求,

    1.给同一个请求的每一行日志增加一个相同的标记,通过这个标记就能请求链路上所有步骤的耗时(在记录打点日志时,我们使用 requestId 将日志串起来,这样方便比较一次请求中的多个步骤的耗时情况)。
    2.我们使用静态代理的方式做切面编程,避免在业务代码中,加入大量打印耗时的日志的代码,减少了对于代码的侵入性,同时编译期的代码注入可以减少(一类是静态代理,典型的代表是 AspectJ,它的特点是在编译期做切面代码注入;另一类是动态代理,典型的代表是 Spring AOP,它的特点是在运行期做切面代码注入)。
    4.采用 traceId + spanId 这两个数据维度来记录服务之间的调用关系(这里 traceId 就是 requestId),也就是使用 traceId 串起单次请求,用 spanId 记录每一次 RPC 调用。
    5.为了避免在排查问题时需要到多台服务器上搜索日志,我们使用消息队列将日志集中起来放在了 Elasticsearch 中。
    6.增加了日志采样率,避免全量日志的打印。

    负载均衡,

    1.一类是代理类的负载均衡服务;另一类是客户端负载均衡服务。
    2.静态策略(轮询的策略,带有权重的轮询策略,Nginx 提供了 ip_hash 和 url_hash 算法,LVS 提供了按照请求的源地址和目的地址做 Hash 的策略,Dubbo 也提供了随机选取策略以及一致性 Hash 的策略)。
    3.动态策略(Dubbo 提供的 LeastAcive 策略,就是优先选择活跃连接数最少的服务,Spring Cloud 全家桶中的 Ribbon 提供了 WeightedResponseTimeRule 是使用响应时间给每个服务节点计算一个权重,然后依据这个权重,来给调用方分配服务节点,Gateway,Zuul)。

    API网关,

    1.一类叫做入口网关,一类叫做出口网关。
    2.作用:对调用外部的 API 做统一的认证、授权、审计以及访问控制。
    3.Netfix开源的API网关Zuul就是采用责任链模式;Kong是在Nginx中运行的Lua程序;Tyk是一种Go语言实现的轻量级API网关,有着丰富的插件资源,对于 Go 语言栈的团队来说,也是一种不错的选择。
    4.性能(I/O 模型);扩展性(随时在网关的执行链路上增加一些逻辑,也可以随时下掉一些逻辑(也就是所谓的热插拔));线程池, 线程隔离或者保护(使用多个线程池 (可以按照业务划分),一个线程池可以按照接口限制使用的线程数)。

    跨地域分布式部署,

    1.一个思路是直接跨机房读取A机房的从库;在机房B部署一个从库,跨机房同步主库的数据,然后机房 B 的应用就可以读取这个从库的数据了。
    2.避免数据跨机房同步调用,而只做异步的数据同步。
    3.同城双活(核心思想是尽量避免跨机房的调用;数据的更新可以更新双机房中的数据,保证数据的一致性;只订阅同机房的 RPC 服务组)。
    4.异地多活(不能太近,否则发生自然灾害时,很可能会波及;基于存储系统的主从复制,比如 MySQL 和 Redis。也就是在一个机房部署主库,在异地机房部署从库,两者同步主从复制实现数据的同步;基于消息队列的方式。一个机房产生写入请求后,会写一条消息到消息队列,另一个机房的应用消费这条消息后再执行业务处理逻辑,写入到存储服务中)。
    5.对用户做分片,让一个用户每次的读写都尽量在同一个机房中。同时,在数据读取和服务调用时,也要尽量调用本机房的服务。

    服务治理,

    1.服务之间的通信协议上,要对多语言友好,要想实现跨语言调用,关键点是选择合适的序列化方式(Protobuf;Thrift)。
    2.国内大厂开源出来的Service Mesh方案中,一般只借鉴Istio的数据平面和控制平面的思路,然后将服务治理策略做到了 Sidecar 中,控制平面只负责策略的下发,这样就不需要每次请求都经过控制平面,性能上会改善很多。
    3.使用iptables的方式来实现流量透明的转发,而Istio就默认了使用iptables来实现数据包的转发。
    4.轻量级客户端,RPC客户端会通过配置的方式,知道Sidecar的部署端口,然后通过一个轻量级客户端,将调用服务的请求发送给Sidecar。
    5.目前在探索的方案还有Cilium
    6.开源领域(lstio,Linkerd,SOFAMesh)。

    监控指标,

    1.延迟指的是请求的响应时间。
    2.通信量可以理解为吞吐量,也就是单位时间内请求量的大小。
    3.错误表示当前系统发生的错误数量。
    4.饱和度指的是服务或者资源到达上限的程度(也可以说是服务或者资源的利用率),比如CPU的使用率、内存使用率、磁盘使用率、缓存数据库的连接数等等。
    5.Agent是一种比较常见的采集数据指标的方式Memcached,JMX,另一种很重要的数据获取方式是在代码中埋点(分布式 Trace 组件中,提到的面向切面编程的方式;在资源客户端中直接计算调用资源或者服务的耗时、调用量,并且发送给监控服务器
    6.监控数据的处理(Spark、Storm)和存储(写入Elasticsearch,然后通过 Kibana 展示数据)监控数据存储在时序数据库有 InfluxDB、OpenTSDB、Graphite。
    7.访问趋势报表,性能报表,资源报表(通过 Grafana 来连接时序数据库,将监控数据绘制成报表)。
    8.这种用户网络监控的所有监控数据均来自客户端,是用户访问数据实时上报,因此能够准确、真实、实时地反映用户操作体验。
    9.它是我们性能优化的指向标,业务架构改造、服务性能优化、网络优化等任何优化行为时,可以反馈用户性能数据,引导业务正向优化接口性能、可用性等指标。
    10.它也能帮助我们监控 CDN 链路质量。


    一次请求过程经历,

    1.等待时间:异步调用时,请求会首先缓存在本地的队列里面,由专门的 I/O 线程负责,那么在 I/O 线程真正处理请求之前,会有一个等待的时间。
    2.DNS 时间:域名解析时间。
    3.握手时间:TCP 三次握手的时间。
    4.SSL 时间:如果服务是 HTTPS 服务,那么就会有一个 SSL 认证的时间。
    5.发送时间:请求包被发送出去的时间。
    6.首包时间:服务端处理后,客户端收到第一个响应包的时间。
    7.包接收时间:我们接收到所有数据的时间。

    全链路压测,

    1.压力测试指的是在高并发大流量下进行的测试,测试人员可以通过观察系统在峰值负载下的表现,从而找到系统中存在的性能隐患。
    2.流量的隔离;风险的控制;流量构造和产生模块;压测数据隔离模块;系统健康度检查和压测流量干预模块。
    3.拷贝系统高峰时期的流量(存储在 HBase, MongoDB)。
    4.直接拷贝负载均衡服务器的访问日志,数据就以文本的方式写入到流量数据工厂中。但是这样产生的数据在发起压测时,需要自己写解析的脚本来解析访问日志,会增加压测时候的成本,不太建议使用。
    5.通过一些开源的工具来实现流量的拷贝。推荐一款轻型的流量拷贝工具GoReplay
    6.读取数据的请求, 下行流量(Mock 服务);写入数据的请求, 上行流量(影子库)。

    配置管理,

    1.dirty_writeback_centisecs: 调整 Page Cache 中脏数据刷新到磁盘上的频率。
    2.tcp_max_syn_backlog: 调整未建立连接队列的长度。
    3.配置文件(git/nginx等);配置中心(携程开源的 Apollo、百度开源的 Disconf、360 开源的 QConf、Nacos、Spring Cloud 的组件 Spring Cloud Config 等等)。
    4.存储(Disconf、Apollo 使用的是 MySQL;QConf 使用的是 ZooKeeper;微博的配置中心使用 Redis 来存储信息;美图用的是 Etcd)。

    降级熔断,

    1.第一类原因是由于依赖的资源或者服务不可用,最终导致整体服务宕机。举例来说,在你的电商系统中就可能由于数据库访问缓慢,导致整体服务不可用;另一类原因是你们乐观地预估了可能到来的流量,当有超过系统承载能力的流量到来时,系统不堪重负,从而出现拒绝服务的情况。
    2.解决第一类问题的思路:降级和熔断;解决另一类问题的思路: 限流。
    3.检测到某一个服务的响应时间出现异常时,切断调用它的服务与它之间的联系,让服务的调用快速返回失败,从而释放这次请求持有的资源。这个思路也就是我们经常提到的降级和熔断机制。
    4.针对读取数据的场景,我们一般采用的策略是直接返回降级数据。
    5.对于一些轮询查询数据的场景,比如每隔 30 秒轮询获取未读数,可以降低获取数据的频率(将获取频率下降到 10 分钟一次)。
    6.对于写数据的场景,一般会考虑把同步写转换成异步写,这样可以牺牲一些数据一致性保证系统的可用性。

    流量控制,

    1.你可以对系统每分钟处理多少请求做出限制;
    2.可以针对单个接口设置每分钟请求流量的限制;
    3.可以限制单个 IP、用户 ID 或者设备 ID 在一段时间内发送请求的数量;
    4.对于服务于多个第三方应用的开放平台来说,每一个第三方应用对于平台方来说都有一个唯一的 appkey 来标识,那么你也可以限制单个 appkey 的访问接口的速率。
    5.固定窗口算法: 在实现它的时候,首先要启动一个定时器定期重置计数,比如你需要限制每秒钟访问次数,需要比较计数值是否大于阈值就可以了。
    6.滑动窗口算法: 这个算法的原理是将时间的窗口划分为多个小窗口,每个小窗口中都有单独的请求计数。滑动窗口的算法解决了临界时间点上突发流量无法控制的问题,但是却因为要存储每个小的时间窗口内的计数,所以空间复杂度有所增加。
    7.漏桶算法: 如果流入的流量在某一段时间内大增,超过了漏桶的承受极限,那么多余的流量就会触发限流策略,被拒绝服务。一般会使用消息队列作为漏桶的实现,流量首先被放入到消息队列中排队,由固定的几个队列处理程序来消费流量,如果消息队列中的流量溢出,那么后续的流量就会被拒绝。
    8.令牌桶算法: 如果我们需要在一秒内限制访问次数为 N 次,那么就每隔 1/N 的时间,往桶内放入一个令牌;在处理请求之前先要从桶中获得一个令牌,如果桶中已经没有了令牌,那么就需要等待新的令牌或者直接拒绝服务;桶中的令牌总数也要有一个限制,如果超过了限制就不能向桶中再增加新的令牌了。这样可以限制令牌的总数,一定程度上可以避免瞬时流量高峰的问题。
    9.漏桶与令牌桶的区别在于, 漏桶的流量始终是平稳的, 令牌桶允许突发流量。

    海量数据的计数器,

    1.数据量巨大;访问量大,对于性能的要求高;对于可用性、数字的准确性要求高。
    2.存储微博维度(微博的计数,转发数、点赞数等等)的数据,你可以这么设计表结构:以微博 ID 为主键,转发数、评论数、点赞数和浏览数分别为单独一列。
    3.数据量达到千万, 分库分表(选择一种哈希算法对 weibo_id 计算哈希值/按照 weibo_id 生成的时间来做分库分表)。
    4.全面使用 Redis 来作为计数的存储组件。
    5.提升写入性能,使用消息队列,注意能合并的要合并。
    6.降低计数系统存储成本,对原生Redis做一些改造,采用新的数据结构和数据类型来存储计数数据(一是原生的 Redis 在存储 Key 时是按照字符串类型来存储的,比如一个 8 字节的 Long 类型的数据,需要 8(sdshdr 数据结构长度)+ 19(8 字节数字的长度)+1(’\0’)=28 个字节,如果我们使用 Long 类型来存储就只需要 8 个字节,会节省 20 个字节的空间;二是去除了原生 Redis 中多余的指针,如果要存储一个 KV 信息就只需要 8(weibo_id)+4(转发数)=12 个字节,相比之前有很大的改进)。

    50万QPS未读数,

    1.可以记录一下在这个列表中每个人看过最后一条消息的 ID,然后统计这个 ID 之后有多少条消息,这就是未读数了。
    2.判断是否需要展示红点时,只需要判断用户的时间戳和全局时间戳的大小,如果用户时间戳小于全局时间戳,代表在用户最后一次点击红点之后又有新的红点推送,那么就要展示红点,反之,就不展示红点了。
    3.首先,在通用计数器中记录每一个用户发布的博文数;然后在 Redis 或者 Memcached 中记录一个人所有关注人的博文数快照,当用户点击未读消息重置未读数为 0 时,将他关注所有人的博文数刷新到快照中;这样,他关注所有人的博文总数减去快照中的博文总数就是他的信息流未读数。
    4.未读数,总之不基于统计, 而基于计算。

    通用信息流系统的推模式和拉模式,

    1.推模式是指用户发送一条微博后,主动将这条微博推送给他的粉丝,从而实现微博的分发,也能以此实现微博信息流的聚合(想要提升读取信息流的性能,可以把收件箱的数据存储在缓存里面,每次获取信息流的时候直接从缓存中读取就好了)。
    2.(使用消息队列)在消息处理上,你可以启动多个线程并行地处理微博写入的消息;由于消息流在展示时可以使用缓存来提升读取性能,所以我们应该尽量保证数据写入数据库的性能,必要时可以采用写入性能更好的数据库存储引擎(TokuDB 作为 MySQL 的存储引擎)。
    3.减少存储方案(先设计一张 Feed 表,这个表主要存储微博的基本信息,包括微博 ID、创建人的 ID、创建时间、微博内容、微博状态(删除还是正常)等等,它使用微博 ID 做哈希分库分表;另外一张表是用户的发件箱和收件箱表,也叫做 TimeLine 表(时间线表),主要有三个字段,用户 ID、微博 ID 和创建时间。它使用用户的 ID 做哈希分库分表)。
    4.拉模式彻底解决了推送延迟的问题,大 V 发微博的时候不再需要推送到粉丝的收件箱,自然就不存在延迟的问题了;存储成本大大降低了;功能扩展性更好了。
    5.查询和聚合(缓存/只缓存了每个用户最近 5 天发布的微博 ID)。
    6.带宽优化(缓存副本. 在部署缓存副本之后,请求会先查询副本中的数据,只有不命中的请求才会查询主缓存的数据)。
    7.推拉结合(核心在于大 V 用户在发布微博的时候,不再推送到全量用户,而是只推送给活跃的用户,不活跃用户采用拉方式)。