在ZooKeeper中,客户端和服务端建立连接后会创建一个session(会话),每一个session对应一个全局唯一的会话ID(Session ID)。就像浏览器和Web服务器一样,首次连接Web服务器的时候为了跟踪客户端,Web服务器会创建一个Session。
zookeeper服务器和客户端之间维持的是一个长连接,客户端会定时向服务器发送心跳来维持Session的有效性(每次发送心跳就会刷新session超时时间)。正常情况下Session一直有效,并且ZK集群所有机器上都保存这个Session信息。当出现网络中断或zookeeper服务挂掉时,客户端会主动在地址列表(实例化ZK对象的时候传入构造方法的那个参数connectString)中选择新的地址进行连接(如果没有备选地址,会不断重试之前的那个地址),如果有新的地址可以连接,在中断时长小于sessionTimeout值时,zookeeper客户端自动重连。如果中断时长大于sessionTimeout值时,将出现session expired的异常,这个时候就需要开发者手动做一些处理,比如使用zookeeper的create重建连接。
在创建zookeeper对象时,指定的Watcher是zookeeper客户端默认的Watcher,可以在它的process方法中监听到Session超时事件,具体做法:
重新new 一个zookeeper即可,使用zookeeper提供的API,如下:
但是需要注意session超时之后,之前使用Java API注册的Watcher将丢失,比如:
所以在重建连接之后,你需要重新添加监听。这篇文章我们重点理解Zookeeper选举机制的思路。
一,Zookeeper选举过程中服务器的状态。
LOOKING:寻找leader状态,该状态下,服务器认为当前集群没有leader,会发起leader选举。在选举过程中,所有服务器的状态都是LOOKING。
FOLLOWING:跟随者状态,该状态下,当前服务器是follower,并且知道leader是谁。此时选举已经结束。
LEADING:领导者状态,该状态下,当前服务器是leader,会与follower维持心跳检测。此时选举已经结束。
OBSERVING:观察者状态,该状态下的服务器是observer,不参与选举。
二,Zookeeper选票数据结构
每个服务器在进行leader选举时,都会发送以下几个关键属性信息:
logicalclock:投票轮次,自增的,volatile的,初始值为1,也就是第一轮选举。
state:当前服务器的状态。
self_id:当前服务器的myid。
self_zxid:当前服务器的最新的zxid。
vote_id:当前服务器推举的leader服务器的myid。
vote_zxid:当前服务器推举的leader服务器的最新的zxid。
三,Zookeeper选举算法
从340版本开始,Zookeeper使用FastLeaderElection选举算法,可以解决之前的LeaderElection算法收敛慢的问题。更为重要的是,FastLeaderElection算法解决了脑裂问题,保证leader的唯一性。也就是说,从Zookeeper340版本开始,Zookeeper可能存在的问题只有2个了:
1,客户端没有缓存。
2,没有自我保护机制。
四,Zookeeper选举流程
1,自增选举轮次。
Zookeeper选举机制有一个前提条件:在一个轮次的选举中,所有选票必须属于该轮次。在选举的某一时刻,确实可能存在某张选票不属于该轮次的情况。所以Zookeeper在选举过程中,始终会先核对选票的轮次。
2,初始化选票。
每个服务器在广播自己的选票时,都会先清空投票箱,这个投票箱存放的是所有接收到的来自其他服务器的选票。投票箱中只记录每个服务器的最后一次投票,如果服务器更新自己的投票,则其他服务器会更新该服务器的选票。
举个例子:服务器2投票给服务器3,服务器3投票给服务器1,则服务器1的投票箱中有如下记录
(2,3),(3,1),(1,1)
当然,这里的选票的结构是简化版的,如果加上选举轮次logicalclock,可能是这样的:
(8,2,3),(8,3,1),(8,1,1)
第一位代表当前的选举轮次,第8轮选举。
3,发送初始化选票。
每个服务器在投票开始阶段,都把票投给自己,然后通过广播通知其他服务器。
4,接收外部选票。
每台服务器都会尝试从其他服务器获取选票,并保存到自己的投票箱。
5,判断选举轮次logicalclock。
确保是同一轮次的投票。如果当前服务器发现自己的轮次落后了,则自增logicalclock,然后重新发送广播告诉其他服务器。
6,选票PK确认自己最终的投票。
注意,在这个阶段,每台服务器都可能改变自己的想法,重新确定把选票投给谁。
有3条规则:
第一条规则:如果当前服务器的logicalclock小于其他服务器,说明自己的选举轮次过期了,此时更新自己的logicalclock,然后重新把自己的选票发送出去。
第二条规则:如果当前服务器的logicalclock等于其他服务器,说明大家进行的是同一轮次的选举,此时比较二者的vote_zxid,vote_zxid大者获胜。如果当前服务器输了,则更新自己的投票为胜者,然后广播告诉其他服务器。
第三条规则:如果当前服务器的logicalclock等于其他服务器,说明大家进行的是同一轮次的选举,此时比较二者的vote_zxid,如果vote_zxid也相等,则比较二者的vote_myid,vote_myid大者获胜。如果当前服务器输了,则更新自己的投票为胜者,然后广播告诉其他服务器。
7,统计选票。
如果已经确定有过半服务器认可了自己的投票,则终止投票。否则继续接收其他服务器的投票。
8,更新服务器状态。
投票结束后,服务器更新自己的状态serverState,如果投给自己的选票过半了,则将自己更新为LEADING,否则将自己更新为FOLLOWING。
这里思考一个问题:Zookeeper启动阶段,myid最大的服务器是不是一定会被选举为leader?
dubbo 是一个远程调用服务的分布式框架,可以实现远程通讯、动态配置、地址路由等等功能。比如在入门demo里的暴露服务,使得远程调用的协议可以使用dobbo协议( dubbo://xxxx )或者其它协议,可以配置zookeeper集群地址,实现软负载均衡并配置均衡方式等。在不搭配注册中心的时候,它也是可以实现服务端和调用端的通信的,这种方式是点对点通信的,所谓“没有中间商”。但是如果配置服务发布和调用端过多特别是集群的方式提供服务的时候,就会暴露许多的问题:增加节点需要修改配置文件、服务端机器宕机后不能被感知等。它可以通过集成注册中心,来动态地治理服务发布和服务调用。相当于把服务注册和发布推送的功能分摊给了(zookeeper)注册中心。
Dubbo实现服务调用是通过RPC的方式,即客户端和服务端共用一个接口(将接口打成一个jar包,在客户端和服务端引入这个jar包),客户端面向接口写调用,服务端面向接口写实现,中间的网络通信交给框架去实现。
咱们来看下Spring 配置声明暴露服务,providerxml文件
再来看服务消费者,consumerxml文件
这就是典型的点对点的服务调用。当然我们为了高可用,可以在consumerxml中配置多个服务提供者,并配置响应的负载均衡策略。
配置多个服务调用者在comsumerxml的dubbo:reference标签的url属性中加入多个地址,中间用分号隔开即可;配置负载均衡策略在comsumerxml的dubbo:reference标签中增加loadbalance属性即可,值可以为如下四种类型:
那么目前的架构有什么问题呢?
1当服务提供者增加节点时,需要修改配置文件。
2当其中一个服务提供者宕机时,服务消费者不能及时感知到,还会往宕机的服务发送请求。
这个时候就需要引入注册中心了,Dubbo目前支持4种注册中心(multicast、zookeeper、redis、simple)推荐使用Zookeeper注册中心,要使用注册中心,只需要将providerxml和consumerxml更改为如下:
如果zookeeper是一个集群,则多个地址之间用逗号分隔即可
把consumerxml中配置的直连的方式去掉
注册信息在zookeeper中如何保存?
启动上面服务后,我们观察zookeeper的根节点多了一个dubbo节点及其他,图示如下
最后一个节点中服务的地址,为什么把最后一个节点标成绿色?因为最后一个节点是临时节点,而其他节点是持久节点,这样,当服务宕机时,这个节点就会自动消失,不再提供服务,服务消费者也不会再请求。如果部署多个DemoService,则providers下面会有好几个节点,一个节点保存一个DemoService的服务地址。
其实一个zookeeper集群能被多个应用公用,因为不同的框架会在zookeeper上建不同的节点,互不影响。如dubbo会创建一个/dubbo节点,storm会创建一个/storm节点。
zookeeper 介绍:
zookeeper是 Apacahe Hadoop 的子项目,是一个树型的目录服务,支持变更推送,适合作为 Dubbo 服务的注册中心,工业强度较高,可用于生产环境,并推荐使用。
流程说明:
支持以下功能:
补充:
dubbo的协议使用什么序列化框架?
dubbo有多种协议,不同的协议默认使用不同的序列化框架。比如dubbo协议默认使用 Hessian2 序列化(说明:Hessian2 是阿里在 Hessian 基础上进行的二次开发,起名为Hessian2)。rmi协议默认为 java 原生序列化,>在分布式系统中,分布式锁的应用场景是非常广泛的。Redis和ZooKeeper是目前比较常见的实现方案,那它们之间有什么区别呢,我们应该如何选择?
对于ZooKeeper来说,它的会话与服务端是通过心跳保持连接的,当心跳超时客户端会收到链接丢失的事件,通常来说这不是问题,因为ZK的客户端对自动连接。但如果一直连接不上,服务端会在会话有效期之后,将会话置为过期。这里有个很重要的细节,即使会话已经过期了,在重新连接上服务器之前,客户端永远也不知道自己已经过期了,因为ZooKeeper的会话过期是由服务器端触发并通知客户端的。
这样,如果客户端a与ZooKeeper服务器之间的通信长时间断开的话,客户端a会误以为自己并没有过期,只是临时性的链接丢失,但实际上服务端有可能已经将它置为过期,而这时其它与ZooKeeper服务器通信正常的客户端就有可能获取同一个锁(因为服务端在将客户端a置过期的同时会清除它所创建的临时节点),进而造成两个客户端同时访问临界区的资源。
要解决这个问题也不难,在链接丢失和重新建立链接时,客户端都会收到相应的事件通知。我们可以在客户端维护一个状态变量(比如valid),在链接丢失时将它置为false,并在重新建立链接后将它置为true,并在客户端访问临界区的资源代码中不定期的检查valid的值,一旦发生valid值变为false时,就说明链接丢失了,这时就暂停临界区资源的访问,并等待重新建立链接。
这么做挺麻烦的,大大增加了使用锁的复杂性。但是否真需要这么玩也是看具体的场景的:
使用Redis实现的锁,并不存在这样的问题,因为key并不会因为客户端怎么样而被删除。但它也有麻烦的一面,为了防止客户端长时间阻塞或者故障宕机而导至锁无法释放,我们需要在加锁的时候指定一个过期时间,不过成本确实比ZooKeeper的实现要低很多。
经过以上分析,我们不难得出以下结论:分布式锁的实现使用ZooKeeper还是redis来实现,取决于加锁的目的。
远程桌面连接(MicrosoftTerminalServicesClient,MSTSC):采用这种方式登录,请确保实例能访问公网。如果在创建实例时没有购买带宽,则不能使用远程桌面连接。
管理终端VNC:无论您在创建实例时是否购买了带宽,只要您本地有网页浏览器,都可以通过管理控制台的管理终端登录实例。
使用远程桌面连接(MSTSC)登录实例
打开开始菜单>远程桌面连接,或在开始菜单>搜索中输入mstsc。也可以使用快捷键Win+R来启动运行窗口,输入mstsc后回车启动远程桌面连接。
在远程桌面连接对话框中,输入实例的公网IP地址。单击显示选项。
输入用户名,如小鸟云默认为niaoyun。单击允许我保存凭据,然后单击连接。这样以后登录就不需要手动输入密码了。通过Watch机制,这保证了客户端可以监听节点的动态。
客户端可以在ZooKeeper节点(我们叫它znodes)上设置watch,当znode更改时,将触发并删除watch。
当触发watch机制时,客户端会受到一个数据包,说明znode已更改。
如果客户端与其中一个ZooKeeper服务器之间的连接中断,客户端将收到本地通知。
而以上这些都是基于ZooKeeper的服务端与客户端建立的长连接(基于Netty实现)。
如何启动zookeeper-336?
启动zookeeper-336的方法:下载安装配置zookeeper的服务器环境-创建文件-设置权限-编辑-重启即可。
具体步骤:
一、登陆linux服务器用cd 命令切换到/etc/rcd/initd/目录下。
二、touch zookeeper创建一个文件。
三、为文件添加可执行权限chmod +x zookeeper。
四、用vi zookeeper来编辑这个文件。
五、在zookeeper里面输入如下内容。
六、保存退出。
七、用service zookeeper start/stop来启动停止zookeeper服务。
八、使用chkconfig --add zookeeper命令吧zookeeper添加到开机启动里面。
九、使用chkconfig --list 来看看添加的zookeeper是否在里面。
十、重启即可。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)