微服务知识概括

分布式session

cookie和session的区别和联系:

  • cookie是本地客户端用来存储少量数据信息的,保存在客户端,用户能够很容易的获取,安全性不高,存储的数据量小
  • session是服务器用来存储部分数据信息,保存在服务器,用户不容易获取,安全性高,储存的数据量相对大,存储在服务器,会占用一些服务器资源,但是对于它的优点来说,这个缺点可以忽略了

session有什么用:

  • 在一次客户端和服务器为之间的会话中,客户端(浏览器)向服务器发送请求,首先cookie会自动携带上次请求存储的数据(JSESSIONID)到服务器,服务器根据请求参数中的JSESSIONID到服务器中的session库中查询是否存在此JSESSIONID的信息,如果存在,那么服务器就知道此用户是谁,如果不存在,就会创建一个JSESSIONID,并在本次请求结束后将JSESSIONID返回给客户端,同时将此JSESSIONID在客户端cookie中进行保存
  • 客户端和服务器之间是通过http协议进行通信,但是http协议是无状态的,不同次请求会话是没有任何关联的,但是优点是处理速度快
  • session是一次浏览器和服务器的交互的会话,当浏览器关闭的时候,会话就结束了,但是会话session还在,默认session是还保留30分钟的

分布式session一致性:

  • 客户端发送一个请求,经过负载均衡后该请求会被分配到服务器中的其中一个,由于不同服务器含有不同的web服务器(例如Tomcat),不同的web服务器中并不能发现之前web服务器保存的session信息,就会再次生成一个JSESSIONID,之前的状态就会丢失

4种分布式session解决方案:

  • 方案一:客户端存储
    ①直接将信息存储在cookie中
    ②cookie是存储在客户端上的一小段数据,客户端通过http协议和服务器进行cookie交互,通常用来存储一些不敏感信息
    缺点:
    <1>数据存储在客户端,存在安全隐患
    <2>cookie存储大小、类型存在限制
    <3>数据存储在cookie中,如果一次请求cookie过大,会给网络增加更大的开销
  • 方案二:session复制
    ①session复制是小型企业应用使用较多的一种服务器集群session管理机制,在真正的开发使用的并不是很多,通过对web服务器(例如Tomcat)进行搭建集群。
    缺点:
    <1>session同步的原理是在同一个局域网里面通过发送广播来异步同步session的,一旦服务器多了,并发上来了,session需要同步的数据量就大了,需要将其他服务器上的session全部同步到本服务器上,会带来一定的网路开销,在用户量特别大的时候,会出现内存不足的情况
    ③优点:
    <1>服务器之间的session信息都是同步的,任何一台服务器宕机的时候不会影响另外服务器中session的状态,配置相对简单
    <2>Tomcat内部已经支持分布式架构开发管理机制,可以对tomcat修改配置来支持session复制,在集群中的几台服务器之间同步session对象,使每台服务器上都保存了所有用户的session信息,这样任何一台本机宕机都不会导致session数据的丢失,而服务器使用session时,也只需要在本机获取即可
    ④如何配置:
    <1>在Tomcat安装目录下的config目录中的server.xml文件中,将注释打开,tomcat必须在同一个网关内,要不然收不到广播,同步不了session
    <2>在web.xml中开启session复制:
  • 方案三:session绑定
    ①Nginx是一款自由的、开源的、高性能的http服务器和反向代理服务器
    ②Nginx能做反向代理、负载均衡、http服务器(动静代理)、正向代理
    ③我们利用nginx的反向代理和负载均衡,之前是客户端会被分配到其中一台服务器进行处理,具体分配到哪台服务器进行处理还得看服务器的负载均衡算法(轮询、随机、ip-hash、权重等),但是我们可以基于nginx的ip-hash策略,可以对客户端和服务器进行绑定,同一个客户端就只能访问该服务器,无论客户端发送多少次请求都被同一个服务器处理
    缺点:容易造成单点故障,如果有一台服务器宕机,那么该台服务器上的session信息将会丢失 前端不能有负载均衡,如果有,session绑定将会出问题
    ⑤优点:配置简单
    ⑥在nginx安装目录下的conf目录中的nginx.conf文件
upstream aaa {
	Ip_hash;
	server 39.105.59.4:8080;
	Server 39.105.59.4:8081;
}
server {
	listen 80;
	server_name www.wanyingjing.cn;
	#root /usr/local/nginx/html;
	#index index.html index.htm;
	location / {
		proxy_pass http:39.105.59.4;
		index index.html index.htm;
	}
}
  • 方案四:基于redis存储session方案
    ①基于redis存储session方案流程示意图
    在这里插入图片描述
    ②优点:
    <1>这是企业中使用的最多的一种方式
    <2>spring为我们封装好了spring-session,直接引入依赖即可
    <3>数据保存在redis中,无缝接入,不存在任何安全隐患
    <4>redis自身可做集群,搭建主从,同时方便管理
    缺点:多了一次网络调用,web容器需要向redis访问
    ④总结:一般会将web容器所在的服务器和redis所在的服务器放在同一个机房,减少网络开销,走内网进行连接
    ⑤配置:
-------------引入pom依赖:-------------
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-session-data-redis</artifactId>
</dependency>
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-data-starter-redis</artifactId>
</dependency>


-------------配置redis:--------------

#redis数据库索引(默认是0)
spring.redis.database=0
spring.redis.host=127.0.0.1
spring.redis.port=6379
#默认密码为空
spring.redis.password=
#连接池最大连接数(负数表示没有限制)
spring.redis.jedis.pool.max-active=1000
#连接池最大阻塞等待时间(负数表示没有限制)
spring.redis.jedis.pool.max-wait=-1ms
#连接池中的最大空闲连接
spring.redis.jedis.pool.max-idle=10
#连接池中的最小空闲连接
spring.redis.jedis.pool.min-idle=2
#连接超时时间(毫秒)
spring.redis.timeout=500ms

分布式ID

分布式自增ID:

  • 方案一:通过中间件方式,可以是把数据库或者redis缓存作为媒介,从中间件获取ID。这种呢,优点是可以体现全局的递增趋势(优点只能想到这个),缺点呢,倒是一大堆,比如,依赖中间件,假如中间件挂了,就不能提供服务了;依赖中间件的写入和事务,会影响效率;数据量大了的话,你还得考虑部署集群,考虑走代理。这样的话,感觉问题复杂化了。
    ①基于redis生成全局id策略,因为Redis是单线的天生保证原子性,可以使用原子性操作INCR和INCRBY来实现,注意在Redis集群情况下,同MySQL一样需要设置不同的增长步长,同时key一定要设置有效期,可以使用Redis集群来获取更高的吞吐量
  • 方案二:通过UUID的方式,java.util.UUID就提供了获取UUID的方法,使用UUID来实现全局唯一ID,优点是操作简单,也能实现全局唯一的效果,缺点呢,就是不能体现全局视野的递增趋势;太长了,UUID是32位,有点浪费;最重要的,是插入的效率低,因为呢,我们使用mysql的话,一般都是B+tree的结构来存储索引,假如是数据库自带的那种主键自增,节点满了,会裂变出新的节点,新节点满了,再去裂变新的节点,这样利用率和效率都很高。而UUID是无序的,会造成中间节点的分裂,也会造成不饱和的节点,插入的效率自然就比较低下了。
  • 方案四:通过snowflake算法如下:SnowFlake算法生成id的结果是一个64bit大小的整数,它的结构如下图:
    在这里插入图片描述

数据库层微服务方案

mysql分布式方案应用层好还是中间层好:

  • 应用层分离:控制上的灵活度更高,实现也简单,随时可以调整策略。但带来的问题就是增加应用层的复杂性以及额外的开发工作,均衡设置、安全防护、分流什么的,不太好加强。适合中小型项目吧,毕竟到后期的扩容方面有点麻烦。
    解决方案有sharding-jdbc等等
  • 中间层分离:专业的事还是专业的proxy来负责,应用层专心做应用层的事,中间层按规则做读写的分离。扩容均衡起来得心应手,连接池、健康切换,这样都是应用层无法实现的。适合大型超大型项目。
    解决方案有MyCat,Atlas等等。

MyCat简单介绍:

  • MyCat是一个开源的分布式数据库系统,是一个实现了MySQL协议的服务器,前端用户可以把它看作是一个数据库代理(类似于Mysql Proxy),用MySQL客户端工具和命令行访问,而其后端可以用MySQL原生协议与多个MySQL服务器通信,也可以用JDBC协议与大多数主流数据库服务器通信,其核心功能是分表分库,即将一个大表水平分割为N个小表,存储在后端MySQL服务器里或者其他数据库里。
  • Mycat发展到现在,适用的场景已经很丰富,而且不断有新用户给出新的创新性的方案,以下是几个典型的应用场景:
    单纯的读写分离,此时配置最为简单,支持读写分离,主从切换;
    分表分库,对于超过1000万的表进行分片,最大支持1000亿的单表分片;
    ③多租户应用,每个应用一个库,但应用程序只连接Mycat,从而不改造程序本身,实现多租户化;
    ④报表系统,借助于Mycat的分表能力,处理大规模报表的统计;
    ⑤替代Hbase,分析大数据;
  • 此外代理层解决方案还有360的Atlas
  • 链接:MyCat详解
    在这里插入图片描述

sharding-jdbc简单介绍:

  • sharding-jdbc定位为轻量级Java框架,在Java的JDBC层提供的额外服务。它使用客户端直连数据库,以jar包形式提供服务,无需额外部署和依赖,可理解为增强版的JDBC驱动,完全兼容JDBC和各种ORM框架。
    适用于任何基于Java的ORM框架,如:JPA, Hibernate, Mybatis, Spring JDBC Template或直接使JDBC。
    基于任何第三方的数据库连接池,如:DBCP, C3P0, BoneCP, Druid, HikariCP等。
    支持任意实现JDBC规范的数据库。目前支持MySQL,Oracle,SQLServer和PostgreSQL。
  • 功能列表:
    数据分片
    <1>分库 & 分表
    <2>读写分离
    <3>分片策略定制化
    <4>无中心化分布式主键
    分布式事务
    <1>标准化事务接口
    <2>XA强一致事务
    <3>柔性事务
    数据库治理
    <1>配置动态化
    <2>编排 & 治理
    <3>数据脱敏
    <4>可视化链路追踪
    <5>弹性伸缩(规划中)

微服务监控方案

两种监控工具的历史简介:

  • prometheus:Kubernetes自从2012年开源以来便以不可阻挡之势成为容器领域调度和编排的领头羊,Kubernetes是Google Borg系统的开源实现,于此对应Prometheus则是Google BorgMon的开源实现。Prometheus是由SoundCloud开发的开源监控报警系统和时序列数据库。从字面上理解,Prometheus由两个部分组成,一个是监控报警系统,另一个是自带的时序数据库(TSDB)。2016年,由Google发起的Linux基金会旗下的原生云基金会(Cloud Native Computing Foundation)将Prometheus纳入其第二大开源项目。Prometheus在开源社区也十分活跃,在GitHub上拥有两万多Star,并且系统每隔一两周就会有一个小版本的更新,而Prometheus与它的“师兄”Kubernetes都自带云原生的光环,天然能够友好协作。
  • zabbix:zabbix官方的发行版本时间可以追朔到2012年,时间上比prometheus早了四年,Zabbix是由Alexei Vladishev开源的分布式监控系统,是一个企业级的分布式开源监控方案。能够监控各种网络参数以及服务器健康性和完整性的软件。使用灵活的通知机制,允许用户为几乎任何事件配置基于邮件的告警。这样可以快速反馈服务器的问题。基于已存储的数据,提供了出色的报告和数据可视化功能。

架构对比:

  • Prometheus:
    ①Prometheus的基本原理是通过HTTP周期性抓取被监控组件的状态,任意组件只要提供对应的HTTP接口并且符合Prometheus定义的数据格式,就可以接入Prometheus监控。
    ②Prometheus Server负责定时在目标上抓取metrics(指标)数据并保存到本地存储里面。Prometheus采用了一种Pull(拉)的方式获取数据,不仅降低客户端的复杂度,客户端只需要采集数据,无需了解服务端情况,而且服务端可以更加方便的水平扩展。
    ③如果监控数据达到告警阈值Prometheus Server会通过HTTP将告警发送到告警模块alertmanger,通过告警的抑制后触发邮件或者webhook。Prometheus支持PromQL提供多维度数据模型和灵活的查询,通过监控指标关联多个tag的方式,将监控数据进行任意维度的组合以及聚合。
  • Zabbix:
    ①zabbix由2部分构成,zabbix server与可选组件zabbix agent。zabbix server可以通过SNMP,zabbix agent,ping,端口监视等方法提供对远程服务器/网络状态的监视,数据收集等功能,它可以运行在Linux,Solaris,HP-UX,AIX,Free BSD,Open BSD,OS X等平台上。
    ②核心组件主要是Agent和Server,其中Agent主要负责采集数据并通过主动或者被动的方式采集数据发送到Server/Proxy,除此之外,为了扩展监控项,Agent还支持执行自定义脚本。Server主要负责接收Agent发送的监控信息,并进行汇总存储,触发告警等。Zabbix Server将收集的监控数据存储到Zabbix Database中。Zabbix Database支持常用的关系型数据库,如果MySQL、PostgreSQL、Oracle等,默认是MySQL,并提供Zabbix Web页面(PHP编写)数据查询。
    ③Zabbix由于使用了关系型数据存储时序数据,所以在监控大规模集群时常常在数据存储方面捉襟见肘。所以从Zabbix 4.2版本后开始支持TimescaleDB时序数据库,不过目前成熟度还不高。

综合对比:
在这里插入图片描述

  • 综合比对:如上面的表格,从开发语言上看,为了应对高并发和快速迭代的需求,监控系统的开发语言已经慢慢从C语言转移到Go。不得不说,Go凭借简洁的语法和优雅的并发,在Java占据业务开发,C占领底层开发的情况下,准确定位中间件开发需求,在当前开源中间件产品中被广泛应用。从系统成熟度上看,Zabbix是老牌的监控系统:Zabbix是在1998年就出现的,系统功能比较稳定,成熟度较高。而Prometheus是最近几年才诞生的,虽然功能还在不断迭代更新,但站在巨人的肩膀之上,在架构设计上借鉴了很多老牌监控系统的经验;从数据存储方面来看,Zabbix采用关系数据库保存,这极大限制了Zabbix采集的性能,而Prometheus自研一套高性能的时序数据库,在V3版本可以达到每秒千万级别的数据存储,通过对接第三方时序数据库扩展历史数据的存储;从配置复杂度上看,Prometheus只有一个核心server组件,一条命令便可以启动,相比而言,其他系统配置相对麻烦,从社区活跃度上看,目前Zabbix比较活跃,但基本都是国内的公司参与,Prometheus在这方面占据绝对优势,社区活跃度虽然不如,但是受到CNCF的支持,后期的发展值得期待;从容器支持角度看,由于Zabbix出现得比较早,当时容器还没有诞生,自然对容器的支持也比较差。而Prometheus的动态发现机制,不仅可以支持swarm原生集群,还支持Kubernetes容器集群的监控,是目前容器监控最好解决方案。
  • 总结:综合来看,Zabbix 的成熟度更高,上手更快,但更好的集成导致灵活性较差,问题更大是,监控数据的复杂度增加后,Zabbix 做进一步定制难度很高,即使做好了定制,也没法利用之前收集到的数据了(关系型数据库造成的问题)。Prometheus 基本上是正相反,上手难度大一些,但由于定制灵活度高,数据也有更多的聚合可能,起步后的使用难度远小于 Zabbix。但如果已经对传统监控系统有技术积累的话,还是要谨慎考虑更换监控。
  • 结论:如果监控的是物理机,用 Zabbix 没毛病,Zabbix在传统监控系统中,尤其是在服务器相关监控方面,占据绝对优势。甚至环境变动不会很频繁的情况下,Zabbix 也会比 Prometheus 好使;但如果是云环境的话,除非是 Zabbix 玩的非常溜,可以做各种定制,否则还是 Prometheus 吧,毕竟人家就是干这个的。Prometheus开始成为主导及容器监控方面的标配,并且在未来可见的时间内被广泛应用。如果是刚刚要上监控系统的话,不用犹豫了,Prometheus 准没错。

Grafana的介绍与使用:

  • Grafana是一款用Go语言开发的开源数据可视化工具,可以做数据监控和数据统计,带有告警功能。目前使用grafana的公司有很多,如paypal、ebay、intel等。
  • 七大特点:
    ①可视化:快速和灵活的客户端图形具有多种选项。面板插件为许多不同的方式可视化指标和日志。
    ②报警:可视化地为最重要的指标定义警报规则。Grafana将持续评估它们,并发送通知。
    ③通知:警报更改状态时,它会发出通知。接收电子邮件通知。
    ④动态仪表盘:使用模板变量创建动态和可重用的仪表板,这些模板变量作为下拉菜单出现在仪表板顶部。
    ⑤混合数据源:在同一个图中混合不同的数据源!可以根据每个查询指定数据源。这甚至适用于自定义数据源。
    ⑥注释:注释来自不同数据源图表。将鼠标悬停在事件上可以显示完整的事件元数据和标记。
    ⑦过滤器:过滤器允许您动态创建新的键/值过滤器,这些过滤器将自动应用于使用该数据源的所有查询。

微服务全文搜索方案

Lucene:

  • Lucene是apache下的一个子项目,是一个开放源代码的全文检索引擎工具包,但它不是一个完整的全文检索引擎,而是一个全文检索引擎的架构,提供了完整的查询引擎和索引引擎,部分文本分析引擎。

Solr:

  • Solr是一个高性能,采用Java5开发,基于Lucene的全文搜索服务器。
  • 同时对其进行了扩展,提供了比Lucene更为丰富的查询语言,同时实现了可配置、可扩展并对查询性能进行了优化,并且提供了一个完善的功能管理界面,是一款非常优秀的全文搜索引擎。
  • Solr的优缺点:
    ①优点
    <1>Solr有一个更大、更成熟的用户、开发和贡献者社区。
    <2>支持添加多种格式的索引,如:HTML、PDF、微软 Office 系列软件格式以及 JSON、XML、CSV 等纯文本格式。
    <3>Solr比较成熟、稳定。
    <4>不考虑建索引的同时进行搜索,速度更快。
    ②缺点:
    <1>建立索引时,搜索效率下降,实时索引搜索效率不高。

Elasticsearch:

  • Elasticsearch跟Solr一样,也是一个基于Lucene的搜索服务器,它提供了一个分布式多用户能力的全文搜索引擎,基于RESTfulweb接口。
  • Elasticsearch的优缺点:
    ①优点:
    <1>Elasticsearch是分布式的。不需要其他组件,分发是实时的,被叫做"Push replication"。
    <2>Elasticsearch 完全支持 Apache Lucene 的接近实时的搜索。
    <3>处理多租户(multitenancy)不需要特殊配置,而Solr则需要更多的高级设置。
    <4>Elasticsearch 采用 Gateway 的概念,使得完备份更加简单。
    <5>各节点组成对等的网络结构,某些节点出现故障时会自动分配其他节点代替其进行工作。
    ②缺点:
    <1>只有一名开发者(当前Elasticsearch GitHub组织已经不只如此,已经有了相当活跃的维护者)
    <2>还不够自动(不适合当前新的Index Warmup API)

Elasticsearch 与 Solr 的比较:

  • 二者安装都很简单;
  • Solr 利用 Zookeeper 进行分布式管理,而 Elasticsearch 自身带有分布式协调管理功能;
  • Solr 支持更多格式的数据,而 Elasticsearch 仅支持json文件格式;
  • Solr 官方提供的功能更多,而 Elasticsearch 本身更注重于核心功能,高级功能多有第三方插件提供;
  • Solr 在传统的搜索应用中表现好于 Elasticsearch,但在处理实时搜索应用时效率明显低于 Elasticsearch。
  • Solr 是传统搜索应用的有力解决方案但 Elasticsearch 更适用于新兴的实时搜索应用。

为什么要用全文搜索引擎:

  • 全文搜索引擎胜在快速和高效的查询大批量非结构化的文本—文档或者其他包好自由结构文本的记录,并且返回这些基于用于搜索匹配的结果文档,他们可以根据具体的数值或者字段去进行快速高效的排序、分类等。一个系统的全文搜索功能应该是丰富并且灵活的,并且还需要支持基本关键字的查询:互联网式+/-语法,布尔运算符的使用,有限的真实或伪自然语言处理,邻近操作,查找类似等。关联排序功能定义了一个查询操作的最好匹配结果。在数据库中,他们通常会作为一个整体:文档中邻近的查询术语的接近度,特定术语,字段或文档的特殊权重等等。
  • 什么时候使用全文搜索引擎?
    ①大量的自由结构的文本数据(或包含此类数据的记录)要搜索或 方面/分类 - 数十万或数百万个文件/记录(或更多)。
    ②支持大量基于交互式文本的查询。
    ③需求非常灵活的全文搜索查询。
    ④对高度相关的搜索结果的需求未被可用的关系数据库所满足。
    ⑤对不同记录类型,非文本数据操作或安全事务处理的需求相对较少。
  • 链接:为什么要用全文搜索引擎:全文搜索引擎 VS 数据库管理系统

SpringCloud分布式锁组件

分布式锁简介:

  • Spring Cloud的分布式架构也会带来了线程安全问题,比如一个商城系统,下单过程可能由不同的微服务协作完成,在高并发的情况下如果不加锁就会有问题,而传统的加锁方式只针对单一架构,对于分布式架构是不适合的,这时就需要用到分布式锁。
  • 实现分布式锁的方式有很多,目前几种较为流行的分布式锁方案:
    基于 Redis 的分布式锁
    基于数据库的分布式锁
    基于 Zookeeper 的分布式锁
  • 链接:SpringCloud认识五之分布式锁

分布式锁先详解:

  • 基于 Redis 的分布式锁:利用 SETNX 和 SETEX。
    ①基本命令主要有:
    <1>SETNX(SET If Not Exists):当且仅当 Key 不存在时,则可以设置,否则不做任何动作。
    <2>SETEX:可以设置超时时间
  • 其原理为:通过 SETNX 设置 Key-Value 来获得锁,随即进入死循环,每次循环判断,如果存在 Key 则继续循环,如果不存在Key,则跳出循环,当前任务执行完成后,删除 Key 以释放锁。
    这种方式可能会导致死锁,为了避免这种情况,需要设置超时时间。
  • 基于数据库的分布式锁:基于数据库表
    ①它的基本原理和 Redis 的 SETNX 类似,其实就是创建一个分布式锁表,加锁后,我们就在表增加一条记录,释放锁即把该数据删掉,具体实现,我这里就不再一一举出。
    ②它同样存在一些问题:
    <1>没有失效时间,容易导致死锁;
    <2>依赖数据库的可用性,一旦数据库挂掉,锁就马上不可用;
    <3>这把锁只能是非阻塞的,因为数据的 insert 操作,一旦插入失败就会直接报错。没有获得锁的线程并不会进入排队队列,要想再次获得锁就要再次触发获得锁操作;
    <4>这把锁是非重入的,同一个线程在没有释放锁之前无法再次获得该锁。因为数据库中数据已经存在了。
  • 基于 Zookeeper 的分布式锁:ZooKeeper 是一个分布式的,开放源码的分布式应用程序协调服务,是 Google Chubby 的一个开源实现,是 Hadoop 和 Hbase 的重要组件。它是一个为分布式应用提供一致性服务的软件,提供的功能包括:配置维护、域名服务、分布式同步、组服务等。
  • 分布式锁实现原理:
    ①建立一个节点,假如名为 lock 。节点类型为持久节点(Persistent)
    ②每当进程需要访问共享资源时,会调用分布式锁的 lock() 或 tryLock() 方法获得锁,这个时候会在第一步创建的 lock 节点下建立相应的顺序子节点,节点类型为临时顺序节点(EPHEMERAL_SEQUENTIAL),通过组成特定的名字 name+lock+顺序号。
    ③在建立子节点后,对 lock 下面的所有以 name 开头的子节点进行排序,判断刚刚建立的子节点顺序号是否是最小的节点,假如是最小节点,则获得该锁对资源进行访问。
    ④假如不是该节点,就获得该节点的上一顺序节点,并监测该节点是否存在注册监听事件。同时在这里阻塞。等待监听事件的发生,获得锁控制权。
    ⑤当调用完共享资源后,调用 unlock() 方法,关闭 ZooKeeper,进而可以引发监听事件,释放该锁。

分布式锁总结:

  • 通过数据库实现分布式锁是最不可靠的一种方式,对数据库依赖较大,性能较低,不利于处理高并发的场景。
  • 通过 Redis 的 Redlock 和 ZooKeeper 来加锁,性能有了比较大的提升。

nginx与微服务网关与负载均衡方案区别

简介:

  • Nginx是服务器常用来做负载均衡,动静分离,反向代理的。
  • 微服务网关有springCloud gateway,zuul,Dubbo Proxy,用做统一入口,负载均衡,动态路由。
  • Ribbon和Dubbo LB是内部服务调用之间的负载均衡。

Nginx与Zuul(微服务网关)区别相同点:

  • Zuul和Nginx都可以实现负载均衡、反向代理、过滤请求、实现网关效果。
  • 不同点:
    ①Nginx采用C语言编写,Zuul采用java语言编写。
    ②Zuul负载均衡实现:采用ribbon+eureka实现本地负载均衡。Nginx负载均衡实现:采用服务器端实现负载均衡。
    ③Nginx比Zuul功能会更加强大,因为Nginx整合一些脚本语言(Nginx+Lua),Nginx适合于服务器端负载均衡+也可以实现网关。
    ③Zuul适合微服务中实现网关,而且使用技术是java语言。
  • 总结:最好建议nginx+zuul实现网关
    nginx作用实现反向代理
    zuul对微服务实现网关拦截
  • 具体实现方案:在公司中一般都是搭集群的,要么就是zuul一主一备,因为网关是统一入口如果单击版的话,网关一死直接瘫痪了.下面这个图就是让Nginx作为统一如果使用反向代理实现zuul的集群,其实下面这个图还需要加个Nginx一主一备.或者Nginx集群版就可以了.
    在这里插入图片描述

springCloud和springBoot的部署

简介:

  • 在单体架构时一般都是由一个具有暴露接口的应用+tomcat多个业务jar包通过本地依赖关系调用组成的。
    ①实现可以为多个springboot
    ②实现可以为多个ssm
    在这里插入图片描述

  • 当业务量骤增时,可以部署多个一样的单体架构来增加处理能力,但是当部署多个一样的架构时会使得一些公共模块冗余,例如认证授权应用,工具应用,用户管理应用等等。
    多个单体应用也就是集群应用。
    在这里插入图片描述

  • 这时需要将项目架构解耦,也就是使用微服务架构,这时每个应用都需要暴露出接口,对于一些公共模块例如认证授权和登录应用也只需要做一套就够了而对于工具类一般使用本地依赖,因为不涉及查询数据库。各个暴露接口的应用通过rpc或rest调用。
    对于微服务架构的同一应用来说也可以做成集群架构。
    在这里插入图片描述

  • 总结:
    ①war包:
    <1>war包没有页面也OK,只要有接口暴露(这个接口不是Controller层暴露,而是Service层通过RPC暴露)就行,这样就可以访问。
    ②jar包:
    <1>同样内置tomcat的jar包也只要有接口暴露就可以访问。
    <2>如果没有内置tomcat的jar包可以暴露接口(Controller层)也可以是一些工具包和依赖包而已。不过还要依赖其他jar包通过启动类启动。
    因此无论是war包还是jar包都无所谓,重要在于根据业务是否写Controlle层暴露给外部使用,对于内部调用写Controller层调用也可以,通过Service层来RPC调用也可以。
    <1>Controller层的话HTTP调用
    <2>Service层的话RPC调用

spring-retry

简述:

  • 在分布式系统中,为了保证数据分布式事务的强一致性,大家在调用RPC接口或者发送MQ时,针对可能会出现网络抖动请求超时情况采取一下重试操作。

步骤:

  • 添加maven依赖
<dependency>
	<groupId>org.springframework.retry</groupId>
	<artifactId>spring-retry</artifactId>
	<version>1.1.2.RELEASE</version>
</dependency>
<dependency>
	<groupId>org.aspectj</groupId>
	<artifactId>aspectjweaver</artifactId>
	<version>1.5.4</version>
</dependency>
  • 在启动里添加重试配置
@SpringBootApplication
@EnableRetry
public class Application { 
	public static void main(String[] args) {
		SpringApplication.run(Application.class, args);
	}
}
  • 编写Service
@Service
public class RemoteService {
 
    private static final Logger logger = LoggerFactory.getLogger(TestController.class);
 
    @Retryable(value= {BusinessException.class},maxAttempts = 3,backoff = @Backoff(delay = 5000l,multiplier = 2))
    public void call() throws Exception {
        logger.info("do something...");
        throw new BusinessException("RPC调用异常");
    }
    @Recover
    public void recover(BusinessException e) {
        logger.info(" ---------------------------  ");
        logger.info(e.getMessage());
    }
}
  • 编写Controller
@RestController
@RequestMapping("/test")
public class TestController {
 
    private static final Logger logger = LoggerFactory.getLogger(TestController.class);
 
    @Autowired
    private RemoteService remoteService;
 
    @RequestMapping("/test")
    public String login() throws Exception {
        remoteService.call();
        return String.valueOf("11");
    }
  • 访问http://localhost:8080/test/test
  • 测试日志
2017-07-25 19:28:07 [INFO]-[http-nio-53602-exec-1]-[com.test.retry.service.RemoteService.call(RemoteService.java:19)] do something...
2017-07-25 19:28:12 [INFO]-[http-nio-53602-exec-1]-[com.test.retry.service.RemoteService.call(RemoteService.java:19)] do something...
2017-07-25 19:28:22 [INFO]-[http-nio-53602-exec-1]-[com.test.retry.service.RemoteService.call(RemoteService.java:19)] do something...
2017-07-25 19:28:22 [INFO]-[http-nio-53602-exec-1]-[com.test.retry.service.RemoteService.recover(RemoteService.java:24)]  ---------------------------  
2017-07-25 19:28:22 [INFO]-[http-nio-53602-exec-1]-[com.test.retry.service.RemoteService.recover(RemoteService.java:25)] RPC调用异常
  • 相关配置说明
    @EnableRetry能否重试。当proxyTargetClass属性为true时,使用CGLIB代理。默认使用标准JAVA注解。在spring Boot中此参数写在程序入口即可。
    @Retryable 标注此注解的方法在发生异常时会进行重试。
    <1>value:指定处理的异常类
    <2>include:指定处理的异常类和value一样,默认为空,当exclude也为空时,默认所有异常
    <3>exclude:指定异常不处理,默认空,当include也为空时,默认所有异常
    <4>maxAttempts:最大重试次数。默认3次
    <5>backoff: 重试等待策略。默认使用@Backoff注解
    @Backoff 重试等待策略。
    <1>不设置参数时,默认使用FixedBackOffPolicy(指定等待时间),重试等待1000ms
    <2>设置delay,使用FixedBackOffPolicy(指定等待时间),重试等待填写的时间
    <3>设置delay和maxDealy时,重试等待在这两个值之间均态分布
    <4>设置delay、maxDealy、multiplier,使用 ExponentialBackOffPolicy(指数级重试间隔的实现 ),multiplier即指定延迟倍数,比如delay=5000l,multiplier=2,则第一次重试为5秒,第二次为10秒,第三次为20秒……
    @Recover 用于@Retryable重试失败后处理方法,此注解注释的方法参数一定要是@Retryable抛出的异常,否则无法识别,可以在该方法中进行日志处理。

限流方案

限流简介:

  • 限流说明:
    ①限流是对某一时间窗口内的请求数进行限制,保持系统的可用性和稳定性,防止因流量暴增而导致的系统运行缓慢和宕机,在面临高并发的抢购请求时,我们如果不对接口进行限流,可能会对后台系统造成极大的压力。大量的请求抢购成功时需要调用下单的接口,过多的请求打到数据库会对系统的稳定性造成影响。
    ②限流是在保证可用的情况下尽可能多增加进入的人数,其余的人在排队等待,或者返回友好提示,保证里面的进行系统的用户可以正常使用,防止系统雪崩。
  • 为什么要限流:
    ①限流在很多场景中用来限制并发和请求量,比如说秒杀抢购,保护自身系统和下游系统不被巨型流量冲垮等。
    ②以微博为例,例如某某明星公布了恋情,访问从平时的50万增加到了500万,系统的规划能力,最多可以支撑200万访问,那么就要执行限流规则,保证是一个可用的状态,不至于服务器崩溃,所有请求不可用。
  • 在开发高并发系统时有三把利器用来保护系统:缓存降级限流
    ①缓存:缓存的目的是提升系统访问速度和增大系统处理容量
    ②降级:降级是当服务器压力剧增的情况下,根据当前业务情况及流量对一些服务和页面有策略的降级,以此释放服务器资源以保证核心任务的正常运行
    ③限流:限流的目的是通过对并发访问/请求进行限速,或者对一个时间窗口内的请求进行限速来保护系统,一旦达到限制速率则可以拒绝服务、排队或等待、降级等处理。
  • 限流的手段通常有计数器漏桶令牌桶
    ①计数器:在一段时间间隔内(时间窗/时间区间),处理请求的最大数量固定,超过部分不做处理。
    ②漏桶:漏桶大小固定,处理速度固定,但请求进入速度不固定(在突发情况请求过多时,会丢弃过多的请求)。
    ③令牌桶:令牌桶的大小固定,令牌的产生速度固定,但是消耗令牌(即请求)速度不固定(可以应对一些某些时间请求过多的情况);每个请求都会从令牌桶中取出令牌,如果没有令牌则丢弃该次请求。

限流解决方案详解:

  • 计时器算法:在一段时间间隔内(时间窗/时间区间),处理请求的最大数量固定,超过部分不做处理。简单粗暴,比如指定线程池大小,指定数据库连接池大小、nginx连接数等,这都属于计数器算法。计数器算法是限流算法里最简单也是最容易实现的一种算法。
    ①缺点:
    <1>这个算法虽然简单,但是有一个十分致命的问题,那就是临界问题。
    <2>假设有一个恶意用户,他在0:59时,瞬间发送了100个请求,并且1:00又瞬间发送了100个请求,那么其实这个用户在 1秒里面,瞬间发送了200个请求。我们刚才规定的是1分钟最多100个请求(规划的吞吐量),也就是每秒钟最多1.7个请求,用户通过在时间窗口的重置节点处突发请求, 可以瞬间超过我们的速率限制。
    <3>用户有可能通过算法的这个漏洞,瞬间压垮我们的应用。

  • 漏斗算法:漏桶算法思路很简单,水(请求)先进入到漏桶里,漏桶以一定的速度出水,当水流入速度过大会直接溢出,可以看出漏桶算法能强行限制数据的传输速率。
    ①缺点:平时不常用,因为超过桶的容量的请求会直接被抛弃。

  • 令牌桶算法:最初来源于计算机网络。在网络传输数据时,为了防止网络拥塞,需限制流出网络的流量,使流量以比较均匀的速度向外发送。令牌桶算法就实现了这个功能,可控制发送到网络上数据的数目,并允许突发数据的发送。大小固定的令牌桶可自行以恒定的速率源源不断地产生令牌。如果令牌不被消耗,或者被消耗的速度小于产生的速度,令牌就会不断地增多,直到把桶填满。后面再产生的令牌就会从桶中溢出。最后桶中可以保存的最大令牌数永远不会超过桶的大小。这意味,面对瞬时大流量,该算法可以在短时间内请求拿到大量令牌,而且拿令牌的过程并不是消耗很大的事情。
    ②优点:拿到令牌的请求去执行业务,拿不到令牌的请求可以一直等待直到获取令牌,或在一定的时间内尝试获取令牌,超时后再抛弃。
    一般来说使用令牌桶算法,Google 开源项目 Guava 中的 RateLimiter 使用的就是令牌桶控制算法。

  • 链接:
    限流:计数器、漏桶、令牌桶三大算法的原理与实战
    计数器、漏桶、令牌桶三大算法

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值