㈠ Redis分布式缓存搭建
花了两天时间整理了之前记录的Redis单体与哨兵模式的搭建与使用,又补齐了集群模式的使用和搭建经验,并对集群的一些个原理做了理解。
笔者安装中遇到的一些问题:
如果make报错,可能是没装gcc或者gcc++编辑器,安装之 yum -y install gcc gcc-c++ kernel-devel ,有可能还是提示一些个c文件编译不过,gcc -v查看下版本,如果不到5.3那么升级一下gcc:
在 /etc/profile 追加一行 source /opt/rh/devtoolset-9/enable
scl enable devtoolset-9 bash
重新make clean, make
这回编译通过了,提示让你最好make test一下/
执行make test ,如果提示 You need tcl 8.5 or newer in order to run the Redis test
那就升级tcl, yum install tcl
重新make test,如果还有error就删了目录,重新tar包解压重新make , make test
o/ All tests passed without errors! ,表示编译成功。
然后make install即可。
直接运行命令: ./redis-server /usr/redis-6.0.3/redis.conf &
redis.conf 配置文件里 bind 0.0.0.0 设置外部访问, requirepass xxxx 设置密码。
redis高可用方案有两种:
常用搭建方案为1主1从或1主2从+3哨兵监控主节点, 以及3主3从6节点集群。
(1)sentinel哨兵
/usr/redis-6.0.3/src/redis-sentinel /usr/redis-6.0.3/sentinel2.conf &
sentinel2.conf配置:
坑1:master节点也会在故障转移后成为从节点,也需要配置masterauth
当kill master进程之后,经过sentinel选举,slave成为了新的master,再次启动原master,提示如下错误:
原因是此时的master再次启动已经是slave了,需要向现在的新master输入密码,所以需要在master.conf
中配置:
坑2:哨兵配置文件要暴露客户端可以访问到的master地址
在 sentinel.conf 配置文件的 sentinel monitor mymaster 122.xx.xxx.xxx 6379 2 中,配置该哨兵对应的master名字、master地址和端口,以及达到多少个哨兵选举通过认为master挂掉。其中master地址要站在redis访问者(也就是客户端)的角度、配置访问者能访问的地址,例如sentinel与master在一台服务器(122.xx.xxx.xxx)上,那么相对sentinel其master在本机也就是127.0.0.1上,这样 sentinel monitor mymaster 127.0.0.1 6379 2 逻辑上没有问题,但是如果另外服务器上的springboot通过lettuce访问这个redis哨兵,则得到的master地址为127.0.0.1,也就是springboot所在服务器本机,这显然就有问题了。
附springboot2.1 redis哨兵配置:
坑3:要注意配置文件.conf会被哨兵修改
redis-cli -h localhost -p 26379 ,可以登到sentinel上用info命令查看一下哨兵的信息。
曾经遇到过这样一个问题,大致的信息如下
slaves莫名其妙多了一个,master的地址也明明改了真实对外的地址,这里又变成127.0.0.1 !
最后,把5个redis进程都停掉,逐个检查配置文件,发现redis的配置文件在主从哨兵模式会被修改,master的配置文件最后边莫名其妙多了一行replicaof 127.0.0.1 7001, 怀疑应该是之前配置错误的时候(见坑2)被哨兵动态加上去的! 总之,实践中一定要多注意配置文件的变化。
(2)集群
当数据量大到一定程度,比如几十上百G,哨兵模式不够用了需要做水平拆分,早些年是使用codis,twemproxy这些第三方中间件来做分片的,即 客户端 -> 中间件 -> Redis server 这样的模式,中间件使用一致性Hash算法来确定key在哪个分片上。后来Redis官方提供了方案,大家就都采用官方的Redis Cluster方案了。
Redis Cluster从逻辑上分16384个hash slot,分片算法是 CRC16(key) mod 16384 得到key应该对应哪个slot,据此判断这个slot属于哪个节点。
每个节点可以设置1或多个从节点,常用的是3主节点3从节点的方案。
reshard,重新分片,可以指定从哪几个节点移动一些hash槽到另一个节点去。重新分片的过程对客户端透明,不影响线上业务。
搭建Redis cluster
redis.conf文件关键的几个配置:
启动6个集群节点
[root@VM_0_11_centos redis-6.0.3]# ps -ef|grep redis
root 5508 1 0 21:25 ? 00:00:00 /usr/redis-6.0.3/src/redis-server 0.0.0.0:7001 [cluster]
root 6903 1 0 21:32 ? 00:00:00 /usr/redis-6.0.3/src/redis-server 0.0.0.0:7002 [cluster]
root 6939 1 0 21:33 ? 00:00:00 /usr/redis-6.0.3/src/redis-server 0.0.0.0:7003 [cluster]
root 6966 1 0 21:33 ? 00:00:00 /usr/redis-6.0.3/src/redis-server 0.0.0.0:7004 [cluster]
root 6993 1 0 21:33 ? 00:00:00 /usr/redis-6.0.3/src/redis-server 0.0.0.0:7005 [cluster]
root 7015 1 0 21:33 ? 00:00:00 /usr/redis-6.0.3/src/redis-server 0.0.0.0:7006 [cluster]
这时候这6个节点还是独立的,要把他们配置成集群:
说明: -a xxxx 是因为笔者在redis.conf中配置了requirepass xxxx密码,然后 --cluster-replicas 1 中的1表示每个master节点有1个从节点。
上述命令执行完以后会有一个询问: Can I set the above configuration? yes同意自动做好的分片即可。
最后 All 16384 slots covered. 表示集群中16384个slot中的每一个都有至少有1个master节点在处理,集群启动成功。
查看集群状态:
坑1:暴露给客户端的节点地址不对
使用lettuce连接发现连不上,查看日志 Connection refused: no further information: /127.0.0.1:7002 ,跟之前哨兵配置文件sentinel.conf里边配置master地址犯的错误一样,集群启动的时候带的地址应该是提供给客户端访问的地址。
我们要重建集群:先把6个redis进程停掉,然后删除 nodes-7001.conf 这些节点配置文件,删除持久化文件 mp.rdb 、 appendonly.aof ,重新启动6个进程,在重新建立集群:
然后,还是连不上,这次报错 connection timed out: /172.xx.0.xx:7004 ,发现连到企鹅云服务器的内网地址上了!
解决办法,修改每个节点的redis.conf配置文件,找到如下说明:
所以增加配置:
然后再重新构建集群,停进程、改配置、删除节点文件和持久化文件、启动进程、配置集群。。。再来一套(累死了)
重新使用Lettuce测试,这次终于连上了!
坑2:Lettuce客户端在master节点故障时没有自动切换到从节点
name这个key在7002上,kill这个进程模拟master下线,然后Lettuce一直重连。我们期望的是应该能自动切换到其slave 7006上去,如下图:
重新启动7002进程,
7006已成为新master,7002成为它的slave,然后Lettuce也能连接上了。
解决办法,修改Lettuce的配置:
笔者用的是springboot 2.1 spring-boot-starter-data-redis 默认的Lettuce客户端,当使用Redis cluster集群模式时,需要配置一下 RedisConnectionFactory 开启自适应刷新来做故障转移时的自动切换从节点进行连接。
重新测试:停掉master 7006,这次Lettuce可以正常切换连到7002slave上去了。(仍然会不断的在日志里报连接错误,因为需要一直尝试重连7006,但因为有7002从节点顶上了、所以应用是可以正常使用的)
Redis不保证数据的强一致性
Redis并不保证数据的强一致性,也就是取CAP定理中的AP
关于一致性Hash算法,可以参考 一致性Hash算法 - (jianshu.com)
Redis cluster使用的是hash slot算法,跟一致性Hash算法不太一样,固定16384个hash槽,然后计算key落在哪个slot里边(计算key的CRC16值再对16384取模),key找的是slot而不是节点,而slot与节点的对应关系可以通过reshard改变并通过gossip协议扩散到集群中的每一个节点、进而可以为客户端获知,这样key的节点寻址就跟具体的节点个数没关系了。也同样解决了普通hash取模算法当节点个数发生变化时,大量key对应的寻址都发生改动导致缓存失效的问题。
比如集群增加了1个节点,这时候如果不做任何操作,那么新增加的这个节点上是没有slot的,所有slot都在原来的节点上且对应关系不变、所以没有因为节点个数变动而缓存失效,当reshard一部分slot到新节点后,客户端获取到新迁移的这部分slot与新节点的对应关系、寻址到新节点,而没迁移的slot仍然寻址到原来的节点。
关于热迁移,猜想,内部应该是先做复制迁移,等迁移完了,再切换slot与节点的对应关系,复制没有完成之前仍按照原来的slot与节点对应关系去原节点访问。复制结束之后,再删除原节点上已经迁移的slot所对应的key。
与哨兵模式比较类似,当1个节点发现某个master节点故障了、会对这个故障节点进行pfail主观宕机,然后会通过gossip协议通知到集群中的其他节点、其他节点也执行判断pfail并gossip扩散广播这一过程,当超过半数节点pfail时那么故障节点就是fail客观宕机。接下来所有的master节点会在故障节点的从节点中选出一个新的主节点,此时所有的master节点中超过半数的都投票选举了故障节点的某个从节点,那么这个从节点当选新的master节点。
所有节点都持有元数据,节点之间通过gossip这种二进制协议进行通信、发送自己的元数据信息给其他节点、故障检测、集群配置更新、故障转移授权等等。
这种去中心化的分布式节点之间内部协调,包括故障识别、故障转移、选主等等,核心在于gossip扩散协议,能够支撑这样的广播协议在于所有的节点都持有一份完整的集群元数据,即所有的节点都知悉当前集群全局的情况。
Redis高可用方案 - (jianshu.com)
面试题:Redis 集群模式的工作原理能说一下么 - 云+社区 - 腾讯云 (tencent.com)
深度图解Redis Cluster原理 - detectiveHLH - 博客园 (cnblogs.com)
Redis学习笔记之集群重启和遇到的坑-阿里云开发者社区 (aliyun.com)
云服务器Redis集群部署及客户端通过公网IP连接问题
㈡ “分布式缓存” 是什么概念,怎么理解
我的理解,分布式缓存系统是为了解决数据库服务器和web服务器之间的瓶颈。
如果一个网站的流量很大,这个瓶颈将会非常明显,每次数据库查询耗费的时间将会非常可观。
对于更新速度不是很快的网站,我们可以用静态化来避免过多的数据库查询。
对于更新速度以秒计的网站,静态化也不会太理想,可以用缓存系统来构建。
如果只是单台服务器用作缓存,问题不会太复杂,如果有多台服务器用作缓存,就要考虑缓存服务器的负载均衡。
㈢ 大型互联网架构概述,看完文章又涨知识了
1. 大型网站系统的特点
2. 大型网站架构演化历程
2.1. 初始阶段架构
问题:网站运营初期,访问用户少,一台服务器绰绰有余。
特征:应用程序、数据库、文件等所有的资源都在一台服务器上。
描述:通常服务器操作系统使用 linux,应用程序使用 PHP 开发,然后部署在 Apache 上,数据库使用 Mysql,通俗称为 LAMP。汇集各种免费开源软件以及一台廉价服务器就可以开始系统的发展之路了。
2.2. 应用服务和数据服务分离
问题:越来越多的用户访问导致性能越来越差,越来越多的数据导致存储空间不足,一台服务器已不足以支撑。
特征:应用服务器、数据库服务器、文件服务器分别独立部署。
描述:三台服务器对性能要求各不相同:应用服务器要处理大量业务逻辑,因此需要更快更强大的 CPU;数据库服务器需要快速磁盘检索和数据缓存,因此需要更快的硬盘和更大的内存;文件服务器需要存储大量文件,因此需要更大容量的硬盘。
2.3. 使用缓存改善性能
问题:随着用户逐渐增多,数据库压力太大导致访问延迟。
特征:由于网站访问和财富分配一样遵循二八定律:80% 的业务访问集中在 20% 的数据上。将数据库中访问较集中的少部分数据缓存在内存中,可以减少数据库的访问次数,降低数据库的访问压力。
描述:缓存分为两种:应用服务器上的本地缓存和分布式缓存服务器上的远程缓存,本地缓存访问速度更快,但缓存数据量有限,同时存在与应用程序争用内存的情况。分布式缓存可以采用集群方式,理论上可以做到不受内存容量限制的缓存服务。
2.4. 使用应用服务器集群
问题:使用缓存后,数据库访问压力得到有效缓解。但是单一应用服务器能够处理的请求连接有限,在访问高峰期,成为瓶颈。
特征:多台服务器通过负载均衡同时向外部提供服务,解决单一服务器处理能力和存储空间不足的问题。
描述:使用集群是系统解决高并发、海量数据问题的常用手段。通过向集群中追加资源,提升系统的并发处理能力,使得服务器的负载压力不再成为整个系统的瓶颈。
2.5. 数据库读写分离
问题:网站使用缓存后,使绝大部分数据读操作访问都可以不通过数据库就能完成,但是仍有一部分读操作和全部的写操作需要访问数据库,在网站的用户达到一定规模后,数据库因为负载压力过高而成为网站的瓶颈。
特征:目前大部分的主流数据库都提供主从热备功能,通过配置两台数据库主从关系,可以将一台数据库服务器的数据更新同步到一台服务器上。网站利用数据库的主从热备功能,实现数据库读写分离,从而改善数据库负载压力。
描述:应用服务器在写操作的时候,访问主数据库,主数据库通过主从复制机制将数据更新同步到从数据库。这样当应用服务器在读操作的时候,访问从数据库获得数据。为了便于应用程序访问读写分离后的数据库,通常在应用服务器端使用专门的数据访问模块,使数据库读写分离的对应用透明。
2.6. 反向代理和 CDN 加速
问题:中国网络环境复杂,不同地区的用户访问网站时,速度差别也极大。
特征:采用 CDN 和反向代理加快系统的静态资源访问速度。
描述:CDN 和反向代理的基本原理都是缓存,区别在于 CDN 部署在网络提供商的机房,使用户在请求网站服务时,可以从距离自己最近的网络提供商机房获取数据;而反向代理则部署在网站的中心机房,当用户请求到达中心机房后,首先访问的服务器时反向代理服务器,如果反向代理服务器中缓存着用户请求的资源,就将其直接返回给用户。
2.7. 分布式文件系统和分布式数据库
问题:随着大型网站业务持续增长,数据库经过读写分离,从一台服务器拆分为两台服务器,依然不能满足需求。
特征:数据库采用分布式数据库,文件系统采用分布式文件系统。
描述:分布式数据库是数据库拆分的最后方法,只有在单表数据规模非常庞大的时候才使用。不到不得已时,更常用的数据库拆分手段是业务分库,将不同的业务数据库部署在不同的物理服务器上。
2.8. 使用 NoSQL 和搜索引擎
问题:随着网站业务越来越复杂,对数据存储和检索的需求也越来越复杂。
特征:系统引入 NoSQL 数据库及搜索引擎。
描述:NoSQL 数据库及搜索引擎对可伸缩的分布式特性具有更好的支持。应用服务器通过统一数据访问模块访问各种数据,减轻应用程序管理诸多数据源的麻烦。
2.9. 业务拆分
问题:大型网站的业务场景日益复杂,分为多个产品线。
特征:采用分而治之的手段将整个网站业务分成不同的产品线。系统上按照业务进行拆分改造,应用服务器按照业务区分进行分别部署。
描述:应用之间可以通过超链接建立关系,也可以通过消息队列进行数据分发,当然更多的还是通过访问同一个数据存储系统来构成一个关联的完整系统。
纵向拆分:将一个大应用拆分为多个小应用,如果新业务较为独立,那么就直接将其设计部署为一个独立的 Web 应用系统。纵向拆分相对较为简单,通过梳理业务,将较少相关的业务剥离即可。
横向拆分:将复用的业务拆分出来,独立部署为分布式服务,新增业务只需要调用这些分布式服务横向拆分需要识别可复用的业务,设计服务接口,规范服务依赖关系。
2.10. 分布式服务
问题:随着业务越拆越小,存储系统越来越庞大,应用系统整体复杂程度呈指数级上升,部署维护越来越困难。由于所有应用要和所有数据库系统连接,最终导致数据库连接资源不足,拒绝服务。
特征:公共业务提取出来,独立部署。由这些可复用的业务连接数据库,通过分布式服务提供共用业务服务。
3. 大型网站架构模式
3.1. 分层
大型网站架构中常采用分层结构,将软件系统分为应用层、服务层、数据层:
分层架构的约束:禁止跨层次的调用(应用层直接调用数据层)及逆向调用(数据层调用服务层,或者服务层调用应用层)。
分层结构内部还可以继续分层,如应用可以再细分为视图层和业务逻辑层;服务层也可以细分为数据接口层和逻辑处理层。
3.2. 分割
将不同的功能和服务分割开来,包装成高内聚低耦合的模块单元。这有助于软件的开发和维护,便于不同模块的分布式部署,提高网站的并发处理能力和功能扩展能力。
3.3. 分布式
大于大型网站,分层和分割的一个主要目的是为了切分后的模块便于分布式部署,即将不同模块部署在不同的服务器上,通过远程调用协同工作。
分布式意味可以用更多的机器工作,那么 CPU、内存、存储资源也就更丰富,能够处理的并发访问和数据量就越大,进而能够为更多的用户提供服务。
分布式也引入了一些问题:
常用的分布式方案:
3.4. 集群
集群即多台服务器部署相同应用构成一个集群,通过负载均衡设备共同对外提供服务。
集群需要具备伸缩性和故障转移机制:伸缩性是指可以根据用户访问量向集群添加或减少机器;故障转移是指,当某台机器出现故障时,负载均衡设备或失效转移机制将请求转发到集群中的其他机器上,从而不影响用户使用。
3.5. 缓存
缓存就是将数据存放在距离最近的位置以加快处理速度。缓存是改善软件性能的第一手段。
网站应用中,缓存除了可以加快数据访问速度以外,还可以减轻后端应用和数据存储的负载压力。
常见缓存手段:
使用缓存有两个前提:
3.6. 异步
软件发展的一个重要目标和驱动力是降低软件耦合性。事物之间直接关系越少,彼此影响就越小,也就更容易独立发展。
大型网站架构中,系统解耦的手段除了分层、分割、分布式等,还有一个重要手段——异步。
业务间的消息传递不是同步调用,而是将一个业务操作拆分成多阶段,每个阶段间通过共享数据的方式异步执行进行协作。
异步架构是典型的生产者消费模式,二者不存在直接调用。异步消息队列还有如下特性:
3.7. 冗余
大型网站,出现服务器宕机是必然事件。要保证部分服务器宕机的情况下网站依然可以继续服务,不丢失数据,就需要一定程度的服务器冗余运行,数据冗余备份。这样当某台服务器宕机是,可以将其上的服务和数据访问转移到其他机器上。
访问和负载很小的服务也必须部署 至少两台服务器构成一个集群,目的就是通过冗余实现服务高可用。数据除了定期备份,存档保存,实现 冷备份 外;为了保证在线业务高可用,还需要对数据库进行主从分离,实时同步实现 热备份。
为了抵御地震、海啸等不可抗因素导致的网站完全瘫痪,某些大型网站会对整个数据中心进行备份,全球范围内部署 灾备数据中心。网站程序和数据实时同步到多个灾备数据中心。
3.8. 自动化
大型网站架构的自动化架构设计主要集中在发布运维方面:
3.9. 安全
4. 大型网站核心架构要素
架构 的一种通俗说法是:最高层次的规划,难以改变的决定。
4.1. 性能
性能问题无处不在,所以网站性能优化手段也十分繁多:
4.2. 可用性
可用性指部分服务器出现故障时,还能否对用户提供服务
4.3. 伸缩性
衡量伸缩的标准就是是否可以用多台服务器构建集群,是否容易向集群中增删服务器节点。增删服务器节点后是否可以提供和之前无差别的服务。集群中可容纳的总服务器数是否有限制。
4.4. 扩展性
衡量扩展性的标准就是增加新的业务产品时,是否可以实现对现有产品透明无影响,不需要任何改动或很少改动,既有功能就可以上线新产品。主要手段有:事件驱动架构和分布式服务。
4.5. 安全性
安全性保护网站不受恶意攻击,保护网站重要数据不被窃取。
欢迎工作一到五年的Java工程师朋友们加入Java程序员开发: 721575865
群内提供免费的Java架构学习资料(里面有高可用、高并发、高性能及分布式、Jvm性能调优、Spring源码,MyBatis,Netty,Redis,Kafka,Mysql,Zookeeper,Tomcat,Docker,Dubbo,Nginx等多个知识点的架构资料)合理利用自己每一分每一秒的时间来学习提升自己,不要再用"没有时间“来掩饰自己思想上的懒惰!趁年轻,使劲拼,给未来的自己一个交代!
㈣ 什么是分布式缓存
分布式缓存能够处理大量的动态数据,因此比较适合应用在Web 2.0时代中的社交网站等需要由用户生成内容的场景。从本地缓存扩展到分布式缓存后,关注重点从CPU、内存、缓存之间的数据传输速度差异也扩展到了业务系统、数据库、分布式缓存之间的数据传输速度差异。
常用的分布式缓存包括Redis和Memcached。
Memcached
Memcached是一个高性能的分布式内存对象缓存系统,用于动态Web应用以减轻数据库负载。Memcached通过在内存中缓存数据和对象来减少读取数据库的次数,从而提高动态、数据库驱动网站的速度。
特点:哈希方式存储;全内存操作;简单文本协议进行数据通信;只操作字符型数据;集群由应用进行控制,采用一致性哈希算法。
限制性:数据保存在内存当中的,一旦机器重启,数据会全部丢失;只能操作字符型数据,数据类型贫乏;以root权限运行,而且Memcached本身没有任何权限管理和认证功能,安全性不足;能存储的数据长度有限,最大键长250个字符,储存数据不能超过1M。
Redis
Redis是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。
特点:
Redis支持的数据类型包括:字符串、string、hash、set、sortedset、list;Redis实现持久化的方式:定期将内存快照写入磁盘;写日志;Redis支持主从同步。
限制性:单核运行,在存储大数据的时候性能会有降低;不是全内存操作;主从复制是全量复制,对实际的系统运营造成了一定负担。
㈤ 如何实现分布式缓存技术
分布式缓存系统是为了解决数据库服务器和web服务器之间的瓶颈。
如果一个网站的流量很大,这个瓶颈将会非常明显,每次数据库查询耗费的时间将会非常可观。
对于更新速度不是很快的网站,可以用静态化来避免过多的数据库查询。
对于更新速度以秒计的网站,静态化也不会太理想,可以用缓存系统来构建。
如果只是单台服务器用作缓存,问题不会太复杂,如果有多台服务器用作缓存,就要考虑缓存服务器的负载均衡。
㈥ Go 分布式缓存简单实现
一个性能优异的系统架构,缓存是必不可少的。但在缓存的使用,可能会遇到一些问题,比如,内存不够、并发写入冲突、单机性能差等问题。帆茄为了解决这些问题,我们引入了分布式缓存。
本次分享主要通过学习 极客兔兔 的文章,对吵森关键节点进行了实现和解读。
一致性哈希的实现是基于哈希环实现的,主要为了解决数据倾斜和节点增删改时缓存雪崩的问题。
缓存击穿指大量请求同时访问缓存中不存在数据,因为缓存中不存在这个数据,请求就会击穿到 DB,造成瞬时 DB 压力骤增。解决方案也很简单,针对相同的 key,在访问时,只发起一次请求升轿亩。
Protobuf 的安装和使用教程可以参考 官网 ,推荐使用 homebrew 进行安装。
㈦ 分布式缓存是什么
分布式缓存主要用于在高并发环境下,减轻数据库的压力,提高系统的响应速度和并发吞吐。当大量的读、写请求涌向数据库时,磁盘的处理速度与内存显然不在一个量级,因此,在数据库之前加一层缓存,能够显着提高系统的响应速度,并降低数据库的压力。作为传统的关系型数据库,MySQL提供完整的ACID操作,支持丰富的数据类型、强大的关联查询、where语句等,能够非常客易地建立查询索引,执行复杂的内连接、外连接、求和、排序、分组等操作,并且支持存储过程、函数等功能,产品成熟度高,功能强大。但是,对于需要应对高并发访问并且存储海量数据的场景来说,出于对性能的考虑,不得不放弃很多传统关系型数据库原本强大的功能,牺牲了系统的易用性,并且使得系统的设计和管理变得更为复杂。这也使得在过去几年中,流行着另一种新的存储解决方案——NoSQL,它与传统的关系型数据库最大的差别在于,它不使用SQL作为查询语言来查找数据,而采用key-value形式进行查找,提供了更高的查询效率及吞吐,并且能够更加方便地进行扩展,存储海量数据,在数千个节点上进行分区,自动进行数据的复制和备份。在分布式系统中,消息作为应用间通信的一种方式,得到了十分广泛的应用。消息可以被保存在队列中,直到被接收者取出,由于消息发送者不需要同步等待消息接收者的响应,消息的异步接收降低了系统集成的耦合度,提升了分布式系统协作的效率,使得系统能够更快地响应用户,提供更高的吞吐。
当系统处于峰值压力时,分布式消息队列还能够作为缓冲,削峰填谷,缓解集群的压力,避免整个系统被压垮。垂直化的搜索引擎在分布式系统中是一个非常重要的角色,它既能够满足用户对于全文检索、模糊匹配的需求,解决数据库like查询效率低下的问题,又能够解决分布式环境下,由于采用分库分表,或者使用NoSQL数据库,导致无法进行多表关联或者进行复杂查询的问题。
㈧ 什么是分布式缓存
分布式缓存使用carp(caching
array
routing
protocol)技术,可以产生一种高效率无接缝式的缓存,使用上让多台缓存服务器形同一台,并且不会造成数据重复存放的情况。
同时还有层次式缓存、动态缓存和计划缓存三种。
㈨ mybatis为什么需要分布式缓存
通常为了减轻数据库的压力,我们会引入缓存。在Dao查询数据库之前,先去缓存中找是否有要找的数据,如果有则用缓存中的数据即可,就不用查询数
据库了。如果没有才去数据库中查找。这样就能分担一下数据库的压力。另外,为了让缓存中的数据与数据库同步,我们应该在该数据发生变化的地方加入更新缓存
的逻辑代码。这样无形之中增加了工作量,同时也是一种对原有代码的入侵。这对于有着代码洁癖的程序员来说,无疑是一种伤害。
MyBatis框架早就考虑到了这些问题,因此MyBatis提供了自定义的二级缓存概念,方便引入我们自己的缓存机制,而不用更改原有的业务逻辑。
㈩ EhCache 分布式缓存/缓存集群
一 缓存系统简介 EhCache 是一个纯 Java 的进程内缓存框架 具有快速 精干等特点 是 Hibernate 中默认的 CacheProvider EhCache 应用架构图 下图是 EhCache 在应用程序中的位置
EhCache 的主要特性有 快速 精干 简单 多种缓存策略 缓存数据有两级 内存和磁盘 因此无需担心容量问题 缓存数据会在虚拟机重启的过程中写入磁盘 可以通过 RMI 可插入 API 等方式进行分布式缓存 具有缓存和缓存管理器的侦听接口 支持多缓存管理器实例 以及一个实例的多个缓存区域 提供 Hibernate 的缓存实现 由于 EhCache 是进程中的缓存系统 一旦将应用部署在集群环境中 每一个节点维护各自的缓存数据 当某个节点对缓存数据进行更新 这些更新的数据无法在其它节点 *** 享 这不仅会降低节点运行的效率 而且会导致数据不同步的情况发生 例如某个网站采用 A B 两个节点作为集群部署 当 A 节点的缓存更新后 而 B 节点缓存尚未更新就可能出现用户在浏览页面的时候 一会是更新后的数据 一会是尚未更新的数据 尽管我们也可以通过 Session Sticky 技术来将用户锁定在某个节点上 但对于一些交互性比较强或者是非 Web 方式的系统来说 Session Sticky 显然不太适合 所以就需要用到 EhCache 的集群解决方案 从 版本开始 Ehcache可以使用分布式的缓存了 EhCache 从 版本开始 支持五种集群方案 分别是 ? Terracotta ? RMI ? JMS ? JGroups ? EhCache Server 其中的三种最为常用集群方式 分别是 RMI JGroups 以及 EhCache Server 本文主要介绍RMI的方式 分布式这个特性是以plugin的方式实现的 Ehcache自带了一些默认的分布式缓存插件实现 这些插件可以满足大部分应用的需要 如果需要使用其他的插件那就需要自己开发了 开发者可以通过查看distribution包里的源代码及JavaDoc来实现它 尽管不是必须的 在使用分布式缓存时理解一些ehcahce的设计思想也是有帮助的 这可以参看分布式缓存设计的页面 以下的部分将展示如何让分布式插件同ehcache一起工作 下面列出的是一些分布式缓存中比较重要的方面 ? 你如何知道集群环境中的其他缓存? ? 分布式传送的消息是什么形式? ? 什么情况需要进行复制?增加(Puts) 更新(Updates)或是失效(Expiries)? ? 采用什么方式进行复制?同步还是异步方式? 为了安装分布式缓存 你需要配置一个PeerProvider 一个CacheManagerPeerListener 它们对于一个CacheManager来说是全局的 每个进行分布式操作的cache都要添加一个cacheEventListener来传送消息
二 集群缓存概念及其配置 正确的元素类型 只有可序列化的元素可以进行复制 一些操作 比如移除 只需要元素的键值而不用整个元素 在这样的操作中即使元素不是可序列化的但键值是可序列化的也可以被复制 成员发现(Peer Discovery) Ehcache进行集群的时候有一个cache组的概念 每个cache都是其他cache的一个peer 没有主cache的存在 刚才我们问了一个问题 你如何知道集群环境中的其他缓存?这个问题可以命名为成员发现(Peer Discovery) Ehcache提供了两种机制用来进行成员发现 就像一辆汽车 手动档和自动档 要使用一个内置的成员发现机制要在ehcache的配置文件中指定元素的class属性为 net sf ehcache distribution 自动的成员发现 自动的发现方式用TCP广播机制来确定和维持一个广播组 它只需要一个简单的配置可以自动的在组中添加和移除成员 在集群中也不需要什么优化服务器的知识 这是默认推荐的 成员每秒向群组发送一个 心跳 如果一个成员 秒种都没有发出信号它将被群组移除 如果一个新的成员发送了一个 心跳 它将被添加进群组 任何一个用这个配置安装了复制功能的cache都将被其他的成员发现并标识为可用状态 要设置自动的成员发现 需要指定ehcache配置文件中元素的properties属性 就像下面这样 peerDiscovery=automatic multicastGroupAddress=multicast address | multicast host name multicastGroupPort=port timeToLive= (timeToLive属性详见常见问题部分的描述) 示例 假设你在集群中有两台服务器 你希望同步sampleCache 和sampleCache 每台独立的服务器都要有这样的配置 配置server 和server <class= net sf ehcache distribution properties= peerDiscovery=automatic multicastGroupAddress= />multicastGroupPort= timeToLive= 手动进行成员发现 进行手动成员配置要知道每个监听器的IP地址和端口 成员不能在运行时动态地添加和移除 在技术上很难使用广播的情况下就可以手动成员发现 例如在集群的服务器之间有一个不能传送广播报文的路由器 你也可以用手动成员发现进行单向的数据复制 只让server 知道server 而server 不知道server 配置手动成员发现 需要指定ehcache配置文件中的properties属性 像下面这样 peerDiscovery=manual rmiUrls=//server:port/cacheName //server:port/cacheName … rmiUrls配置的是服务器cache peers的列表 注意不要重复配置 示例 假设你在集群中有两台服务器 你要同步sampleCache 和sampleCache 下面是每个服务器需要的配置 配置server <class= net sf ehcache distribution properties= peerDiscovery=manual />rmiUrls=//server : /sampleCache |//server : /sampleCache 配置server <class= net sf ehcache distribution properties= peerDiscovery=manual />rmiUrls=//server : /sampleCache |//server : /sampleCache 配置CacheManagerPeerListener 每个CacheManagerPeerListener监听从成员们发向当前CacheManager的消息 配置CacheManagerPeerListener需要指定一个 它以插件的机制实现 用来创建CacheManagerPeerListener 的属性有 class – 一个完整的工厂类名 properties – 只对这个工厂有意义的属性 使用逗号分隔 Ehcache有一个内置的基于RMI的分布系统 它的监听器是RMICacheManagerPeerListener 这个监听器可以用 RMI来配置 <class= net sf ehcache distribution RMI properties= hostName=localhost port= />socketTimeoutMillis= 有效的属性是 hostname (可选) – 运行监听器的服务器名称 标明了做为集群群组的成员的地址 同时也是你想要控制的从集群中接收消息的接口在CacheManager初始化的时候会检查hostname是否可用 如果hostName不可用 CacheManager将拒绝启动并抛出一个连接被拒绝的异常 如果指定 hostname将使用InetAddress getLocalHost() getHostAddress()来得到 警告 不要将localhost配置为本地地址 因为它在网络中不可见将会导致不能从远程服务器接收信息从而不能复制 在同一台机器上有多个CacheManager的时候 你应该只用localhost来配置 port – 监听器监听的端口 socketTimeoutMillis (可选) – Socket超时的时间 默认是 ms 当你socket同步缓存请求地址比较远 不是本地局域网 你可能需要把这个时间配置大些 不然很可能延时导致同步缓存失败 配置CacheReplicators 每个要进行同步的cache都需要设置一个用来向CacheManagerr的成员复制消息的缓存事件监听器 这个工作要通过为每个cache的配置增加一个cacheEventListenerFactory元素来完成 <! Sample cache named sampleCache ><cache name= sampleCache maxElementsInMemory= eternal= false timeToIdleSeconds= timeToLiveSeconds= overflowToDisk= false ><cacheEventListenerFactory class= net sf ehcache distribution RMICacheReplicatorFactory properties= replicateAsynchronously=true replicatePuts=true replicateUpdates=true replicateUpdatesViaCopy=false replicateRemovals=true /></cache>class – 使用net sf ehcache distribution RMICacheReplicatorFactory 这个工厂支持以下属性 replicatePuts=true | false – 当一个新元素增加到缓存中的时候是否要复制到其他的peers 默认是true replicateUpdates=true | false – 当一个已经在缓存中存在的元素被覆盖时是否要进行复制 默认是true replicateRemovals= true | false – 当元素移除的时候是否进行复制 默认是true replicateAsynchronously=true | false – 复制方式是异步的(指定为true时)还是同步的(指定为false时) 默认是true replicatePutsViaCopy=true | false – 当一个新增元素被拷贝到其他的cache中时是否进行复制指定为true时为复制 默认是true replicateUpdatesViaCopy=true | false – 当一个元素被拷贝到其他的cache中时是否进行复制(指定为true时为复制) 默认是true 你可以使用ehcache的默认行为从而减少配置的工作量 默认的行为是以异步的方式复制每件事 你可以像下面的例子一样减少RMICacheReplicatorFactory的属性配置 <! Sample cache named sampleCache All missing RMICacheReplicatorFactory properties default to true ><cache name= sampleCache maxElementsInMemory= eternal= true overflowToDisk= false memoryStoreEvictionPolicy= LFU ><cacheEventListenerFactory class= net sf ehcache distribution RMICacheReplicatorFactory /></cache> 常见的问题 Windows上的Tomcat 有一个Tomcat或者是JDK的bug 在tomcat启动时如果tomcat的安装路径中有空格的话 在启动时RMI监听器会失败 参见 bin/wa?A =ind &L=rmi users&P= 和 doc/faq howto bugs/l 由于在Windows上安装Tomcat默认是装在 Program Files 文件夹里的 所以这个问题经常发生 广播阻断 自动的peer discovery与广播息息相关 广播可能被路由阻拦 像Xen和VMWare这种虚拟化的技术也可以阻拦广播 如果这些都打开了 你可能还在要将你的网卡的相关配置打开 一个简单的办法可以告诉广播是否有效 那就是使用ehcache remote debugger来看 心跳 是否可用 广播传播的不够远或是传得太远 你可以通过设置badly misnamed time to live来控制广播传播的距离 用广播IP协议时 timeToLive的值指的是数据包可以传递的域或是范围 约定如下 是限制在同一个服务器 是限制在同一个子网 是限制在同一个网站 是限制在同一个region 是限制在同一个大洲 是不限制 译者按 上面这些资料翻译的不够准确 请读者自行寻找原文理解吧 在Java实现中默认值是 也就是在同一个子网中传播 改变timeToLive属性可以限制或是扩展传播的范围
三 RMI方式缓存集群/配置分布式缓存 RMI 是 Java 的一种远程方法调用技术 是一种点对点的基于 Java 对象的通讯方式 EhCache 从 版本开始就支持 RMI 方式的缓存集群 在集群环境中 EhCache 所有缓存对象的键和值都必须是可序列化的 也就是必须实现 java io Serializable 接口 这点在其它集群方式下也是需要遵守的 下图是 RMI 集群模式的结构图
采用 RMI 集群模式时 集群中的每个节点都是对等关系 并不存在主节点或者从节点的概念 因此节点间必须有一个机制能够互相认识对方 必须知道其它节点的信息 包括主机地址 端口号等 EhCache 提供两种节点的发现方式 手工配置和自动发现 手工配置方式要求在每个节点中配置其它所有节点的连接信息 一旦集群中的节点发生变化时 需要对缓存进行重新配置 由于 RMI 是 Java 中内置支持的技术 因此使用 RMI 集群模式时 无需引入其它的 Jar 包 EhCache 本身就带有支持 RMI 集群的功能 使用 RMI 集群模式需要在 ehcache xml 配置文件中定义 节点 分布式同步缓存要让这边的cache知道对方的cache 叫做Peer Discovery(成员发现) EHCache实现成员发现的方式有两种 手动查找 A 在ehcache xml中配置PeerDiscovery成员发现对象 Server 配置 配置本地hostName port是 分别监听 : 的mobileCache和 : 的mobileCache 注意这里的mobileCache是缓存的名称 分别对应着server server 的cache的配置 <?xml version= encoding= gbk ?><ehcache xmlns:xsi= instance xsi:noNamespaceSchemaLocation= ehcache xsd > <diskStore path= java io tmpdir /> <! 集群多台服务器中的缓存 这里是要同步一些服务器的缓存 server hostName: port: cacheName:mobileCache server hostName: port: cacheName:mobileCache server hostName: port: cacheName:mobileCache 注意 每台要同步缓存的服务器的RMI通信socket端口都不一样 在配置的时候注意设置 > <! server 的配置 > < class= net sf ehcache distribution properties= hostName=localhost port= socketTimeoutMillis= peerDiscovery=manual rmiUrls=// : /mobileCache|// : /mobileCache /></ehcache>以上注意元素出现的位置在diskStore下
同样在你的另外 台服务器上增加配置 Server 配置本地host port为 分别同步 : 的mobileCache和 : 的mobileCache <! server 的配置 >< class= net sf ehcache distribution properties= hostName=localhost port= socketTimeoutMillis= peerDiscovery=manual rmiUrls=// : /mobileCache|// : /mobileCache />Server 配置本地host port为 分别同步 : 的mobileCache缓存和 : 的mobileCache缓存 <! server 的配置 >< class= net sf ehcache distribution properties= hostName=localhost port= socketTimeoutMillis= peerDiscovery=manual rmiUrls=// : /mobileCache|// : /mobileCache />这样就在三台不同的服务器上配置了手动查找cache的PeerProvider成员发现的配置了 值得注意的是你在配置rmiUrls的时候要特别注意url不能重复出现 并且端口 地址都是对的 如果指定 hostname将使用InetAddress getLocalHost() getHostAddress()来得到 警告 不要将localhost配置为本地地址 因为它在网络中不可见将会导致不能从远程服务器接收信息从而不能复制 在同一台机器上有多个CacheManager的时候 你应该只用localhost来配置 B 下面配置缓存和缓存同步监听 需要在每台服务器中的ehcache xml文件中增加cache配置和cacheEventListenerFactory cacheLoaderFactory的配置 <defaultCache maxElementsInMemory= eternal= false timeToIdleSeconds= timeToLiveSeconds= overflowToDisk= false /><! 配置自定义缓存 maxElementsInMemory:缓存中允许创建的最大对象数 eternal:缓存中对象是否为永久的 如果是 超时设置将被忽略 对象从不过期 timeToIdleSeconds:缓存数据空闲的最大时间 也就是说如果有一个缓存有多久没有被访问就会被销毁 如果该值是 就意味着元素可以停顿无穷长的时间 timeToLiveSeconds:缓存数据存活的时间 缓存对象最大的的存活时间 超过这个时间就会被销毁 这只能在元素不是永久驻留时有效 如果该值是 就意味着元素可以停顿无穷长的时间 overflowToDisk:内存不足时 是否启用磁盘缓存 memoryStoreEvictionPolicy:缓存满了之后的淘汰算法 每一个小时更新一次缓存( 小时过期) ><cache name= mobileCache maxElementsInMemory= eternal= false overflowToDisk= true timeToIdleSeconds= timeToLiveSeconds= memoryStoreEvictionPolicy= LFU > <! RMI缓存分布同步查找 class使用net sf ehcache distribution RMICacheReplicatorFactory 这个工厂支持以下属性 replicatePuts=true | false – 当一个新元素增加到缓存中的时候是否要复制到其他的peers 默认是true replicateUpdates=true | false – 当一个已经在缓存中存在的元素被覆盖时是否要进行复制 默认是true replicateRemovals= true | false – 当元素移除的时候是否进行复制 默认是true replicateAsynchronously=true | false – 复制方式是异步的 指定为true时 还是同步的 指定为false时 默认是true replicatePutsViaCopy=true | false – 当一个新增元素被拷贝到其他的cache中时是否进行复制 指定为true时为复制 默认是true replicateUpdatesViaCopy=true | false – 当一个元素被拷贝到其他的cache中时是否进行复制 指定为true时为复制 默认是true = > <! 监听RMI同步缓存对象配置 注册相应的的缓存监听类 用于处理缓存事件 如put remove update 和expire > <cacheEventListenerFactory class= net sf ehcache distribution RMICacheReplicatorFactory properties= replicateAsynchronously=true /> replicatePuts=true replicateUpdates=true replicateUpdatesViaCopy=false replicateRemovals=true <! 用于在初始化缓存 以及自动设置 > <bootstrapCacheLoaderFactory class= net sf ehcache bootstrap BootstrapCacheLoaderFactory /></cache> C 这样就完成了 台服务器的配置 下面给出server 的完整的ehcache xml的配置 <?xml version= encoding= gbk ?><ehcache xmlns:xsi= instance xsi:noNamespaceSchemaLocation= ehcache xsd > <diskStore path= java io tmpdir /> <!集群多台服务器中的缓存 这里是要同步一些服务器的缓存 server hostName: port: cacheName:mobileCache server hostName: port: cacheName:mobileCache server hostName: port: cacheName:mobileCache 注意每台要同步缓存的服务器的RMI通信socket端口都不一样 在配置的时候注意设置 > <! server 的配置 > < class= net sf ehcache distribution properties= hostName=localhost port= socketTimeoutMillis= peerDiscovery=manual rmiUrls=// : /mobileCache|// : /mobileCache /> <defaultCache maxElementsInMemory= eternal= false timeToIdleSeconds= timeToLiveSeconds= overflowToDisk= false /> <! 配置自定义缓存 maxElementsInMemory:缓存中允许创建的最大对象数 eternal:缓存中对象是否为永久的 如果是 超时设置将被忽略 对象从不过期 timeToIdleSeconds:缓存数据空闲的最大时间 也就是说如果有一个缓存有多久没有被访问就会被销毁 如果该值是 就意味着元素可以停顿无穷长的时间 timeToLiveSeconds:缓存数据存活的时间 缓存对象最大的的存活时间 超过这个时间就会被销毁 这只能在元素不是永久驻留时有效 如果该值是 就意味着元素可以停顿无穷长的时间 overflowToDisk:内存不足时 是否启用磁盘缓存 memoryStoreEvictionPolicy:缓存满了之后的淘汰算法 每一个小时更新一次缓存( 小时过期) > <cache name= mobileCache maxElementsInMemory= eternal= false overflowToDisk= true timeToIdleSeconds= timeToLiveSeconds= memoryStoreEvictionPolicy= LFU > <! RMI缓存分布同步查找 class使用net sf ehcache distribution RMICacheReplicatorFactory 这个工厂支持以下属性 replicatePuts=true | false – 当一个新元素增加到缓存中的时候是否要复制到其他的peers 默认是true replicateUpdates=true | false – 当一个已经在缓存中存在的元素被覆盖时是否要进行复制 默认是true replicateRemovals= true | false – 当元素移除的时候是否进行复制 默认是true replicateAsynchronously=true | false – 复制方式是异步的 指定为true时 还是同步的 指定为false时 默认是true replicatePutsViaCopy=true | false – 当一个新增元素被拷贝到其他的cache中时是否进行复制 指定为true时为复制 默认是true replicateUpdatesViaCopy=true | false – 当一个元素被拷贝到其他的cache中时是否进行复制 指定为true时为复制 默认是true = > <! 监听RMI同步缓存对象配置 注册相应的的缓存监听类 用于处理缓存事件 如put remove update 和expire > <cacheEventListenerFactory class= net sf ehcache distribution RMICacheReplicatorFactory properties= replicateAsynchronously=true /> replicatePuts=true replicateUpdates=true replicateUpdatesViaCopy=false replicateRemovals=true <! 用于在初始化缓存 以及自动设置 > <bootstrapCacheLoaderFactory class= net sf ehcache bootstrap BootstrapCacheLoaderFactory /> </cache></ehcache> 自动发现 自动发现配置和手动查找的方式有一点不同 其他的地方都基本是一样的 同样在ehcache xml中增加配置 配置如下 <! 搜索某个网段上的缓存timeToLive 是限制在同一个服务器 是限制在同一个子网 是限制在同一个网站 是限制在同一个region 是限制在同一个大洲 是不限制 >< class= net sf ehcache distribution properties= peerDiscovery=automatic multicastGroupAddress= multicastGroupPort= timeToLive= /> lishixin/Article/program/Java/hx/201311/25706