构建 NodeJS 影院预订微服务并使用 docker 部署(03/4)

news2024/9/20 5:07:03

一、说明

        构建一个微服务的电影网站,需要Docker、NodeJS、MongoDB,这样的案例您见过吗?如果对此有兴趣,您就继续往下看吧。

你好社区,这是🏰“构建 NodeJS 影院微服务”系列的第三篇文章。本系列文章演示了如何使用 ES6、¿ES7 ...8?,连接到 MongoDB 副本集,本文还演示了如何将其部署到 docker 容器中,并模拟此微服务在云环境中的运行方式。

二、我们前几章的快速回顾

  • 我们谈论什么是微服务,我们看到了微服务的优点和缺点
  • 我们定义了影院微服务架构。
  • 我们设计和开发了电影服务和电影目录服务。
  • 我们为每个服务制作了一个API,并对我们的API进行了单元测试
  • 我们编写我们的 API 以使其成为服务并将其运行到 Docker 容器中。
  • 我们对在 Docker 上运行的服务进行了集成测试
  • 我们谈论微服务安全性,我们实现HTTP / 2协议。
  • 我们对电影目录服务进行了压力测试

        如果你还没有读过前面的章节,你错过了一些有趣的东西🤘🏽,我会把链接放在下面,所以你可以看看👀。

        在前面的章节中,我们已经实现了下图中的高级子体系结构,我们将在本章中开始开发较差的子体系结构。

此时,最终用户已经可以看到电影院有哪些电影首映,可以选择电影院并请求预订,因此在本文中,我们将继续构建电影院架构,我们将看到预订服务内部发生了什么,所以跟进😎,让我们学习一些有趣的事情。

我们将在本文中使用的是:

  • NodeJS 版本 7.5.0
  • MongoDB 3.4.1
  • Docker for Mac 1.13

跟进文章的先决条件:

  • 已完成上一章中的示例。

如果你还没有,我已经上传了一个 github 存储库,所以你可以在分支步骤 2 上获得最新的存储库链接。

三、NodeJS 中的依赖注入

        到目前为止,我们已经为我们的微服务构建了 2 个 API,但在这些微服务中,我们还没有做太多的配置和这么多的开发,因为它的性质和简单性,但这一刻已经到来,在我们的预订微服务中,我们将看到与其他服务更多的交互,为此我们将需要更多的依赖项来完成分配给此微服务的任务, 但是为了不开始制作一些意大利面条代码,作为优秀的开发人员,我们将跟进一些🍝开发设计模式,为此我们将看到什么是“依赖注入”。

为了实现优秀的设计模式,我们必须很好地理解并应用S.O.L.I.D.原则,我用javascript写了一篇关于这个的文章,所以你可以看一看🤓,看看这个原则是什么,我们如何从中受益。

        在我们开始讨论依赖注入之前,如果您不熟悉它,可以在继续之前观看以下视频。

依赖关系注入是一种软件设计模式,其中一个或多个依赖关系(或服务)被注入或通过引用传递到依赖对象中。

        为什么理解什么是依赖注入很重要?,这很重要,因为它为我们提供了开发模式中的 3 个要点,如下所示:

  • 解耦:依赖注入使我们的模块耦合更少,并且随着它的实现,我们获得了主要的可维护性。
  • 单元测试:通过依赖注入,我们可以为每个模块进行更好的单元测试,我们的代码也会减少错误。
  • 更快的开发:通过依赖注入,在定义接口后,可以轻松工作,没有任何合并冲突。

因此,到目前为止,在我们的微服务中,我们已经在index.js

// more code

mediator.on('db.ready', (db) => {
  let rep
  // here we are making DI to the repository
  // we are injecting the database object and the ObjectID object
  repository.connect({
    db, 
    ObjectID: config.ObjectID
  })
  .then(repo => {
      console.log('Connected. Starting Server')
      rep = repo
      // here we are also making DI to the server
      // we are injecting serverSettings and the repo object
      return server.start({
        port: config.serverSettings.port,
        ssl: config.serverSettings.ssl,
        repo
      })
    })
    .then(app => {
      console.log(`Server started succesfully, running on port: ${config.serverSettings.port}.`)
      app.on('close', () => {
        rep.disconnect()
      })
    })
})

// more code

        我们在文件中所做的是手动DI,因为我们不需要做更多的事情,但是知道在预订服务中,我们需要制定更好的DI方法,让我们看看为什么我们需要它,所以在我们开始构建API之前,让我们弄清楚预订服务需要做什么。index.js

  • 预订服务需要一个预订对象和一个用户对象,在执行预订操作后,我们需要首先验证这些对象。
  • 一旦验证,我们就能够继续开始购买门票的过程。
  • 预订服务需要用户信用卡信息才能通过支付服务购买门票。
  • 成功收费后,我们需要通过通知服务发送通知。
  • 此外,我们需要为用户生成票证,将票证和采购订单ID代码发送回给用户。

        因此,这里的开发任务已经增加了一点,因此代码也会增加,这就是为什么我们需要为DI创建单一事实来源的原因,因为我们将要做更多的功能。

四、构建微服务

        好的,首先让我们看看我们的RAML文件将如何用于预订服务。

#%RAML 1.0
title: Booking Service
version: v1
baseUri: /

types:
  Booking:
    properties:
      city: string
      cinema: string
      movie: string
      schedule: datetime
      cinemaRoom: string
      seats: array
      totalAmount: number


  User:
    properties:
      name: string
      lastname: string
      email: string
      creditcard: object
      phoneNumber?: string
      membership?: number

  Ticket:
    properties:
      cinema: string
      schedule: string
      movie: string
      seat: string
      cinemaRoom: string
      orderId: string


resourceTypes:
  GET:
    get:
      responses:
        200:
          body:
            application/json:
              type: <<item>>

  POST:
    post:
      body:
        application/json:
          type: <<item>>
          type: <<item2>>
      responses:
        201:
          body:
            application/json:
              type: <<item3>>


/booking:
  type:   { POST: {item : Booking, item2 : User, item3: Ticket} }
  description: The booking service need a Booking object that contains all
    the needed information to make a purchase of cinema tickets.
    Needs a user information to make the booking succesfully.
    And returns a ticket object.

  /verify/{orderId}:
    type:  { GET: {item : Ticket} }
    description: This route is for verify orders, and would return all the details
      of a specific purchased by orderid.

        我们定义了 3 个模型对象,即预订、用户票证,因此由于这是我们在本系列中看到的第一个 POST 请求,因此有一个我们尚未使用的 NodeJS 最佳实践,即数据验证。 我从文章“构建漂亮的节点API”中读到了一个很好的引用,其中说了以下内容:

        因此,我们将从这里开始构建我们的预订服务。与上一章一样,我们仍将使用相同的项目结构,但这次我们将进行更多的修改。所以让我们停止谈论🗣理论,让饥饿游戏开始,再次抱歉,所以让乐趣开始让我们做一些“编码!👩🏻‍💻👨🏻‍💻.

        首先,我们需要在名为/srcmodels

booking-service/src $ mkdir models
# Now let's move to the folder and create some files
booking-service/src/models $ touch user.js booking.js ticket.js
# Now is moment to install a new npm package for data validation
npm i -S joi --silent

        好的,现在我们已经准备好了,是时候开始编码我们的模式验证对象了,MongoDB 也内置了一个验证对象,但这里我们需要验证的是对象是完整的,这就是我选择 joi 的原因,joi 也允许我们同时验证数据,所以让我们从 开始,最后从booking.model.jsticket.model.jsuser.model.js

const bookingSchema = (joi) => ({
  bookingSchema: joi.object().keys({
    city: joi.string(),
    schedule: joi.date().min('now'),
    movie: joi.string(),
    cinemaRoom: joi.number(),
    seats: joi.array().items(joi.string()).single(),
    totalAmount: joi.number()
  })
})

module.exports = bookingSchema
const ticketSchema = (joi) => ({
  ticketSchema: joi.object().keys({
    cinema: joi.string(),
    schedule: joi.date().min('now'),
    movie: joi.string(),
    seat: joi.array().items(joi.string()).single(),
    cinemaRoom: joi.number(),
    orderId: joi.number()
  })
})

module.exports = ticketSchema
const userSchema = (joi) => ({
  userSchema: joi.object().keys({
    name: joi.string().regex(/^[a-bA-B]+/).required(),
    lastName: joi.string().regex(/^[a-bA-B]+/).required(),
    email: joi.string().email().required(),
    phoneNumber: joi.string().regex(/^(\+0?1\s)?\(?\d{3}\)?[\s.-]\d{3}[\s.-]\d{4}$/),
    creditCard: joi.string().creditCard().required(),
    membership: joi.number().creditCard()
  })
})

module.exports = userSchema

如果你不知道,你可以在这里查看他们的github文档:链接到文档。joi

现在,让我们对模型进行编码,以公开如下所示的验证函数:index.js

const joi = require('joi')
const user = require('./user.model')(joi)
const booking = require('./booking.model')(joi)
const ticket = require('./ticket.model')(joi)

const schemas = Object.create({user, booking, ticket})

const schemaValidator = (object, type) => {
  return new Promise((resolve, reject) => {
    if (!object) {
      reject(new Error('object to validate not provided'))
    }
    if (!type) {
      reject(new Error('schema type to validate not provided'))
    }

    const {error, value} = joi.validate(object, schemas[type])

    if (error) {
      reject(new Error(`invalid ${type} data, err: ${error}`))
    }
    resolve(value)
  })
}

module.exports = Object.create({validate: schemaValidator})

        因此,我们所做的,我们应用了单一责任,从每个模型都有自己的验证的坚实原则,我们还应用了开-关原则,其中模式验证器函数能够验证我们声明的任意数量的模型,所以让我们看看这个模型的测试文件如何。

/* eslint-env mocha */
const test = require('assert')
const {validate} = require('./')

console.log(Object.getPrototypeOf(validate))

describe('Schemas Validation', () => {
  it('can validate a booking object', (done) => {
    const now = new Date()
    now.setDate(now.getDate() + 1)

    const testBooking = {
      city: 'Morelia',
      cinema: 'Plaza Morelia',
      movie: 'Assasins Creed',
      schedule: now,
      cinemaRoom: 7,
      seats: ['45'],
      totalAmount: 71
    }

    validate(testBooking, 'booking')
      .then(value => {
        console.log('validated')
        console.log(value)
        done()
      })
      .catch(err => {
        console.log(err)
        done()
      })
  })

  it('can validate a user object', (done) => {
    const testUser = {
      name: 'Cristian',
      lastName: 'Ramirez',
      email: 'cristiano@nupp.com',
      creditCard: '1111222233334444',
      membership: '7777888899990000'
    }

    validate(testUser, 'user')
      .then(value => {
        console.log('validated')
        console.log(value)
        done()
      })
      .catch(err => {
        console.log(err)
        done()
      })
  })

  it('can validate a ticket object', (done) => {
    const testTicket = {
      cinema: 'Plaza Morelia',
      schedule: new Date(),
      movie: 'Assasins Creed',
      seats: ['35'],
      cinemaRoom: 1,
      orderId: '34jh1231ll'
    }

    validate(testTicket, 'ticket')
      .then(value => {
        console.log('validated')
        console.log(value)
        done()
      })
      .catch(err => {
        console.log(err)
        done()
      })
  })
})

        下一个要审查的文件将是 在这一点上,我们开始陷入很多麻烦,为什么?,因为在这里我们将与两个外部服务进行交互,支付服务和通知服务,这种交互可以引导我们重新思考微服务的架构, 有一个叫做事件驱动的数据管理和 CQRS,但这些主题将被保存到本系列的后续章节中,并且不会使本章变得冗长和复杂,因此,在此期间,让我们简化与本章服务的交互。api/booking.js

'use strict'
const status = require('http-status')

module.exports = ({repo}, app) => {
  app.post('/booking', (req, res, next) => {
    
    // we grab the dependencies need it for this route
    const validate = req.container.resolve('validate')
    const paymentService = req.container.resolve('paymentService')
    const notificationService = req.container.resolve('notificationService')

    Promise.all([
      validate(req.body.user, 'user'),
      validate(req.body.booking, 'booking')
    ])
    .then(([user, booking]) => {
      const payment = {
        userName: user.name + ' ' + user.lastName,
        currency: 'mxn',
        number: user.creditCard.number,
        cvc: user.creditCard.cvc,
        exp_month: user.creditCard.exp_month,
        exp_year: user.creditCard.exp_year,
        amount: booking.amount,
        description: `
          Tickect(s) for movie ${booking.movie},
          with seat(s) ${booking.seats.toString()}
          at time ${booking.schedule}`
      }

      return Promise.all([
        // we call the payment service
        paymentService(payment),
        Promise.resolve(user),
        Promise.resolve(booking)
      ])
    })
    .then(([paid, user, booking]) => {
      return Promise.all([
        repo.makeBooking(user, booking),
        repo.generateTicket(paid, booking)
      ])
    })
    .then(([booking, ticket]) => {
      // we call the notification service
      notificationService({booking, ticket})
      res.status(status.OK).json(ticket)
    })
    .catch(next)
  })

  app.get('/booking/verify/:orderId', (req, res, next) => {
    repo.getOrderById(req.params.orderId)
      .then(order => {
        res.status(status.OK).json(order)
      })
      .catch(next)
  })
}

        正如你在这里看到的,我们正在使用 expressjs 中间件,我们正在利用从单一事实来源注册依赖项容器

        但是DI容器在哪里?

        好吧,我们对项目结构进行了一些更改,主要是在文件夹中,现在如下所示:config

. 
|-- config 
|   |-- db 
|   |   |-- index.js 
|   |   |-- mongo.js 
|   |   `-- mongo.spec.js 
|   |-- di 
|   |   |-- di.js 
|   |   `-- index.js 
|   |-- ssl
|   |   |-- certificates 
|   |   `-- index.js
|   |-- config.js
|   |-- index.spec.js 
|   `-- index.js

        在文件中,我们主要包括所有配置以及 DI 服务:config/index.js

const {dbSettings, serverSettings} = require('./config')
const database = require('./db')
const {initDI} = require('./di')
const models = require('../models')
const services = require('../services')
const init = initDI.bind(null, {serverSettings, dbSettings, database, models, services})
module.exports = Object.assign({}, {init})

        在上面的代码中,我们看到了一些罕见的东西,让我再次为您缩放它:

initDI.bind(null, {serverSettings, dbSettings, database, models, services})

        我们在这里做什么?,我说我们正在配置 DI,但在这里我们正在制作一种叫做控制反转的东西,是的,是的,我知道这是很多技术术语,可能听起来很臃肿,但它很容易理解,一旦你得到它,如果你还没有听说过 IoC我建议你观看以下视频:

        所以我们的 DI 函数不需要知道我们的依赖项来自哪里,它只需要注册我们的依赖项即可在我们的应用程序中使用,所以我们的文件如下所示:di.js

const { createContainer, asValue, asFunction, asClass } = require('awilix')

function initDI ({serverSettings, dbSettings, database, models, services}, mediator) {
  mediator.once('init', () => {
    mediator.on('db.ready', (db) => {
      const container = createContainer()
      
      // loading dependecies in a single source of truth
      container.register({
        database: asValue(db).singleton(),
        validate: asValue(models.validate),
        booking: asValue(models.booking),
        user: asValue(models.booking),
        ticket: asValue(models.booking),
        ObjectID: asClass(database.ObjectID),
        serverSettings: asValue(serverSettings),
        paymentService: asValue(services.paymentService),
        notificationService: asValue(services.notificationService)
      })
      
      // we emit the container to be able to use it in the API
      mediator.emit('di.ready', container)
    })

    mediator.on('db.error', (err) => {
      mediator.emit('di.error', err)
    })

    database.connect(dbSettings, mediator)

    mediator.emit('boot.ready')
  })
}

module.exports.initDI = initDI

        如您所见,我们正在使用一个名为依赖注入的 npm 包,awilix 在 nodejs 中实现了依赖注入的机制(我目前正在评估这个库,但我在这里使用它来说明示例),所以要安装它,我们需要执行下一个命令:awilix

npm i -S awilix --silent

        要进一步了解 awilix 的工作原理,您可以查看作者在以下链接中撰写的依赖注入系列文章:DI 系列和 awilix 文档。

        现在或主文件将如下所示:index.js

'use strict'
const {EventEmitter} = require('events')
const server = require('./server/server')
const repository = require('./repository/repository')
const di = require('./config')
const mediator = new EventEmitter()

console.log('--- Booking Service ---')
console.log('Connecting to movies repository...')

process.on('uncaughtException', (err) => {
  console.error('Unhandled Exception', err)
})

process.on('uncaughtRejection', (err, promise) => {
  console.error('Unhandled Rejection', err)
})

mediator.on('di.ready', (container) => {
  repository.connect(container)
    .then(repo => {
      container.registerFunction({repo})
      return server.start(container)
    })
    .then(app => {
      app.on('close', () => {
        container.resolve('repo').disconnect()
      })
    })
})

di.init(mediator)

mediator.emit('init')

        正如你现在看到的,我们只使用一个单一的事实来源,它有我们需要的所有依赖项,可以通过容器请求它,那么我们如何将其设置为expressjs中间件,就像之前注释的那样,它只是几行代码:

const express = require('express')
const morgan = require('morgan')
const helmet = require('helmet')
const bodyparser = require('body-parser')
const cors = require('cors')
const spdy = require('spdy')
const _api = require('../api/booking')

const start = (container) => {
  return new Promise((resolve, reject) => {
    
    // here we grab our dependencies needed for the server
    const {repo, port, ssl} = container.resolve('serverSettings')

    if (!repo) {
      reject(new Error('The server must be started with a connected repository'))
    }
    if (!port) {
      reject(new Error('The server must be started with an available port'))
    }

    const app = express()
    app.use(morgan('dev'))
    app.use(bodyparser.json())
    app.use(cors())
    app.use(helmet())
    app.use((err, req, res, next) => {
      if (err) {
        reject(new Error('Something went wrong!, err:' + err))
        res.status(500).send('Something went wrong!')
      }
      next()
    })
    
    // here is where we register the container as middleware
    app.use((req, res, next) => {
      req.container = container.createScope()
      next()
    })
    
    // here we inject the repo to the API, since the repo is need it for all of our functions
    // and we are using inversion of control to make it available
    const api = _api.bind(null, {repo: container.resolve('repo')})
    api(app)

    if (process.env.NODE === 'test') {
      const server = app.listen(port, () => resolve(server))
    } else {
      const server = spdy.createServer(ssl, app)
        .listen(port, () => resolve(server))
    }
  })
}

        所以基本上我们将容器对象附加到 expressjs req 对象,这就是我们如何通过所有 expressjs 路由使用它。如果你想更深入地了解中间件如何与 expressjs 一起工作,你可以访问此链接并查看 expressjs 文档。

嗯,有句话说,越好越好,最后我们要审查文件:repository.js

'use strict'
const repository = (container) => {
  // we get the db object via the container
  const {db} = container.resolve('database')

  const makeBooking = (user, booking) => {
    return new Promise((resolve, reject) => {
      // payload to be insterted to the booking collection 
      const payload = {
        city: booking.city,
        cinema: booking.cinema,
        book: {
          userType: (user.membership) ? 'loyal' : 'normal',
          movie: {
            title: booking.movie.title,
            format: booking.movie.format,
            schedule: booking.schedule
          }
        }
      }

      db.collection('booking').insertOne(payload, (err, booked) => {
        if (err) {
          reject(new Error('An error occuered registring a user booking, err:' + err))
        }
        resolve(booked)
      })
    })
  }

  const generateTicket = (paid, booking) => {
    return new Promise((resolve, reject) => {
      // payload of ticket
      const payload = Object.assign({}, {booking, orderId: paid._id})
      db.collection('tickets').insertOne(payload, (err, ticket) => {
        if (err) {
          reject(new Error('an error occured registring a ticket, err:' + err))
        }
        resolve(ticket)
      })
    })
  }

  const getOrderById = (orderId) => {
    return new Promise((resolve, reject) => {
      const ObjectID = container.resolve('ObjectID')
      const query = {_id: new ObjectID(orderId)}
      const response = (err, order) => {
        if (err) {
          reject(new Error('An error occuered retrieving a order, err: ' + err))
        }
        resolve(order)
      }
      db.collection('booking').findOne(query, {}, response)
    })
  }

  const disconnect = () => {
    db.close()
  }

  return Object.create({
    makeBooking,
    getOrderById,
    generateTicket,
    disconnect
  })
}

const connect = (container) => {
  return new Promise((resolve, reject) => {
    if (!container.resolve('database')) {
      reject(new Error('connection db not supplied!'))
    }
    resolve(repository(container))
  })
}

module.exports = Object.assign({}, {connect})

        好的,所以在我们的相关性上没有太多的相关性,可能是我们第一次在系列中使用该方法,但是我想在这个文件中指出一件事,特别是在方法上,如果您看到有效负载对象,这是集合数据模型模式,但为什么? 为什么我们会使用这种方法,如果我们使用它,我们不是会重复很多信息吗?repository.jsinsertOne()makeBooking()

        嗯,是的,我们将重复信息,这不是最佳做法,但这是有原因的,直到下次🐾我才会告诉你,为什么是因为该系列有一些非常有趣的东西......

        如果你想要一个提示,我会留下这个给你好奇 😁

   ----------------------------------------
  |                                        |
  |                                        v
  |                       Jane  ------(went to)----------
  |                         |                            |
  |                         | (loyal vistor)             |
  |                         v                            v
 Joe --(normal visitor)--> Movie Name <--(displayed)-- Plaza Morelia
                             |                           |
                             |  (format)                 | (city)
                             v                           v
                            4DX                       Morelia

        如果您能发现即将发生的事情,欢迎您在评论部分发表评论。

        好吧,让我们继续,我们已经评论说我们正在与两个外部服务进行交互,为简单起见,让我们看看我们需要从这些外部服务中获得什么

# for the payment service we will need to implement something like the following
module.exports = (paymentOrder) => {
  return new Promise((resolve, reject) => {
    supertest('url to the payment service')
      .get('/makePurchase')
      .send({paymentOrder})
      .end((err, res) => {
        if (err) {
          reject(new Error('An error occured with the payment service, err: ' + err))
        }
        resolve(res.body.payment)
      })
  })
}
# since we haven't made the payment service yet, let's make something simple to fulfill the article example, like the following    
module.exports = (paymentOrder) => {
  return new Promise((resolve, reject) => {
    resolve({orderId: Math.floor((Math.random() * 1000) + 1)})
  })
}
# for the notification service, at the moment we don't need any information from this service we will not implement it, this service will have the task for sending an email, sms or another notification, but we will make this service in the next chapter.

        好吧,我们已经完成了这个微服务的构建,所以,现在是时候使用以下命令在存储库中执行文件了:

$ bash < start_service

        让我们的微服务准备就绪并完全正常运行到 docker 容器中,并开始进行集成测试

五、是时候回顾一下了

        我们做了什么...?如果您遵循了我之前的章节,我们有一个如下系统架构:

影院系统架构

        如果你注意到我们的系统开始成形,但有些东西让我们感觉不对,那就是在工作线程 1 和工作线程 2 中,我们没有任何微服务运行,那是因为我们没有在其中创建任何服务,但我们很快就会这样做。docker-machines

        现在在影院微服务架构中,我们几乎完成了下图:

我们只是构建预订服务,然后简单实现支付服务和通知服务。

因此,我们在本章🤔中学习了依赖注入,我们看到了一点 SOLID 原则和控制反转,使用 NodeJS,我们还在微服务中发出了第一个 POST 请求,我们还学习了如何使用 joi 库验证对象和数据。

我们已经在 NodeJS 中看到了很多开发,但我们可以做和学习的东西还有很多,这只是一个先睹为快的高峰。我希望这已经展示了一些有趣和有用的东西,你可以在你的工作流程中用于Docker和NodeJS

六、即将推出

        在接下来的剧集中,我们将创建并完成支付服务和通知服务的实现,但这不是有趣的部分,有趣的是我们将创建我们的 API 网关,因为我们的影院微服务开始增长并且微服务有必要相互通信。但是要拥有一个非常强大的微服务系统还有很多事情要做,在后面的章节中,我们将看到如何使十二因素应用程序适应微服务。

# 在 Github 上完成代码

您可以在以下链接中查看文章的完整代码。克里斯蒂安·拉米雷斯

·

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Ae 效果:CC Line Sweep

过渡/CC Line Sweep Transition/CC Line Sweep CC Line Sweep&#xff08;CC 线条扫描&#xff09;通过线条的扫描动作来制作一个过渡效果&#xff0c;线条可以根据设定的方向、厚度、倾斜度和方向翻转来扫过画面&#xff0c;从而将一个场景过渡到另一个。 ◆ ◆ ◆ 效果属性…

数据治理核心知识点总结-1

管理数据的人员必须由业务人员和信息技术人员两类角色共同承担&#xff1b; 数据管理的12个原则&#xff0c;这12个原则分为数据管理原则、数据价值、数据管理需求是业务的需求、数据管理依赖于不同的技能&#xff0c;数据管理是生命周期管理5个方面&#xff1b;这5个方面中&am…

6-3 使用函数输出水仙花数

分数 20 全屏浏览题目 切换布局 作者 张高燕 单位 浙大城市学院 水仙花数是指一个N位正整数&#xff08;N≥3&#xff09;&#xff0c;它的每个位上的数字的N次幂之和等于它本身。例如&#xff1a;153135333。 本题要求编写两个函数&#xff0c;一个判断给定整数是否水仙花数…

深度学习-实验1

一、Pytorch基本操作考察&#xff08;平台课专业课&#xff09; 使用&#x1d413;&#x1d41e;&#x1d427;&#x1d42c;&#x1d428;&#x1d42b;初始化一个 &#x1d7cf;&#x1d7d1;的矩阵 &#x1d474;和一个 &#x1d7d0;&#x1d7cf;的矩阵 &#x1d475;&am…

node配合charles写文件并下载

利用charles的Copy URL 和Copy cURL Request 进行抓取喜马拉雅的m4a文件 将抓取的下载链接粘贴进txt,利用如下代码去重,并生成新的sh文件,执行sh文件即可下载 const fs require("fs");//need config const inputFile "input.txt"; const outputFile &q…

JCTools Mpsc源码详解(一)

Jctools介绍--jctools是一个Java开源并发非阻塞数据结构实现,其中主要实现了非阻塞Map和非阻塞queue,旨在为Java提供高性能并发数据结构实现; jctool的特点--为什么性能高: lazyset--putOrderedObject,使用loadload内存屏障,写不会立即可见大量的位运算伪共享-通过pad类实现内…

嵌入式系统中常见内存的划分方法

看到有小伙伴在讨论关于单片机内存的话题&#xff0c;今天就结合STM32给大家描述一下常见的划分区域。 在一个STM32程序代码中&#xff0c;从内存高地址到内存低地址&#xff0c;依次分布着栈区、堆区、全局区&#xff08;静态区&#xff09;、常量区、代码区&#xff0c;其中全…

npm install 安装依赖,报错 Host key verification failed

设置 git 的身份和邮箱 git config --global user.name "你的名字" > 用户名 git config --global user.email “你的邮箱" > 邮箱进入 > 用户 > [你的用户名] > .ssh文件夹下,删除 known_hosts 文件即可 进入之后有可能会看到 known_hosts…

亿赛通电子文档安全管理系统 RCE漏洞

亿赛通电子文档安全管理系统 RCE漏洞 一、 产品简介二、 漏洞概述三、 复现环境四、 漏洞复现小龙POC检测: 五、 修复建议 免责声明&#xff1a;请勿利用文章内的相关技术从事非法测试&#xff0c;由于传播、利用此文所提供的信息或者工具而造成的任何直接或者间接的后果及损失…

【学会动态规划】 最长递增子序列(26)

目录 动态规划怎么学&#xff1f; 1. 题目解析 2. 算法原理 1. 状态表示 2. 状态转移方程 3. 初始化 4. 填表顺序 5. 返回值 3. 代码编写 写在最后&#xff1a; 动态规划怎么学&#xff1f; 学习一个算法没有捷径&#xff0c;更何况是学习动态规划&#xff0c; 跟我…

【Django】 Task5 DefaultRouter路由组件和自定义函数

文章目录 【Django】 Task5 DefaultRouter路由组件和自定义函数1.路由组件1.1路由组件介绍1.2SimpleRouter1.3DefaultRouter1.4DefaultRouter示例1.5查看访问服务接口url 2.自定义函数 【Django】 Task5 DefaultRouter路由组件和自定义函数 Task5 主要了解了DefaultRouter路由…

如何使用LLM实现文本自动生成视频

推荐&#xff1a;使用 NSDT场景编辑器 助你快速搭建可二次编辑的3D应用场景 介绍 基于扩散的图像生成模型代表了计算机视觉领域的革命性突破。这些进步由Imagen&#xff0c;DallE和MidJourney等模型开创&#xff0c;展示了文本条件图像生成的卓越功能。有关这些模型内部工作的…

SpringMVC 反射型跨站点脚本攻击

解决方案&#xff1a; 服务端校验&#xff0c;添加拦截器 配置web,xml <filter><filter-name>xssFilter </filter-name><filter-class>com.fh.filter.XssFilter </filter-class></filter> XssFilter package com.fh.filter;import com…

【C++】运算符重载 | 赋值运算符重载

Ⅰ. 运算符重载 引入 ❓什么叫运算符重载&#xff1f; 就是&#xff1a;运用函数&#xff0c;将现有的运算符重新定义&#xff0c;使其能满足各种自定义类型的运算。 回想一下&#xff0c;我们以前运算的对象是不是都是int、char这种内置类型&#xff1f; 那我们自定义的“…

编写Dockerfile制作Web应用系统nginx镜像,生成镜像nginx:v1.1,并推送其到私有仓库

Docker 镜像是一个特殊的文件系统&#xff0c;除了提供容器运行时所需的程序、库、资源、配置等文件外&#xff0c;还包含了一些为运行时准备的一些配置参数&#xff08;如匿名卷、环境变量、用户等&#xff09;。镜像不包含任何动态数据&#xff0c;其内容在构建之后也不会被改…

存储系统性能优化中IOMMU的作用是什么?

一、IOMMU原理 IOMMU(Input/Output Memory Management Unit)是一种用于管理计算机内存的技术,它允许将物理内存映射到虚拟地址空间。IOMMU通过使用专用的硬件来管理和优化内存访问,从而提高系统性能和稳定性。本文将详细介绍IOMMU的原理,并介绍一些应用案例和典型的问题解…

Android13新特性之通知权限提升

Android13新特性之通知权限提升 随着移动通信的高速发展&#xff0c;保障通信的安全性变得尤为重要。在Android 13的最新版本中&#xff0c;通知权限的管理得到了进一步加强。为了实现安全的通信和确保用户的隐私&#xff0c;必须正确申请通知权限。本文将详细探讨如何在Andro…

时空智友企业流程化管控系统文件上传漏洞复现

0x01 产品简介 时空智友企业流程化管控系统是一个功能丰富、灵活可定制的企业管理工具。通过该系统&#xff0c;企业能够实现流程的自动化、协同的提升、数据的洞察和决策的优化&#xff0c;从而提高工作效率、管理水平和企业竞争力。 0x02 漏洞概述 时空智友企业流程化管控系…

Embedding 向量生成GPT数据使用相关

如果使用python3.6的版本&#xff0c;使用pycharm创建工程&#xff0c;那么默认会使用 docx包&#xff0c;这样运行程序会爆异常&#xff0c;突然想起以前请教的一个大神&#xff0c;想当 初&#xff0c;这个问题困扰了我 两天时间&#xff0c;在此记录一下&#xff1a; pytho…

ReactiveApi

reactivity api: https://v3.vuejs.org/api/reactivity-api 1. 获取响应式数据 API传入返回备注reactiveplain-object对象代理深度代理对象中的所有成员readonlyplain-object or proxy对象代理只能读取代理对象中的成员&#xff0c;不可修改refany{ value: ... }对value的访问…