由于REST服务的远程调用特性,它们一直处于相互竞争的状态。竞争的日常资源之一是会话。为了实用,您需要能够在您的过程开始时对资源设置一个锁,并在您完成该资源时将其解除。
现在我的问题是,Spring会话是否有任何功能来处理会话条目上的争用条件?
或Java的任何其他库/框架!
发布于 2016-04-02 07:04:01
如果使用Spring控制器,则可以使用
RequestMappingHandlerAdapter.setSynchronizeOnSession-boolean-
这将使每个Controller方法在存在会话时同步。
HttpSession.setAttribute是线程安全的。然而,getAttribute后面跟着setAttribute必须手动使胎面安全。
synchronized(session) {
session.setAttribute("foo", "bar");
session.getAttribute("foo");
}在spring会话bean的情况下也可以这样做。
synchronized(session) {
//do something with the session bean
}#编辑
如果多个容器具有正常的spring会话bean,则必须使用sticky sessions。这将确保将一个会话状态存储在一个容器上,并且每次请求同一个会话时都访问容器。这必须在负载均衡器的帮助下进行,比如BigIP cookies。Rest的工作方式与单个会话存在单个容器的方式相同,因此锁定会话就足够了。
如果您希望跨实例使用会话共享,则在容器(如Tomcat和防波堤 )上有支持
这些方法使用后端数据库或其他持久性机制来存储状态。
出于同样的目的,您可以尝试使用春季会议。这对于使用Redis进行配置非常简单。因为Redis是单线程的,所以它确保原子地访问条目的一个实例。
上述方法是非侵入性的。数据库和基于Redis的方法都支持交易记录。
但是,如果您想要更多地控制分布式状态和锁定,可以尝试使用诸如Hazelcast和Gemfire之类的分布式数据网格。
我个人曾与黑兹尔广播公司合作,它确实提供了方法来锁定映射中的条目。。
#编辑2
虽然我认为Spring会话和Redis应该足以处理事务,但是要确保您需要分布式锁定。必须从Redis本身获取Lock对象。由于Redis是单线程的,所以个人实现也可以通过使用类似于英格尔的东西来工作。
算法如下所示
//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提供了这个分布式锁实现。有关用法在这里的更多文档。
如果您决定使用不带spring的普通Jedis,那么对于Jedis:吉迪斯·洛克,这里有一个分布式锁。
//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的锁定完全一样。
发布于 2016-04-02 18:24:43
正如先前的答复所述。如果您正在使用Spring会话,并且在并发访问会话时关心线程安全,则应该设置:
RequestMappingHandlerAdapter.setSynchronizeOnSession(true);在这里可以找到一个例子,EnableSynchronizeOnSessionPostProcessor:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
public class EnableSynchronizeOnSessionPostProcessor implements BeanPostProcessor {
private static final Logger logger = LoggerFactory
.getLogger(EnableSynchronizeOnSessionPostProcessor.class);
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
// NO-OP
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof RequestMappingHandlerAdapter) {
RequestMappingHandlerAdapter adapter = (RequestMappingHandlerAdapter) bean;
logger.info("enable synchronizeOnSession => {}", adapter);
adapter.setSynchronizeOnSession(true);
}
return bean;
}
}关于集群应用程序和会话,这里有一篇很好的文章讨论这个主题:粘性会话和会话复制
根据我的经验,您将同时希望Sticky会话和会话复制。您可以使用粘性会话来消除跨节点的并发会话访问,因为粘性会话将将会话锁定到单个节点,并且对同一会话的每个后续请求都将始终指向该节点。这消除了跨节点会话访问的顾虑。
复制的会话主要是在节点出现故障的情况下使用。通过复制会话,当节点发生故障时,对现有会话的未来请求将被定向到另一个节点,该节点将拥有原始会话的副本,并使故障转移对用户透明。
有许多支持会话复制的框架。我在大型项目中使用的是开源哈泽尔卡斯特。
回应您在@11维贴子上的评论:
我认为你处在一个有挑战性的领域。基本上,您希望在集群中的节点之间强制所有会话操作为原子操作。这导致我倾向于跨节点的公共会话存储,其中访问是同步的(或类似的)。
多个会话存储/复制框架肯定支持外部存储概念,我相信Reddis支持。我最熟悉哈泽尔卡斯特,并将以此为例。
Hazelcast允许将会话持久性配置为使用公共数据库。如果您查看地图持久化部分,它将显示一个示例和对选项的描述。
对这一概念的说明指出:
Hazelcast允许您从持久数据存储(如关系数据库)加载和存储分布式映射条目。为此,您可以使用Hazelcast的MapStore和MapLoader接口。 数据存储需要是一个可以从所有Hazelcast节点访问的集中式系统。不支持本地文件系统的持久性。 Hazelcast支持读、写和写持久性模式,在下面的小节中将对这些模式进行解释。
有趣的模式是:
直通式 通过将写延迟秒属性设置为0,可以将MapStore配置为“直接写入”.这意味着条目将同步放置到数据存储区。 在此模式下,当map.put(键,值)调用返回时: 成功地调用了MapStore.store(key,value),因此该条目被持久化。更新内存中的条目。在其他JVM上成功地创建内存中的备份副本(如果备份计数大于0)。同样的行为也适用于map.remove(键)调用。唯一的区别是在删除条目时调用了MapStore.delete(键)。
我认为,使用这个概念,再加上正确地为存储设置数据库表以锁定insert/update/deletes上的条目,您可以完成所需的任务。
祝好运!
https://stackoverflow.com/questions/36235402
复制相似问题