Vue2+Echarts+koa2+websocket电商平台数据可视化实时检测系统后端项目搭建(二)

 1. KOA2——基于 Node.js平台的Web服务器框架

 Express原班人马打造

Express  Koa , Koa2都是 Web服务器的框架,他们之间的差别和关系可以通过下面这个表格表示出

框架名

异步处

Express

web框架

调函数

Koa

web框架

Generator+yield

Koa2 

web框架

async/await

  环境依赖 Node   v7.6.0 及以上

由于 Koa2 它是支持 async await ,所以它对 Node 的版本是有要求的,它要求 Node 的版本至 少是在7.6级以上,为语法糖 asyncawait 是在 Node7.6 版本之后出现才支持

 1.1 洋葱模型的中间件

如下图所示, 对于服务器而言,它其就是来处理一个又一个的请求,   Web服务器接收由浏览器发 过来的一个又一请求之后,它形成一个又一个的响应返回给浏览器. 而请求到达我们的服务器是 需要经过程序处理,程序处理完之后才会形成响应,返回给浏览器,我们服务器处理请求的这一块程序,在 Koa2 的世界当中就把它称之为中间件

 这种中间件可能还不仅仅只有一个,可能会存在多个,比如上图所示, 它就存在三层中间件,这三 层中间件处理请求的过程以及它调用的顺序为:

  当一个请求到达咱们的服务器,最先最先处理这个请求的是第一层中间件

  第一层的中间件在处理这个请求之后,它会把这个请求给第二层的中间件

  第二层的中间件在处理这个请求之后,它会把这个请求给第三层的中间件

  第三中间件内部并没有中间件了, 所以第三层中间件在处理完所有的代码之后,这个请求又

到了第二层的中间件,所以第二层中间件对这个请求经过了两次的处处理

  第二的中间件在处理完这个请求之后,又到了第一层的中间件, 所以第一层的中间件也对这

个请求经过了两次的处

这个调用顺序就是洋葱模型, 中间件对请求的处理有一种先进后出的感觉,请求最先到达第一层中 间件,最后也是第一层中间件对请求再次处理了一下

2. KOA2 的快速上手

 检查 Node 的版本

node -v 的命令可以帮助我们检查 Node 的版本,  Koa2 的使用要求 Node版本在7.6及以上

 Koa2

这个命令可快速的创建出 package.json 的文件, 这个文件可以维护项目中第三方包的信息

npm install koa

这个命可以在线的联网下载最新版本 koa到当前项目中, 由于线上最新版本的 koa就是  koa2 , 所以我们并不需要执行 npm install koa2

写入口文件 app.js, 创建 Koa 的实例对象

// 1.创建koa对象

const Koa = require('koa') // 导入构造方法

const app = new Koa() // 通过构造方法 , 创建实例对象

  编写响应函数(中间件)

响应函是通过use的方式才能产生效果, 这个函数有两个参数, 一个是 ctx ,一个是 next   ctx :

上下文, 指的是请求所处于的Web容器,我们可以通过 ctx.request拿到请求对象, 以通过 ctx.response拿到响应对象

  next :内层中间件执行的入口

// 2.编写响应函数(中间件)

app.use((ctx, next) => {

console.log(ctx.request.url)

ctx.response.body = 'hello world'

})

明端口号:通过 app.listen就可以指明一个端口号

// 3.绑定端口号 3000

app.listen(3000)

 启动服务器:通 node app.js 就可以启动服务器了

随即打开浏览器, 在浏览器中输入 127.0.0.1:3000/ 你将会看到浏览器中出现  hello world  的字符 , 并且在服务器的终端中, 也能看到请求的 url

3. KOA2 中间件的特点

Koa2 实例对象通过 use方法加入一个中间件,一个间件就是一个函数,这个函数具备两个参数,分别是 ctx next。中间件的执行符合洋葱模型,内层中间件能否执行取决于外层中间件的 next 函数是调用。调用 next 函数得到的是 Promise对象, 如果想得到 Promise所包装的数据, 可以结合 awaitasync

app.use(async (ctx, next) => {

// 刚进入中间件想做的事情

await next()

// 内层所有中间件结束之后想做的事

})

4.台项目的开发

4.1.后台项目的目标

后台项目需要达到这以下几个目标:

1.算服务器处理请求的总耗时

计算出服务器对于这个请求它的所有中间件总耗时时长

2.在响应头上加上响应内容的 mime 类型

加入mime类型, 可以让浏览器更好的来处理由服务器返回的数据.

如果应给前端浏览器是 json 格式的数据,这时候就需要在咱们的响应头当中增加Type 它的值就是application/json 就是 json 数据类型的 mime 类型

3.根据URL读取指定目录下的文件内容

为了简化后服务器的代码,前端图表所要的数据, 并没有存在数据库当中,而是将存在文件当中的,这种操只是为了简化咱们后台的代码. 所以咱们是需要去读取某一个目录下面的文件内容 

每一个目就是一个中间件需要实现的功能, 所以后台项目中需要有三个中间件

4.2.台项目的开发步骤

创建一个新的文件夹, 叫做 koa_server , 这个文件夹就是后台项目的文件夹

   1.安装包

npm init -y

npm install koa

   2.创建文件和目录结构

 代码目录结构

 app.js是后台服务器的入口文件

data 目录是用来存放所有模块的 json文件数据

middleware用来存放所有的中间件代码

koa_response_data.js是业务逻辑中间件

koa_response_duration.js是计算服务器处理时长的中间件

koa_response_header.js是用来专门设置响应头的中间件

接着将各个模块的 json数据文件复制到 data 的目录之下, 接着在 app.js文件中写上代码如下:

// 服务器的入口文件

// 1.创建KOA的实例对

const Koa = require('koa')

const app = new Koa()

// 2.绑定中间

// 绑定第一层中间件

// 绑定第二层中间件

// 绑定第三层中间件

// 3.绑定端口号 8888

app.listen(8888)

   4.3.总耗时中间

   1.1层中间件

        总耗时中间件的功能就是计算出服务器所有中间件的总耗时,应该位于第一层,因为第一层 的中间件是先处理请求的中间件,同时也是最后处理请求的中间件。第一次进入咱们中间件的时候,就记录一个开始的时间。当其他所有中间件都执行完之后,再记录下结束时间以后将两者相减就得出总耗时。

    

   3.设置响应

        将计算出来的结果,设置到响应头的 X-Response-Time , 单位是毫秒 ms

具体代码如下:

app.js 

// 绑定第一层中间件

const respDurationMiddleware =

require('./middleware/koa_response_duration')

app.use(respDurationMiddleware)

koa_response_duration.js

// 计算服务器消耗时长的中间件

module.exports = async (ctx, next) => {

// 记录开始时间

const start = Date.now()

// 让内层中间件得到执行

await next()

// 记录结束的时间

const end = Date.now()

// 设置响应头 X-Response-Time

const duration = end - start

// ctx.set 设置响应

ctx.set('X-Response-Time', duration + 'ms')

}

   4.4 响应头中间

   1.2层中间件

        这个第2层中间件没有特定的要

       

   2.获取 mime类型

        由于响应给前端浏览器当中的数据都是 json 格式的字符串,所以 mime 类型可以统一

的给它写成 application/json , 当然这一块也是简化的处理了,因为 mime 类型有几十几百 种,,所以这里简化处理一下

   3.设置响应

        响应头的keyContent-Type ,它的值是 application/json , 顺便加上 charset=utf-8

告诉浏览器,我这部分响应的数据,它的类型是 application/json ,同时它的编码是具体代码如下:

app.js

// 绑定第二层中间件
const respHeaderMiddleware = require('./middleware/koa_response_header')
app.use(respHeaderMiddleware)

koa_response_header.js

 

// 设置响应头的中间件
module.exports = async (ctx, next) => {
  const contentType = 'application/json; charset=utf-8'
  ctx.set('Content-Type', contentType)
  await next()
}

   4.5 业务逻辑中间

   1.3层中间件

        第三层中间件处理实际的业务逻辑,处理前端请求并返回

        

   2.读取文件内容

          获取 URL 请求路径

const url = ctx.request.url

  根据URL请求路径,拼接出文件的绝对路

let filePath = url.replace('/api', '')

filePath = '../data' + filePath + '.json'

filePath = path.join(__dirname, filePath)

 filePath就是需要读取文件的绝对路径

  读取这个文件的内

使 fs模块中的 readFile方法进行实现

   3.设置响应

ctx.response.body

具体代码如下:

app.js

// 绑定第三层中间件

const respDataMiddleware = require('./middleware/koa_response_data') app.use(respDataMiddleware)

koa_response_data.js

// 处理业务逻辑的中间件 ,读取某个json文件的数据
const path = require('path')
const fileUtils = require('../utils/file_utils')
module.exports = async (ctx, next) => {
// 根据url
const url = ctx.request.url // /api/seller   ../data/seller.json
let filePath = url.replace('/api', '') //  /seller
filePath = '../data' + filePath + '.json'  // ../data/seller.json
filePath = path.join(__dirname, filePath)
try {
const ret = await fileUtils.getFileJsonData(filePath)
ctx.response.body = ret
} catch (error) {
const errorMsg = {
message: '读取文件内容失败 , 文件资源不存在', status: 404
}
ctx.response.body = JSON.stringify(errorMsg) }
console.log(filePath)
await next()
}

file_utils.js

// 读取文件的工具方法
const fs = require('fs')
module.exports.getFileJsonData = (filePath) => {
  // 根据文件的路径, 读取文件的内容
  return new Promise((resolve, reject) => {
    fs.readFile(filePath, 'utf-8', (error, data) => {
      if(error) {
        // 读取文件失败
        reject(error)
      } else {
        // 读取文件成功
        resolve(data)
      }
    })
  })
}

   4.6 允许跨域

  设置响应头koa_response_header.js 添加

// 设置响应头的中间件
module.exports = async (ctx, next) => {
  const contentType = 'application/json; charset=utf-8'
  ctx.set('Content-Type', contentType)
  ctx.set("Access-Control-Allow-Origin", "*")
  ctx.set("Access-Control-Allow-Methods", "OPTIONS, GET, PUT, POST, DELETE")
  await next()
}

5.引入webScoket

WebSocket以保持着浏览器和客户端之间的长连接,   通过 WebSocket可以实现数据由后端推送到前 端,保证了数据传输的实时性. WebSocket涉及到前端代码和后端代码的改

5.1. WebSocket 的使用

  安装 WebSocket

npm i ws -S

创建 service\web_socket_service.js 文件

 创建 WebSocket实例对象

const WebSocket = require('ws')
// 创建WebSocket服务端的对象, 绑定的端口号是9998
const wss = new WebSocket.Server({
  port: 9998
})
wss.on("connection", client => {
console.log("有客户端连接 ...")
client.on("message", msg => {
console.log("客户端发送数据过来了")
// 发送数据给客户端
client.send('hello socket')
})
})

监听事件

 app.js 中引入 web_socket_service.js 这个文件, 并调用 listen 方法

const webSocketService = require('./service/web_socket_service') 
webSocketService.listen()

5.2 约定客户端之间数据交互格式

客户端和服务端之间的数据交互采用 JSON 格式

  客户端发送数据给服务端的字段如下:

{
"action": "getData",
"socketType": "trendData",
"chartName": "trend",
"value": ""
}
或者
{
"action": "fullScreen",
"socketType": "fullScreen",
"chartName": "trend",
"value": true
}
或者
{
"action": "themeChange",
"socketType": "themeChange",
"chartName": "",
"value": "chalk"
}

 其:

  action : 代表某项行,可选值有

  •  getData 代表获取图表数据
  •  fullScreen 代表产生了全屏事件
  • themeChange 代表产生了主题切换的事件

socketType : 代表业务模块, 这个值代表前端注册数据回调函数的标识, 可选值有:

  • trendData
  • sellerData
  • mapData
  • rankData
  • hotData
  • stockData
  • fullScreen
  • themeChange 

  chartName : 代表图表名称, 如果是主题切换事件, 可不传此值, 可选值有:

  • trend 
  • seller
  • rank
  • stock

  value : 代表 具体的数据值, 在获取图表数据时, 可不传此值, 可选值有

  如果是全屏事件,  true代表全屏,  false代表非全屏

  如果是主题切换事件, 可选值有 chalk或者 vintage

  服务端发送给客户端的数据如下:

{
"action": "getData",
"socketType": "trendData",
"chartName": "trend",
"value": "",
"data": "从文件读取出来的json文件的内容"
}
或者
{
"action": "fullScreen",
"socketType": "fullScreen",
"chartName": "trend",
"value": true
}
或者
{
"action": "themeChange",    "socketType": "themeChange", "chartName": "",
"value": "chalk"
}

注意, 除了 action getData , 服务器会在客户端发过来数据的基础之上, 增加 data字段, 其他的情况, 服务器会原封不动的将从某一个客户端发过来的数据转发给每一个处于连接状态 客户端

  5.3.代码实现 service\web_socket_service.js 

const path = require('path')
const fileUtils = require('../utils/file_utils')
const WebSocket = require('ws')
// 创建WebSocket服务端的对象, 绑定的端口号是9998
const wss = new WebSocket.Server({
  port: 9998
})
// 服务端开启了监听
module.exports.listen = () => {
  // 对客户端的连接事件进行监听
  // client:代表的是客户端的连接socket对象
  wss.on('connection', client => {
    console.log('有客户端连接成功了...')
    // 对客户端的连接对象进行message事件的监听
    // msg: 由客户端发给服务端的数据
    client.on('message',async msg => {
      console.log('客户端发送数据给服务端了: ' + msg)
      let payload = JSON.parse(msg)
      const action = payload.action
      if (action === 'getData') {
        let filePath = '../data/' + payload.chartName + '.json'
        // payload.chartName // trend seller map rank hot stock
        filePath = path.join(__dirname, filePath)
        const ret = await fileUtils.getFileJsonData(filePath)
        // 需要在服务端获取到数据的基础之上, 增加一个data的字段
        // data所对应的值,就是某个json文件的内容
        payload.data = ret
        client.send(JSON.stringify(payload))
        console.log(payload.data)
      } else {
        // 原封不动的将所接收到的数据转发给每一个处于连接状态的客户端
        // wss.clients // 所有客户端的连接
        wss.clients.forEach(client => {
          console.log("客户端触发"+action+"事件")
          // client.send(msg)
          payload.data = JSON.parse(msg)
          client.send(JSON.stringify(payload) )
        })
      }
      // 由服务端往客户端发送数据
      // client.send('hello socket from backend')
    })
  })
}

猜你喜欢

转载自blog.csdn.net/qq_36384657/article/details/129311071
今日推荐