一、說明
????????構(gòu)建一個(gè)微服務(wù)的電影網(wǎng)站,需要Docker、NodeJS、MongoDB,這樣的案例您見過嗎?如果對(duì)此有興趣,您就繼續(xù)往下看吧。
圖片取自網(wǎng)絡(luò) — 封面由我制作
這是??“構(gòu)建 NodeJS 影院微服務(wù)”系列的第二篇文章。
二、對(duì)第一部分的快速回顧
- 我們討論什么是微服務(wù)。
- 我們看到了微服務(wù)的優(yōu)點(diǎn)和缺點(diǎn)。
- 我們定義了一個(gè)影院微服務(wù)架構(gòu)。
- 我們使用?RAML?設(shè)計(jì)我們的電影服務(wù) API?規(guī)范。
- 我們使用?NodeJS 和 ExpressJS?開發(fā)我們的電影服務(wù) API。
- 我們對(duì) API 進(jìn)行了單元測(cè)試。
- 我們編寫我們的 API 以使其成為服務(wù),并將我們的電影服務(wù)運(yùn)行到?Docker?容器中。
- 我們對(duì)在?Docker?上運(yùn)行的電影服務(wù)進(jìn)行了集成測(cè)試。
如果你還沒有讀過第一章,我會(huì)把鏈接放在下面,所以你可以看看??。
構(gòu)建一個(gè) NodeJS 影院微服務(wù)并使用 docker 部署它 — 第 1 部分
這是“構(gòu)建 NodeJS 影院微服務(wù)”系列的第一章,這個(gè)系列是關(guān)于構(gòu)建 NodeJS...
在本文中,我們將繼續(xù)構(gòu)建我們的電影院微服務(wù),這次我們將開發(fā)電影院目錄服務(wù),以完成下圖。
我們將在本文中使用的是:
- NodeJS 版本 7.2.0
- MongoDB 3.4.1
- Docker for Mac 1.13
跟進(jìn)文章的先決條件:
- 已完成上一章中的示例。
如果您還沒有,我已經(jīng)在以下 github 存儲(chǔ)庫上傳了存儲(chǔ)庫,因此您可以在分支步驟 1?處獲得最新的存儲(chǔ)庫鏈接。
三、微服務(wù)安全和? HTTP/2?
????????在第一章中,我們做了一個(gè)簡(jiǎn)單的微服務(wù)來實(shí)現(xiàn)HTTP/1.1協(xié)議。HTTP/2 是?15 年來對(duì) HTTP 協(xié)議的首次重大升級(jí),經(jīng)過高度優(yōu)化,性能更好。HTTP/2是新的Web標(biāo)準(zhǔn),最初是Google的SPDY協(xié)議。它已經(jīng)被許多流行的網(wǎng)站使用,并被大多數(shù)主流瀏覽器支持。
HTTP/2 只有一些規(guī)則必須滿足才能實(shí)現(xiàn)它。
- 它僅適用于HTTPS協(xié)議(我們需要有效的SSL證書)。
- 它是向后兼容的。如果運(yùn)行應(yīng)用程序的瀏覽器或設(shè)備不支持?HTTP/2,它將回退到 HTTP1.1。
- 它具有開箱即用的巨大性能改進(jìn)。
- 它不需要您在客戶端執(zhí)行任何操作,只需在服務(wù)器端執(zhí)行基本實(shí)現(xiàn)即可。
- 一些新的有趣的功能將加快Web項(xiàng)目的加載時(shí)間,其方式甚至是HTTP1.1實(shí)現(xiàn)無法想象的。
四、為微服務(wù)啟用網(wǎng)絡(luò)架構(gòu)的 HTTP/2
這意味著我們需要在客戶端和服務(wù)器之間啟用單個(gè)連接,然后在“網(wǎng)絡(luò)”中利用 Y 軸分片等功能(更多地談?wù)撓盗兄械目s放立方體)來保持 HTTP/2 對(duì)客戶端的性能優(yōu)勢(shì),同時(shí)實(shí)現(xiàn)微服務(wù)架構(gòu)的所有操作和開發(fā)優(yōu)勢(shì)。
????????那么我們?yōu)槭裁匆獙?shí)施新的HTTP / 2協(xié)議,這是因?yàn)樽鳛閮?yōu)秀的開發(fā)人員,我們必須盡可能保護(hù)我們的應(yīng)用程序,基礎(chǔ)架構(gòu),通信,以防止惡意攻擊,也因?yàn)樽鳛閮?yōu)秀的開發(fā)人員,我們遵循我們認(rèn)為對(duì)我們有益的最佳實(shí)踐,就像這個(gè)。
????????微服務(wù)的一些安全最佳實(shí)踐如下所示:
安全性顯然將在采用和部署微服務(wù)應(yīng)用程序以供生產(chǎn)使用的決定中發(fā)揮重要作用。根據(jù)2016 Research發(fā)布的研究報(bào)告《451年軟件定義的基礎(chǔ)設(shè)施展望》,近45%的企業(yè)已經(jīng)實(shí)施或計(jì)劃在未來12個(gè)月內(nèi)推出基于容器的應(yīng)用程序。隨著 DevOps 實(shí)踐在企業(yè)中站穩(wěn)腳跟,容器應(yīng)用程序變得越來越普遍,安全管理員需要用保護(hù)應(yīng)用程序的專業(yè)知識(shí)武裝自己?!?@Ranga 拉賈戈帕蘭
- 發(fā)現(xiàn)和監(jiān)視服務(wù)間通信
- 對(duì)應(yīng)用程序和服務(wù)進(jìn)行分段和隔離
- 加密傳輸中的數(shù)據(jù)和靜態(tài)數(shù)據(jù)
????????我們要做的是加密我們的微服務(wù)通信以滿足合規(guī)性要求并提高安全性,尤其是當(dāng)流量通過公共網(wǎng)絡(luò)時(shí),這就是我們要實(shí)施HTTP / 2的原因之一,以獲得更好的性能和安全性改進(jìn)。
五、在微服務(wù)中實(shí)現(xiàn) HTTP/2
????????首先,讓我們更新上一章的電影服務(wù)并實(shí)現(xiàn)HTTP?/ 2協(xié)議,但是在我們打算在config文件夾中創(chuàng)建一個(gè)ssl文件夾之后。
movies-service/config:/ $ mkdir ssl
movies-service/config:/ $ cd ssl
????????現(xiàn)在,一旦進(jìn)入?ssl?文件夾,讓我們創(chuàng)建一個(gè)自簽名 SSL 證書,以開始在我們的服務(wù)中實(shí)現(xiàn)?HTTP/2?協(xié)議。
# Let's generate the server pass key
ssl/: $ openssl genrsa -des3 -passout pass:x -out server.pass.key 2048
# now generate the server key from the pass key
ssl/: $ openssl rsa -passin pass:x -in server.pass.key -out server.key
# we remove the pass key
ssl/: $ rm server.pass.key
# now let's create the .csr file
ssl/: $ openssl req -new -key server.key -out server.csr
...
Country Name (2 letter code) [AU]:MX
State or Province Name (full name) [Some-State]:Michoacan
...
A challenge password []:
...
# now let's create the .crt file
ssl/: $ openssl x509 -req -sha256 -days 365 -in server.csr -signkey server.key -out server.crt
????????接下來我們需要使用以下命令安裝?SPDY:
cinema-catalog-service/: $ npm i -S spdy --silent
????????首先讓我們?cè)谖募A中創(chuàng)建一個(gè)文件,使用以下代碼,這里是我們加載密鑰和證書文件的地方,這可能是我們可以使用的少數(shù)情況之一:index.js
ssl/
fs.readFileSync()
const fs = require('fs')
module.exports = {
key: fs.readFileSync(`${__dirname}/server.key`),
cert: fs.readFileSync(`${__dirname}/server.crt`)
}
然后我們需要修改幾個(gè)文件,讓我們先修改:config.js
const dbSettings = { ... }
// The first modification is adding the ssl certificates to the
// serverSettings
const serverSettings = {
port: process.env.PORT || 3000,
ssl: require('./ssl')
}
module.exports = Object.assign({}, { dbSettings, serverSettings })
接下來,讓我們修改文件,如下所示:server.js
...
const spdy = require('spdy')
const api = require('../api/movies')
const start = (options) => {
...
const app = express()
app.use(morgan('dev'))
app.use(helmet())
app.use((err, req, res, next) => {
reject(new Error('Something went wrong!, err:' + err))
res.status(500).send('Something went wrong!')
})
api(app, options)
// here is where we made the modifications, we create a spdy
// server, then we pass the ssl certs, and the express app
const server = spdy.createServer(options.ssl, app)
.listen(options.port, () => resolve(server))
})
}
module.exports = Object.assign({}, {start})
最后讓我們修改主文件:index.js
'use strict'
const {EventEmitter} = require('events')
const server = require('./server/server')
const repository = require('./repository/repository')
const config = require('./config/')
const mediator = new EventEmitter()
...
mediator.on('db.ready', (db) => {
let rep
repository.connect(db)
.then(repo => {
console.log('Connected. Starting Server')
rep = repo
return server.start({
port: config.serverSettings.port,
// here we pass the ssl options to the server.js file
ssl: config.serverSettings.ssl,
repo
})
})
.then(app => { ... })
})
...
現(xiàn)在我們需要使用以下命令重建我們的 docker 鏡像:
$ docker build -t movies-service .
并使用以下參數(shù)運(yùn)行我們的 docker 映像:movies-service
$ docker run --name movies-service -p 443:3000 -d movies-service
最后,我們使用Chrome瀏覽器對(duì)其進(jìn)行了測(cè)試,我們可以證實(shí)我們的HTTP / 2協(xié)議完全有效。
Chrome 開發(fā)工具
我們還可以證實(shí)使用wireshark進(jìn)行一些網(wǎng)絡(luò)捕獲,我們可以看到SSL確實(shí)有效。
線鯊幀捕獲
5.1 將 JWT 實(shí)現(xiàn)到微服務(wù)
加密和保護(hù)微服務(wù)通信的另一種方法是使用該協(xié)議,但我們將在后面的系列??中看到此實(shí)現(xiàn)。json web token
5.2 構(gòu)建微服務(wù)
好的,現(xiàn)在我們知道了如何實(shí)現(xiàn)?HTTP/2?協(xié)議,讓我們繼續(xù)構(gòu)建電影目錄服務(wù)。?我們將使用與電影服務(wù)相同的項(xiàng)目結(jié)構(gòu),因此 少說話??多編碼 ???? ?????? ?? .
在我們開始設(shè)計(jì) API 之前,這次我們需要為我們的數(shù)據(jù)庫設(shè)計(jì)我們的?Mongo 模式,因?yàn)槲覀儗⑹褂靡韵聝?nèi)容:
- 地點(diǎn)(國家、州和城市)
- 電影院(電影院、時(shí)間表、電影)
5.3? 模型數(shù)據(jù)設(shè)計(jì)
????????本文主要專注于創(chuàng)建微服務(wù),因此我不會(huì)花費(fèi)大量時(shí)間為我們的電影院數(shù)據(jù)庫進(jìn)行“模型數(shù)據(jù)設(shè)計(jì)”,而是將重點(diǎn)介紹這些領(lǐng)域和要點(diǎn)。
# posible collections for the cinemas db.
# For locations
- countries
- states
- cities
# For cinemas
- cinemas
- cinemaRooms
- schedules
????????因此,對(duì)于我們的位置,一個(gè)國家/地區(qū)具有許多州,而州具有一個(gè)國家/地區(qū),因此第一種關(guān)系是一對(duì)多關(guān)系,但這也適用于,一個(gè)州有許多城市,一個(gè)城市具有一個(gè)州,因此讓我們看看我們的關(guān)系示例如何。
國家 — 國家關(guān)系
????????州/城市關(guān)系
????????但是這種關(guān)系也是可能的,一個(gè)城市有很多電影院一個(gè)電影院屬于一個(gè)城市,另一個(gè)關(guān)系我們可以看到就是一個(gè)電影院房間有很多檔期,一個(gè)檔期屬于一個(gè)電影院房間,所以讓我們看看這種關(guān)系是怎樣的。
????????如果電影院陣列或時(shí)間表陣列的增長受到限制,則上圖中的這種引用可能很有用。假設(shè)一個(gè)電影室每天最多有 5 個(gè)時(shí)間表,所以在這里我們可以將時(shí)間表文檔嵌入到電影院文檔中。
嵌入式數(shù)據(jù)模型允許應(yīng)用程序在同一數(shù)據(jù)庫記錄中存儲(chǔ)相關(guān)信息。因此,應(yīng)用程序可能需要發(fā)出較少的查詢和更新來完成常見操作?!?MongoDB Docs
因此,這是我們數(shù)據(jù)庫模式設(shè)計(jì)的最終結(jié)果。
5.4 將數(shù)據(jù)導(dǎo)入我們的數(shù)據(jù)庫
????????我已經(jīng)準(zhǔn)備了一些數(shù)據(jù)示例,其中包含上面看到的模式設(shè)計(jì),這些文件位于 github 存儲(chǔ)庫中,有 4 個(gè) json 文件,因此您可以將其導(dǎo)入電影院數(shù)據(jù)庫,但首先我們需要知道哪個(gè)數(shù)據(jù)庫服務(wù)器是主要的,因此找出并執(zhí)行以下命令:cinema-catalog-service/src/mock
# first we need to copy the files one by one or we can zip it and pass the zip file
$ docker cp countries.json mongoNodeContainer:/tmp
$ docker cp state.json mongoNodeContainer:/tmp
$ docker cp city.json mongoNodeContainer:/tmp
$ docker cp cinemas.json mongoNodeContainer:/tmp
執(zhí)行上述命令后,讓我們將其導(dǎo)入數(shù)據(jù)庫,如下所示:
$ docker exec mongoNode{number} bash -c 'mongoimport --db cinemas --collection countries --file /tmp/countries.json --jsonArray -u $MONGO_USER_ADMIN -p $MONGO_PASS_ADMIN --authenticationDatabase "admin"'
$ docker exec mongoNode{number} bash -c 'mongoimport --db cinemas --collection states --file /tmp/states.json --jsonArray -u $MONGO_USER_ADMIN -p $MONGO_PASS_ADMIN --authenticationDatabase "admin"'
$ docker exec mongoNode{number} bash -c 'mongoimport --db cinemas --collection cities --file /tmp/cities.json --jsonArray -u $MONGO_USER_ADMIN -p $MONGO_PASS_ADMIN --authenticationDatabase "admin"'
$ docker exec mongoNode{number} bash -c 'mongoimport --db cinemas --collection cinemas --file /tmp/cinemas.json --jsonArray -u $MONGO_USER_ADMIN -p $MONGO_PASS_ADMIN --authenticationDatabase "admin"'
????????現(xiàn)在我們已經(jīng)準(zhǔn)備好了數(shù)據(jù)庫模式設(shè)計(jì),數(shù)據(jù)也準(zhǔn)備好了,可以查詢了,所以我們現(xiàn)在可以為電影目錄服務(wù)設(shè)計(jì)我們的 API,定義路由的一種方法是制作一些句子,如下所示:
- 我們需要一個(gè)城市來展示可用的電影院。
- 我們需要電影院來展示電影首映式。
- 我們需要電影首映并顯示時(shí)間表。
- 我們需要時(shí)間表,以查看是否有可供預(yù)訂的座位。
????????讓我們假設(shè) Cinépolis IT 部門的其他團(tuán)隊(duì)正在執(zhí)行其他 CRUD 操作,我們的任務(wù)是使“R”成為讀取數(shù)據(jù),讓我們假設(shè)一些?Cinépolis?電影院運(yùn)營人員已經(jīng)為電影院制定了計(jì)劃,因此我們的任務(wù)是檢索這些計(jì)劃。
????????電影院目錄服務(wù)所關(guān)注的只是電影院和時(shí)間表,僅此而已,上面我們看到我們進(jìn)行了位置收集,但這是對(duì)另一個(gè)微服務(wù)的關(guān)注,但我們依賴于能夠顯示電影院和時(shí)間表的位置。
????????現(xiàn)在我們已經(jīng)定義了我們的需求,我們可以構(gòu)建我們的 RAML 文件,如下所示:
#%RAML 1.0
title: Cinema Catalog Service
version: v1
baseUri: /
uses:
object: types.raml
stack: ../movies-service/api.raml
types:
Cinemas: object.Cinema []
Movies: stack.MoviePremieres
Schedules: object.Schedule []
traits:
FilterByLocation:
queryParameters:
city:
type: string
resourceTypes:
GET:
get:
responses:
200:
body:
application/json:
type: <<item>>
/cinemas:
type: { GET: {item : Cinemas } }
get:
is: [FilterByLocation]
description: we already have the location defined to display the cinemas
/cinemas/{cinema_id}:
type: { GET: {item : Movies } }
description: we have selected the cinema to display the movie premieres
/cinemas/{cinema_id}/{movie_id}:
type: { GET: {item : Schedules } }
description: we have selceted a movie to display the schedules
????????我們已經(jīng)滿足了上面 3 句話中的 4 句,第 4 句話用于在電影院預(yù)訂,但我的朋友屬于預(yù)訂服務(wù),他們負(fù)有這一責(zé)任,所以請(qǐng)繼續(xù)關(guān)注“構(gòu)建 NodeJs 電影院微服務(wù)?— 系列”。
現(xiàn)在我們可以繼續(xù)為電影目錄服務(wù)開發(fā)我們的 NodeJS?API,其結(jié)構(gòu)和配置與電影服務(wù)幾乎相同,所以我將開始向您展示此 API。repository.js
// more code above
const getCinemasByCity = (cityId) => {
return new Promise((resolve, reject) => {
const cinemas = []
const query = {city_id: cityId}
const projection = {_id: 1, name: 1}
// example of making a find query to mongoDB,
// passign a query and projection objects.
const cursor = db.collection('cinemas').find(query, projection)
const addCinema = (cinema) => {
cinemas.push(cinema)
}
const sendCinemas = (err) => {
if (err) {
reject(new Error('An error occured fetching cinemas, err: ' + err))
}
resolve(cinemas)
}
cursor.forEach(addCinema, sendCinemas)
})
}
const getCinemaById = (cinemaId) => {
return new Promise((resolve, reject) => {
const query = {_id: new ObjectID(cinemaId)}
const projection = {_id: 1, name: 1, cinemaPremieres: 1}
const response = (err, cinema) => {
if (err) {
reject(new Error('An error occuered retrieving a cinema, err: ' + err))
}
resolve(cinema)
}
// example of using findOne method from mongodb,
// we do this because we only need one record.
db.collection('cinemas').findOne(query, projection, response)
})
}
const getCinemaScheduleByMovie = (options) => {
return new Promise((resolve, reject) => {
const match = { $match: {
'city_id': options.cityId,
'cinemaRooms.schedules.movie_id': options.movieId
}}
const project = { $project: {
'name': 1,
'cinemaRooms.schedules.time': 1,
'cinemaRooms.name': 1,
'cinemaRooms.format': 1
}}
const unwind = [{ $unwind: '$cinemaRooms' }, { $unwind: '$cinemaRooms.schedules' }]
const group = [{ $group: {
_id: {
name: '$name',
room: '$cinemaRooms.name'
},
schedules: { $addToSet: '$cinemaRooms.schedules.time' }
}}, { $group: {
_id: '$_id.name',
schedules: {
$addToSet: {
room: '$_id.room',
schedules: '$schedules'
}
}
}}]
const sendSchedules = (err, result) => {
if (err) {
reject('An error has occured fetching schedules by movie, err: ' + err)
}
resolve(result)
}
// example of using a aggregation method from mongoDB
// we difine our pipline above, we are using also ES6 spread operator
db.collection('cinemas').aggregate([match, project, ...unwind, ...group], sendSchedules)
})
}
// more code below
要查看完整的文件,您可以在“github 存儲(chǔ)庫分支步驟-2”中檢查它。repository.js
在這里,我們定義了 3 個(gè)函數(shù):
- getCinemasByCity:這個(gè)函數(shù)將獲取城市中所有可用的電影院,我們通過city_id找到電影院,這個(gè)函數(shù)的結(jié)果幫助我們調(diào)用下一個(gè)電影院。
- getCinemaById:此函數(shù)將通過cinema_id查詢來檢索可用的名稱、id 和首映電影,該函數(shù)的結(jié)果將幫助我們最終獲得時(shí)間表。
- getCinemaScheduleByMovie:?此功能將為我們提供城市中所有電影院上可用的電影的所有時(shí)間表。
可能還有另一個(gè)功能,或者我們可以修改getCinemaById,以顯示當(dāng)前電影院的時(shí)間表,這對(duì)您來說可能是一個(gè)很好的挑戰(zhàn),如果您想練習(xí),這不會(huì)那么困難,因?yàn)槲乙呀?jīng)為您提供了所有需要的信息。
下一個(gè)要檢查的文件是我們的 API 文件 .cinemas-catalog.js
'use strict'
const status = require('http-status')
module.exports = (app, options) => {
const {repo} = options
app.get('/cinemas', (req, res, next) => {
repo.getCinemasByCity(req.query.cityId)
.then(cinemas => {
res.status(status.OK).json(cinemas)
})
.catch(next)
})
app.get('/cinemas/:cinemaId', (req, res, next) => {
repo.getCinemaById(req.params.cinemaId)
.then(cinema => {
res.status(status.OK).json(cinema)
})
.catch(next)
})
app.get('/cinemas/:cityId/:movieId', (req, res, next) => {
const params = {
cityId: req.params.cityId,
movieId: req.params.movieId
}
repo.getCinemaScheduleByMovie(params)
.then(schedules => {
res.status(status.OK).json(schedules)
})
.catch(next)
})
}
如您所見,這里我們實(shí)現(xiàn)端點(diǎn),正如我們?cè)?RAML?文件中定義的那樣,并根據(jù)路由調(diào)用函數(shù)。repository.js
在我們的第一條路線中,我們用于獲取值并查詢我們的數(shù)據(jù)庫以按城市獲取電影院,而在其他路線中,我們用于獲取值并能夠查詢。 ??req.query.cityId
city_id
req.params
cinemaId
movieId
schedules
最后我們可以看到用于測(cè)試的文件:cinema-catalog.spec.js
/* eslint-env mocha */
const request = require('supertest')
const server = require('../server/server')
process.env.NODE = 'test'
describe('Movies API', () => {
let app = null
const testCinemasCity = [{
'_id': '588ac3a02d029a6d15d0b5c4',
'name': 'Plaza Morelia'
}, {
'_id': '588ac3a02d029a6d15d0b5c5',
'name': 'Las Americas'
}]
const testCinemaId = {
'_id': '588ac3a02d029a6d15d0b5c4',
'name': 'Plaza Morelia',
'cinemaPremieres': [
{
'id': '1',
'title': 'Assasins Creed',
'runtime': 115,
'plot': 'Lorem ipsum dolor sit amet',
'poster': 'link to poster...'
},
{
'id': '2',
'title': 'Aliados',
'runtime': 124,
'plot': 'Lorem ipsum dolor sit amet',
'poster': 'link to poster...'
},
{
'id': '3',
'title': 'xXx: Reactivado',
'runtime': 107,
'plot': 'Lorem ipsum dolor sit amet',
'poster': 'link to poster...'
}
]
}
const testSchedulesMovie = [{
'_id': 'Plaza Morelia',
'schedules': [{
'room': 2.0,
'schedules': [ '10:15' ]
}, {
'room': 1.0,
'schedules': [ '6:55', '4:35', '10:15' ]
}, {
'room': 3.0,
'schedules': [ '10:15' ]
}]
}, {
'_id': 'Las Americas',
'schedules': [ {
'room': 2.0,
'schedules': [ '3:25', '10:15' ]
}, {
'room': 1.0,
'schedules': [ '12:15', '10:15' ]
}]
}]
let testRepo = {
getCinemasByCity (location) {
console.log(location)
return Promise.resolve(testCinemasCity)
},
getCinemaById (cinemaId) {
console.log(cinemaId)
return Promise.resolve(testCinemaId)
},
getCinemaScheduleByMovie (cinemaId, movieId) {
console.log(cinemaId, movieId)
return Promise.resolve(testSchedulesMovie)
}
}
beforeEach(() => {
return server.start({
port: 3000,
repo: testRepo
}).then(serv => {
app = serv
})
})
afterEach(() => {
app.close()
app = null
})
it('can return cinemas by location', (done) => {
const location = {
city: '588ababf2d029a6d15d0b5bf'
}
request(app)
.get(`/cinemas?cityId=${location.city}`)
.expect((res) => {
res.body.should.containEql(testCinemasCity[0])
res.body.should.containEql(testCinemasCity[1])
})
.expect(200, done)
})
it('can get movie premiers by cinema', (done) => {
request(app)
.get('/cinemas/588ac3a02d029a6d15d0b5c4')
.expect((res) => {
res.body.should.containEql(testCinemaId)
})
.expect(200, done)
})
it('can get schedules by cinema and movie', (done) => {
request(app)
.get('/cinemas/588ababf2d029a6d15d0b5bf/1')
.expect((res) => {
res.body.should.containEql(testSchedulesMovie[0])
res.body.should.containEql(testSchedulesMovie[1])
})
.expect(200, done)
})
})
????????最后,我們可以構(gòu)建我們的 docker 鏡像并在我們的容器中運(yùn)行它,我們將從?movies 服務(wù)中使用相同的鏡像,以使此過程更加自動(dòng)化,讓我們?yōu)榇巳蝿?wù)創(chuàng)建一個(gè) bash 腳本,如下所示:cinema-catalog-service
dockerfile
start-service.sh
#!/usr/bin/env bash
eval `docker-machine env manager1`
docker build -t catalog-service .
docker run --name catalog-service -p 3000:3000 --env-file env -d catalog-service
????????隨著我們開始制作更多服務(wù),我們需要小心我們服務(wù)可用的端口,所以這次,我將使用端口 3000,另外我將使用 a?開始使用我們的?NodeJS?服務(wù)中的配置,我們的 env 文件將如下所示:env file
process.env
DB=cinemas
DB_USER=cristian
DB_PASS=cristianPassword2017
DB_REPLS=rs1
DB_SERVERS='192.168.99.100:27017 192.168.99.101:27017 192.168.99.102:27017'
PORT=3000
????????這被認(rèn)為是在 devOps 領(lǐng)域之外的最佳實(shí)踐。
????????最后,我們需要像下面這樣運(yùn)行我們的小 bash 腳本:
# execute our script
$ bash < start-service.sh
# check running docker contianers
$ docker ps
我們需要這樣的東西:
????????碼頭工人狀態(tài)
????????我們可以在Chrome瀏覽器中測(cè)試我們的服務(wù),并驗(yàn)證我們的HTTP / 2協(xié)議是否正常工作,以及我們的服務(wù)是否正常工作。

????????為了好玩,我們可以使用?JMeter?進(jìn)行壓力測(cè)試,壓力測(cè)試文件也位于 github 存儲(chǔ)庫的文件夾中。integration-test/
JMeter capture
stress test example
5.5?是時(shí)候回顧一下了
????????已經(jīng)實(shí)現(xiàn)的:

????????我們已經(jīng)完成了此圖的微服務(wù),您可能會(huì)說我們沒有在我們的電影院目錄服務(wù)中使用電影服務(wù),是的,這是正確的,我們所做的只是來自我們服務(wù)的?GET?請(qǐng)求,并且在電影院目錄服務(wù)中使用我們的電影服務(wù)是通過進(jìn)行?POST?請(qǐng)求完成影院首映堆棧電影以便能夠制定時(shí)間表,但由于我們的任務(wù)是從我們團(tuán)隊(duì)中的 CRUD 操作制作?R,這就是為什么我們沒有看到這種交互,但稍后在該系列中,我們將在微服務(wù)之間進(jìn)行更多的?CRUD?操作,請(qǐng)耐心等待,:D保持好奇心。
????????因此,我們?cè)诒菊??中了解了HTTP / 2協(xié)議,并看到了如何在微服務(wù)中實(shí)現(xiàn)它。我們還看到了如何設(shè)計(jì) MongoDB 模式,我們沒有深入,但我突出顯示它是為了更好地了解電影院目錄服務(wù)中發(fā)生的事情,然后我們使用?RAML?設(shè)計(jì) API,我們開始構(gòu)建我們的?API?服務(wù),然后我們進(jìn)行了相應(yīng)的單元測(cè)試,最后我們編寫所有內(nèi)容以使我們的微服務(wù)完成。
????????然后我們使用之前微服務(wù)中的相同?dockerfile,并編寫一個(gè)腳本來自動(dòng)化此過程,我們還介紹了如何使用和?env 文件并在?Docker?容器中使用它,準(zhǔn)備好所有設(shè)置后,我向您展示了 JMeter 進(jìn)行壓力測(cè)試以補(bǔ)充集成測(cè)試的簡(jiǎn)短圖片。environment variables
????????我們已經(jīng)在?NodeJS?中看到了很多開發(fā),但我們可以做和學(xué)習(xí)的東西還有很多,這只是一個(gè)先睹為快的高峰。我希望這已經(jīng)展示了一些有趣和有用的東西,你可以在你的工作流程中用于Docker和NodeJS。
5.6 即將推出
????????現(xiàn)在我們已經(jīng)完成了第一個(gè)圖表,我們將開發(fā)第二個(gè)圖表,預(yù)訂服務(wù)圖和......??????????????????
微服務(wù)圖
六、在 Github 上完成代碼
您可以在以下鏈接中查看文章的完整代碼。
Crizstian/cinema-microservice
影院微服務(wù) - 影院微服務(wù)示例
github.com
七、# 參考資料延伸閱讀
為了更好地使用NodeJS,您可以查看此站點(diǎn)文章來源:http://www.zghlxwxcb.cn/news/detail-663885.html
- 10年成為更好的節(jié)點(diǎn)開發(fā)人員的2017個(gè)技巧
- NodeJS 教程系列 —?Node Hero(幾乎涵蓋了所有節(jié)點(diǎn)主題)
- 簡(jiǎn)單的HTTP / 2服務(wù)器與Node.js和Express.js
- HTTP/2:好的、壞的和丑陋的
克里斯蒂安·拉米雷斯文章來源地址http://www.zghlxwxcb.cn/news/detail-663885.html
到了這里,關(guān)于構(gòu)建 NodeJS 影院微服務(wù)并使用 docker 部署它(02/4)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!