Django 应用程序在部署时变得混乱。比赛条件?

Django app goes haywire when deployed. Race conditions?

我写了一个用于测验的 django 应用程序,它会检查用户的答案并在用户提交答案后立即更新分数。这是执行此操作的相应视图 -

current_question_key = 0 #This is a global variable.
def check_answer(request):
    current_user = request.user
    current_team = Team.objects.get(user = current_user)
    current_score = current_team.score

    if request.method == "POST":
        answer = request.POST.get('answer')
        question = Question.objects.get(id = current_question_key)
        if answer == question.answer:
            if question in current_team.questions_answered.all(): #This is required to prevent the score from increasing if the somebody submits a correct answer to the same question more than once
                pass
            else:
                current_team.score = current_score + question.score_increment
                current_team.questions_answered.add(question)
                current_team.save()        
        else:
            # This is required to prevent the score from decreasing if someone has answered it correctly earlier
            if question in current_team.questions_answered.all():
                pass
            else :
                current_team.score = current_score - question.score_increment//negative_marking_factor
                current_team.save()
        return HttpResponse(status=204) #This means that the server has successfully processed the request and is not going to return any data.
    else:
        return HttpResponse("Error404")

current_question_key的值从用于发送问题到前端的视图更改 -

def game(request):   
    if request.method == "POST":
        key = request.POST.get('questionKey')
        global current_question_key
        current_question_key = key
        question = Question.objects.get(id = key)
        question_text = question.question_text
        data = {
            'question_text':question_text
        }
        return JsonResponse(data)
    else:
        current_user = request.user
        current_team = Team.objects.get(user = current_user)
        score = current_team.score
        name = current_user.username
        return render(request, 'Base/main.html', {'teamname':name, 'score':score})

在 django 的开发服务器上进行测试时,即使有大约 10 个人同时使用它,它也能正常工作。但是,一旦我尝试使用 nginx(托管在我的笔记本电脑上,同时有 5 个用户)为其提供服务,该应用程序就完全失控了,甚至正确的答案也被评估为错误的。

我也试过apache,但遇到了同样的问题。几乎所有的请求都被错误地处理了。这可能与竞争条件有关吗?这里到底发生了什么?

您不能在 Django 中使用这样的全局变量。 Django 应用程序通常在不共享内存的多个服务器进程中运行。调用 game 视图只会在其中一个进程中设置全局变量 current_question_key。所有其他进程仍将具有旧值。由于任何进程都可以处理请求,因此您或多或少会得到随机结果。

Django 开发服务器使用多线程而不是多处理。与进程相反,线程共享相同的内存,因此所有请求都看到 current_question_key.

的相同值

您必须以所有进程都可以访问的方式为每个用户存储 current_question_key。最明显的解决方案是将此信息存储在用户的 session:

request.session['current_question_key'] = ...

或者,您可以将其存储在数据库中,例如在 customer user model 中使用 ForeignKey,或者如果您想在单独的 table 中跟踪任何游戏,如下所示:

from django.contrib.auth import get_user_model
from django.db import models

class Game(model.Model)
    user = models.ForeignKey(
        get_user_model(),
        on_delete=models.CASCADE
    )
    question = models.ForeignKey(Question, on_delete=models.CASCADE)
    created_at = models.DateTimeField(auto_now_add=True, db_index=True)

然后您可以通过按创建日期排序来获取用户的当前游戏:

Game.objects.filter(user=request.user).order_by('-created_at').first()

根据当前问题的变化频率,您也可以考虑使用像 Redis 这样的键值对,尽管这会使事情变得有点复杂。