------------分布式、集群------------
1.什么是分布式架构?什么是集群?
- 分布式 :一个业务分拆为多个子业务,分别布署到不同服务器上,需要各子业务之间配合才能完成整个业务逻辑。比如一个网站,会员系统放在A服务器,消息系统放在B服务器,支付系统放在C服务器等等,那么这个网站也算是一个分布式网站。
- 集群 :同一个业务被布署到不同的服务器以降低单台服务器的压力,多台服务器一起实现同一个业务。
相同点:分布式和集群都是需要有很多节点服务器通过网络协同工作完成整体的任务目标。
区别:
- 分布式是指将不同的业务分给不同的服务器,每一个服务器都完成不同的业务,每个服务器缺一不可;而集群指的是将几台服务器集中在一起,实现同一业务。每台服务器并不是缺一不可的。
- 分布式是以缩短单个任务的执行时间来提升效率的,而集群则是通过提高单位时间内执行的任务数来提升效率的。
【注】分布式中的每一个节点,都可以做集群。 而集群并不一定就是分布式的。
举个例子说明:
一个小饭店里,原来只有一个厨师,买菜洗菜切菜炒菜全都是这厨师一人干,这叫 单机结构 。后来饭店客人多了,一个厨师确实忙活不过来,于是饭店又请来了个厨师,现在饭店有了两个厨师,这两个厨师都能从头到尾做一样的菜,这两个厨师就是 集群 。这样两个厨师集群确实能做更多的活,客人点10个菜可以分配每个人抄5个,单个厨师压力减少了。但是这样还不行,为了让厨师专心做菜,把菜做得更好,于是饭店又请来了采购和配菜师,采购负责买菜,配菜师负责把菜挑拣洗好切好给厨师做好准备,那么采购、配菜师、厨师之间就行程了 分布式系统 。后来一个采购和一个配菜师也忙不过来了,于是又再请多了个采购和配菜师,那么两个采购又形成了集群,两个配菜师也形成了集群,同样两个厨师也还是集群,这样多个集群一起就行程了 分布式集群系统 。
分布式和集群的关系:
分布式主要的功能是将我们的系统模块化,将系统进行解耦,方便我们的维护和开发。但是其并不能解决并发问题,也无法保证我们的系统在服务器宕机后的正常运转。
集群恰好弥补了分布式的缺陷,集群,就是多个服务器处理相同的业务,一方面可以解决或者说改善我们系统的并发问题,另一方面可以解决我们服务器如果出现一定数量的宕机后,系统仍然可以正常运转。
2. 为什么要用分布式?为什么要用集群?
为什么用分布式?
传统的项目中我们将各个模块放在一个系统中,系统过于庞大,开发维护困难,各个功能模块之间的耦合度高,无法针对单个模块进行优化。而使用分布式架构将系统模块化,便于我们的维护和开发。
我们的项目之所以利用分布式架构开发,是因为整个项目实现的功能较多,使每个功能模块独立出来,降低了各系统之间的耦合度,增删一个功能不会影响其他功能模块。
为什么用集群?
项目如果部署在一台Tomcat上,所有的请求都由这一台服务器处理,会存在很大风险:
- 单台服务器并发处理能力有限。(一般单台服务器处理的并发量为250左右,超过250,可能会出现数据丢失,链接不稳定的情况)
- 使用单台服务器,一旦服务器故障,整个服务就无法访问了。
- 单台服务器计算能力低,无法完成复杂的海量数据计算。
集群是是指将多台服务器集中在一起,每台服务器都实现相同的业务,做相同的事情。但是每台服务器并不是缺一不可,存在的作用主要是缓解并发压力和单点故障转移问题。
集群的两大特点与两大能力:
两大特点:可扩展性、高可用性
- 可扩展性:集群的性能不限制于单一的服务实体,新的服务实体可以动态的添加到集群,从而增强集群的性能。
- 高可用性:集群当其中一个节点发生故障时,这台节点上面所运行的应用程序将在另一台节点被自动接管,消除单点故障对于增强数据可用性、可达性和可靠性是非常重要的。
两大能力:负载均衡、错误恢复
- 负载均衡:负载均衡把任务比较均匀的分布到集群环境下的计算和网络资源,以提高数据吞吐量。
- 错误恢复:如果集群中的某一台服务器由于故障或者维护需要无法使用,资源和应用程序将转移到可用的集群节点上。这种由于某个节点的资源不能工作,另一个可用节点中的资源能够透明的接管并继续完成任务的过程,叫做错误恢复。
3.为什么一般集群都要用3台服务器来组建?
- 一台机器叫单机,不算分布式。
- 两台机器组成的的集群,当有一台机器出现故障另一台机器并不能及时作出反应,况且两台机器当稳定性不一定要比单机更好。
- 当有三台服务器是能实现zookeeper容错。
4. 你说项目基于SOA架构,什么是SOA架构?
SOA:面向服务的架构。也就是把工程都拆分成服务层工程、表现层工程。服务层中包含业务逻辑,只需要对外提供服务即可。表现层只需要处理和页面的交互,业务逻辑都是调用服务层的服务来实现。工程都可以独立部署。
项目基于SOA的架构,表现层和服务层是不同的工程。所以要实现商品列表查询需要两个系统之间进行通信。如何实现远程通信?
使用dubbo。使用rpc协议进行远程调用,直接使用socket通信,传输效率高,并且可以统计出系统之间的调用关系、调用次数,管理服务。
4.介绍一下dubbo.
Dubbo是一个分布式服务框架,致力于提供高性能和透明化的RPC远程服务调用方案。
(注意不要与负载均衡搞混:负载均衡是对外提供一个公共地址,请求过来时通过轮询、随机等,路由到不同的服务器。)
说一下dubbo的架构、服务注册与发现的流程:
Dubbo有5种节点角色:
- Privoder(服务提供者):用于暴露服务
- Consumer(服务消费者):用来调用远程服务
- Registry(注册中心):注册与发现服务
- Monitor(监控中心):统计服务的调用次数与调用时间
- Container(服务运行容器)
流程:
0. 服务容器用来启动、加载、运行服务提供者;
1. 服务提供者在启动时,向注册中心注册自己提供的服务;
2. 服务消费者在启动时,想注册中心订阅自己所需的服务;
3. 注册中心返回服务提供者地址列表给消费者。如果有变更,注册中心将基于长连接推送变更数据给消费者;
4. 服务消费者从提供者地址列表中,(基于软负载均衡)选一台服务提供者进行调用,如果调用失败,再选另一台调用;
5. 服务提供者和消费者,在内存中累计调用次数与调用时间,定时每分钟发送统计数据到监控中心。
注册中心的作用?Dubbo的注册中心有哪些?你使用的哪个?
注册中心存储着Provider注册的远程服务,并将其所管理的服务列表通知给服务消费方(Consumer),且注册中心和提供方和消费方之间均保持长连接,可以获取Provider发布的服务的变化情况,并将最新的服务列表推送给Consumer。
Dubbo的注册中心有Zookeeper、Redis、Multicast、Simple等。我们使用的是Zookeeper。
Dubbo的spring配置方式?
服务定义部分放在服务提供方的xml文件:在提供方增加暴露服务配置<dubbo:service>
<!-- ================项目中服务层为提供方,使用dubbo发布服务=================== --> <!-- 提供方应用信息,用于计算依赖关系 --> <dubbo:application name="taotao-manager" /> <dubbo:registry protocol="zookeeper" address="192.168.25.129:2181" /> <!-- 用dubbo协议在20880端口暴露服务 --> <dubbo:protocol name="dubbo" port="20880" /> <!-- 声明需要暴露的服务接口 --> <dubbo:serviceinterface="com.taotao.service.ItemService" ref="itemServiceImpl" />
服务引用部分放在服务消费方的xml文件:在消费方增加引用服务配置<dubbo:reference>
<!-- ================项目中表现层为消费方,引用dubbo服务======================= --> <dubbo:application name="taotao-manager-web" /> <dubbo:registry protocol="zookeeper" address="192.168.25.129:2181" /> <dubbo:referenceinterface="com.taotao.service.ItemService" id="itemService" />
【注】address的值是自己的zookeeper所在系统的ip地址和端口,如果是集群,就用逗号分开:
<dubbo:registry protocol="zookeeper" address="192.168.25.140:2181,192.168.25.140:2182,192.168.25.140:2183" /dubbo:registry>
Dubbo默认使用的是什么通信框架,还有别的选择吗?
默认也推荐使用netty框架,还有mina。
5.介绍Zoookeeper
ZooKeeper是一个开源的分布式协调服务,它是集群的管理者,监视着集群中各个节点的状态根据节点提交的反馈进行下一步合理操作。最终,将简单易用的接口和性能高效、功能稳定的系统提供给用户。
分布式应用程序可以基于Zookeeper实现诸如数据发布/订阅、负载均衡、命名服务、分布式协调/通知、集群管理、Master选举、分布式锁和分布式队列等功能。
- Zookeeper的核心是原子广播,这个机制保证了各个Server之间的同步。实现这个机制的协议叫做Zab协议。Zab协议有两种模式,它们分别是恢复模式(选主)和广播模式(同步)。当服务启动或者在领导者崩溃后,Zab就进入了恢复模式,当领导者被选举出来,且大多数Server完成了和leader的状态同步以后恢复模式就结束了。状态同步保证了leader和Server具有相同的系统状态。
- 为了保证事务的顺序一致性,zookeeper采用了递增的事务id号(zxid)来标识事务。所有的提议(proposal)都在被提出的时候加上了zxid。实现中zxid是一个64位的数字,它高32位是epoch用来标识leader关系是否改变,每次一个leader被选出来,它都会有一个新的epoch,标识当前属于那个leader的统治时期。低32位用于递增计数。
服务器角色:Leader、Follower、Observer
- Leader(领导者):事务请求的唯一调度和处理者,保证集群事务处理的顺序性;集群内部各服务的调度者。
- Follower(跟随者):处理客户端的非事务请求,转发事务请求给Leader服务器;参与Leader选举投票。
- Observer(观察者):3.3版本以后引入的一个服务器角色,处理客户端的非事务请求,转发事务请求给Leader服务器;不参与任何形式的投票。
Zookeeper 下Server工作状态:
服务器具有四种状态,分别是Looking、Following、Leading、Observing。
- Looking:寻找Leader状态。当服务器处于该状态时,它会认为当前集群中没有Leader,因此需要进入Leader选举状态。
- Following:跟随者状态。表明当前服务器角色是Follower。
- Leading:领导者状态。表明当前服务器角色是Leader。
- Observing:观察者状态。表明当前服务器角色是Observer。
Leader 选举:半数通过
每次投票后,服务器都会统计投票信息,判断是否已经有过半机器接受到相同的投票信息,如果有,此时便认为已经选出了Leader。
------------Redis------------
我的这篇博客Redis面试题
还有一篇读书笔记《Redis开发与运维》
1.为什么用redis?
像项目中首页的大广告和商品类目这些不需要经常修改的数据,如果用户每次刷新页面的时候都要去数据库中查询,这样会浪费资源和增加数据库的压力。所以我们想当把这些数据添加到一个缓存中,用户去访问的时候,先去缓存中查询,如果没有,再去数据库中查询,然后把查询到的数据添加到缓存中。(操作缓存就是直接操作内存,所以速度相当快)
2.介绍一下redis.
Redis是一种基于 key-value 对的非关系型数据库(nosql),与很多键值对数据库不同的是,Redis中的值可以是由string(字符串)、hash(哈希)、list(列表)、set(集合)、zset(有序集合)等多种数据结构和算法组成,因此Redis可以满足很多的应用场景。而且因为Redis会将所有数据都存放在内存中,所以它的读写性能非常惊人。不仅如此,Redis还提供了持久化功能(RDB和AOF),防止redis宕机时的数据丢失。(可以将内存的数据利用快照(RDB)和日志(AOF)的形式保存到硬盘上,这样在发生类似断电或者机器故障的时候,内存中的数据不会“丢失”)。
Redis的主要缺点是数据库容量受到物理内存的限制,不能用作海量数据的高性能读写,因此Redis适合的场景主要局限在较小数据量的高性能操作和运算上。
我们使用的是spring与jedis整合的客户端,可以利用jedis做分片式集群,解决了redis内存受限的问题。
3.你说使用redis的集群,为什么搭建redis集群?搭建redis集群的步骤?
使用集群的原因就是 单台服务器并发处理能力有限,并且一旦服务器故障,整个服务就无法访问了。集群就是将多台服务器都集中在一起,每台服务器都实现相同的业务,并且如有一台服务器发生故障,他所运行的业务可以被其他服务器进行接管,这样就缓解并发压力和单点故障转移问题,保障了redis的性能。
搭建redis集群的步骤:
(至少3个节点,为了集群的高可用,为每一个节点增加一个备份机。(6台服务器,3主3从)。搭建伪分布式集群方案:在一台机器里面运行6个redis实例。端口需要不同(7001-7006)。)
1、首先在linux系统上安装一个redis:步骤就是【首先上传redis源码包(put)并解压(tar -zxvf),然后进行编译(make)和安装】
- 第1步:将redis的源码包上传到linux系统。 Alt+p打开sftp窗口:输入put "F:/java/ziyuan/redis-3.0.0.tar.gz"
- 第2步:解压:tar -zxvf redis-3.0.0.tar.gz
- 第3步:进行编译。 cd到解压后的目录 输入命令:make
- 第4步:进行安装。 输入命令:make install PREFIX=/usr/local/redis
2、然后创建我们集群要安装的目录(redis-cluster),copy之前搭建好的redis共6份,然后分别进入其bin目录修改redis.conf配置文件,分别更改端口号(为7001到7006),并开启集群cluster-enabled yes。
- 创建我们集群要安装的目录:[root@localhost local]# mkdir redis-cluster
- copy 之前搭建好的redis 并改名为redis01 :[root@localhost local]# cp redis/ redis-cluster/redis01 -r
- 分别进入其bin目录修改redis.conf配置文件[root@localhost bin]# vim redis.conf
- 然后启动各个节点并检查 redis 启动情况。cd /usr/local/redis-cluster/redis01/bin ./redis-server redis.conf
3、Redis 官方提供了 redis-trib.rb 这个工具来创建集群,这个工具是用ruby脚本实现的,所以需要安装ruby:
yum -y
install
ruby ruby-devel rubygems rpm-build
gem
install
redis
4、然后使用ruby脚本搭建集群。从解压目录下的src下的拷贝redis-trib.rb文件到redis-cluster目录中 cp redis-trib.rb /usr/local/redis-cluster/,接着执行下面命令就可完成安装:
[root@localhost redis-cluster]# ./redis-trib.rb create --replicas 1 192.168.25.153:7001 192.168.25.153:7002 192.168.25.153:7003 192.168.25.153:7004 192.168.25.153:7005 192.168.25.153:7006
说一下redis cluster原理:
redis cluster在设计的时候,就考虑到了去中心化,去中间件,也就是说,集群中的每个节点都是平等的关系,每个节点都保存各自的数据和整个集群的状态。每个节点都和其他所有节点连接,而且这些连接保持活跃,这样就保证了我们只需要连接集群中的任意一个节点,就可以获取到其他节点的数据。
Redis 集群没有并使用传统的一致性哈希来分配数据,而是采用一种叫做哈希槽 (hash slot)
的方式来分配数据的。redis cluster 默认分配了 16384 个slot,当需要在 Redis 集群中放置一个 key-value 时,redis先对key使用crc16算法算出一个结果,然后把结果对16384求余数,这样每个key都会对应一个编号在 0-16383之间的哈希槽,redis会根据节点数量大致均等的将哈希槽映射到不同的节点。
Redis 集群会把数据存在一个 master节点,然后在这个 master 和其对应的salve之间进行数据同步。当读取数据时,也根据一致性哈希算法到对应的 master 节点获取数据。只有当一个master 挂掉之后,才会启动一个对应的 salve 节点,充当 master 。
需要注意的是:必须要3个或以上
的主节点,否则在创建集群时会失败,并且当存活的主节点数小于总节点数的一半时,整个集群就无法提供服务了。
https://www.cnblogs.com/wuxl360/p/5920330.html
4.如何保证Ridis数据与MySql数据一致性?
采用如下策略:
- 读的时候:先读缓存,读到数据则直接返回;如果没有读到,就读数据库,同时将数据放入缓存,并返回响应。
- 更新的时候:先更新数据库,然后再删除缓存。
为什么?
-
读的逻辑大家都很容易理解,谈谈更新。如果不采取我提到的这种更新方法,你还能想到什么更新方法呢?大概会是:先删除缓存,然后再更新数据库。这么做引发的问题是,如果A,B两个线程同时要更新数据,并且A,B已经都做完了删除缓存这一步,接下来,A先更新了数据库,C线程读取数据,由于缓存没有,则查数据库,并把A更新的数据,写入了缓存,最后B更新数据库。那么缓存和数据库的值就不一致了。
-
另外有人会问,如果采用你提到的方法,为什么最后是把缓存的数据删掉,而不是把更新的数据写到缓存里?这么做引发的问题是,如果A,B两个线程同时做数据更新,A先更新了数据库,B后更新数据库,则此时数据库里存的是B的数据。而更新缓存的时候,是B先更新了缓存,而A后更新了缓存,则缓存里是A的数据。这样缓存和数据库的数据也不一致。
5. redis两种持久化方式?
Redis支持RDB和AOF两种持久化机制,持久化功能有效地避免因进程退出造成的数据丢失问题,当下次重启时利用之前持久化的文件即可实现数据恢复。
RDB持久化:是把当前进程数据生成快照保存到硬盘的过程。触发RDB持久化过程分为手动触发(save、bgsave)和自动触发。
- 优点:
- RDB是一个紧凑压缩的二进制文件,代表Redis在某个时间点上的数据快照。非常适用于备份,全量复制等场景。比如每6小时执行bgsave备份,并把RDB文件拷贝到远程机器或者文件系统中(如hdfs),用于灾难恢复。
- Redis加载RDB恢复数据远远快于AOF的方式。
- 缺点:
- RDB方式数据没办法做到实时持久化/秒级持久化。因为bgsave每次运行都要执行fork操作创建子进程,属于重量级操作,频繁执行成本过高。
AOF(append only file)持久化:以独立日志的方式记录每次写命令,重启时再重新执行AOF文件中的命令达到恢复数据的目的。AOF的主要作用是解决了数据持久化的实时性。
AOF默认是默认不开启的,开启AOF功能需要设置配置:appendonly yes。
AOF缓冲区同步文件策略,由参数appendfsync控制:
appendfsync always #每次有数据修改发生时都会写入AOF文件,这样会严重降低Redis的速度
appendfsync everysec #每秒钟同步一次,显示地将多个写命令同步到硬盘
appendfsync no #让操作系统决定何时进行同步
【注】如果同时配了RDB和AOF,优先加载AOF。
5. 你们redis 存的是什么格式的数据,都是怎么存的?
redis中存放数据都是key -value的形式。
我们商城使用String格式来存放的。拿商品来说:商品的Id就是Key ,商品相关的商品信息组成一个JSON存放。
------------数据库-----------
用到了数据库事务
<!-- 事务管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <!-- 数据源 --> <property name="dataSource" ref="dataSource" /> </bean> <!-- 通知 --> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <!-- 传播行为 --> <tx:method name="save*" propagation="REQUIRED" /> <tx:method name="insert*" propagation="REQUIRED" /> <tx:method name="add*" propagation="REQUIRED" /> <tx:method name="create*" propagation="REQUIRED" /> <tx:method name="delete*" propagation="REQUIRED" /> <tx:method name="update*" propagation="REQUIRED" /> <tx:method name="find*" propagation="SUPPORTS" read-only="true" /> <tx:method name="select*" propagation="SUPPORTS" read-only="true" /> <tx:method name="get*" propagation="SUPPORTS" read-only="true" /> </tx:attributes> </tx:advice> <!-- 切面 --> <aop:config> <aop:advisor advice-ref="txAdvice" pointcut="execution(* com.taotao.service.*.*(..))" /> </aop:config>