python - 由于Django + Nginx + Gunicorn环境中的Gunicorn多个工作程序而导致应用程序意外行为的建议/解决方案

标签 python django django-views gunicorn

最近,我在Django + Nginx + Gunicorn + MySQL的生产环境中被意外的Django应用程序行为困扰。此行为在Django开发服务器上不存在,因此在部署之前我几乎没有发现它。但是,在分析生产日志之后,我已经找到了解决方法,但是我想知道是否有更好的方法可以解决此问题,或者我的设计存在缺陷,因此我需要重新设计。

为了简化我的问题,我分离并提取了发生此现象的场景:应用程序使用一个基于类的视图和一个模型,以在一次页面加载时向前端提供一个数字(用户单击下一页加载,应用会提供下一个数字)。该数字来自五个数字的列表。这五个数字是按顺序输入的。当所有五个输入完毕后,列表将被重新排列,重新排列后的列表中的五个数字将被重新按顺序输入。

例如,我有一个列表[0,1,2,3,4]。加载第一页将通过读取URL参数将0馈送到前端(因此视图知道它正在开始)。在前端,显示0,然后单击下一页旁边。下一页将显示1。直到页面5为止,行为相同。此时单击“下一步”时,列表[0、1、2、3、4]将被改组,例如,变为[4、2、1, 0,3]。并且4将显示在页面上。然后单击下一步,下一页显示2,依次类推。直到3。然后随机播放。然后相同的过程。

该模型用于让视图知道要输入的编号。我简化了模型,因为其他领域无关紧要。这是我的简化模型。

#models.py
class Question(models.Model):
    q_set = models.PositiveIntegerField()
    q_index = models.PositiveIntegerField()


在views.py和基于类的视图之外,定义了数字QUESTION_VALUES的列表,并在最初对其进行了混洗。基于类的视图使用get()读取URL中的“ set”和“ index”参数,并确定要馈入前端的数字的​​索引,并在馈入当前列表中的所有数字时对列表进行随机排序。该视图使用post()来确定下一页的参数。简而言之,页面从视图“获取”编号,并将当前参数“发布”回视图,以便视图确定下一页的参数。这是我的简化视图。

#views.py
QUESTION_VALUES = [0, 1, 2, 3, 4]
random.shuffle(QUESTION_VALUES)

class QuestionView(TemplateView):
    model = Question
    template_name = 'question.html'

    def get(self, request, *args, **kwargs):
        q = get_object_or_404(Question, q_set=kwargs['set'], q_index=kwargs['index'])
        q_index = int(q.q_index)
        if q_index < 5:
            q_val = QUESTION_VALUES[q_index - 1]
        elif q_index == 5:
            q_val = QUESTION_VALUES[q_index - 1]
            random.shuffle(QUESTION_VALUES)
        return render(request, self.template_name, locals())

    def post(self, request, *args, **kwargs):
        q = get_object_or_404(Question, q_set=kwargs['set'], q_index=kwargs['index'])
        q_set = int(kwargs['set'])
        q_index = int(kwargs['index'])
        if 'next-button' in request.POST:
            if q_index and q_index < 5:
                q_index += 1
                return HttpResponseRedirect(reverse('question', kwargs={'set': q_set,'index': q_index}))
            elif q_index and q_index == 5:
                if q_set and q_set < 5:
                    q_set += 1
                    q_index = 1
                    return HttpResponseRedirect(reverse('question', kwargs={'set': q_set,'index': q_index}))
                elif q_set and q_set == 5:
                    return HttpResponseRedirect('end')


在Django开发服务器上,一切正常。一页显示一个号码,单击下一步,下一页显示当前列表中的下一个号码。输入所有五个数字后,列表将被重新排列。下一页显示混洗列表(第2组)中的第一个数字。当页面上的“下一个”单击显示编号5的第五个数字时,应用程序将被定向到“结束”。 **更重要的是,集合中显示了不同的数字。这意味着不可能在五个页面上分别显示0、0、1、2、3。 **

但是,当我测试已部署的应用程序时,我发现分别在五个页面上显示0、0、1、2、3,这是意外的。我不知道出了什么问题,于是我将打印品添加到views.py并检查Gunicorn的日志。我发现的原因是,我有3个同步工作进程来处理Gunicorn上的请求,并按照设置教程(2 x CPU数量)+1的设置教程将其设置为3。我发现当一个工作进程正在处理请求并处理查看并在一组列表中的喂入编号中间,它可以退出以“刷新”,并让另一个工作人员继续处理该请求。我知道每个工作进程都会有一个解释器,因此,当前列表会丢失,而拥有另一个解释器的后继工作器则不知道当前列表,因此在进入基于类的视图之前会得到一个新的混洗列表。我当前的解决方法是将worker的数量设置为1,以便该单个worker始终知道当前列表。现在一切正常。

我的第一个问题是,如果还有更好的方法来解决这个问题,同时仍然有3个工人。尽管一名工人似乎足以应付当前的应用流量,但我仍然想了解我的方案中的最佳实践。我的第二个问题是,是否有针对这种数字馈送逻辑的视图方法的更好设计。

我的生产环境是Django 1.11,Gunicorn 19.7.1,Nginx 1.10.3,MySQL 5.6和Ubuntu 16.04。该应用程序由DigitalOcean部署在单个CPU上。

我将不胜感激任何建议和讨论。

最佳答案

您遇到的问题比您想象的要多。订单是特定于工作人员的,但由所有请求共享给每个工作人员,因此每个用户将获得恰好为他们为每个请求命中的工作人员设置的相同订单。

您不应该在这样的过程中尝试存储数据。在请求之间需要保留的数据应该放在session中;这样,它就特定于用户,并且无论他们碰到哪个工作者都可以使用。

关于python - 由于Django + Nginx + Gunicorn环境中的Gunicorn多个工作程序而导致应用程序意外行为的建议/解决方案,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49678805/

相关文章:

django - NoReverseMatch Django,CreateView 中的 get_success_url

django - 为 Django View get_context_data() 方法编写测试

python - 在下拉菜单中选择动态元素,Selenium Python

python - (ctypes.c_int * len(x))(*x) 是做什么的?

python - 如何修复 spaCy en_training 与当前 spaCy 版本不兼容的问题

android - OkHttp 不断收到 StreamResetException : stream was reset: INTERNAL_ERROR when it's 200

python - 将值传播到 pandas 数据框中的列(透视)传感器事件

django - refresh()得到了意外的关键字参数 'index'

python - 如何使用 TDD 创建现有对象的数据库表示?

python - Django 如何根据选择字段验证请求的 URL?