构建 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 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

        首先让我们在文件夹中创建一个文件,使用以下代码,这里是我们加载密钥和证书文件的地方,这可能是我们可以使用的少数情况之一: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 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 => { ... })
})
...

现在我们需要使用以下命令重建我们的 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.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

        我们已经满足了上面 3 句话中的 4 句,第 4 句话用于在电影院预订,但我的朋友属于预订服务,他们负有这一责任,所以请继续关注“构建 NodeJs 电影院微服务 — 系列”。

现在我们可以继续为电影目录服务开发我们的 NodeJS API,其结构和配置与电影服务几乎相同,所以我将开始向您展示此 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 存储库分支步骤-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} = 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)
  })
}

如您所见,这里我们实现端点,正如我们在 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 = '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)
  })
})

        最后,我们可以构建我们的 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

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:好的、坏的和丑陋的

克里斯蒂安·拉米雷斯

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/82788.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

netty(一):NIO——处理消息边界

处理消息边界 为什么要处理边界 因为会存在半包和粘包的问题 1.客户端和服务端约定一个固定长度 优点&#xff1a;简单 缺点&#xff1a;可能造成浪费 2.客户端与服务端约定一个固定分割符 *缺点 效率低 3.先发送长度&#xff0c;再发送数据 TLV格式&#xff1a; type…

Git分布式版本控制系统

目录 2、安装git 2.1 初始环境 2.2 Yum安装Git 2.3 编译安装 2.4 初次运行 Git 前的配置 2.5 初始化及获取 Git 仓库 2.6 Git命令常规操作 2.6.2 添加新文件 2.6.3 删除git内的文件 2.6.4 重命名暂存区数据 2.6.5 查看历史记录 2.6.6 还原历史数据 2.6.7 还原未来…

星际争霸之小霸王之小蜜蜂(四)--事件监听-让小蜜蜂动起来

目录 前言 一、监听按键并作出判断 二、持续移动 三、左右移动 总结&#xff1a; 前言 今天开始正式操控我们的小蜜蜂了&#xff0c;之前学java的时候是有一个函数监听鼠标和键盘的操作&#xff0c;我们通过传过来不同的值进行判断&#xff0c;现在来看看python是否一样的实现…

lvs-DR

lvs-DR数据包流向分析 client向目标VIP发出请求。 DIR根据负载均衡算法一台active的RS&#xff08;RIR1&#xff09;&#xff0c;将RIP1所在的网卡的mac地址作为目标的mac地址&#xff0c;发送到局域网里。 RIRI在局域网中的收到这个帧&#xff0c;拆开后发现目标&#xff08…

【ARM】Day4 点亮LED灯

1. 思维导图 2. 自己编写代码实现三盏灯点亮 .text .global _start _start: /**********LED1&#xff0c;LED2,LED3点灯:PE10,PF10,PE8**************/ RCC_INIT:使能GPIOE组/GPIOF组控制器,通过RXCC_MP_AHB4ENSETR设置第[5:4]位写1,地址:0x50000A28[5:4]1ldr r0,0x50000A28 …

【SA8295P 源码分析】03 - SA8295P QNX Host上电开机流程分析

【SA8295P 源码分析】03 - SA8295P QNX Host上电开机流程分析 一、阶段1 固件开机自检 (SM BIST)&#xff1a;APPS PBL加载XBL后触发 INT_RESET进行Warm Reset二、阶段2 固件开机自检 (SM BIST)&#xff1a;加载TZ&#xff0c;初始Hypervisor&#xff0c;启动QNX Kernel&#x…

22年电赛B题——具有自动泊车功能的电动车——做题记录以及经验分享

前言 这道题目也是小车类电赛题目&#xff0c;十月份的电赛题&#xff0c;由于之前积累了一些经验&#xff0c;这道题目在做下来的感觉还行,但是我们看题目没有仔细审题&#xff0c;和题目要求有一些些偏差&#xff0c;但是基础大功能还是做出来辽&#xff0c;大家还是可以参考…

LeetCode283.移动零

这道题还是很简单的&#xff0c;我用的是双指针&#xff0c;左指针i从头开始遍历数组&#xff0c;右指针j是从i后面第一个数开始遍历&#xff0c;当左指针i等于0的时候&#xff0c;右指针j去寻找i右边第一个为0的数和i交换位置&#xff0c;交换完了就break内层循环&#xff0c;…

Linux网络编程:网络基础

文章目录&#xff1a; 一&#xff1a;协议 二&#xff1a;网络应用设计模式_BS模式和CS模式 三&#xff1a;网络分层模型&#xff08;OSI七层 TCP/IP四层&#xff09; 四&#xff1a;通信过程 五&#xff1a;协议格式 1.数据包封装 2.以太网帧格式和ARP数据报格式 …

【SA8295P 源码分析】06 - SA8295P XBL Loader 阶段 sbl1_main_ctl 函数代码分析

【SA8295P 源码分析】06 - SA8295P XBL Loader 阶段 sbl1_main_ctl 函数代码分析 一、XBL Loader 汇编源码分析1.1 解析 boot\QcomPkg\XBLLoader\XBLLoader.inf1.2 boot\QcomPkg\XBLDevPrg\ModuleEntryPoint.S&#xff1a;跳转 sbl1_entry 函数1.3 XBLLoaderLib\sbl1_Aarch64.s…

Communication Channels

沟通渠道 n * (n - 1) / 2 你1 相关方3 4 4 * 3 / 2 6 你1 相关方3 相关方1 5 5 * 4 / 2 10 人越多&#xff0c;沟通渠道越多&#xff0c;沟通成本理论越高

Roxy-Wi 命令执行漏洞复现

漏洞描述 Roxy-WI是开源的一款用于管理 Haproxy、Nginx 和 Keepalived 服务器的 Web 界面 Roxy-WI 6.1.1.0 之前的版本存在安全漏洞,该漏洞源于系统命令可以通过 subprocess_execute 函数远程运行,远程攻击者利用该漏洞可以执行远程代码。 免责声明 技术文章仅供参考,任…

jstack(Stack Trace for Java)Java堆栈跟踪工具

jstack&#xff08;Stack Trace for Java&#xff09;Java堆栈跟踪工具 jstack&#xff08;Stack Trace for Java&#xff09;命令用于生成虚拟机当前时刻的线程快照&#xff08;一般称为threaddump或者javacore文件&#xff09;。 线程快照就是当前虚拟机内每一条线程正在执…

使用预制体画刷在游戏场景中快速布置预制体、粒子特效等

有时候在使用tilemap的时候&#xff0c;会希望在场景中添加更复杂的对象。 在2d-extras中&#xff0c;加入了预制件笔刷&#xff08;Prefab Brush&#xff09;&#xff0c;可以将游戏物体预制体作为瓦片&#xff0c;来方便的在游戏场景中快速的绘制。可以自动适应游戏物体的位置…

GitHub 开启 2FA 双重身份验证的方法

最近收到GitHub官方发来的邮件&#xff0c;全是英文的&#xff0c;如图&#xff1a; 使用邮箱翻译插件&#xff0c;进行翻译&#xff0c;哦&#xff0c;原来是要我进行2FA注册&#xff0c;如果不注册&#xff0c;GitHub的访问将收到限制&#xff0c;所以还是得注册一下 然后怎…

【Redis基础篇】浅谈分布式系统(一)

一、浅谈分布式系统 1. 单机架构&#xff1a;只有一台服务器&#xff0c;这个服务器负责所有的工作。 如果遇到了服务器不够的场景怎么处理? 开源&#xff1a;增加更多的硬件资源节流&#xff1a;软件上的优化&#xff0c;优化代码等…一台服务器资源使用有限&#xff0c;就…

无涯教程-PHP - 常量声明

常量值不能更改。默认情况下&#xff0c;常量区分大小写。按照约定&#xff0c;常量标识符始终为大写。与变量不同&#xff0c;您不需要具有"$"的常量。 constant 函数 如名称所示&#xff0c;此函数将返回常量的值。 当您要检索常量的值但不知道其名称时&#xf…

WebGL的剪裁空间

推荐&#xff1a;使用NSDT场景编辑器助你快速搭建可二次编辑的3D应用场景 什么是WebGL的剪裁空间 WebGL的剪裁空间&#xff08;Clipping Space&#xff09;是在图形渲染过程中处理视图体积裁剪的一种特定空间。它是指定义在3D世界坐标系和屏幕窗口之间的虚拟空间&#xff0c;用…

宝塔部署Java+Vue前后端分离项目经验总结

前言 之前部署服务器都是在Linux环境下自己一点一点安装软件&#xff0c;听说用宝塔傻瓜式部署更快&#xff0c;这次浅浅尝试了一把。 确实简单&#xff01; 1、 买服务器 咋买服务器略&#xff0c;记得服务器装系统就装 Cent OS 7系列即可&#xff0c;我装的7.6。 2、创建…

css 实现svg动态图标效果

效果演示&#xff1a; 实现思路&#xff1a;主要是通过css的stroke相关属性来设置实现的。 html代码: <svgt"1692441666814"class"icon"viewBox"0 0 1024 1024"version"1.1"xmlns"http://www.w3.org/2000/svg"p-id"…