首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何获得对某些会话条目的独占访问?

如何获得对某些会话条目的独占访问?
EN

Stack Overflow用户
提问于 2016-03-26 12:40:50
回答 2查看 1.7K关注 0票数 7

由于REST服务的远程调用特性,它们一直处于相互竞争的状态。竞争的日常资源之一是会话。为了实用,您需要能够在您的过程开始时对资源设置一个锁,并在您完成该资源时将其解除。

现在我的问题是,Spring会话是否有任何功能来处理会话条目上的争用条件?

或Java的任何其他库/框架!

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2016-04-02 07:04:01

如果使用Spring控制器,则可以使用

RequestMappingHandlerAdapter.setSynchronizeOnSession-boolean-

这将使每个Controller方法在存在会话时同步。

HttpSession.setAttribute是线程安全的。然而,getAttribute后面跟着setAttribute必须手动使胎面安全。

代码语言:javascript
复制
synchronized(session) {
    session.setAttribute("foo", "bar");
    session.getAttribute("foo");
}

在spring会话bean的情况下也可以这样做。

代码语言:javascript
复制
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是单线程的,所以个人实现也可以通过使用类似于英格尔的东西来工作。

算法如下所示

代码语言:javascript
复制
//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:吉迪斯·洛克,这里有一个分布式锁。

代码语言:javascript
复制
//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的锁定完全一样。

票数 3
EN

Stack Overflow用户

发布于 2016-04-02 18:24:43

正如先前的答复所述。如果您正在使用Spring会话,并且在并发访问会话时关心线程安全,则应该设置:

代码语言:javascript
复制
RequestMappingHandlerAdapter.setSynchronizeOnSession(true);

在这里可以找到一个例子,EnableSynchronizeOnSessionPostProcessor

代码语言:javascript
复制
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上的条目,您可以完成所需的任务。

祝好运!

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/36235402

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档