如何构建一个 NodeJS 影院微服务并使用 Docker 部署
文章目录
- 前言
- 什么是微服务?
- 构建电影目录微服务
- 构建微服务
- 从 NodeJS 连接到 MongoDB 数据库
- 总结
前言
如何构建一个 NodeJS 影院微服务并使用 Docker 部署。在这个系列中,将构建一个 NodeJS 微服务,并使用 Docker Swarm 集群进行部署。
以下是将要使用的工具:
- NodeJS 版本7.2.0
- MongoDB 3.4.1
- Docker for Mac 1.12.6
在尝试本指南之前,应该具备:
- NodeJS 的基本知识
- Docker 的基本知识(并且已经安装了 Docker)
- MongoDB 的基本知识(并且数据库服务正在运行)
什么是微服务?
微服务是一个单独的自包含单元,与其他许多单元一起构成一个大型应用程序。通过将应用程序拆分为小单元,每个部分都可以独立部署和扩展,可以由不同的团队和不同的编程语言编写,并且可以单独进行测试。
微服务架构意味着应用程序由许多较小的、独立的应用程序组成,这些应用程序能够在自己的内存空间中运行,并且可以在可能的多个独立计算机上独立扩展。
微服务的好处:
- 应用程序启动更快,这使得开发人员更具生产力,并加快了部署速度。
- 每个服务可以独立于其他服务部署 — 更容易频繁部署服务的新版本。
- 更容易扩展开发,也可能具有性能优势。
- 消除对技术栈的长期承诺。在开发新服务时,可以选择新的技术栈。
- 微服务通常更好组织,因为每个微服务有一个非常具体的工作,不涉及其他组件的工作。
- 解耦的服务也更容易重新组合和重新配置,以服务不同应用程序的目的(例如,同时为 Web 客户端和公共 API 提供服务)。
微服务的缺点:
- 开发人员必须处理创建分布式系统的额外复杂性。
- 部署复杂性。在生产环境中,部署和管理许多不同服务类型的系统也会带来操作复杂性。
- 在构建新的微服务架构时,可能会发现许多交叉关注点,这些交叉关注点在设计时没有预料到。
构建电影目录微服务
假设正在一家电影院的 IT 部门工作,给了我们一个任务,将他们的电影票务和杂货店从单体系统重构为微服务。
因此,在“构建 NodeJS 电影目录微服务”系列中,将仅关注电影目录服务。
在这个架构中,可以看到有 3 种不同的设备使用该微服务,即 POS(销售点)、移动设备/平板电脑和计算机。POS 和移动设备/平板电脑都有自己的应用程序(在 electron 中开发),并直接使用微服务,而计算机则通过 Web 应用程序访问微服务(一些专家也将 Web 应用程序视为微服务)。
构建微服务
现在,来模拟在最喜欢的电影院预订一场电影首映的过程。
首先,想看看电影院目前正在上映哪些电影。以下图表显示了通过 REST 进行的内部通信,通过此 REST 通信,可以使用 API 来获取目前正在上映的电影。
电影服务的 API 将具有以下 RAML 规范:
#%RAML 1.0
title: cinema
version: v1
baseUri: /types:Movie:properties:id: stringtitle: stringruntime: numberformat: stringplot: stringreleaseYear: numberreleaseMonth: numberreleaseDay: numberexample:id: "123"title: "Assasins Creed"runtime: 115format: "IMAX"plot: "Lorem ipsum dolor sit amet"releaseYear : 2017releaseMonth: 1releaseDay: 6MoviePremieres:type: Movie []resourceTypes:Collection:get:responses:200:body:application/json:type: <<item>>/movies:/premieres:type: { Collection: {item : MoviePremieres } }/{id}:type: { Collection: {item : Movie } }
如果不了解 RAML,可以查看这个很好的教程。
API 项目的结构将如下所示:
- api/ # 我们的API
- config/ # 应用程序配置
- mock/ # 不是必需的,仅用于数据示例
- repository/ # 抽象出数据库
- server/ # 服务器设置代码
- package.json # 依赖项
- index.js # 应用程序的主入口
首先要看的部分是 repository。这是对数据库进行查询的地方。
'use strict'
// factory function, that holds an open connection to the db,
// and exposes some functions for accessing the data.
const repository = (db) => {// since this is the movies-service, we already know// that we are going to query the `movies` collection// in all of our functions.const collection = db.collection('movies')const getMoviePremiers = () => {return new Promise((resolve, reject) => {const movies = []const currentDay = new Date()const query = {releaseYear: {$gt: currentDay.getFullYear() - 1,$lte: currentDay.getFullYear()},releaseMonth: {$gte: currentDay.getMonth() + 1,$lte: currentDay.getMonth() + 2},releaseDay: {$lte: currentDay.getDate()}}const cursor = collection.find(query)const addMovie = (movie) => {movies.push(movie)}const sendMovies = (err) => {if (err) {reject(new Error('An error occured fetching all movies, err:' + err))}resolve(movies)}cursor.forEach(addMovie, sendMovies)})}const getMovieById = (id) => {return new Promise((resolve, reject) => {const projection = { _id: 0, id: 1, title: 1, format: 1 }const sendMovie = (err, movie) => {if (err) {reject(new Error(`An error occured fetching a movie with id: ${id}, err: ${err}`))}resolve(movie)}// fetch a movie by id -- mongodb syntaxcollection.findOne({id: id}, projection, sendMovie)})}// this will close the database connectionconst disconnect = () => {db.close()}return Object.create({getAllMovies,getMoviePremiers,getMovieById,disconnect})
}const connect = (connection) => {return new Promise((resolve, reject) => {if (!connection) {reject(new Error('connection db not supplied!'))}resolve(repository(connection))})
}
// this only exports a connected repo
module.exports = Object.assign({}, {connect})
可能已经注意到,向 repository 的 connect ( connection ) 方法提供了一个 connection
对象。在这里,使用了 JavaScript 的一个重要特性“闭包”,repository 对象返回了一个闭包,其中的每个函数都可以访问 db
对象和 collection
对象,db
对象保存着数据库连接。在这里,抽象了连接的数据库类型,repository 对象不知道数据库是什么类型的,对于这种情况来说,是一个 MongoDB 连接。甚至不需要知道是单个数据库还是复制集连接。虽然使用了 MongoDB 语法,但可以通过应用 SOLID 原则中的依赖反转原则,将存储库功能抽象得更深,将 MongoDB 语法转移到另一个文件中,并只调用数据库操作的接口(例如,使用 mongoose 模型)。
有一个 repository/repository.spec.js
文件来测试这个模块,稍后在文章中会谈到测试。
接下来要看的是 server.js
文件。
'use strict'
const express = require('express')
const morgan = require('morgan')
const helmet = require('helmet')
const movieAPI = require('../api/movies')const start = (options) => {return new Promise((resolve, reject) => {// we need to verify if we have a repository added and a server portif (!options.repo) {reject(new Error('The server must be started with a connected repository'))}if (!options.port) {reject(new Error('The server must be started with an available port'))}// let's init a express app, and add some middlewaresconst 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!')})// we add our API's to the express appmovieAPI(app, options)// finally we start the server, and return the newly created server const server = app.listen(options.port, () => resolve(server))})
}module.exports = Object.assign({}, {start})
在这里,创建了一个新的 express 应用程序,验证是否提供了 repository 和 server port 对象,然后为 express 应用程序应用一些中间件,例如用于日志记录的 morgan
,用于安全性的 helmet
,以及一个错误处理函数,最后导出一个 start 函数来启动服务器。
Helmet 包含了整整 11 个软件包,它们都用于阻止恶意方破坏或使用应用程序来伤害其用户。
好的,现在既然服务器使用了电影的 API,继续查看 movies.js 文件。
'use strict'
const status = require('http-status')module.exports = (app, options) => {const {repo} = options// here we get all the movies app.get('/movies', (req, res, next) => {repo.getAllMovies().then(movies => {res.status(status.OK).json(movies)}).catch(next)})// here we retrieve only the premieresapp.get('/movies/premieres', (req, res, next) => {repo.getMoviePremiers().then(movies => {res.status(status.OK).json(movies)}).catch(next)})// here we get a movie by idapp.get('/movies/:id', (req, res, next) => {repo.getMovieById(req.params.id).then(movie => {res.status(status.OK).json(movie)}).catch(next)})
}
在这里,为API创建了路由,并根据监听的路由调用了 repo 函数。repo 在这里使用了接口技术方法,在这里使用了著名的“为接口编码而不是为实现编码”,因为 express 路由不知道是否有一个数据库对象、数据库查询逻辑等,它只调用处理所有数据库问题的 repo 函数。
所有文件都有与源代码相邻的单元测试,看看 movies.js
的测试是如何进行的。
可以将测试看作是对正在构建的应用程序的安全保障。不仅会在本地机器上运行,还会在 CI 服务上运行,以确保失败的构建不会被推送到生产系统。
为了编写单元测试,必须对所有依赖项进行存根,即为模块提供虚拟依赖项。看看 spec
文件。
/* eslint-env mocha */
const request = require('supertest')
const server = require('../server/server')describe('Movies API', () => {let app = nulllet testMovies = [{'id': '3','title': 'xXx: Reactivado','format': 'IMAX','releaseYear': 2017,'releaseMonth': 1,'releaseDay': 20}, {'id': '4','title': 'Resident Evil: Capitulo Final','format': 'IMAX','releaseYear': 2017,'releaseMonth': 1,'releaseDay': 27}, {'id': '1','title': 'Assasins Creed','format': 'IMAX','releaseYear': 2017,'releaseMonth': 1,'releaseDay': 6}]let testRepo = {getAllMovies () {return Promise.resolve(testMovies)},getMoviePremiers () {return Promise.resolve(testMovies.filter(movie => movie.releaseYear === 2017))},getMovieById (id) {return Promise.resolve(testMovies.find(movie => movie.id === id))}}beforeEach(() => {return server.start({port: 3000,repo: testRepo}).then(serv => {app = serv})})afterEach(() => {app.close()app = null})it('can return all movies', (done) => {request(app).get('/movies').expect((res) => {res.body.should.containEql({'id': '1','title': 'Assasins Creed','format': 'IMAX','releaseYear': 2017,'releaseMonth': 1,'releaseDay': 6})}).expect(200, done)})it('can get movie premiers', (done) => {request(app).get('/movies/premiers').expect((res) => {res.body.should.containEql({'id': '1','title': 'Assasins Creed','format': 'IMAX','releaseYear': 2017,'releaseMonth': 1,'releaseDay': 6})}).expect(200, done)})it('returns 200 for an known movie', (done) => {request(app).get('/movies/1').expect((res) => {res.body.should.containEql({'id': '1','title': 'Assasins Creed','format': 'IMAX','releaseYear': 2017,'releaseMonth': 1,'releaseDay': 6})}).expect(200, done)})
})
/* eslint-env mocha */
const server = require('./server')describe('Server', () => {it('should require a port to start', () => {return server.start({repo: {}}).should.be.rejectedWith(/port/)})it('should require a repository to start', () => {return server.start({port: {}}).should.be.rejectedWith(/repository/)})
})
可以看到,为 movies API 存根了依赖项,并验证了需要提供一个 server port 和一个 repository 对象。
继续看一下如何创建传递给 repository 模块的 db 连接对象,现在定义说每个微服务都必须有自己的数据库,但是对于示例,将使用一个 MongoDB 复制集服务器,但每个微服务都有自己的数据库。
从 NodeJS 连接到 MongoDB 数据库
以下是需要从 NodeJS 连接到 MongoDB 数据库的配置。
const MongoClient = require('mongodb')// here we create the url connection string that the driver needs
const getMongoURL = (options) => {const url = options.servers.reduce((prev, cur) => prev + `${cur.ip}:${cur.port},`, 'mongodb://')return `${url.substr(0, url.length - 1)}/${options.db}`
}// mongoDB function to connect, open and authenticate
const connect = (options, mediator) => {mediator.once('boot.ready', () => {MongoClient.connect( getMongoURL(options), {db: options.dbParameters(),server: options.serverParameters(),replset: options.replsetParameters(options.repl)}, (err, db) => {if (err) {mediator.emit('db.error', err)}db.admin().authenticate(options.user, options.pass, (err, result) => {if (err) {mediator.emit('db.error', err)}mediator.emit('db.ready', db)})})})
}module.exports = Object.assign({}, {connect})
这里可能有更好的方法,但基本上可以这样创建与 MongoDB 的复制集连接。
传递了一个 options 对象,其中包含 Mongo 连接所需的所有参数,并且传递了一个事件 — 中介者对象,当通过认证过程时,它将发出 db 对象。
注意 在这里,使用了一个事件发射器对象,因为使用 promise 的方法在某种程度上并没有在通过认证后返回 db 对象,顺序变得空闲。所以这可能是一个很好的挑战,看看发生了什么,并尝试使用 promise 的方法。
现在,既然正在传递一个 options 对象来进行参数设置,让我们看看这是从哪里来的,因此要查看的下一个文件是 config.js。
// simple configuration file// database parameters
const dbSettings = {db: process.env.DB || 'movies',user: process.env.DB_USER || 'cristian',pass: process.env.DB_PASS || 'cristianPassword2017',repl: process.env.DB_REPLS || 'rs1',servers: (process.env.DB_SERVERS) ? process.env.DB_SERVERS.split(' ') : ['192.168.99.100:27017','192.168.99.101:27017','192.168.99.102:27017'],dbParameters: () => ({w: 'majority',wtimeout: 10000,j: true,readPreference: 'ReadPreference.SECONDARY_PREFERRED',native_parser: false}),serverParameters: () => ({autoReconnect: true,poolSize: 10,socketoptions: {keepAlive: 300,connectTimeoutMS: 30000,socketTimeoutMS: 30000}}),replsetParameters: (replset = 'rs1') => ({replicaSet: replset,ha: true,haInterval: 10000,poolSize: 10,socketoptions: {keepAlive: 300,connectTimeoutMS: 30000,socketTimeoutMS: 30000}})
}// server parameters
const serverSettings = {port: process.env.PORT || 3000
}module.exports = Object.assign({}, { dbSettings, serverSettings })
这是配置文件,大部分配置代码都是硬编码的,但正如看到的,一些属性使用环境变量作为选项。环境变量被视为最佳实践,因为这可以隐藏数据库凭据、服务器参数等。
最后,对于构建电影服务 API 的最后一步是使用 index.js 将所有内容组合在一起。
'use strict'
// we load all the depencies we need
const {EventEmitter} = require('events')
const server = require('./server/server')
const repository = require('./repository/repository')
const config = require('./config/')
const mediator = new EventEmitter()// verbose logging when we are starting the server
console.log('--- Movies Service ---')
console.log('Connecting to movies repository...')// log unhandled execpetions
process.on('uncaughtException', (err) => {console.error('Unhandled Exception', err)
})
process.on('uncaughtRejection', (err, promise) => {console.error('Unhandled Rejection', err)
})// event listener when the repository has been connected
mediator.on('db.ready', (db) => {let reprepository.connect(db).then(repo => {console.log('Repository Connected. Starting Server')rep = reporeturn server.start({port: config.serverSettings.port,repo})}).then(app => {console.log(`Server started succesfully, running on port: ${config.serverSettings.port}.`)app.on('close', () => {rep.disconnect()})})
})
mediator.on('db.error', (err) => {console.error(err)
})// we load the connection to the repository
config.db.connect(config.dbSettings, mediator)
// init the repository connection, and the event listener will handle the rest
mediator.emit('boot.ready')
在这里,组合了所有的电影 API 服务,添加了一些错误处理,然后加载配置、启动存储库,并最后启动服务器。
因此,到目前为止,已经完成了与 API 开发相关的所有内容。
下面是项目中需要用到的初始化以及运行命令:
- npm install # 设置Node依赖项
- npm test # 使用mocha进行单元测试
- npm start # 启动服务
- npm run node-debug # 以调试模式运行服务器
- npm run chrome-debug # 使用chrome调试Node
- npm run lint # 使用standard进行代码lint
最后,第一个微服务已经在本地运行,并通过执行 npm start 命令启动。
现在是时候将其放入 Docker 容器中。
首先,需要使用“使用 Docker 部署 MongoDB 复制集”的文章中的 Docker 环境,如果没有,则需要进行一些额外的修改步骤,以便为微服务设置数据库,以下是一些命令,进行测试电影服务。
首先创建 Dockerfile,将 NodeJS 微服务制作成 Docker 容器。
# Node v7作为基本映像以支持ES6
FROM node:7.2.0
# 为新容器创建一个新用户,并避免root用户
RUN useradd --user-group --create-home --shell /bin/false nupp && \apt-get clean
ENV HOME=/home/nupp
COPY package.json npm-shrinkwrap.json $HOME/app/
COPY src/ $HOME/app/src
RUN chown -R nupp:nupp $HOME/* /usr/local/
WORKDIR $HOME/app
RUN npm cache clean && \npm install --silent --progress=false --production
RUN chown -R nupp:nupp $HOME/*
USER nupp
EXPOSE 3000
CMD ["npm", "start"]
使用 NodeJS 镜像作为 Docker 镜像的基础,然后为镜像创建一个用户以避免非 root 用户,接下来,将 src 复制到镜像中,然后安装依赖项,暴露一个端口号,并最后实例化电影服务。
接下来,需要构建 Docker 镜像,使用以下命令:
$ docker build -t movies-service .
首先看一下构建命令。
docker build
告诉引擎要创建一个新的镜像。-t movies-service
用标记movies-service
标记此镜像。从现在开始,可以根据标记引用此镜像。- .:使用当前目录来查找
Dockerfile
。
经过一些控制台输出后,新镜像中就有了 NodeJS 应用程序,所以现在需要做的就是使用以下命令运行镜像:
$ docker run --name movie-service -p 3000:3000 -e DB_SERVERS="192.168.99.100:27017 192.168.99.101:27017 192.168.99.100:27017" -d movies-service
在上面的命令中,传递了一个环境变量,它是一个服务器数组,需要连接到 MongoDB 复制集的服务器,这只是为了说明,有更好的方法可以做到这一点,比如读取一个环境变量文件。
现在,容器已经运行起来了,获取 docker-machine
IP地址,以获取微服务的 IP 地址,现在准备对微服务进行一次集成测试,另一个测试选项可以是JMeter,它是一个很好的工具,可以模拟HTTP请求。
这是集成测试,将检查一个 API 调用。
/* eslint-env mocha */
const supertest = require('supertest')describe('movies-service', () => {const api = supertest('http://192.168.99.100:3000')it('returns a 200 for a collection of movies', (done) => {api.get('/movies/premiers').expect(200, done)})
})
总结
创建了用于查询电影院正在上映的电影的 movies 服务,使用 RAML 规范设计了 API,然后开始构建 API,并进行了相应的单元测试,最后,组合了所有内容,使微服务完整,并能够启动 movies 服务服务器。
然后,将微服务放入 Docker 容器中,以进行一些集成测试。
微服务架构可以为大型应用程序带来许多好处,但也需要小心管理和设计,以处理分布式系统的复杂性和其他挑战。使用 Docker 容器可以简化微服务的部署和管理,使其更加灵活和可扩展。
相关文章:

如何构建一个 NodeJS 影院微服务并使用 Docker 部署
文章目录 前言什么是微服务?构建电影目录微服务构建微服务从 NodeJS 连接到 MongoDB 数据库总结 前言 如何构建一个 NodeJS 影院微服务并使用 Docker 部署。在这个系列中,将构建一个 NodeJS 微服务,并使用 Docker Swarm 集群进行部署。 以下…...
BEVFusion(ICRA-2023)--01
说明:论文 BEVFusion: Multi-Task Multi-Sensor Fusion with Unified Bird’s-Eye View Representation 原代码运行记录 原始环境:python 3.9 torch 1.12 (失败) 报错: python setup.py develop/mmdet3d/ops/ball_qu…...
Java——Iterator迭代器
在程序开发中,经常需要遍历集合中的所有元素。针对这种需求,JDK专门提供了一个接口java.util.Iterator。Iterator接口也是Java集合中的一员,但它与Collection、Map接口有所不同,Collection接口与Map接口主要用于存储元素ÿ…...

GCC编译过程:预处理->编译->汇编->链接
目录 引言 概括介绍 一、预处理 二、编译 三、汇编 四、链接 总结 引言 当使用集成开发环境(IDE)进行C语言编程时,点击"编译"按钮后,整个C程序从源代码到可执行文件的生成过程会自动完成。IDE会在后台为我们执行C…...

JVM笔记 —— 出现内存溢出错误时时如何排查
一、出现内存溢出的几种情况 内存溢出错误分为StackOverflowError和OutOfMemoryError,前者是栈中出现溢出,后者一般是堆或方法区出现溢出,简称OOM 1. 栈溢出 StackOverflowError 栈溢出一般都是因为没有正确的结束递归导致的,无…...
多级嵌套引入组件导致Vue提示子组件未注册问题
发生此问题的时候是页面解析的时报错的, 所以可以放在beforeCreate中执行注册组件这个时候是在页面运行时执行的,运行时编译就结束了 第一种:可以在父组件中动态注册组件 export default{beforeCreate(){//require是commonJS,commonJS导入ES…...
vue3+element-plus组件下拉列表,数组数据转成树形数据
引入组件 可以直接在项目中引入element-plus表格组件,如果需要变成下拉列表样式需要添加以下属性: row-key 必填 最好给数字或唯一属性 , 给每个节点设置id 不填的话 没有办法实现展开效果 load 这个是动态添加数据的 前提(开启…...
【kubernetes】调度约束
目录 调度约束 Pod 启动典型创建过程如下 调度过程 指定调度节点 查看详细事件(发现未经过 scheduler 调度分配) 获取标签帮助 需要获取 node 上的 NAME 名称 给对应的 node 设置标签分别为 ggls 和 gglm 查看标签 修改成 nodeSelector 调度方…...

【深度学习MOT】SMILEtrack SiMIlarity LEarning for Multiple Object Tracking,论文
论文:https://arxiv.org/abs/2211.08824 文章目录 AbstractIntroduction2. 相关工作2.1 基于检测的跟踪2.1.1 检测方法2.1.2 数据关联方法 2.2 基于注意力的跟踪 3. 方法3.1 架构概述3.2 用于重新识别的相似性学习模块(SLM) Experimental Res…...

jmeter通过BeanShell对接口参数进行MD5和HmacSHA256加密【杭州多测师_王sir】
一、在eclipse里面编写MD5加密算法 package com.Base64;import java.security.MessageDigest; import java.security.NoSuchAlgorithmException;public class Md5Utils {public static String md5(String sourceStr) {String result "";try {MessageDigest md Mess…...

基于自适应曲线阈值和非局部稀疏正则化的压缩感知图像复原研究【自适应曲线阈值去除加性稳态白/有色高斯噪声】(Matlab代码实现)
💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…...
Spring AOP 切点表达式
参考博客: 参考博客...

打破传统直播,最新数字化升级3DVR全景直播
导语: 近年来,随着科技的不断创新和发展,传媒领域也正经历着一场前所未有的变革。在这个数字化时代,直播已经不再仅仅是在屏幕上看到一些人的视频,而是将观众带入一个真实世界的全新体验。其中,3DVR全景直…...
网络安全--利用awk分析Apache日志
一、溯源 你会溯源吗?怎么溯 拿到日志(ssh登录日志,Apache日志),通过日志溯到ip,对日志进行每天的拆分,第二通过awk日志分析工具对每天的日志进行拆分,分析某一个ip今天对我访问多…...

计算机视觉一 —— 介绍与环境安装
傲不可长 欲不可纵 乐不可极 志不可满 一、介绍 研究理论和应用 - 研究如何使机器“看”的科学 - 让计算机具有人类视觉的所有功能 - 让计算机从图像中,提取有用的信息,并解释 - 重构人眼;重构视觉皮层;重构大脑剩余部分 计…...

如何看懂统一社会信用代码?
在查看企业信息的时候,我们通常第一时间查看的就是该企业的照面信息:企业名称,企业信用代码,企业法人等等。 其中统一社会信用代码就是给各个企业组织编号,是便于统一识别管理的一串代码,类似我们的身份证…...

systrace: 系统级跟踪工具的解析
关于作者:CSDN内容合伙人、技术专家, 从零开始做日活千万级APP。 专注于分享各领域原创系列文章 ,擅长java后端、移动开发、人工智能等,希望大家多多支持。 目录 一、导读二、概览三、获取systrace文件3.1 通过python命令获取3.1.…...

关于青少年学习演讲与口才对未来的领导力的塑造的探析
标题:青少年学习演讲与口才对未来领导力的塑造:一项探析 摘要: 本论文旨在探讨青少年学习演讲与口才对未来领导力的塑造的重要性和影响。通过分析演讲和口才对青少年的益处,以及如何培养这些技能来促进领导力的发展,我…...

大数据分析案例-基于KMeans和DBSCAN算法对汽车行业客户进行聚类分群
🤵♂️ 个人主页:艾派森的个人主页 ✍🏻作者简介:Python学习者 🐋 希望大家多多支持,我们一起进步!😄 如果文章对你有帮助的话, 欢迎评论 💬点赞Ǵ…...

大型活动交通拥堵治理的视觉算法应用
大型活动下智慧交通的视觉分析应用 一、背景与挑战 大型活动(如演唱会、马拉松赛事、高考中考等)期间,城市交通面临瞬时人流车流激增、传统摄像头模糊、交通拥堵识别滞后等问题。以演唱会为例,暖城商圈曾因观众集中离场导致周边…...
JVM暂停(Stop-The-World,STW)的原因分类及对应排查方案
JVM暂停(Stop-The-World,STW)的完整原因分类及对应排查方案,结合JVM运行机制和常见故障场景整理而成: 一、GC相关暂停 1. 安全点(Safepoint)阻塞 现象:JVM暂停但无GC日志,日志显示No GCs detected。原因:JVM等待所有线程进入安全点(如…...
DeepSeek 技术赋能无人农场协同作业:用 AI 重构农田管理 “神经网”
目录 一、引言二、DeepSeek 技术大揭秘2.1 核心架构解析2.2 关键技术剖析 三、智能农业无人农场协同作业现状3.1 发展现状概述3.2 协同作业模式介绍 四、DeepSeek 的 “农场奇妙游”4.1 数据处理与分析4.2 作物生长监测与预测4.3 病虫害防治4.4 农机协同作业调度 五、实际案例大…...

【从零学习JVM|第三篇】类的生命周期(高频面试题)
前言: 在Java编程中,类的生命周期是指类从被加载到内存中开始,到被卸载出内存为止的整个过程。了解类的生命周期对于理解Java程序的运行机制以及性能优化非常重要。本文会深入探寻类的生命周期,让读者对此有深刻印象。 目录 …...

从 GreenPlum 到镜舟数据库:杭银消费金融湖仓一体转型实践
作者:吴岐诗,杭银消费金融大数据应用开发工程师 本文整理自杭银消费金融大数据应用开发工程师在StarRocks Summit Asia 2024的分享 引言:融合数据湖与数仓的创新之路 在数字金融时代,数据已成为金融机构的核心竞争力。杭银消费金…...
为什么要创建 Vue 实例
核心原因:Vue 需要一个「控制中心」来驱动整个应用 你可以把 Vue 实例想象成你应用的**「大脑」或「引擎」。它负责协调模板、数据、逻辑和行为,将它们变成一个活的、可交互的应用**。没有这个实例,你的代码只是一堆静态的 HTML、JavaScript 变量和函数,无法「活」起来。 …...

aardio 自动识别验证码输入
技术尝试 上周在发学习日志时有网友提议“在网页上识别验证码”,于是尝试整合图像识别与网页自动化技术,完成了这套模拟登录流程。核心思路是:截图验证码→OCR识别→自动填充表单→提交并验证结果。 代码在这里 import soImage; import we…...

数据分析六部曲?
引言 上一章我们说到了数据分析六部曲,何谓六部曲呢? 其实啊,数据分析没那么难,只要掌握了下面这六个步骤,也就是数据分析六部曲,就算你是个啥都不懂的小白,也能慢慢上手做数据分析啦。 第一…...
无需布线的革命:电力载波技术赋能楼宇自控系统-亚川科技
无需布线的革命:电力载波技术赋能楼宇自控系统 在楼宇自动化领域,传统控制系统依赖复杂的专用通信线路,不仅施工成本高昂,后期维护和扩展也极为不便。电力载波技术(PLC)的突破性应用,彻底改变了…...
PostgreSQL 与 SQL 基础:为 Fast API 打下数据基础
在构建任何动态、数据驱动的Web API时,一个稳定高效的数据存储方案是不可或缺的。对于使用Python FastAPI的开发者来说,深入理解关系型数据库的工作原理、掌握SQL这门与数据库“对话”的语言,以及学会如何在Python中操作数据库,是…...