簡體   English   中英

為什么 Nginx 會截斷 gRPC 流響應?

[英]Why is Nginx truncating the gRPC streaming response?

我之前問過這個問題,但決定刪除那個舊問題,並用最小可重現的例子重新表述它。 問題是,當我在 nginx 上部署我的 gunicorn 網絡服務器時,我通過 gRPC 來自我的 go 服務器的流式響應被截斷。 所有詳細信息都可以在存儲庫中找到。 我對這個站點的 nginx 配置如下所示:

server {
    listen 80 default_server;
    server_name example.com;

    location / {
    #include proxy_params;
    proxy_pass http://localhost:5000;
    proxy_buffering off;
    chunked_transfer_encoding off;
    }
}

前端接收和解析響應的代碼如下所示:

        <script>
            (async function(){
                const response = await fetch("{{ url_for('realtimedata') }}");
                const reader = response.body.pipeThrough(new TextDecoderStream()).getReader();
                while (true) {
                    const {done, value} = await reader.read();
                    if (done) break;
                    try {
                        console.log('Received', value);
                        const rtd = JSON.parse(value);
                        console.log('Parsed', rtd);
                    } catch(err) {
                        console.log(err);
                    }
                }
            })()
        </script>

關於來自 go 服務器的數據,需要注意的是,一個服務提供一個具有 96 個字段的數據對象,另一個服務提供一個具有 200 個字段的數據。 這使得傳入的流響應具有可變長度(以字節為單位)。

我想使用 gunicorn 因為我可能同時有多個聽眾。 使用 gunicorn 解決了一個問題,即所有響應都發送到網絡服務器,但它們分布在活動客戶端之間。 所以每個客戶端都會得到不同的響應,但不是所有的。

編輯:我嘗試將 goserver 上的響應對象大小更改為與兩個服務相同,但截斷仍然發生。 具有可變長度似乎不是問題。 我也試過用 uWSGI 而不是 gunicorn 來做這件事,但問題仍然存在。 我什至將uwsgi_buffering off;設置uwsgi_buffering off; 並且問題仍然存在。

更新:我已經用 Apache2 而不是 Nginx 運行了最小的可重現示例,我遇到了同樣的問題。 也許問題出在其他方面。

查看您的python代碼,似乎使用websockets將數據從后端推送到前端會做得更好。 我已經重寫了您的后端以使用 FastAPI 而不是 Flask 並修改了 nginx 配置。

main.py

import asyncio
import dependencies.rpc_pb2 as r
import dependencies.rpc_pb2_grpc as rpc
from fastapi import FastAPI, WebSocket, Request
from fastapi.templating import Jinja2Templates
import grpc
import json
import os

os.environ["GRPC_SSL_CIPHER_SUITES"] = 'HIGH+ECDSA'

app = FastAPI()
templates = Jinja2Templates(directory="templates")
server_addr = "localhost"
server_port = 3567

@app.get("/")
def read_root(request: Request):
    return templates.TemplateResponse("index.html", {"request": request})

def parseRtd(rtd):
    rtdDict = {}
    rtdDict["source"] = rtd.source
    rtdDict["is_scanning"] = rtd.is_scanning
    rtdDict["timestamp"] = int(rtd.timestamp)
    rtdDict["data"] = {}
    for key, v in rtd.data.items():
        rtdDict["data"][int(key)] = {"name": v.name, "value": v.value}
    return rtdDict

def get_rtd():
    channel = grpc.insecure_channel(f"{server_addr}:{server_port}")
    stub = rpc.RpcServiceStub(channel)
    for rtd in stub.SubscribeDataStream(r.SubscribeDataRequest()):
        yield parseRtd(rtd)

@app.websocket("/ws")
async def websocket_endpoint(websocket: WebSocket):
    await websocket.accept()
    await websocket.send_json({"test": "this is a test"})
    it = get_rtd()
    while True:
        await asyncio.sleep(0.1)
        payload = next(it)
        await websocket.send_json(payload)

index.html

<html>
    <head>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.4.0/socket.io.js" integrity="sha512-nYuHvSAhY5lFZ4ixSViOwsEKFvlxHMU2NHts1ILuJgOS6ptUmAGt/0i5czIgMOahKZ6JN84YFDA+mCdky7dD8A==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
    </head>
    <body>
        <script>
            var ws = new WebSocket("ws://localhost:5000/ws");     
            ws.onopen = function () {
                console.log("websocket was open");
            };
            ws.onclose = () => {
                console.log("Websocket was closed!");
            }
            ws.onerror = (error) =>{
                console.error("Websocket error: " + JSON.stringify(error));
            };
            ws.onmessage = (message) => {
                console.log("MSG: " + message.data );
            };
        </script>
    </body>
</html>

webserver.conf

server {
    listen 80 default_server;
    server_name example.com;

    location / {
        include proxy_params;
        proxy_pass http://localhost:5000;
    }

    location /ws {
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $host;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "Upgrade";
        proxy_pass http://localhost:5000;
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM