# dj_sse **Repository Path**: duans/dj_sse ## Basic Information - **Project Name**: dj_sse - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-03-16 - **Last Updated**: 2026-03-19 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # SSE(server side event)服务端事件 ## websocket和sse的区别  ## SSE实现服务端消息推送 1. **单向性匹配**:你的需求是“服务端推给前端”,不需要前端频繁发数据给服务端(除了心跳或重连),这完美契合 SSE 的单向特性。 2. **自动重连**:网络波动导致连接断开时,浏览器会自动尝试重连(这是 `EventSource` 的内置功能),无需你写复杂的断线重连逻辑。 3. **开发简单**:前端只需几行 JS,后端只需返回一个流式响应,不需要像 WebSocket 那样处理握手、帧解析等复杂协议。 4. **资源消耗低**:相比 WebSocket,SSE 基于 HTTP,对服务器并发连接的开销略小(取决于具体实现),且更容易穿过防火墙和代理。 ### 流式数据接口 服务端 ```python # views.py from django.http import StreamingHttpResponse # 定义流式接口 def stream(request): import time # 定义生成器函数, 不断生成数据 def generated_result(): # 实际场景: 查询数据库中的最新数据 time.sleep(0.5) # SSE要求的数据格式 data: 响应数据 yield f'data: {json.dumps({"message": "有新订单了-"+str(randint(1000,9999))})}\n\n' # 创建流式响应对象: 需要传入生成器函数, 设置content_type响应头为text/event-stream response=StreamingHttpResponse(generated_result(),content_type='text/event-stream') # 设置响应数据格式: sse需要的数据格式 # resposne['content_type']="text/event-stream" # 禁止浏览器缓存 response['Cache-Control'] = 'no-cache' # 禁止Nginx缓存 response['X-Accel-Buffering'] = 'no' # 返回响应对象 return response ``` 客户端 ```js // 创建eventSource对象, 和服务端建立链接, 默认get请求方式 event=new EventSource('http://localhost:8000/stream/') // 监听服务端的消息 event.onmessage = function(event) { // 接受服务端响应的流式数据, 并将其转换为js对象 const data = JSON.parse(event.data); }; // 监听链接中断事件 event.onerror = function(err) { console.log('err') }; ``` ## 完整实现方案 (Django + 前端) 我们将实现一个功能:当数据库产生新订单时,后台管理页面立即弹出通知。 ### 1. 核心架构设计 - **推送通道**:前端通过 `EventSource` 连接 Django 的 SSE 视图。 - 触发机制 - **方案 A (推荐 - 生产级)**:订单创建后,发送消息到 **Redis Pub/Sub** 或 **Celery Signal**。SSE 视图订阅 Redis 频道,一有消息立刻推送。 - **方案 B (简单 - 演示级)**:SSE 视图轮询数据库(不推荐,有延迟且浪费资源)。 - **方案 C (Django Channels)**:使用 Django Channels 的 Group 功能(功能最强,但部署复杂)。 **下面采用最通用且高效的“方案 A:Redis Pub/Sub”模式进行演示。** ### 2. 后端实现 (Django + Redis) 首先安装依赖: ``` pip install redis ``` **views.py**: ```python from django.shortcuts import render,redirect import json import redis from django.http import StreamingHttpResponse from django.conf import settings from datetime import datetime from random import randint from django.http import HttpResponse,HttpResponseRedirect from django.views import View import uuid # 初始化 Redis 连接 r = redis.Redis(host='localhost', port=6379, db=0) def index(request): return redirect('order_list') def order_list(request): return render(request, 'order_list.html') class Publish(View): def get(self,request): return render(request,'publish.html') def post(self,request): # body=json.loads(request.body.decode()) body=request.POST payload = { "type": "new_order", "order_id": uuid.uuid4().hex, "customer": body.get('customer','匿名'), "amount": body.get('amount',0), "time": datetime.now().strftime('%Y-%m-%d %H:%M:%S') } # 发布订单消息到redis的new_orders频道 r.publish('new_orders', json.dumps(payload)) # 可以传递路径地址, 也可以传递路由name # return redirect('publish') # 必须传路径地址 return HttpResponseRedirect('/publish/') # 监听redis channel消息 def subscribe(request): """ SSE 视图:监听 Redis 频道,有新订单时推送 """ user_id = request.user.id # 每个用户或管理员组可以订阅不同的频道,这里假设所有管理员订阅 'new_orders' channel = "new_orders" def event_generator(): # 创建对象 pubsub = r.pubsub() # 监听频道 pubsub.subscribe(channel) try: # 监听 Redis 消息 for message in pubsub.listen(): if message['type'] == 'message': # 将字节串转换为字符串 data = message['data'].decode('utf-8') # 格式化为 SSE 标准格式 yield f"data: {data}\n\n" elif message['type'] == 'subscribe': # 订阅成功,可以发个欢迎消息 yield f'data: {{"message": "连接成功,正在监听新订单..."}}\n\n' except (BrokenPipeError, ConnectionAbortedError, KeyboardInterrupt): print(f"用户 {user_id} 断开连接") finally: pubsub.unsubscribe(channel) pubsub.close() response = StreamingHttpResponse( event_generator(), content_type='text/event-stream' ) # 关键配置:禁止缓存,防止 Nginx/浏览器缓存导致收不到实时消息 response['Cache-Control'] = 'no-cache' response['X-Accel-Buffering'] = 'no' # response['Connection'] = 'keep-alive' return response ``` ### 3. 前端实现 (HTML + JavaScript) 在后台管理系统的模板中加入以下代码: ```html
| 订单编号 | 下单用户 | 订单金额 | 下单时间 |
|---|