什么是Zookeeper?
Zookeeper 是一个开放源码的分布式应用程序协调服务,它包含一个简单的原语集,分布式应用程序可以基于它实现同步服务、配置维护和命名服务等等。
采用下图描述zookeeper协调服务。
Zookeeper特性
ZooKeeper非常快速且非常简单。但是,由于其目标是构建更复杂的服务(如同步)的基础,因此它提供了一系列保证。这些是:
- 顺序一致性 - 客户端的更新将按发送顺序应用。
- 原子性 - 更新成功或失败。没有部分结果。
- 单系统映像 - 无论服务器连接到哪个服务器,客户端都将看到相同的服务视图。
- 可靠性 - 一旦应用了更新,它将从那时起持续到客户端覆盖更新。
- 及时性 - 系统的客户视图保证在特定时间范围内是最新的。
什么是分布式锁
顾名思义,分布式锁肯定是用在分布式环境下。在分布式环境下,使用分布式锁的目的也是保证同一时刻只有一个线程来修改共享变量,修改共享缓存……。
前景:
jdk提供的锁只能保证线程间的安全性,但分布式环境下,各节点之间的线程同步执行却得不到保障,分布式锁由此诞生。
实现方式有以下几种:
- 基于数据库实现分布式锁;
- 基于缓存(Redis等)实现分布式锁;
- 基于Zookeeper实现分布式锁;
本示例利用zookeeper实现分布式锁。
实现思路
zookeeper 可以根据有序节点+watch实现,实现思路,如:
为每个线程生成一个有序的临时节点,为确保有序性,在排序一次全部节点,获取全部节点,每个线程判断自己是否最小,如果是的话,获得锁,执行操作,操作完删除自身节点。如果不是第一个的节点则监听它的前一个节点,当它的前一个节点被删除时,则它会获得锁,以此类推。
使用Zookeeper实现分布式锁的优点
有效的解决单点问题,不可重入问题,非阻塞问题以及锁无法释放的问题。实现起来较为简单。
使用Zookeeper实现分布式锁的缺点
性能上不如使用缓存实现分布式锁。因为需要频繁的创建删除节点。并且需要对ZK的原理有所了解。
参考链接:
三种方案实现分布式锁
Spring Boot 使用 Thymeleaf
<!-- 引入thymeleaf模板引擎-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
一、标准表达式语法
它又分为:
消息
变量
选择表达式
链接URL
片段
文字
附加文本
字面替代
算术运算
比较与平等
条件表达式
默认表达式
无操作令牌
数据转换/格式化
预处理
我就只介绍常用的了
${…} 表达式实际上是在上下文中包含的变量的地图上执行的OGNL(Object-Graph Navigation Language)对象。
1、变量
<p>Today is: <span th:text="${today}">13 february 2011</span>.</p>
1
意味着 <span>标签中的内容会被表达式${today}的值所替代,无论模板中它的内容是什么,之所以在模板中“多此一举“地填充它的内容,完全是为了它能够作为原型在浏览器中直接显示出来。
假设today的值为2015年8月14日,那么渲染结果为:<p>Today is: 2015年8月14日.</p>。可见Thymeleaf的基本变量和JSP一样,都使用${.}表示获取变量的值。
2、URL
URL在Web应用模板中占据着十分重要的地位,需要特别注意的是Thymeleaf对于URL的处理是通过语法@{…}来处理的。Thymeleaf支持绝对路径URL:
<a th:href="@{http://www.thymeleaf.org}">Thymeleaf</a>
1
同时也能够支持相对路径URL:
另外,如果需要Thymeleaf对URL进行渲染,那么务必使用th:href,th:src等属性,下面是一个例子
<!-- Will produce 'http://localhost:8080/gtvg/order/details?orderId=3' (plus rewriting) -->
<a href="details.html"
th:href="@{http://localhost:8080/gtvg/order/details(orderId=${o.id})}">view</a>
<!-- Will produce '/gtvg/order/details?orderId=3' (plus rewriting) -->
<a href="details.html" th:href="@{/order/details(orderId=${o.id})}">view</a>
<!-- Will produce '/gtvg/order/3/details' (plus rewriting) -->
<a href="details.html" th:href="@{/order/{orderId}/details(orderId=${o.id})}">view</a>
几点说明:
上例中URL最后的(orderId=${o.id})表示将括号内的内容作为URL参数处理,该语法避免使用字符串拼接,大大提高了可读性@{...}表达式中可以通过{orderId}访问Context中的orderId变量@{/order}是Context相关的相对路径,在渲染时会自动添加上当前Web应用的Context名字,假设context名字为app,那么结果应该是/app/order
3、字符串替换
很多时候可能我们只需要对一大段文字中的某一处地方进行替换,可以通过字符串拼接操作完成:
<span th:text="'Welcome to our application, ' + ${user.name} + '!'">
1
一种更简洁的方式是:
<span th:text="|Welcome to our application, ${user.name}!|">
1
当然这种形式限制比较多,|…|中只能包含变量表达式${…},不能包含其他常量、条件表达式等。
4、运算符
在表达式中可以使用各类算术运算符,例如+, -, *, /, %
th:with="isEven=(${prodStat.count} % 2 == 0)"
1
逻辑运算符>, <, <=,>=,==,!=都可以使用,唯一需要注意的是使用<,>时需要用它的HTML转义符:
th:if="${prodStat.count} > 1&quo