NodeJS 作中間服務器

NodeJS 作中間服務器

實現先後端分離

node 天生適合處理業務層面的邏輯,可是不適合做爲數據計算處理。在作web項目的時候,使用 node 來作中間服務器,處理客戶端的頁面渲染,轉發請求到接口服務器,實現先後端的分離,後端再也不須要管理前端的業務渲染,專一於接口的抽象化和靈活化,前端再也不須要等待路由頁面的添加才能寫頁面,能夠本身往路由添加須要的頁面。html

共同管理

中間服務器的功能很是簡單,所以先後端能夠共同管理,前端負責添加頁面層級的路由,後端負責轉發到接口服務器的路由前端

淘寶基於 Node 的先後端分離


上圖是淘寶基於Node的先後端分離分層,以及Node的職責範圍。簡單解釋下:node

  • 最上端是服務端,就是咱們常說的後端。後端對於咱們來講,就是一個接口的集合,服務端提供各類各樣的接口供咱們使用。由於有Node層,也不用侷限是什麼形式的服務。對於後端開發來講,他們只用關心業務代碼的接口實現。web

  • 服務端下面是Node應用。express

  • Node應用中有一層Model Proxy與服務端進行通信。這一層主要目前是抹平咱們對不一樣接口的調用方式,封裝一些view層須要的Model。json

  • Node層還能輕鬆實現原來vmcommon,tms(引用淘寶內容管理系統)等需求。後端

  • Node層要使用什麼框架由開發者本身決定。不過推薦使用express+xTemplate的組合,xTemplate能作到先後端公用。api

  • 怎麼用Node你們本身決定,可是使人興奮的是,咱們終於可使用Node輕鬆實現咱們想要的輸出方式:JSON/JSONP/RESTful/HTML/BigPipe/Comet/Socket/同步、異步,想怎麼整就怎麼整,徹底根據你的場景決定。跨域

  • 瀏覽器層在咱們這個架構中沒有變化,也不但願由於引入Node改變你之前在瀏覽器中開發的認知。瀏覽器

  • 引入Node,只是把本該就前端控制的部分交由前端掌控。

node + express 構建中間服務器

server.js

var express = require('express')
var rMiddle = require('./router/rMiddle.js')

// remoteapi 開頭的轉發給接口服務器
app.use('/remoteapi', rMiddle);

// 前端頁面渲染
app.get('/index', function(req, res) {
    res.sendFile(__dirname + 'index.html');
})

app.listen('8080', function(err) {
    if (err) {
        return;
    }
    console.log('Listening at localhost:8080');
});

/router/rMiddle.js

var express = require('express');
var http = require('http');
var querystring = require('querystring');
var router = express.Router();

// 你的接口服務器地址
var connectServer = {
    port: '9000',
    protocol: 'http://',
    host: '192.168.0.1'
};
var serverDomainPort = connectServer.port;
var serverDomainProtocol = connectServer.protocol;
var serverDomainHost = connectServer.host
var serverDomain = serverDomainProtocol + serverDomainHost + ':' + serverDomainPort;

// 定義一個通用 Get 接口,轉接全部數據,再也不一個個寫
router.get('*', (req, res) => {
    let reqUrl = serverDomain + req.url;
    console.log('[GET Request]: ', reqUrl);
    http.get(reqUrl, (sres) => {
        var statusCode = sres.statusCode;
        var contentType = sres.headers['content-type'];

        let error;
        if (statusCode !== 200) {
            error = new Error(`Request Failed.\n` + `Status Code: ${statusCode}`);
        } else if (!/^application\/json/.test(contentType)) {
            error = new Error(`Invalid content-type.\n` + `Expected application/json but received ${contentType}`);
        }
        if (error) {
            // consume response data to free up memory
            sres.resume();
            res.status(500).end();
            return;
        }

        sres.setEncoding('utf8');
        let rawData = '';
        sres.on('data', (chunk) => rawData += chunk);
        sres.on('end', () => {
            try {
                let parsedData = JSON.parse(rawData);
                res.json(parsedData);

            } catch (e) {
                res.status(500).send(e.message);
            }
        });
    }).on('error', (e) => {
        console.log(`Got error: ${e.message}`);
        res.status(500).end();
    });  
})

// 定義一個通用的 Post 接口,轉接全部數據
router.post('*', (req, res) => {
    var reqUrl = serverDomain + req.url;
    var reqContentType = req.headers['content-type'];
    var reqBody = req.body;
    // 根據 請求的 content-type 判斷用哪一種格式化方式
    var reqData = reqContentType.indexOf('json') !== -1 ? JSON.stringify(reqBody) : querystring.stringify(reqBody);
    var postOpt = {
        host: serverDomainHost,
        port: serverDomainPort,
        path: req.url,
        method: 'POST',
        headers: {
            'Content-Type': reqContentType
        }
    };
    var sreq = http.request(postOpt, (sres) => {
        var body = '';
        var error;
        if (sres.statusCode !== 200) {
            error = new Error(`Request Failed.\n` + `Status Code: ${sres.statusCode}`)
        }
        if (error) {
            console.log(error.message);
            sres.resume();
            res.status(500).end();
            return;
        }
        sres.on('data', (data) => {
            body += data;
        })
        .on('end', () => {
            try {
                var parsedData = JSON.parse(body);
                res.json(parsedData);
            } catch (e) {
                console.log(e.message);
                res.status(500).send(e.message);
            }
        })
        .on('error', () => {
            console.log('[ERROR] when req url:', reqUrl, reqData);
            res.status(500).send('error');
        })
    })
    sreq.write(reqData);
    sreq.end();
})

module.exports = router;

透傳仍是不透傳

中間服務器在轉發請求的時候,若是隻是單純的請求數據,那麼透傳是很好的方法,可是若是想作進一步的優化,可能就須要中間層作必定的處理。

其餘資源

相關文章
相關標籤/搜索