从码农到架构师---设计大型网站架构模式和核心要素

参考资料大型网站技术架构核心原理与案例分析(作者李智慧)
参考资料架构修炼之道(作者王新栋)

从码农到架构师---设计大型网站架构模式和核心要素_第1张图片

从码农到架构师---设计大型网站架构模式和核心要素_第2张图片

网关

@1-API网关的基本功能与七种措施
从码农到架构师---设计大型网站架构模式和核心要素_第3张图片
从码农到架构师---设计大型网站架构模式和核心要素_第4张图片
@2-如何设计一个API:

常用工具(设计API和生成API文档)

  • RAML和Swagger
  • RAML的五个过程
    Design->Build->Test->Document->Share
  • Swagger的五个过程
    Design->Build->Test->Document->Standardize(标准化)
  • 五个过程-设计(Design)
    见名知意,
    入参快速验证,设定业务规定错误码(非常明确)
    出参只公开必要的参数(防止透露过多信息)所以对外的DTO或者PO都需要在内部转换后对外输出.
  • 五个过程-构建(Build)
    根据设计好的约束和规范编程,尽早暴露非法不合格请求
  • 五个过程-文档(Document)
    注释清晰,入参和返回参数,方法作用和异常抛出明细
  • 五个过程-测试(Test)
    可维护性,可测试性,是否易于集成,是否功能完备
  • 五个过程-分享或者标准化(Share/Standardize)
    SDK服务API的使用者,可以使用上述工具例如Swagger生成SDK.
    生命周期的另外两个过程
  • 运行
    API监控::性能监控(TP99-TP999)/可用率监控/调用量监控 .统计报表:可用率达标率和性能达标率做好下一步优化
  • 下线
    上下游系统做好版本控制,做好系统功能兼容.如果完全不需要了,则下线通知功能.

@3-网关基础–泛化调用

普通的调用:
Consumer 添加jar包依赖通过maven管理或者gradle,利用本地仓库,私服(自己搭建的私服仓库比如用nexus),中央仓库Apache ,解析到 你的服务端provider提供的接口中.
缺点:如果现在作为一个分布式服务框架,提供的N个接口对接N个jar包,这样就导致维护的成本越来越高.
泛化调用:
不需要接口提供方的jar包,将参数和返回值中的所有POJO都用Map表示,然后提供泛化服务处理逻辑.
底层的原理跟普通的RPC调用是一致的,网络,序列化,反射这些底层的技术也一致.

@4-网关与RPC(如何发布API到网关系统)

1/解决API数据存储问题------redis
2/解决依赖服务问题-------泛化调用
从码农到架构师---设计大型网站架构模式和核心要素_第5张图片

@5-管道技术的实现与应用于网关

网关系统与管道技术的联系:

网关系统不做任何业务处理,可以将其比喻为高速公路(相当于管道),但是上高速公路之前需要安检,限流,客流是否超载等措施…
而对于接口而言,在网关系统需要对应 -参数校验.,黑白名单,限流控制,接口调用等功能串行化为一个个管道,按照顺序串联起来.

如何实现管道:

  • 每个管道代表一个功能,所以第一步定义一个pipe
  • 将每个Pipe包装成一个Task:new PipeTask(pipe),并且该Task实现了Runnable接口
  • 实现一个PipeTask,获取管道池
  • 将Task任务也就是第二步交给线程池处理,则可以遍历管道池将对应的管道一一执行.

设计模式的思维结构化:
管道模式其实来源于责任链模式的应用

@6-网关的高可用,高性能,可伸缩,可扩展,安全性

对于高流量,高并发系统,基本上符合如下特点:访问流量大,调用接口多,依赖系统多.
运行架构图如下:
从码农到架构师---设计大型网站架构模式和核心要素_第6张图片
监控系统指标参数如这篇文章
CPU

I/O请求线程和业务处理线程隔离技术实现

  • 网关的异步化思维与Servlet3实践结合
  • Servlet的特性与服务器tomcat有版本对应支持,不能误用
  • 同步/异步/阻塞/非阻塞
  • 同步/异步是数据通信的方式
  • 阻塞/非阻塞是通信方式中的状态
  • Servlet3在服务器tomcat中的实践流程图
    从码农到架构师---设计大型网站架构模式和核心要素_第7张图片
  • Servlet3异步使用步骤
  • 实现上下文监听器ServletContextListener为AppContextListener(初始化下面用到的业务线程池)并声明注解@WebListener
    eg:在该AppContextListener监听器中,创建业务工作线程池,并将业务工作线程池放入ServletContextEvent事件的ServletContext对象中.而这个ServletContext其实就是下文中的AsyncRunningServlet
    eg:监听器中初始化
    eg:ThreadPoolExecutor executor = new ThreadPoolExecutor(100,200,50000L,TimeUnit.MILLISECONDS,new ArrayBlockingQueue泛型Runnable(100));
    eg:servletCOntextEvent.getServletContext().setAttribute(“executor”,executor);
    eg:监听器中销毁
    eg:ThreadPoolExecutor executor = (ThreadPoolExecutor) request.getServletContext().getAttribute(“executor”);
    eg:executor.shutDown();
  • 实现个类继承HttpServlet假设为AsyncRunningServlet异步处理任务,并用注解声明WebServlet,增加asyncSupported属性,开启异步支持,路径设为AsyncRunningServlet
    eg:@WebServlet(urlPatterns = “/AsyncRunningServlet”,asyncSupported =true)
  • 通过request获取异步上下文AsyncContext.
    eg:AsyncContext asyncCtx=request.startAsync();
  • 添加最终接受返回结果的异步监听器(可以通过实现AsyncListener接口实现假设类为AppAsyncListener=)到异步上下文中
    eg:asyncCtx.addListener(new MyAsyncListener)
  • 获取业务工作线程池,开启业务逻辑处理线程,并将AsyncContext传递给业务线程
    eg:ThreadPoolExecutor executor = (ThreadPoolExecutor) request.getServletContext().getAttribute(“executor”);
    eg:executor.execute(new AsyncRequestProcessor(asyncCtx,secs))
  • 在异步业务逻辑处理线程中,通过asyncContext获取request和response,处理对应的业务
  • 业务逻辑处理线程处理完成之后调用AsyncContext的complete方法:结束异步线程处理.

CPU利用率

  • 显示程序在运行期间实时占用的CPU百分比,
  • CPU打满100%的危害与排查-------->雪崩效应.
    数据库:::实际案例如果数据库参考这篇文章的问题
    应用系统:::如果应用CPU由于大访问量请求,或者超时MQ拉取机制,或者其他应用超时连接.总之线程数突然达到系统能承受最大值,则CPU将不再有资源时间分片处理新的请求,则直接挂掉宕机.

CPU负载

  • 显示一段时间内正在使用和等待使用CPU的平均任务数
  • 通过uptime或者top命令查看

磁盘

磁盘使用率和磁盘负载百分比
防止日志使用不到,以及异常出现大量I/O读写磁盘,将磁盘资源耗尽.严格使用日志级别.
由于线上我这里出现过类似问题,我采用了异常开启核心日志.临时可控关键路径日志.
日志不能多,但是当评审代码如果未发现问题上线后出现异常,很难快速定位尤其是大量数据的情况下,异步处理.发现问题的时候已经是好几天之后.所以一旦出现异常,自动开启关键路径日志,并触发报警机制,及时人工介入将危害降至最低.

网络
网络抖动,不稳定,如果应用系统本身没有问题,则网络将是最重要的一个因素.
我在生产环境也曾遇到过一次网络问题导致请求无法复现的问题.而这个问题由于异常日志未能关键捕获直接导致最终无>>>法完全定位根本原因.但是排查的原因最终结果导向为网络,是该事件发生的最特殊因素.


全异步网关非阻塞I/O对于网关系统的应用.
线程数不再成为网关系统瓶颈,慢API也不会引起网关系统的不稳定.但是随之带来缺点,易于调试增加编程复杂度
从码农到架构师---设计大型网站架构模式和核心要素_第8张图片
补充知识点I/O知识:
I/O多路复用技术:
从码农到架构师---设计大型网站架构模式和核心要素_第9张图片
I/O多路复用概述:

I/O多路复用中的I/O是指网络I/O,多路指多个TCP连接(或多个channel),复用指复用一个或少量线程.
多路复用:多个网络I/O复用一个或少量的线程来处理这些链接.

@6.1用户进程和内核
从码农到架构师---设计大型网站架构模式和核心要素_第10张图片

@6.2select和recvfrom

  • Selector的原理:
    (1)向Selector注册连接,读写3种I/O事件
    (2)开始轮训Selector中的selectKey集合
    (3)从selectKey集合中通过key找到对应的Channel,然后使用Handler对象读写事先绑定的buffer
    从码农到架构师---设计大型网站架构模式和核心要素_第11张图片
  • Recvfrom:
    一般用于UDP协议,如果TCP中,调用Connect函数后,也可以使用它用于从(已连接)套接口上接收数据并捕获数据发送源地址

@6.3阻塞和非阻塞
从码农到架构师---设计大型网站架构模式和核心要素_第12张图片

@6.4Tomcat中的NIO
tomcat6开始支持NIO模型,实现是基于JDK的java.nio包.
tomcat7人为可以在Connector节点中配置,protocol=“org.apache.coyote.http11.Http11NioProtocol”
tomcat8.5+无BIO的connector,如果显式配置,则会警告.
从码农到架构师---设计大型网站架构模式和核心要素_第13张图片
它一共包含LimitLatch、Acceptor、Poller、SocketProcessor、Excutor5个部分。LimitLatch是连接控制器,它负责维护连接数的计算,nio模式下默认是10000,达到这个阈值后,就会拒绝连接请求。Acceptor负责接收连接,默认是1个线程来执行,将请求的事件注册到事件列表。有Poller来负责轮询,Poller线程数量是cpu的核数Math.min(2,Runtime.getRuntime().availableProcessors())。由Poller将就绪的事件生成SocketProcessor同时交给Excutor去执行。Excutor线程池的大小就是我们在Connector节点配置的maxThreads的值。在Excutor的线程中,会完成从socket中读取http request,解析成HttpServletRequest对象,分派到相应的servlet并完成逻辑,然后将response通过socket发回client。在从socket中读数据和往socket中写数据的过程,并没有像典型的非阻塞的NIO的那样,注册OP_READ或OP_WRITE事件到主Selector,而是直接通过socket完成读写,这时是阻塞完成的,但是在timeout控制上,使用了NIO的Selector机制,但是这个Selector并不是Poller线程维护的主Selector,而是BlockPoller线程中维护的Selector,称之为辅Selector。
-从码农到架构师---设计大型网站架构模式和核心要素_第14张图片从码农到架构师---设计大型网站架构模式和核心要素_第15张图片
从码农到架构师---设计大型网站架构模式和核心要素_第16张图片
小结:对于NIO的优势实际体现在其能够更好的最大化使用CPU.大量维护长连接的应用里更适合于I/O复用模型的NIO.

@7-网关的脱库和多级缓存

什么是脱库:

先讲下我这里的场景:最高时候数据量3亿,日常大促几千万左右.目前是分库分表,目前数据量20亿左右,我拆分512张表,8个 分片逻辑库,配置16CPU,8G内存,512GB硬盘.有个超过12亿数据量的日志表中,最基本的插入SQL会成为慢SQL,为什么?因 为在最简单的插入操作的时候,也需要重新建索引,换句话说, 当数据量大的时候,平时日常不是问题的,都会变成问题SQL,当 并发上来的时候成为灾难,直接打满CPU,耗尽资源.当然数据量的评估选型存储方案的时候就会选择大数据量平台,比如 Hbase 等

比如还有最简单的count语句.如下这篇文章

如何脱库: 从数据库依赖剥离出去,以前的架构可能是MySQL集群,分库分表对于一定量级的数据量是可以抗住的.比如我这里最简单的查询操作能达到QPS:8K.每张表500w数据左右,但是当达到每张表千万级别以上,慢SQL会一直层出不穷,不会根治.然后针对数据结构方式不复杂的情况下,redis就会成为一个比较好的方案.redis利用集群和RDB和AOF对于系统数据不是要求百分百重要的情况下就是非常合适的方案.

多级缓存:
简单作为高查询数据库的缓存抗流量方案.但是需要注意缓存穿透率,防止缓存穿透,雪崩
直接当数据库使用,缓存持久化,不设置过期时间.
这里要说的 多级缓存 数据异构. 将MySQL中的binlog日志,通过使用数据异构系统将数据推送给缓存,一级缓存可以用JVM的cache.如下架构图
从码农到架构师---设计大型网站架构模式和核心要素_第17张图片

数据异构方向:

  • DB-DB,DB-redis,DB-ES,DB-hbase
    数据异构的常用方法:
  • 完整克隆(离线统计)
  • 标记同步(日志数据)
  • binlog方式(通过MQ订阅这些日志,通过数据异构到es或者其他系统)–canal+MQ方式来保证消费,并用全量同步方式来保证最终一致性
  • MQ方式(写入DB并发送MQ,简单业务使用,很难保证数据一致性)

@8-热更新
在不停止应用的情况下,使用MQ方式,RPC方式,Zookeeper方式来达到修改应用的变量值实现热更新.

微服务

@1-微服务特点

  • 一套小型服务
  • 以独立的进程运行
  • 轻量级的通信
  • 围绕业务能力去构建
  • 可以独立自动部署
  • 可以使用不同的语言
  • 可以使用不同的数据存储技术

@2-微服务下的拆库

从码农到架构师---设计大型网站架构模式和核心要素_第18张图片

容错

@1-降级与限流

降级:利用托底数据避免暴力降级
限流:

分布式限流

  • 计数器(利用redis的incr原子自增操作)
  • 限速器(通过计数器来实现限速器控制请求的次数)
  • 限流的维度
  • 流量包:类似于阿里云或者其他网站我们买的每日单词请求接口数.不同价格不同的流量请求次数.
  • 令牌桶:分布式令牌桶:redis+lua
  • 漏桶

单机限流

  • 令牌桶:桶容器,放入令牌的速度,和消耗令牌的速度
  • 漏桶:强制平滑限流利用Guava的SmoothWarmingUp.
  • 信号量等

@2-线程池隔离
依赖于网络访问的服务,采用线程池隔离,防止单个环节的服务超时不可用导致整体不可用
依赖于内存缓存的服务,采用信号量隔离
熔断技术实现Hystrix和Sentinel

你可能感兴趣的:(职场@架构师)