Zookeeper应用之——栅栏(barrier)
Zookeeper应用之——栅栏(barrier)
栅栏(barrier)简介
barrier的作用是所有的线程等待,知道某一时刻,锁释放,所有的线程同时执行。举一个生动的例子,比如跑步比赛,所有 运动员都要在起跑线上等待,直到枪声响后,所有运动员同时起跑,冲向终点。在这个例子中,所有的运动员就是所有的线程, 枪声是所有线程的共享锁,枪声一响,锁释放,所有线程同时执行。
java的concurrent包中已经为我们提供了barrier的实现,它叫做CyclicBarrier。但是它只能在一个java进程中提供barrier, 在分布式、集群的情况下,java是不能提供barrier的。在分布式、集群的环境下,我们需要借助外部工具实现barrier,今天我们 介绍使用zookeeper实现barrier。
Zookeeper实现Barrier的方式
Zookeeper的安装,无论是集群还是单点,请参阅其他文档,这里不再赘述了。Zookeeper的基本概念,如:节点、临时节点、 树结构、观察器(watcher)等请参阅上一篇文章,这里也不细讲。
我们通过在Zookeeper设置栅栏节点实现Barrier,节点的名字我们叫做/zookeeper/barrier,具体的逻辑如下:
- 客户端在Barrier节点上调用exists()方法,并设置观察器
- 如果Barrier节点不存在,则线程继续执行
- 如果Barrier节点存在,则线程等待,直到触发观察器事件,并且事件是Barrier节点消失的事件,唤起线程
具体程序实现
首先连接Zookeeper,创建Barrier节点,如下:
1 2 3 4 | [root @vultr ~]# /opt/zookeeper- 3.5 . 4 -beta/bin/zkCli.sh -server localhost: 2181 [zk: localhost: 2181 (CONNECTED) 1 ] create /zookeeper/barrier Created /zookeeper/barrier |
然后我们创建类BarrierExample,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 | public class BarrierExample implements Runnable,Watcher { private ZooKeeper zk; /** * 构造函数 * @param address zookeeper地址 */ public BarrierExample(String address){ try { this .zk = new ZooKeeper(address, 3000 , this ); } catch (IOException e) { e.printStackTrace(); this .zk = null ; } } @Override public void run() { try { //监听Barrier节点,并设置观察器 Stat stat = zk.exists( "/zookeeper/barrier" , true ); //Barrier节点存在,线程等待 if (stat!= null ){ System.out.println(Thread.currentThread().getName()+ "——barrier节点存在,线程等待" ); synchronized ( this ){ this .wait(); } } //模拟业务代码 businessCode(); } catch (KeeperException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } } /** * 模拟业务代码 */ private void businessCode() { try { Thread.sleep( 2000 ); System.out.println(Thread.currentThread().getName()+ "——执行业务逻辑耗时:2s" ); } catch (InterruptedException e) { e.printStackTrace(); } } /** * 观察器 * @param event */ @Override public void process(WatchedEvent event) { //不是节点删除事件,返回 if (event.getType() != Event.EventType.NodeDeleted) return ; try { //查看Barrier节点是否存在 Stat stat = zk.exists( "/zookeeper/barrier" , true ); //Barrier节点消失,唤起所有等待线程 if (stat== null ){ System.out.println(Thread.currentThread().getName()+ "——barrier节点消失,唤起所有线程" ); synchronized ( this ) { this .notify(); } } } catch (KeeperException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } } } |
BarrierExample实现了Runnable和Watcher接口,线程开始时,会调用run()方法,发现Barrier节点存在,线程等待。 然后我们将Barrier节点删除,触发Watch事件,发现Barrier节点已消失,唤起等待的线程。接下来,创建主函数类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | public class Application { public static void main(String[] args) { //设置log级别为Error setLog(); //创建5个线程 ExecutorService es = Executors.newFixedThreadPool( 5 ); for ( int i= 0 ;i< 5 ;i++){ //执行BarrierExample es.execute( new BarrierExample( "149.28.37.147:2181" )); } es.shutdown(); System.out.println( "主线程完成!" ); } /** * 设置log级别为Error */ public static void setLog(){ //1.logback LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory(); //获取应用中的所有logger实例 List<Logger> loggerList = loggerContext.getLoggerList(); //遍历更改每个logger实例的级别,可以通过http请求传递参数进行动态配置 for (ch.qos.logback.classic.Logger logger:loggerList){ logger.setLevel(Level.toLevel( "ERROR" )); } } } |
我们创建了5个线程,发现Barrier节点,等待,具体执行如下:
运行main()函数,后台打印结果如下:
1 2 3 4 5 6 | 主线程完成! pool- 1 -thread- 1 ——barrier节点存在,线程等待 pool- 1 -thread- 5 ——barrier节点存在,线程等待 pool- 1 -thread- 2 ——barrier节点存在,线程等待 pool- 1 -thread- 4 ——barrier节点存在,线程等待 pool- 1 -thread- 3 ——barrier节点存在,线程等待 |
然后我们将Barrier节点删除:
1 | [zk: localhost: 2181 (CONNECTED) 0 ] delete /zookeeper/barrier |
Barrier节点消失,唤起所有等待线程,后台打印结果如下:
1 2 3 4 5 6 7 8 9 10 | main-EventThread——barrier节点消失,唤起所有线程 main-EventThread——barrier节点消失,唤起所有线程 main-EventThread——barrier节点消失,唤起所有线程 main-EventThread——barrier节点消失,唤起所有线程 main-EventThread——barrier节点消失,唤起所有线程 pool- 1 -thread- 2 ——执行业务逻辑耗时:2s pool- 1 -thread- 1 ——执行业务逻辑耗时:2s pool- 1 -thread- 5 ——执行业务逻辑耗时:2s pool- 1 -thread- 3 ——执行业务逻辑耗时:2s pool- 1 -thread- 4 ——执行业务逻辑耗时:2s |
Zookeeper的Barrier实现就介绍完了,以后我们还会介绍Zookeeper的其他应用。
*如果您觉得对您有帮助,请关注+推荐
*您的支持就是我继续下去的动力
*承接项目+V:ronaldoliubo
***************************************
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 周边上新:园子的第一款马克杯温暖上架
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?
· 使用C#创建一个MCP客户端
2017-07-03 利用java反射机制编写solr通用的java客户端
2017-07-03 solr的基本概念