python --实现sse推送(nginx配置)、redis

flask-sse

import json

import redis
from flask import Flask, current_app
from flask_cors import CORS
from flask_sse import sse
from redis import Redis

app=Flask(__name__)
CORS(app, supports_credentials=True)

app.config["REDIS_URL"]="redis://localhost"
app.register_blueprint(sse,url_prefix='/stream')   # sse请求地址(订阅消息)

@app.route('/send')   # 发布消息
def send_message():
    sse.publish({"message":"Hello!"},type='greeting')
    return"Message sent!"

页面demo

<!DOCTYPE html>
<html>
<head>
    <title>SSE Demo</title>
</head>
    <body>
    <div id="message-div"></div>
    <script>
        var source = new EventSource("http://127.0.0.1:5000/stream") // 创建EventSource对象,连接到SSE流

        source.addEventListener("message", function (event) { // 监听"message"事件
            console.log(event.data)});
    </script>
    </body>
</html>

自实现:

import json

import loguru
import redis
from flask import Flask, current_app, request
from flask_cors import CORS

class PubSub(object):
    '''发布订阅者'''

    def __init__(self):
        self._conn = redis.Redis(connection_pool=redis.ConnectionPool(decode_responses=True))

    def pub(self, message, channel_name, type):
        '''
        发布
        @params message       --> 消息内容; 字符串格式;
        @params channel_name  --> 频道;
        @params type          --> 自定义类型;
        '''
        self._conn.publish(channel_name, json.dumps({'data': {'message': message}, 'type': type}))

    def sub(self, channel_name):
        '''
        订阅
        @params channel_name  --> 频道;
        响应格式:
            event:greeting
            data:{"message": "Hello!"}

            event:greeting
            data:{"message": "Hello!"}
        '''
        pubsub = self._conn.pubsub()  # 生成订阅对象
        pubsub.subscribe(channel_name)
        try:
            for pubsub_message in pubsub.listen():
                loguru.logger.debug(f'消息中间件:{pubsub_message}')
                yield getattr(self, pubsub_message['type'])(pubsub_message)
        finally:
            try:
                pubsub.unsubscribe(channel_name)
            except ConnectionError: ...

    def message(self, pubsub_message):
        '''常规消息'''
        msg_dict = json.loads(pubsub_message['data'])
        return f'event:{msg_dict["type"]}' + '\n' + f"data:{str(msg_dict['data'])}" + '\n\n'

    def subscribe(self, *args):
        '''发起连接消息'''
        return f'event:connect' + '\n' + f"data:ok" + '\n\n'


app=Flask(__name__)
CORS(app, supports_credentials=True)
p = PubSub()
@app.route('/sse')
def sse():
    '''
    订阅消息
    Eg:
    <script>
        var source = new EventSource("/sse?channel_name=demo") // 创建EventSource对象,连接到SSE流

        source.addEventListener("message", function (event) { // 监听"message"事件
            console.log(event.data)});
    </script>
    '''
    channel_name = request.args.get('channel_name')
    return current_app.response_class(
        p.sub(channel_name),
        mimetype='text/event-stream',
    )

@app.route('/send')
def send():
    '''
    发布消息
    /send?channel_name=demo&mes=测试下
    '''
    mes = request.args.get('mes')
    channel_name = request.args.get('channel_name')
    p.pub(channel_name=channel_name, message=mes, type='message')
    return f'send:{mes}'


if __name__ == '__main__':
    app.run(debug=True)

nginx配置

 server {
        listen       443 default ssl;
        listen       [::]:443 default ssl;
        server_name  _;

        ssl_certificate /root/project/code/tree_hole_gpt/ssl/(文件);
        ssl_certificate_key /root/project/code/tree_hole_gpt/ssl/(文件);
        ssl_session_timeout 5m;
        ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;
        ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
        ssl_prefer_server_ciphers on;

        gzip on;
        gzip_buffers 4 32k;
        gzip_types "*";
        gzip_vary on;
        gzip_min_length 1k;
        gzip_comp_level 6;
        gzip_http_version 1.1;

        proxy_set_header Host $http_host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_read_timeout 120s;      #nginx代理等待后端服务器的响应时间
        proxy_connect_timeout 120s;  #nginx代理与后端服务器连接超时时间(代理连接超时)
        proxy_send_timeout 120s;    #后端服务器数据回传给nginx代理超时时间


        location / {
            proxy_pass http://127.0.0.1:8001;
        }

		location /ws/ {#ws协议时
            proxy_http_version 1.1;
            proxy_set_header Host  $host;
            proxy_set_header X-Real-Ip $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Nginx-Proxy true;
            proxy_redirect off;
            client_max_body_size 10m;
            proxy_pass http://127.0.0.1:8001;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";
            proxy_connect_timeout 300s;
            proxy_read_timeout 300s;
            proxy_send_timeout 300s;
       }

        location /sse/ {
            proxy_set_header Host $http_host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
            proxy_cache off;
            proxy_buffering off;
            proxy_pass http://127.0.0.1:8001;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "Upgrade";
        }
        location /media/ {
            alias /root/project/code/tree_hole_gpt/media/;
            expires 30d;
        }

        location /static/ {
            alias /root/project/static/;
            expires 30d;
        }
    }
}

django sse

REDIS_CACHE = redis.Redis(host=REDIS_HOST, port=REDIS_PORT, db=1, password='Admin123.', decode_responses=True)
TIXIAN_CHANNEL_NAME = 'TIXIAN'  # 提现消息订阅者频道名

class SubMessageView(View):
    '''消息订阅'''

    def get(self, request, *args, **kwargs) -> StreamingHttpResponse:
        '''消息订阅者'''
        response = StreamingHttpResponse()
        response.streaming_content = self.chat()
        response['Cache-Control'] = 'no-cache'
        # response['Content-Type'] = 'application/json'
        response['Content-Type'] = 'text/event-stream'
        return response

    def chat(self):
        pubsub = REDIS_CACHE.pubsub()
        pubsub.subscribe(TIXIAN_CHANNEL_NAME)
        try:
            for pubsub_message in pubsub.listen():
                loguru.logger.debug(f'消息中间件:{pubsub_message}')
                if pubsub_message['type'] == 'subscribe':
                    yield f'event:connect' + '\n' + f"data:ok" + '\n\n'
                else:
                    msg_dict = json.loads(pubsub_message['data'])
                    yield f'event:{msg_dict["type"]}' + '\n' + f"data:{json.dumps(msg_dict['data'])}" + '\n\n'
        finally:
            try:
                pubsub.unsubscribe(TIXIAN_CHANNEL_NAME)
            except ConnectionError:
            ...

发布

REDIS_CACHE.publish(TIXIAN_CHANNEL_NAME,
                            json.dumps({'data':
                                            {'message': f'{SERVER_URL}/media/music/music.mp3'},
                                        'type': 'tixian_message'}
                                       ))

redis 列表操作

在Python中,可以使用redis-py库来操作Redis数据库中的列表数据结构。以下是一些常用的Redis列表操作方法:

lpush(key, value): 在列表的左侧插入一个或多个值

rpush(key, value): 在列表的右侧插入一个或多个值

lpop(key): 移除并返回列表的左侧第一个元素

rpop(key): 移除并返回列表的右侧第一个元素

lrange(key, start, end): 获取列表指定范围内的元素

llen(key): 获取列表的长度

lindex(key, index): 获取列表指定索引位置的元素

lset(key, index, value): 设置列表指定索引位置的元素的值

lrem(key, count, value): 移除列表中指定值的元素

ltrim(key, start, end): 截取列表指定范围内的元素

这些方法可以帮助你对Redis中的列表数据进行增删改查操作。你可以根据具体的需求选择合适的方法来操作Redis列表。

hash操作

# 定义用户信息
user_id = 1
user_info = {
    'username': 'john_doe',
    'email': 'john@example.com',
    'age': 30,
    'city': 'New York'
}

# 将用户信息存储到Redis的Hash中
r.hmset(f'user:{user_id}', user_info)

# 获取用户信息
stored_user_info = r.hgetall(f'user:{user_id}')

# 打印用户信息
print("Stored User Info:")
for field, value in stored_user_info.items():
    print(f"{field.decode('utf-8')}: {value.decode('utf-8')}")

# 修改用户信息
r.hset(f'user:{user_id}', 'age', 31)

# 获取修改后的用户信息
updated_user_info = r.hgetall(f'user:{user_id}')

# 打印修改后的用户信息
print("\nUpdated User Info:")
for field, value in updated_user_info.items():
    print(f"{field.decode('utf-8')}: {value.decode('utf-8')}")

# 删除用户信息
r.delete(f'user:{user_id}')

# 检查用户信息是否被删除
deleted_user_info = r.hgetall(f'user:{user_id}')
if not deleted_user_info:
    print("\nUser info has been deleted.")
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

像风一样的男人@

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值