0

我正在尝试使用 实现 Oauth2 身份验证django-oauth-toolkit,并且当我使用内置的 django 服务器时,密钥交换工作。但是,当我使用 gunicorn 时,我的反应是空洞的。所有其他端点都可以与 gunicorn 正常工作:

独角兽命令

gunicorn --bind 127.0.0.1:8000 api_name.wsgi

视图.py


from rest_framework.response import Response
from rest_framework.decorators import api_view, permission_classes
from rest_framework.permissions import AllowAny

import requests


from .serializers import CreateUserSerializer


@api_view(['POST'])
@permission_classes([AllowAny])
def register(request):
    '''
    Registers user to the server. Input should be in the format:
    {"username": "username", "password": "1234abcd"}
    '''
    # Put the data from the request into the serializer
    serializer = CreateUserSerializer(data=request.data)
    # Validate the data
    if serializer.is_valid():
        # If it is valid, save the data (creates a user).
        serializer.save()
        # Then we get a token for the created user.
        # This could be done differentley
        r = requests.post('http://127.0.0.1:8000/o/token/', data={
                'grant_type': 'password',
                'username': request.data['username'],
                'password': request.data['password'],
                'client_id': get_client_id(),
                'client_secret': get_client_secret(),
            },
        )
        return Response(r.json())
    return Response(serializer.errors)


@api_view(['POST'])
@permission_classes([AllowAny])
def token(request):
    '''
    Gets tokens with username and password. Input should be in the format:
    {"username": "username", "password": "1234abcd"}
    '''
    r = requests.post('http://127.0.0.1:8000/o/token/', data={
            'grant_type': 'password',
            'username': request.data['username'],
            'password': request.data['password'],
            'client_id': get_client_id(),
            'client_secret': get_client_secret(),
            },
    )
    return Response(r.json())


@api_view(['POST'])
@permission_classes([AllowAny])
def refresh_token(request):
    '''
    Registers user to the server. Input should be in the format:
    {"refresh_token": "<token>"}
    '''
    r = requests.post('http://127.0.0.1:8000/o/token/', data={
            'grant_type': 'refresh_token',
            'refresh_token': request.data['refresh_token'],
            'client_id': get_client_id(),
            'client_secret': get_client_secret(),
        },
    )
    return Response(r.json())


@api_view(['POST'])
@permission_classes([AllowAny])
def revoke_token(request):
    '''
    Method to revoke tokens.
    {"token": "<token>"}
    '''
    r = requests.post( 'http://127.0.0.1:8000/o/revoke_token/', data={
            'token': request.data['token'],
            'client_id': get_client_id(),
            'client_secret': get_client_secret(),
        },
    )
    # If it goes well return sucess message (would be empty otherwise)
    if r.status_code == requests.codes.ok:
        return Response({'message': 'token revoked'}, r.status_code)
    # Return the error if it goes badly
    return Response(r.json(), r.status_code)

网址.py

from django.urls import path

from . import views

urlpatterns = [
    path('register/', views.register),
    path('token/', views.token),
    path('token/refresh/', views.refresh_token),
    path('token/revoke/', views.revoke_token),
]

当我启动请求时:

curl -d "username=new_user&password=12345abcd" "127.0.0.1:8000/authentication/register/" -v

我有回应:

使用django manage.py runserver

*   Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to 127.0.0.1 (127.0.0.1) port 8000 (#0)
> POST /authentication/register/ HTTP/1.1
> Host: 127.0.0.1:8000
> User-Agent: curl/7.58.0
> Accept: */*
> Content-Length: 37
> Content-Type: application/x-www-form-urlencoded
> 
* upload completely sent off: 37 out of 37 bytes
< HTTP/1.1 200 OK
< Date: Tue, 31 Mar 2020 09:55:21 GMT
< Server: WSGIServer/0.2 CPython/3.6.2
< Content-Type: application/json
< Vary: Accept, Authorization, Cookie
< Allow: POST, OPTIONS
< X-Frame-Options: SAMEORIGIN
< Content-Length: 160
< 
* Connection #0 to host 127.0.0.1 left intact

{"access_token":"<access-token>","expires_in":36000,"token_type":"Bearer","scope":"read write","refresh_token":"<refresh-token>"}%

独角兽

*   Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to 127.0.0.1 (127.0.0.1) port 8000 (#0)
> POST /authentication/register/ HTTP/1.1
> Host: 127.0.0.1:8000
> User-Agent: curl/7.58.0
> Accept: */*
> Content-Length: 37
> Content-Type: application/x-www-form-urlencoded
> 
* upload completely sent off: 37 out of 37 bytes
* Empty reply from server
* Connection #0 to host 127.0.0.1 left intact

curl: (52) Empty reply from server

独角兽日志

[2020-03-31 12:11:03 +0200] [3639] [INFO] Starting gunicorn 20.0.4
[2020-03-31 12:11:03 +0200] [3639] [INFO] Listening at: http://127.0.0.1:8000 (3639)
[2020-03-31 12:11:03 +0200] [3639] [INFO] Using worker: sync
[2020-03-31 12:11:03 +0200] [3686] [INFO] Booting worker with pid: 3686
[2020-03-31 12:11:46 +0200] [3639] [CRITICAL] WORKER TIMEOUT (pid:3686)
4

1 回答 1

3

啊,我明白发生了什么。

您正在gunicorn与单个工作人员一起使用,并且您的视图回调到您自己的应用程序('http://127.0.0.1:8000/o/token/'<--> --bind 127.0.0.1:8000)。

虽然runserver默认情况下是线程化的,但 Gunicorn 不是,当它为请求提供服务时,您正在该请求中执行另一个请求......死锁时间!

要么为 Gunicorn 启用更多工作人员,要么以不需要通过 HTTP 发出内部请求的方式重构您的应用程序。

于 2020-03-31T10:27:11.963 回答