分布式思想
概念: 将系统按照指定的规则进行拆分.形成了多个业务的子系统.
优势:如果其中一个宕机,则不会影响整个项目的正常运行.
作用: 为了降低系统架构的耦合性.
弊端: 之前可能只需要运维一个服务器. 现在需要运维多个服务器. 运维的成本变高了.
垂直拆分之后,项目实现了松耦合.当一个模块出现问题.不会影响整个项目运行.
在垂直拆分的基础之上进行水平拆分,按照代码的层级进行拆分,将层级拆分为具体项目。一般大项目才会进行水平拆分.因为多人维护同一个项目时容易出错。
Nginx是一款轻量级的Web 服务器/反向代理服务器及电子邮件(IMAP/POP3)代理服务器
,在BSD-like 协议下发行。其特点是占有内存少,并发能力强,事实上nginx的并发能力在同类型的网页服务器中表现较好,中国大陆使用nginx网站用户有:百度、京东、新浪、网易、腾讯、淘宝等。
特点: 占有内存少 不超过2M ,并发能力强 3-5万次/秒
Nginx是当下最热的Web容器,网站优化的重要点在于静态化网站,网站静态化的关键点则是是动静分离,动静分离是让动态网站里的动态网页根据一定规则把不变的资源和经常变的资源区分开来,动静资源做好了拆分以后,我们则根据静态资源的特点将其做缓存操作。
让静态的资源只走静态资源服务器,动态的走动态的服务器
Nginx的静态处理能力很强,但是动态处理能力不足,因此,在企业中常用动静分离技术。
对于静态资源比如图片,js,css等文件,我们则在反向代理服务器nginx中进行缓存。这样浏览器在请求一个静态资源时,代理服务器nginx就可以直接处理,无需将请求转发给后端服务器tomcat。
若用户请求的动态文件,比如servlet,jsp则转发给Tomcat服务器处理,从而实现动静分离。这也是反向代理服务器的一个重要的作用。
为了避免服务器崩溃,大家会通过负载均衡的方式来分担服务器压力。将对台服务器组成一个集群,当用户访问时,先访问到一个转发服务器,再由转发服务器将访问分发到压力更小的服务器。
Nginx负载均衡实现的策略有以下五种:
每个请求按时间顺序逐一分配到不同的后端服务器,如果后端某个服务器宕机,能自动剔除故障系统。
upstream backserver {
server 192.168.0.12;
server 192.168.0.13;
}
weight的值越大分配到的访问概率越高,主要用于后端每台服务器性能不均衡的情况下
。其次是为在主从
的情况下设置不同的权值,达到合理有效的地利用主机资源。
upstream backserver {
server 192.168.0.12 weight=2;
server 192.168.0.13 weight=8;
}
权重越高,在被访问的概率越大,如上例,分别是20%,80%。
每个请求按访问IP的哈希结果分配,使来自同一个IP的访客固定访问一台后端服务器,并且可以有效解决动态网页存在的session共享问题
upstream backserver {
ip_hash;
server 192.168.0.12:88;
server 192.168.0.13:80;
}
弊端:
服务器宕机时则可能影响用户的使用
.负载不均
的现象.必须安装upstream_fair模块。
对比 weight、ip_hash更加智能的负载均衡算法,fair算法可以根据页面大小和加载时间长短智能地进行负载均衡,响应时间短的优先分配。
upstream backserver {
server server1;
server server2;
fair;
}
哪个服务器的响应速度快,就将请求分配到那个服务器上
upstream backserver {
server squid1:3128;
server squid2:3128;
hash $request_uri;
hash_method crc32;
}
缓存的基本思想其实很简单,就是我们非常熟悉的空间换时间
。不要把缓存想的太高大上,虽然,它的确对系统的性能提升的性价比非常高。
其实,我们在学习使用缓存的时候,你会发现缓存的思想实际在操作系统或者其他地方都被大量用到。 比如 CPU Cache 缓存的是内存数据用于解决 CPU 处理速度和内存不匹配的问题,内存缓存的是硬盘数据用于解决硬盘访问速度过慢的问题。 再比如操作系统在 页表方案 基础之上引入了 快表 来加速虚拟地址到物理地址的转换。我们可以把块表理解为一种特殊的高速缓冲存储器(Cache)。
回归到业务系统来说:我们为了避免用户在请求数据的时候获取速度过于缓慢,所以我们在数据库之上增加了缓存这一层来弥补。
说明:使用缓存机制主要的目的就是为了降低用户访问物理设备的频次.从缓存服务器中直接获取数据,快速的响应用户,提高整体的查询速度.用户体验更好.
如何实现:
1.缓存机制应该采用什么样的数据结构 进行构建? K-V结构
K必须唯一
2.应该使用什么语言进行开发? C语言
3.缓存的运行环境是哪? 内存
4.内存断电即擦除, 如何保证数据的安全性?? 实现持久化(写入磁盘)
操作
5.内存中的数据如何进行优化 (不能一直存? ) 内存优化的算法 LRU算法
Redis 是一个开源(BSD许可)的,内存
中的数据结构存储系统,它可以用作数据库、缓存和消息中间件。 它支持多种类型的数据结构,如 字符串(strings), 散列(hashes), 列表(lists), 集合(sets), 有序集合(sorted sets)
与范围查询, bitmaps, hyperloglogs 和 地理空间(geospatial) 索引半径查询。 Redis 内置了 复制(replication),LUA脚本(Lua scripting), LRU驱动事件(LRU eviction),事务(transactions) 和不同级别的 磁盘持久化(persistence), 并通过 Redis哨兵(Sentinel)和自动 分区(Cluster)提供高可用性(high availability).
速度快:
tomcat: 150-220/秒
nginx: 3-5万/秒
redis: 写 8.6万/秒 读 11.2万/秒 ~ 平均10万次/秒
Redis中的数据都保存在内存中.如果服务关闭或者宕机则内存资源直接丢失.导致缓存失效.
Redis中默认的持久化的方式为RDB模式
.
特点说明:
1.RDB模式采用定期持久化的方式. 风险:可能丢失数据.
2.RDB模式记录的是当前Redis的内存记录快照. 只记录当前状态. 持久化效率最高的
3.RDB模式是默认的持久化方式.
持久化命令:
命令1: save 同步操作. 要求记录马上持久化. 可能对现有的操作造成阻塞
名来2: bgsave 异步操作. 开启单独的线程实现持久化任务.
持久化周期:
save 900 1 在900秒内,如果执行一次更新操作,则持久化一次.
save 300 10 在300秒内,如果执行10次更新操作,则持久化一次.
save 60 10000 在60秒内,如果执行10000次更新操作,则持久化一次.
save 1 1 ???不可以 容易阻塞 性能太低.不建议使用.
用户操作越频繁,则持久化周期越短.
特点:
1.AOF模式默认是关闭状态 如果需要则手动开启.
2.AOF能够记录程序的执行过程 可以实现数据的实时持久化. AOF文件占用的空间较大.恢复数据的速度较慢.
3.AOF模式开启之后.RDB模式将不生效.
AOF配置:
持久化周期配置:
appendfsync always 实时持久化.
appendfsync everysec 每秒持久化一次 略低于rdb模式
appendfsync no 自己不主动持久化(被动:由操作系统解决)
Redis运行的空间是内存.内存的资源比较紧缺.所以应该维护redis内存数据,将改让redis保留热点数据.
LRU是Least Recently Used的缩写,即最近最少使用
,是一种常用的页面置换算法,选择最近最久未使用的页面予以淘汰。该算法赋予每个页面一个访问字段,用来记录一个页面自上次被访问以来所经历的时间 t,当须淘汰一个页面时,选择现有页面中其 t 值最大的,即最近最少使用的页面予以淘汰。
维度: 自上一次使用的时间T
最为理想的内存置换算法.
LFU(least frequently used (LFU) page-replacement algorithm)。即最不经常使用页置换算法
,要求在页置换时置换引用计数最小的页,因为经常使用的页应该有一个较大的引用次数。但是有些页在开始时使用次数很多,但以后就不再使用,这类页将会长时间留在内存中,因此可以将引用计数寄存器定时右移一位,形成指数衰减的平均使用次数。
least frequently used (LFU) page-replacement algorithm
即最不经常使用页置换算法,要求在页置换时置换引用计数最小的页,因为经常使用的页应该有一个较大的引用次数。但是有些页在开始时使用次数很多,但以后就不再使用,这类页将会长时间留在内存中,因此可以将引用计数寄存器定时右移一位,形成指数衰减的平均使用次数。
维度: 引用次数
随机算法
一致性哈希算法在1997年由麻省理工学院提出,是一种特殊的哈希算法,目的是解决分布式缓存的问题
。在移除或者添加一个服务器时,能够尽可能小地改变
已存在的服务请求与处理请求服务器之间的映射关系
。一致性哈希解决了
简单哈希算法在分布式哈希表( Distributed Hash Table,DHT) 中存在的动态伸缩
等问题 [2] 。
如果现在node2和node4节点中间增加一个node5节点,那么在node4和node2之间的这些数据要存储的节点就会有所变化。在图中的黄色区域的数据将会从原来的node4节点挪到node5节点。
删除了node2节点后,原本在node2节点上的数据就会被重新定位node4上。这样就产生一个影响:原来node2的数据转移到node4上,这样node4的内存使用率会骤增,如果node2上存在热点数据,node4会扛不住甚至会可能挂掉,挂掉之后数据又转移给node3,如此循环会造成所有节点崩溃,也就是前面所说的雪崩的情况。
节点太少的话可能造成数据倾斜的情况,如图中中只有俩节点,可能会造成大量数据存放在node A节点上,而node B节点存储很少的数据。
为了解决雪崩现象和数据倾斜现象,提出了虚拟节点这个概念
。就是将真实节点计算多个哈希形成多个虚拟节点并放置到哈希环上,定位算法不变,只是多了一步虚拟节点到真实节点映射的过程
以雪崩现象来说明:如下图节点real1节点又俩个虚拟节点v100和v101,real2有俩个虚拟节点v200和v201,real3节点有v300和v301俩个虚拟节点。
当real1节点挂掉后,v100和v101节点也会随即消失,这时k1数据就会被分配到v301上,k4就会被分配到了v200上,这就解决了雪崩的问题,当某个节点宕机后,其数据并没有全部分配给某一个节点,而是被分到了多个节点。
正因为加入了虚拟节点机制,数据倾斜的问题也随之解决
注意:真实节点不放置到哈希环上,只有虚拟节点才会放上去。
平衡性是指hash的结果应该平均分配到各个节点,这样从算法上解决了负载均衡问题
说明:引入虚拟节点,实现数据的平衡
但是平衡是相对的。不是绝对的。
分散性是指数据应该分散地存放在分布式集群中的各个节点
(节点自己可以有备份),不必每个节点都存储所有的数据。
鸡蛋不要放到一个篮子里。
redis可以通过修改内存的大小实现数据的保存。但是内存的资源不宜设置的过大
,因为很多的时间都浪费在内存的寻址
中;
如果有海量的数据,需要redis存储
,那么应该如何处理?我们可以采用Redis分片机制
实现内存数据的扩容。
优点:可以实现redis内存的动态扩容.
缺点:没有实现高可用(当其中一台redis服务器发生宕机,则会影响整个分片使用)
哨兵(Sentinel)主要是为了解决在主从复制架构中出现宕机的情况
Sentinel(哨兵)是Redis 的高可用性解决方案:由一个或多个Sentinel 实例 组成的Sentinel 系统可以监视任意多个主服务器,以及这些主服务器属下的所有从服务器,并在被监视的主服务器进入下线状态时,自动将下线主服务器属下的某个从服务器升级为新的主服务器。
在Server1 掉线后:
利用选举机制算法,挑选Server2 为新的主服务器:
优点:实现Redis节点的高可用。
缺点:哨兵本身没有高可用效果. 如果哨兵宕机则整个服务宕机;Redis中的数据不能实现内存的扩容。
Redis集群相当于整合redis分片机制(内存扩容),redis哨兵机制(高可用).Redis集群由所有的主节点负责监控和选举.从而在不依赖第三方的基础之后实现了redis集群的高可用.
细节:集群中的所有的节点都能互相通信.(PING-PONG)
通常,为了提高网站响应速度,总是把热点数据保存在内存中而不是直接从后端数据库中读取。
Redis是一个很好的Cache工具。大型网站应用,热点数据量往往巨大,几十G上百G是很正常的事儿。
由于内存大小的限制,使用一台 Redis 实例显然无法满足需求,这时就需要使用多台 Redis作为缓存数据库。但是如何保证数据存储的一致性呢,这时就需要搭建redis集群.采用合理的机制,保证用户的正常的访问需求.
采用redis集群,可以保证数据分散存储,同时保证数据存储的一致性.并且在内部实现高可用的机制.实现了服务故障的自动迁移.
原理说明:
Redis的所有节点都会保存当前redis集群中的全部主从状态信息
。并且每个节点都能够相互通信。当一个节点发生宕机现象。则集群中的其他节点通过PING-PONG检测机制检查Redis节点是否宕机。当有半数以上
的节点认为宕机,则认为主节点宕机。同时由Redis剩余的主节点进入选举机制
。投票选举链接宕机的主节点的从机,实现故障迁移。
特点:集群中如果主机宕机,那么从机可以继续提供服务,当主机中没有从机时,则向其它主机借用多余的从机(其他主机有多台从机,自己至少保留一台从机,其他可以外借
)。继续提供服务。如果主机宕机时没有从机可用,则集群崩溃。
答案:9个redis节点,节点宕机5-7次时集群才崩溃。
如图所示:
问题分析:
补充说明:如果想让集群尽可能不宕机,则适当增加从节点的数量. 2-3个从即可.
主机
为什么是奇数集群存活节点数量 > n/2
3 > 1.5 3台可以搭建集群 宕机1台
4 > 2 4台可以搭建集群 宕机1台
通过上述证明,集群搭建的主机台数是奇数时,解是最优的。
一般情况下的集群是奇数的要求. zookeeper Eureka集群
规定投票进行3次.如果连续平票的概率是1/8。概率偏低。如果适当增加主节点的数量
则可以有效的降低平票导致脑裂的风险.
redis cluster采用数据分片的哈希槽来进行数据存储和数据的读取。redis cluster一共有2^14(16384)个槽,所有的master节点都会有一个槽区比如0~1000,槽数是可以迁移的。master节点的slave节点不分配槽,只拥有读权限。但是注意在代码中redis cluster执行读写操作的都是master节点,并不是你想 的读是从节点,写是主节点。第一次新建redis cluster时,16384个槽是被master节点均匀分布的。
RedisCluster采用此分区,所有的键根据哈希函数(CRC16[key]&16384)映射到0-16383槽内,共16384个槽位,每个节点维护部分槽及槽所映射的键值数据。根据主节点的个数,均衡划分区间。
算法:哈希函数: Hash()=CRC16[key]&16384按位与。
当向redis集群中插入数据时,首先将key进行计算.之后将计算结果匹配到具体的某一个槽的区间内,之后再将数据set到管理该槽的节点中。
Redis分区只负责数据应该存储到哪里的问题。至于是否能存储的下,完全由Redis内存决定。
缓存穿透说简单点就是大量请求的 key 根本不存在于缓存中,导致请求直接到了数据库上,根本没有经过缓存这一层
。举个例子:某个黑客故意制造我们缓存中不存在的 key 发起大量请求,导致大量请求落到数据库。
影响:由于用户高并发访问,则数据库可能存在宕机
的风险.
SET key value EX 10086
。这种方式可以解决请求的 key 变化不频繁的情况,如果黑客恶意攻击,每次构建不同的请求 key,会导致 Redis 中缓存大量无效的 key 。很明显,这种方案并不能从根本上解决此问题。如果非要用这种方式来解决穿透问题的话,尽量将无效的 key 的过期时间设置短一点比如 1 分钟。表名:列名:主键名:主键值
。public Object getObjectInclNullById(Integer id) {
// 从缓存中获取数据
Object cacheValue = cache.get(id);
// 缓存为空
if (cacheValue == null) {
// 从数据库中获取
Object storageValue = storage.get(key);
// 缓存空对象
cache.set(key, storageValue);
// 如果存储数据为空,需要设置一个过期时间(300秒)
if (storageValue == null) {
// 必须设置过期时间,否则有被攻击的风险
cache.expire(key, 60 * 5);
}
return storageValue;
}
return cacheValue;
}
把所有可能存在的请求的值都存放在布隆过滤器中,当用户请求过来,先判断用户发来的请求的值是否存在于布隆过滤器中。不存在的话,直接返回请求参数错误信息给客户端,存在的话才会走下面的流程。
但是,需要注意的是布隆过滤器可能会存在误判的情况。总结来说就是: 布隆过滤器说某个元素存在,小概率会误判。布隆过滤器说某个元素不在,那么这个元素一定不在。
为什么会出现误判的情况呢? 我们还要从布隆过滤器的原理来说!
我们先来看一下,当一个元素加入布隆过滤器中的时候,会进行哪些操作:
由于高并发的环境下.大量的用户访问服务器. 缓存中大量的数据在同一时间超时(删除),导致后面的请求都直接落到了数据库上,造成数据库短时间内承受大量请求。
针对 Redis 服务不可用的情况:
针对热点缓存失效的情况:
3. 设置不同的失效时间比如随机设置缓存的失效时间。
4. 缓存永不失效。
由于用户高并发的访问. 访问的数据刚开始有缓存,但是由于特殊原因 导致缓存失效.(数据是单个
)
缓存击穿是指热点key在某个时间点过期的时候,而恰好在这个时间点对这个Key有大量的并发请求过来,从而大量的请求打到db。
设置热点数据永远不过期。
加互斥锁,互斥锁参考代码如下:
说明:
1)缓存中有数据,直接走上述代码13行后就返回结果了
2)缓存中没有数据,第1个进入的线程,获取锁并从数据库去取数据,没释放锁之前,其他并行进入的线程会等待100ms,再重新去缓存取数据。这样就防止都去数据库重复取数据,重复往缓存中更新数据情况出现。
3)当然这是简化处理,理论上如果能根据key值加锁就更好了,就是线程A从数据库取key1的数据并不妨碍线程B取key2的数据,上面代码明显做不到这点。
参考文章
都可以作为注册中心的Zookeeper和Eureka,想要理解两种注册中心的区别,首先要理解CAP理论。
CPA理论:C–>Consistency(一致性):数据一致更新,所有数据变动都是同步的。A–>Availability(可用性):好的相应性能。P–>Partition tolerance(分区容忍性):可靠性。
一个分布式系统不可能同时满足C(一致性)、A(可用性)和P(分区容错性)。由于分区容错性在分布式系统中是必须要保证的,因此我们只能在A和C之间权衡。
故在此Zookeeper保证的是CP,而Eureka则保证的是AP。
Zookeeper:
当向注册中心查询服务列表时,我们可以容忍注册中心返回的是几分钟以前的注册信息,但不能接受服务直接down掉不可用。也就是说,服务注册功能对可用性的要求要高于一致性。但是Zookeeper会出现这样一种情况,当master节点因为网络故障与其他节点失去联系时,剩余节点会重新进行leader选举。问题在于,选举leader的时间太长,30s~120s,并且选举期间整个Zookeeper集群都是不可用的,这就导致在选举期间注册服务瘫痪。在云部署的环境下,因网络问题是得Zookeeper集群失去master节点是较大概率会发生的事,虽然服务能够最终恢复,但是漫长的选举时间导致的注册长期不可用是不能容忍的。
Eureka:
Eureka看明白了这点,因此在设计师就有限保证可用性,Eureka各个节点都是平等的,几个节点挂掉不会影响正常节点的工作,剩余的节点依然可以提供注册和查询服务。而Eureka的客户端在向某个Eureka注册时如果发现连接失败,会自动切换至其他节点,只要有一台Eureka还在,就能保证注册服务可用(保证可用性),只不过查到的信息可能不是最新的(不保证强一致性)。除此之外,Eureka还有一种自我保护机制,如果在15分钟内超过85%的节点都没有正常的心跳,那么Eureka就认为客户端与注册中心出现了网络故障,此时会出现以下几种情况:
1、Eureka不再从注册列表中溢出因为长时间没有收到心跳而应该过期的服务。
2、Eureka仍然能够接受新服务的注册和查询请求,但是不会被同步到其他节点上(即保证当前节点依然可用)。
3、当网络稳定是,当前实例新的注册信息会被同步到其他节点中。
因此,Eureka可以很好的应对网络故障导致部分节点失去联系的情况,而不会像zookeeper那样使整个注册服务瘫痪
Dubbo支持dubbo、rmi、hessian、http、webservice、thrift、redis等多种协议,但是Dubbo官网是推荐我们使用Dubbo协议的。
反向代理服务器位于用户与目标服务器之间,但是对于用户而言,反向代理服务器就相当于目标服务器,即用户直接访问反向代理服务器就可以获得目标服务器的资源。同时,用户不需要知道目标服务器的地址,也无须在用户端作任何设定。反向代理服务器通常可用来作为Web加速,即使用反向代理作为Web服务器的前置机来降低网络和服务器的负载,提高访问效率。
特点:
特征: 用户不清楚真实目标服务器是谁.
反向代理服务器的优点是什么?
反向代理服务器可以隐藏源服务器的存在和特征。它充当互联网云和web服务器之间的中间层。这对于安全方面来说是很好的,特别是当您使用web托管服务时。
正向代理,意思是一个位于客户端和原始服务器(origin server)之间的服务器,为了从原始服务器取得内容,客户端向代理发送一个请求并指定目标(原始服务器),然后代理向原始服务器转交请求并将获得的内容返回给客户端。客户端才能使用正向代理。
特点:
1.代理服务器位于用户和服务器之间
2.用户发起请求之前已经清楚的知道谁的目标服务器.
3.用户通过代理服务器到指定的目标服务器获取资源.
4.正向代理保护的是客户端信息**,是客户端代理**
正向代理是客户端代理,用户清楚的知道访问的服务器是谁. 保护了客户端信息
反向代理是服务器端代理.用户不清楚访问的真实服务到底是谁. 保护了服务端信息\