Docker一键部署项目,无需登录XShell

文章目录

  • 一键部署项目
    • Docker手动部署SpringBoot项目
      • 编写docker部署的脚本文件
        • script.sh 脚本内容
      • 特别注意!
      • 编写dockerfile
        • dockerfile 文件内容
      • 上传后端服务的jar包到服务器中
      • 执行 script 脚本部署后端服务
    • 自动部署SpringBoot项目
      • 引入jsch依赖
      • 编写jsch工具类
      • 执行部署命令
    • Docker手动部署Vue项目
      • 编写docker部署的脚本文件
        • script.sh 脚本内容
      • 编写dockerfile
        • dockerfile文件内容
      • 编写nginx配置文件
        • nginx配置文件内容
      • 上传前端服务的打包文件到服务器
      • 执行 script 脚本部署前端服务
      • 访问 ip:9002 查看是否运行成功
    • 自动部署Vue项目
      • 工具类(慎重!包含服务器的删除操作!)
      • main 函数
  • 总结

一键部署项目

Git仓库地址!

我们每次部署项目都需要手动传输 jar 包,或者前端打包好的文件到服务器上,再连接服务器执行部署脚本,为了省去这个步骤,我们直接借助 jsch 包,来通过执行一个方法,就可以将打包好的文件上传至服务器,并且自动执行部署脚本。

Docker手动部署SpringBoot项目

编写docker部署的脚本文件

cd / # 进入根目录
mkdir develop/docker-deploy # 创建两级目录
cd develop/docker-deploy
vi script.sh # 编写部署脚本

chmod +x ./script.sh # 授予脚本文件可执行的权限

script.sh 脚本内容

特别注意!

在 script.sh 脚本文件中,docker build 命令使用 -f 参数指定 dockerfile 文件的路径,并且指定docker镜像构建的上下文

  • dockerfile 文件路径为 /develop/docker-deploy/dockerfile

  • 指定 docker 镜像构建上下文目录为 /develop/docker-deploy/

那么在镜像构建的时候,就会把当前服务器的 /develop/docker-deploy/目录下的所有文件都发送给 docker 的守护进程。

在构建的时候会去执行 dockerfile 中的命令,在 dockerfile 中,ADD <src> <dest> 将 src 文件复制到容器的 dest 中,这里的 src 只能使用相对路径,相对的就是构建镜像的上下文,即 /develop/docker-deploy/ 目录。

我们后边使用 jsch 进行自动化部署时,由于 jsch 无法执行 cd 命令,不能切换路径,因此都需要通过绝对路径来指定docker的构建环境!

#!/bin/bash

#构建docker镜像
echo "========================================正在构建镜像================================="
# -f 指定dockerfile文件  docker build最后边的参数不可以使用. 如果在其他目录执行该脚本文件
# 就会把执行脚本文件的目录当作构建镜像的上下文传送给docker的守护进程,那么在dockerfile中通过add添加的文件
# 都是以相对路径添加的,那么就会出现找不到文件的情况
docker build -t docker-deploy:01 -f /develop/docker-deploy/dockerfile /develop/docker-deploy/ 

# 停止并删除容器
echo "========================================正在删除容器================================="
docker stop docker-deploy
docker rm docker-deploy

# 运行容器
echo "========================================正在创建新容器================================="
docker run --name docker-deploy -p 9001:9001 -d docker-deploy:01

编写dockerfile

cd develop/docker-deploy
vi dockerfile # 编写 dockerfile,如下

dockerfile 文件内容

#基础镜像
FROM openjdk:8-jdk-alpine
#作者
MAINTAINER abc
#执行命令,主要用来安装相关的软件
#RUN
# ADD将文件复制到镜像中
ADD Docker-Deploy-0.0.1-SNAPSHOT.jar /usr/local 
RUN chmod u+x /usr/local/Docker-Deploy-0.0.1-SNAPSHOT.jar # 给当前目录下的test.py文件所有者添加执行权限
#挂载目录到容器
#VOLUME ["/data"]
#环境变量设置
#ENV #开放端口
EXPOSE 9001
#启动时执行的命令
CMD ["/bin/bash"]
#启动时执行的命令
ENTRYPOINT ["java","-jar","/usr/local/Docker-Deploy-0.0.1-SNAPSHOT.jar"]

上传后端服务的jar包到服务器中

最后目录如下:

在这里插入图片描述

执行 script 脚本部署后端服务

/develop/docker-deploy/script.sh # 测试绝对路径执行脚本

显示结果如下,表示执行成功

在这里插入图片描述

使用 docker ps 查看是否部署 docker 镜像成功

在这里插入图片描述

那么,手动部署没问题了,我们就可以通过 Java 的 jsch 包来实现不需要去服务器就可以进行部署项目。

自动部署SpringBoot项目

引入jsch依赖

<!--    jsch	-->
<dependency>
    <groupId>com.jcraft</groupId>
    <artifactId>jsch</artifactId>
    <version>0.1.54</version>
</dependency>

编写jsch工具类

package com.example.jschquickdeploy.utils;

import com.jcraft.jsch.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

/**
 * @author 千祎来了
 * @date 2023/8/7 15:08
 */
public class ShellCommandExecution {

    private static final Logger logger = LoggerFactory.getLogger(ShellCommandExecution.class);

    public static Session getSession(String username, String password, String host, int port) {
        // 创建JSch对象
        JSch jSch = new JSch();
        Session jSchSession = null;
        try {

            // 根据主机账号、ip、端口获取一个Session对象
            jSchSession = jSch.getSession(username, host, port);

            // 存放主机密码
            jSchSession.setPassword(password);

            Properties config = new Properties();

            // 去掉首次连接确认
            config.put("StrictHostKeyChecking", "no");

            jSchSession.setConfig(config);

            // 超时连接时间为3秒
            jSchSession.setTimeout(3000);
            // 进行连接
            jSchSession.connect();

        } catch (Exception e) {
            logger.warn(e.getMessage());
        }
        return jSchSession;
    }

    /**
     * 下载文件,将 sourceFile 文件下载至 destinationFile
     * @param jSchSession
     * @param sourceFile
     * @param destinationFile
     * @throws Exception
     */
    public static void downloadFiles(Session jSchSession, String sourceFile, String destinationFile) throws Exception {
        // Opening an SFTP Channel
        ChannelSftp chSftp = (ChannelSftp)jSchSession.openChannel("sftp");

        // Example Establish a connection to the SFTP channel
        chSftp.connect();

        // Set encoding format
        chSftp.setFilenameEncoding("UTF-8");

        /**
         * 说明:
         * 1、当前上读取文件信息没有任何反馈,如果没有异常则代表成功
         * 2、如果需要判断是否读取成功的进度,可参考https://blog.csdn.net/coding99/article/details/52416373?locationNum=13&fps=1
         * 3、将src文件下载到dst路径中
         */
        chSftp.get(sourceFile, destinationFile);

        logger.info("download file " + sourceFile + " success,location is:" + destinationFile);
    }

    /**
     * 将 sourceFile 上传至 destinationFile
     * @param jSchSession
     * @param sourceFile
     * @param destinationFile
     */
    public static void uploadFiles(Session jSchSession, String sourceFile, String destinationFile) throws Exception{
        // Opening an SFTP Channel
        ChannelSftp chSftp = (ChannelSftp)jSchSession.openChannel("sftp");

        // Example Establish a connection to the SFTP channel
        chSftp.connect();

        // Set encoding format
        chSftp.setFilenameEncoding("UTF-8");

        /**
         * 说明:
         * 1、当前上读取文件信息没有任何反馈,如果没有异常则代表成功
         * 2、如果需要判断是否读取成功的进度,可参考https://blog.csdn.net/coding99/article/details/52416373?locationNum=13&fps=1
         * 3、将src文件下载到dst路径中
         */
        chSftp.put(sourceFile, destinationFile);

        logger.info("upload file " + sourceFile + " success,location is:" + destinationFile);
    }

    /**
     * 执行命令
     * @param command
     * @param jSchSession
     * @return
     * @throws Exception
     */
    public static StringBuffer execCommand(String command, Session jSchSession) throws Exception{
        Channel exec = null;
        StringBuffer result = new StringBuffer();
        try {
            exec = jSchSession.openChannel("exec");

            ((ChannelExec) exec).setCommand(command);

            exec.setInputStream(null);
            // 错误信息输出流,用于输出错误的信息,当exitstatus<0的时候
            ((ChannelExec) exec).setErrStream(System.err);

            // 执行命令,等待结果
            exec.connect();

            // 获取命令执行结果
            InputStream in = exec.getInputStream();

            // 推出状态码
            int exitStatus = 0;
            /**
             * 通过channel获取信息的方式,采用官方Demo代码
             */
            byte[] tmp = new byte[1024];
            while (true) {
                while (in.available() > 0) {
                    int i = in.read(tmp, 0, 1024);
                    if (i < 0) {
                        break;
                    }
                    result.append(new String(tmp, 0, i));
                }
                // 从channel获取全部信息之后,channel会自动关闭
                if (exec.isClosed()) {
                    if (in.available() > 0) {
                        continue;
                    }
                    exitStatus = exec.getExitStatus();
                    break;
                }
                try {
                    Thread.sleep(1000);
                } catch (Exception ee) {
                }
            }
            logger.info("命令:【" + command + "】的执行结果为:" + result);

            // 如果状态码不是0,则表示执行失败
            if (exitStatus != 0) {
                logger.error("状态码为:" + exitStatus + ",命令执行失败:" + result);
                throw new Exception("命令:【" + command + "】执行失败,执行结果为:" + result);
            }
        } finally {
            // 关闭 jschChannel 流
            if (exec != null && exec.isConnected()) {
                exec.disconnect();
            }
        }
        return result;
    }
}

执行部署命令

在 main 方法中,只需要做2件事就可以一键部署了:

  1. 将我们使用 maven 工具打包好的 jar 包的路径上传到服务器指定目录
  2. 调用服务器上的执行脚本即可

注意:这里执行需要使用绝对路径,在 jsch 中无法使用 cd 命令

package com.example.jschquickdeploy.utils;

import com.jcraft.jsch.JSch;
import com.jcraft.jsch.Session;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Properties;

/**
 * @author 千祎来了
 * @date 2023/8/9 21:43
 */
public class QuickDeploy {

    private static final Logger logger = LoggerFactory.getLogger(ShellCommandExecution.class);

    public static void main(String[] args) {
        /**
         * 连接服务器
         */
        String username = "root";
        String password = "123456";
        String host = "127.0.0.1";
        int port = 2222;
        Session jSchSession = null;
        try {
            jSchSession = ShellCommandExecution.getSession(username, password, host, port);


            /**
             * 上传 jar 包到服务器
             */
            ShellCommandExecution.uploadFiles(jSchSession, "D:\\Project\\IDEA\\Docker-Deploy\\target\\Docker-Deploy-0.0.1-SNAPSHOT.jar", "/develop/docker-deploy");

            /**
             * 执行部署命令
             */
            ShellCommandExecution.execCommand("/develop/docker-deploy/script.sh", jSchSession);

            /**
             * 查看镜像
             */
            ShellCommandExecution.execCommand("docker ps", jSchSession);

        } catch (Exception e) {
            logger.warn(e.getMessage());
        } finally {
            // close jschSesson stream
            if (jSchSession != null && jSchSession.isConnected()) {
                jSchSession.disconnect();
            }
        }
    }
}

Docker手动部署Vue项目

编写docker部署的脚本文件

cd /develop # 进到部署对应目录
mkdir docker-deploy-front # 创建前端部署目录
cd docker-deploy-front
vi script.sh # 编写部署脚本
chmod +x ./script.sh # 授予脚本执行权限

script.sh 脚本内容

#!/bin/bash

echo "========================================正在构建镜像================================="
docker build -t docker-deploy-front:1.0 -f /develop/docker-deploy-front/dockerfile /develop/docker-deploy-front/

# 停止并删除容器
echo "========================================正在删除容器================================="
docker stop docker-deploy-front
docker rm docker-deploy-front

# 运行容器
echo "========================================正在创建新容器================================="
docker run --name docker-deploy-front -d -p 9002:80  docker-deploy-front:1.0
~                                                                                    

编写dockerfile

vi dockerfile

dockerfile文件内容

# 设置基础镜像,这里使用最新的nginx镜像,前面已经拉取过了
FROM nginx
#作者
MAINTAINER abc
#执行命令,主要用来安装相关的软件
RUN rm /etc/nginx/conf.d/default.conf
# 将 nginx 配置文件复制到镜像中
ADD default.conf /etc/nginx/conf.d/ 

#添加文件
#将dist文件中的内容复制到 /usr/share/nginx/html/ 这个目录下面
#容器内nginx默认的html文件目录为 /usr/share/nginx/html/
COPY dist/  /usr/share/nginx/html/

编写nginx配置文件

vi default.conf

nginx配置文件内容

server {
    listen       80;
    listen  [::]:80;
    server_name  localhost;

    location / {
        root   /usr/share/nginx/html;
        index  index.html index.htm;
        try_files $uri $uri/ /index.html;
    }

    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }
}

上传前端服务的打包文件到服务器

进入 vue 项目根目录,调用 npm run build 打包前端项目,生成 dist 文件夹

最后,前端需要的所有文件如下:

在这里插入图片描述

执行 script 脚本部署前端服务

执行 ./script 即可

/develop/docker-deploy-front/script # 执行脚本
docker ps # 查看docker容器是否启动成功

访问 ip:9002 查看是否运行成功

http://localhost:9002/ (我这里是本地,服务器要加上服务器ip)

在这里插入图片描述

自动部署Vue项目

这里使用的项目和自动部署后端项目使用的是同一个项目,因此依赖和工具类不需要重新引入

由于自动部署后端代码时,上传的只有一个 jar 包,因此上传功能比较好些,但是 vue 项目打包生成的是 dist 目录,因此需要给工具类添加上传目录的方法,如下:

工具类(慎重!包含服务器的删除操作!)

拿到工具类之后,不要立即执行在,将路径换为自己服务器上的路径再执行!以免误删文件!

uploadDir 方法中有一个删除操作,删除的是服务器上已经存在的打包的目录,也就是服务器上的 dist 文件夹所在目录!

相较于后端部署时,多添加了一个 uploadDir 方法,用于上传一整个目录

该方法中使用递归去遍历文件,因为 jsch 中并没有直接上传目录的操作,因此需要我们自己去遍历目录,并上传每一个文件

package com.example.jschquickdeploy.utils;

import com.jcraft.jsch.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Objects;
import java.util.Properties;

/**
 * @author 千祎来了
 * @date 2023/8/7 15:08
 */
public class ShellCommandExecution {

    private static final Logger logger = LoggerFactory.getLogger(ShellCommandExecution.class);

    private static Session jSchSession = null;

    public static Session getSession(String username, String password, String host, int port) {
        // 创建JSch对象
        JSch jSch = new JSch();
        Session jSchSession = null;
        try {

            // 根据主机账号、ip、端口获取一个Session对象
            jSchSession = jSch.getSession(username, host, port);

            // 存放主机密码
            jSchSession.setPassword(password);

            Properties config = new Properties();

            // 去掉首次连接确认
            config.put("StrictHostKeyChecking", "no");

            jSchSession.setConfig(config);

            // 超时连接时间为3秒
            jSchSession.setTimeout(3000);
            // 进行连接
            jSchSession.connect();

            ShellCommandExecution.jSchSession = jSchSession;
        } catch (Exception e) {
            logger.warn(e.getMessage());
        }
        return jSchSession;
    }

    /**
     * 下载文件,将 sourceFile 文件下载至 destinationFile
     * @param sourceFile
     * @param destinationFile
     * @throws Exception
     */
    public static void downloadFiles(String sourceFile, String destinationFile) throws Exception {
        // Opening an SFTP Channel
        ChannelSftp chSftp = (ChannelSftp)jSchSession.openChannel("sftp");

        // Example Establish a connection to the SFTP channel
        chSftp.connect();

        // Set encoding format
        chSftp.setFilenameEncoding("UTF-8");

        /**
         * 说明:
         * 1、当前上读取文件信息没有任何反馈,如果没有异常则代表成功
         * 2、如果需要判断是否读取成功的进度,可参考https://blog.csdn.net/coding99/article/details/52416373?locationNum=13&fps=1
         * 3、将src文件下载到dst路径中
         */
        chSftp.get(sourceFile, destinationFile);

        logger.info("download file " + sourceFile + " success,location is:" + destinationFile);
    }

    /**
     * 将 sourceDir 目录下的所有文件上传至 destinationDir 目录下
     * 假设 sourceDir = "D:\Project\IDEA\Docker-Quick-Deploy\docker-deploy-front\dist"
     * destinationDir = "/develop/docker-deploy-front"
     * 那么该方法将 dist 目录所有文件都传输至 /develop/docker-deploy-front/dist 目录中
     * @param sourceDir
     * @param destinationDir
     * @throws Exception
     */
    public static void uploadDir(String sourceDir, String destinationDir) throws Exception{

        // Opening an SFTP Channel
        ChannelSftp chSftp = (ChannelSftp)jSchSession.openChannel("sftp");

        // Example Establish a connection to the SFTP channel
        chSftp.connect();

        // Set encoding format
        chSftp.setFilenameEncoding("UTF-8");

        // 文件夹名称
        String dirName = sourceDir.substring(sourceDir.lastIndexOf("\\") + 1);
        String linuxRootDir =  destinationDir + "/" + dirName;
        String windowsRootDir =  sourceDir;
        execCommand("rm -rf " +  linuxRootDir);
        logger.info("删除文件夹" + linuxRootDir);
        execCommand("mkdir " +  linuxRootDir);
        logger.info("创建文件夹" + linuxRootDir);

        /**
         * 将 sourceDir 目录下的文件传输至 targetDir
         */
        transFile(chSftp, sourceDir, windowsRootDir, linuxRootDir);

    }


    /**
     * 递归传输文件
     *
     * @param chSftp
     * @param sourceDir
     * @param windowsRootDir
     * @param linuxRootDir
     * @throws IOException
     * @throws SftpException
     */
    private static void transFile(ChannelSftp chSftp, String sourceDir, String windowsRootDir, String linuxRootDir) throws Exception {
        File root = new File(sourceDir);
        for (File file : Objects.requireNonNull(root.listFiles())) {
            if (file.isFile()) {
                Path src = Paths.get(file.getPath());
                String des = file.getPath().replace(windowsRootDir, linuxRootDir).replace("\\", "/");
                file.getPath().replace(windowsRootDir, "");
                logger.info("上传源文件:" + src.toString() + "到目的文件:" + des);
                chSftp.put(Files.newInputStream(src), des);
            } else if (file.isDirectory()) {
                String dirName = file.getPath().replace(windowsRootDir, linuxRootDir).replace("\\", "/");
                execCommand("mkdir " + dirName);
                logger.info("创建文件夹:" + dirName);
                transFile(chSftp, file.getPath(), windowsRootDir, linuxRootDir);
            }
        }
    }

    /**
     * 将 sourceFile 上传至 destinationFile
     * @param sourceFile 必须为文件
     * @param destinationFile
     */
    public static void uploadFiles(String sourceFile, String destinationFile) throws Exception{
        ChannelSftp chSftp = null;
        try {
            // Opening an SFTP Channel
            chSftp = (ChannelSftp)jSchSession.openChannel("sftp");

            // Example Establish a connection to the SFTP channel
            chSftp.connect();

            // Set encoding format
            chSftp.setFilenameEncoding("UTF-8");

            /**
             * 说明:
             * 1、当前上读取文件信息没有任何反馈,如果没有异常则代表成功
             * 2、如果需要判断是否读取成功的进度,可参考https://blog.csdn.net/coding99/article/details/52416373?locationNum=13&fps=1
             * 3、将src文件下载到dst路径中
             */
            chSftp.put(sourceFile, destinationFile, ChannelSftp.OVERWRITE);

            logger.info("upload file " + sourceFile + " success,location is:" + destinationFile);
        } finally {
            // 关闭 jschChannel 流
            if (chSftp != null && chSftp.isConnected()) {
                chSftp.disconnect();
            }
        }
    }

    /**
     * 执行命令
     * @param command
     * @return
     * @throws Exception
     */
    public static StringBuffer execCommand(String command) throws Exception{
        Channel exec = null;
        StringBuffer result = new StringBuffer();
        try {
            exec = jSchSession.openChannel("exec");

            ((ChannelExec) exec).setCommand(command);

            exec.setInputStream(null);
            // 错误信息输出流,用于输出错误的信息,当exitstatus<0的时候
            ((ChannelExec) exec).setErrStream(System.err);

            // 执行命令,等待结果
            exec.connect();

            // 获取命令执行结果
            InputStream in = exec.getInputStream();

            // 推出状态码
            int exitStatus = 0;
            /**
             * 通过channel获取信息的方式,采用官方Demo代码
             */
            byte[] tmp = new byte[1024];
            while (true) {
                while (in.available() > 0) {
                    int i = in.read(tmp, 0, 1024);
                    if (i < 0) {
                        break;
                    }
                    result.append(new String(tmp, 0, i));
                }
                // 从channel获取全部信息之后,channel会自动关闭
                if (exec.isClosed()) {
                    if (in.available() > 0) {
                        continue;
                    }
                    exitStatus = exec.getExitStatus();
                    break;
                }
                try {
                    Thread.sleep(1000);
                } catch (Exception ee) {
                }
            }
            logger.info("命令:【" + command + "】的执行结果为:" + result);

            // 如果状态码不是0,则表示执行失败
            if (exitStatus != 0) {
                logger.error("状态码为:" + exitStatus + ",命令执行失败:" + result);
                throw new Exception("命令:【" + command + "】执行失败,执行结果为:" + result);
            }
        } finally {
            // 关闭 jschChannel 流
            if (exec != null && exec.isConnected()) {
                exec.disconnect();
            }
        }
        return result;
    }
}


main 函数

package com.example.jschquickdeploy.utils;

import com.jcraft.jsch.Session;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @author 千祎来了
 * @date 2023/8/10 9:51
 */
public class QuickDeployFront {

    private static final Logger logger = LoggerFactory.getLogger(ShellCommandExecution.class);

    public static void main(String[] args) {
        /**
         * 连接服务器
         */
        String username = "root";
        String password = "123456";
        String host = "127.0.0.1";
        int port = 2222;
        Session jSchSession = null;
        try {
            jSchSession = ShellCommandExecution.getSession(username, password, host, port);


            /**
             * 上传 Vue 打包文件到服务器
             */
            ShellCommandExecution.uploadDir(jSchSession, "D:\\Project\\IDEA\\Docker-Quick-Deploy\\docker-deploy-front\\dist", "/develop/docker-deploy-front");

            /**
             * 执行部署命令
             */
            ShellCommandExecution.execCommand("/develop/docker-deploy-front/script.sh", jSchSession);

            /**
             * 查看镜像
             */
            ShellCommandExecution.execCommand("docker ps", jSchSession);

        } catch (Exception e) {
            logger.warn(e.getMessage());
        } finally {
            // close jschSesson stream
            if (jSchSession != null && jSchSession.isConnected()) {
                jSchSession.disconnect();
            }
        }
    }
}

总结

这样一键部署项目就完成了,部署步骤如下:

后端部署:

  1. 对 springboot 项目打成 jar 包
  2. 在方法中指定 jar 包位置和上传服务器位置
  3. 在方法中指定部署脚本 script 执行命令
  4. 执行 main 方法

前端部署:

  1. 对 vue 项目打包
  2. 在方法中指定 dist 文件夹位置和上传服务器位置
  3. 在方法中指定部署脚本 script 执行命令
  4. 执行 main 方法

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

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

相关文章

工业4.0:欢迎来到智能制造

制造业正在经历一场被称为“工业4.0”的全新技术革命&#xff0c;这场革命将数字化、网络化、智能化和自动化技术融合在一起&#xff0c;旨在打造高质、高效、高产且可持续的智能工厂。工业4.0将彻底改变产品制造的方式&#xff0c;颠覆我们对制造业的传统认知。 什么是工业4.…

【Java】try|catch|throws 具体详解+应用

目录 tryCatch 基本介绍 使用细节 throws异常处理 基本介绍 ​ 使用细节 自定义异常 基本概念 步骤 throw和throws的区别 tryCatch 基本介绍 使用细节 throws异常处理 基本介绍 使用细节 自定义异常 基本概念 步骤 throw和throws的区别

【数据结构】Disruptor环形数组无锁并发框架阅读

Disruptor 是苹国外厂本易公司LMAX开发的一个高件能列&#xff0c;研发的初夷是解决内存队列的延识问顾在性能测试中发现竟然与10操作处于同样的数量级)&#xff0c;基于Disruptor开发的系统单线程能支撑每秒600万订单&#xff0c;2010年在QCn演讲后&#xff0c;获得了业界关注…

MinGW-W64 下载、安装与配置(支持最新版的GCC,目前 GCC 13.2.0)

文章目录 一、简介1. MinGW 和 MinGW-W64 区别和联系2. MSVCRT 和 UCRT 介绍 二、下载1. 从 sourceforge.net 下载2. 从 github 下载3. 从 镜像站点 下载4. 自己编译 三、安装与配置1. 在线安装2. 离线安装3. 环境配置 四、总结 一、简介 1. MinGW 和 MinGW-W64 区别和联系 M…

数组slice、splice字符串substr、split

一、定义 这篇文章主要对数组操作的两种方法进行介绍和使用&#xff0c;包括&#xff1a;slice、splice。对字符串操作的两种方法进行介绍和使用&#xff0c;包括&#xff1a;substr、split (一)、数组 slice:可以操作的数据类型有&#xff1a;数组字符串 splice:数组 操作数组…

【go语言学习笔记】04 Go 语言工程管理

文章目录 一、质量保证1. 单元测试1.1 定义1.2 Go 语言的单元测试1.3 单元测试覆盖率 2. 基准测试2.1 定义2.2 Go 语言的基准测试2.3 计时方法2.4 内存统计2.5 并发基准测试2.6 基准测试实战 3. 特别注意 二、性能优化1. 代码规范检查1.1 定义1.2 golangci-lint1.2.1 安装1.2.2…

vue3 + ts+element-plus学习笔记

子组件通过defineProps方法接收父组件传递过来的数据&#xff0c;是vue3中提供的方法&#xff0c;不需要引入&#xff0c;直接使用 方法的写法&#xff1a; const onClick (){... }自定义事件&#xff1a; 子组件点击事件 全局事件总线 mitt 兄弟组件之间的事件&#x…

【福建事业单位-资料分析】03 比重和平均数

【福建事业单位-资料分析】03 比重和平均数 一、比重&#xff08;现期比重&#xff0c;基期比重、两期比重&#xff09;1.1 现期比重增长贡献量&#xff0c;利润率 1.2基期比重&#xff08;用现期和增长率逆求&#xff09;1.3两期比重&#xff08;难点重点&#xff09;——比较…

基础实验篇 | QGC实时调整控制器参数实验

PART 1 实验名称及目的 QGC实时调整控制器参数实验&#xff1a;在进行硬件在环仿真和真机实验时&#xff0c;常常需要在QGC地面站中观察飞行状态&#xff0c;并对控制器参数进行实时调整&#xff0c;以使得飞机达到最佳的控制效果&#xff0c;但是&#xff0c;在Simulink中设…

改进的麻雀算法优化最大相关峭度解卷积(SCSSA-MCKD),实现早期微弱故障诊断,MATLAB代码实现

01 引言 由于一些设备的早期故障产生的冲击十分微弱&#xff0c;易被系统噪声干扰&#xff0c;如何有效地对设备的原始故障信号进行降噪并增强信号中微弱冲击成分&#xff0c;是进行该类部件早期故障诊断的关键。 最大相关峭度解卷积&#xff08;MCKD&#xff09;通过解卷积运算…

logstash日志换行处理小解

logstash主用于日志实时数据收集、解析&#xff0c;并将数据转发的工具&#xff0c;内置的功能也相当强大。但&#xff0c;同时意味着&#xff0c;他可能接收到各种情况的数据。 此处&#xff0c;我们主要讲解我实际使用中&#xff0c;碰到的一个小问题&#xff0c;换行(\n)。…

Zabbix监控系统详解及配置

前言 作为一个运维&#xff0c;需要会使用监控系统查看服务器状态以及网站流量指标&#xff0c;利用监控系统的数据去了解上线发布的结果&#xff0c;和网站的健康状态。利用一个优秀的监控软件&#xff0c;我们可以&#xff1a; 通过一个友好的界面进行浏览整个网站所有的服务…

JavaScript应用:五子棋游戏实战开发

&#x1f3c6;作者简介&#xff0c;黑夜开发者&#xff0c;全栈领域新星创作者✌&#xff0c;CSDN博客专家&#xff0c;阿里云社区专家博主&#xff0c;2023年6月csdn上海赛道top4。 &#x1f3c6;数年电商行业从业经验&#xff0c;历任核心研发工程师&#xff0c;项目技术负责…

MChat-Gpt V1.0.0 (将ChatGpt机器人接入内网供全体使用)

Github>https://github.com/MartinxMax/MChat-Gpt 首页 MChat-Gpt V1.0.0将ChatGpt机器人接入内网供全体使用 你需要一个ChatGpt账户如果您在中国则需要使用代理访问,设置TUN代理模式 安装依赖 选择你的系统进行安装 服务端配置 #python3 ChatGpt_Server.py -h 使用&a…

汉字形近字(OCR)

近期做中文OCR识别的优化&#xff0c;抓破头皮却收获甚微。 为了百尺竿头更进一步&#xff0c;遂将目光聚焦在中文汉字特有的形近字和生僻字问题上&#xff0c;于是怒发整理形近字大全&#xff08;花了不少刀&#xff09;&#xff0c;希望对同行朋友们也有帮助&#xff1a; 地表…

【BMC】OpenBMC开发基础3:引入新的开源配方

引入新的开源配方 前面介绍了如何在OpenBMC中通过新建配方引入自己的程序&#xff0c;也介绍了如何修改原有的程序&#xff0c;下面要介绍的是如何引入开源的新程序&#xff0c;这在OE系统上是很方便的&#xff0c;重点就在于引入新的配方。 OE为了方便开发者使用&#xff0c…

【如何在Linux环境下进入Docker容器中的MySQL】

如何在Linux环境下进入Docker容器中的MySQL 查看所有容器 docker ps进入容器 docker exce -it {NAMES/CONTAINER ID} bash根据容器别名获取容器ID都可以进入到容器当中 3. 输入MySQL的账号和密码登录MySQL mysql -uroot -p{password}

竞赛项目 深度学习的视频多目标跟踪实现

文章目录 1 前言2 先上成果3 多目标跟踪的两种方法3.1 方法13.2 方法2 4 Tracking By Detecting的跟踪过程4.1 存在的问题4.2 基于轨迹预测的跟踪方式 5 训练代码6 最后 1 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 基于深度学习的视频多目标跟踪实现 …

HTTP代理授权方式介绍

在网络爬虫过程中&#xff0c;我们经常需要使用HTTP代理来实现IP隐藏、突破限制或提高抓取效率。而为了确保代理的正常使用&#xff0c;并避免被滥用&#xff0c;代理服务商通常会采用授权方式。在本文中&#xff0c;我们将介绍几种常见的HTTP代理授权方式&#xff0c;以帮助你…

Goland报错 : Try to open it externally to fix format problem

这句报错的意思也就是 : 尝试在外部打开以解决格式问题 解决方案 : 将图片格式该为.png格式&#xff0c;再粘贴进去就可以了! 改变之后的效果 : 那么&#xff0c;这样就ok了
最新文章