如果你使用的是 Spring 控制器,那么你可以使用
RequestMappingHandlerAdapter.setSynchronizeOnSession-boolean-
这将使每个 Controller 方法在存在会话时同步。
HttpSession.setAttribute 是线程安全的。但是 getAttribute 后跟 setAttribute 必须手动确保胎面安全。
synchronized(session) {
session.setAttribute("foo", "bar");
session.getAttribute("foo");
}
在 Spring session bean 的情况下也可以这样做。
synchronized(session) {
//do something with the session bean
}
#编辑
如果多个容器带有普通的 Spring 会话 bean,您将不得不使用 sticky sessions。这将确保一个会话状态存储在一个容器上,并且每次请求同一会话时都会访问该容器。这必须借助 BigIP cookies 之类的东西在负载均衡器上完成。 Rest 的工作方式与单个会话的工作方式相同,存在单个容器,因此锁定会话就足够了。
如果您想跨实例使用会话共享,则容器上支持 Tomcat 和 Jetty
这些方法使用后端数据库或其他一些持久性机制来存储状态。
出于同样的目的,您可以尝试使用Spring Session。使用Redis 进行配置很简单。由于 Redis 是单线程的,因此可以确保以原子方式访问条目的一个实例。
以上方法是非侵入性的。基于数据库和 Redis 的方法都支持transactions。
但是,如果您想更好地控制分布式状态和锁定,您可以尝试使用 Hazelcast 和 Gemfire 等分布式数据网格。
我亲自与 Hazelcast 合作过,它确实提供了 methods to lock entries made in the map。
#Edit2
虽然我相信使用 Spring Session 和 Redis 处理事务就足够了,以确保您需要分布式锁定。锁定对象必须从 Redis 本身获取。由于 Redis 是单线程的,因此个人实现也可以使用 INCR 之类的东西来工作
算法如下所示
//lock_num is the semaphore/lock object
lock_count = INCR lock_num
while(true) {
if(lock_count != 1) {
DECR lock_num
} else {
break
}
wait(wait_time_period)
}
//do processing in critical section
DECR lock_num
不过,幸运的是 Spring 已经通过 RedisLockRegistry 提供了这种分布式锁实现。有关usage is here 的更多文档。
如果您决定使用没有弹簧的普通 Jedis,那么这里有一个分布式锁,就像 Jedis 一样:Jedis Lock。
//from https://github.com/abelaska/jedis-lock
Jedis jedis = new Jedis("localhost");
JedisLock lock = new JedisLock(jedis, "lockname", 10000, 30000);
lock.acquire();
try {
// do some stuff
}
finally {
lock.release();
}
这两个都应该像 Hazelcast 锁定一样工作。