CI/CD实践(五)Jenkins Docker 自动化构建部署Node服务

news2024/9/20 20:20:35

微服务CI/CD实践系列:
 
微服务CI/CD实践(一)环境准备及虚拟机创建
微服务CI/CD实践(二)服务器先决准备
微服务CI/CD实践(三)gitlab部署及nexus3部署
微服务CI/CD实践(四)Jenkins部署及环境配置
微服务CI/CD实践(五)Jenkins + Dokcer 部署微服务后端项目
微服务CI/CD实践(六)Jenkins + Dokcer 部署微服务前端VUE项目
微服务CI/CD实践(七)Minio服务器部署及应用

文章目录

  • 一、先决条件
    • 1.1 服务器先决条件
    • 1.2 项目配置
      • Dockerfile
      • Nginx配置文件
      • 部署脚本
  • 二、Jenkins构建部署
    • 2.1 创建项目
    • 2.2 配置项目基本信息
    • 2.3 定义 Pipeline script
    • 2.4 构建部署项目

前端项目是基于NodeJS(Vue)框架开发,我们通过打包成Docker镜像的方式进行部署,原理是先将项目打包成静态页面,然后再将静态页面直接copy到Nginx镜像中运行。构建部署流程如下:

  • 拉取代码
  • jenkins服务器进行nodejs编译
  • 使用dockerfile构建镜像并打包镜像
  • 上传镜像包
  • 执行sh

一、先决条件

1.1 服务器先决条件

Jenkins 和 server服务器先决条件参考微服务CI/CD实践(二)服务器先决准备 和 微服务CI/CD实践(四)Jenkins部署及环境配置

1.2 项目配置

Dockerfile

使用Jenkins本地编译项目在构建镜像

FROM nginx:latest
# 将生成的静态页面文件复制到nginx的/usr/share/nginx/html/目录
COPY dist/ /usr/share/nginx/html/
# 将mime文件复制到nginx的/etc/nginx/目录 后续配置ng会使用
COPY mime.types /etc/nginx/mime.types
# 容器启动时运行的命令
CMD ["nginx", "-g", "daemon off;"]

也可以直接使用docker 镜像编译-构建镜像,此模式jenkins服务器可以不需要node环境

# Install dependencies
FROM node:18.20.4 as builder
WORKDIR /app
# Install pnpm
RUN npm i -g pnpm
# copy file for next stage
COPY . /app
RUN pnpm install && pnpm run build
# copy dist from the first stage for Production
FROM nginx:latest AS runner
COPY --from=builder /app/dist/ /usr/share/nginx/html
COPY --from=builder /app/nginx.conf /etc/nginx/conf.d/default.conf

不过此模式在docker-hub停止国内服务后可能无法正常拉取镜像。

Nginx配置文件

根据项目要求编写ng配置

cd /data/container/nginx/etc
vi nginx.conf
# 编写配置并保存

vi mime.types
# 编写配置并保存

以下为nginx.conf配置示例


events {
    worker_connections 1024;
}

http {
    # 需要引入mime.types配置或者显示配置静态文件mimetype类型,否则运行后,浏览器会因为文件类型导致无法正常加载静态文件
    include       mime.types;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile        on;
    tcp_nopush      on;
    tcp_nodelay     on;
    keepalive_timeout  65;
    types_hash_max_size 2048;
    client_max_body_size 100m;

    server {
        listen       80;
        listen  [::]:80;
        server_name  localhost;
        
        # 设置 CORS 相关的响应头
        add_header 'Access-Control-Allow-Origin' '*' always;
        add_header 'Access-Control-Allow-Methods' '*' always;
        add_header 'Access-Control-Max-Age' 1728000 always;
        add_header 'Access-Control-Allow-Headers' '*' always;
        add_header 'Access-Control-Allow-Credentials' 'true' always;
        
        gzip on;
        gzip_buffers 32 4k;
        gzip_comp_level 6;
        gzip_min_length 100;
        gzip_types application/javascript text/css text/xml text/plain application/x-javascript image/jpeg image/gif image/png;
        gzip_disable "MSIE [1-6]\.";
        gzip_vary on;

        charset utf8;

        location / {
            root   /usr/share/nginx/html;
            index  index.html index.htm;
            try_files $uri $uri/ /index.html;
            if (!-e $request_filename) {
                rewrite ^/(.*) /index.html last;
                break;
            }
        }

        location  ~ .*\.(jpg|png|js|css|woff2|ttf|woff|eot)$ {
            root   /usr/share/nginx/html;
        }

        #error_page  404              /404.html;

        # redirect server error pages to the static page /50x.html
        #
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   /usr/share/nginx/html;
        }
        
        # 配置全局代理并统一处理CORS 
        location /gateway-api/ {
           proxy_set_header Host $http_host;               
           proxy_set_header X-Real-Ip $remote_addr;
           proxy_set_header REMOTE-HOST $remote_addr;
           proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
           proxy_pass http://192.168.1.103:10000/;

           # 添加 CORS 相关的响应头
           add_header 'Access-Control-Allow-Origin' '*' always;
           add_header 'Access-Control-Allow-Methods' '*' always;
           add_header 'Access-Control-Max-Age' 1728000 always;
           add_header 'Access-Control-Allow-Headers' '*' always;
           add_header 'Access-Control-Allow-Credentials' 'true' always;

           # 处理 OPTIONS 请求
           if ($request_method = 'OPTIONS') {
               return 204;
           }
       }
    }
}

以下为mime.type示例

types {
    text/html                 html htm shtml;
    text/css                  css;
    image/gif                gif;
    image/jpeg               jpeg jpg;
    application/javascript    js;
    application/xml          xml;
    application/json         json;
    application/pdf          pdf;
    application/rss+xml      rss;
    application/atom+xml     atom;
    text/mathml              mml;
    text/plain               txt;
    text/vnd.sun.j2me.app-descriptor jad;
    text/vnd.wap.wml         wml;
    text/x-component         htc;
    image/png               png;
    image/svg+xml           svg;
    image/tiff              tif tiff;
    image/vnd.wap.wbmp      wbmp;
    image/x-icon            ico;
    image/x-jng             jng;
    image/x-ms-bmp          bmp;
    application/zip          zip;
    application/tar          tar;
    application/x-7z-compressed 7z;
    application/x-java-archive jar;
    application/x-rar-compressed rar;
    application/x-web-app-manifest+json webapp;
    application/xhtml+xml   xhtml;
    application/x-msdownload  exe dll;
    audio/midi              mid midi kar;
    audio/mpeg             mp3;
    video/mp4              mp4;
    video/mpeg             mpeg mpg;
    video/webm             webm;
    video/x-msvideo         avi;
    video/x-ms-wmv         wmv;
    video/x-ms-asf         asx asf;
    video/x-flv            flv;
    application/x-shockwave-flash swf;
    application/vnd.ms-excel  xls;
    application/vnd.openxmlformats-officedocument.wordprocessingml.document docx;
    application/vnd.openxmlformats-officedocument.spreadsheetml.sheet  xlsx;
    application/vnd.openxmlformats-officedocument.presentationml.presentation pptx;
    application/vnd.ms-fontobject eot;
    application/vnd.apple.mpegurl m3u8;
    application/x-font-ttf  ttc ttf;
    application/x-httpd-php-source phps;
}

部署脚本

step1 定义入参
可以通过Jenkins任务将参数传入脚本中,我们定义了下面7个参数:
container_name : 容器名称
image_name : 镜像名称
version : 镜像版本
portal_port: 宿主主机端口映射
server_port: 容器内服务端口
portal_ssl_port: 宿主主机端口映射
serve_sslr_port: 容器内服务端口

step2 定义入参对参数进行检查
将必传参数放在最前面,这里根据自己的实际情况判断,检查是否传递参数。比如设置container_name、image_name、version 、portal_port、server_port5个参数必须传入,就设置参数的个数不能小于5。

echo "param validate"
if [ $# -lt 5 ]; then  
  echo "you must use like this : /usr/docker-sh/your_script.sh <container_name> <image_name> <version> [portal port] [server port] [portal ssl port] [server ssl port]"  
  exit  
fi

step3 入参赋值
如果有参数传入,则赋值参数

# 前五个参数是必传参数,无需判断直接赋值
container_name="$1"
image_name="$2"
version="$3"
portal_port="$4"
server_port="$5"
if [ "$6" != "" ]; then
   portal_ssl_port="$6"
fi
echo "portal_ssl_port=" $portal_ssl_port
if [ "$7" != "" ]; then
   serve_sslr_port="$7"
fi

step4 停止并删除容器

echo "执行docker ps"
docker ps 
if [[ "$(docker inspect $container_name 2> /dev/null | grep $container_name)" != "" ]]; 
then 
  echo $container_name "容器存在,停止并删除"
  echo "docker stop" $container_name
  docker stop $container_name
  echo "docker rm" $container_name
  docker rm $container_name
else 
  echo $container_name "容器不存在"
fi

step5 停止并删除镜像

# 删除镜像
echo "执行docker images"
docker images
if [[ "$(docker images -q $image_name 2> /dev/null)" != "" ]]; 
then 
  echo $image_name '镜像存在,删除镜像'
  docker rmi $(docker images -q $image_name 2> /dev/null) --force
else 
  echo $image_name '镜像不存在'
fi

step6 备份和加载安装包

#bak image
echo "bak image" $image_name
BAK_DIR=/opt/bak/docker/$image_name/`date +%Y%m%d`
mkdir -p "$BAK_DIR"
cp "/opt/tmp/$container_name.tar" "$BAK_DIR"/"$image_name"_`date +%H%M%S`.tar

echo "docker load" $image_name
docker load --input /opt/tmp/$container_name.tar

step7 执行运行镜像命令

echo "docker run" $image_name
docker run -d -p $portal_port:$server_port --name=$container_name --network=my-network -e TZ="Asia/Shanghai" --restart=always -v /data/container/nginx/www:/var/www -v /data/container/nginx/logs:/var/log/nginx -v /data/container/nginx/etc:/etc/nginx -v /data/container/nginx/etc/nginx.conf:/etc/nginx/nginx.conf -v /data/container/nginx/etc/mime.types:/etc/nginx/mime.types -v /etc/localtime:/etc/localtime -v /usr/share/zoneinfo/Asia/Shanghai:/etc/timezone $image_name

step8 执行删除安装包命令

echo "remove tmp " $image_name
rm -rf /opt/tmp/$container_name.tar

以下为完整的安装部署脚本

#!/usr/bin/env bash

echo "param validate"
if [ $# -lt 5 ]; then  
  echo "you must use like this : /usr/docker-sh/your_script.sh <container_name> <image_name> <version> [portal port] [server port] [portal ssl port] [server ssl port]"  
  exit  
fi

container_name="$1"
image_name="$2"
version="$3"
portal_port="$4"
server_port="$5"
if [ "$6" != "" ]; then
   portal_ssl_port="$6"
fi
echo "portal_ssl_port=" $portal_ssl_port
if [ "$7" != "" ]; then
   serve_sslr_port="$7"
fi

echo "执行docker ps"
docker ps 
if [[ "$(docker inspect $container_name 2> /dev/null | grep $container_name)" != "" ]]; 
then 
  echo $container_name "容器存在,停止并删除"
  echo "docker stop" $container_name
  docker stop $container_name
  echo "docker rm" $container_name
  docker rm $container_name
else 
  echo $container_name "容器不存在"
fi
# 删除镜像
echo "执行docker images"
docker images
if [[ "$(docker images -q $image_name 2> /dev/null)" != "" ]]; 
then 
  echo $image_name '镜像存在,删除镜像'
  docker rmi $(docker images -q $image_name 2> /dev/null) --force
else 
  echo $image_name '镜像不存在'
fi

#bak image
echo "bak image" $image_name
BAK_DIR=/opt/bak/docker/$image_name/`date +%Y%m%d`
mkdir -p "$BAK_DIR"
cp "/opt/tmp/$container_name.tar" "$BAK_DIR"/"$image_name"_`date +%H%M%S`.tar

echo "docker load" $image_name
docker load --input /opt/tmp/$container_name.tar

echo "docker run" $image_name
docker run -d -p $portal_port:$server_port --name=$container_name --network=my-network -e TZ="Asia/Shanghai" --restart=always -v /data/container/nginx/www:/var/www -v /data/container/nginx/logs:/var/log/nginx -v /data/container/nginx/etc:/etc/nginx -v /data/container/nginx/etc/nginx.conf:/etc/nginx/nginx.conf -v /data/container/nginx/etc/mime.types:/etc/nginx/mime.types -v /etc/localtime:/etc/localtime -v /usr/share/zoneinfo/Asia/Shanghai:/etc/timezone $image_name

echo "remove tmp " $image_name
rm -rf /opt/tmp/$container_name.tar

echo "Docker Portal is starting,please try to access $container_name conslone url"

二、Jenkins构建部署

2.1 创建项目

新建一个流水线任务
在这里插入图片描述

2.2 配置项目基本信息

创建完成项目,点击项目进入项目页面,点击左侧菜单》配置,进行项目基本配置
step1 项目构建历史存储策略配置
在这里插入图片描述
根据项目实际情况配置存储策略
step2 配置参数化构建过程
Jenkins List Git Branches插件 构建选择指定git分支,点击添加参数选择List Git branchers选项进行Jenkins List Git Branches插件配置
在这里插入图片描述
Jenkins List Git Branches插件配置流程如下:

  • 配置name
  • 配置仓库并选择凭证
  • 选择Parameter Type
  • 配置Branch Filter

在这里插入图片描述

2.3 定义 Pipeline script

step1 配置全局变量

environment {   
    REPOSITORY="http://192.168.1.101:8929/hka/hka-admin-wocwin.git"
	projectdir="hka-web-01"
	projectname="hka-admin-wocwin"
}

step2 获取代码
检出选择指定git分支的代码

stages {
        stage('获取代码') {
			steps {
				echo "start fetch code from git:${REPOSITORY} ${branch}"
				deleteDir()
				checkout([
                    $class: 'GitSCM',
                    branches: [[name: '${branch}']],
                    doGenerateSubmoduleConfigurations: false,
                    extensions: [],
                    userRemoteConfigs: [[
                        credentialsId: '2',
                        url: 'http://192.168.1.101:8929/hka/hka-admin-wocwin.git'
                    ]]
                ])
			}
		}

step3 编译项目

这里需要显示指定node的环境变量,否则执行编译命令会抛异常

stage('Build NodeJS Vue') {
            steps {
                echo "build nodejs code"
                nodejs('node') {
                    sh 'export PATH="/usr/local/nodejs/bin:$PATH"'
                    sh 'node -v'
                    sh 'npm -v'
                    sh 'pnpm -v'
                    sh 'pnpm install'
                    sh 'pnpm run prod'
                }
                echo "build nodejs success"
            }
        }

step4 删除历史容器和镜像
如何没有在jenkins服务器运行容器可以忽略Delete Old Docker Container步骤

stage('Delete Old Docker Container') {
            steps {
                echo "delete docker container"
                sh '''if [[ "$(docker inspect ${projectname} 2> /dev/null | grep ${projectname})" != "" ]]; 
                then 
                  echo ${projectname} "容器存在,停止并删除"
                  echo "docker stop" ${projectname}
                  docker stop ${projectname}
                  echo "docker rm" ${projectname}
                  docker rm ${projectname}
                else 
                  echo ${projectname} "容器不存在"
                fi'''
            }
        }
        
        stage('Delete Old Docker Image') {
            steps {
                echo "delete docker image"
                sh '''if [[ "$(docker images -q ${projectname} 2> /dev/null)" != "" ]]; 
                    then 
                      echo ${projectname} \'镜像存在,删除镜像\'
                      docker rmi $(docker images -q ${projectname} 2> /dev/null) --force
                    else 
                      echo ${projectname} \'镜像不存在,创建镜像\'
                    fi'''
            }
            
        }

step5 构建镜像

stage('Build Docker Image') {
             steps {
                echo "start docker build ${projectname} code"
                sh 'docker build -t ${projectname} .'
                echo "save docker images tar"
                sh 'docker save -o ${projectname}.tar ${projectname}'
             }
            
        }
stage('Delete New Docker Image') {
            steps {
                echo "delete docker image"
                sh '''if [[ "$(docker images -q ${projectname} 2> /dev/null)" != "" ]]; 
                    then 
                      echo ${projectname} \'镜像存在,删除镜像\'
                      docker rmi $(docker images -q ${projectname} 2> /dev/null) --force
                    else 
                      echo ${projectname} \'镜像不存在,创建镜像\'
                    fi'''
            }
        }

step6 上传镜像包
这里的configName: ‘103’, 就是微服务CI/CD实践(四)Jenkins部署及环境配置### 2.2.4 全局系统配置 SSH Server配置
该流水线步骤会通过ssh将 镜像tar包上传到SSH Server配置的Remote Directory目录下

stage('Upload img tar') {
            steps {
                sshPublisher(
                    publishers: [
                        sshPublisherDesc(
                            configName: '103',
                            transfers: [
                                sshTransfer(
                                    cleanRemote: false,
                                    excludes: '',
                                    makeEmptyDirs: false,
                                    noDefaultExcludes: false,
                                    patternSeparator: '[, ]+',
                                    remoteDirectory: '',
                                    remoteDirectorySDF: false,
                                    removePrefix: '',
                                    sourceFiles: '${projectname}.tar'
                                )
                            ],
                            usePromotionTimestamp: false,
                            useWorkspaceInPromotion: false,
                            verbose: false
                        )
                    ]
                )
            }
        }

step7 执行安装部署脚本
这里的configName: ‘103’, 就是微服务CI/CD实践(四)Jenkins部署及环境配置### 2.2.4 全局系统配置 SSH Server配置
该步骤通过ssh远程执行sh安装部署脚本

stage('Execute Command sh') {
            steps {
                sshPublisher(
                    publishers: [
                        sshPublisherDesc(
                            configName: '103',
                            transfers: [
                                sshTransfer(
                                    execCommand: '/usr/docker-sh/publish_hka-admin-wocwin.sh hka-admin-wocwin hka-admin-wocwin latest 80 80',
                                    execTimeout: 300000
                                )
                            ],
                            usePromotionTimestamp: false,
                            useWorkspaceInPromotion: false,
                            verbose: false
                        )
                    ]
                )
            }
        }

以下为完整流水线定义

pipeline {
    agent any
    
    environment {   
        REPOSITORY="http://192.168.1.101:8929/hka/hka-admin-wocwin.git"
		projectdir="hka-web-01"
		projectname="hka-admin-wocwin"
    }
    
    stages {
        stage('获取代码') {
			steps {
				echo "start fetch code from git:${REPOSITORY} ${branch}"
				deleteDir()
				checkout([
                    $class: 'GitSCM',
                    branches: [[name: '${branch}']],
                    doGenerateSubmoduleConfigurations: false,
                    extensions: [],
                    userRemoteConfigs: [[
                        credentialsId: '2',
                        url: 'http://192.168.1.101:8929/hka/hka-admin-wocwin.git'
                    ]]
                ])
			}
		}
		
        stage('Build NodeJS Vue') {
            steps {
                echo "build nodejs code"
                nodejs('node') {
                    sh 'export PATH="/usr/local/nodejs/bin:$PATH"'
                    sh 'node -v'
                    sh 'npm -v'
                    sh 'pnpm -v'
                    sh 'pnpm install'
                    sh 'pnpm run prod'
                }
                echo "build nodejs success"
            }
        }
        
        stage('Delete Old Docker Container') {
            steps {
                echo "delete docker container"
                sh '''if [[ "$(docker inspect ${projectname} 2> /dev/null | grep ${projectname})" != "" ]]; 
                then 
                  echo ${projectname} "容器存在,停止并删除"
                  echo "docker stop" ${projectname}
                  docker stop ${projectname}
                  echo "docker rm" ${projectname}
                  docker rm ${projectname}
                else 
                  echo ${projectname} "容器不存在"
                fi'''
            }
        }
        
        stage('Delete Old Docker Image') {
            steps {
                echo "delete docker image"
                sh '''if [[ "$(docker images -q ${projectname} 2> /dev/null)" != "" ]]; 
                    then 
                      echo ${projectname} \'镜像存在,删除镜像\'
                      docker rmi $(docker images -q ${projectname} 2> /dev/null) --force
                    else 
                      echo ${projectname} \'镜像不存在,创建镜像\'
                    fi'''
            }
            
        }
        
        stage('Build Docker Image') {
             steps {
                echo "start docker build ${projectname} code"
                sh 'docker build -t ${projectname} .'
                echo "save docker images tar"
                sh 'docker save -o ${projectname}.tar ${projectname}'
             }
            
        }
        
        stage('Delete New Docker Image') {
            steps {
                echo "delete docker image"
                sh '''if [[ "$(docker images -q ${projectname} 2> /dev/null)" != "" ]]; 
                    then 
                      echo ${projectname} \'镜像存在,删除镜像\'
                      docker rmi $(docker images -q ${projectname} 2> /dev/null) --force
                    else 
                      echo ${projectname} \'镜像不存在,创建镜像\'
                    fi'''
            }
        }
        
        stage('Upload img tar') {
            steps {
                sshPublisher(
                    publishers: [
                        sshPublisherDesc(
                            configName: '103',
                            transfers: [
                                sshTransfer(
                                    cleanRemote: false,
                                    excludes: '',
                                    makeEmptyDirs: false,
                                    noDefaultExcludes: false,
                                    patternSeparator: '[, ]+',
                                    remoteDirectory: '',
                                    remoteDirectorySDF: false,
                                    removePrefix: '',
                                    sourceFiles: 'hka-admin-wocwin.tar'
                                )
                            ],
                            usePromotionTimestamp: false,
                            useWorkspaceInPromotion: false,
                            verbose: false
                        )
                    ]
                )
            }
        }

        stage('Execute Command sh') {
            steps {
                sshPublisher(
                    publishers: [
                        sshPublisherDesc(
                            configName: '103',
                            transfers: [
                                sshTransfer(
                                    execCommand: '/usr/docker-sh/publish_hka-admin-wocwin.sh hka-admin-wocwin hka-admin-wocwin latest 80 80 4413 4413',
                                    execTimeout: 300000
                                )
                            ],
                            usePromotionTimestamp: false,
                            useWorkspaceInPromotion: false,
                            verbose: false
                        )
                    ]
                )
            }
        }
        
        stage('Publish Results') {
            steps {
               echo "End Publish ${projectname}"  
            }
        }

        
    }
}

2.4 构建部署项目

回到项目页面,点击参数化构建,选择用于构建的分支点击Build执行构建任务。
在这里插入图片描述

在这里插入图片描述

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

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

相关文章

Linux驱动学习之input子系统

简介 input 子系统就是管理输入的子系统&#xff0c;和pinctrl、gpio 子系统一样&#xff0c;都是 Linux 内核针对某一类设备而创建的框架。按键、鼠标、键盘、触摸屏等都属于输入设备&#xff0c;linux内核为此专门做了一个叫做input子系统的框架来处理输入事件。输入设备本…

day44——面向对象特征

一、封装 1.1 面向对象的三大特质 封装、继承、多态&#xff0c;如果问有四大特征&#xff0c;可以外加一个抽象 封装&#xff1a;将实现同一事物的所有的属性&#xff08;成员变量&#xff09;和行为&#xff08;成员函数&#xff09;封装到一个整体&#xff0c;我们称之为…

【Datawhale AI夏令营】从零上手CV竞赛Task2

文章目录 前言一、YOLO是什么&#xff1f;二、YOLO的历史三、性能指标四、 性能指标计算公式五、性能优化总结 前言 本文的Task2是对Task1的baseline代码进行优化的过程。 一、YOLO是什么&#xff1f; 首先简单介绍一下YOLO模型&#xff1a; 物体检测算法主要分为两类&#…

MSTP多实例生成树的配置

SW1配置&#xff1a; vlan batch 1 to 100 interface GigabitEthernet0/0/1 port link-type trunk port trunk allow-pass vlan 2 to 4094 interface GigabitEthernet0/0/2 port link-type trunk port trunk allow-pass vlan 2 to 4094 stp mode mstp //修改生成树的模式为…

【多线程】创建线程到底是多少种方法?

&#x1f970;&#x1f970;&#x1f970;来都来了&#xff0c;不妨点个关注叭&#xff01; &#x1f449;博客主页&#xff1a;欢迎各位大佬!&#x1f448; 文章目录 1. 创建线程的两种方式总结(最官方)1.1 继承 Thread 类1.2 实现 Runnable 接口1.3 优先考虑使用第二种 —— …

三. Spring Boot 当中的“容器功能” 和 “配置绑定” 的详细剖析(附+源代码流程)

三. Spring Boot 当中的“容器功能” 和 “配置绑定” 的详细剖析(附源代码流程) 文章目录 三. Spring Boot 当中的“容器功能” 和 “配置绑定” 的详细剖析(附源代码流程)1. Spring Boot 是继续支持了 Spring 当中的注解的1.2 Spring 当中的 Component&#xff0c;Controller…

通过visual studio进行dump文件调试和分析

0、前言 很多时候程序crash之后需要分析原因。对于C/C程序&#xff0c;一般最常见的场景和方法就是根据dump文件进行分析。 1、分析的前提条件 进行dump文件分析&#xff0c;需要以下文件&#xff1a; 进程crash时产生的dump文件程序源码进程对应的程序exe文件编译exe文件时产…

QT Quick QML 添加海康威视SDK云台控制模块

文章目录 1. 配置海康威视 SDK 下载SDK文件移植工程文件添加 2. 函数调用流程接口参考代码 3. 代码后端核心代码前端核心代码 GitHub 源码: QmlLearningPro &#xff0c;选择子工程 HkwsDemo.pro &#xff08;暂未上传&#xff09; QML 其它文章请点击这里: QT QUICK …

Monibuca实战:如何用Go语言打造高效的直播后端

简介 Monibuca&#xff08;简称&#xff1a;m7s&#xff09; 是一个开源的实时流媒体服务器开发框架&#xff0c;使用 Go 语言编写。 它的设计目标是提供一个高性能、可扩展、易于定制的实时流媒体服务器解决方案。 Monibuca 的核心理念是模块化&#xff0c;允许开发者根据需…

文件禁止外发的方法有哪些?企业如何禁止文件外发:六个控制文件外发的小窍门!

想象一下&#xff0c;企业信息如同珍贵的宝藏&#xff0c;而文件外发就像不经意间打开的后门&#xff0c;让宝藏暴露在风雨之中&#xff01;今天&#xff0c;我们就来聊聊如何给这扇后门加上六道坚实的锁&#xff0c;确保企业信息的安全无虞。让我们一起探索六个控制文件外发的…

Mask R-CNN论文原理讲解

论文:arxiv.org/pdf/1703.06870 代码&#xff1a;maskrcnn-benchmark:Fast, modular reference implementation of Instance Segmentation and Object Detection algorithms in PyTorch. - GitCode Mask R-CNN简介 Mask R-CNN是何凯明大神的新作。Mask R-CNN是一种在有效检测…

武器弹药制造5G智能工厂物联数字孪生平台,推进制造业数字化转型

武器弹药制造领域作为国防工业的重要组成部分&#xff0c;其数字化转型更是关乎国家安全与军事实力提升的关键。随着5G、物联网、大数据、云计算及人工智能等先进技术的融合应用&#xff0c;武器弹药制造5G智能工厂物联数字孪生平台应运而生&#xff0c;正逐步成为推进制造业数…

分享5款支持论文写作网站先稿后付的网站!

在当今学术研究和学术写作领域&#xff0c;AI论文写作工具已经成为不可或缺的助手。这些工具不仅能够提高写作效率&#xff0c;还能帮助研究人员生成高质量的论文内容。特别是那些提供“先稿后付”服务模式的网站&#xff0c;更是为用户提供了极大的便利和保障。以下是五款值得…

记录|SPC公式小结

目录 前言一、基本缩写二、Xbar和R的控制线三、相关系数表更新时间 前言 参考文章&#xff1a; 参考视频&#xff1a; SPC公式小结 一、基本缩写 二、Xbar和R的控制线 三、相关系数表 更新时间 2024.08.29&#xff1a;创建

旗帜分田(华为od机考题)

一、题目 1.原题 从前有个村庄&#xff0c;村民们喜欢在各种田地上插上小旗子&#xff0c;旗子上标识了各种不同的数字。 某天集体村民决定将覆盖相同数字的最小矩阵形的土地的分配给为村里做出巨大贡献的村民&#xff0c; 请问&#xff0c;此次分配土地&#xff0c;做出贡献…

DevEco Studio5 新建项目

Deveco Studio安装结束之后&#xff0c;开始新建一个项目&#xff1a; 1.点击Create Project新建项目 2.选择空的项目–>点击next 3.配置项目信息–>点击Finish 4.进入项目 6.右上方目前处于No Devices(无设备)状态–>点击下拉三角–>点击Devide Manager管理设…

Life long learning

现象&#xff1a;一个model进行multi-task learning做的还可以&#xff0c;说明模型是可以同时学会多个任务的&#xff0c;但是如果训练过程是1个task 1个task的顺序进行&#xff0c;模型就会发生灾难性的遗忘现象&#xff0c;只会做刚学完的task。 目标&#xff1a;让模型不要…

94.WEB渗透测试-信息收集-Google语法(8)

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 内容参考于&#xff1a; 易锦网校会员专享课 上一个内容&#xff1a;93.WEB渗透测试-信息收集-Google语法&#xff08;7&#xff09; • Filetype • Filetype…

二叉树前序,中序,后序非递归遍历(Java)

1. 思路&#xff1a; 首先创建一个栈和顺序表&#xff0c;按照根左右的前序遍历顺序去遍历这棵树&#xff0c;一直往左孩子方向遍历&#xff0c;每遍历到一个结点就入栈并且加入到顺序表里&#xff0c;如果没有左孩子了&#xff0c;就拿出栈顶元素&#xff0c;看它是否有右孩子…

2024年牛客网最全1000道Java中高级面试题包含答案详解,看完稳了

我相信大多 Java 开发的程序员或多或少经历过 BAT 一些大厂的面试&#xff0c;也清楚一线互联网大厂 Java 面试是有一定难度的&#xff0c;小编经历过多次面试&#xff0c;有满意的也有备受打击的。因此呢小编想把自己这么多次面试经历以及近期的面试真题来个汇总分析&#xff…