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

Vue2+Echarts+koa2+websocket电商平台数据可视化实时检测系统(一)文中,介绍了前端项目搭建过程,本文将继续描述后端使用koa2搭建后台服务系统流程。 本文将带你体验使用javascript写后端应用。

思考几个问题

单线程的javascript怎么写后端服务

JavaScript 是一种单线程语言,这意味着它在执行代码时只能做一件事情。然而,这并不意味着 JavaScript 不能用于后端服务的开发。事实上,有很多 JavaScript 运行时环境,如 Node.js,可以让 JavaScript 用于后端服务的开发。

在 Node.js 中,JavaScript 可以用于开发高性能的后端服务,包括 Web 服务器、API 服务器、数据库服务器等等。在这种情况下,JavaScript 的单线程特性并不是一个问题,因为 Node.js 运行时环境本身是多线程的,并且可以使用多线程技术来处理并发请求。

在开发后端服务时的JavaScript 与前端 JavaScript 有一些区别,主要是以下几点:

  1. 运行环境: 前端 JavaScript 运行在浏览器中,而后端 JavaScript 运行在 Node.js 运行时环境中。
  2. API 支持: 前端 JavaScript 可以使用浏览器提供的 API,如 DOM API、BOM API、Web API 等等,而后端 JavaScript 可以使用 Node.js 提供的 API,如文件系统 API、网络 API、数据库 API 等等。
  3. 安全性: 由于前端 JavaScript 运行在浏览器中,因此它的安全性受到限制,而后端 JavaScript 运行在服务器端,因此它的安全性可以得到更好的控制。
  4. 框架和库: 在前端 JavaScript 中,有很多流行的框架和库,如 React、Angular、Vue.js 等等,而在后端 JavaScript 中,也有很多流行的框架和库,如 Express、Koa、Nest.js 等等。

总的来说,在开发后端服务时,JavaScript 与前端 JavaScript 有一些区别,但是它们的基本语法和语义是相同的,因此可以相互转换。

node.js是什么

Node.js 是一个开源的 JavaScript 运行时环境(也称为 JavaScript 引擎),它允许开发者使用 JavaScript 语言编写服务器端应用程序。Node.js 的核心是一个基于 Chrome V8 引擎的事件驱动、非阻塞 I/O 的平台,这使得它非常适合处理大量并发连接和 I/O 密集型任务,如实时通信、Web 服务器、API 服务等。

Node.js 为 JavaScript 做了以下几个关键贡献:

  1. 扩展性: 通过非阻塞 I/O 和事件驱动模型,Node.js 可以轻松处理高并发,使得 JavaScript 可以用于后端开发,而不仅仅是前端浏览器环境。
  2. 实时性: Node.js 适用于实时通信,如实时聊天应用、WebSocket 服务等,因为它的事件循环机制可以确保数据的即时发送和接收。
  3. 模块化: Node.js 提供了模块系统,使得代码易于组织和复用,开发者可以使用 npm(Node Package Manager)来管理和安装第三方模块。
  4. 生态系统丰富: Node.js 拥有丰富的第三方库支持,如 Express(Web 框架)、Socket.io(实时通信库)、Mongoose(MongoDB ORM)等,极大地扩展了其功能和适用范围。
  5. 易于学习: 对于熟悉 JavaScript 的开发者来说,学习 Node.js 的门槛相对较低,因为它使用的是同一种语言。

Node.js 使得 JavaScript 不再局限于浏览器环境,而是成为了一种多用途的编程语言,不仅可用于构建前端应用,也能用于构建高性能的后端服务

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

Koa是一个基于Node.js的Web框架,它提供了一组强大的工具和函数,帮助开发者构建高效、可靠的后端应用程序。Koa的设计理念是中间件(Middleware)的洋葱模型,通过一系列的中间件来处理HTTP请求和响应,使得开发者可以更灵活地控制应用程序的流程。

在Node.js中,Koa与Express等框架类似,都是用来简化和加速后端开发的工具。Koa相对于Express更加轻量级,它采用了ES6的语法特性,支持异步函数和Promise,使得编写异步代码更加简洁和优雅。

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')
    })
  })
}

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

三月的一天

你的鼓励将是我前进的动力。

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

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

打赏作者

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

抵扣说明:

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

余额充值