Java项目打包部署

一、Spring Boot项目打Jar包发布(项目直接创建(IDEA同理):https://start.spring.io/)

1:java -jar XXX.jar  特点:当前ssh窗口被锁定,可按CTRL + C打断程序运行,或直接关闭窗口,程序退出

2:java -jar XXX.jar &  &代表在后台运行。特定:当前ssh窗口不被锁定,但是当窗口关闭时,程序中止运行。

3:nohup java -jar XXX.jar &  nohup 意思是不挂断运行命令,当账户退出或终端关闭时,程序仍然运行

4:nohup java -jar XXX.jar >temp.txt &(或 nohup java -jar XXX.jar >temp.txt 2>1 &)  即输出内容不打印到屏幕上,而是输出到temp.txt文件中。可以使用 top 命令查看运行中的进程,也可以用 ps -aux 查看进程 kill -9 pid删除

  • 发布:nohup java -jar .jar &
  • 日志查看:tail -f nohup.out
  • 查看执行jar进程:ps -ef| grep .jar 或者使用jobs命令查看后台运行任务
  • 杀死进程:kill -9 pid

二、Spring Boot项目打war包部署到外部Tomcat

1、修改POM文件 

<packaging>war</packaging>

2、添加spring-boot-starter-tomcat依赖,scope设置为provided

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>

3、注册启动类,创建ServletInitializer.java,继承SpringBootServletInitializer ,覆盖configure(),把启动类Application注册进去。外部web应用服务器构建Web Application Context的时候,会把启动类添加进去。

public class ServletInitializer extends SpringBootServletInitializer {
  @Override
  protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
    return application.sources(Application.class); //(项目名称)Application(根据自己的项目名称来定)
  }
}

4、通过IDEA的Maven工具生成,点击run as ---maven install,生成war,可以直接拷到tomcat的webapps目录下,启动Tomcat即可。

5、注意事项

  1) 注意同一端口号的多个项目部署时,在tomcat/config/server.xml中添加,如:

<Context path="/project" reloadable="true" docBase="/home/develop/tomcat-8.5/webapps"/> 
<Context path="/project2" reloadable="true" docBase="/home/develop/tomcat-8.5/webapps"/>

  通过访问:http://127.0.0.1:8080/project  http://127.0.0.1:8080/project 对应各子的项目

2) 跳转找不到项目名称或者是想直接访问ip地址就能访问项目地址的操作如下:
进入的Tomcat / conf/ server.xml的文件,修改Tomcat的端口号。
(1)端口号

<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />

(2)修改ip和docBase

<Engine name="Catalina" defaultHost="127.0.0.1">

<Host name="127.0.0.1" appBase="webapps"
unpackWARs="true" autoDeploy="true">
<Context path="" docBase="/usr/local/apache-tomcat-8.5/webapps/war包名称" debug="0" reloadable="true"/>

   autoDeploy: 如果此项设为true,表示Tomcat服务处于运行状态时,能够监测appBase下的文件,如果有新有web应用加入进来,会自运发布这个WEB应用。

  unpackWARs: 如果此项设置为true,表示把WEB应用的WAR文件先展开为开放目录结构后再运行。如果设为false将直接运行为WAR文件

三、Docker 部署优化:配置可视化,解放双手迈出一小步

 1、项目打包pom.依赖build修改

springboot 不重启释放内存 springboot项目不停机部署_java

springboot 不重启释放内存 springboot项目不停机部署_spring_02

<build>
        <!-- 打包名称 -->
        <finalName>${project.artifactId}</finalName>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <includeSystemScope>true</includeSystemScope>
                </configuration>
            </plugin>
            <!-- 打jar包时忽略配置文件 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>**/application.yml</exclude>
<!--                        <exclude>**/application*.properties</exclude>-->
<!--                        <exclude>**/logback*.xml</exclude>-->
<!--                        <exclude>**/bootstrap.yml</exclude>-->
<!--                        <exclude>**/setting.properties</exclude>-->
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>

View Code

 2、启动jar包时候需要指定外部的启动文件

  • a、指定的是目录: java -jar -Dspring.config.location=/opt/java/config/ fdemo.jar
  • b、指定的是文件: java -jar -Dspring.config.location=/opt/java/config/application.yml fdemo.jar
  • c、配置文件放在jar包同级,或者新建config文件夹放入,spring默认会去./config目录中查询

 3、配置实现可视化:/opt/java/config/application.yml

   a、可视化工具 pom依赖

<dependency>
       <groupId>org.yaml</groupId>
       <artifactId>snakeyaml</artifactId>
</dependency>

   b、工具类

springboot 不重启释放内存 springboot项目不停机部署_java

springboot 不重启释放内存 springboot项目不停机部署_spring_02

public class YamlUtils {

    private final static DumperOptions OPTIONS = new DumperOptions();

    private static File file;

    private static InputStream ymlInputSteam;

    private static Object CONFIG_MAP;

    private static Yaml yaml;

    static {
        //将默认读取的方式设置为块状读取
        OPTIONS.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
    }

    /**
     * 使用其他方法之前必须调用一次 设置yml的输出文件,当没有设置输入流时可以不设置输入流,默认以此文件读入
     *
     * @param file 输出的文件
     */
    public static void setYmlFile(File file) throws FileNotFoundException {
        YamlUtils.file = file;
        if (ymlInputSteam == null) {
            setYmlInputSteam(new FileInputStream(file));
        }
    }


    /**
     * 使用其他方法之前必须调用一次 设置yml的输入流
     *
     * @param inputSteam 输入流
     */
    public static void setYmlInputSteam(InputStream inputSteam) {
        ymlInputSteam = inputSteam;
        yaml = new Yaml(OPTIONS);
        CONFIG_MAP = yaml.load(ymlInputSteam);
    }

    /**
     * 根据键获取值
     *
     * @param key 键
     * @return 查询到的值
     */
    @SuppressWarnings("unchecked")
    public static Object getByKey(String key) {
        if (ymlInputSteam == null) {
            return null;
        }
        String[] keys = key.split("\\.");
        Object configMap = CONFIG_MAP;
        for (String s : keys) {
            if (configMap instanceof Map) {
                configMap = ((Map<String, Object>) configMap).get(s);
            } else {
                break;
            }
        }
        return configMap == null ? "" : configMap;
    }

    public static void saveOrUpdateByKey(String key, Object value) throws IOException {
        KeyAndMap keyAndMap = new KeyAndMap(key).invoke();
        key = keyAndMap.getKey();
        Map<String, Object> map = keyAndMap.getMap();
        map.put(key, value);
        //将数据重新写回文件
        yaml.dump(CONFIG_MAP, new FileWriter(file));
    }

    public static void removeByKey(String key) throws Exception {
        KeyAndMap keyAndMap = new KeyAndMap(key).invoke();
        key = keyAndMap.getKey();
        Map<String, Object> map = keyAndMap.getMap();
        Map<String, Object> fatherMap = keyAndMap.getFatherMap();
        map.remove(key);
        if (map.size() == 0) {
            Set<Map.Entry<String, Object>> entries = fatherMap.entrySet();
            for (Map.Entry<String, Object> entry : entries) {
                if (entry.getValue() == map) {
                    fatherMap.remove(entry.getKey());
                }
            }
        }
        yaml.dump(CONFIG_MAP, new FileWriter(file));
    }

    private static class KeyAndMap {
        private String key;
        private Map<String, Object> map;
        private Map<String, Object> fatherMap;

        public KeyAndMap(String key) {
            this.key = key;
        }

        public String getKey() {
            return key;
        }

        public Map<String, Object> getMap() {
            return map;
        }

        public Map<String, Object> getFatherMap() {
            return fatherMap;
        }

        @SuppressWarnings("unchecked")
        public KeyAndMap invoke() {
            if (file == null) {
                System.err.println("请设置文件路径");
            }
            if (null == CONFIG_MAP) {
                CONFIG_MAP = new LinkedHashMap<>();
            }
            String[] keys = key.split("\\.");
            key = keys[keys.length - 1];
            map = (Map<String, Object>) CONFIG_MAP;
            for (int i = 0; i < keys.length - 1; i++) {
                String s = keys[i];
                if (map.get(s) == null || !(map.get(s) instanceof Map)) {
                    map.put(s, new HashMap<>(4));
                }
                fatherMap = map;
                map = (Map<String, Object>) map.get(s);
            }
            return this;
        }
    }
}

View Code

  c、测试使用

springboot 不重启释放内存 springboot项目不停机部署_java

springboot 不重启释放内存 springboot项目不停机部署_spring_02

public static void main(String[] args) throws Exception {
        String pathName = "..\\config\\application.yml";
        File yml = new File(pathName);
        YamlUtils.setYmlFile(yml);
        YamlUtils.getByKey("spring.datasource.username");
        YamlUtils.saveOrUpdateByKey("spring.datasource.password", "123456");
        YamlUtils.removeByKey("server.port");
    }

View Code

  4、使用Dockerfile 构建镜像发布

  • 构建镜像:docker build -t javaweb . 
  • 挂载宿主机配置启动:docker run -dit --name testweb -p 80:80 -v  /opt/java/config:/opt/config  javaweb

#镜像支持 直接java:8需要600多M,这样只需要140M
FROM java:8-jdk-alpine
#配置jdk
ENV JAVA_OPTS="$JAVA_OPTS -Dfile.encoding=UTF8 -Duser.timezone=GMT+08"
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories && \
    apk add -U tzdata && \
    ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
#拷贝当前文件至容器
COPY *.jar /opt/
COPY start.sh /
#设置RUN CMD ENTRYPOINT COPY ADD指令的工作目录
WORKDIR /
#编译镜像时运行的脚本
RUN chmod +x start.sh
#设置容器的自动命令
CMD ["/start.sh"]
  • View Code

Dockerfile内容

  •  
  • 使用start.sh脚本启动(ENTRYPOINT指令运行同理)

springboot 不重启释放内存 springboot项目不停机部署_java_07