代码发布平台前戏

单纯做后端的话,可以用FastAPI框架

1.服务端主动给客户端推送消息

截至目前为止,我们所写的web项目基本都是基于HTTP协议的

HTTP协议有四大特性:无链接(我请求 你响应 我俩没关系了 直接断开链接)

基于HTTP协议实现服务端主动给客户端推送消息好像有点麻烦~~~

我们都经历过,浏览器打开一个网站不动,网站过一会儿自动弹出消息

再比如网页版本的微信和qq,我们所有人创建一个群聊,所有人加入群聊之后都不动

我朝群中发送一个消息,你们所有人的页面上都会出现我发送的消息

如何实现服务端主动给客户端推送消息的效果

伪实现

可不可以让客户端浏览器每隔一段时间偷偷的去服务器请求数据

这样能实现效果,但是内部本质还是客户端朝服务端发送消息

  • 轮询
  • 长轮询

真实现

  • Websocket

它的诞生真正的实现了服务端主动给客户端推送消息

疑问

为什么要做到服务端主动给客户端推送消息,该技术点有哪些应用场景

  • 大屏幕投票实时展示
  • 任务的执行流程
  • 群聊功能。。。

2.gojs插件

前端插件,主要用来通过代码渲染图标

参考网站:https://gojs.net/latest/index.html

3.paramiko模块

类似于XShell远程链接服务器并操作

4.gitpython模块

用python代码操作git

内容回顾

  • ajax操作

    异步提交,局部刷新

    用它就可以偷偷的朝服务端发送请求

    $.ajax({
      url:‘‘,  # 控制后端提交路径
      type:‘‘,  # 控制请求方式
      data:{},  # 控制提交的数据
      dataType:"JSON",  # django后端用HttpResponse返回json格式字符串,args不会自动反序列化,拿到的还是json格式字符串string字符类型,而如果是用JsonResponse返回的那么args会自动返序列化成前端js的对象类型
      success:function(args){
        # 异步回调机制
    })
    
    def index(request):
        if request.method == ‘POST‘:
            back_dic = {‘msg‘:‘hahaha‘}
            return HttpResponse(json.dumps(back_dic))  #    需要
            return JsonResponse(back_dic)  # 不需要
        return render(request,‘index.html‘)
    # 后续在写ajax请求的时候建议你加上dataType参数
  • 队列

    队列:先进先出

    堆栈:先进后出

    python内部在内存中帮我们维护了一个队列

    import queue
    ?
    ?
    # 创建一个队列
    q = queue.Queue()
    ?
    ?
    # 往队列中添加数据
    q.put(111)
    q.put(222)
    ?
    ?
    # 从队列中取数据
    v1 = q.get()
    v2 = q.get()
    # v3 = q.get()  # 没有数据原地阻塞直到有数据
    # v4 = q.get_nowait()  # 没有数据直接报错
    try:
        v5 = q.get(timeout=3)  # 没有数据等待10s再没有就报错 queue.Empty
    except queue.Empty as e:
        pass
    print(v1,v2)
    ?
    # 实际生产中不会使用上述的消息队列 会使用功能更加的强大的
    """
    消息队列
        redis
        kafka
        rebittMQ
    """

    基于ajax与队列其实就可以实现服务端给客户端推送消息的效果

    服务端给每一个客户端维护一个队列,然后再浏览器上面通过ajax请求朝对应队列获取数据,没有数据就原地阻塞(pending状态),有就直接拿走渲染即可

    群聊:获取群聊中某个人发送的消息,将该消息给每一个队列

    一会儿我们用代码实现一下

  • 递归

    # python中有最大递归限制 997 998 官网给出的是1000
    """
    在python中是没有尾递归优化的!!!
    """
    def func():
      func()
    func()  # 不行
    ?
    # 在js中 是没有递归的概念的 函数可以自己调用自己 属于正常的事件机制
    function func1(){
      $.ajax({
        url:‘‘,
        type:‘‘,
        data:‘‘,
        dataType:‘JSON‘,
        success:function({
          func1()  # 可以
        })
      })
    }
    func1()
  • 校验性组件

    forms组件

    modelform组件(它是forms组件的加强版本,功能和代码差不多,但是更加的方便)

今日内容详细

服务端向客户端推送消息

  • 轮询
  • 长轮询
  • websocket

轮询(效率极低,基本不用)

"""
让浏览器定时(例如每隔5秒发一次)通过ajax朝服务端发送请求获取数据
?
缺点:
    消息延迟严重
    请求次数多 消耗资源过大
"""

长轮询(兼容性好)

"""
服务端给每个浏览器创建一个队列,让浏览器通过ajax向后端偷偷的发送请求,去各自对应的队列中获取数据,如果没有数据则会有阻塞,但是不会一直阻塞,比如最多阻塞30秒(pending)后给一个响应,无论响应是否是真正的数据,都会再次通过回调函数调用请求数据的代码
?
有点:
    消息基本没有延迟
    请求次数降低 消耗资源减少
"""
# 大公司需要考虑兼容性问题 追求兼容 目前网页版本的微信和qq用的就是长轮询
?
?
# ps:给标签绑定事件的方式大致有两种
# 1标签查找绑定
$(‘p‘).click()
# 2直接写函数  注意括号不能少
<p onclick="sendMsg()"></p>

基于ajax,队列以及异常处理实现简易版本的群聊功能(长轮询)

后端

import queue
?
q_dict = {}  # {唯一标示:对应的队列,唯一标示:对应的队列}
?
def home(request):
    # 获取客户端浏览器的唯一标识
    name = request.GET.get(‘name‘)
    # 生成一一对应关系
    q_dict[name] = queue.Queue()
    return render(request,‘home.html‘,locals())
?
def send_msg(request):
    if request.method == ‘POST‘:
        # 获取用户发送的消息
        message = request.POST.get(‘content‘)
        print(message)
        # 将消息给所有的队列发送一份
        for q in q_dict.values():
            q.put(message)
        return HttpResponse(‘OK‘)
?
def get_msg(request):
    # 获取用户唯一标示
    name = request.GET.get(‘name‘)
    # 回去对应的队列
    q = q_dict.get(name)
    back_dic = {‘status‘:True,‘msg‘:‘‘}
    try:
        data = q.get(timeout=10)
        back_dic[‘msg‘] = data
    except queue.Empty as e:
        back_dic[‘status‘] = False
    return JsonResponse(back_dic)

前端

<h1>聊天室:{{ name }}</h1>
<input type="text" id="txt">
<button onclick="sendMsg()">提交</button>
?
<h1>聊天记录</h1>
<div class="record">
?
</div>
?
<script>
   function sendMsg() {
        // 朝后端发送消息
       $.ajax({
           url:‘/send_msg/‘,
           type:‘post‘,
           dataType:‘JSON‘,
           data:{‘content‘:$(‘#txt‘).val()},
           success:function (args) {
?
           }
       })
   }
?
   function getMsg() {
        // 偷偷的朝服务端要数据
        $.ajax({
            url:‘/get_msg/‘,
            type:‘get‘,
            data:{‘name‘:‘{{ name }}‘},
            success:function (args) {
                if (args.status){
                    // 获取消息 动态渲染到页面上
                    // 1 创建一个p标签
                    var pEle = $(‘<p>‘);
                    // 2 给p标签设置文本内容
                    pEle.text(args.msg);
                    // 3 将p标签添加到div内部
                    $(‘.record‘).append(pEle)
                }
                getMsg()
            }
        })
   }
   // 页面加载完毕立刻执行
   $(function () {
        getMsg()
   })
</script>

websocket(主流浏览器都支持)

"""
网络协议
    HTTP  不加密传输
    HTTPS  加密传输
        上面两个都是短链接/无链接
    WebSocket  加密传输
        浏览器和服务端创建链接之后默认不断开(联想网络编程TCP recv和send方法)
        它的诞生能够真正的实现服务端给客户端推送消息
"""

内部原理

"""
websocket实现原理可以分为两部分
    1.握手环节(handshake):并不是所有的服务端都支持websocket 所以用握手环节来验证服务端是否支持websocket
    2.收发数据环节:数据解密
"""
?
"""
1.握手环节
    浏览器访问服务端之后浏览器会立刻生成一个随机字符串
    浏览器会将生成好的随机字符串发送给服务端(基于HTTP协议 放在请求头中),并且自己也保留一份

    服务端和客户端都会对该随机字符串做以下处理
    1.1 先拿随机字符串跟magic string(固定的字符串)做字符串的拼接
    1.2 将拼接之后的结果做加密处理(sha1+base64)

    服务端将生成好的处理结果发送给浏览器(基于HTTP协议 放在响应头中)
    浏览器接受服务端发送过来的随机字符串跟本地处理好的随机字符串做比对,如果一致说明服务端支持websocket,如果不一致说明不支持
?
2.收发数据环节
    前提知识点:
        1.基于网络传输数据都是二进制格式 在python中可以用bytes类型对应
        2.进制换算

    先读取第二个字节的后七位数据(payload) 根据payload做不同的处理
    =127:继续往后读取8个字节数据(数据报10个字节)
    =126:继续往后读取2个字节数据(数据报4个字节)
    <=125:不再往后读取(数据2个字节)

    上述操作完成后,会继续往后读取固定长度4个字节的数据(masking-key)
    依据masking-key解析出真实数据
"""
# 关键字:magic string、sha1/base64、payload(127,126,125)、masking-key

代码验证(无需掌握)

# 请求头中的随机字符串
Sec-WebSocket-Key: NlNG/FK/FrQS/RH5Bcy9Gw==
# 响应头
tpl = "HTTP/1.1 101 Switching Protocols\r\n"       "Upgrade:websocket\r\n"       "Connection: Upgrade\r\n"       "Sec-WebSocket-Accept: %s\r\n"       "WebSocket-Location: ws://127.0.0.1:8080\r\n\r\n"
response_str = tpl %ac.decode(‘utf-8‘)  # 处理到响应头中
import socket
import hashlib
import base64

# 正常的socket代码
sock = socket.socket()  # 默认就是TCP
# 避免mac本重启服务经常报地址被占用的错误
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind((‘127.0.0.1‘, 8080))
sock.listen(5)

conn, address = sock.accept()
data = conn.recv(1024)  # 获取客户端发送的消息
# print(data.decode(‘utf-8‘))

def get_headers(data):
    """
    将请求头格式化成字典
    :param data:
    :return:
    """
    header_dict = {}
    data = str(data, encoding=‘utf-8‘)

    header, body = data.split(‘\r\n\r\n‘, 1)
    header_list = header.split(‘\r\n‘)
    for i in range(0, len(header_list)):
        if i == 0:
            if len(header_list[i].split(‘ ‘)) == 3:
                header_dict[‘method‘], header_dict[‘url‘], header_dict[‘protocol‘] = header_list[i].split(‘ ‘)
        else:
            k, v = header_list[i].split(‘:‘, 1)
            header_dict[k] = v.strip()
    return header_dict

def get_data(info):
    """
    按照websocket解密规则针对不同的数字进行不同的解密处理
    :param info:
    :return:
    """
    payload_len = info[1] & 127
    if payload_len == 126:
        extend_payload_len = info[2:4]
        mask = info[4:8]
        decoded = info[8:]
    elif payload_len == 127:
        extend_payload_len = info[2:10]
        mask = info[10:14]
        decoded = info[14:]
    else:
        extend_payload_len = None
        mask = info[2:6]
        decoded = info[6:]

    bytes_list = bytearray()
    for i in range(len(decoded)):
        chunk = decoded[i] ^ mask[i % 4]
        bytes_list.append(chunk)
    body = str(bytes_list, encoding=‘utf-8‘)

    return body

header_dict = get_headers(data)  # 将一大堆请求头转换成字典数据  类似于wsgiref模块
client_random_string = header_dict[‘Sec-WebSocket-Key‘]  # 获取浏览器发送过来的随机字符串
magic_string = ‘258EAFA5-E914-47DA-95CA-C5AB0DC85B11‘  # 全球共用的随机字符串 一个都不能写错
value = client_random_string + magic_string  # 拼接
ac = base64.b64encode(hashlib.sha1(value.encode(‘utf-8‘)).digest())  # 加密处理

tpl = "HTTP/1.1 101 Switching Protocols\r\n"       "Upgrade:websocket\r\n"       "Connection: Upgrade\r\n"       "Sec-WebSocket-Accept: %s\r\n"       "WebSocket-Location: ws://127.0.0.1:8080\r\n\r\n"
response_str = tpl %ac.decode(‘utf-8‘)  # 处理到响应头中

# 基于websocket收发消息
conn.send(bytes(response_str,encoding=‘utf-8‘))

while True:
    data = conn.recv(1024)
    # print(data)  # 加密数据 b‘\x81\x89\n\x94\xac#\xee)\x0c\xc6\xaf)I\xb6\x80‘
    value = get_data(data)
    print(value)

总结:上述代码知识为了诠释websocket内部本质,实际应用直接使用别人封装好的模块即可

实际应用中,并不是所有的后端框架默认都支持websocket协议,如果你想使用的话,可能需要借助于不同的第三方模块

"""
后端框架
django
    默认不支持websocket
    第三方模块:channels
?
flask
    默认不支持websocket
    第三方模块:geventwebsocket

tornado
    默认支持websocket
"""

django如何支持websocket

# 下载channels模块需要注意的点
# 1.版本不要用最新版 推荐使用2.3版本即可 如果你安装最新版可能会出现自动将你本地的django版本升级为最新版
# 2.python解释器建议使用3.6版本(3.5可能会有问题,3.7可能会有问题 具体说明问题没有给解释)
pip3 install channles==2.3
"""channels模块内部帮你封装了握手/加密/解密等所有操作"""

基本使用

  • 注册app

    INSTALLED_APPS = [
        ‘channels‘
    ]

    注册完成后,django会无法启动,会直接报错

    CommandError: You have not set ASGI_APPLICATION, which is needed to run the server.

  • 配置
    # 2 配置变量
    ASGI_APPLICATION = ‘s13_day01.routing.application‘
    ASGI_APPLICATION = ‘项目名同名的文件名.文件夹下py文件名默认就叫routing.该py文件内部的变量名默认就叫application‘
  • 去项目名同名的文件夹下面新建一个py文件,定义application变量
    from channels.routing import ProtocolTypeRouter,URLRouter
    ?
    ?
    application = ProtocolTypeRouter({
        ‘websocket‘:URLRouter([
            # 书写websocket路由与视图函数对应关系
        ])
    })

上述操作配置完成后,启动django会由原来的wsgiref启动变成asgi启动(内部:达芙妮)

并且启动之后django即支持websocket也支持http协议

基于http的操作还是在urls.py和views.py中完成

基于websocket的操作则在routing.py和consumer.py(对应的应用中创建)中完成

如:

routing.py

from channels.routing import ProtocolTypeRouter,URLRouter
from django.conf.urls import url
from app01 import consumer
?
application = ProtocolTypeRouter({
    ‘websocket‘:URLRouter([
        # 书写websocket路由与视图函数对应关系
        url(r‘index/‘,consumer.XXXClass)  #CBV
    ])
})

consumer.py

class XXXClass:
    pass

原文地址:https://www.cnblogs.com/baohanblog/p/12693898.html

时间: 2024-10-09 11:25:03

代码发布平台前戏的相关文章

手游公司运维之利用Rundeck自动化运维工具和Shell脚本构建测试环境代码发布平台和生产环境代码发布平台

在做手游运维工作之前,我接触的代码发布都是常规的软件发布,有固定的发布周期.之前工作的那个外企有严格的发布周期,一年中的所有发布计划都是由Release Manager来控制,每次发布之前都需要做一些准备工作,如填写发布表单,上传发布需要的资源文件,联系发布过程中的相关人员,如开发和测试.最后在公司内部开发的发布平台上按照指定的时间点击鼠标对一个集群内的几台主机或全部主机进行代码发布.这个发布平台还是基于rsync服务实现的.虽然每个星期都有各种服务的发布,但是整个发布流程是可以控制的,并且发布

运维自动化--代码发布平台

随着业务线和项目增加,使用rsync 命令方式发布代码已经无法满足需求.所以想搞一套代码发布平台. [需求收集] 1.年前开发了一个版本,当时只是和前端PHP团队沟通过.开发完发现后端需求和前端的需求有出入.所以这个版本给废掉了. 2.春节后,跟各业务线Leader重新沟通了一下需求,准备重新开发. [开发架构] 1.前端设计: 页面使用Bootstrap.CSS和Jquery. 2.数据库设计: 数据库使用Mysql. 3.后端开发语言: 比较熟悉Django,所以使用Django开发. [功

APICloud发布低代码开发平台

云原生的出现,致使传统IT模式正在集中向云架构.云开发转型,其中在企业业务的互联网化.数字化进程中尤为突出,并衍生出"敏捷开发"."快速迭代"的刚性需求.面对双模IT,如何打造全新的IT团队与模式?并更好的满足由业务部门发起庞大且零散的IT需求,成为众多CIO与CTO的巨大挑战.在复杂的供需环境中,低代码开发平台正在快速的出现和普及. 与此同时,效率一直是企业生产力水平的重要标杆,而效率的服务对象则是需求:数字化时代的下半场,能否突破传统效率边界,甚至决定着一个企业

盘点类似于GitHub的代码托管平台码云的2016年度热门项目排行榜TOP 50

码云平台发展至今,涌现了越来越多优秀的开源项目,越来越多的开源作者在上面分享自己的作品.我们希望通过此榜单在回顾总结2016年国内开源趋势,了解开源作者现状的同时,也能便于大家更好地找到自己需要的项目. 2016 年度码云热门项目排行榜 TOP 50 是通过开源项目2016年在码云上的 Watch.Star.Fork 数量来评定的榜单. 1.JFinal 简介: JFinal 是基于 Java 语言的极速 WEB + ORM 框架,其核心设计目标是开发迅速.代码量少.学习简单.功能强大.轻量级.

Git学习总结_03_代码托管平台简介

可以说GitHub的出现完全颠覆了以往大家对代码托管网站的认识.GitHub不但是一个代码托管网站,更是一个程序员的SNS社区.GitHub真正迷人的是它的创新能力与Geek精神,这些都是无法模仿的.在GitHub出现后,国内也出现了大量的代码托管网站,国内外有的代码托管网站也都开始引入GitHub的某些元素.开源中国 http://git.oschina.net/Git @ OSC 是开源中国社区团队基于开源项目 GitLab 开发的在线代码托管平台.Git @ OSC 除了提供最基础的 gi

程序员必须知道的几个Git代码托管平台

说到Git代码托管平台,首先推荐的是GitHub,好多好的开源项目都来自GitHub,但是GitHub只能新建公开的Git仓库,私有 仓库要收费,如果你做的是一个开源项目,可以首选GitHub.下面推荐几个比较好的Git代码托管平台,这里我不做过多的说明和评价,也好让大家多看 看,比较一下,找到自己的"真爱". 1.GitHub 关于GItHub相信大家都有耳闻,我就不详细介绍了.GitHub地址:https://github.com/,其首页如图: 2.Gitlab 对于有些人,提到

代码托管平台

常用Git代码托管服务分享 Git Repository代码托管服务越来越流行,目前有很多商业公司和个人团队逐渐切换项目到 Git平台进行代码托管.本文分享一些常用的Git代码托管服务,其中一些提供私有项目保护服务,特别有利于远程团队协作开发项目使用. GitHub https://github.com/ 目前最流行的Git服务,也是人气最旺盛的Git代码托管网站.但是私有项目需要付费,个人认为GitHub更适合开源项目使用,很多开发人员在这个平台上分享开发经验,同时协作完成项目. GitLab

软件工程第一周作业:软件工程和代码托管平台

0x01 :请问 "软件" 和 "软件工程" 这些词汇是如何出现的 - 何时.何地.何人 软件:最早的软件的概念,由Richard R. Carhart在1953年8月,Rand Corporation的备忘录中提出,并将其使用于工程背景中(The earliest known publication of the term "software" in an engineering context was in August 1953 by Ri

【转】程序员必须知道的几个Git代码托管平台

 一.VS2013中克隆远程Git仓库和SSH的配置 1.VS2013中克隆远程项目  首先感谢园友的评论和补充,今日又仔细看了一下,VS2013中是可以克隆项目的,只是我一直用的GitHub来克隆的,所以没有注意到.我们打开VS2013,切换到团队资源管理器,如图: 点击连接到团队项目...,会看到如下图界面: 点击克隆,输入远程Git地址,然后点击克隆按钮,就会将远程仓库的项目克隆到本地,路径为下面文本框中的地址,我们也可以点击后面的...按钮进行修改或者手动输入. 2.SSH的配置 如果安