西安建设和住房保障局网站首页,新手做网站怎么上传系统,企业网站建设教程pdf,做a网站一、说明 构建一个微服务的电影网站#xff0c;需要Docker、NodeJS、MongoDB#xff0c;这样的案例您见过吗#xff1f;如果对此有兴趣#xff0c;您就继续往下看吧。 图片取自网络 — 封面由我制作 这是✌️“构建 NodeJS 影院微服务”系列的第二篇文章。 二、对第一部分的… 一、说明 构建一个微服务的电影网站需要Docker、NodeJS、MongoDB这样的案例您见过吗如果对此有兴趣您就继续往下看吧。 图片取自网络 — 封面由我制作 这是✌️“构建 NodeJS 影院微服务”系列的第二篇文章。 二、对第一部分的快速回顾 我们讨论什么是微服务。我们看到了微服务的优点和缺点。我们定义了一个影院微服务架构。我们使用 RAML 设计我们的电影服务 API 规范。我们使用 NodeJS 和 ExpressJS 开发我们的电影服务 API。我们对 API 进行了单元测试。我们编写我们的 API 以使其成为服务并将我们的电影服务运行到 Docker 容器中。我们对在 Docker 上运行的电影服务进行了集成测试。 如果你还没有读过第一章我会把链接放在下面所以你可以看看。 构建一个 NodeJS 影院微服务并使用 docker 部署它 — 第 1 部分 这是“构建 NodeJS 影院微服务”系列的第一章这个系列是关于构建 NodeJS... 在本文中我们将继续构建我们的电影院微服务这次我们将开发电影院目录服务以完成下图。 我们将在本文中使用的是 NodeJS 版本 7.2.0MongoDB 3.4.1Docker for Mac 1.13 跟进文章的先决条件 已完成上一章中的示例。 如果您还没有我已经在以下 github 存储库上传了存储库因此您可以在分支步骤 1 处获得最新的存储库链接。 三、微服务安全和 HTTP/2 在第一章中我们做了一个简单的微服务来实现HTTP/1.1协议。HTTP/2 是 15 年来对 HTTP 协议的首次重大升级经过高度优化性能更好。HTTP/2是新的Web标准最初是Google的SPDY协议。它已经被许多流行的网站使用并被大多数主流浏览器支持。 HTTP/2 只有一些规则必须满足才能实现它。 它仅适用于HTTPS协议我们需要有效的SSL证书。它是向后兼容的。如果运行应用程序的浏览器或设备不支持 HTTP/2它将回退到 HTTP1.1。它具有开箱即用的巨大性能改进。它不需要您在客户端执行任何操作只需在服务器端执行基本实现即可。一些新的有趣的功能将加快Web项目的加载时间其方式甚至是HTTP1.1实现无法想象的。 四、为微服务启用网络架构的 HTTP/2 这意味着我们需要在客户端和服务器之间启用单个连接然后在“网络”中利用 Y 轴分片等功能更多地谈论系列中的缩放立方体来保持 HTTP/2 对客户端的性能优势同时实现微服务架构的所有操作和开发优势。 那么我们为什么要实施新的HTTP / 2协议这是因为作为优秀的开发人员我们必须尽可能保护我们的应用程序基础架构通信以防止恶意攻击也因为作为优秀的开发人员我们遵循我们认为对我们有益的最佳实践就像这个。 微服务的一些安全最佳实践如下所示 安全性显然将在采用和部署微服务应用程序以供生产使用的决定中发挥重要作用。根据2016 Research发布的研究报告《451年软件定义的基础设施展望》近45%的企业已经实施或计划在未来12个月内推出基于容器的应用程序。随着 DevOps 实践在企业中站稳脚跟容器应用程序变得越来越普遍安全管理员需要用保护应用程序的专业知识武装自己。— Ranga 拉贾戈帕兰 发现和监视服务间通信对应用程序和服务进行分段和隔离加密传输中的数据和静态数据 我们要做的是加密我们的微服务通信以满足合规性要求并提高安全性尤其是当流量通过公共网络时这就是我们要实施HTTP / 2的原因之一以获得更好的性能和安全性改进。 五、在微服务中实现 HTTP/2 首先让我们更新上一章的电影服务并实现HTTP / 2协议但是在我们打算在config文件夹中创建一个ssl文件夹之后。 movies-service/config:/ $ mkdir ssl
movies-service/config:/ $ cd ssl 现在一旦进入 ssl 文件夹让我们创建一个自签名 SSL 证书以开始在我们的服务中实现 HTTP/2 协议。 # Lets generate the server pass keyssl/: $ openssl genrsa -des3 -passout pass:x -out server.pass.key 2048
# now generate the server key from the pass keyssl/: $ openssl rsa -passin pass:x -in server.pass.key -out server.key
# we remove the pass keyssl/: $ rm server.pass.key
# now lets 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 lets 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 首先让我们在文件夹中创建一个文件使用以下代码这里是我们加载密钥和证书文件的地方这可能是我们可以使用的少数情况之一index.jsssl/fs.readFileSync() const fs require(fs)
module.exports {key: fs.readFileSync(${__dirname}/server.key),cert: fs.readFileSync(${__dirname}/server.crt)
} 然后我们需要修改几个文件让我们先修改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 appconst 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 reprepository.connect(db).then(repo {console.log(Connected. Starting Server)rep reporeturn server.start({port: config.serverSettings.port,// here we pass the ssl options to the server.js filessl: config.serverSettings.ssl,repo})}).then(app { ... })
})
... 现在我们需要使用以下命令重建我们的 docker 镜像 $ docker build -t movies-service . 并使用以下参数运行我们的 docker 映像movies-service $ docker run --name movies-service -p 443:3000 -d movies-service 最后我们使用Chrome浏览器对其进行了测试我们可以证实我们的HTTP / 2协议完全有效。 Chrome 开发工具 我们还可以证实使用wireshark进行一些网络捕获我们可以看到SSL确实有效。 线鲨帧捕获 5.1 将 JWT 实现到微服务 加密和保护微服务通信的另一种方法是使用该协议但我们将在后面的系列中看到此实现。json web token 5.2 构建微服务 好的现在我们知道了如何实现 HTTP/2 协议让我们继续构建电影目录服务。 我们将使用与电影服务相同的项目结构因此 少说话多编码 . 在我们开始设计 API 之前这次我们需要为我们的数据库设计我们的 Mongo 模式因为我们将使用以下内容 地点国家、州和城市电影院电影院、时间表、电影 5.3 模型数据设计 本文主要专注于创建微服务因此我不会花费大量时间为我们的电影院数据库进行“模型数据设计”而是将重点介绍这些领域和要点。 # posible collections for the cinemas db.
# For locations
- countries
- states
- cities
# For cinemas
- cinemas
- cinemaRooms
- schedules 因此对于我们的位置一个国家/地区具有许多州而州具有一个国家/地区因此第一种关系是一对多关系但这也适用于一个州有许多城市一个城市具有一个州因此让我们看看我们的关系示例如何。 国家 — 国家关系 州/城市关系 但是这种关系也是可能的一个城市有很多电影院一个电影院属于一个城市另一个关系我们可以看到就是一个电影院房间有很多档期一个档期属于一个电影院房间所以让我们看看这种关系是怎样的。 如果电影院阵列或时间表阵列的增长受到限制则上图中的这种引用可能很有用。假设一个电影室每天最多有 5 个时间表所以在这里我们可以将时间表文档嵌入到电影院文档中。 嵌入式数据模型允许应用程序在同一数据库记录中存储相关信息。因此应用程序可能需要发出较少的查询和更新来完成常见操作。— MongoDB Docs 因此这是我们数据库模式设计的最终结果。 5.4 将数据导入我们的数据库 我已经准备了一些数据示例其中包含上面看到的模式设计这些文件位于 github 存储库中有 4 个 json 文件因此您可以将其导入电影院数据库但首先我们需要知道哪个数据库服务器是主要的因此找出并执行以下命令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 执行上述命令后让我们将其导入数据库如下所示 $ 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 现在我们已经准备好了数据库模式设计数据也准备好了可以查询了所以我们现在可以为电影目录服务设计我们的 API定义路由的一种方法是制作一些句子如下所示 我们需要一个城市来展示可用的电影院。我们需要电影院来展示电影首映式。我们需要电影首映并显示时间表。我们需要时间表以查看是否有可供预订的座位。 让我们假设 Cinépolis IT 部门的其他团队正在执行其他 CRUD 操作我们的任务是使“R”成为读取数据让我们假设一些 Cinépolis 电影院运营人员已经为电影院制定了计划因此我们的任务是检索这些计划。 电影院目录服务所关注的只是电影院和时间表仅此而已上面我们看到我们进行了位置收集但这是对另一个微服务的关注但我们依赖于能够显示电影院和时间表的位置。 现在我们已经定义了我们的需求我们可以构建我们的 RAML 文件如下所示 #%RAML 1.0
title: Cinema Catalog Service
version: v1
baseUri: /
uses:object: types.ramlstack: ../movies-service/api.ramltypes:Cinemas: object.Cinema []Movies: stack.MoviePremieresSchedules: object.Schedule []traits:FilterByLocation:queryParameters:city:type: stringresourceTypes: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 我们已经满足了上面 3 句话中的 4 句第 4 句话用于在电影院预订但我的朋友属于预订服务他们负有这一责任所以请继续关注“构建 NodeJs 电影院微服务 — 系列”。 现在我们可以继续为电影目录服务开发我们的 NodeJS API其结构和配置与电影服务几乎相同所以我将开始向您展示此 API。repository.js // more code aboveconst 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 operatordb.collection(cinemas).aggregate([match, project, ...unwind, ...group], sendSchedules)})}// more code below 要查看完整的文件您可以在“github 存储库分支步骤-2”中检查它。repository.js 在这里我们定义了 3 个函数 getCinemasByCity这个函数将获取城市中所有可用的电影院我们通过city_id找到电影院这个函数的结果帮助我们调用下一个电影院。getCinemaById此函数将通过cinema_id查询来检索可用的名称、id 和首映电影该函数的结果将帮助我们最终获得时间表。getCinemaScheduleByMovie 此功能将为我们提供城市中所有电影院上可用的电影的所有时间表。 可能还有另一个功能或者我们可以修改getCinemaById以显示当前电影院的时间表这对您来说可能是一个很好的挑战如果您想练习这不会那么困难因为我已经为您提供了所有需要的信息。 下一个要检查的文件是我们的 API 文件 .cinemas-catalog.js use strict
const status require(http-status)module.exports (app, options) {const {repo} optionsapp.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)})
} 如您所见这里我们实现端点正如我们在 RAML 文件中定义的那样并根据路由调用函数。repository.js 在我们的第一条路线中我们用于获取值并查询我们的数据库以按城市获取电影院而在其他路线中我们用于获取值并能够查询。 req.query.cityIdcity_idreq.paramscinemaIdmovieIdschedules 最后我们可以看到用于测试的文件cinema-catalog.spec.js /* eslint-env mocha */
const request require(supertest)
const server require(../server/server)
process.env.NODE testdescribe(Movies API, () {let app nullconst 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)})
}) 最后我们可以构建我们的 docker 镜像并在我们的容器中运行它我们将从 movies 服务中使用相同的镜像以使此过程更加自动化让我们为此任务创建一个 bash 脚本如下所示cinema-catalog-servicedockerfilestart-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 随着我们开始制作更多服务我们需要小心我们服务可用的端口所以这次我将使用端口 3000另外我将使用 a 开始使用我们的 NodeJS 服务中的配置我们的 env 文件将如下所示env fileprocess.env DBcinemas
DB_USERcristian
DB_PASScristianPassword2017
DB_REPLSrs1
DB_SERVERS192.168.99.100:27017 192.168.99.101:27017 192.168.99.102:27017
PORT3000 这被认为是在 devOps 领域之外的最佳实践。 最后我们需要像下面这样运行我们的小 bash 脚本 # execute our script
$ bash start-service.sh
# check running docker contianers
$ docker ps 我们需要这样的东西 码头工人状态 我们可以在Chrome浏览器中测试我们的服务并验证我们的HTTP / 2协议是否正常工作以及我们的服务是否正常工作。 铬浏览器 — 测试 为了好玩我们可以使用 JMeter 进行压力测试压力测试文件也位于 github 存储库的文件夹中。integration-test/ JMeter capture stress test example 5.5 是时候回顾一下了 已经实现的 休息沟通 我们已经完成了此图的微服务您可能会说我们没有在我们的电影院目录服务中使用电影服务是的这是正确的我们所做的只是来自我们服务的 GET 请求并且在电影院目录服务中使用我们的电影服务是通过进行 POST 请求完成影院首映堆栈电影以便能够制定时间表但由于我们的任务是从我们团队中的 CRUD 操作制作 R这就是为什么我们没有看到这种交互但稍后在该系列中我们将在微服务之间进行更多的 CRUD 操作请耐心等待:D保持好奇心。 因此我们在本章中了解了HTTP / 2协议并看到了如何在微服务中实现它。我们还看到了如何设计 MongoDB 模式我们没有深入但我突出显示它是为了更好地了解电影院目录服务中发生的事情然后我们使用 RAML 设计 API我们开始构建我们的 API 服务然后我们进行了相应的单元测试最后我们编写所有内容以使我们的微服务完成。 然后我们使用之前微服务中的相同 dockerfile并编写一个脚本来自动化此过程我们还介绍了如何使用和 env 文件并在 Docker 容器中使用它准备好所有设置后我向您展示了 JMeter 进行压力测试以补充集成测试的简短图片。environment variables 我们已经在 NodeJS 中看到了很多开发但我们可以做和学习的东西还有很多这只是一个先睹为快的高峰。我希望这已经展示了一些有趣和有用的东西你可以在你的工作流程中用于Docker和NodeJS。 5.6 即将推出 现在我们已经完成了第一个图表我们将开发第二个图表预订服务图和...... 微服务图 六、在 Github 上完成代码 您可以在以下链接中查看文章的完整代码。 Crizstian/cinema-microservice 影院微服务 - 影院微服务示例 github.com 七、# 参考资料延伸阅读 为了更好地使用NodeJS您可以查看此站点 10年成为更好的节点开发人员的2017个技巧NodeJS 教程系列 — Node Hero几乎涵盖了所有节点主题简单的HTTP / 2服务器与Node.js和Express.jsHTTP/2好的、坏的和丑陋的 克里斯蒂安·拉米雷斯