SpringBoot之二:运维

多Module

什么是多Module

多模块项目指的是包含多个子模块(Module)的项目,在多模块项目中,父项目只作为一个子项目的容器而存在,不包含具体的代码。

为什么要用多Module

多Module本质上是一种封装,做一些解耦的工作。我觉得可以用在:

  • 公共模块的抽离

    抽离公共的模块,其他工程可以快速的引入、使用

  • api与service之间的分离

    如前后台系统,通过api的不同来做隔离,而service来共享服务

  • 为下一步多服务做一些准备

目前看,我觉得比较不错的分组方式是:

| - parent
		  | - common
		  | - api
		  | - service

这种适合api分离的场景

或者:

| - parent
		  | - common
		  | - biz

这种api不分离的场景

如何使用多Module

参考参考

  1. 父POM

    这里需要主要这几个配置:

    packaging:打包类型,可选选择jar、war、pom,父POM是pom类型,默认是jar,目前springboot中war用处减少了。

    modules:声明子模块

    dependencies:将全部的依赖都定义在父POM中,所有子模块都使用相同这些依赖,这样做的好处是子POM中的依赖就比较少了,坏处时不管该子POM需不需要,父POM中的依赖就会存在该子模块中。

    dependencyManagement:全部的依赖都声明在父POM中,主要管理这些POM的版本。子POM中再次声明时,就不用定义版本了,这样做到了版本一致。并且只有当子POM声明时,才会被引入到该子模块中。

    这样将dependencies与dependencyManagement连用,全部dependency的版本用dependencyManagement来管理,公共的dependency通过dependencies来声明。

    <!-- 基本信息 -->
        <description>SpringBoot 多模块构建</description>
        <modelVersion>4.0.0</modelVersion>
        <name>spring-boot-multi-module</name>
        <packaging>pom</packaging>
    
        <!-- 项目说明:这里作为聚合工程的父工程 -->
        <groupId>com.hehe</groupId>
        <artifactId>spring-boot-multi-module</artifactId>
        <version>1.0.0.RELEASE</version>
    
        <!-- 继承说明:这里继承SpringBoot提供的父工程 -->
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>1.5.7.RELEASE</version>
            <relativePath/>
        </parent>
    
        <!-- 模块说明:这里声明多个子模块 -->
        <modules>
            <module>mm-commont</module>
            <module>mm-service</module>
            <module>mm-api</module>
        </modules>
    
    <!-- 公共依赖-->
    <dependencies>
        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>19.0</version>
        </dependency>
    </dependencies>
    
        <!-- 版本说明:这里统一管理依赖的版本号 -->
        <<dependencyManagement>
        	<dependencies>
            	<dependency>
                	<groupId>org.apache.commons</groupId>
                	<artifactId>commons-lang3</artifactId>
                	<version>3.9</version>
            	</dependency>
        </dependencies>
    </dependencyManagement>
    
    </project>
    
    

    父级中,只留pom.xml与.gitignore即可,其他的.mvn、src等等都可以删除掉

  2. 子Module

    在父工程中增加新的Module,pom需要注意:

    parent: 声明parent项目

    build: 在可执行的jar上声明,构建可执行的JAR

    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
    
        <!-- 基本信息 -->
        <groupId>com.hehe</groupId>
        <artifactId>mm-web</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <packaging>jar</packaging>
        <name>mm-web</name>
    
        <!-- 继承本项目的父工程 -->
        <parent>
            <groupId>com.hehe</groupId>
            <artifactId>springboot-integration</artifactId>
            <version>1.0.0.RELEASE</version>
        </parent>
    
        <!-- Web模块相关依赖 -->
        <dependencies>
            <dependency>
                <groupId>com.hehe</groupId>
                <artifactId>mm-service</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
        </dependencies>
    
        <build>
            <plugins>
                <plugin>
                    <!--该插件主要用途:构建可执行的JAR -->
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                </plugin>
            </plugins>
        </build>
    </project>
    

Maven

这里对maven进行一个梳理,参考

简介

maven是java平台的自动化构建工具。这里的构建指的是把Web项目经过编译得到的编译结果,然后部署到服务器上的整个过程。

其各个周期包括:

  • 清理clean:将以前编译得到的旧文件class字节码文件删除

    mvn clean

  • 编译compile:将java源程序编译成class字节码文件

    mvn compile

  • 测试test:自动测试,自动调用junit程序

    mvn test-compile 与 mvn test

  • 报告report:测试程序执行的结果

  • 打包package:动态Web工程打War包,java工程打jar包

    mvn package

  • 安装install:将将包安装至本地仓库,以让其它项目依赖

    mvn install

  • 部署deploy:将动态Web工程生成的war包复制到Servlet容器下,使其可以运行

仓库和坐标

  • pom.xml 是maven核心配置文件

  • 坐标

    坐标指的是GAV:groupId-artifactId-version

  • 仓库

    仓库有2种:

    本地仓库:$HOME/.m2/repository

    远程仓库:私服,搭建在局域网中,一般公司都会有私服,私服一般使用nexus来搭建。

    ​ 中央仓库,架设在Internet上,像刚才的springframework就是在中央仓库上

依赖

  • maven解析依赖信息时会到本地仓库中取查找被依赖的jar包

对于本地仓库中没有的会去中央仓库去查找maven坐标来获取jar包,获取到jar之后会下载到本地仓库,如果没有则编译失败

  • 如果依赖的是自己或者团队开发的maven工程,需要先使用install命令把被依赖的maven工程的jar包导入到本地仓库中

    现在我再创建第二个maven工程HelloFriend,其中用到了第一个Hello工程里类的sayHello(String name)方法。我们在给HelloFriend项目使用 mvn compile命令进行编译的时候,会提示缺少依赖Hello的jar包。怎么办呢?

    到第一个maven工程中执行 mvn install后,你再去看一下本地仓库,你会发现有了Hello项目的jar包。一旦本地仓库有了依赖的maven工程的jar包后,你再到HelloFriend项目中使用 mvn compile命令的时候,可以成功编译

    示例

    WebMavenDemo项目依赖JavaMavenService1,JavaMavenService1项目依赖JavaMavenService2

    pom.xml文件配置好依赖关系后,必须首先mvn install后,依赖的jar包才能使用。

    • WebMavenDemo的pom.xml文件想能编译通过,JavaMavenService1必须mvn install
    • JavaMavenService的pom.xml文件想能编译通过,JavaMavenService2必须mvn install
  • 依赖范围

    **1、compile,**默认值,适用于所有阶段(开发、测试、部署、运行),本jar会一直存在所有阶段。

    **2、provided,**只在开发、测试阶段使用,目的是不让Servlet容器和你本地仓库的jar包冲突 。如servlet.jar。

    **3、runtime,**只在运行时使用,如JDBC驱动,适用运行和测试阶段。

    **4、test,**只在测试时使用,用于编译和运行测试代码。不会随项目发布。

    **5、system,**类似provided,需要显式提供包含依赖的jar,Maven不会在Repository中查找它。

  • 工具

    通过 mvn dependency:tree

    IDEA Maven Helper插件

  • 排除特定依赖

    exclusion

生命周期

Maven有三套相互独立的生命周期,

  • Clean Lifecycle 在进行真正的构建之前进行一些清理工作

  • Default Lifecycle 构建的核心部分,编译,测试,打包,部署等等。

  • Site Lifecycle 生成项目报告,站点,发布站点

build插件

<build>
    <plugins>
        <plugin>
            <!--该插件主要用途:构建可执行的JAR -->
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>

前后端分离部署

这里主要是前后端分离情况下的部署问题,有如下3种部署方式:

  • 前端部署在jar中

    将前端打包的js、css、html等文件,拷贝到SpringBoot项目的src/main/resources/static目录下

  • 前端直接部署在nginx下

    将前端打包的文件,通过nginx静态文件代理的方式来访问

    location ~ .*\.(js|css|ico|png|jpg|eot|svg|ttf|woff|html|txt|pdf|) {
       root /usr/local/nginx/html/;			#所有静态文件直接读取硬盘
       expires 30d;					 #缓存30天
    } 
    
  • 专门的前端服务,部署在nginx后

    这种可以采用Node来做模板渲染,又分成2种情况:

    1. 浏览器渲染方式,则Node只渲染一个空html模板,页面内容完全由JS生成。
    2. 同构渲染方式,让Node渲染模板时可以直接使用Vue和React的同构组件,直出页面后,用户的交互体验又如单页应用般流畅镜像。

这里简单对比这3种方案:

第一种方案最简单,甚至不需要nginx。问题在于前端如果修改、部署,那jar就需要重新部署,前后端有耦合

第二种方案通过nginx来代理,让前端脱离了后端部署,一定程度上做了解耦。但它又增加了nginx与前端的耦合,如果是采用docker方式,前端打包的要么打包进nginx,要么通过mount的方式映射本地磁盘,显然后者更好一些。但这种方式在K8S方式下,就显得不那么好了。

第三种方案真正意义上解耦了前后端,而且不同于前两者的前端渲染(CSR)方式,它可以既提供前端渲染,又提供后端渲染。的确是最好的方式。

目前情况下我们在主nginx下,前端服务又用nginx做了一个静态服务器来做,这样做暂时也可以,属于第三种方案,但没有SSR的第3种方案。

参考1

参考2

搭建Docker镜像

这里简单区分一下Dockerfile中RUN、CMD、ENTTRYPOINT

命令作用格式
RUN运行安装命令RUN ["executable", "param1", "param2"]
CMD容器启动时的命令CMD["executable", "param1", "param2"]
ENTRYPOINT容器启动后的命令ENTRYPOINT ["executable", "param1", "param2"]

dockerfile-maven-plugin插件

pom的build中增加该插件,如下:

<project>
	<properties>
    	 <java.version>1.8</java.version>
    	<docker.image.prefix>springbucks</docker.image.prefix>
	</properties>
	<!--略-->
	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
            
			<plugin>
				<groupId>com.spotify</groupId>
				<artifactId>dockerfile-maven-plugin</artifactId>
				<version>1.4.10</version>
				<executions>
					<execution>
						<id>default</id>
						<goals>
							<goal>build</goal>
							<goal>push</goal>
						</goals>
					</execution>
				</executions>
                
				<configuration>
					<repository>${docker.image.prefix}/${project.artifactId}</repository>
					<tag>${project.version}</tag>
					<buildArgs>
						<JAR_FILE>${project.build.finalName}.jar</JAR_FILE>
					</buildArgs>
				</configuration>
			</plugin>
		</plugins>
	</build>
</project>

docker.image.prefix是自定义的参数

project.actifactId、project.version等都是pom文件中的属性

project.build.finalName是build之后的结果

注意:这里的JAR_FILE参数,后边的dockerfile中会用到

dockerfile

这个的JAR_FILE为上文的参数

FROM java:8
EXPOSE 8080
ARG JAR_FILE
ADD target/${JAR_FILE} 	/myservice.jar
ENTRYPOINT ["java", "-jar","/myservice.jar"]

这里相当于docker build --build-arg JAR_FILE=...

执行构建

执行打包命令:

mvn clean package -Dmaven.test.skip=true

上传大约可以用:

mvn dockerfile:push

# spring 

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×