Vue2+Echarts+koa2+websocket ECプラットフォーム データ可視化 リアルタイム検知システム バックエンドプロジェクト構築(2)

 1. KOA2——  Node .jsプラットフォームに基づくWebサーバー フレームワーク

 Expressのオリジナル チームによって構築されました

Express   Koa  Koa 2は両方ともWebサーバー用のフレームワークであり それらの間の違いと関係は次の表で表すことができます。

フレームワーク名

効果

非同期処理

特急_

ウェブフレームワーク

コールバック関数

それで

ウェブフレームワーク

発電機+発電量

コア2_ 

ウェブフレームワーク

非同期/待機

  環境は Node    v7.6.0以降に依存します

 Koa2 はasyncawaitをサポートしている ためNodeのバージョンに関する要件があります。糖衣構文asyncawaitNode 7.6以降でのみサポートされるためNodeバージョンが少なくともレベル7.6である必要があります。          

 1.1オニオンモデルのミドルウェア

下図に示すようにサーバーは実際にはリクエストを次々と処理することになっており、    Webサーバーはブラウザから 送信されたリクエストを次々と受信すると、次々とレスポンスを作成してブラウザに返しますサーバーに到達するには プログラム が処理する必要があります。プログラムが処理された後、応答が形成されてブラウザに返されます。サーバーがリクエストを処理するプログラムは、 Koa 2の世界ではミドルウェアと呼ばれます。

 この種のミドルウェアは1 つだけではなく、複数存在する場合があります。たとえば、上図のように3層のミドルウェアがあり、リクエスト処理プロセスと呼び出しの順序は次のとおりです 

  リクエストがサーバーに到着すると、ミドルウェアの最初の層が最初にリクエストを処理します。

  第 1 層ミドルウェアがリクエストを処理した後、リクエストを第 2 層ミドルウェアに渡します。

  第 2 層ミドルウェアがリクエストを処理した後、リクエストを第 3 層ミドルウェアに送信します。

  第 3層ミドルウェア内にはミドルウェアがないため第 3 層ミドルウェアがすべてのコードを処理した後、リクエストは返されます。

これは第 2 レベルのミドルウェアに到達するため、第 2 レベルのミドルウェアはこのリクエストを 2 回処理します。

  リクエストを処理した後、第 2層のミドルウェアが第 1 層のミドルウェアに到達するため第 1 層のミドルウェアもこのリクエストに応答します。

リクエストが 2 回処理され

この呼び出しシーケンスはオニオン モデルですミドルウェアはリクエストの処理において先入れ後出しの感覚を持ちます。リクエストは最初に第 1 レベルの ミドルウェア最後に第 1 レベルのミドルウェアがリクエストを再度処理します。

2.  KOA2のクイックスタート

  ノード のバージョンを確認する

node  -vコマンドは、 Nodeのバージョンを確認するのに役立ちます。Koa 2を使用するには、  Nodeバージョン7.6以降が必要です。    

Koa 2をインストールする 

このコマンドプロジェクト内のサードパーティ パッケージの情報を維持できるpackage.jsonファイルを迅速に作成できます。  

npm インストール koa

このコマンドはkoa onlineの最新バージョンを 現在のプロジェクトにダウンロードできます。koa onlineの最新バージョンはkoa 2であるためnpm install koa 2を実行する必要はありません。       

エントリー ファイルapp.jsを記述しKoaのインスタンスオブジェクト作成します。  

//  1. koaオブジェクトを作成する_

const  Koa  = require ( ' koa ' ) //コンストラクターをインポートします

const  app  =  new  Koa () //構築メソッドを通じてインスタンス オブジェクトを作成します

  応答関数を作成する(ミドルウェア)

応答関数は使用することによってのみ有効になりますこの関数には 2 つのパラメータがあり1 つはctxで、もう 1 つはnext  ctxです     

コンテキストは、リクエスト配置されているWebコンテナを指します。ctx.requestを通じてリクエスト オブジェクトを取得するctx.responseを通じて応答オブジェクト取得できます。  

  next :内部

// 2.応答関数を書く(ミドルウェア)

アプリuse (( ctx , next ) => {

コンソールログ( ctx .リクエスト.url ) _

ctx 応答body  = ' hello  world '

} )

ポート番号 を指定するapp.listenを通じてポート番号を指定できます。

// 3.ポート番号300 0をバインドします

アプリ聞いてください( 3000 )

 サーバーの起動:ノードapp .jsを通じて サーバーを起動できます  

次に、ブラウザを開きブラウザに「127.0.0.1:3000/」入力します。 ブラウザにhello world文字列が表示され、サーバーのターミナルに要求されたURLも表示されます。       

3.  KOA2ミドルウェアの特徴

Koa 2のインスタンス オブジェクトはuseメソッドを通じて ミドルウェアを追加します。ミドルウェアは関数でありこの関数にはctxnextという 2 つのパラメータがあります。ミドルウェアの実行はオニオンモデルに準拠しており、内側のミドルウェアが実行できるかどうかは、外側のミドルウェアの次の関数が呼び出されるかどうかによって決まります。次の関数を呼び出してPromiseオブジェクトを取得しますPromiseでラップされたデータを取得したい場合はawaitasyncを組み合わせます。         

アプリuse ( async  ( ctx , next ) => {

// ミドルウェアが実行したいことを入力するだけです

 を待ってください()

//内層のミドルウェアがすべて終了した後にやりたいこと

} )

4.バックグラウンドプロジェクトの開発

4.1.バックグラウンドプロジェクトの目標

バックグラウンド プロジェクトでは、次の目標を達成する必要があります

1.サーバーがリクエストを処理するのに費やした合計時間を計算します

このリクエストに対してサーバーがすべてのミドルウェアで費やした合計時間を計算します。

2.応答コンテンツMIMEタイプを応答ヘッダーに追加します

MIMEタイプを追加すると、ブラウザはサーバーから返されたデータをより適切に処理できるようになります

フロントエンド ブラウザへの応答がjson形式のデータである場合、応答ヘッダーに Type を追加する必要があります。その値はapplication/jsonで、これはjsonデータ型のMIMEタイプです。  

3. URLに従って、指定されたディレクトリ内のファイルの内容を読み取ります。

バックグラウンド サーバーのコードを簡素化するために、フロントエンド チャートに必要なデータはデータベースに存在しませんがファイルに保存されます。この操作はバックグラウンド コードを簡素化するためだけですしたがって、以下を読み取る必要があります。あるディレクトリ以下のファイル 

目標はミドルウェアが実装する必要がある機能であるためバックグラウンド プロジェクトには 3 つのミドルウェアが必要です

4.2.バックグラウンドプロジェクトの開発手順

koa_serverという名前の新しいフォルダーこれはバックグラウンドプロジェクトのフォルダーです。  

   1.インストールパッケージ

npm  init  - y

npm インストール koa

   2.ファイルとディレクトリ構造を作成します。

 コードのディレクトリ構造

 app.jsはバックグラウンドサーバーのエントリファイルです

データディレクトリは、すべてのモジュールの jsonファイル データを保存するために使用されます。 

ミドルウェアはすべてのミドルウェア コードを保存するために使用されます

koa_response_data.jsはビジネスロジックミドルウェアです_ _ _

koa_response_duration.jsはサーバーの処理時間を計算するためのミドルウェアです

koa_response_header.js は、レスポンスヘッダーを具体的に設定するために使用されるミドルウェアです

次に、 各モジュールのjsonデータ ファイルをデータディレクトリにコピーし次のようにapp.jsファイルにコードを記述します   

//サーバーエントリーファイル

// 1. KOAのインスタンスオブジェクトを作成する

const  Koa  = require ( ' koa ' )

const  app  =新しい Koa ( )

// 2.ミドルウェアをバインドする

//最初の層のミドルウェアをバインドします

//第 2 層のミドルウェアをバインドする

//第三層ミドルウェアをバインドする

// 3.ポート番号888 8をバインドします

アプリ聞いてください( 8888 )

   4.3.合計時間のかかるミドルウェア

   1.レイヤー1ミドルウェア

        合計時間消費ミドルウェアの機能は、サーバー上のすべてのミドルウェアの合計時間を計算することです。最初の層のミドルウェアは最初にリクエストを処理するミドルウェアであるため、最初の層に配置する必要があり ますリクエストを最後に処理するミドルウェアでもあります。初めてミドルウェアに入るときは、開始時間を記録します。他のすべてのミドルウェアが実行された後、終了時間を記録し、その 2 つを減算して合計時間を取得します。

    

   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