java 计时实时更新 页面后台_基于Websocket实现实时更新页面(前后端及部署)

  • Post author:
  • Post category:java


项目做了两个多月,总想写点东西做个总结,但一直没时间,再加上自己比较懒,就耽搁了。现在这个项目已经进入测试阶段,暂时有个喘息的机会了……

我主要负责的是web端,然后写前端页面和后端接口,顺带后来负责websocket与同事写的服务端做通信,再往后部署上线也掺了一腿…..,所以对于自己负责的这部分还是比较了解的。web方面琐碎的小知识点比较多,就不一一介绍了,后面写到哪里想到了就单独抽出来,否则我写10篇也写不完。废话不多说,开始今天的websocket之旅!


1.什么是websocket



WebSocket

是一种在单个TCP连接上进行全双工通信的协议。这句话是百度百科给的定义,我的理解ws就是一种类似http的通信协议,只不过这种通信协议使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。这样在WebSocket API中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。因为目前学识浅陋,所以感觉很多需要实时推送的数据(比如hupu篮球文字直播,各个评论实时刷新还有我这里用的金融行情资讯,股票行情等)都可以用ws来做吧?后面随着学习的深入可能会打脸,所以这里保留个问号,哈哈。




2.为什么用websocket



既然提到为什么用,肯定接下来要说的就是使用ws的优点了,至于有哪些优点,自行百度去吧,因为我也不能全都讲出来
5fd6f9b4a875f0c5d9c3df4209920d04.png
5fd6f9b4a875f0c5d9c3df4209920d04.png
5fd6f9b4a875f0c5d9c3df4209920d04.png
5fd6f9b4a875f0c5d9c3df4209920d04.png
5fd6f9b4a875f0c5d9c3df4209920d04.png
。我只知道,当初他们有在讨论使用轮询,我也做了一个小demo。

轮询就是隔一段时间(比如1s)前端发一个请求给后台,然后后台把数据推到前台展示,这个可以用ajax做,但是有很多缺点:发送的http请求,其中真正有效的数据可能只是很小的一部分,显然这样会浪费很多的带宽等资源;推送不够及时;过度消耗服务端资源,因为服务端还有其他更重要的事情,单独开一个接口,有点多余。

而websocket则有以下几个优点:

编辑

  • 较少的控制开销。在连接创建后,服务器和客户端之间交换数据时,用于协议控制的数据包头部相对较小。在不包含扩展的情况下,对于服务器到客户端的内容,此头部大小只有2至10字节(和数据包长度有关);对于客户端到服务器的内容,此头部还需要加上额外的4字节的掩码。相对于HTTP请求每次都要携带完整的头部,此项开销显著减少了。

  • 更强的实时性。由于协议是全双工的,所以服务器可以随时主动给客户端下发数据。相对于HTTP请求需要等待客户端发起请求服务端才能响应,延迟明显更少;即使是和Comet等类似的长轮询比较,其也能在短时间内更多次地传递数据。

  • 保持连接状态。与HTTP不同的是,Websocket需要先创建连接,这就使得其成为一种有状态的协议,之后通信时可以省略部分状态信息。而HTTP请求可能需要在每个请求都携带状态信息(如身份认证等)。

  • 更好的二进制支持。Websocket定义了二进制帧,相对HTTP,可以更轻松地处理二进制内容。

  • 可以支持扩展。Websocket定义了扩展,用户可以扩展协议、实现部分自定义的子协议。如部分浏览器支持压缩等。

  • 更好的压缩效果。相对于HTTP压缩,Websocket在适当的扩展支持下,可以沿用之前内容的上下文,在传递类似的数据时,可以显著地提高压缩率。

ws的握手协议:

  • WebSocket 是独立的、创建在 TCP 上的协议。

  • Websocket 通过HTTP/1.1 协议的101状态码进行握手。

  • 为了创建Websocket连接,需要通过浏览器发出请求,之后服务器进行回应,这个过程通常称为“握手”(handshaking)


以上内容部分借鉴

百度百科

https://baike.baidu.com/item/WebSocket/1953845?fr=aladdin




3.怎么用websocket



具体哪些使用方法,可以直接看文档,如果不知道去哪个网站,可以直接去刘某的博客
5fd6f9b4a875f0c5d9c3df4209920d04.png
https://www.cnblogs.com/michealjy/p/11908694.html
a09ee4c4b5e4621dbed85d016c0158bb.png
,然后选择(websocket入门)。下面不说废话了,直接开始讲项目代码。



根据架构设计,前端监控页面(ws的客户端)与后台(py文件,ws服务端)通信,用于传送数据,而ws服务端一方面通过userapi与tkernel通信(C++写,用于从交易所拿数据推到web端;后者web有页面操作,传数据给userapi);另一方面与strategy engine(python写)通信,用于控制策略启停。所以ws扮演一个枢纽的作用,而且用到了多线程,所以这块花了很多时间来搞。


先看前端页面,也就是客户端:(需要引入websocket.js)

9f16eceff54c73f79d89dc93d05b9259.png



这里定义了一个ws客户端,写法是固定的,通过ip+端口与服务端进行通信。On开头的几个方法是ws类下的,可以直接用ws调用,其中有几个是js配置文件中初始化就有的,包括OnFrontConnected/OnFrontDisconnected等,只要去js文件里看就可以了。其他的是我仿照他的格式自定义的方法,用于业务交互,完全照着写,然后测试就可以了。下图是js文件

01bfe0e106e3707f1e0b6901e1857f3e.png



这个可能不是原生的js文件,应该是公司的大佬(公司有一个四五十岁的骨灰级程序员,一般不到公司上班,特殊情况才会出马,我也想未来这样
5fd6f9b4a875f0c5d9c3df4209920d04.png
)扩展后的,其实思路就是,仿照原生的定义大方法(或者称之为类),大方法里面先写配置(类的init),然后写类的方法,这样生成一个客户端对象,就可以使用其中的方法了。如下图:

8c2c02322d3d96de321c670e9ef94031.png

function的具体逻辑可以在这写,也可以在调用的地方写,一般谁调用谁写,其实就是面向对象里的多态,注意定义完要记得加入配置中,如下图

335317c4447ac3cadb019212ed85f829.png

至此,前端(客户端)部分基本上搞定,其他的用法需要的时候去查文档。



后端(服务端)是一个python写的脚本文件,只不过要引入userapi以及策略相关的文件(本质就是导包,调用方法,然后传数据,启停进程,此外还有读写数据库)。


需要起两个进程,一个进程(api)将userapi拿到的数据放到队列里,然后另一个进程(socket)负责从队列中取数据;然后再开一个多线程,从这个进程中拿到数据资源传给客户端。

28330cdda5429915d3a4b3c308ca6840.png

5fad7a00a67f9d23138092995fb94cd2.png


首先看一下ws与userapi通信的类(是一个搞C++的同事写的,本科竟然跟我一个专业,世界可真小,哈哈)

d6451db74200f43a687618c1d616e78b.png


继承的类就是一个C++打包的文件,这些On开头的方法,可以直接调用,不过有些这里没有用到。api会把数据传到Queue(用python多进程 (multiprocessing)创建)中,在socket这个类中写一个方法,可以对数据进行清洗,格式化成前端需要,如下图:

a547c674d1d80cc46df8739549ed25dc.png


这里注意最后一句话,需要给所有的客户端发数据。不然会报错:BrokenPipeError: [Errno 32] Broken pipe


解决这个问题前,要记住,前端每刷新一次,就相当于一个客户端,这样如果在同一个页面刷新,那么前一个客户端已经断开了,但是服务端还没来得及和他说再见(发消息了,但是人跑了),就换人了,所以我觉得就会报这个错。


到这,核心思路和代码基本上就结束了,当然踩了很多坑。ws服务端还要一部分是与数据库进行交互的,即写入操作日志,这里没有django,可以定义一个Mysql类,用sqlalchemy这个包,具体使用方法,自行百度。


下面再说说部署上线,这个地方也踩了很多坑,主要记住下面几点:

1.客户端使用的ip和端口,一定是公网ip+端口。

2.服务端使用的是私有ip+端口(window:ipconfig.   linux:ifconfig。查看)