gpt4 book ai didi

c# - 启用 ASP.Net Core session 锁定?

转载 作者:行者123 更新时间:2023-12-03 20:25:23 34 4
gpt4 key购买 nike

根据 ASP.Net Core docs , session 状态的行为已经改变,因为它现在是非锁定的:
session 状态是非锁定的。如果两个请求同时尝试修改 session 的内容,最后一个请求会覆盖第一个请求。 Session 是作为一个连贯的 session 来实现的,这意味着所有的内容都存储在一起。当两个请求试图修改不同的 session 值时,最后一个请求可能会覆盖第一个请求所做的 session 更改。
我的理解是,这与 .Net Framework 中 session 的行为不同,其中用户的 session 根据请求被锁定,因此无论何时读取/写入它,都不会覆盖另一个请求的数据或读取过时的数据,对于该用户。
我的问题:

  • 有没有办法在 .Net Core 中重新启用用户 session 的每个请求锁定?
  • 如果没有,是否有可靠的方法来使用 session 来防止给定用户重复提交数据?举一个具体的例子,我们有一个支付流程,该流程涉及用户从外部托管的 ThreeDSecure (3DS) iFrame(支付卡安全流程)返回。我们注意到有时(以某种方式)用户在 iFrame 中多次提交表单,我们无法控制。因此,这会触发对我们应用程序的多个回调。在我们之前的 .Net Framework 应用程序中,我们使用 session 来指示付款是否正在进行中。如果在 session 中设置了此标志并且您再次点击 3DS 回调,应用程序将阻止您继续。然而,现在似乎因为 session 没有被锁定,当这些几乎同时发生的重复回调发生时,线程“A”设置“付款进行中=真”但线程“B”没有及时看到,它是快照的 session 仍然看到'payment in progress = false'并且回调逻辑被处理了两次。

  • 既然 session 的工作方式发生了变化,那么处理访问同一 session 的同时请求有哪些好方法?

    最佳答案

    您遇到的问题称为竞争条件( stackoverflowwiki )。为了切入,您希望获得对 session 状态的独占访问权限,您可以通过多种方式实现这一点,并且它们高度依赖于您的架构。
    进程内同步
    如果你有一台机器,一个进程处理所有请求(例如你使用自托管服务器,Kestrel),你可以使用 lock .只要正确地做,而不是@TMG 建议的那样。
    这是一个实现引用:

  • 使用单个全局对象锁定所有线程:
  •   private static object s_locker = new object();

    public bool Process(string transaction) {
    lock (s_locker) {
    if(!HttpContext.Session.TryGetValue("TransactionId", out _)) {
    ... handle transaction
    }
    }
    }
    优点:一个简单的解决方案
    缺点:所有用户的所有请求都将等待此锁
  • 使用每个 session 锁定对象。想法是相似的,但您只使用字典而不是单个对象:
  •     internal class LockTracker : IDisposable
    {
    private static Dictionary<string, LockTracker> _locks = new Dictionary<string, LockTracker>();
    private int _activeUses = 0;
    private readonly string _id;

    private LockTracker(string id) => _id = id;

    public static LockTracker Get(string id)
    {
    lock(_locks)
    {
    if(!_locks.ContainsKey(id))
    _locks.Add(id, new LockTracker(id));
    var res = _locks[id];
    res._activeUses += 1;
    return res;
    }
    }

    void IDisposable.Dispose()
    {
    lock(_locks)
    {
    _activeUses--;
    if(_activeUses == 0)
    _locks.Remove(_id);
    }
    }
    }


    public bool Process(string transaction)
    {
    var session = HttpContext.Session;
    var locker = LockTracker.Get(session.Id);
    using(locker) // remove object after execution if no other sessions use it
    lock (locker) // synchronize threads on session specific object
    {
    // check if current session has already transaction in progress
    var transactionInProgress = session.TryGetValue("TransactionId", out _);
    if (!transactionInProgress)
    {
    // if there is no transaction, set and handle it
    HttpContext.Session.Set("TransactionId", System.Text.Encoding.UTF8.GetBytes(transaction));
    HttpContext.Session.Set("StartTransaction", BitConverter.GetBytes(DateTimeOffset.UtcNow.ToUnixTimeSeconds()));
    // handle transaction here
    }
    // return whatever you need, here is just a boolean.
    return transactionInProgress;
    }
    }
    优点:在 session 级别管理并发
    缺点:更复杂的解决方案
    请记住,基于锁的选项仅在处理 的网络服务器上的相同进程时才起作用。全部 用户的请求——锁是进程内同步机制!根据您用作 session 的持久层的内容(如 NCache 或 Redis),此选项可能是性能最高的。
    跨进程同步
    如果机器上有多个进程(例如你有 IIS 并且 apppool 被配置为运行多个工作进程),那么你需要使用内核级同步原语,如 Mutex .
    跨机同步
    如果您的 webfarm 前面有一个负载均衡器 (LB),以便 N 台机器中的任何一台都可以处理用户的请求,那么获得独占访问权限就不是那么简单了。
    这里的一种选择是通过启用“ sticky session”来简化问题。 ' 选项,以便来自同一用户( session )的所有请求都将路由到同一台机器。在这种情况下,您可以使用任何跨进程或进程内同步选项(取决于您在那里运行的内容)。
    另一种选择是外部化同步,例如,将其移动到事务数据库,类似于@HoomanBahreini 建议的内容。请注意,您在处理失败情况时需要非常谨慎:您可能会将 session 标记为进行中,然后处理它的网络服务器崩溃,将其锁定在数据库中。
    重要的
    在所有这些选项中,您必须确保获得锁 之前 阅读状态和 持有直到你更新状态。
    请说明哪个选项最接近您的情况,我可以提供更多技术细节。

    关于c# - 启用 ASP.Net Core session 锁定?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62563250/

    34 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com