《Spring Boot 2.0极简教程》(陈光剑)
—— 基于 Gradle + Kotlin的企业级应用开发最佳实践
前面的章节中,咱们都是在IDE环境中开发运行测试 Spring Boot 应用程序。在开发测试发布整个软件生命周期的过程当中,咱们一般须要完成打包部署发布到平常、预发、线上机器运行等运维相关工做。
本章前半部分介绍 Spring Boot 应用的打包和部署,后半部分重点介绍如何使用 Docker 来构建部署运行 Spring Boot 应用。html
首先,使用http://start.spring.io/ 建立一个打包方式为 war 的 Spring Boot Kotlin 应用,采用 Gradle 构建。点击 Generate Project 等待建立完毕,下载 zip 包,导入 IDEA 中。能够看到,相比于项目打成jar 包方式,打成 war 包的项目中多了一个用于初始化Servlet的ServletInitializer类。代码以下java
class ServletInitializer : SpringBootServletInitializer() { override fun configure(application: SpringApplicationBuilder) : SpringApplicationBuilder { return application.sources(DemoPackageAndDeployApplication::class.java) } }
咱们知道Spring Boot 默认集成了内嵌web容器(例如 Tomcat、Jetty 等),这个时候,Spring Boot 应用支持“一键启动”,像一个普通Java程序同样,从main函数入口开始启动。如今,咱们是将项目打包成war包,放到独立的web容器中。
而若是咱们这个 war 包中没有配置Spring MVC 的 DispatcherServlet 的 web.xml 文件或者初始化 Servlet的类,那么这个 war 包就不会被 Tomcat识别启动 。这个时候,咱们须要告诉 Tomcat 这个 war 包的启动入口。而SpringBootServletInitializer就是来完成这件事情的。
经过重写configure (SpringApplicationBuilder) 方法,使用SpringApplicationBuilder 来配置应用程序的sources类。为了测试应用运行的效果,咱们在DemoPackageAndDeployApplication.kt 中添加HelloWorld REST接口方便测试linux
@SpringBootApplication open class DemoPackageAndDeployApplication fun main(args: Array<String>) { runApplication<DemoPackageAndDeployApplication>(*args) } @RestController class HelloWorld { @GetMapping(value = ["", "/"]) fun hello(): Map<String, Any> { val result = mutableMapOf<String, Any>() result["msg"] = "Hello,World" result["time"] = Date() return result } }
在 IDEA 的右边的 Gradle 工具栏中列出了 Gradle 构建项目的命令,以下图git
图16-1 Gradle 构建项目的命令
咱们能够直接点击 bootJar 把项目打成 jar 包。固然,在运维部署脚本中一般使用命令行: gradle bootJar 。执行日志以下github
17:44:21: Executing task 'bootJar'... :compileKotlin UP-TO-DATE :compileJava NO-SOURCE :processResources UP-TO-DATE :classes UP-TO-DATE :bootJar UP-TO-DATE BUILD SUCCESSFUL in 1s 3 actionable tasks: 3 up-to-date 17:44:22: Task execution finished 'bootJar'.
执行完毕,咱们能够在项目的build/libs 目录下看到打好的 jar 包,以下图所示web
图16-2 项目的build/libs 目录下打好的 jar 包
而后,咱们就能够直接使用 java –jar 命令执行该 jar 包了
$ java -jar build/libs/demo_package_and_deploy-0.0.1-SNAPSHOT.jar
此时,咱们浏览器访问 http://127.0.0.1:8080/ , 能够看到输出spring
{ "msg": "Hello,World", "time": "2018-02-09T09:38:31.933+0000" }
不过,使用java –jar 命令行来启动系统的这种方式
java -jar build/libs/demo_package_and_deploy-0.0.1-SNAPSHOT.jar
只要控制台关闭,服务就不能访问了。咱们可使用nohup 与 & 命令让进程在后台运行:
nohup java -jar build/libs/demo_package_and_deploy-0.0.1-SNAPSHOT.jar &docker
咱们也能够在启动的时候选择读取不一样的配置文件。例如,在项目src/main/resources 目录下面有不一样环境下的配置文件。以下图所示:shell
图16-3 不一样环境的属性配置文件
其中,application-dev.properties中配置服务器端口号为9000:数据库
server.port=9000
执行 bootJar从新打jar 包,执行下面的命令:
java -jar build/libs/demo_package_and_deploy-0.0.1-SNAPSHOT.jar --spring.profiles.active=dev
能够看到应用成功启动,并监听9000端口:
… o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 9000 (http) with context path '' 2018-02-09 18:18:47.336 INFO 69156 --- [ main] .e.s.d.DemoPackageAndDeployApplicationKt : Started DemoPackageAndDeployApplicationKt in 6.493 seconds (JVM running for 7.589)
在上面建立的项目中,Gradle 构建配置文件 build.gradle 内容以下:
buildscript { … } … apply plugin: 'war' … configurations { providedRuntime } dependencies { … providedRuntime('org.springframework.boot:spring-boot-starter-tomcat') }
其中,apply plugin: 'war' 是使用 war 插件来完成项目的打包工做。
直接使用 gradle bootWar,便可把项目打成 war包。而后,就能够像普通J2EE项目同样部署到web容器。一样的,war 包的路径默认也是放在 build/libs 下面。
另外,若是下面这行代码还在:
@SpringBootApplication open class DemoPackageAndDeployApplication fun main(args: Array<String>) { runApplication<DemoPackageAndDeployApplication>(*args) }
项目打成的war包,依然支持java –jar 运行:
$ java -jar build/libs/demo_package_and_deploy-0.0.1-SNAPSHOT.war
这个 war 包很不错,既能够直接扔到 Tomcat 容器中执行,也能够直接命令行启动运行。
提示:项目打 war包的示例项目源代码:https://github.com/EasySpring...
本节简单介绍一些 Spring Boot 应用的生产运维的一些内容。
使用命令:
ps -ef|grep java
拿到对于Java程序的pid (第2列):
501 69156 68678 0 6:18PM ttys002 0:21.59 /usr/bin/java -jar build/libs/demo_package_and_deploy-0.0.1-SNAPSHOT.jar --spring.profiles.active=dev
能够根据java自带的jinfo命令:
jinfo -flags 69156
来查看jar 启动后使用的是什么gc、新生代、老年代,分批的内存都是多少,示例以下:
$ jinfo -flags 69156 Attaching to process ID 69156, please wait... Debugger attached successfully. Server compiler detected. JVM version is 25.40-b25 Non-default VM flags: -XX:CICompilerCount=3 -XX:InitialHeapSize=134217728 -XX:MaxHeapSize=2147483648 -XX:MaxNewSize=715653120 -XX:MinHeapDeltaBytes=524288 -XX:NewSize=44564480 -XX:OldSize=89653248 -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseFastUnorderedTimeStamps -XX:+UseParallelGC
其中的参数简单说明如表16-1所示。
表16-1 JVM参数
参数说明
-XX:CICompilerCount
最大的并行编译数
-XX:InitialHeapSize 和 -XX:MaxHeapSize
指定JVM的初始堆内存和最大堆内存大小
-XX:MaxNewSize
JVM堆区域新生代内存的最大可分配大小
-XX:+UseParallelGC
垃圾回收使用Parallel收集器
咱们能够在 Java 命令行中配置咱们须要的JVM参数指标。
提示:更多关于 JVM 选项参数配置参考:http://www.oracle.com/technet... 。
要想重启应用,要首先找到该应用的 java进程,而后kill掉 java 进程。完成这个逻辑的shell 脚本以下:
kill -9 $(ps -ef|grep java|awk '{print $2}')
而后,再使用命令行从新启动应用便可。
本节介绍如何使用 Docker 来构建部署 Spring Boot 应用。
Docker 是一个Go语言开发的开源的轻量级应用容器引擎,诞生与2013年。Docker的核心概念是:镜像、容器、仓库。关键字是: 分布式应用(distributed applications), 微服务( microservices), 容器( containers ), 虚拟化(docker virtualization)。
Docker容器“轻量级”的含义主要是跟传统的虚拟机方式的对比而言。以下图所示:
图16-4 Docker “轻量级”容器VS.传统的虚拟机方式
传统的虚拟机技术是在硬件层面实现虚拟化,须要额外的虚拟机管理软件跟虚拟机操做系统这层。而 Docker 是在操做系统层面上的虚拟化,直接使用的是本地操做系统资源,所以更加轻量级。
Docker 的主要目标是经过对应用组件的封装、分发、部署、运行等生命周期的管理,作到“一次封装,处处运行”。
Docker 是实现微服务( microservices )应用程序开发的理想选择。开发、部署和回滚都将变成“一键操做”。传统的在服务器上进行各类软件包的安装、环境配置、应用程序的打包部署、启动进程等零散的运维操做——被更高层次的“抽象”,放到了一个“集装箱”中,咱们只是“开箱即用”。Docker把交付运行环境比做“海运”:OS如同一个货轮,每个在OS上运行的软件都如同一个集装箱,用户能够经过标准化手段自由组装运行环境,同时集装箱的内容能够由用户自定义,也能够由专业人员制造——这样交付一个软件,就是一系列标准化组件集的交付,如同乐高积木,用户只须要选择合适的积木组合,最后个标准化组件就是给用户的应用程序。这就是基于docker的PaaS()产品的原型。
一个完整的Docker有如下几个部分组成:
DockerClient客户端
Docker Daemon守护进程
Docker Image镜像
DockerContainer容器
在docker的网站上介绍了使用docker的典型场景:
Automating the packaging and deployment of applications(应用打包部署自动化)
Creation of lightweight, private PAAS environments(建立轻量、私有的PaaS环境)
Automated testing and continuous integration/deployment(实现自动化测试和持续的集成/部署)
Deploying and scaling web apps, databases and backend services(部署与扩展web app、数据库和后端服务)
因为Docker 基于LXC的轻量级虚拟化的特色,相比 KVM 之类虚拟机而言,最明显的特色就是启动快,资源占用小(轻量级)——这正是构建隔离的标准化的运行环境,轻量级的PaaS,构建自动化测试和持续集成环境,以及一切能够横向扩展的应用等场景的最佳选择。
提示:更多关于 Docker 的介绍参考: https://docs.docker.com 。Dockers Github 项目空间是:https://github.com/docker
本小节介绍如何搭建 Docker 环境。
安装 Docker
去 docker 官网 https://docs.docker.com/install/ 下载对应的操做系统上的安装包。安装完毕,打开Docker运行,能够看到Mac 系统菜单栏上的显示的 Docker 应用信息以下
图16-5 Mac 系统菜单栏上的 Docker 图标
想知道 docker 提供了哪些命令行操做吗?执行docker help便可看到一个详细的命令说明。例如,在命令行查看 Docker 版本信息:
$ docker version Client: Version: 17.12.0-ce API version: 1.35 Go version: go1.9.2 Git commit: c97c6d6 Built: Wed Dec 27 20:03:51 2017 OS/Arch: darwin/amd64 Server: Engine: Version: 17.12.0-ce API version: 1.35 (minimum version 1.12) Go version: go1.9.2 Git commit: c97c6d6 Built: Wed Dec 27 20:12:29 2017 OS/Arch: linux/amd64 Experimental: false
查看详细的 docker 信息
$ docker info Containers: 0 Running: 0 Paused: 0 Stopped: 0 Images: 1 Server Version: 17.12.0-ce …
从仓库 pull Java 环境镜像
使用sudo docker pull java命令从 Docker 官方仓库获取 Java 运行环境镜像:
$ sudo docker pull java Password: Using default tag: latest latest: Pulling from library/java ... bb9cdec9c7f3: Pull complete Digest: sha256:c1ff613e8ba25833d2e1940da0940c3824f03f802c449f3d1815a66b7f8c0e9d Status: Downloaded newer image for java:latest
下载完毕以后,能够经过docker images命令查看镜像列表:
$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE Java latest d23bdf5b1b1b 12 months ago 643MB
能够看到,本地镜像中已经有了 java 运行环境。
本节介绍如何把上面的 Spring Boot 项目 Docker 容器化。过程主要分为以下3步:
1)添加 docker构建插件。
2)配置Dockerfile文件建立自定义的镜像。
3)构建Docker镜像。
下面咱们就来分别详细介绍。
在 Gradle 项目构建配置文件build.gradle 中添加com.palantir.docker插件:
buildscript { ext { kotlinVersion = '1.2.20' springBootVersion = '2.0.0.RC1' } repositories { // gradle-docker plugin repo maven { url "https://plugins.gradle.org/m2/" } ... } dependencies { ... classpath('gradle.plugin.com.palantir.gradle.docker:gradle-docker:0.17.2') } } apply plugin: 'com.palantir.docker' ... docker { name "${project.group}/${jar.baseName}" files jar.archivePath buildArgs(['JAR_FILE': "${jar.archiveName}"]) }
其中,buildArgs(['JAR_FILE': "${jar.archiveName}"]) 中配置的'JAR_FILE': "${jar.archiveName}" 是咱们的 Spring Boot 项目打成 jar包的名称,会传递到Dockerfile文件中使用(下一步骤中将会看到)。
提示:关于Docker 插件com.palantir.docker的介绍参考文档: https://github.com/palantir/g...
这个插件发布在https://plugins.gradle.org/m2...,因此咱们添加 maven 仓库的依赖
repositories { // gradle-docker plugin repo maven { url "https://plugins.gradle.org/m2/" } ... }
gradle-docker提供的版本有:
https://plugins.gradle.org/m2...
Dockerfile文件放置在项目根目录:
图16-6 Dockerfile文件放置在项目根目录
Dockerfile文件内容以下:
FROM java:latest VOLUME /tmp ARG JAR_FILE ADD ${JAR_FILE} app.jar ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]
配置构建参数JAR_FILE,这里的JAR_FILE是在 build.gradle 中buildArgs中配置的
docker { name "${project.group}/${jar.baseName}" files jar.archivePath buildArgs(['JAR_FILE': "${jar.archiveName}"]) } ADD ${JAR_FILE} app.jar
将文件${JAR_FILE}拷贝到docker container的文件系统对应的路径app.jar
ENTRYPOINT ["java", "-Djava.security.egd= file:/dev/./urandom", "-jar", "/app.jar"]
Docker container启动时执行的命令。注意:一个Dockerfile中只能有一条ENTRYPOINT命令。若是多条,则只执行最后一条。
-Djava.security.egd=file:/dev/./urandom
配置 JRE 使用非阻塞的 Entropy Source。SecureRandom generateSeed 使用 /dev/random 生成种子。可是 /dev/random 是一个阻塞数字生成器,若是它没有足够的随机数据提供,它就一直等,这迫使 JVM 等待。经过在 JVM 启动参数中配置这么一行:-Djava.security.egd=file:/dev/./urandom 解决这个阻塞问题。
Dockerfile是一个文本格式的配置文件,咱们可使用Dockerfile文件快速建立自定义的镜像。Dockerfile支持的丰富的运维指令。这些指令分为4部分:
基础镜像信息
维护者信息
镜像操做指令
容器启动时的执行指令
...
直接在命令行执行:
$ docker run -p 8080:9000 -t com.easy.springboot/demo_package_and_deploy
便可启动咱们构建发布在 Docker 镜像仓库中的Spring Boot 应用镜像了。
咱们的 Spring Boot 应用镜像运行在 Docker容器沙箱环境中,端口号是9000,做为外部Host OS环境要访问这个服务, 须要添加TCP端口映射:把本机8080端口映射到 Docker 容器端口9000,以下图所示:
图16-7 把本机8080端口映射到 Docker 容器端口9000
其中:
-p 是将容器的端口9000映射到 docker 所在操做系统的端口8080;
-t 是打开一个伪终端,以便后续能够进入查看控制台 log。
使用 docker ps 命令查看运行中的容器:
$ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 36fbfaf05359 com.easy.springboot/demo_package_and_deploy "java -Djava.securit…" 25 minutes ago Up 25 minutes 0.0.0.0:8080->9000/tcp infallible_kare
……
而后,执行 push 命令便可:
$ docker push com.easy.springboot/demo_package_and_deploy
提示:本节项目源代码:https://github.com/EasySpring...
本章简单介绍了Spring Boot项目的打包、分环境运行、生产运维等操做。一般,在企业项目实践中,会实现一套 Spring Boot应用部署发布的自动化运维平台工具。本章还给出了一个完整的 Spring Boot项目 Docker 化的实战案例。通过前面的学习,相信您已经对如何使用基于 Kotlin 编程语言的 Spring Boot项目开发有了一个比较好的掌握。