构建 NodeJS 影院微服务并使用 docker 部署它(02/4)
一、说明
构建一个微服务的电影网站,需要Docker、NodeJS、MongoDB,这样的案例您见过吗?如果对此有兴趣,您就继续往下看吧。
图片取自网络 — 封面由我制作
这是✌️“构建 NodeJS 影院微服务”系列的第二篇文章。
二、对第一部分的快速回顾
- 我们讨论什么是微服务。
- 我们看到了微服务的优点和缺点。
- 我们定义了一个影院微服务架构。
- 我们使用 RAML 设计我们的电影服务 API 规范。
- 我们使用 NodeJS 和 ExpressJS 开发我们的电影服务 API。
- 我们对 API 进行了单元测试。
- 我们编写我们的 API 以使其成为服务,并将我们的电影服务运行到 Docker 容器中。
- 我们对在 Docker 上运行的电影服务进行了集成测试。
如果你还没有读过第一章,我会把链接放在下面,所以你可以看看👀。
构建一个 NodeJS 影院微服务并使用 docker 部署它 — 第 1 部分
这是“构建 NodeJS 影院微服务”系列的第一章,这个系列是关于构建 NodeJS...
在本文中,我们将继续构建我们的电影院微服务,这次我们将开发电影院目录服务,以完成下图。
我们将在本文中使用的是:
- NodeJS 版本 7.2.0
- MongoDB 3.4.1
- Docker 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 协议。
# Let's 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 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
首先让我们在文件夹中创建一个文件,使用以下代码,这里是我们加载密钥和证书文件的地方,这可能是我们可以使用的少数情况之一:index.js
ssl/
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.cityId
city_id
req.params
cinemaId
movieId
schedules
最后我们可以看到用于测试的文件: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 = 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-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
随着我们开始制作更多服务,我们需要小心我们服务可用的端口,所以这次,我将使用端口 3000,另外我将使用 a 开始使用我们的 NodeJS 服务中的配置,我们的 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
这被认为是在 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.js
- HTTP/2:好的、坏的和丑陋的
克里斯蒂安·拉米雷斯
相关文章:
构建 NodeJS 影院微服务并使用 docker 部署它(02/4)
一、说明 构建一个微服务的电影网站,需要Docker、NodeJS、MongoDB,这样的案例您见过吗?如果对此有兴趣,您就继续往下看吧。 图片取自网络 — 封面由我制作 这是✌️“构建 NodeJS 影院微服务”系列的第二篇文章。 二、对第一部分的…...
HTML <style> 标签
实例 <html> <head> <style type="text/css"> h1 {color:red} p {color:blue} </style> </head><body> <h1>Header 1</h1> <p>A paragraph.</p> </body> </html>定义和用法 <style>…...
设计模式——迪米特法则
文章目录 基本介绍应用实例应用实例改进迪米特法则注意事项和细节 基本介绍 一个对象应该对其他对象保持最少的了解类与类关系越密切,耦合度越大迪米特法则(Demeter Principle)又叫最少知道原则,即一个类对自己依赖的类知道的越少越好。也就是说&#x…...
区块链基本概念与当前生态简介
区块链是一种去中心化的分布式账本技术,它通过将数据按照时间顺序链接成区块,并使用密码学算法确保数据的安全性和完整性。每个区块包含一定数量的交易记录,而且每个区块都包含了前一个区块的哈希值,这样形成了一个不可篡改的链式…...
mac安装lrzsz出错Command failed with exit 128: git
终端检查电脑是否安装了rz和sz which sz若报错,则需要下载。由于网络和代理的原因,以下命令会报错: brew install lrzsz是因为brew和git配置的代理存在冲突,对于无外网链接功能,无特殊配置的git而言,需要…...
“深入探索JVM内部机制:揭秘Java虚拟机“
标题:深入探索JVM内部机制:揭秘Java虚拟机 摘要:本文将深入探索Java虚拟机(JVM)的内部机制,从内存管理、垃圾回收、即时编译等方面进行详细剖析。通过了解JVM的工作原理,我们可以更好地理解Jav…...
lvs-DR
lvs-DR数据包流向分析 client向目标VIP发出请求。 DIR根据负载均衡算法一台active的RS(RIR1),将RIP1所在的网卡的mac地址作为目标的mac地址,发送到局域网里。 RIRI在局域网中的收到这个帧,拆开后发现目标(…...
Vue 项目运行 npm install 时,卡在 sill idealTree buildDeps 没有反应
解决方法:切换到淘宝镜像。 以下是之前安装的 xmzs 包,用于控制切换淘宝镜像。 该截图是之前其他项目切换淘宝镜像的截图。 切换镜像后,顺利执行 npm install 。...
ShardingSphere介绍
ShardingSphere从4.X到5.X的内容发生了很多的改变,感兴趣的伙伴可以到ShardingSphere的博客查看各个版本的新特性。https://blog.csdn.net/ShardingSphere?typeblog 此次使用最新版本 shardingShpere5.4.0,实现数据库读写分离、数据分片、分布式事务等…...
【SA8295P 源码分析】44 - 如何替换 NON-HLOS.bin 中的 Wifi Firmware 固件
【SA8295P 源码分析】44 - 如何替换 NON-HLOS.bin 中的 Wifi Firmware 固件 1、提取 NON-HLOS.bin 中的 Wifi Firmware 出来2、把提取出来的 wifi 固件放到代码中3、重新打包生成 NON-HLOS.bin4、将生成的 NON-HLOS.bin 与 老的 NON-HLOS.bin 对比5、使用fastboot 下载测试wifi…...
市面上那里有稳定L2股票行情数据接口?
随着市场的发展和技术的进步,level2股票行情数据接口已经成为股票交易软件的标准配置之一。虽然这些券商软件的功能在很大程度上相似,但它们仍然有自己的特点和优势。 例如:通过股票交易所以其专业的研究报告和丰富的信息服务而受到广泛关注&…...
个人信息保护影响评估(PIA)怎么做?解发条件、实施步骤、操作指南
个人信息保护一直是人们关注的热点话题,互联网、人工智能、大数据等新兴技术的快速发展极大地增强了入侵个人信息的能力,对个人信息的随意收集、违法获取、过度使用、非法买卖、泄露等问题引起了全球各国的普遍关注。同时随着用户的个人信息保护意识的逐…...
HTML <sub> 标签
例子 这段文本包含 <sub>下标</sub> 定义和用法 <sub> 标签可定义下标文本。 包含在 <sub> 标签和其结束标签 </sub> 中的内容将会以当前文本流中字符高度的一半来显示,但是与当前文本流中文字的字体和字号都是一样的。 提示&am…...
C# 设置、获取程序,产品版本号
右键,程序属性。打开“程序集信息” 选择需要设置的版本信息。下面的代码,获取不同的设置内容。 string 其他 Assembly.GetExecutingAssembly().FullName; string 程序集版本 Assembly.GetExecutingAssembly().G…...
LeetCode 面试题 01.04. 回文排列
文章目录 一、题目二、C# 题解 一、题目 给定一个字符串,编写一个函数判定其是否为某个回文串的排列之一。 回文串是指正反两个方向都一样的单词或短语。排列是指字母的重新排列。 回文串不一定是字典当中的单词。 点击此处跳转题目。 示例1: 输入&…...
CentOS 7 安装MySQL8.0.33
一、查看 CentOS 版本 要查看当前 CentOS 版本,你可以执行以下命令: cat /etc/centos-release 该命令将显示当前 CentOS 的版本信息,例如: CentOS Linux release 7.9.2009 (Core) 在这个示例中,CentOS 版本为 7.…...
OpenCV(二)——图像基本处理(四)
目录 4.图像形态学操作 4.1 图像腐蚀 4.2 图像膨胀 4.3 开运算 4.4 闭运算...
11.小程序的配置项
window导航配置 全局配置通过 app.json进行 “window”: { “backgroundTextStyle”: “light”, “navigationBarBackgroundColor”: “#fff”, “navigationBarTitleText”: “Weixin”, “navigationBarTextStyle”: “black” }, 局部配置通过页面的xx.json配置 { “navig…...
一文科普,配资门户网是什么?
配资门户网是一个为投资者提供配资服务的平台。配资是指通过借用他人资金进行投资交易的一种金融操作方式。配资门户网作为一个线上平台,为投资者提供了方便、快捷的配资服务。 配资门户网提供了多种不同的配资方案,以满足不同投资者的需求。投资者可以…...
编写一个俄罗斯方块
编写俄罗斯方块 思路。 1、创建容器数组,方块, 2、下落,左右移动,旋转,判断结束,消除。 定义一个20行10列的数组表示游戏区。初始这个数组里用0填充,1表示有一个方块,2表示该方块固…...
认识容器,走进Docker
文章目录 容器技术简介容器的核心技术容器平台技术容器的支持技术 Docker理念Docker安装配置阿里云镜像加速器 容器技术简介 一切在云端,万物皆容器,说到容器,大家都会想到Docker,Docker现在几乎是容器的代名词,什么是Docker&…...
初始web
华子目录 前后端与全栈BS架构网页开发原则前端三剑客初始htmlhtml的基本框架如何使用vscode创建网页网页基本框架html基本标签 前后端与全栈 前端:给用户看的内容 – 荧幕前(负责显示) 后端:在后台处理数据 – 荧幕后(负责处理) …...
JVM中释放内存的三种方法
判断是否需要垃圾回收可以采用分析。 1标记--清除算法 分为两个阶段,标记和清除,先利用可达性分型标记还存活的对象,之后将没有被标记的对象删除,这样容易生成空间碎片,而且效率不稳定 标记阶段: 标记阶段…...
图床项目进度(一)——UI首页
1. 前言 前面我不是说了要做一个图床吗,现在在做ui。 我vue水平不够高,大部分参考b站项目照猫画虎。 vue实战后台 我使用ts,vite,vue3进行了重构。 当然,我对这些理解并不深刻,许多代码都是游离于表面&am…...
vue3父子组件相互调用方法详解
vue3父子组件相互调用方法详解 1、前言2、父组件调用子组件方法2.1 子组件Child.vue2.2 父组件Father.vue 3、子组件调用父组件方法3.1 父组件Father.vue3.2 子组件Child.vue 1、前言 在vue3项目开发中,我们常常会遇到父子组件相互调用的场景,下面以set…...
Java之接口
作者简介: zoro-1,目前大一,正在学习Java,数据结构等 作者主页: zoro-1的主页 欢迎大家点赞 👍 收藏 ⭐ 加关注哦!💖💖 Java之接口 接口的概念语法规则接口特性接口使用案…...
QT学习笔记-QT5.15编译及安装谷歌拼音输入法(QtInputMethod_GooglePinyin)
QT学习笔记-QT5.15编译及安装谷歌拼音输入法(QtInputMethod_GooglePinyin) 0、背景1、环境2、下载QtInputMethod_GooglePinyin源码3、使用MinGW64构建套件编译3.1 编译QtInputMethod_GooglePinyin源码3.2、部署tgtsmlInputContextPlugin输入法插件3.3、运…...
python 使用 pdf2image 库将PDF转换为图片
在 Ubuntu 上实现网络穿透:手把手教你搭建FRPS服务器 初环境步骤一:安装pdf2image库步骤二:导入必要的库步骤三:指定PDF文件路径步骤四:将PDF转换为图片步骤五:保存图像为图片文件完整代码运行结果 在数字化…...
kubernetes(namespace、pod、deployment、service、ingress)
NameSpace NameSpace名称空间 用来隔离资源,但是不隔离网络 使用命令行: kubectl create ns hello #创建 kubectl delete ns hello #删除 kubectl get ns #查看使用配置文件: vi hello.yamlapiVersion: v1 kind: Namespace metadata:name…...
深度学习loss变为nan的问题
在网上查了一些资料,但是这个情况和网上都不太一样。前100epoch能正常训练,loss缓慢下降,精度缓慢增大,但是突然loss就Nan了,我想应该不是样本问题也不是梯度爆炸或者loss中有除0吧,毕竟都训练了100epoch了…...
网站建设 专家/优化大师软件大全
物联网技术发展趋势是LPWAN,其中尤其以NB-IoT和eMTC最为代表。NB-IoT和eMTC各有优劣,使用场景互有不同。 低功耗可以说是物联网技术的核心,本着关注低功耗的方向,适当了解NB IoT在整个LTE中的位置,NB-IoT的协议&#x…...
企业网站的党建文化怎么做/学电子商务出来能干嘛
一 .进程概念理解 1.前面必备知识点 #一 操作系统的作用:1:隐藏丑陋复杂的硬件接口,提供良好的抽象接口2:管理、调度进程,并且将多个进程对硬件的竞争变得有序#二 多道技术:1.产生背景:针对单核…...
wordpress主题转换/目前网络推广平台
1. 剑指 Offer 22. 链表中倒数第k个节点 输入一个链表,输出该链表中倒数第k个节点。为了符合大多数人的习惯,本题从1开始计数,即链表的尾节点是倒数第1个节点。例如,一个链表有6个节点,从头节点开始,它们的…...
o2o电子商务网站建设/海口关键词优化报价
点击蓝字关注我们吧!Python的起源Python的作者,Guido von Rossum,荷兰人。1982年,Guido从阿姆斯特丹大学获得了数学和计算机硕士学位。然而,尽管他算得上是一位数学家,但他更加享受计算机带来的乐趣。用他的…...
日本网站欣赏/宁海关键词优化怎么优化
一、需求:有这么一张表前四个属性当作联合主键需要把该表所有的行在前端以表格形式显示出来,要求activityId相同时合并成一行,activityCode相同时,合并一行,activityVersion相同时也合并一行。类似这种:二、…...
上海微网站建设/低价刷粉网站推广
为什么80%的码农都做不了架构师?>>> 如果nginx一直出现 nginx bad gateway 5XX 之类的 首先用python manager.py runserver 0.0.0.0:8000 并且把debug改为True 输出调试信息 因为99%的可能性是项目源码中出现了问题 或者是uwsgi和nginx链接的socke…...