当前位置: 首页 > news >正文

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

一、说明

        构建一个微服务的电影网站,需要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 codemediator.on('db.ready', (db) => {let rep// here we are making DI to the repository// we are injecting the database object and the ObjectID objectrepository.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 objectreturn 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: stringcinema: stringmovie: stringschedule: datetimecinemaRoom: stringseats: arraytotalAmount: numberUser:properties:name: stringlastname: stringemail: stringcreditcard: objectphoneNumber?: stringmembership?: numberTicket:properties:cinema: stringschedule: stringmovie: stringseat: stringcinemaRoom: stringorderId: stringresourceTypes: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 allthe 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 detailsof 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 routeconst 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 servicepaymentService(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 servicenotificationService({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 truthcontainer.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 APImediator.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 serverconst {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 middlewareapp.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 availableconst 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 containerconst {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 ticketconst 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                            vJoe --(normal visitor)--> Movie Name <--(displayed)-- Plaza Morelia|                           ||  (format)                 | (city)v                           v4DX                       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 上完成代码

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

·

相关文章:

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

一、说明 构建一个微服务的电影网站&#xff0c;需要Docker、NodeJS、MongoDB&#xff0c;这样的案例您见过吗&#xff1f;如果对此有兴趣&#xff0c;您就继续往下看吧。 你好社区&#xff0c;这是&#x1f3f0;“构建 NodeJS 影院微服务”系列的第三篇文章。本系列文章演示了…...

html写一个向flask_socketio发送消息和接收消息并显示在页面上

以下是一个简单的HTML页面&#xff0c;它包含一个输入框、一个发送按钮和一个显示区域。用户可以在输入框中输入消息&#xff0c;点击发送按钮&#xff0c;然后这个消息会被发送到 Flask-SocketIO 服务器。当服务器回应消息时&#xff0c;它会在页面的显示区域显示出来。 <…...

C#使用.Net Core进行跨平台开发

使用 .NET Core 进行跨平台开发是一种灵活的方法&#xff0c;可以在多个操作系统上运行 C# 应用程序。以下是在 C# 中使用 .NET Core 进行跨平台开发的一般步骤&#xff1a; 安装 .NET Core SDK&#xff1a; 在开始之前&#xff0c;需要安装适用于操作系统的 .NET Core SDK。可…...

Java“牵手”天猫店铺所有商品API接口数据,通过店铺ID获取整店商品详情数据,天猫API申请指南

天猫商城是一个网上购物平台&#xff0c;售卖各类商品&#xff0c;包括服装、鞋类、家居用品、美妆产品、电子产品等。天猫商品详情可以帮助消费者更好的了解宝贝信息&#xff0c;从而做出购买决策。同时&#xff0c;消费者也可以通过商品详情了解其他买家对宝贝的评价&#xf…...

php输入post过滤函数,入库出库,显示

第一部分 php输入post过滤函数 function GLOBAL_POST($str) {$str_origin$str; if (empty($str)) return false;$str str_replace( /, "", $str);//替换关键词 $str str_replace("\\", "", $str); $str str_replace("&gt", &…...

matlab-对数据集加噪声并实现tsne可视化

matlab-对数据集加噪声并实现tsne可视化 最近才知道&#xff0c;原来可以不用模型&#xff0c;也能实现对数据集数据的可视化。 **一、**以COIL-100数据集为例子。 问题&#xff1a; 前提&#xff1a;首先对COIL-100数据集根据角度0-175和180-255&#xff0c;分别划分成C1,C…...

【BASH】回顾与知识点梳理(三十八)

【BASH】回顾与知识点梳理 三十八 三十八. 源码概念及简单编译38.1 开放源码的软件安装与升级简介什么是开放源码、编译程序与可执行文件什么是函式库什么是 make 与 configure什么是 Tarball 的软件如何安装与升级软件 38.2 使用传统程序语言进行编译的简单范例单一程序&#…...

Sql注入攻击的三种方式

SQL注入是指web应用程序对用户输入数据的合法性没有判断或过滤不严,攻击者可以在web应用程序中事先定义好的查询语句的结尾上添加额外的SQL语句,在管理员不知情的情况下实现非法操作,以此来实现欺骗数据库服务器执行非授权的任意查询,从而进一步得到相应的数据信息。SQL 注…...

dockerfile部署前端vue打包的dist文件实战

背景&#xff1a;一般前端开发后会将打包后的dist文件交由我们部署&#xff0c;部署的方式有很多&#xff0c;这里提供一种思路 在服务器的路径下新建一个目录&#xff0c;在目录中新建Dockerfile&#xff0c;编辑这个文件 FROM nginxCOPY ./dist /home/front COPY nginx.con…...

[技术杂谈]MobaXterm中文乱码编码问题一种解决方法

今日使用mobaxterm连接树莓派发现安装出现乱码&#xff0c;看不清文字是什么。最最简单方式是ssh设置终端字体&#xff0c;具体步骤为&#xff1a; 1. 右键会话&#xff0c;点击编辑会话 2.在以下画面点击终端字体设置 3.选择编码&#xff1a;GBK或者ISO-8859-1...

mac os M1 安装并启动 postgreSQL 的问题

Homebrew 安装 postgreSQL brew install postgresql启动 brew services start postgresql但报错&#xff1a; uninitialized constant Homebrew::Service::System解决方案 brew doctor按照 brew doctor 中的建议进行操作&#xff0c;如果不行&#xff0c;如下&#xff1a; h…...

如何使用Wireshark进行网络流量分析?

如何使用Wireshark进行网络流量分析。Wireshark是一款强大的网络协议分析工具&#xff0c;可以帮助我们深入了解网络通信和数据流动。 1. 什么是Wireshark&#xff1f; Wireshark是一个开源的网络协议分析工具&#xff0c;它可以捕获并分析网络数据包&#xff0c;帮助用户深入…...

抖音web主页视频爬虫

需要抖音主页视频爬虫源码的发私信&#xff0c;小偿即可获得长期有效的采集程序。 比构造 s_v_web_id 验证滑块的方法更快&#xff0c;更稳定。...

常用的jar包【maven坐标格式】

文章目录 &#x1f412;个人主页&#x1f3c5;JavaEE系列专栏&#x1f4d6;前言&#xff1a;&#x1f3e8;mysql加载启动项jar包&#x1f993;mybatis加载启动项jar包 &#x1f412;个人主页 &#x1f3c5;JavaEE系列专栏 &#x1f4d6;前言&#xff1a; 本篇博客主要以介绍常…...

【分布式】VMware FT概要

讨论了1primary 1backup的情况&#xff0c;比较好理解&#xff0c; 6.824中以该论文为例&#xff0c;介绍了分布式系统中复制的概念&#xff0c;复制的方式。以下简要讲述一些关键点&#xff0c;来源于MIT6.824课程&#xff0c;翻译版本 https://mit-public-courses-cn-transl…...

江西南昌电气机械三维测量仪机械零件3d扫描-CASAIM中科广电

精密机械零部件是指机械设备中起到特定功能的零件&#xff0c;其制造精度要求非常高。这些零部件通常由金属、塑料或陶瓷等材料制成&#xff0c;常见的精密机械零部件包括齿轮、轴承、螺丝、活塞、阀门等。精密机械零部件的制造需要高精度的加工设备和工艺&#xff0c;以确保其…...

MySQL三大日志(binlog、redo log和undo log)详解

1.redo log redo log是InnoDB存储引擎层的日志&#xff0c;又称重做日志文件。 用于记录事务操作的变化&#xff0c;记录的是数据修改之后的值&#xff0c;不管事务是否提交都会记录下来 redo log包括两部分&#xff1a;一个是内存中的日志缓冲(redo log buffer)&#xff0c;另…...

七大排序算法详解

1.概念 1.排序的稳定性 常见的稳定的排序有三种&#xff1a;直接插入排序&#xff0c;冒泡排序&#xff0c;归并排序 对于一组数据元素排列&#xff0c;使用某种排序算法对它进行排序&#xff0c;若相同数据之间的前后位置排序后和未排序之前是相同的&#xff0c;我们就成这种…...

[docker][WARNING]: Empty continuation line found in:

报警内容&#xff1a; 下面展示一些 内联代码片。 //执行 sudo docker build ubuntu:v1.00 . [WARNING]: Empty continuation line found in:出现上述错误原因为18行多了一个 " \" 符号&#xff0c;去除即可...

探工业互联网的下一站!腾讯云助力智造升级

引言 数字化浪潮正深刻影响着传统工业形态。作为第四次工业革命的重要基石&#xff0c;工业互联网凭借其独特的价值快速崛起&#xff0c;引领和推动着产业变革方向。面对数字化时代给产业带来的机遇与挑战&#xff0c;如何推动工业互联网的规模化落地&#xff0c;加速数字经济…...

SpringBoot上传文件的实现与优化

一、什么是文件上传&#xff1f; 文件上传是指客户端将本地的文件通过HTTP协议发送到服务器端的过程。文件上传是Web开发中常见的功能之一&#xff0c;例如用户可以上传头像、照片、视频、文档等各种类型的文件。文件上传涉及到客户端和服务器端的交互&#xff0c;需要考虑文件…...

学习python可以做什么?有前景么

Python被热门领域广泛应用 学习者就业优势明显&#xff01; 说到Python的优势&#xff0c;就不得不提这句玩笑话&#xff1a;Python除了不会生孩子&#xff0c;其他的都会。 Web开发、网络爬虫、数据分析、人工智能、自动化、云计算、网络编程、游戏开发等领域&#xff0c;统…...

还不知道怎么提示LLM?ChatGPT提示入门

文章目录 简介&#xff1a;什么是人工智能&#xff1f;什么是提示过程&#xff1f;为什么会出现这样的差异&#xff1f; 为什么需要提示过程&#xff1f;1) 文章摘要2) 数学问题求解 如何进行提示过程&#xff1f;角色提示&#xff1a;多范例提示&#xff1a;无范例提示单范例提…...

反射机制-体会反射的动态性案例(尚硅谷Java学习笔记)

// 举例01 public class Reflect{ // 静态性 public Person getInstance(){return new Person(); }// 动态性 public T<T> getInstance(String className) throws Exception{Calss clzz Class.forName(className);Constructor con class.getDeclaredConstructor();con…...

uniapp离线打包apk - Android Studio

uniapp 离线打包 基于uni-app的andiord 离线打包 开发工具及所需要的jar包​1.将下载的App离线SDK解压打开&#xff0c;找到HBuilder-Integrate-AS &#xff0c;在Android Studio打开2.打开HBuilder X&#xff0c;发行->原生app本地打包->生成本地打包app资源3.在“HBuil…...

cuda面试准备(一),架构调试

1 cuda架构 硬件方面 SP (streaming Process) ,SM (streaming multiprocessor) 是硬件(GPUhardware) 概念。而thread,block,grid,warp是软件上的(CUDA) 概念 SP:最基本的处理单元,streaming processor,也称为CUDA core,最后具体的指令和任务都是在SP上处理的。GPU进行并行…...

docker containers logs清理

容器的磁盘占用 每次创建一个容器时&#xff0c;都会有一些文件和目录被创建&#xff0c;例如&#xff1a; /var/lib/docker/containers/ID目录&#xff0c;如果容器使用了默认的日志模式&#xff0c;他的所有日志都会以JSON形式保存到此目录下。 /var/lib/docker/overlay2 目…...

Ubuntu安装RabbitMQ

一、安装 更新系统软件包列表&#xff1a; sudo apt update安装RabbitMQ的依赖组件和GPG密钥&#xff1a; sudo apt install -y curl gnupg curl -fsSL https://github.com/rabbitmq/signing-keys/releases/download/2.0/rabbitmq-release-signing-key.asc | sudo gpg --dearmo…...

Vue3获取当前环境信息

获取.env.development和.env.production内的信息及环境信息 在业务逻辑文件中可以通过 import.meta.env的方式获取&#xff0c;例如&#xff1a; const { MODE, VITE_APP_BASE_API} import.meta.env在vite.config.js中获取&#xff1a; import { defineConfig, loadEnv } f…...

Linux 系统 diff 文件比较命令详解

diff 命令用于比较两个文件或目录之间的差异。它会逐行比较文件的内容&#xff0c;并且在不同的行上显示不同之处。下面是 diff 命令的使用方法和选项&#xff1a; 基本语法&#xff1a; diff [选项] 文件1 文件2常见选项&#xff1a; -c 或 --context&#xff1a;显示上下文…...

【负载均衡】Nacos简单入门

Nacos简单入门 快速安装 在Nacos的GitHub页面&#xff0c;提供有下载链接&#xff0c;可以下载编译好的Nacos服务端或者源代码&#xff1a; 下载完压缩包之后&#xff0c;放在任意目录下面进行解压&#xff1a; GitHub主页&#xff1a;https://github.com/alibaba/nacos G…...

实验一 ubuntu 网络环境配置

ubuntu 网络环境配置 【实验目的】 掌握 ubuntu 下网络配置的基本方法&#xff0c;能够通过有线网络连通 ubuntu 和开发板 【实验环境】 ubuntu 14.04 发行版FS4412 实验平台 【注意事项】 实验步骤中以“$”开头的命令表示在 ubuntu 环境下执行&#xff0c;以“#”开头的…...

ubuntu can应用开发环境搭建指南

sudo apt-get update sudo apt-get install can-utils libsocketcan-dev can数据发送这个采用来自网上的一段代码进行测试: can_send.c代码内容如下: /* 1. 报文发送程序 */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <…...

全流程R语言Meta分析核心技术高阶应用

查看原文>>>全流程R语言Meta分析核心技术高阶应用 目录 专题一、Meta分析的选题与检索 专题二、Meta分析与R语言数据清洗及统计方法 专题三、R语言Meta分析与作图 专题四、R语言Meta回归分析 专题五、R语言Meta诊断分析 专题六、R语言Meta分析的不确定性 专题…...

windows下安装使用git-lfs克隆大文件

下载安装git-lfs工具 首先去git-lfs这里&#xff0c;下载相应平台的工具&#xff0c;我下载的windows版本&#xff0c;非安装版本&#xff0c;直接配置到系统环境变量里 执行以下命令验证是否成功 git lfs install 克隆数据集 这样自动会下载里边的大文件 git clone https:/…...

Node.js下载安装及环境配置教程

一、进入官网地址下载安装包 https://nodejs.org/zh-cn/download/ 选择对应你系统的Node.js版本&#xff0c;这里我选择的是Windows系统、64位 Tips&#xff1a;如果想下载指定版本&#xff0c;点击【以往的版本】&#xff0c;即可选择自己想要的版本下载 二、安装程序 &a…...

半导体低压热氧工艺中的真空度精密控制解决方案

摘要&#xff1a;在目前的各种半导体材料热氧化工艺中&#xff0c;往往需要对正负压力进行准确控制并对温度变化做出快速的响应&#xff0c;为此本文提出了热氧化工艺的正负压力控制解决方案。解决方案的核心是基于动态平衡法分别对进气和排气流量进行快速调节&#xff0c;具体…...

TCP的可靠性之道:确认重传和流量控制

TCP 全称为 Transmission Control Protocol&#xff08;传输控制协议&#xff09;&#xff0c;是一种面向连接的、可靠的、基于字节流的传输层通信协议&#xff0c;其中可靠性是相对于其他传输协议的优势点。TCP 为了确保数据传输的可靠性主要做了以下几点&#xff1a; 发送确…...

基于spring boot校园疫情信息管理系统/疫情管理系统

摘要 随着计算机技术&#xff0c;网络技术的迅猛发展&#xff0c;Internet 的不断普及&#xff0c;网络在各个领域里发挥了越来越重要的作用。特别是随着近年人民生活水平不断提高&#xff0c;校园疫情信息管理系统给学校带来了更大的帮助。 由于当前疫情防控形势复杂&#xff…...

使用Python批量将飞书文档转为MD

说明&#xff1a;飞书是在线文档平台&#xff0c;本文介绍如何使用Python程序批量将飞书文档转为MD文档&#xff0c;并下载到本地&#xff1b; 复制地址 首先&#xff0c;把文档的URL都复制下来&#xff0c;这个需要一个一个点&#xff0c;并复制拷贝&#xff0c;但却是工作量…...

Nacos配置管理、Feign远程调用、Gateway服务网关

1.Nacos配置管理 1.1.将配置交给Nacos管理的步骤 1.在Nacos中添加配置 Data Id服务名称-环境名称.yaml eg&#xff1a;userservice-dev.yaml 2.引入nacos-config依赖 在user-service服务中&#xff0c;引入nacos-config的客户端依赖 <!--nacos配置管理依赖--> <dep…...

解决Spring Boot前后端分离开发模式中的跨域问题

在实际开发中&#xff0c;经常会遇到前端Vue应用与后端Spring Boot API接口存在跨域访问的问题。本篇博客将分享解决Spring Boot前端Vue跨域问题的实战经验&#xff0c;帮助开发者快速解决该问题。 一、跨域问题的原因 跨域问题是由于浏览器的同源策略引起的。同源策略限制了…...

常见前端面试之VUE面试题汇总五

13. assets 和 static 的区别 相同点&#xff1a; assets 和 static 两个都是存放静态资源文件。项目中所 需要的资源文件图片&#xff0c;字体图标&#xff0c;样式文件等都可以放在这两个文件 下&#xff0c;这是相同点 不相同点&#xff1a;assets 中存放的静态资源文件在…...

带着问题看SpringBoot

带着问题看SpringBoot 1、Spring容器具体是什么&#xff1f; 跟进run方法&#xff0c;context this.createApplicationContext()&#xff0c;得出容器是AnnotationConfigServletWebServerApplicationContext类。 SpringApplication.run(ServeroneApplication.class, args);…...

【Go 基础篇】Go语言匿名函数详解:灵活的函数表达式与闭包

介绍 在Go语言中&#xff0c;函数是一等公民&#xff0c;这意味着函数可以像其他类型的值一样被操作、传递和赋值。匿名函数是一种特殊的函数&#xff0c;它没有固定的函数名&#xff0c;可以在代码中被直接定义和使用。匿名函数在Go语言中具有重要的地位&#xff0c;它们常用…...

MobileNet、MobileNetV2和MobileNetV3创新点总结

当谈论MobileNet、MobileNetV2和MobileNetV3时&#xff0c;我们指的是一系列基于深度学习的轻量级神经网络架构&#xff0c;这些架构旨在在保持高度准确性的同时减少模型的计算和参数量。以下是它们各自的创新点的详细总结&#xff1a; MobileNet&#xff1a; 深度可分离卷积&…...

算法:数据转换处理2(云台显控)

#define DISPLAYFUNC #include"define.h" extern OS_EVENT *KEYMsg; uchar mBlank[21] = " " ; u c h a r s t r v g a [ ] = " 0.00 V "; uchar str_vga[] = "0.00V...

让大数据平台数据安全可见-行云管家

数字化经济在快速发展&#xff0c;大数据时代已经到来&#xff0c;大数据已经成为企业和政府决策的重要依据。然而大数据行业快速发展所带来的一系列安全问题也继续解决&#xff0c;例如数据安全更难保障&#xff0c;例如认证体系不完善等等。为此行云管家推出了大数据平台数据…...

微信小程序开发教学系列(3)- 页面设计与布局

3. 页面设计与布局 在微信小程序开发中&#xff0c;页面的设计和布局是非常重要的。一个好的页面设计可以提升用户体验&#xff0c;并使小程序更加吸引人。本章节将介绍如何设计和布局微信小程序的页面。 3.1 页面结构和样式的创建和设置 在创建微信小程序页面时&#xff0c…...

基于JSP+Servlet+mysql员工权限管理系统

基于JSPServletmysql员工权限管理系统 一、系统介绍二、功能展示四、其他系统实现五、获取源码 一、系统介绍 项目类型&#xff1a;Java web项目 项目名称&#xff1a;基于JSPServlet的员工权限管理系统[qxxt] 项目架构&#xff1a;B/S架构 开发语言&#xff1a;Java语言 …...