首页 > 解决方案 > Django 应用程序在部署时出现问题。比赛条件?

问题描述

我编写了一个用于测验的 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 并且遇到了同样的问题。几乎所有的请求都被错误地处理了。这可能与比赛条件有关吗?这里到底发生了什么?

标签: djangopython-3.xnginxweb-deploymentdjango-2.1

解决方案


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

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

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

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

或者,您可以将其存储在数据库中,例如ForeignKey客户用户模型中,或者如果您想在单独的表中跟踪游戏,如下所示:

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 这样的键值对,尽管这会使事情变得有点复杂。


推荐阅读