背 景
分布式锁服务在大家的项目中或许用的不多因为大家都把排他放在数据库那一层来挡当大量的行锁表锁事务充斥着数据库的时候不如换个角度思考问题一般web应用很多的瓶颈都在数据库上这里给大家介绍的是减轻数据库锁负担的一种方案
简 介
如果我们的需求很简单例如对于用户的账户资金要保证原子性操作并且不同的客户端在同一时间内只能提交一个对象操作lock单例?!在单台上还可以但是大型web项目上负载均衡是常用的技术手段手段同一意义的对象可能存在不同的副本这时我们又如何保证排他操作数据库的事务!除了这个接下来我们引出本章的主题分布式锁服务
一个简单的锁服务实现起来并不难甚至利用memcache很快就能构造一套分布式锁系统我们只需要在操作对象时写入kv键值对操作完毕时释放kv在读取对象时判断kv中是否有数据就可以了我们甚至还可以给它一个默认的释放时间
这是一种解决方案但是如果我们的要求更高一点我们需要权限认证(例如只能来自xxx域名的请求)需要上下级节点关联(例如一个用户的资金账户被锁住同时锁住他的购物车积分等)需要监视器回调甚至需要考虑单点故障问题那么虫子在这里推荐另一套方案zookeeper
Zookeeper是Hadoop中的一个模块是一个分布式的开源的分布式应用程序协调服务用它可以来现同步服务配置维护
更多的内容大家看文档吧或者直接网上搜一下理论性的内容写多了让人困我们直接看实践
性能篇
服务器ubuntu (虚拟机一台)
客户端window
服务端安装好java环境 然后跟着官方的介绍部署
启动zkserver
我们测试下锁服务相关的操作
ps试下本机的windows 因为是本地环境 不于上面做对比 仅看看zookeeper本身的数据处理效率
功能篇
一张图就可以介绍完普通功能
再看下watcher
publicclassMyWatch:IWatcher
{
publicvoidProcess(WatchedEventqevent)
{
ConsoleWriteLine(thisisMyWatch);
}
}
publicclassMyWatch:IWatcher
{
publicvoidProcess(WatchedEventqevent)
{
ConsoleWriteLine(thisisMyWatch);
}
}
创建连接时new ZooKeeper(: new TimeSpan( ) new MyWatch());
检查是否存在时zkExists(Dir new MyWatch());
获取数据时zkGetData(Dir new MyWatch() stat);
我们再运行一遍之前的demo 去掉delete操作
加上delete操作
浅 析
创建连接
获取服务主机列表
设置超时时间
注册客户端事件
以线程安全的方式创建请求连接(启动客户端请求队列循环队列基于socket通信根据请求类型执行不同的请求动作)
请求流程
构造请求头构造requestreponse构造响应头构造Packet对象packet对象准备好后把整个对象放入一个outgoingQueue
packet被放入outgoingQueue中等待SendThread把packet对应的内容发送给serverserver处理分步在 doio方法中ReadLength ReadConnectResult ReadResponse直到ReadResponse方法中确定packet请求结束
响应流程
针对心跳的ping请求的resp针对auth请求的resp一般接口请求的resp如果接口请求要求了watcher当watcher关注的内容有变化时的notification
锁相关部分API方法
创建节点create
demozkCreate(Dir severnameGetBytes() IdsOPEN_ACL_UNSAFE CreateModePersistent);
其中CreateMode分为类PersistentPersistentSequentialEphemeralEphemeralSequential
PERSISTENT 创建持久化节点对应机器关闭连接后节点/数据不会消失
PERSISTENT_SEQUENTIAL 如果PATH是以/结尾则以这个PATH作为父节点创建一个子节点其子节点名字是一个按先后顺序排列的数值否则创建一个名字是/后面字符加上先后顺序排列的数值字符串的节点同样创建持久节点
EPHEMERAL 创建瞬时节点Zookeeper在感知连接机器宕机后会清除它创建的瞬时节点
EPHEMERAL_SEQUENTIAL 穿件瞬时顺序节点和PERSISTENT_SEQUENTIAL一样区别在于它是瞬时的
删除节点 delete
demo zkDelete(Dir );
前一个参数代表节点名称(一般用作路径)后一个是版本号 表示全匹配
查看节点 exists
demo zkExists(Dir new MyWatch());
获取数据 getData
demo zkGetData(Dir new MyWatch() stat);
获取一个节点的数据可注入watcher
设置数据 setData
demo zkSetData(Dir new byte[] );
获取下级节点集合 GetChildren
demo zkGetChildren(Dir true);
存 储
znodes类似文件和目录但它不是一个典型的文件系统zookeeper数据保存在内存中这意味着zookeeper可以实现高吞吐量和低延迟
watcher
Zookeeper有两种watches一种是data watches另一种是child watches其中getData()和exists()以及create()等会添加data watchesgetChildren()会添加child watches而delete()涉及到删除数据和子节点会同时触发data watches和child watches