• docker版jxTMS使用指南:使用webSocket


    本文讲解4.6版jxTMS中如何使用webSocket,整个系列的文章请查看:4.6版升级内容

    docker版本的使用,请查看:docker版jxTMS使用指南

    4.0版jxTMS的说明,请查看:4.0版升级内容

    4.2版jxTMS的说明,请查看:4.2版升级内容

    4.4版jxTMS的说明,请查看:4.4版升级内容

    4.6版jxTMS的核心是解决分布式问题,但之所以要考虑分布式处理,诱因就是一个高频数据处理问题。这个问题需要对采集到的数据进行分析后建立一个模型。

    由于对此问题还没有充分的认识,所以想先做一个简单的数据分析并显示到页面上,好先建立起一个感性的认识。

    过程比较简单,就是抓数据然后做成一个四分位的图来显示一下,这个用matplotlib来做很容易。但问题在于数据频次要求比较高,用javascript轮询来显示,0.1秒一次就已经开始丢数据了【报ns_binding_aborted错误】,频率再高一点根本就不显示了。

    所以就想到使用webSocket来做。干起来!

    实现起来并不难,关键是要无缝的嵌入到我们之前的web体系中。所以整个解决方案是:

    1、增加了两个和websocket相关的uri:

    • /openWS:用来打开webSocket

    • /ws:用来处理webSocket的数据交换

    2、webSocket功能是动态的,通过命令行开关【–webSocket】进行控制,即命令行中给出了开关【–webSocket】,就注册并使能上面的两个路由,否则webSocket功能是关闭的

    3、webSocket功能是可定制、可扩展的

    即webSocket功能可以灵活的嵌入到任何一个页面中,使用纯javascript编写前端,可以非常容易的就为任何一个页面提供webSocket功能。

    后端可支持多个具有webSocket功能的不同页面的不同用户的同时访问,增加后端对应功能也非常简单。

    示例

    我们想实现在浏览器中显示一个图片,该图片是由后台实时刷新的,每接收通过webSocket发送过来的一帧图像数据,我们就动态的刷新到该图片中。

    1、定义一个需要启用webSocket功能的页面,我们直接上代码:

    def webPage():
        '''
    
    
    
    
    test
    
    
    
        
    
    
        '''
        pass
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45

    上述代码定义了一个webPage函数,其__doc__是一个用html描述的web页面,该页面的核心就是【{{WSJSCript}}】,除了这个之外的都是用户根据自己的需要进行定义就好,然后在需要启用webSocke功能前增加此语句即可。

    jxTMS启用webSocke功能后,对所有访问路由【/openWS】的请求,都会直接返回上述定义的html,并将其中的{{WSJSCript}}替换为dsWebSocket类的定义。

    dsWebSocket类的对象在初始时有5个参数:

    • name:该页面名,也是该webSocke功能的名字,前后端要统一起来,否则会无法显示

    • cid:用来区分不同的用户id

    • wsRecv:接收webSocke数据的回调函数

    • wsOpen:webSocke打开事件,同时回送所创建的dsWebSocket对象,可以调用其send函数来发送数据

    • wsClosed:webSocke关闭事件

    2、将此页面注册到系统中:

    from jx.web import openWebSocketHandler
    openWebSocketHandler.register('p1',webPage.__doc__)
    
    • 1
    • 2

    register有两个参数:

    • page:该参数就是创建dsWebSocket对象时应给出的name,两者必须一致,否则无法正确访问到

    • html描述,我们是用一个函数的__doc__来存放的

    注册后,我们只要访问:http://127.0.0.1:10028/openWS?page=p1。就会显示我们在webPage函数的__doc__中定义的web页面了。

    3、定义处理webSocke数据的函数

    def recvFromWS(event,ws,cid,data):
        if event == 'open':
            print(f'recvFromWS open {cid}:{data}')
            t=threading.Thread(target=dispImg,name='dispImgThread',args=[ws])
            t.start()
        else:
            print(type(data))
            print(f'recvFromWS {cid}:{data}')
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    该函数有四个参数:

    • event:webSocke触发的事件,有两个:open和recv,前者是连接打开,后者是接收到webSocke发送的数据

    • ws:后端的webSocke管理对象,主要用来发送数据

    • cid:就是上面在创建dsWebSocket对象时给出的cid,用来区分不同的用户,具体含义由用户自己定义与设置

    • data:前端发送过来的数据,都是字符串类型,所以如果前端发送的是json对象,需要自己转换;如果是open事件,则为None

    大家看到在open时,我们启动了一个线程并把ws作为参数送入。这是由于python的webSocke使用了asyncio,其需要启动事件循环来分发异步事件,而我们的主线程并没有启用这样的事件循环,所以我们单独启动一个线程来执行webSocke的数据发送工作,并在此线程中启动asyncio所需要的事件循环:

    def dispImg(ws):
        loop = asyncio.new_event_loop()
        asyncio.set_event_loop(loop)
        while True:
            image = getImage()
            #print(f'ws send data:{len(image)}')
            ws.write_message(image, binary=True)
            time.sleep(0.005)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    dispImg在启动asyncio所需要的事件循环之外,主要就是以5毫秒为间隔循环来生成数据对象并发送到web前端。

    getImage的代码我们也贴一下吧:

    import matplotlib.pyplot as plt
    
    y_list = []
    def getImage():
    	#用随机数填充数列,只保留最新的20个数据
        y_list.append(random.randint(1, 100))
        if len(y_list) > 20:
            y_list.pop(0)
    	#清空当前图形
        plt.clf()
        fig, ax = plt.subplots()
        #用y_list中的数据生成四分位图
        ax.boxplot(y_list)
        #设置x坐标
        ax.set_xticklabels(['test123'], rotation=90)
        #设置y轴的范围
        ax.set_ylim(1,100)
    
        #将生成的四分位图以png图片格式保存到内存的字节流中
        memdata = io.BytesIO()
        plt.savefig(memdata, format='png')
        image = memdata.getvalue()
    
    	#我们是5毫秒生成一张图片,速度太快,手动清除以避免内存消耗过快而有溢出
        plt.close()
        return image
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    上述代码,都在【app/module/web_test.py】文件中。

    注:需要pip install matplotlib

    4、注册webSocke数据的接收函数

    webSocketHandler.register('p1',recvFromWS)
    
    • 1

    大家看到,我们又使用了【p1】注册了recvFromWS函数,表明我们前端所使用的p1页面将和recvFromWS函数进行数据的交互。

    这样一来,我们用一个共同的名字【应用名,这里是p1】将下面三个东东绑到了一起:

    • 页面网址,通过uri【/openWS?page=应用名】来访问

    • 前端页面,通过openWebSocketHandler.register(应用名,htmlDoc)进行注册

    • 后端数据接收函数,通过webSocketHandler.register(应用名,处理函数)进行注册

    完成上述工作后,我们需要通过命令行启用webSocket功能:

    python3 main_slave.py --web --module --webSocket
    其中:
    --web开关是启动web服务,默认端口是10028
    --module开关是加载module目录下的各个模块,因为我们的web_test.py文件就放在module目录下
    --webSocket开关是启用webSocket功能,即自动注册【/openWS、/ws】两个webSocket相关的路由
    
    • 1
    • 2
    • 3
    • 4
    • 5

    webSocket开关单独启用是不起作用的,必须配合–web开关一起使用。

    参考资料:

    jxTMS设计思想

    jxTMS编程手册

    下面的系列文章讲述了如何用jxTMS开发一个实用的业务功能:

    如何用jxTMS开发一个功能

    下面的系列文章讲述了jxTMS的一些基本开发能力:

    jxTMS的HelloWorld

  • 相关阅读:
    【EKF】EKF原理
    ParCNetV2: Oversized Kernel with Enhanced Attention(超大的内核,增强了注意力)
    page、request、session和application有什么区别?以及cookie的含义
    SLAM从入门到精通(代码调试)
    Liteos信号量的使用
    20_ue4进阶末日生存游戏开发[AI基础框架搭建]
    ElasticSearch--配置--大全/详解
    Leetcode刷题day1|数组一|704.二分查找,27.移除元素,35.搜索插入位置
    大促场景下,如何做好网关高可用防护
    [论文阅读] Curriculum Semi-supervised Segmentation
  • 原文地址:https://blog.csdn.net/jxandrew/article/details/134077262