Wildfly-秘籍-全-
Wildfly 秘籍(全)
原文:
zh.annas-archive.org/md5/42db5120d439e65deb20a3d0a5193850译者:飞龙
前言
Java 已经存在多年,如果您把所有年份都加起来,应该得到一个数字——20 多年。这并不意味着 Java 已经过时或消亡;相反,Java 比以往任何时候都充满活力,新的 Java 8 语言规范就是证明。
由于 Java 并未消亡,WildFly 希望给您带来前所未有的更多功能。得益于其小巧的内存占用,WildFly 可以在 Raspberry Pi 上运行(口袋大小的设备),也可以通过 Docker(微服务架构的基础)在 Linux 容器中扩展,或者通过 OpenShift Online 平台进入云端。
此外,WildFly 的新模块化特性允许您根据需要自定义其系统。您可以通过提供自己的扩展和子系统来扩展 WildFly。
WildFly 的模块化类加载让您能够精细控制应用程序加载所需的库和 Java 类,这使您能够在持续集成和持续交付实践中正确使用 WildFly。
此外,WildFly 提供了一套管理 API,可用于管理整个平台。您可以通过命令行界面(CLI)与 WildFly 交互,这是一个强大的工具,用于管理整个系统。如果您更习惯于 UI,您可以使用精心设计的 Web 控制台。
选择 WildFly 的另一个原因是其充满活力的社区和整个 Java EE 环境。别忘了,WildFly 是唯一由其社区支持的开放源代码 Java EE 7 应用服务器!
本书涵盖的内容
第一章,欢迎来到 WildFly!,介绍了 WildFly Java 应用服务器及其与 Java EE 7 平台相关的核心特性。
第二章,以独立模式运行 WildFly,解释了独立操作模式以及您如何以这种方式管理实例。
第三章,以域模式运行 WildFly,解释了域操作模式及其包含的所有内容,例如域控制器和主机控制器。
第四章,使用 CLI 管理日志子系统,描述了如何配置和管理日志子系统以跟踪 WildFly 和应用程序的操作。
第五章,使用 CLI 管理数据源子系统,描述了如何配置和管理数据源子系统。
第六章,在集群中运行 WildFly,介绍了如何在两种操作模式下运行 WildFly,并解释了如何进行;展示了 TCP 和 UDP 网络配置。
第七章, WildFly 的负载均衡,涵盖了如何使用 Apache HTTP 服务器和 mod_cluster 平衡 WildFly 实例——使用 HTTP 和 AJP 协议。
第八章, 使用 CLI 进行命令操作,解释了如何使用 CLI 检索配置和运行时信息;两种操作模式都被使用。
第九章, 征服 CLI,讨论了如何使用 CLI 在两种操作模式下改变 WildFly 的状态,例如部署、取消部署、停止服务器、停止服务器组等。
第十章, 强化 WildFly 通信,解释了您如何强化 WildFly 通信,例如 Web 控制台通过 HTTPS 在安全通道上进行通信、域控制器和主机控制器。
第十一章, 强化 WildFly 配置,描述了强化 WildFly 配置的技术,例如哈希密码和使用保险库。
第十二章, 使用 WildFly 进行基于角色的访问控制,介绍了 RBAC 提供者以访问 WildFly Web 控制台,并展示了如何对其进行自定义。
第十三章, 使用 WildFly 进行消息传递,描述了您如何配置和管理消息子系统(嵌入式 HornetQ)及其组件,例如队列和主题。
第十四章, 使用 OpenShift 将 WildFly 带入云端,介绍了 OpenShift Online 平台,以及您如何直接在云上部署 WildFly 应用程序。
第十五章, 使用 Docker 与 WildFly,介绍了使用 Docker 的 Linux 容器,以及如何在上面运行 WildFly。
附录,WildFly 域和独立模式,是一个附加章节,带您了解 WildFly 的域和独立模式。您可以从www.packtpub.com/sites/default/files/downloads/2413OS_Appendix.pdf下载它。
您需要为这本书准备什么
要充分利用这本书,您首先需要一个 4GB RAM 的 PC,以及大约 50GB 的空闲磁盘空间。此外,互联网连接是必需的。
从软件角度来看,如果您想跟随这本书,您需要一个 Fedora 21 操作系统,以及 JDK 8 和 WildFly 9。
我们还将使用其他工具,例如 Maven、Git、Apache JMeter 和 MySQL。
本书面向的对象
本书旨在面向中间件系统管理员和 Java 开发者,实际上是对架构设计和实现有要求的优秀 Java 开发者。无论你是 WildFly 的初学者,还是来自之前的版本,如 JBoss AS 5、6 和 7,或者是对其有经验的专家,你都将能够掌握 WildFly 的基本和高级功能。
顺便说一句,WildFly 的核心组件大部分都是全新的,例如它的管理工具,即 CLI;它的操作模式,包括独立模式和域模式;以及它提供的由 Undertow 实现的 Web 服务器。即使你对 JBoss 和 WildFly 完全没有经验,你也可以从这本书中受益。
部分
在本书中,你会发现一些经常出现的标题(准备就绪、如何做、如何工作、还有更多、参见等)。
为了清楚地说明如何完成食谱,我们使用以下这些部分:
准备就绪
本节将告诉你可以在食谱中期待什么,并描述如何设置任何软件或任何为食谱所需的初步设置。
如何做…
本节包含遵循食谱所需的步骤。
如何工作…
本节通常包含对前一个章节发生情况的详细解释。
还有更多…
本节包含有关食谱的附加信息,以便使读者对食谱有更多的了解。
参见
本节提供了对其他有用信息的有帮助链接。
规范
在本书中,你会发现许多文本样式,用于区分不同类型的信息。以下是一些这些样式的示例及其含义的解释。
文本中的代码单词、数据库表名、文件夹名、文件名、文件扩展名、路径名、虚拟 URL、用户输入和 Twitter 处理方式如下所示:“WFC 文件夹仅用于不干扰你的当前环境。”
代码块设置如下:
<VirtualHost 10.0.0.1:6666>
<Directory />
Order deny,allow
Deny from all
Allow from 10.0.0.1
</Directory>
ServerAdvertise off
EnableMCPMReceive
</VirtualHost>
当我们希望引起你对命令行块中特定部分的注意时,相关的行或项目将以粗体显示:
$ cd $WILDFLY_HOME
$ ./bin/standalone.sh -Djboss.bind.address=10.0.0.1
...
22:56:05,531 INFO [org.wildfly.extension.undertow] (MSC service thread 1-3) WFLYUT0006: Undertow HTTP listener default listening on /10.0.0.1:8080
任何命令行输入或输出都按如下方式编写:
[disconnected /] connect
[standalone@localhost:9990 /] /socket-binding-group=standard-sockets/socket-binding=http:read-attribute(name=port)
{
"outcome" => "success",
"result" => expression "${jboss.http.port:8080}"
}
新术语和重要词汇以粗体显示。你在屏幕上看到的单词,例如在菜单或对话框中,在文本中显示如下:“你首先需要标记接受许可协议选项以启用链接。”
注意
警告或重要注意事项以如下方式显示。
小贴士
小贴士和技巧看起来像这样。
读者反馈
我们读者的反馈总是受欢迎的。告诉我们你对这本书的看法——你喜欢什么或不喜欢什么。读者反馈对我们很重要,因为它帮助我们开发出你真正能从中获得最大收益的标题。
要向我们发送一般反馈,只需发送电子邮件至 <feedback@packtpub.com>,并在邮件主题中提及本书的标题。
如果您在某个主题上具有专业知识,并且您对撰写或为书籍做出贡献感兴趣,请参阅我们的作者指南www.packtpub.com/authors。
客户支持
现在您是 Packt 图书的骄傲拥有者,我们有一些事情可以帮助您从您的购买中获得最大收益。
勘误
尽管我们已经尽一切努力确保内容的准确性,但错误仍然可能发生。如果您在我们的某本书中发现错误——可能是文本或代码中的错误——如果您能向我们报告这一点,我们将不胜感激。通过这样做,您可以避免其他读者的挫败感,并帮助我们改进本书的后续版本。如果您发现任何勘误,请通过访问www.packtpub.com/submit-errata,选择您的书籍,点击勘误提交表链接,并输入您的勘误详情来报告。一旦您的勘误得到验证,您的提交将被接受,勘误将被上传到我们的网站或添加到该标题的勘误部分下的现有勘误列表中。
要查看之前提交的勘误表,请访问www.packtpub.com/books/content/support,并在搜索字段中输入书籍名称。所需信息将在勘误部分显示。
侵权
互联网上版权材料的侵权是一个持续存在的问题,涉及所有媒体。在 Packt,我们非常重视保护我们的版权和许可证。如果您在互联网上发现任何形式的非法复制我们的作品,请立即提供位置地址或网站名称,以便我们可以寻求补救措施。
请通过<copyright@packtpub.com>与我们联系,并提供涉嫌侵权材料的链接。
我们感谢您在保护我们的作者以及为我们提供有价值内容的能力方面提供的帮助。
问题
如果您在这本书的任何方面遇到问题,您可以通过<questions@packtpub.com>联系我们,我们将尽力解决问题。
第一章. 欢迎来到 WildFly!
在本章中,你将学习以下食谱:
-
软件先决条件
-
下载和安装 WildFly
-
理解 WildFly 目录概述
-
以独立模式运行 WildFly
-
以域模式运行 WildFly
-
以服务形式运行 WildFly
简介
在本章中,我们将描述 WildFly 的历史、其先决条件、如何获取它以及如何安装它。我们还将解释独立和域运行模式,即如何启动它们。
JBoss.org 社区是一个庞大的社区,世界各地的人们在这里开发、测试和编写代码片段。其中有很多项目,除了 JBoss AS 或最近的 WildFly 之外,如 Infinispan、Undertow、PicketLink、Arquillian、HornetQ、RESTeasy、AeroGear 和 Vert.x。要查看所有项目的完整列表,请访问以下网站:www.jboss.org/projects/。
尽管有营销原因,因为没有首选项目,社区希望将 JBoss AS 项目的名称改为不同的名称,以免与社区名称冲突。另一个原因是 Red Hat JBoss 支持的版本名为 JBoss 企业应用平台(EAP)。这又是一个取代 JBoss AS 名称的理由。
社区是如何改变名称的?他们是如何决定的?很简单——向我们,社区,提出新的名称。选举过程开始了,来自 JBoss 社区、JBoss 用户组(JBUGs)、Java 用户组(JUGs)以及世界各地的相关社区的人们都表达了自己的偏好。
JBoss AS 的新名称应该暗示 Java 应用程序服务器的能力和亲和力,如集成、云、移动、消息、敏捷、强大、开源、自由精神等。你猜对了赢家!
"一只野蜂极其敏捷、轻量、野性十足,真正自由。"
这个全新的名字是在 2013 年巴西的 JUDCon 上宣布的。Zzzzhhh... 欢迎来到 WildFly!
让我们谈谈 WildFly 的功能和特性:
-
WildFly 取代了 JBoss AS。WildFly 的第一个版本是 8.0,它基于 JBoss AS 7.1。为了简化,社区决定保持相同的编号。
-
WildFly 获得了 Java EE 7 Full 平台兼容实现徽章,这意味着它拥有最新的 Java 技术。易于开发、更好的安全性、更好的集成、更好的管理!
-
WildFly 启动只需几秒钟。所有服务一起启动,但只需启动它需要的那些。这是因为有一个集中的元数据缓存和模块化类加载系统,这可以防止著名的类路径地狱。
-
另一个重大变化是默认的 Web 服务器;现在 WildFly 使用 Undertow。
"Undertow 是一个灵活高效的 Java 编写的 Web 服务器,提供基于 NIO 的阻塞和非阻塞 API。"
-
它轻量级,核心 jar 文件小于 1 MB,运行时小于 4 MB。Undertow 是可嵌入的,灵活的;它支持 WebSocket(HTTP 升级协议)和 Servlet 3.1。在本书的后面,我们将看到如何配置和调整嵌入在 WildFly 中的 Undertow。
-
在 WildFly 的新版本中,引入了一个基于角色的访问控制(RBAC)系统。这个新功能实际上赋予了定义用户、组和角色的功能。这样,你将不仅仅有一个超级用户,而是一个能够完成其适当任务而不影响安全性的用户。它高度可定制,并且可以与大多数身份存储系统集成,如 LDAPs 和 ADs。
-
WildFly 只有一个配置文件,因此所有设置都集中在一个地方。
-
你可以通过管理控制台(也称为 Web 控制台)、命令行界面(CLI)、REST API 和 Java API 来管理你的配置。所有这些工具都赋予了你强大的自定义管理设置的能力。在本书中,我们将主要集中讨论 CLI 和管理控制台。
注意
WildFly 是用 Java SE 1.7 构建的;因此,它要求你至少拥有 JRE 版本 1.7。
话虽如此,让我们开始吧!
在接下来的菜谱中,我们将看到启动 JBoss AS、ops 和 WildFly 需要什么,在哪里获取,其文件夹结构是什么样的,以及在哪里可以找到其配置文件。
软件先决条件
WildFly 运行在 Java 平台之上。它至少需要 Java 运行时环境(JRE)版本 1.7 才能运行(对版本 1.7 和 7 的进一步引用应视为相同——同样适用于版本 1.8 和 8),但它也可以与最新的 JRE 版本 8 完美兼容。
由于我们还需要编译和构建 Java 网络应用程序,我们需要Java 开发工具包(JDK),它提供了与 Java 源代码一起工作的必要工具。在 JDK 全景中,我们可以找到 Oracle JDK,由 Oracle 开发和维护,以及 OpenJDK,它依赖于社区贡献。
然而,自 2015 年 4 月起,Oracle 将不再向其公共下载站点发布 Java SE 7 的更新,如www.oracle.com/technetwork/java/javase/downloads/eol-135779.html中所述。此外,请记住,Java 关键补丁更新是按季度发布的;因此,出于稳定性和功能支持的原因,我们将使用 Oracle JDK 8,该版本可在www.oracle.com/technetwork/java/javase/downloads/index.html免费下载。
在编写这本书的时候,最新的稳定 Oracle JDK 版本是 1.8.0_31(以及 8u31)。因此,对Java 虚拟机(JVM)、Java、JRE 和 JDK 的所有引用都将是指 Oracle JDK 1.8.0_31。为了简化问题,如果你不介意,请使用相同的版本。
除了 JDK 之外,我们还需要 Apache Maven 3,这是一个 Java 项目的构建工具。它可以在maven.apache.org/download.cgi免费下载。通用下载链接可以在www.us.apache.org/dist/maven/maven-3/3.2.5/binaries/apache-maven-3.2.5-bin.tar.gz找到。
准备中
要完全遵循书中的食谱,使用相同的环境是一个基本要求。由于我无法复制相同的食谱以适应不同的设置(如 Windows、Mac 和 Linux),我将使用 Linux(实际上是 Fedora 21)作为基础操作系统。
如果您正在运行不同的系统,并且想仔细遵循书中的内容,您可以使用 VirtualBox 软件轻松安装和运行 Fedora 21 虚拟机,该软件可在www.virtualbox.org/wiki/Downloads找到。
-
选择与您的实际系统兼容的版本。您可以通过下载其镜像来安装 Fedora 21,链接为
getfedora.org/en/workstation/。注意
上述软件的安装超出了本书的范围。
-
要安装 Oracle JDK,您需要打开浏览器并将其指向
www.oracle.com/technetwork/java/javase/downloads/index.html。 -
一旦进入,点击 JDK 下载链接,如下图所示:
![准备中]()
-
链接将带您进入下载页面,在那里您首先需要勾选接受许可协议选项以启用链接,如下截图所示:
![准备中]()
-
当您接受协议时,所有链接都会激活。选择最适合您硬件和操作系统的链接。
![准备中]()
我正在运行一个 64 位硬件支持的 Fedora 21 Linux 机器,因此我将使用jdk-8u40-linux-x64.tar.gz包。我本可以使用 RPM 包,但我更喜欢安装存档版本,以便更好地满足我在路径方面的需求;什么放在哪里。
-
接下来,我们将创建一个名为
WFC的文件夹,代表WildFly 食谱,用于存储所有必要软件、代码和文件,以便遵循书中的所有食谱。打开您的终端应用程序并运行以下命令:$ cd && mkdir WFC注意
WFC文件夹仅用于不干扰您当前的环境。
如何操作…
-
选择包存档;下载完成后,打开您的命令行并将内容提取到
WFC文件夹中,如下所示:$ cd ~/WFC && tar zxvf jdk-8u40-linux-x64.tar.gz这将把 Oracle JDK 软件提取到
WFC文件夹内的jdk1.8.0_40文件夹中,从您的home文件夹开始。为了方便,我们将使用不同的文件夹名,如jdk8,来指代先前的 JDK 安装文件夹。运行以下命令:$ cd ~/WFC && mv jdk1.8.0_40 jdk8现在我们需要设置
JAVA_HOME环境变量,并使 JDK 命令可以从我们的 shell(也称为终端)中可用。 -
使用您选择的文本编辑器,并将以下指令添加到位于您
home文件夹中的.bash_profile文件中:export JAVA_HOME=~/WFC/jdk8 export PATH=$JAVA_HOME/bin:$PATH前两个命令将设置
JAVA_HOME变量,并将JAVA_HOME/bin路径分别导出到您的PATH系统变量中。波浪线~符号是 Unix-like 系统中用户主目录的快捷方式。为了使更改生效,您可以注销并重新登录,或者只需发出以下命令:
$ source ~/.bash_profile -
安装阶段完成后,通过在终端应用程序中执行
java -version命令来测试您的新环境,您应该看到(或多或少)以下图片中所示的内容:![如何操作…]()
-
接下来,我们需要安装 Apache Maven 3。如果您还没有下载,请点击以下链接:
www.us.apache.org/dist/maven/maven-3/3.2.5/binaries/apache-maven-3.2.5-bin.tar.gz -
下载完成后,打开您的命令行,将其内容提取到
WFC文件夹中:$ cd ~/WFC && tar zxvf apache-maven-3.2.5-bin.tar.gz这将把 Apache Maven(也称为 Maven)软件提取到
WFC文件夹内的apache-maven-3.2.5文件夹中,从您的home文件夹开始。为了方便,我们将使用不同的文件夹名,如maven,来指代先前的 Maven 安装文件夹。运行以下命令:$ cd ~/WFC && mv apache-maven-3.2.5 maven现在我们需要设置
M2_HOME环境变量,并使 Maven 的命令可以从我们的 shell(也称为终端)中可用。 -
使用您选择的文本编辑器,并将以下指令添加到位于您
home文件夹中的.bash_profile文件中:export M2_HOME=~/WFC/maven export PATH=$JAVA_HOME/bin:M2_HOME/bin:$PATH前两个命令将设置
M2_HOME变量,并将M2_HOME/bin路径分别导出到您的PATH系统变量中。波浪线~符号是 Unix-like 系统中用户主目录的快捷方式。为了使更改生效,您可以注销并重新登录,或者只需发出以下命令:
$ source ~/.bash_profile -
安装阶段完成后,通过在终端应用程序中执行
mvn -version命令来测试您的新环境,您应该看到(或多或少)以下图片中所示的内容:![如何操作…]()
-
最后,但同样重要的是,我们需要安装
git,这是一个分布式版本控制系统。它主要用于源代码,但也用作配置仓库。为了安装git工具,我们将依赖yum软件管理器,这使得安装过程变得简单。打开终端并按照以下步骤操作:$ sudo yum -y install git -
完成后,尝试执行以下命令:
$ git version git version 2.1.0
还有更多...
现在我们已经安装了git,我们可以继续下载本书使用的代码仓库(或 repo),它位于以下 GitHub 账户的 URL:github.com/foogaro/wildfly-cookbook.git。
您可以git-clone仓库或直接下载 ZIP 存档。无论哪种方式,请在WFC文件夹中创建一个名为github的文件夹,并将源文件放入其中。
使用git-clone命令,按照以下步骤操作:
$ cd ~/WFC
$ mkdir github
$ cd github
$ git clone https://github.com/foogaro/wildfly-cookbook.git
一旦git完成了仓库的克隆,您将找到一个名为wildfly-cookbook的新文件夹,其中包含本书中使用的所有项目。
要构建项目,只需进入相应的文件夹并执行maven-package命令。
例如,要构建example项目,请按照以下步骤操作:
$ cd ~/WFC/github/wildfly-cookbook/example
$ mvn -e clean package
前面的命令会构建项目,并将 Web 应用程序工件生成到名为target的文件夹中。在那里您可以找到名为example.war的应用程序,准备部署。
好的,我们终于完成了所有软件的安装,这些软件将在本书中使用。为了确保您没有遗漏任何部分,您应该拥有以下图像所示的环境:

下载和安装 WildFly
在这个菜谱中,我们将学习如何获取和安装 WildFly。像往常一样,在开源世界中,您可以用不同的方式做同样的事情。WildFly 可以使用您首选的软件管理器或通过下载wildfly.org网站提供的捆绑包进行安装。我们将选择第二种方式,按照 JDK 的要求。
准备工作
只需打开您喜欢的浏览器并将它指向wildfly.org/downloads/。您应该看到一个类似于以下截图的页面:

WildFly 的下载页面
在撰写本书时,最新的 WildFly 版本是 9.0.0.Beta2。最终版本现在可用并正在使用。
现在,将最新版本下载到WFC文件夹中。
如何操作...
-
下载完成后,打开终端并将内容提取到
WFC文件夹中,执行以下命令:$ cd ~/WFC && tar zx wildfly-9.0.0.Beta2.tar.gz前面的命令首先指向我们的
WildFly Cookbook文件夹;然后它会从中提取 WildFly 存档。列出WFC文件夹,我们应该找到新创建的名为wildfly-9.0.0.Beta2的 WildFly 文件夹。 -
为了更好地记住和处理 WildFly 的安装目录,将其重命名为
wildfly,如下所示:$ cd ~/WFC && mv wildfly-9.0.0.Beta2 wildfly顺便说一句,WildFly 也可以使用传统的
YUM,Fedora 的软件管理器进行安装。注意
在生产环境中,你不会将 WildFly 安装目录放置在特定用户的
home文件夹中。相反,你将把它放置在相对于你工作上下文的不同路径中。 -
现在我们需要创建
JBOSS_HOME环境变量,这是 WildFly 在启动时用作基础目录的变量(可能在未来的版本中,这将更新为WILDFLY_HOME)。我们还将创建WILDFLY_HOME环境变量,我们将在整个书中使用它来引用 WildFly 的安装目录。因此,使用你喜欢的文本编辑器打开位于你的home文件夹中的.bash_profile文件,并添加以下指令:export JBOSS_HOME=~/WFC/wildfly export WILDFLY_HOME=$JBOSS_HOME -
要使更改生效,你可以要么注销并重新登录,要么只需发出以下命令:
$ source ~/.bash_profile
如果你仔细遵循了前两个食谱,你的 .bash_profile 文件应该看起来像以下图片:

理解 WildFly 的目录概览
现在我们已经完成了 WildFly 的安装,让我们来看看它的文件夹。这个食谱将稍微有些理论性。
如何操作…
-
打开你的终端并运行以下命令:
$ cd $WILDFLY_HOME $ pwd && ls -la -
你命令的输出应该类似于以下图片:
![如何操作…]()
WildFly 的文件夹概览
如何工作…
前面的图片展示了文件系统中 WildFly 的文件夹。以下表格中概述了每个文件夹:
| 文件夹名称 | 描述 |
|---|---|
appclient |
由该安装启动的应用客户端容器使用的配置文件、部署内容和可写区域。 |
bin |
包含启动脚本、启动配置文件以及 Unix 和 Windows 环境中可用的各种命令行工具,如 Vault、add-user 和 Java 诊断报告。 |
bin/client |
包含用于非 Maven 基础客户端的客户端 jar。 |
docs/schema |
XML 架构定义文件。 |
docs/examples/configs |
代表特定用例的示例配置文件。 |
domain |
由该安装启动的域模式进程使用的配置文件、部署内容和可写区域。 |
modules |
WildFly 基于模块化类加载架构。服务器中使用的各种模块存储在这里。 |
standalone |
由该安装启动的单个独立服务器使用的配置文件、部署内容和可写区域。 |
welcome-content |
默认欢迎页面内容。 |
在前面的表中,我强调了“domain”和“standalone”文件夹,这些文件夹决定了 WildFly 将运行在哪种模式下:独立模式或域模式。在接下来的几个食谱中,我们将概述它们,并在本书的后续部分进行深入了解。
注意
在此之后,每当提到 WildFly 的家目录时,将指代 $WILDFLY_HOME。
以独立模式运行 WildFly
独立模式下的 WildFly 意味着一个 WildFly 实例是独立启动和管理的。您可以拥有任意数量的独立 WildFly 实例,但您必须分别管理它们。这意味着每个配置、数据源、部署和模块都必须为每个实例管理一次。
注意
独立模式和域模式之间的主要区别在于管理,而不是功能。功能由您选择运行 WildFly 的配置确定。
准备工作
让我们来看看独立文件夹:
| 文件夹名称 | 描述 |
|---|---|
configuration |
由此安装的单个独立服务器运行时使用的配置文件。 |
deployments |
由此安装的单个独立服务器运行时使用的部署内容。 |
lib |
由此安装的单个独立服务器运行时使用的库。 |
log |
由此安装的单个独立服务器运行时创建的日志文件。 |
因此,配置文件夹包含所有配置文件。是的,您可以拥有多个,但您必须选择要运行的配置。deployments文件夹包含所有要部署的应用程序,已部署和未部署的(正如我们稍后将要看到的,甚至有失败的部署标记)。lib文件夹包含使用扩展列表机制引用的应用程序的所有库 jar 文件。最后,log文件夹包含 WildFly 的server.log文件。
如前所述,在configuration文件夹中,您可以找到以下文件:
| 文件名 | 描述 |
|---|---|
standalone.xml (默认) |
Java 企业版 7 网络配置认证配置,包含所需的技术。 |
standalone-ha.xml |
Java 企业版 7 网络配置认证配置,具有高可用性。 |
standalone-full.xml |
Java 企业版 7 完整配置认证配置,包括所有必需的 EE 7 技术,包括消息传递—JMS。 |
standalone-full-ha.xml |
Java 企业版 7 完整配置认证配置,具有高可用性。 |
在 Java EE 7 配置的基础上,WildFly 定义了自己的配置。对于独立模式,每个文件对应一个 WildFly 配置,即standalone.xml对应默认配置,standalone-ha.xml对应ha配置,standalone-full.xml对应full配置,而standalone-full-ha.xml对应full-ha配置。在域模式下也可以找到相同的 WildFly 配置。
如何操作...
让我们尝试以默认设置运行 WildFly 的独立模式,如下所示:
$ cd $WILDFLY_HOME
$ ./bin/standalone.sh =========================================================================
JBoss Bootstrap Environment
JBOSS_HOME: /home/wildfly/WFC/wildfly
JAVA: /home/wildfly/WFC/jdk8/bin/java
JAVA_OPTS: -server -XX:+UseCompressedOops -server -XX:+UseCompressedOops -Xms64m -Xmx512m -XX:MaxPermSize=256m -Djava.net.preferIPv4Stack=true -Djboss.modules.system.pkgs=org.jboss.byteman -Djava.awt.headless=true
=========================================================================
Java HotSpot(TM) 64-Bit Server VM warning: ignoring option MaxPermSize=256m; support was removed in 8.0
08:43:50,658 INFO [org.jboss.modules] (main) JBoss Modules version 1.4.2.Final
08:43:50,799 INFO [org.jboss.msc] (main) JBoss MSC version 1.2.4.Final
08:43:50,850 INFO [org.jboss.as] (MSC service thread 1-6) WFLYSRV0049: WildFly Full 9.0.0.Beta2 (WildFly Core 1.0.0.Beta2) starting
08:43:51,543 INFO [org.jboss.as.controller.management-deprecated] (ServerService Thread Pool -- 26) WFLYCTL0028: Attribute enabled is deprecated, and it might be removed in future version!
08:43:51,564 INFO [org.jboss.as.server] (Controller Boot Thread) WFLYSRV0039: Creating http management service using socket-binding (management-http)
08:43:51,592 INFO [org.xnio] (MSC service thread 1-11) XNIO version 3.3.0.Final
08:43:51,601 INFO [org.xnio.nio] (MSC service thread 1-11) XNIO NIO Implementation Version 3.3.0.Final
08:43:51,627 WARN [org.jboss.as.txn] (ServerService Thread Pool -- 54) WFLYTX0013: Node identifier property is set to the default value. Please make sure it is unique.
08:43:51,626 INFO [org.jboss.as.security] (ServerService Thread Pool -- 53) WFLYSEC0002: Activating Security Subsystem
08:43:51,631 INFO [org.wildfly.extension.io] (ServerService Thread Pool -- 37) WFLYIO001: Worker 'default' has auto-configured to 16 core threads with 128 task threads based on your 8 available processors
08:43:51,635 INFO [org.jboss.as.security] (MSC service thread 1-10) WFLYSEC0001: Current PicketBox version=4.9.0.Beta2
08:43:51,649 INFO [org.jboss.as.jsf] (ServerService Thread Pool -- 44) WFLYJSF0007: Activated the following JSF Implementations: [main]
08:43:51,650 INFO [org.jboss.as.clustering.infinispan] (ServerService Thread Pool -- 38) WFLYCLINF0001: Activating Infinispan subsystem.
08:43:51,680 INFO [org.jboss.as.naming] (ServerService Thread Pool -- 46) WFLYNAM0001: Activating Naming Subsystem
08:43:51,686 INFO [org.jboss.remoting] (MSC service thread 1-11) JBoss Remoting version 4.0.8.Final
08:43:51,687 INFO [org.jboss.as.webservices] (ServerService Thread Pool -- 56) WFLYWS0002: Activating WebServices Extension
08:43:51,704 INFO [org.jboss.as.connector.subsystems.datasources] (ServerService Thread Pool -- 33) WFLYJCA0004: Deploying JDBC-compliant driver class org.h2.Driver (version 1.3)
08:43:51,707 INFO [org.wildfly.extension.undertow] (MSC service thread 1-5) WFLYUT0003: Undertow 1.2.0.Beta10 starting
08:43:51,707 INFO [org.wildfly.extension.undertow] (ServerService Thread Pool -- 55) WFLYUT0003: Undertow 1.2.0.Beta10 starting
08:43:51,714 INFO [org.jboss.as.connector] (MSC service thread 1-3) WFLYJCA0009: Starting JCA Subsystem (IronJacamar 1.2.3.Final)
08:43:51,725 INFO [org.jboss.as.connector.deployers.jdbc] (MSC service thread 1-5) WFLYJCA0018: Started Driver service with driver-name = h2
08:43:51,813 INFO [org.jboss.as.naming] (MSC service thread 1-5) WFLYNAM0003: Starting Naming Service
08:43:51,814 INFO [org.jboss.as.mail.extension] (MSC service thread 1-7) WFLYMAIL0001: Bound mail session [java:jboss/mail/Default]
08:43:51,876 INFO [org.wildfly.extension.undertow] (ServerService Thread Pool -- 55) WFLYUT0014: Creating file handler for path /Users/foogaro/wildfly9/wildfly-9.0.0.Beta2/welcome-content
08:43:51,904 INFO [org.wildfly.extension.undertow] (MSC service thread 1-16) WFLYUT0012: Started server default-server.
08:43:51,926 INFO [org.wildfly.extension.undertow] (MSC service thread 1-2) WFLYUT0018: Host default-host starting
08:43:51,990 INFO [org.wildfly.extension.undertow] (MSC service thread 1-12) WFLYUT0006: Undertow HTTP listener default listening on /127.0.0.1:8080
08:43:52,122 INFO [org.jboss.as.connector.subsystems.datasources] (MSC service thread 1-12) WFLYJCA0001: Bound data source [java:jboss/datasources/ExampleDS]
08:43:52,166 INFO [org.jboss.as.server.deployment.scanner] (MSC service thread 1-5) WFLYDS0013: Started FileSystemDeploymentService for directory /Users/foogaro/wildfly9/wildfly-9.0.0.Beta2/standalone/deployments
08:43:52,244 INFO [org.jboss.ws.common.management] (MSC service thread 1-11) JBWS022052: Starting JBoss Web Services - Stack CXF Server 5.0.0.Beta3
08:43:52,403 INFO [org.jboss.as] (Controller Boot Thread) WFLYSRV0060: Http management interface listening on http://127.0.0.1:9990/management
08:43:52,403 INFO [org.jboss.as] (Controller Boot Thread) WFLYSRV0051: Admin console listening on http://127.0.0.1:9990
08:43:52,403 INFO [org.jboss.as] (Controller Boot Thread) WFLYSRV0025: WildFly Full 9.0.0.Beta2 (WildFly Core 1.0.0.Beta2) started in 1970ms - Started 202 of 379 services (210 services are lazy, passive or on-demand)
它是如何工作的...
前几行会告诉你 WildFly 的位置和 JVM 选项。接下来是 Undertow HTTP 监听器,它是处理 HTTP 请求的组件,监听于http://127.0.0.1:8080。实际上,你可以通过指向前面的地址来测试 WildFly 是否正在运行正确,你应该会看到以下类似图像:

WildFly 的欢迎内容
接下来是关于 WildFly 管理监听器的日志,我已强调。第一个监听器是 HTTP 管理监听器,它实际上是一个 HTTP API,用于通过 HTTP 调用命令。第二个是管理控制台,它为你提供了一个 Web 控制台,以简化 WildFly 的大多数配置。HTTP 管理接口和管理控制台分别监听于http://127.0.0.1:9990/management和http://127.0.0.1:9990。
要访问管理控制台,打开浏览器并将它指向http://127.0.0.1:9990/。你应该会看到一个页面,如下面的图像所示:

WildFly 的管理错误页面
WildFly 正在运行,但此时管理控制台不可见,因为没有用户注册到属于管理控制台的安全上下文(WildFly 的正确术语是域)。我们将在本食谱和随后的章节中讨论安全和域。
注意
因此,无论何时我使用“管理控制台”或“Web 控制台”这个术语,它们都指的是同一件事。
此外,前面的屏幕截图告诉你如何创建一个用户来访问管理控制台。我们需要通过使用 WildFly 在bin文件夹中提供的add-user.sh脚本来创建管理员用户。
再次打开你的命令行(除非你正在关闭你的 PC,否则你不应该关闭它)并执行以下命令:
$ cd $WILDFLY
$ ./bin/add-user.sh
下面的屏幕截图是前面代码的结果:

检查脚本及其交互:
-
在前面的屏幕截图中,脚本首先询问用户类型;在我们的情况下,我们需要一个管理用户。所以只需按Enter键或输入
a然后按Enter键。我们不需要在应用级别创建用户,用于创建安全上下文。 -
我们指定用户名,例如
wildfly。 -
然后我们需要输入符合指定策略的密码。所以我们输入
cookbook.2015并按Enter键。 -
我们通过重新输入之前提供的密码来确认上一步提供的密码。
-
我们确认要将我们的用户“wildfly”添加到
ManagementRealm域。所以我们输入yes并按Enter键。 -
在最后一步,脚本会询问用户是否将用于连接 WildFly 的一个进程到另一个进程,或者用于对 EJB 进行认证。在这种情况下,我们输入
no并按Enter键。
现在,让我们再次指向管理地址 http://127.0.0.1:9990。页面现在要求你输入用户名和密码。将用户名指定为 wildfly,密码指定为 cookbook.2015,你应该会看到如下 WildFly 管理控制台:

WildFly 的 Web 控制台
参见
我们很快将深入研究独立模式。在 第二章,以独立模式运行 WildFly 中,将详细描述所有配置文件,你应该使用哪个,以及如何使用管理控制台和 CLI 管理你的实例。我们还将部署管理工具。
以域模式运行 WildFly
自从 JBoss AS 4、5 和 6 以来,域模式是一种全新的功能。它首次在 JBoss AS 7 中引入。
这涉及到将几个 WildFly 实例组合成一个单一组,即使用 WildFly 命名法的服务器组。实际上,我们将 WildFly 服务器组合成一个逻辑服务器组,所有 WildFly 实例将共享相同的配置。通过这种方式,我们意味着它们将共享相同的 WildFly 配置文件(default、ha、full 和 full-ha),相同的部署,等等。不会共享的是特定实例配置,例如 IP 绑定地址、端口等。
假设你有一个应用程序,并且你希望它在你的基础设施的四个服务器上部署(或者更确切地说,是在测试而不是预生产或生产环境中)。你需要配置一个服务器组,将四个服务器实例与之关联,然后你就完成了。仅处理服务器组时,所有设置和更改都将传播到所有相关服务器实例。这绝对是 JBoss AS 中的一个重大遗漏,但现在我们有了它。
注意
记住,服务器组在任何方式下都不会形成一个集群。
准备工作
在处理域模式时,有两个新术语需要了解和理解:域控制器(DC)和主机控制器(HC)。
第一个,即域控制器(DC),充当父进程,乐队的“总指挥”。所有更改都是由 DC 向所有服务器组的所有 HC 提供的。DC 配置文件命名为 domain.xml,而 HC 配置文件命名为 host.xml。
注意
为了避免混淆,最好明确我们在书中将使用的术语。我们可能会将运行域控制器的服务器称为“主服务器”或“域”。或者,我们可能会将不是域的运行 WildFly 实例称为“主机”或“从服务器”。
另一件需要了解的事情是,与独立模式不同,在独立模式下,你有不同配置文件的不同文件,在域模式下,你最终只有一个文件(还有一个名为 host.xml 的文件,但我们很快就会谈到它),其中包含了为你配置的所有配置文件。配置文件与独立模式相同。我们将在专门介绍域模式的章节中学习如何将配置文件关联到服务器组。
让我们看看domain文件夹:
| 文件夹名称 | 描述 |
|---|---|
configuration |
域和宿主控制器以及在此安装上运行的任何服务器的配置文件。域内管理的服务器的所有配置信息都位于此处,并且是配置信息的唯一位置。 |
content |
宿主控制器控制此安装的内部工作区域。这是它内部存储部署内容的地方。此目录由 WildFly 启动时生成,不打算由最终用户操作。请注意,域模式不支持基于扫描文件系统部署内容。 |
lib/ext |
使用扩展列表机制引用的已安装库 jar 文件的位置。 |
log |
宿主控制器进程写入其日志的位置。进程控制器,一个实际产生其他宿主控制器进程和任何应用服务器进程的小型轻量级进程,也在这里写入日志。 |
| servers | 由从该安装运行的应用服务器实例使用的可写区域。每个应用服务器实例将拥有自己的子目录,当服务器首次启动时创建。在每个服务器的子目录中,将包含以下子目录:
-
data:服务器写入需要从服务器重启中恢复的信息 -
log:服务器的日志文件 -
tmp:服务器写入的临时文件的位置
|
tmp |
服务器写入的临时文件的位置 |
|---|---|
tmp/auth |
专门用于与本地客户端交换认证令牌的位置,以便它们可以确认它们是正在运行的 AS 进程的本地。 |
因此,configuration 文件夹包含所有配置文件。让我们看看它们全部:
| 文件名称 | 描述 |
|---|---|
domain.xml (默认名称) |
这是包含所有 WildFly 配置文件以及运行 AS 所需的所有其他配置的主配置文件。当 WildFly 启动时,如果未指定其他文件,它将寻找名为domain.xml的文件。 |
host.xml (默认名称) |
这是 WildFly 安装提供的宿主控制器配置文件。在这里,您将找到有关服务器实例的所有特定配置。当 WildFly 启动时,如果未指定其他文件,它将寻找名为host.xml的文件。 |
host-master.xml |
这是 WildFly 安装提供的宿主控制器配置示例文件。它展示了如何配置仅运行域控制器的 WildFly。 |
host-slave.xml |
这是 WildFly 安装提供的宿主控制器配置示例文件,用于配置作为从机运行的 WildFly 并连接到域控制器。 |
如何操作...
让我们尝试使用默认设置(两个服务器组——第一个有两个名为server-one和server-two的实例,最后一个有一个名为server-three的实例,自动启动已禁用)在域模式下运行 WildFly。按照以下步骤操作:
$ CD $WILDFLY_HOME
$ ./bin/domain.sh -b 0.0.0.0 -bmanagement 0.0.0.0
====================================================================
JBoss Bootstrap Environment
JBOSS_HOME: /home/wildfly/WFC/wildfly
JAVA: /home/wildfly/WFC/jdk8/bin/java
JAVA_OPTS: -Xms64m -Xmx512m -XX:MaxPermSize=256m -Djava.net.preferIPv4Stack=true -Djboss.modules.system.pkgs=org.jboss.byteman -Djava.awt.headless=true
====================================================================
Java HotSpot(TM) 64-Bit Server VM warning: ignoring option MaxPermSize=256m; support was removed in 8.0
08:50:53,715 INFO [org.jboss.modules] (main) JBoss Modules version 1.4.2.Final
08:50:53,804 INFO [org.jboss.as.process.Host Controller.status] (main) WFLYPC0018: Starting process 'Host Controller'
[Host Controller] Java HotSpot(TM) 64-Bit Server VM warning: ignoring option MaxPermSize=256m; support was removed in 8.0
[Host Controller] 08:50:54,154 INFO [org.jboss.modules] (main) JBoss Modules version 1.4.2.Final
[Host Controller] 08:50:54,841 INFO [org.jboss.msc] (main) JBoss MSC version 1.2.4.Final
[Host Controller] 08:50:54,869 INFO [org.jboss.as] (MSC service thread 1-7) WFLYSRV0049: WildFly Full 9.0.0.Beta2 (WildFly Core 1.0.0.Beta2) starting
[Host Controller] 08:50:55,326 INFO [org.xnio] (MSC service thread 1-7) XNIO version 3.3.0.Final
[Host Controller] 08:50:55,328 INFO [org.jboss.as] (Controller Boot Thread) WFLYHC0003: Creating http management service using network interface (management) port (9990) securePort (-1)
[Host Controller] 08:50:55,332 INFO [org.xnio.nio] (MSC service thread 1-7) XNIO NIO Implementation Version 3.3.0.Final
[Host Controller] 08:50:55,391 INFO [org.jboss.remoting] (MSC service thread 1-7) JBoss Remoting version 4.0.8.Final
[Host Controller] 08:50:55,415 INFO [org.jboss.as.remoting] (MSC service thread 1-1) WFLYRMT0001: Listening on 0.0.0.0:9999
[Host Controller] 08:50:56,189 INFO [org.jboss.as.host.controller] (Controller Boot Thread) WFLYHC0023: Starting server server-one
08:50:56,199 INFO [org.jboss.as.process.Server:server-one.status] (ProcessController-threads - 3) WFLYPC0018: Starting process 'Server:server-one'
[Server:server-one] 08:50:56,527 INFO [org.jboss.modules] (main) JBoss Modules version 1.4.2.Final
[Server:server-one] 08:50:56,692 INFO [org.jboss.msc] (main) JBoss MSC version 1.2.4.Final
[Server:server-one] 08:50:56,753 INFO [org.jboss.as] (MSC service thread 1-7) WFLYSRV0049: WildFly Full 9.0.0.Beta2 (WildFly Core 1.0.0.Beta2) starting
...
[Host Controller] 08:50:57,401 INFO [org.jboss.as.domain.controller.mgmt] (Remoting "master:MANAGEMENT" task-4) WFLYHC0021: Server [Server:server-one] connected using connection [Channel ID 56504cde (inbound) of Remoting connection 0f0a1d33 to /192.168.59.3:50968]
[Host Controller] 08:50:57,420 INFO [org.jboss.as.host.controller] (Controller Boot Thread) WFLYHC0023: Starting server server-two
08:50:57,423 INFO [org.jboss.as.process.Server:server-two.status] (ProcessController-threads - 3) WFLYPC0018: Starting process 'Server:server-two'
[Host Controller] 08:50:57,430 INFO [org.jboss.as.host.controller] (server-registration-threads - 1) WFLYHC0020: Registering server server-one
...
[Server:server-two] 08:50:58,213 INFO [org.jboss.modules] (main) JBoss Modules version 1.4.2.Final
[Server:server-two] 08:50:58,513 INFO [org.jboss.msc] (main) JBoss MSC version 1.2.4.Final
[Server:server-two] 08:50:58,621 INFO [org.jboss.as] (MSC service thread 1-6) WFLYSRV0049: WildFly Full 9.0.0.Beta2 (WildFly Core 1.0.0.Beta2) starting
它是如何工作的...
域控制器负责启动所有本地主机控制器以及配置的实例。现在,打开 WildFly 管理控制台,你可以看到您实例的详细信息概览:

WildFly 的运行时域概述
从域的角度来看,你可以看到两个名为main-server-group的服务器组,具有full配置文件,以及具有full-ha配置文件的other-server-group。后者没有运行,因为其自动启动属性设置为 false。
注意
这次,即使第一次在域模式下运行,我们也不必创建新用户,因为我们已经在设置独立模式时创建了。
参见
我们很快将深入探讨域模式。在第三章中,在域模式下运行 WildFly,将详细描述所有配置文件,除了学习如何选择正确的配置文件以及如何使用管理控制台和 CLI 管理您的实例。我们将分析域和主机控制器进程。我们将创建各种服务器组示例,包括本地运行和在伪不同机器上运行。我们还将部署管理工具并分析其在域模式下的行为。
将 WildFly 作为服务运行
在这个菜谱中,你将学习如何将 WildFly 作为服务安装,实际上是在操作系统启动时自动运行 WildFly。如果你需要手动按需运行 Wildfly,你可以跳过这个菜谱,除非出于知识目的。
大多数类 Unix 系统都有不同的“运行级别”(可以将它们视为系统运行阶段的步骤)。在操作系统级别,只有当其他服务已成功激活时,才能激活服务。因此,如果你激活一个在之前一个服务启动并运行之前需要网络的服务的服务,它将出现故障或变得无用。这就是“运行级别”的基本作用。
以下是一个运行级别的列表:
-
rc1.d:单用户模式
-
rc2.d:带网络的单用户模式
-
rc3.d:多用户模式—以文本模式启动
-
rc4.d:未定义
-
rc5.d:多用户模式—在 X 窗口中启动
-
rc6.d:关机
大多数生产级 Linux 系统使用运行级别 3(不需要 UI,这将是一种资源浪费),但为了达到所有受众,我们将使用级别 2、3 和 5。
如何做到这一点...
WildFly 附带一个预定义的脚本,可以用来将 WildFly 作为服务运行。此脚本位于 WildFly 安装文件夹的bin/init.d文件夹中。因此,我们只需将系统文件夹/etc/init.d中的文件复制进去,并将其设置为服务,如下所示:
$ sudo cp $WILDFLY_HOME/bin/init.d/wildfly-init-redhat.sh /etc/init.d/wildfly
$ sudo chkconfig --add wildfly
$ sudo chkconfig wildfly on --level 235
还有另一个文件我们需要查看,那就是wildfly.conf,位于 WildFly 安装文件夹的同一bin/init.d目录中。以下就是该文件的内容:
# General configuration for the init.d scripts,
# not necessarily for JBoss AS itself.
# default location: /etc/default/wildfly
## Location of JDK
# JAVA_HOME="/usr/lib/jvm/default-java"
## Location of WildFly
# JBOSS_HOME="/opt/wildfly"
## The username who should own the process.
# JBOSS_USER=wildfly
## The mode WildFly should start, standalone or domain
# JBOSS_MODE=standalone
## Configuration for standalone mode
# JBOSS_CONFIG=standalone.xml
## Configuration for domain mode
# JBOSS_DOMAIN_CONFIG=domain.xml
# JBOSS_HOST_CONFIG=host-master.xml
## The amount of time to wait for startup
# STARTUP_WAIT=60
## The amount of time to wait for shutdown
# SHUTDOWN_WAIT=60
## Location to keep the console log
# JBOSS_CONSOLE_LOG="/var/log/wildfly/console.log"
之前的配置文件基本上设置了一系列参数,告诉init-script使用哪种 WildFly 模式,哪个配置文件,WildFly 应该以哪个用户运行,等等。任何更新都应该放入该文件中。
目前,我们将依赖默认设置,除了我们将通过取消注释行# JBOSS_USER=wildfly,移除#符号来明确提到的用户。你可能已经注意到,你也可以指定 WildFly 将运行的模式:域模式或独立模式。
我们现在需要创建wildfly用户,并将 WildFly 的home文件夹的所有权赋予wildfly用户。按照以下步骤操作:
$ sudo groupadd -r wildfly
$ sudo useradd -r -g wildfly -s /sbin/nologin -c "WildFly user" wildfly
$ sudo passwd -d wildfly
$ sudo chown -R :wildfly $WILDFLY_HOME/*
现在,如果你重启系统,WildFly 将以默认设置作为服务启动并运行,由wildfly用户启动。
第二章:以独立模式运行 WildFly
在本章中,我们将涵盖以下主题:
-
从自定义配置文件夹运行 WildFly
-
将 WildFly 绑定到自定义端口
-
将 WildFly 绑定到自定义 IP
-
配置多个 WildFly 实例在同一台机器上运行,具有不同的端口
-
配置多个 WildFly 实例在同一台机器上运行,具有不同的 IP 地址
-
使用部署文件夹管理应用程序
-
连接到 CLI
-
通过 CLI 检查服务器状态
-
通过 CLI 部署应用程序
-
通过 CLI 卸载应用程序
-
通过 CLI 批量执行命令
-
通过 CLI 重新加载服务器配置
-
通过 CLI 关闭和重新启动实例
-
通过 CLI 暂停和恢复实例
-
通过 CLI 备份配置文件
简介
在本章中,你将学习如何管理以独立模式运行的 WildFly。首先,我们将讨论哪个配置文件符合我们的需求,然后我们将介绍如何使用特定的配置文件运行 WildFly、通过 Web 控制台导航以及 CLI。
你还将学习如何在只有一个网络接口的情况下,在同一台机器上运行多个 WildFly 实例,这些实例具有不同的端口和 IP 地址。你将学习如何连接到 CLI、检索服务器信息、重新加载服务器配置以及关闭、部署和卸载应用程序。
从自定义配置文件夹运行 WildFly
在这个菜谱中,你将学习如何从自定义配置文件夹运行 WildFly。如果你想要使用相同的 WildFly 安装文件夹,但运行两个或更多具有不同配置的 WildFly 实例,这可能会很有用。
如何操作…
-
你需要做的只是将
standalone文件夹复制到你的选择路径。 -
就这样!现在只需像往常一样运行 WildFly,通过传递
-Djboss.server.base.dir指令来指定你的配置文件夹:$ cd $WILDFLY_HOME $ ./bin/standalone.sh -Djboss.server.base.dir=/your/config/path
它是如何工作的…
在指定不同的 jboss.server.base.dir 目录时,WildFly 尝试从预期的文件夹中获取 standalone.xml 文件。实际上,你的配置路径必须保持相同的文件夹结构。WildFly 允许你覆盖不同的路径,如果你需要的话。
以下是一个总结所有此类路径的表格:
| 属性名称 | 用途 | 默认值 |
|---|---|---|
java.ext.dirs |
JDK 扩展目录路径 | Null |
jboss.home.dir |
WildFly 安装根目录 | 由 standalone.sh 设置为 $JBOSS_HOME |
jboss.server.base.dir |
服务器内容的基目录 | jboss.home.dir/standalone |
jboss.server.config.dir |
基配置目录 | jboss.server.base.dir/configuration |
jboss.server.data.dir |
用于持久数据文件存储的目录 | jboss.server.base.dir/data |
jboss.server.log.dir |
包含 server.log 文件的目录 |
jboss.server.base.dir/log |
jboss.server.temp.dir |
用于临时文件存储的目录 | jboss.server.base.dir/tmp |
jboss.server.deploy.dir |
用于存储已部署内容的目录 | jboss.server.data.dir/content |
更多内容...
此外,你可以通过指定--server-config指令来使用不同的配置文件,如下所示:
$ $WILDFLY_HOME/bin/standalone.sh --server-config=standalone-ha.xml
将 WildFly 绑定到自定义端口
你为什么要将 WildFly 绑定到自定义端口?这是因为你可能在同一 IP:PORT 上运行不同的服务(即另一个 WildFly 或 JBoss 实例、Tomcat 或 GlassFish)。
如何操作...
只需打开你的命令行并按照以下方式启动你的 WildFly 独立实例:
$ cd $WILDFLY_HOME
$ ./bin/standalone.sh
现在,你可以使用 Web 控制台或 CLI 来更改端口号:
使用 Web 控制台
-
将你的浏览器指向以下地址:
http://127.0.0.1:8080/console。 -
通过输入第一章中指定的凭据登录,欢迎来到 WildFly!在添加管理用户时;我们输入
wildfly作为用户名,cookbook.2015作为密码。 -
选择配置选项卡,然后在左侧的常规配置菜单中选择Socket 绑定,并选择查看,如图所示:
![使用 Web 控制台]()
-
选择
http属性,然后滚动到页面底部编辑端口号,如图所示:![使用 Web 控制台]()
-
现在,将端口号从
8080更改为9080,然后点击标有保存的按钮。你会收到通知,但 GUI 会显示更新成功,并且需要重新加载服务器才能利用新的更改。使用 Web 控制台 -
转到运行时选项卡。你应该看到一个标有重新加载的按钮,点击它并确认,如图所示:
![使用 Web 控制台]()
-
现在打开你的 Web 控制台,使用新的端口号,如下所示:
http://localhost:9080/console。
使用 CLI
在不同的终端中,连接到 CLI 并按照以下步骤操作:
$ ./bin/jboss-cli.sh
你目前处于断开连接状态;输入connect连接到服务器或输入help查看支持的命令列表:
[disconnected /] connect
[standalone@localhost:9990 /] /socket-binding-group=standard-sockets/socket-binding=http:read-attribute(name=port)
{
"outcome" => "success",
"result" => expression "${jboss.http.port:8080}"
}
[standalone@localhost:9990 /] /socket-binding-group=standard-sockets/socket-binding=http:read-attribute(name=bound-port)
{
"outcome" => "success",
"result" => 8080
}
[standalone@localhost:9990 /] /socket-binding-group=standard-sockets/socket-binding=http:write-attribute(name=port,value=9080)
{
"outcome" => "success",
"response-headers" => {
"operation-requires-reload" => true,
"process-state" => "reload-required"
}
}
[standalone@localhost:9990 /] reload
[standalone@localhost:9990 /] /socket-binding-group=standard-sockets/socket-binding=http:read-attribute(name=port)
{
"outcome" => "success",
"result" => 9080
}
[standalone@localhost:9990 /]
更多内容...
实际上还有另一种更改端口号的方法,即通过传递standalone.sh,一个 Java 参数(以大写字母"D"开头),如下所示:
$ cd $WILDFLY_HOME
$ ./bin/standalone.sh -Djboss.http.port=9080
这将匹配standalone.xml中socket-binding标签指定的名为http的属性,对于port属性。
注意
如果你来自 JBoss AS 5 或 6,你可能已经使用了port-offset,它仍然通过将偏移量添加到默认值(即8080)来更改端口号,但也会更改其他端口号。在 WildFly 中,它还会更改管理端口。
因此,我们将端口号偏移量指定为1000,如下所示:
$ ./bin/standalone.sh -Djboss.socket.binding.port-offset=1000
我们最终将使 WildFly 监听在端口 9080(即 8080+1000)上,而 WildFly 的管理端口将是 10990(即 9090+1000)。
将 WildFly 绑定到自定义 IP
至于端口号,你可能希望将 WildFly 绑定到与默认地址不同的地址;例如,你的公共 IP 地址。
如何操作…
要实现这种定制化,你可以同时使用 Web 控制台和 CLI。然而,通常情况下,你会对运行 WildFly 的服务器有 SSH 访问权限,或者更糟(从安全角度来说更好),你可能在到达 WildFly 服务器之前,在不同的服务器上使用 SSH。在这种情况下,你将只能使用 CLI,这就是为什么从现在开始我们将主要使用 CLI 来管理服务器。
小贴士
习惯使用命令行界面(CLI)将使你更深入地了解 WildFly 的配置设置,并且它将更加易于记忆,这样你就不会忘记它。
让我们按照以下方式更改 IP 地址:
$ cd $WILDFLY_HOME
$ ./bin/standalone.sh -Djboss.bind.address=10.0.0.1
...
22:56:05,531 INFO [org.wildfly.extension.undertow] (MSC service thread 1-3) WFLYUT0006: Undertow HTTP listener default listening on /10.0.0.1:8080
...
它是如何工作的…
如果你查看 standalone.xml,你会注意到在以下 {} 内部的属性,并且以美元符号 $ 开头。这个属性将由在启动服务器时指定的同名参数的值所替换。
还有更多...
此外,当你想要修改管理接口地址时,相同的逻辑也适用。在这种情况下,你将需要传递 jboss.bind.address.management 参数。
顺便说一下,这两个参数都可以使用快捷方式指定,例如,对于 jboss.bind.address 参数,可以使用 -b 10.0.0.1,而对于 jboss.bind.address.management,可以使用 -bmanagement 10.0.0.1。
配置多个 WildFly 实例在同一台机器上以不同的端口运行
在某些情况下,主要是因为架构原因,你可能在单个服务器上需要运行多个 WildFly 实例。你可以通过隔离每个实例并为其分配不同的绑定端口来实现这一点。
准备工作
首先,我们需要为每个我们想要设置和运行的实例创建一个独立配置。我们所需做的就是复制在 从自定义配置文件夹运行 WildFly 菜单中解释的概念。假设我们想要两个运行节点/实例,我们使用以下命令:
$ cd $WILDFLY_HOME
$ cp -a standalone node-1
$ cp -a standalone node-2
现在,我们已经准备好配置每个实例。
如何操作…
要实现这样的要求,你可以使用在 在自定义端口绑定 WildFly 菜单中解释的任何一种方法。让我们看看它们中的每一个。
使用 jboss.http.port
首先尝试的是通过传递参数 jboss.http.port 来运行两个 WildFly 实例,显然,它们的值是不同的。实际上,其中一个可能使用默认值:
$ cd $WILDFLY_HOME
$ ./bin/standalone.sh -Djboss.server.base.dir=$WILDFLY_HOME//node-1 -Djboss.http.port=8180
...
10:30:23,924 INFO [org.wildfly.extension.undertow] (MSC service thread 1-7) WFLYUT0006: Undertow HTTP listener default listening on /127.0.0.1:8180
...
现在,我们将在不同的终端窗口中通过传递不同的 jboss.http.port 参数来运行另一个 WildFly 实例:
$ cd $WILDFLY_HOME
$ ./bin/standalone.sh -Djboss.server.base.dir=$WILDFLY_HOME$WILDFLY_HOME/node-2 -Djboss.http.port=8280
...
10:30:34,205 INFO [org.wildfly.extension.undertow] (MSC service thread 1-6) WFLYUT0006: Undertow HTTP listener default listening on /127.0.0.1:8280
...
10:30:34,473 ERROR [org.jboss.msc.service.fail] (MSC service thread 1-8) MSC000001: Failed to start service jboss.serverManagement.controller.management.http: org.jboss.msc.service.StartException in service jboss.serverManagement.controller.management.http: WFLYSRV0083: Failed to start the http-interface service
...
10:30:34,685 INFO [org.jboss.as] (Controller Boot Thread) WFLYSRV0054: Admin console is not enabled
10:30:34,685 ERROR [org.jboss.as] (Controller Boot Thread) WFLYSRV0026: WildFly Full 9.0.0.Beta2 (WildFly Core 1.0.0.Beta2) started (with errors) in 3252ms - Started 196 of 377 services (2 services failed or missing dependencies, 210 services are lazy, passive or on-demand)
哎呀!出错了!
如您从日志中看到的,由于 "地址已被占用" 的消息,http-interface 无法正确启动。这是因为我们更改了 jboss.http.port,但没有更改管理接口的类似配置,即 standalone.xml 中定义的 http-interface:

standalone.xml 中定义的 "http-interface"
每个独立实例都有自己的管理接口,因此我们需要更改其绑定。让我们修复它:
$ cd $WILDFLY_HOME
$ ./bin/standalone.sh -Djboss.server.base.dir=$WILDFLY_HOME//node-1 -Djboss.http.port=8180 -Djboss.management.http.port=9991
...
11:11:05,862 INFO [org.wildfly.extension.undertow] (MSC service thread 1-4) WFLYUT0006: Undertow HTTP listener default listening on /127.0.0.1:8180
...
11:11:06,405 INFO [org.jboss.as] (Controller Boot Thread) WFLYSRV0060: Http management interface listening on http://127.0.0.1:9991/management
11:11:06,406 INFO [org.jboss.as] (Controller Boot Thread) WFLYSRV0051: Admin console listening on http://127.0.0.1:9991
...
这是在不同的终端窗口中:
$ cd $WILDFLY_HOME
$ ./bin/standalone.sh -Djboss.server.base.dir=$WILDFLY_HOME$WILDFLY_HOME/node-2 -Djboss.http.port=8280 -Djboss.management.http.port=9992
...
11:11:59,777 INFO [org.wildfly.extension.undertow] (MSC service thread 1-3) WFLYUT0006: Undertow HTTP listener default listening on /127.0.0.1:8280
...
11:12:00,358 INFO [org.jboss.as] (Controller Boot Thread) WFLYSRV0060: Http management interface listening on http://127.0.0.1:9992/management
11:12:00,359 INFO [org.jboss.as] (Controller Boot Thread) WFLYSRV0051: Admin console listening on http://127.0.0.1:9992
...
好的!现在如果你检查操作系统级别的开放套接字,通过 java 进程进行过滤,你会看到以下内容:

使用 jboss.socket.binding.port-offset
好的,让我们尝试使用 port-offset 指令:
$ cd $WILDFLY_HOME
$ ./bin/standalone.sh -Djboss.serever.base.dir=$WILDFLY_HOME/node-1 -Djboss.socket.binding.port-offset=100
...
11:35:05,783 INFO [org.wildfly.extension.undertow] (MSC service thread 1-3) WFLYUT0006: Undertow HTTP listener default listening on /127.0.0.1:8180
...
11:35:06,512 INFO [org.jboss.as] (Controller Boot Thread) WFLYSRV0060: Http management interface listening on http://127.0.0.1:10090/management
11:35:06,513 INFO [org.jboss.as] (Controller Boot Thread) WFLYSRV0051: Admin console listening on http://127.0.0.1:10090
11:35:06,513 INFO [org.jboss.as] (Controller Boot Thread) WFLYSRV0025: WildFly Full 9.0.0.Beta2 (WildFly Core 1.0.0.Beta2) started in 3228ms - Started 202 of 379 services (210 services are lazy, passive or on-demand)
以下是在不同的终端窗口中输入的:
$ cd $WILDFLY_HOME
$ ./bin/standalone.sh -Djboss.serever.base.dir=$WILDFLY_HOME/node-2 -Djboss.socket.binding.port-offset=200
...
11:35:23,389 INFO [org.wildfly.extension.undertow] (MSC service thread 1-5) WFLYUT0006: Undertow HTTP listener default listening on /127.0.0.1:8280
...
11:35:24,030 INFO [org.jboss.as] (Controller Boot Thread) WFLYSRV0060: Http management interface listening on http://127.0.0.1:10190/management
11:35:24,030 INFO [org.jboss.as] (Controller Boot Thread) WFLYSRV0051: Admin console listening on http://127.0.0.1:10190
11:35:24,031 INFO [org.jboss.as] (Controller Boot Thread) WFLYSRV0025: WildFly Full 9.0.0.Beta2 (WildFly Core 1.0.0.Beta2) started in 3191ms - Started 202 of 379 services (210 services are lazy, passive or on-demand)
如您所注意到的,两个服务器都正常启动,无需任何额外的配置或预防措施。最后,只需在操作系统级别检查套接字:

还有更多...
使用 jboss.socket.binding.port-offset 指令简化了所有配置需求,相比之下,使用 jboss.http.port 时需要每次单独更新配置。
此外,使用 WildFly,通过使用 port-offset 配置,你可以获得更多的好处,因为你还需要调整远程套接字。
配置多个 WildFly 实例在相同机器上以不同的 IP 运行
在某些情况下,主要是由于架构原因,你可能在单个服务器上运行多个 WildFly 实例。你可以通过隔离每个实例并为其提供不同的绑定 IP 来实现这一点。这些 IP 可能是虚拟的,或者实际上是由你系统上安装的物理网络接口提供的。两种方式都很好。
准备中
如果你决定使用虚拟 IP,你可以按照以下方式操作:
$ sudo ifconfig YOUR_NIC:1 10.0.1.1 netmask 255.255.255.0
$ sudo ifconfig YOUR_NIC:2 10.0.1.2 netmask 255.255.255.0
这里 YOUR_NIC 可能是 eth0。现在,如果你列出服务器上所有可用的接口,你也会看到以下新的接口:
$ ifconfig
eth0: flags=4099<UP,BROADCAST,MULTICAST> mtu 1500
ether f0:de:f1:99:b2:94 txqueuelen 1000 (Ethernet)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 0 bytes 0 (0.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
device interrupt 20 memory 0xf2600000-f2620000
eth0:1: flags=4099<UP,BROADCAST,MULTICAST> mtu 1500
inet 10.0.1.1 netmask 255.255.255.0 broadcast 10.0.1.255
ether f0:de:f1:99:b2:94 txqueuelen 1000 (Ethernet)
device interrupt 20 memory 0xf2600000-f2620000
eth0:2: flags=4099<UP,BROADCAST,MULTICAST> mtu 1500
inet 10.0.1.2 netmask 255.255.255.0 broadcast 10.0.1.255
ether f0:de:f1:99:b2:94 txqueuelen 1000 (Ethernet)
device interrupt 20 memory 0xf2600000-f2620000
之后,我们需要为每个我们想要设置和运行的实例创建一个独立配置。如果您已经从之前的菜谱(配置多个 WildFly 实例在相同机器上以不同的端口运行)中这样做,您可以直接跳转到 如何操作... 部分。否则,我们只需要复制 绑定 WildFly 到自定义 IP 菜谱中解释的概念。假设我们想要两个运行节点/实例,命令如下:
$ cd $WILDFLY_HOME
$ cp -a standalone node-1
$ cp -a standalone node-2
现在我们已经准备好配置每个实例。
如何操作...
打开一个终端窗口并输入以下命令:
$ cd $WILDFLY_HOME
$ ./bin/standalone.sh -Djboss.server.base.dir=$WILDFLY_HOME/node-1 -Djboss.bind.address=10.0.1.1
…
22:32:34,259 INFO [org.wildfly.extension.undertow] (MSC service thread 1-6) WFLYUT0006: Undertow HTTP listener default listening on /10.0.1.1:8080
22:32:34,549 INFO [org.jboss.as.server.deployment.scanner] (MSC service thread 1-3) WFLYDS0013: Started FileSystemDeploymentService for directory /home/luigi/WFC/wildfly/node-1/deployments
22:32:34,623 INFO [org.jboss.as.connector.subsystems.datasources] (MSC service thread 1-7) WFLYJCA0001: Bound data source [java:jboss/datasources/ExampleDS]
22:32:34,729 INFO [org.jboss.ws.common.management] (MSC service thread 1-6) JBWS022052: Starting JBoss Web Services - Stack CXF Server 5.0.0.Beta3
22:32:35,022 INFO [org.jboss.as] (Controller Boot Thread) WFLYSRV0060: Http management interface listening on http://127.0.0.1:9990/management
22:32:35,023 INFO [org.jboss.as] (Controller Boot Thread) WFLYSRV0051: Admin console listening on http://127.0.0.1:9990
22:32:35,024 INFO [org.jboss.as] (Controller Boot Thread) WFLYSRV0025: WildFly Full 9.0.0.Beta2 (WildFly Core 1.0.0.Beta2) started in 3260ms - Started 202 of 379 services (210 services are lazy, passive or on-demand)
在不同的终端窗口中输入以下内容:
$ cd $WILDFLY_HOME
./bin/standalone.sh -Djboss.server.base.dir=$WILDFLY_HOME/node-2 -Djboss.bind.address=10.0.1.2
...
22:33:02,522 INFO [org.wildfly.extension.undertow] (MSC service thread 1-8) WFLYUT0006: Undertow HTTP listener default listening on /10.0.1.2:8080
22:33:02,735 INFO [org.jboss.as.server.deployment.scanner] (MSC service thread 1-8) WFLYDS0013: Started FileSystemDeploymentService for directory /home/luigi/WFC/wildfly/node-2/deployments
22:33:02,830 ERROR [org.jboss.msc.service.fail] (MSC service thread 1-1) MSC000001: Failed to start service jboss.serverManagement.controller.management.http: org.jboss.msc.service.StartException in service jboss.serverManagement.controller.management.http: WFLYSRV0083: Failed to start the http-interface service
at org.jboss.as.server.mgmt.UndertowHttpManagementService.start(UndertowHttpManagementService.java:269)
at org.jboss.msc.service.ServiceControllerImpl$StartTask.startService(ServiceControllerImpl.java:1948)
at org.jboss.msc.service.ServiceControllerImpl$StartTask.run(ServiceControllerImpl.java:1881)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
Caused by: java.lang.RuntimeException: java.net.BindException: Address already in use
at org.jboss.as.domain.http.server.ManagementHttpServer.start(ManagementHttpServer.java:160)
at org.jboss.as.server.mgmt.UndertowHttpManagementService.start(UndertowHttpManagementService.java:235)
... 5 more
Caused by: java.net.BindException: Address already in use
at sun.nio.ch.Net.bind0(Native Method)
at sun.nio.ch.Net.bind(Net.java:436)
at sun.nio.ch.Net.bind(Net.java:428)
at sun.nio.ch.ServerSocketChannelImpl.bind(ServerSocketChannelImpl.java:214)
at sun.nio.ch.ServerSocketAdaptor.bind(ServerSocketAdaptor.java:74)
at sun.nio.ch.ServerSocketAdaptor.bind(ServerSocketAdaptor.java:67)
at org.xnio.nio.NioXnioWorker.createTcpConnectionServer(NioXnioWorker.java:182)
at org.xnio.XnioWorker.createStreamConnectionServer(XnioWorker.java:243)
at org.jboss.as.domain.http.server.ManagementHttpServer.start(ManagementHttpServer.java:147)
... 6 more
22:33:02,858 INFO [org.jboss.as.connector.subsystems.datasources] (MSC service thread 1-7) WFLYJCA0001: Bound data source [java:jboss/datasources/ExampleDS]
22:33:02,907 INFO [org.jboss.ws.common.management] (MSC service thread 1-3) JBWS022052: Starting JBoss Web Services - Stack CXF Server 5.0.0.Beta3
22:33:02,911 ERROR [org.jboss.as.controller.management-operation] (Controller Boot Thread) WFLYCTL0013: Operation ("add") failed - address: ([
("core-service" => "management"),
("management-interface" => "http-interface")
]) - failure description: {"WFLYCTL0080: Failed services" => {"jboss.serverManagement.controller.management.http" => "org.jboss.msc.service.StartException in service jboss.serverManagement.controller.management.http: WFLYSRV0083: Failed to start the http-interface service
Caused by: java.lang.RuntimeException: java.net.BindException: Address already in use
Caused by: java.net.BindException: Address already in use"}}
22:33:02,969 INFO [org.jboss.as.controller] (Controller Boot Thread) WFLYCTL0183: Service status report
WFLYCTL0186: Services which failed to start: service jboss.serverManagement.controller.management.http: org.jboss.msc.service.StartException in service jboss.serverManagement.controller.management.http: WFLYSRV0083: Failed to start the http-interface service
22:33:03,253 INFO [org.jboss.as] (Controller Boot Thread) WFLYSRV0063: Http management interface is not enabled
22:33:03,254 INFO [org.jboss.as] (Controller Boot Thread) WFLYSRV0054: Admin console is not enabled
22:33:03,254 ERROR [org.jboss.as] (Controller Boot Thread) WFLYSRV0026: WildFly Full 9.0.0.Beta2 (WildFly Core 1.0.0.Beta2) started (with errors) in 3277ms - Started 196 of 377 services (2 services failed or missing dependencies, 210 services are lazy, passive or on-demand)
哎呀!又失败了!
现在怎么了?几乎和上一个菜谱中一样的问题。我们没有更改管理接口的任何绑定参数(IP 地址或端口号)。
实际上,正如您可以从node-1实例的日志中看到的那样,管理和 HTTP 接口绑定到本地地址,这是默认地址。因此,node-2实例将具有相同的设置,因此出现错误:地址已被占用。
让我们修复这个错误:
$ ./bin/standalone.sh -Djboss.server.base.dir=$WILDFLY_HOME/node-1 -Djboss.bind.address=10.0.1.1 -Djboss.bind.address.management=10.0.1.1
...
22:38:41,054 INFO [org.wildfly.extension.undertow] (MSC service thread 1-6) WFLYUT0006: Undertow HTTP listener default listening on /10.0.1.1:8080
22:38:41,313 INFO [org.jboss.as.server.deployment.scanner] (MSC service thread 1-6) WFLYDS0013: Started FileSystemDeploymentService for directory /home/luigi/WFC/wildfly/node-1/deployments
22:38:41,372 INFO [org.jboss.as.connector.subsystems.datasources] (MSC service thread 1-2) WFLYJCA0001: Bound data source [java:jboss/datasources/ExampleDS]
22:38:41,450 INFO [org.jboss.ws.common.management] (MSC service thread 1-8) JBWS022052: Starting JBoss Web Services - Stack CXF Server 5.0.0.Beta3
22:38:41,792 INFO [org.jboss.as] (Controller Boot Thread) WFLYSRV0060: Http management interface listening on http://10.0.1.1:9990/management
22:38:41,793 INFO [org.jboss.as] (Controller Boot Thread) WFLYSRV0051: Admin console listening on http://10.0.1.1:9990
22:38:41,794 INFO [org.jboss.as] (Controller Boot Thread) WFLYSRV0025: WildFly Full 9.0.0.Beta2 (WildFly Core 1.0.0.Beta2) started in 3224ms - Started 202 of 379 services (210 services are lazy, passive or on-demand)
在不同的终端窗口中输入以下内容:
$ ./bin/standalone.sh -Djboss.server.base.dir=$WILDFLY_HOME/node-2 -Djboss.bind.address=10.0.1.2 -Djboss.bind.address.management=10.0.1.2
…
22:39:55,815 INFO [org.wildfly.extension.undertow] (MSC service thread 1-1) WFLYUT0006: Undertow HTTP listener default listening on /10.0.1.2:8080
22:39:56,054 INFO [org.jboss.as.server.deployment.scanner] (MSC service thread 1-4) WFLYDS0013: Started FileSystemDeploymentService for directory /home/luigi/WFC/wildfly/node-2/deployments
22:39:56,176 INFO [org.jboss.as.connector.subsystems.datasources] (MSC service thread 1-8) WFLYJCA0001: Bound data source [java:jboss/datasources/ExampleDS]
22:39:56,241 INFO [org.jboss.ws.common.management] (MSC service thread 1-7) JBWS022052: Starting JBoss Web Services - Stack CXF Server 5.0.0.Beta3
22:39:56,567 INFO [org.jboss.as] (Controller Boot Thread) WFLYSRV0060: Http management interface listening on http://10.0.1.2:9990/management
22:39:56,567 INFO [org.jboss.as] (Controller Boot Thread) WFLYSRV0051: Admin console listening on http://10.0.1.2:9990
22:39:56,568 INFO [org.jboss.as] (Controller Boot Thread) WFLYSRV0025: WildFly Full 9.0.0.Beta2 (WildFly Core 1.0.0.Beta2) started in 3271ms - Started 202 of 379 services (210 services are lazy, passive or on-demand)
就这样!我们同时运行了两个 WildFly 实例,每个实例都使用一个专用的 IP 地址,即使它们都在使用相同的端口。让我们在操作系统级别检查一下:

还有更多…
正如您在本食谱和上一个食谱中注意到的,我们可以自定义 WildFly 相对于 IP 地址和端口号的绑定,这既适用于服务组件(即您的应用程序),也适用于管理组件(即管理控制台和 CLI)。
显然,我们甚至可以混合自定义,从而更改 IP 和端口,并更改服务组件的端口号和管理接口的 IP 地址。只要您不进行等于绑定,您就可以做任何想做的事情。
典型的配置将服务组件绑定到公共接口(即外部可见的 IP 地址)和管理接口绑定到私有接口(即仅本地或网络内可见的 IP 地址)。
使用部署文件夹管理应用程序
配置和自定义 WildFly 独立实例后,是时候部署我们的第一个应用程序了。WildFly 提供了许多部署应用程序的方法,其中之一是通过部署扫描器(对于那些来自 JBoss AS 版本的人所熟知)。基本上,您需要做的就是将您的工件复制到独立实例的deployments文件夹中。
在生产环境中,您最好关闭部署扫描器,以避免意外替换部署——您会遇到很大的麻烦。请使用 CLI 或管理控制台进行适当的“部署”操作。我们将在本章中看到这两种方法。
准备工作
在本食谱中,我们需要一个 Java Web 应用程序。如果您愿意,可以使用我 GitHub 账户中的一个项目,地址如下:github.com/foogaro/wildfly-cookbook.git。
您可以使用git-clone克隆仓库,或者将其作为 ZIP 存档下载。无论哪种方式,都在WFC文件夹中创建一个名为github的文件夹,并将源文件放入其中。
使用git-clone命令,请按照以下步骤操作:
$ cd ~/WFC
$ mkdir github
$ cd github
$ git clone https://github.com/foogaro/wildfly-cookbook.git
一旦git完成了仓库的克隆,您将找到一个名为example的项目。要编译项目,请按照以下步骤操作:
$ cd ~/WFC/github/wildfly-cookbook/example
$ mvn -e clean package
前面的命令编译项目并将 Web 应用程序工件生成到名为target的文件夹中。在那里您可以找到名为example.awar的应用程序,准备部署。
注意
此外,还有官方 WildFly 快速入门存储库,其中包含大量宝贵的资源可供参考,并且它也在此地址上可供贡献:github.com/wildfly/quickstart。
现在,假设我们将使用默认的standalone文件夹作为我们实例的基本配置路径。为了看到一切在行动,最好首先运行 WildFly,然后使用deployments文件夹开始管理应用程序本身。
如何操作…
-
首先,让我们启动 WildFly:
$ cd $WILDFLY_HOME $ ./bin/standalone.sh ... WFLYSRV0025: WildFly Full 9.0.0.Beta2 (WildFly Core 1.0.0.Beta2) started in 3087ms - Started 202 of 379 services (210 services are lazy, passive or on-demand) -
现在,保持您的终端窗口可见,打开您喜欢的文件管理工具,并将
example.war复制到deployments文件夹中。或者,您可以使用新终端复制 Web 应用程序,使用以下命令:
$ cp ~/WFC/github/wildfly-cookbook/example/target/example.war $WILDFLY_HOME/standalone/deployments -
几秒钟后(定时器间隔设置为每 5 秒一次,因此您可能需要等待几毫秒或 5 秒),我们将得到以下输出:
22:51:31,396 INFO [org.jboss.as.repository] (DeploymentScanner-threads - 1) WFLYDR0001: Content added at location /home/luigi/WFC/wildfly/standalone/data/content/21/7dd6250d5bc4afcabdffb0b25c99db92239b5a/content 22:51:31,447 INFO [org.jboss.as.server.deployment] (MSC service thread 1-3) WFLYSRV0027: Starting deployment of "example.war" (runtime-name: "example.war") 22:51:31,986 INFO [org.wildfly.extension.undertow] (MSC service thread 1-4) WFLYUT0021: Registered web context: /example 22:51:32,046 INFO [org.jboss.as.server] (DeploymentScanner-threads - 1) WFLYSRV0010: Deployed "example.war" (runtime-name : "example.war")并且您也应该在
deployments文件夹中获得一个名为example.war.deployed的新文件。这是一个标记,表示应用程序已成功部署。如果发生错误,任何操作都会回滚,并创建一个名为example.war.failed的新文件。 -
现在,你能猜到如何取消部署吗?是的...将标记文件扩展名重命名为
.undeploy,如下所示:22:55:17,702 INFO [org.wildfly.extension.undertow] (MSC service thread 1-7) WFLYUT0022: Unregistered web context: /example 22:55:17,736 INFO [org.hibernate.validator.internal.util.Version] (MSC service thread 1-4) HV000001: Hibernate Validator 5.1.3.Final 22:55:17,801 INFO [org.jboss.as.server.deployment] (MSC service thread 1-3) WFLYSRV0028: Stopped deployment example.war (runtime-name: example.war) in 109ms 22:55:17,817 INFO [org.jboss.as.repository] (DeploymentScanner-threads - 1) WFLYDR0002: Content removed from location /home/luigi/WFC/wildfly/standalone/data/content/21/7dd6250d5bc4afcabdffb0b25c99db92239b5a/content 22:55:17,817 INFO [org.jboss.as.server] (DeploymentScanner-threads - 1) WFLYSRV0009: Undeployed "example.war" (runtime-name: "example.war")在
deployments文件夹中,已创建一个名为example.war.undeployed的新文件。现在我们如何重新部署它?删除两个标记文件,或者创建一个名为example.war.dodeploy的新文件。
它是如何工作的…
为了回顾我们迄今为止所学的内容,标记文件始终具有与应用程序相同的名称加上后缀。以下表格总结了所有可用的标记:
| 后缀 | 描述 |
|---|---|
.dodeploy |
用户生成,以指示给定内容应部署。 |
.skipdeploy |
用户生成,以指示给定内容应跳过自动部署,只要文件存在。这允许您修改展开的内容,而无需在更新过程中由扫描器启动重新部署。对于压缩内容也是如此。 |
.isdeploying |
由部署扫描器服务放置,以指示它已注意到.dodeploy文件或新的或更新的自动部署模式内容,并正在部署内容。当部署过程完成时,此标记文件将被删除,因此您可能看不到它对于小型工件。 |
.deployed |
由部署扫描器服务放置,以指示给定内容已部署。如果您删除此文件,内容将被取消部署。 |
.failed |
由部署扫描器服务放置,以指示给定内容在其部署过程中失败。在文件中,您将找到有关部署过程中出现问题的信息。请注意,在自动部署模式下,删除此文件将使部署有资格再次部署。 |
.isundeploying |
由部署扫描服务放置,以指示它已经注意到.deployed文件已被删除,内容正在被卸载。当卸载过程完成时,此标记文件将被删除,因此你可能看不到它对于小型工件。 |
.undeployed |
由部署扫描服务放置,以指示给定内容已被卸载。如果你删除此文件,将没有任何影响。 |
.pending |
由部署扫描服务放置,以指示它已经注意到需要部署内容,但尚未指示服务器部署它。如果扫描器检测到某些自动部署内容仍在复制过程中,或者存在阻止自动部署的问题,则会创建此文件。只要这种条件保持不变,扫描器就不会指示服务器部署或卸载任何内容(不仅仅是直接受影响的内容)。 |
连接到 CLI
有三种方式来管理你的 WildFly,即通过编辑 XML 文件、控制台和 CLI。
-
首先,直接编辑 XML 可能会出错,因为它可能会浪费你宝贵的几秒钟;此外,每次更改都需要重启服务器。
-
控制台为你提供了可视编辑功能,但它并不完整。Web 控制台只能执行所有可用操作的一个子集,这些操作你可以使用 CLI 执行。
-
CLI 代表命令行界面,它是一个你被推荐用来管理你的 WildFly 应用服务器的工具。
为什么选择 CLI?大多数时候,出于安全原因,你通过 SSH 连接到企业环境,因此根本无法看到 Web 控制台。CLI WildFly 在这种情况下非常有用。
CLI 是一个强大的工具,它让你完全控制 WildFly;你可以部署和卸载应用程序,创建和管理数据源,管理日志,更改系统属性,停止和启动实例,等等。使用 CLI 还可以帮助你理解 WildFly 的核心逻辑,这样你才能真正成为一个 WildFly 管理员专家。尽管如此,如果你真的还想有一个 GUI,你可以在 GUI 版本中拥有 CLI;只需在你运行 WildFly 时执行以下命令:
$ cd $WILDFLY_HOME
$ ./bin/jboss-cli.sh --gui
以下截图展示了 CLI GUI 工具:

CLI GUI 工具
现在,是时候用 CLI 玩玩儿了。让我们看看你能做什么!
准备工作
由于 CLI 是一个管理工具,你需要确保你的 WildFly 实例正在运行。在这种情况下,我们也将使用默认的独立配置。
打开一个终端窗口并启动 WildFly:
$ cd $WILDFLY_HOME
$ ./bin/standalone.sh
...
22:12:23,600 INFO [org.jboss.as] (Controller Boot Thread) WFLYSRV0025: WildFly Full 9.0.0.Beta2 (WildFly Core 1.0.0.Beta2) started in 3087ms - Started 202 of 379 services (210 services are lazy, passive or on-demand)
如何操作…
-
让我们打开一个终端窗口:
$ cd $WILDFLY_HOME $ ./bin/jboss-cli.sh You are disconnected at the moment. Type connect to connect to the server or help for the list of supported commands. [disconnected /] -
一旦进入,我们需要连接到正在运行的实例。通过输入命令
connect,该工具使用默认参数连接到实例,即localhost:9990。[disconnected /] connect [standalone@localhost:9990 /] -
你也可以通过命令行直接连接到 CLI,如下所示传递--connect 参数:
$ cd $WILDFLY_HOME $ ./bin/jboss-cli.sh --connect [standalone@localhost:9990 /]从现在开始,我们将直接连接到 CLI,而无需从内部连接到它。
-
现在尝试使用
ls命令列出(就像在 Linux 中做的那样):![如何操作…]()
-
列表命令为您提供了可以操作的所有组件,基本上就是您在编辑
standalone.xml时可以看到的内容。实际上,列出子系统时,您将看到在standalone.xml中声明的所有子系统:![如何操作…]()
更多内容…
记住,您的键盘上的Tab键是您的朋友。如果您不记得一个命令,只需按Tab。如果您不记得如何完成一个命令,只需按Tab。按Tab键将显示当前上下文中的所有可能解决方案。
CLI 中有两个其他特殊字符:/(正斜杠)和:(冒号)。正斜杠用于在上下文中导航,而冒号用于在最后选定的上下文中调用方法操作。请查看下一道菜谱中的示例。
通过 CLI 检查服务器状态
在这个配方中,我们将学习如何检查服务器状态。WildFly 可能根据其阶段有不同的状态:启动中、停止、已启动和已停止。有时,仅通过在操作系统级别查找其进程来检查其状态可能还不够。
准备工作
由于 CLI 是一个管理工具,您需要确保您的 WildFly 实例已启动并运行。此外,对于这个配方,我们将依赖默认的独立配置。
打开一个终端窗口并启动 WildFly:
$ cd $WILDFLY_HOME
$ ./bin/standalone.sh
...
22:12:23,600 INFO [org.jboss.as] (Controller Boot Thread) WFLYSRV0025: WildFly Full 9.0.0.Beta2 (WildFly Core 1.0.0.Beta2) started in 3226ms - Started 202 of 379 services (210 services are lazy, passive or on-demand)
如何操作…
在一个新的终端窗口中,输入以下内容:
$ ./bin/jboss-cli.sh
You are disconnected at the moment. Type 'connect' to connect to the server or 'help' for the list of supported commands.
[disconnected /] connect
[standalone@localhost:9990 /] :read-attribute(name=server-state)
{
"outcome" => "success",
"result" => "running"
}
[standalone@localhost:9990 /]
它是如何工作的…
当您连接到主机控制器时,您就在配置的根目录。因此,您可以在其上调用操作,而要做到这一点,您需要从冒号(:)符号开始。
在我们的示例中,我们使用了read-attribute方法,但您可以使用任何可用的方法。要查看它们,只需在冒号符号后按Tab键,如下所示:

回到我们的服务器状态,我们收到了两个响应:一个表示我们的调用成功,另一个报告结果,运行。
通过 CLI 部署应用程序
在这个配方中,我们将学习如何使用 CLI 部署应用程序。在这里,我们将仅使用基本选项,但请记住,在部署期间您有多个选项,我们将在专门介绍 CLI 的章节中深入分析。
准备工作
在这个配方中,以及接下来的一个配方中,我们都需要一个应用程序来测试我们的配置。对于这个配方,我们需要名为example的应用程序,您可以在我的 GitHub 仓库中找到它。如果您跳过了使用部署文件夹管理应用程序配方,请参阅它以下载您需要的源代码和项目。
如何操作…
-
当您的 WildFly 实例启动并运行时,打开一个终端并像往常一样连接到 CLI:
$ ./bin/jboss-cli.sh --connect [standalone@localhost:9990 /] -
现在我们需要告诉 CLI 按照以下方式部署我们的应用程序:
[standalone@localhost:9990 /] deploy example.war [standalone@localhost:9990 /] -
让我们看看
server.log文件:23:02:29,511 INFO [org.jboss.as.repository] (management-handler-thread - 3) WFLYDR0001: Content added at location /home/luigi/WFC/wildfly/standalone/data/content/21/7dd6250d5bc4afcabdffb0b25c99db92239b5a/content 23:02:29,517 INFO [org.jboss.as.server.deployment] (MSC service thread 1-6) WFLYSRV0027: Starting deployment of "example.war" (runtime-name: "example.war") 23:02:29,613 INFO [org.wildfly.extension.undertow] (MSC service thread 1-3) WFLYUT0021: Registered web context: /example 23:02:29,641 INFO [org.jboss.as.server] (management-handler-thread - 3) WFLYSRV0010: Deployed "example.war" (runtime-name : "example.war") -
现在,让我们检查从 CLI 部署的情况:
[standalone@localhost:9990 /] deployment-info --name=example.war NAME RUNTIME-NAME PERSISTENT ENABLED STATUS example.war example.war true true OK [standalone@localhost:9990 /]
如何工作…
我强调了一行日志以引起你的注意。正如你所见,WildFly 会自动将其工件保存到其data/content文件夹中。在该文件夹中,你会找到许多带有哈希名称的文件夹,其中包含工件。然后工件被重命名为content。
如果你尝试使用unzip命令查看content文件信息,你将发现你的工件结构如下所示:

更多内容...
你也可以部署一个禁用、状态停止的工件,并在方便的时候启用它,如下所示:
[standalone@localhost:9990 /] deploy example.war --disabled
这将仅将工件添加到您运行模式下的 WildFly 的data/content文件夹中,以便稍后启用:
[standalone@localhost:9990 /] deploy --name=example.war
在standalone.xml配置文件内部也有对部署的引用;打开它并向下滚动到末尾。

部署后更新的 Standalone.xml
是的,你会找到name和runtime-name以及哈希值。在如何工作...部分,我向你展示了部署实际持久化的位置,在这种情况下,它被存储在$WILDFLY_HOME/standalone/data/content/文件夹中。
你注意到了吗?sha1值的第一个字符表示第一个文件夹,sha1哈希值的其余部分是包含已重命名为content的工件的子文件夹。
小贴士
获取原始部署文件有时可以救你一命,尤其是在处理生产环境时,最后一刻的工作并没有那么“集成”。请确保备份安全。
现在让我们看看deployments文件夹。它是空的。这是因为所有内容都进入了运行时的data/content文件夹。尝试停止实例并将example.war应用程序复制到deployments文件夹。
现在再次启动实例。你得到了什么?类似于以下内容:
23:16:54,708 INFO [org.jboss.modules] (main) JBoss Modules version 1.4.2.Final
23:16:54,934 INFO [org.jboss.msc] (main) JBoss MSC version 1.2.4.Final
23:16:55,003 INFO [org.jboss.as] (MSC service thread 1-6) WFLYSRV0049: WildFly Full 9.0.0.Beta2 (WildFly Core 1.0.0.Beta2) starting
23:16:56,170 INFO [org.jboss.as.controller.management-deprecated] (ServerService Thread Pool -- 20) WFLYCTL0028: Attribute enabled is deprecated, and it might be removed in future version!
23:16:56,243 ERROR [org.jboss.as.controller.management-operation] (Controller Boot Thread) WFLYCTL0013: Operation ("add") failed - address: ([("deployment" => "example.war")]) - failure description: "WFLYCTL0212: Duplicate resource [(\"deployment\" => \"example.war\")]"
23:16:56,249 FATAL [org.jboss.as.server] (Controller Boot Thread) WFLYSRV0056: Server boot has failed in an unrecoverable manner; exiting. See previous messages for details.
23:16:56,260 INFO [org.jboss.as.server] (Thread-2) WFLYSRV0220: Server shutdown has been requested.
23:16:56,285 INFO [org.jboss.as] (MSC service thread 1-3) WFLYSRV0050: WildFly Full 9.0.0.Beta2 (WildFly Core 1.0.0.Beta2) stopped in 16ms
这是因为你尝试部署了同一个工件两次,一次使用 CLI,一次使用deployment文件夹。有一个名为deployment-scanner的监听器,每次你修改deployment文件夹的内容时都会被触发。顺便说一句,你可以使用 CLI 解决前面的问题,或者通过将自动生成的 XML 代码移除到standalone.xml中,并将工件留在deployments文件夹中。
通过 CLI 卸载应用程序
在这个菜谱中,我们将学习如何使用 CLI 卸载应用程序。根据deploy命令,我们将使用基本可用的选项。我们将在本书的后面深入分析undeploy命令。
准备工作
这个配方遵循之前的配方,即 通过 CLI 部署应用程序。这意味着我们将找到 example 应用程序已准备好进行 undeploy。如果您跳过了之前的配方,请遵循它提供的说明,或者您可以提供自己要卸载的应用程序。
如何操作...
-
当您的 WildFly 实例运行时,打开终端并像往常一样连接到 CLI:
$ ./bin/jboss-cli.sh --connect [standalone@localhost:9990 /] -
现在我们需要告诉 CLI 按以下方式卸载我们的应用程序:
[standalone@localhost:9990 /] undeploy example.war [standalone@localhost:9990 /] -
并且
server.log记录以下内容:23:19:50,912 INFO [org.wildfly.extension.undertow] (MSC service thread 1-4) WFLYUT0022: Unregistered web context: /example 23:19:50,961 INFO [org.hibernate.validator.internal.util.Version] (MSC service thread 1-2) HV000001: Hibernate Validator 5.1.3.Final 23:19:51,010 INFO [org.jboss.as.server.deployment] (MSC service thread 1-5) WFLYSRV0028: Stopped deployment example.war (runtime-name: example.war) in 115ms 23:19:51,058 INFO [org.jboss.as.repository] (management-handler-thread - 1) WFLYDR0002: Content removed from location /home/luigi/WFC/wildfly/standalone/data/content/21/7dd6250d5bc4afcabdffb0b25c99db92239b5a/content 23:19:51,059 INFO [org.jboss.as.server] (management-handler-thread - 1) WFLYSRV0009: Undeployed "example.war" (runtime-name: "example.war")
还有更多...
如果您复制并粘贴了之前的命令,您可能没有注意到 undeploy 命令的一个有用功能。
您可以告诉 CLI 首先列出所有部署,这样您就可以使用以下命令选择正确的要卸载的应用程序:
[standalone@localhost:9990 /] undeploy -l
NAME RUNTIME-NAME ENABLED STATUS
example.war example.war true OK
[standalone@localhost:9990 /]
通过 CLI 批处理模式执行命令
在这个配方中,我们将学习如何在批处理模式下调用命令。实际上,您可以声明并准备一个要执行的命令列表,并一次性按顺序执行它们。批处理模式使您的操作和配置保持一致性。
准备工作
让我们的 WildFly 实例运行起来;从我们下载 GitHub 存储库的文件夹 WFC/github/wildfly-cookbook,将 example.war 和 simple.war 应用程序复制到 $JBOSS_HOME 文件夹,并连接到 CLI。
如果您能找到应用程序,您可能需要编译项目。我将向您展示如何为 simple 应用程序操作,其他应用程序也是如此。打开终端并按以下步骤操作:
$ cd ~/WFC/github/wildfly-cookbook
$ cd simple
$ mvn -e clean package
$ cp target/simple.war ~/WFC/wildfly/
如何操作...
我们将按以下顺序执行以下操作:
-
部署
example.war。 -
部署
simple.war。 -
让我们去 CLI:
$ cd ~/WFC/wildfly $ ./bin/jboss-cli.sh --connect [standalone@localhost:9990 /] batch [standalone@localhost:9990 / #] deploy example.war [standalone@localhost:9990 / #] deploy simple.war [standalone@localhost:9990 / #] run-batch The batch executed successfully [standalone@localhost:9990 /] -
并且
server.log记录以下内容:... 07:33:02,191 INFO [org.jboss.as.repository] (management-handler-thread - 4) JBAS014900: Content added at location /home/lfugaro/wildfly/standalone/data/content/7a/a7c67cb54e0affa9d60cf98230e0c17efd1119/content 07:33:02,205 INFO [org.jboss.as.repository] (management-handler-thread - 4) JBAS014900: Content added at location /home/lfugaro/wildfly/standalone/data/content/e0/2abb62b1b29f97f532ef1501910d64af194b21/content 07:33:02,227 INFO [org.jboss.as.server.deployment] (MSC service thread 1-2) JBAS015876: Starting deployment of "simple.war" (runtime-name: "simple.war") 07:33:02,227 INFO [org.jboss.as.server.deployment] (MSC service thread 1-7) JBAS015876: Starting deployment of "example.war" (runtime-name: "example.war") 07:33:02,689 INFO [org.wildfly.extension.undertow] (MSC service thread 1-1) JBAS017534: Registered web context: /simple 07:33:02,689 INFO [org.wildfly.extension.undertow] (MSC service thread 1-6) JBAS017534: Registered web context: /example 07:33:02,746 INFO [org.jboss.as.server] (management-handler-thread - 4) JBAS018559: Deployed "simple.war" (runtime-name : "simple.war") 07:33:02,746 INFO [org.jboss.as.server] (management-handler-thread - 4) JBAS018559: Deployed "example.war" (runtime-name : "example.war")
通过 CLI 重新加载服务器配置
在这个配方中,我们将学习如何在无需完全重启的情况下重新加载服务器配置。通常,有一些更改需要重新加载服务器。大多数时候,您会在服务器启动后或测试期间意识到某个特定设置需要不同的值。因此,您无需停止和启动整个应用程序服务器,只需重新加载配置即可,除非更改涉及 JVM 或启动时需要的设置。
准备工作
我们已经看到如何在玩 IP 和端口绑定时重新加载服务器,但再次提一下。
如何操作...
命令本身很简单:
$ ./bin/jboss-cli.sh --connect
[standalone@localhost:9990 /] reload
[standalone@localhost:9990 /]
它是如何工作的...
基本上,reload 命令按顺序发出 stop 命令,停止所有 WildFly 活动服务,然后发出 start 命令,再次启动服务。这应该给您提供重要信息。JVM 保持不变。如果您在发出 reload 命令前后在操作系统级别测试活动进程,您将注意到进程 ID 是相同的。
$ ps -efa | grep java | grep -v grep
luigi 4915 4879 80 16:07 pts/3 00:00:11 /home/luigi/WFC/jdk8/bin/java -D[Standalone] -server -Xms64m -Xmx512m -XX:MaxPermSize=256m -Djava.net.preferIPv4Stack=true -Djboss.modules.system.pkgs=org.jboss.byteman -Djava.awt.headless=true -Dorg.jboss.boot.log.file=/home/luigi/WFC/wildfly/standalone/log/server.log -Dlogging.configuration=file:/home/luigi/WFC/wildfly/standalone/configuration/logging.properties -jar /home/luigi/WFC/wildfly/jboss-modules.jar -mp /home/luigi/WFC/wildfly/modules org.jboss.as.standalone -Djboss.home.dir=/home/luigi/WFC/wildfly -Djboss.server.base.dir=/home/luigi/WFC/wildfly/standalone
luigi 5031 5023 36 16:07 pts/2 00:00:01 /home/luigi/WFC/jdk8/bin/java -Djboss.modules.system.pkgs=com.sun.java.swing -Dlogging.configuration=file:/home/luigi/WFC/wildfly/bin/jboss-cli-logging.properties -jar /home/luigi/WFC/wildfly/jboss-modules.jar -mp /home/luigi/WFC/wildfly/modules org.jboss.as.cli --connect
最后但同样重要的是,您的 CLI 不会断开连接。
通过 CLI 关闭和重新启动实例
在这个食谱中,我们将学习如何通过 CLI 停止 WildFly 实例,或者通过 CLI 重新启动它。重新启动选项与上一个食谱中的 reload 命令相反。
准备工作
启动你的 WildFly,以便我们可以通过 CLI 直接连接到它,如下所示:
$ cd ~/WFC/wildfly
$ ./bin/standalone.sh
如何操作…
命令本身很简单:
$ ./bin/jboss-cli.sh --connect
[standalone@localhost:9990 /] shutdown
[disconnected /]
工作原理…
前面的命令停止了一切并将你从 CLI 中移除。因此,如果你需要重新启动你的 WildFly 实例,你需要再次执行 standalone.sh 脚本,以及任何之前定义的参数。
还有更多...
或者,如果你真正需要的是完全停止和启动,你可以依靠 shutdown 命令的 --restart=true 选项。
在使用带有 restart 选项的 shutdown 命令之前,注意 WildFly 实例的进程 ID (PID),如下所示:
$ ps -efa | grep java | grep -v grep
luigi 5031 5023 0 16:07 pts/2 00:00:03 /home/luigi/WFC/jdk8/bin/java -Djboss.modules.system.pkgs=com.sun.java.swing -Dlogging.configuration=file:/home/luigi/WFC/wildfly/bin/jboss-cli-logging.properties -jar /home/luigi/WFC/wildfly/jboss-modules.jar -mp /home/luigi/WFC/wildfly/modules org.jboss.as.cli --connect
luigi 5285 5249 99 16:29 pts/3 00:00:09 /home/luigi/WFC/jdk8/bin/java -D[Standalone] -server -Xms64m -Xmx512m -XX:MaxPermSize=256m -Djava.net.preferIPv4Stack=true -Djboss.modules.system.pkgs=org.jboss.byteman -Djava.awt.headless=true -Dorg.jboss.boot.log.file=/home/luigi/WFC/wildfly/standalone/log/server.log -Dlogging.configuration=file:/home/luigi/WFC/wildfly/standalone/configuration/logging.properties -jar /home/luigi/WFC/wildfly/jboss-modules.jar -mp /home/luigi/WFC/wildfly/modules org.jboss.as.standalone -Djboss.home.dir=/home/luigi/WFC/wildfly -Djboss.server.base.dir=/home/luigi/WFC/wildfly/standalone
好的,现在回到 CLI,按照以下步骤操作:
[standalone@localhost:9990 /] shutdown --restart=true
[standalone@localhost:9990 /]
第一个明显的区别是,你的 CLI 不会断开连接。现在让我们回到终端,列出操作系统级别的可用进程,就像之前做的那样:
$ ps -efa | grep java | grep -v grep
luigi 5421 5413 10 16:29 pts/2 00:00:01 /home/luigi/WFC/jdk8/bin/java -Djboss.modules.system.pkgs=com.sun.java.swing -Dlogging.configuration=file:/home/luigi/WFC/wildfly/bin/jboss-cli-logging.properties -jar /home/luigi/WFC/wildfly/jboss-modules.jar -mp /home/luigi/WFC/wildfly/modules org.jboss.as.cli --connect
luigi 5482 5249 99 16:29 pts/3 00:00:10 /home/luigi/WFC/jdk8/bin/java -D[Standalone] -server -Xms64m -Xmx512m -XX:MaxPermSize=256m -Djava.net.preferIPv4Stack=true -Djboss.modules.system.pkgs=org.jboss.byteman -Djava.awt.headless=true -Dorg.jboss.boot.log.file=/home/luigi/WFC/wildfly/standalone/log/server.log -Dlogging.configuration=file:/home/luigi/WFC/wildfly/standalone/configuration/logging.properties -jar /home/luigi/WFC/wildfly/jboss-modules.jar -mp /home/luigi/WFC/wildfly/modules org.jboss.as.standalone -Djboss.home.dir=/home/luigi/WFC/wildfly -Djboss.server.base.dir=/home/luigi/WFC/wildfly/standalone
这可能是与 reload 命令最重要的区别。你将获得一个全新的 JVM,操作系统级别的不同进程 ID。前面的命令在 CLI 中完全关闭了当前实例并启动了一个新的实例,如下面的日志条目所示:
...
10:43:31,997 INFO [org.jboss.as.server] (management-handler-thread - 2) WFLYSRV0211: Suspending server
10:43:32,002 INFO [org.jboss.as.server] (Thread-2) WFLYSRV0220: Server shutdown has been requested.
10:43:32,016 INFO [org.wildfly.extension.undertow] (MSC service thread 1-10) WFLYUT0019: Host default-host stopping
10:43:32,017 INFO [org.jboss.as.connector.subsystems.datasources] (MSC service thread 1-1) WFLYJCA0010: Unbound data source [java:jboss/datasources/ExampleDS]
10:43:32,025 INFO [org.jboss.as.connector.deployers.jdbc] (MSC service thread 1-11) WFLYJCA0019: Stopped Driver service with driver-name = h2
10:43:32,058 INFO [org.wildfly.extension.undertow] (MSC service thread 1-1) WFLYUT0008: Undertow HTTP listener default suspending
10:43:32,065 INFO [org.wildfly.extension.undertow] (MSC service thread 1-1) WFLYUT0007: Undertow HTTP listener default stopped, was bound to /0.0.0.0:8080
10:43:32,066 INFO [org.wildfly.extension.undertow] (MSC service thread 1-5) WFLYUT0004: Undertow 1.2.0.Beta10 stopping
10:43:32,152 INFO [org.jboss.as] (MSC service thread 1-2) WFLYSRV0050: WildFly Full 9.0.0.Beta2 (WildFly Core 1.0.0.Beta2) stopped in 130ms
Restarting JBoss...
Java HotSpot(TM) 64-Bit Server VM warning: ignoring option MaxPermSize=256m; support was removed in 8.0
10:43:32,639 INFO [org.jboss.modules] (main) JBoss Modules version 1.4.2.Final
10:43:32,858 INFO [org.jboss.msc] (main) JBoss MSC version 1.2.4.Final
10:43:32,936 INFO [org.jboss.as] (MSC service thread 1-7) WFLYSRV0049: WildFly Full 9.0.0.Beta2 (WildFly Core 1.0.0.Beta2) starting
10:43:34,064 INFO [org.jboss.as.controller.management-deprecated] (ServerService Thread Pool -- 22) WFLYCTL0028: Attribute enabled is deprecated, and it might be removed in future version!
10:43:34,088 INFO [org.jboss.as.server] (Controller Boot Thread) WFLYSRV0039: Creating http management service using socket-binding (management-http)
10:43:34,120 INFO [org.xnio] (MSC service thread 1-2) XNIO version 3.3.0.Final
10:43:34,130 INFO [org.xnio.nio] (MSC service thread 1-2) XNIO NIO Implementation Version 3.3.0.Final
10:43:34,186 INFO [org.jboss.as.clustering.infinispan] (ServerService Thread Pool -- 38) WFLYCLINF0001: Activating Infinispan subsystem.
10:43:34,185 INFO [org.wildfly.extension.io] (ServerService Thread Pool -- 37) WFLYIO001: Worker 'default' has auto-configured to 16 core threads with 128 task threads based on your 8 available processors
10:43:34,193 INFO [org.jboss.remoting] (MSC service thread 1-2) JBoss Remoting version 4.0.8.Final
10:43:34,220 INFO [org.jboss.as.naming] (ServerService Thread Pool -- 46) WFLYNAM0001: Activating Naming Subsystem
10:43:34,230 INFO [org.jboss.as.security] (ServerService Thread Pool -- 53) WFLYSEC0002: Activating Security Subsystem
10:43:34,228 INFO [org.jboss.as.connector.subsystems.datasources] (ServerService Thread Pool -- 33) WFLYJCA0004: Deploying JDBC-compliant driver class org.h2.Driver (version 1.3)
10:43:34,242 WARN [org.jboss.as.txn] (ServerService Thread Pool -- 54) WFLYTX0013: Node identifier property is set to the default value. Please make sure it is unique.
10:43:34,242 INFO [org.jboss.as.security] (MSC service thread 1-10) WFLYSEC0001: Current PicketBox version=4.9.0.Beta2
10:43:34,245 INFO [org.jboss.as.jsf] (ServerService Thread Pool -- 44) WFLYJSF0007: Activated the following JSF Implementations: [main]
10:43:34,294 INFO [org.jboss.as.webservices] (ServerService Thread Pool -- 56) WFLYWS0002: Activating WebServices Extension
10:43:34,311 INFO [org.wildfly.extension.undertow] (ServerService Thread Pool -- 55) WFLYUT0003: Undertow 1.2.0.Beta10 starting
10:43:34,328 INFO [org.wildfly.extension.undertow] (MSC service thread 1-3) WFLYUT0003: Undertow 1.2.0.Beta10 starting
10:43:34,305 INFO [org.jboss.as.connector] (MSC service thread 1-7) WFLYJCA0009: Starting JCA Subsystem (IronJacamar 1.2.3.Final)
10:43:34,350 INFO [org.jboss.as.connector.deployers.jdbc] (MSC service thread 1-9) WFLYJCA0018: Started Driver service with driver-name = h2
10:43:34,421 INFO [org.jboss.as.naming] (MSC service thread 1-12) WFLYNAM0003: Starting Naming Service
10:43:34,421 INFO [org.jboss.as.mail.extension] (MSC service thread 1-14) WFLYMAIL0001: Bound mail session [java:jboss/mail/Default]
10:43:34,616 INFO [org.wildfly.extension.undertow] (ServerService Thread Pool -- 55) WFLYUT0014: Creating file handler for path /home/wildfly/WFC/wildfly/welcome-content
10:43:34,651 INFO [org.wildfly.extension.undertow] (MSC service thread 1-16) WFLYUT0012: Started server default-server.
10:43:34,670 INFO [org.wildfly.extension.undertow] (MSC service thread 1-16) WFLYUT0018: Host default-host starting
10:43:34,749 INFO [org.wildfly.extension.undertow] (MSC service thread 1-11) WFLYUT0006: Undertow HTTP listener default listening on /0.0.0.0:8080
10:43:34,882 INFO [org.jboss.as.connector.subsystems.datasources] (MSC service thread 1-2) WFLYJCA0001: Bound data source [java:jboss/datasources/ExampleDS]
10:43:34,948 INFO [org.jboss.as.server.deployment.scanner] (MSC service thread 1-5) WFLYDS0013: Started FileSystemDeploymentService for directory /home/wildfly/WFC/wildfly/standalone/deployments
10:43:35,080 INFO [org.jboss.ws.common.management] (MSC service thread 1-2) JBWS022052: Starting JBoss Web Services - Stack CXF Server 5.0.0.Beta3
[standalone@localhost:9990 /] 10:43:35,314 INFO [org.jboss.as] (Controller Boot Thread) WFLYSRV0060: Http management interface listening on http://0.0.0.0:9990/management
10:43:35,320 INFO [org.jboss.as] (Controller Boot Thread) WFLYSRV0051: Admin console listening on http://0.0.0.0:9990
10:43:35,321 INFO [org.jboss.as] (Controller Boot Thread) WFLYSRV0025: WildFly Full 9.0.0.Beta2 (WildFly Core 1.0.0.Beta2) started in 2990ms - Started 202 of 379 services (210 services are lazy, passive or on-demand)
记住,PID 会改变,所以如果你是按照 PID 监控你的实例,请考虑更新你的监控工具/脚本!
通过 CLI 暂停和恢复实例
在这个食谱中,我们将学习如何在不终止活动请求的情况下挂起一个实例。一旦运行中的请求完成,下一个请求将不会被接受。这是 WildFly 9 中的一项新功能。
准备工作
为了测试这个食谱,我们需要名为 grace 的应用程序,你可以在我的 GitHub 仓库中找到它。如果你跳过了 使用部署文件夹管理应用程序 的食谱,请参考它以下载所有所需的源代码和项目。
启动你的 WildFly,以便我们可以通过 CLI 直接连接到它,如下所示:
$ cd ~/WFC/wildfly
$ ./bin/standalone.sh
如何操作…
-
首先,部署应用程序
grace.war。一旦应用程序被部署,使用浏览器在以下 URL 打开它http://127.0.0.1:8080/grace这将访问
index.jsp页面,该页面增加一个计数器然后休眠 10 秒,只是为了模拟一个长时间运行的请求。 -
在运行时,打开一个终端窗口并像往常一样连接到 CLI。一旦进入,按照以下步骤操作:
$ ./bin/jboss-cli.sh --connect [standalone@localhost:9990 /] :suspend -
现在,回到浏览器上,你的请求应该已经完成,你应该会看到一个类似于以下页面的页面:
![如何操作…]()
-
另一方面,要恢复实例,只需调用以下命令:
[standalone@localhost:9990 /] :resume
工作原理…
命令本身很简单。你应该知道下一个用户的请求会发生什么。下一个用户会看到以下页面:

显然,你可以通过一个特殊的页面来处理 HTTP 503 状态码,警告用户由于系统升级导致的技術问题。
在 WildFly 日志中你应该捕获以下条目:
16:38:05,374 INFO [org.jboss.as.server] (management-handler-thread - 7) WFLYSRV0211: Suspending server
resume命令也很简单。需要注意的一点是,用户不会丢失他当前和活跃的会话,这对于用户来说是一个非常好的特性。
在 WildFly 日志中,你应该捕获以下条目:
16:38:25,347 INFO [org.jboss.as.server] (management-handler-thread - 8) WFLYSRV0212: Resuming server
现在,如果你回到应用程序并刷新页面,你应该看到计数器从停止的地方开始增加,如图所示:

通过 CLI 备份配置文件
在这个菜谱中,我们将学习如何通过 CLI 备份我们的配置。本质上,CLI 通过将其复制到snapshot目录来备份standalone.xml文件。
准备工作
启动你的 WildFly,以便我们可以通过 CLI 直接连接到它,如下所示:
$ cd ~/WFC/wildfly
$ ./bin/standalone.sh
如何操作...
命令本身很简单:
$ ./bin/jboss-cli.sh --connect
[standalone@localhost:9990 /] :take-snapshot
{
"outcome" => "success",
"result" => "/home/luigi/WFC/wildfly/standalone/configuration/standalone_xml_history/snapshot/20150301-165737562standalone.xml"
}
就这样!standalone.xml文件的备份被复制到snapshot文件夹中,文件名以当前日期和时间作为前缀。
小贴士
总是备份你的配置,尤其是在生产环境中操作时。
第三章。在域模式下运行 WildFly
在本章中,你将学习以下内容:
-
运行域模式
-
将主机控制器连接到域控制器
-
通过 Web 控制台停止和启动服务器组
-
通过 CLI 停止和启动你的服务器组
-
通过 CLI 停止和启动你的服务器
-
通过 Web 控制台将应用程序部署和取消部署到特定的服务器组
-
通过 CLI 将应用程序部署和取消部署到特定的服务器组
-
通过 CLI 检查服务器状态
简介
在本章中,你将学习如何管理在域模式下运行的 WildFly。你还将学习如何通过 CLI 和 Web 控制台对其进行配置和管理。我们还将回顾适用于域模式的独立章节中的一些主题。如果你想了解以下主题,可以参考本书的附录部分:
-
域模式和服务器组
-
理解
domain.xml的结构 -
理解
host.xml的结构
注意
在本章中,所有示例和内容都使用第一章中描述的 WildFly 安装,即欢迎来到 WildFly!,该链接指向$WILDFLY_HOME变量。
运行域模式
在本食谱中,我们将学习如何在域模式下运行 WildFly。有几个概念你需要了解——域控制器、主机控制器和服务器组。然而,这些概念都在本书的附录部分中解释。
准备工作
WildFly 附带预配置的domain.xml和host.xml文件。定义了两个服务器组:一个名为main-server-group,引用了full配置文件;另一个名为other-server-group,引用了full-ha配置文件。

domain.xml中的服务器组
然后每个服务器组都通过服务器或主机列表引用到host.xml文件中。

在 host.xml 中声明的服务器
如前述 XML 代码片段所示,每个服务器定义都使用属性group引用服务器组成员资格。此外,auto-start属性定义服务器是否会自动启动,或者是否需要从管理控制台或通过 CLI 手动启动。
如何操作…
只需打开你的命令行,然后按照以下方式在域模式下启动 WildFly:
$ cd $WILDFLY_HOME
$ ./bin/domain.sh
...
22:22:53,403 INFO [org.jboss.modules] (main) JBoss Modules version 1.4.2.Final
22:22:53,525 INFO [org.jboss.as.process.Host Controller.status] (main) WFLYPC0018: Starting process 'Host Controller'
[Host Controller] Java HotSpot(TM) 64-Bit Server VM warning: ignoring option MaxPermSize=256m; support was removed in 8.0
[Host Controller] 22:22:53,960 INFO [org.jboss.modules] (main) JBoss Modules version 1.4.2.Final
[Host Controller] 22:22:54,131 INFO [org.jboss.msc] (main) JBoss MSC version 1.2.4.Final
[Host Controller] 22:22:54,168 INFO [org.jboss.as] (MSC service thread 1-7) WFLYSRV0049: WildFly Full 9.0.0.Beta2 (WildFly Core 1.0.0.Beta2) starting
...
22:22:56,441 INFO [org.jboss.as.process.Server:server-one.status] (ProcessController-threads - 3) WFLYPC0018: Starting process 'Server:server-one'
...
[Host Controller] 22:22:58,352 INFO [org.jboss.as.domain.controller.mgmt] (Remoting "master:MANAGEMENT" task-4) WFLYHC0021: Server [Server:server-one] connected using connection [Channel ID 075c2e34 (inbound) of Remoting connection 2865b4ba to /127.0.0.1:37199]
[Host Controller] 22:22:58,407 INFO [org.jboss.as.host.controller] (Controller Boot Thread) WFLYHC0023: Starting server server-two
[Host Controller] 22:22:58,444 INFO [org.jboss.as.host.controller] (server-registration-threads - 1) WFLYHC0020: Registering server server-one
22:22:58,457 INFO [org.jboss.as.process.Server:server-two.status] (ProcessController-threads - 3) WFLYPC0018: Starting process 'Server:server-two'
...
[Server:server-one] 22:23:04,175 INFO [org.jboss.as] (Controller Boot Thread) WFLYSRV0025: WildFly Full 9.0.0.Beta2 (WildFly Core 1.0.0.Beta2) started in 7642ms - Started 229 of 405 services (217 services are lazy, passive or on-demand)
...
[Server:server-two] 22:23:07,024 INFO [org.jboss.as] (Controller Boot Thread) WFLYSRV0025: WildFly Full 9.0.0.Beta2 (WildFly Core 1.0.0.Beta2) started in 8229ms - Started 229 of 405 services (217 services are lazy, passive or on-demand)
访问管理控制台,你可以看到server-one和server-two正在运行,而server-three仍然处于停止状态,如下面的图像所示:

它是如何工作的…
当你在域模式下启动 WildFly 时,脚本会启动 WildFly,它会查找其默认的配置文件。默认情况下,它会查找domain.xml和host.xml文件。当 WildFly 找到这两个文件时,它会解析它们,并开始启动配置文件中所需和声明的所有服务。
在操作系统层面,当你以域模式启动 WildFly 时,实际上是在启动一个名为Process Controller的进程,它必须启动另一个名为Host Controller的进程。Host Controller负责管理服务器配置以及启动在host.xml文件中声明的 WildFly 实例,即服务器。
根据weblogs.java.net/blog/arungupta/archive/2014/05/30/wildfly-managed-domain-raspberry-pi-tech-tip-27,
一个主机控制器实例被配置为充当域控制器。每个主机上的主机控制器,作为远程服务器(无论是物理的还是虚拟的),与域控制器交互,以控制其主机上运行的应用服务器实例的生命周期,并协助域控制器管理它们。
如果你检查操作系统层面正在运行的进程,你可以看到以下内容:
wildfly 1525 1429 7 08:26 ? 00:00:01 /home/wildfly/WFC/jdk8/bin/java -D[Process Controller] -server -Xms64m -Xmx512m -XX:MaxPermSize=256m -Djava.net.preferIPv4Stack=true -Djboss.modules.system.pkgs=org.jboss.byteman -Djava.awt.headless=true -Dorg.jboss.boot.log.file=/home/wildfly/WFC/wildfly/domain/log/process-controller.log -Dlogging.configuration=file:/home/wildfly/WFC/wildfly/domain/configuration/logging.properties -jar /home/wildfly/WFC/wildfly/jboss-modules.jar -mp /home/wildfly/WFC/wildfly/modules org.jboss.as.process-controller -jboss-home /home/wildfly/WFC/wildfly -jvm /home/wildfly/WFC/jdk8/bin/java -mp /home/wildfly/WFC/wildfly/modules -- -Dorg.jboss.boot.log.file=/home/wildfly/WFC/wildfly/domain/log/host-controller.log -Dlogging.configuration=file:/home/wildfly/WFC/wildfly/domain/configuration/logging.properties -server -Xms64m -Xmx512m -XX:MaxPermSize=256m -Djava.net.preferIPv4Stack=true -Djboss.modules.system.pkgs=org.jboss.byteman -Djava.awt.headless=true -- -default-jvm /home/wildfly/WFC/jdk8/bin/java
wildfly 1542 1525 62 08:26 ? 00:00:10 /home/wildfly/WFC/jdk8/bin/java -D[Host Controller] -Dorg.jboss.boot.log.file=/home/wildfly/WFC/wildfly/domain/log/host-controller.log -Dlogging.configuration=file:/home/wildfly/WFC/wildfly/domain/configuration/logging.properties -server -Xms64m -Xmx512m -XX:MaxPermSize=256m -Djava.net.preferIPv4Stack=true -Djboss.modules.system.pkgs=org.jboss.byteman -Djava.awt.headless=true -jar /home/wildfly/WFC/wildfly/jboss-modules.jar -mp /home/wildfly/WFC/wildfly/modules org.jboss.as.host-controller -mp /home/wildfly/WFC/wildfly/modules --pc-address 127.0.0.1 --pc-port 56310 -default-jvm /home/wildfly/WFC/jdk8/bin/java -Djboss.home.dir=/home/wildfly/WFC/wildfly
wildfly 1611 1525 99 08:26 ? 00:00:22 /home/wildfly/WFC/jdk8/bin/java -D[Server:server-one] -Xms64m -Xmx512m -server -Djava.awt.headless=true -Djava.net.preferIPv4Stack=true -Djboss.home.dir=/home/wildfly/WFC/wildfly -Djboss.modules.system.pkgs=org.jboss.byteman -Djboss.server.log.dir=/home/wildfly/WFC/wildfly/domain/servers/server-one/log -Djboss.server.temp.dir=/home/wildfly/WFC/wildfly/domain/servers/server-one/tmp -Djboss.server.data.dir=/home/wildfly/WFC/wildfly/domain/servers/server-one/data -Dlogging.configuration=file:/home/wildfly/WFC/wildfly/domain/servers/server-one/data/logging.properties -jar /home/wildfly/WFC/wildfly/jboss-modules.jar -mp /home/wildfly/WFC/wildfly/modules org.jboss.as.server
wildfly 1657 1525 99 08:26 ? 00:00:22 /home/wildfly/WFC/jdk8/bin/java -D[Server:server-two] -Xms64m -Xmx512m -server -Djava.awt.headless=true -Djava.net.preferIPv4Stack=true -Djboss.home.dir=/home/wildfly/WFC/wildfly -Djboss.modules.system.pkgs=org.jboss.byteman -Djboss.server.log.dir=/home/wildfly/WFC/wildfly/domain/servers/server-two/log -Djboss.server.temp.dir=/home/wildfly/WFC/wildfly/domain/servers/server-two/tmp -Djboss.server.data.dir=/home/wildfly/WFC/wildfly/domain/servers/server-two/data -Dlogging.configuration=file:/home/wildfly/WFC/wildfly/domain/servers/server-two/data/logging.properties -jar /home/wildfly/WFC/wildfly/jboss-modules.jar -mp /home/wildfly/WFC/wildfly/modules org.jboss.as.server
在之前的控制台输出中,我强调了进程 ID 及其 JVM 名称,总结如下:
| 进程 ID | 父进程 ID | JVM 名称 | 描述 |
|---|---|---|---|
1525 |
1429 |
Process Controller |
这是负责创建主机控制器和启动服务器的主要进程。它还负责在主机控制器崩溃的情况下重新创建主机控制器。其父进程 ID 是启动它的domain.shbash 脚本进程。 |
1542 |
1525 |
Host Controller |
这是管理在host.xml文件中定义的所有主机的进程。 |
1611 |
1525 |
Server:server-one |
这是名为server-one的主机的 JVM。 |
1657 |
1525 |
Server:server-two |
这是名为server-two的主机的 JVM。 |
还有更多...
如Process Controller JVM 的描述中所述,这是生成主机控制器和名为server-one和server-two的 WildFly 实例的主要进程。在操作系统层面,这意味着如果 PC 崩溃,所有其子进程也会随之关闭。
相反,如果Host Controller崩溃,真实的 WildFly 实例不会受到影响,它们将继续运行并服务于你的客户端。此外,当Host Controller崩溃时,Process Controller负责重新启动它。让我给你演示一下。
在不同的终端中,终止与Host Controller ID 匹配的进程,在这个例子中是1542:
$ kill -9 1542
在你以域模式启动 WildFly 的终端中,你应该看到以下日志条目:
[Host Controller]
08:30:41,738 INFO [org.jboss.as.process.Host Controller.status] (reaper for Host Controller) WFLYPC0011: Process 'Host Controller' finished with an exit status of 137
08:30:41,740 INFO [org.jboss.as.process-controller.server] (reaper for Host Controller) WFLYPC0021: Waiting 1 seconds until trying to restart process Host Controller.
08:30:42,742 INFO [org.jboss.as.process.Host Controller.status] (reaper for Host Controller) WFLYPC0018: Starting process 'Host Controller'
[Host Controller] Java HotSpot(TM) 64-Bit Server VM warning: ignoring option MaxPermSize=256m; support was removed in 8.0
[Host Controller] 08:30:43,205 INFO [org.jboss.modules] (main) JBoss Modules version 1.4.2.Final
[Host Controller] 08:30:43,376 INFO [org.jboss.msc] (main) JBoss MSC version 1.2.4.Final
[Host Controller] 08:30:43,414 INFO [org.jboss.as] (MSC service thread 1-7) WFLYSRV0049: WildFly Full 9.0.0.Beta2 (WildFly Core 1.0.0.Beta2) starting
[Host Controller] 08:30:44,005 INFO [org.xnio] (MSC service thread 1-1) XNIO version 3.3.0.Final
[Host Controller] 08:30:44,007 INFO [org.jboss.as] (Controller Boot Thread) WFLYHC0003: Creating http management service using network interface (management) port (9990) securePort (-1)
[Host Controller] 08:30:44,014 INFO [org.xnio.nio] (MSC service thread 1-1) XNIO NIO Implementation Version 3.3.0.Final
[Host Controller] 08:30:44,146 INFO [org.jboss.remoting] (MSC service thread 1-1) JBoss Remoting version 4.0.8.Final
[Host Controller] 08:30:44,178 INFO [org.jboss.as.remoting] (MSC service thread 1-6) WFLYRMT0001: Listening on 127.0.0.1:9999
[Host Controller] 08:30:45,096 INFO [org.jboss.as.controller.management-deprecated] (Controller Boot Thread) WFLYCTL0028: Attribute enabled is deprecated, and it might be removed in future version!
[Host Controller] 08:30:45,151 INFO [org.jboss.as.controller.management-deprecated] (Controller Boot Thread) WFLYCTL0028: Attribute enabled is deprecated, and it might be removed in future version!
[Host Controller] 08:30:45,159 INFO [org.jboss.as.controller.management-deprecated] (Controller Boot Thread) WFLYCTL0028: Attribute default-stack is deprecated, and it might be removed in future version!
[Host Controller] 08:30:45,167 INFO [org.jboss.as.controller.management-deprecated] (Controller Boot Thread) WFLYCTL0028: Attribute enabled is deprecated, and it might be removed in future version!
[Host Controller] 08:30:45,181 INFO [org.jboss.as.controller.management-deprecated] (Controller Boot Thread) WFLYCTL0028: Attribute enabled is deprecated, and it might be removed in future version!
[Host Controller] 08:30:45,185 INFO [org.jboss.as.controller.management-deprecated] (Controller Boot Thread) WFLYCTL0028: Attribute default-stack is deprecated, and it might be removed in future version!
[Host Controller] 08:30:45,485 INFO [org.jboss.as.host.controller] (Controller Boot Thread) WFLYHC0018: Reconnecting server server-one
[Host Controller] 08:30:45,486 INFO [org.jboss.as.host.controller] (Controller Boot Thread) WFLYHC0020: Registering server server-one
[Host Controller] 08:30:45,492 INFO [org.jboss.as.host.controller] (Controller Boot Thread) WFLYHC0018: Reconnecting server server-two
[Host Controller] 08:30:45,492 INFO [org.jboss.as.host.controller] (Controller Boot Thread) WFLYHC0020: Registering server server-two
[Host Controller] 08:30:45,507 INFO [org.jboss.as] (Controller Boot Thread) WFLYSRV0060: Http management interface listening on http://127.0.0.1:9990/management
[Host Controller] 08:30:45,509 INFO [org.jboss.as] (Controller Boot Thread) WFLYSRV0051: Admin console listening on http://127.0.0.1:9990
[Host Controller] 08:30:45,509 INFO [org.jboss.as] (Controller Boot Thread) WFLYSRV0025: WildFly Full 9.0.0.Beta2 (WildFly Core 1.0.0.Beta2) (Host Controller) started in 2668ms - Started 50 of 52 services (15 services are lazy, passive or on-demand)
[Host Controller] 08:30:45,982 INFO [org.jboss.as.domain.controller.mgmt] (Remoting "master:MANAGEMENT" task-7) WFLYHC0021: Server [Server:server-two] connected using connection [Channel ID 3d3f3eed (inbound) of Remoting connection 067bd212 to /127.0.0.1:43342]
[Host Controller] 08:30:45,982 INFO [org.jboss.as.domain.controller.mgmt] (Remoting "master:MANAGEMENT" task-8) WFLYHC0021: Server [Server:server-one] connected using connection [Channel ID 1448766c (inbound) of Remoting connection 4261cbff to /127.0.0.1:42083]
因此,Process Controller注意到Host Controller崩溃,并启动了一个新的Host Controller。正在运行的 WildFly 实例成功重新连接到它们的Host Controller。
再次执行命令ps -efa | grep java,你将得到与之前类似的过程列表,但Host Controller进程将是新的,如下所示:
wildfly 1525 1429 1 08:26 ? 00:00:07 /home/wildfly/WFC/jdk8/bin/java -D[Process Controller] -server -Xms64m -Xmx512m -XX:MaxPermSize=256m -Djava.net.preferIPv4Stack=true -Djboss.modules.system.pkgs=org.jboss.byteman -Djava.awt.headless=true -Dorg.jboss.boot.log.file=/home/wildfly/WFC/wildfly/domain/log/process-controller.log -Dlogging.configuration=file:/home/wildfly/WFC/wildfly/domain/configuration/logging.properties -jar /home/wildfly/WFC/wildfly/jboss-modules.jar -mp /home/wildfly/WFC/wildfly/modules org.jboss.as.process-controller -jboss-home /home/wildfly/WFC/wildfly -jvm /home/wildfly/WFC/jdk8/bin/java -mp /home/wildfly/WFC/wildfly/modules -- -Dorg.jboss.boot.log.file=/home/wildfly/WFC/wildfly/domain/log/host-controller.log -Dlogging.configuration=file:/home/wildfly/WFC/wildfly/domain/configuration/logging.properties -server -Xms64m -Xmx512m -XX:MaxPermSize=256m -Djava.net.preferIPv4Stack=true -Djboss.modules.system.pkgs=org.jboss.byteman -Djava.awt.headless=true -- -default-jvm /home/wildfly/WFC/jdk8/bin/java
wildfly 1611 1525 6 08:26 ? 00:00:29 /home/wildfly/WFC/jdk8/bin/java -D[Server:server-one] -Xms64m -Xmx512m -server -Djava.awt.headless=true -Djava.net.preferIPv4Stack=true -Djboss.home.dir=/home/wildfly/WFC/wildfly -Djboss.modules.system.pkgs=org.jboss.byteman -Djboss.server.log.dir=/home/wildfly/WFC/wildfly/domain/servers/server-one/log -Djboss.server.temp.dir=/home/wildfly/WFC/wildfly/domain/servers/server-one/tmp -Djboss.server.data.dir=/home/wildfly/WFC/wildfly/domain/servers/server-one/data -Dlogging.configuration=file:/home/wildfly/WFC/wildfly/domain/servers/server-one/data/logging.properties -jar /home/wildfly/WFC/wildfly/jboss-modules.jar -mp /home/wildfly/WFC/wildfly/modules org.jboss.as.server
wildfly 1657 1525 6 08:26 ? 00:00:29 /home/wildfly/WFC/jdk8/bin/java -D[Server:server-two] -Xms64m -Xmx512m -server -Djava.awt.headless=true -Djava.net.preferIPv4Stack=true -Djboss.home.dir=/home/wildfly/WFC/wildfly -Djboss.modules.system.pkgs=org.jboss.byteman -Djboss.server.log.dir=/home/wildfly/WFC/wildfly/domain/servers/server-two/log -Djboss.server.temp.dir=/home/wildfly/WFC/wildfly/domain/servers/server-two/tmp -Djboss.server.data.dir=/home/wildfly/WFC/wildfly/domain/servers/server-two/data -Dlogging.configuration=file:/home/wildfly/WFC/wildfly/domain/servers/server-two/data/logging.properties -jar /home/wildfly/WFC/wildfly/jboss-modules.jar -mp /home/wildfly/WFC/wildfly/modules org.jboss.as.server
wildfly 2887 1525 7 08:30 ? 00:00:15 /home/wildfly/WFC/jdk8/bin/java -D[Host Controller] -Dorg.jboss.boot.log.file=/home/wildfly/WFC/wildfly/domain/log/host-controller.log -Dlogging.configuration=file:/home/wildfly/WFC/wildfly/domain/configuration/logging.properties -server -Xms64m -Xmx512m -XX:MaxPermSize=256m -Djava.net.preferIPv4Stack=true -Djboss.modules.system.pkgs=org.jboss.byteman -Djava.awt.headless=true -jar /home/wildfly/WFC/wildfly/jboss-modules.jar -mp /home/wildfly/WFC/wildfly/modules org.jboss.as.host-controller -mp /home/wildfly/WFC/wildfly/modules --pc-address 127.0.0.1 --pc-port 56310 -default-jvm /home/wildfly/WFC/jdk8/bin/java -Djboss.home.dir=/home/wildfly/WFC/wildfly --process-restarted
连接主机控制器到域控制器
在这个菜谱中,你将学习如何将主机控制器连接到域控制器。我们将把域控制器称为master,而将主机控制器称为slave。
准备就绪
本菜谱的目的是展示如何运行一个域控制器,以及一系列连接到它的其他主机控制器(我们将使用两个主机控制器)。无论域控制器是在同一台机器上还是不同的机器上,连接类型都意味着是“远程”。
对于菜谱,我们将模拟两台机器,为每个将要使用的服务器定义不同的配置文件夹。这对于开发环境来说也是一个好的实践,因为你可以测试一个伪生产场景。然而,由于我们将有一个Host Controller进程和一个Domain Controller进程同时运行,并且因为它们都负责远程管理,默认的管理接口设置为监听localhost:9990,所以我们必须为每个主机控制器分别绑定它们。
在第二章,以独立模式运行 WildFly中,我们看到了如何在同一台机器上运行多个 WildFly 实例,但具有不同的 IP 或端口号。这里我们也要做同样的事情。为了简洁,我们将看到如何在不同端口上绑定主机控制器。
最后,连接到域控制器的主机控制器需要进行身份验证。因此,我们需要通过运行add-user.sh脚本来创建一个新用户,提供以下截图中的信息:

此用户属于ManagementRealm,与第一章中使用的相同,欢迎来到 WildFly!创建wildfly用户以访问 Web 控制台。
记住最后一行,红色字符,我们稍后将在菜谱中使用。
如何做到这一点...
首先,我们需要创建Domain Controller和两个Host Controller配置文件夹。让我们创建我们的目录结构:
-
打开你的终端,并按照以下方式输入:
$ cd $WILDFLY_HOME $ cp -a domain master $ cp -a domain slave-1 $ cp -a domain slave-2 $ mv slave-1/configuration/domain.xml slave-1/configuration/domain.xml.unused $ mv slave-2/configuration/domain.xml slave-2/configuration/domain.xml.unused -
现在打开
master目录下的domain.xml文件,并将server-groups声明替换为以下 XML 代码片段:<server-groups> <server-group name="server-group-REST-app" profile="default"> <jvm name="default"> <heap size="64m" max-size="512m"/> </jvm> <socket-binding-group ref="standard-sockets"/> </server-group> <server-group name="server-group-SOAP-app" profile="default"> <jvm name="default"> <heap size="64m" max-size="512m"/> </jvm> <socket-binding-group ref="standard-sockets"/> </server-group> </server-groups> -
在这里,我们正在模拟一个专门针对 RESTful 技术的服务器组,以及一个专门针对 SOAP 技术的服务器组。这只是一个例子。服务器组可能与特定的应用程序或技术(因此有更多应用程序)相匹配;选择取决于你。我的建议是在做出此类决定时尽可能保持一致。
-
编辑
master目录下的host.xml文件,并删除整个servers声明。完成后,按照以下方式运行主服务器:$ ./bin/domain.sh -Djboss.domain.base.dir=master -
日志应该看起来如下:
08:44:53,589 INFO [org.jboss.modules] (main) JBoss Modules version 1.4.2.Final 08:44:53,730 INFO [org.jboss.as.process.Host Controller.status] (main) WFLYPC0018: Starting process 'Host Controller' [Host Controller] Java HotSpot(TM) 64-Bit Server VM warning: ignoring option MaxPermSize=256m; support was removed in 8.0 [Host Controller] 08:44:54,319 INFO [org.jboss.modules] (main) JBoss Modules version 1.4.2.Final [Host Controller] 08:44:54,503 INFO [org.jboss.msc] (main) JBoss MSC version 1.2.4.Final [Host Controller] 08:44:54,543 INFO [org.jboss.as] (MSC service thread 1-7) WFLYSRV0049: WildFly Full 9.0.0.Beta2 (WildFly Core 1.0.0.Beta2) starting [Host Controller] 08:44:55,145 INFO [org.xnio] (MSC service thread 1-5) XNIO version 3.3.0.Final [Host Controller] 08:44:55,147 INFO [org.jboss.as] (Controller Boot Thread) WFLYHC0003: Creating http management service using network interface (management) port (9990) securePort (-1) [Host Controller] 08:44:55,155 INFO [org.xnio.nio] (MSC service thread 1-5) XNIO NIO Implementation Version 3.3.0.Final [Host Controller] 08:44:55,262 INFO [org.jboss.remoting] (MSC service thread 1-5) JBoss Remoting version 4.0.8.Final [Host Controller] 08:44:55,301 INFO [org.jboss.as.remoting] (MSC service thread 1-1) WFLYRMT0001: Listening on 127.0.0.1:9999 [Host Controller] 08:44:56,312 INFO [org.jboss.as.controller.management-deprecated] (Controller Boot Thread) WFLYCTL0028: Attribute enabled is deprecated, and it might be removed in future version! [Host Controller] 08:44:56,381 INFO [org.jboss.as.controller.management-deprecated] (Controller Boot Thread) WFLYCTL0028: Attribute enabled is deprecated, and it might be removed in future version! [Host Controller] 08:44:56,387 INFO [org.jboss.as.controller.management-deprecated] (Controller Boot Thread) WFLYCTL0028: Attribute default-stack is deprecated, and it might be removed in future version! [Host Controller] 08:44:56,400 INFO [org.jboss.as.controller.management-deprecated] (Controller Boot Thread) WFLYCTL0028: Attribute enabled is deprecated, and it might be removed in future version! [Host Controller] 08:44:56,413 INFO [org.jboss.as.controller.management-deprecated] (Controller Boot Thread) WFLYCTL0028: Attribute enabled is deprecated, and it might be removed in future version! [Host Controller] 08:44:56,419 INFO [org.jboss.as.controller.management-deprecated] (Controller Boot Thread) WFLYCTL0028: Attribute default-stack is deprecated, and it might be removed in future version! [Host Controller] 08:44:56,710 INFO [org.jboss.as] (Controller Boot Thread) WFLYSRV0060: Http management interface listening on http://127.0.0.1:9990/management [Host Controller] 08:44:56,710 INFO [org.jboss.as] (Controller Boot Thread) WFLYSRV0051: Admin console listening on http://127.0.0.1:9990 [Host Controller] 08:44:56,711 INFO [org.jboss.as] (Controller Boot Thread) WFLYSRV0025: WildFly Full 9.0.0.Beta2 (WildFly Core 1.0.0.Beta2) (Host Controller) started in 2825ms - Started 50 of 52 services (15 services are lazy, passive or on-demand)完全没有运行的服务器。
现在是时候配置我们的第一个从节点,
slave-1。 -
编辑
slave-1/configuration/host.xml文件并执行以下操作:-
将主机命名为
slave-1:<host name="slave-1" > -
在
ManagementRealm定义中添加hostcontroller用户的秘密值:<server-identities> <secret value="aG9zdGNvbnRyb2xsZXIuMjAxNQ=="/> </server-identities> -
更改管理接口的端口号:
<management-interfaces> <native-interface security-realm="ManagementRealm"> <socket interface="management" port="${jboss.management.native.port:19999}"/> </native-interface> <http-interface security-realm="ManagementRealm" http-upgrade-enabled="true"> <socket interface="management" port="${jboss.management.http.port:19990}"/> </http-interface> </management-interfaces> -
使用远程
domain-controller:<domain-controller> <remote host="${jboss.domain.master.address}" port="${jboss.domain.master.port:9999}" security-realm="ManagementRealm" username="hostcontroller"/> </domain-controller> -
定义主机:
<servers> <server name="REST-server-one" group="server-group-REST-app"> <jvm name="default"> <heap size="64m" max-size="256m"/> </jvm> </server> </servers>
-
-
你最终应该得到一个看起来像以下 XML 代码片段的文件:
<?xml version='1.0' encoding='UTF-8'?> <host name="slave-1" > <extensions> <extension module="org.jboss.as.jmx"/> </extensions> <management> <security-realms> <security-realm name="ManagementRealm"> <server-identities> <secret value="aG9zdGNvbnRyb2xsZXIuMjAxNQ=="/> </server-identities> <authentication> <local default-user="$local" skip-group- loading="true"/> <properties path="mgmt-users.properties" relative- to="jboss.domain.config.dir"/> </authentication> <authorization map-groups-to-roles="false"> <properties path="mgmt-groups.properties" relative-to="jboss.domain.config.dir"/> </authorization> </security-realm> <security-realm name="ApplicationRealm"> <authentication> <local default-user="$local" allowed-users="*" skip-group-loading="true"/> <properties path="application-users.properties" relative-to="jboss.domain.config.dir"/> </authentication> <authorization> <properties path="application-roles.properties" relative-to="jboss.domain.config.dir"/> </authorization> </security-realm> </security-realms> <audit-log> <formatters> <json-formatter name="json-formatter"/> </formatters> <handlers> <file-handler name="host-file" formatter="json-formatter" relative-to="jboss.domain.data.dir" path="audit-log.log"/> <file-handler name="server-file" formatter="json-formatter" relative-to="jboss.server.data.dir" path="audit-log.log"/> </handlers> <logger log-boot="true" log-read-only="false" enabled="false"> <handlers> <handler name="host-file"/> </handlers> </logger> <server-logger log-boot="true" log-read-only="false" enabled="false"> <handlers> <handler name="server-file"/> </handlers> </server-logger> </audit-log> <management-interfaces> <native-interface security-realm="ManagementRealm"> <socket interface="management" port="${jboss.management.native.port:19999}"/> </native-interface> <http-interface security-realm="ManagementRealm" http-upgrade-enabled="true"> <socket interface="management" port="${jboss.management.http.port:19990}"/> </http-interface> </management-interfaces> </management> <domain-controller> <remote protocol="remote" host="${jboss.domain.master.address}" port="${jboss.domain.master.port:9999}" security-realm="ManagementRealm" username="hostcontroller" /> </domain-controller> <interfaces> <interface name="management"> <inet-address value="${jboss.bind.address.management:127.0.0.1}"/> </interface> <interface name="public"> <inet-address value="${jboss.bind.address:127.0.0.1}"/> </interface> <interface name="unsecure"> <!-- Used for IIOP sockets in the standard configuration. To secure JacORB you need to setup SSL --> <inet-address value="${jboss.bind.address.unsecure:127.0.0.1}"/> </interface> </interfaces> <jvms> <jvm name="default"> <heap size="64m" max-size="256m"/> <permgen size="256m" max-size="256m"/> <jvm-options> <option value="-server"/> </jvm-options> </jvm> </jvms> <servers> <server name="REST-server-one" group="server-group-REST-app"> <jvm name="default"> <heap size="64m" max-size="256m"/> </jvm> </server> </servers> <profile> <subsystem > <expose-resolved-model/> <expose-expression-model/> <remoting-connector/> </subsystem> </profile> </host> -
现在,我们已经准备好运行我们的
slave-1主机:$ ./bin/domain.sh -Djboss.domain.base.dir=slave-1 -Djboss.domain.master.address=127.0.0.1 -
如果你检查主节点的日志输出,你会注意到
slave-1已将自己注册到Domain Controller:[Host Controller] 23:45:58,523 INFO [org.jboss.as.domain] (Host Controller Service Threads - 32) WFLYHC0019: Registered remote slave host "slave-1", JBoss WildFly Full 9.0.0.Beta2 (WildFly 1.0.0.Beta2) -
对第二个从节点重复相同的更改。将主机名设置为
slave-2并添加以下服务器定义:<servers> <server name="SOAP-server-one" group="server-group-SOAP-app"> <socket-bindings port-offset="150"/> </server> <server name="SOAP-server-two" group="server-group-SOAP-app"> <socket-bindings port-offset="250"/> </server> </servers> -
此外,设置
jboss.management.native.port和jboss.management.http.port属性的默认值分别为29999和29990。你应该得到一个看起来像以下 XML 代码片段的文件:<?xml version='1.0' encoding='UTF-8'?> <host name="slave-2" > <extensions> <extension module="org.jboss.as.jmx"/> </extensions> <management> <security-realms> <security-realm name="ManagementRealm"> <server-identities> <secret value="aG9zdGNvbnRyb2xsZXIuMjAxNQ=="/> </server-identities> <authentication> <local default-user="$local" skip-group-loading="true"/> <properties path="mgmt-users.properties" relative-to="jboss.domain.config.dir"/> </authentication> <authorization map-groups-to-roles="false"> <properties path="mgmt-groups.properties" relative-to="jboss.domain.config.dir"/> </authorization> </security-realm> <security-realm name="ApplicationRealm"> <authentication> <local default-user="$local" allowed-users="*" skip-group-loading="true"/> <properties path="application-users.properties" relative-to="jboss.domain.config.dir"/> </authentication> <authorization> <properties path="application-roles.properties" relative-to="jboss.domain.config.dir"/> </authorization> </security-realm> </security-realms> <audit-log> <formatters> <json-formatter name="json-formatter"/> </formatters> <handlers> <file-handler name="host-file" formatter="json-formatter" relative-to="jboss.domain.data.dir" path="audit-log.log"/> <file-handler name="server-file" formatter="json- formatter" relative-to="jboss.server.data.dir" path="audit-log.log"/> </handlers> <logger log-boot="true" log-read-only="false" enabled="false"> <handlers> <handler name="host-file"/> </handlers> </logger> <server-logger log-boot="true" log-read-only="false" enabled="false"> <handlers> <handler name="server-file"/> </handlers> </server-logger> </audit-log> <management-interfaces> <native-interface security-realm="ManagementRealm"> <socket interface="management" port="${jboss.management.native.port:29999}"/> </native-interface> <http-interface security-realm="ManagementRealm" http-upgrade-enabled="true"> <socket interface="management" port="${jboss.management.http.port:29990}"/> </http-interface> </management-interfaces> </management> <domain-controller> <remote protocol="remote" host="${jboss.domain.master.address}" port="${jboss.domain.master.port:9999}" security-realm="ManagementRealm" username="hostcontroller" /> </domain-controller> <interfaces> <interface name="management"> <inet-address value="${jboss.bind.address.management:127.0.0.1}"/> </interface> <interface name="public"> <inet-address value="${jboss.bind.address:127.0.0.1}"/> </interface> <interface name="unsecure"> <!-- Used for IIOP sockets in the standard configuration. To secure JacORB you need to setup SSL --> <inet-address value="${jboss.bind.address.unsecure:127.0.0.1}"/> </interface> </interfaces> <jvms> <jvm name="default"> <heap size="64m" max-size="256m"/> <permgen size="256m" max-size="256m"/> <jvm-options> <option value="-server"/> </jvm-options> </jvm> </jvms> <servers> <server name="SOAP-server-one" group="server-group-SOAP-app"> <socket-bindings port-offset="150"/> </server> <server name="SOAP-server-two" group="server-group-SOAP-app"> <socket-bindings port-offset="250"/> </server> </servers> <profile> <subsystem > <expose-resolved-model/> <expose-expression-model/> <remoting-connector/> </subsystem> </profile> </host> -
现在我们已经准备好运行我们的
slave-2主机:$ ./bin/domain.sh -Djboss.domain.base.dir=slave-2 -Djboss.domain.master.address=127.0.0.1 -
如果你检查
master的日志输出,你会注意到slave-2已将自己注册到Domain Controller:[Host Controller] 23:51:34,134 INFO [org.jboss.as.domain] (Host Controller Service Threads - 34) WFLYHC0019: Registered remote slave host "slave-2", JBoss WildFly Full 9.0.0.Beta2 (WildFly 1.0.0.Beta2)
它是如何工作的...
让我们先谈谈主节点,然后再谈谈两个从节点。
主节点
在主节点中,你定义运行你的 WildFly 实例所需的全部配置。根据你的需求选择一个配置文件,设置 IP、端口、服务器和组。所有内容都持久化到domain.xml文件或你选择的任何文件中。
当你启动domain.sh脚本时,默认情况下,进程会查找domain.xml文件和host.xml文件来读取配置,从jboss.home.dir(无论$WILDFLY_HOME是什么)开始,附加domain和configuration,除非你指定了不同的路径。
我们通过添加jboss.domain.base.dir属性并设置为master来指定不同的路径。因此,WildFly 从jboss.home.dir开始附加master和configuration。根据独立模式,域模式下的 WildFly 允许你在必要时覆盖不同的路径。
以下表格总结了所有属性:
| 属性名称 | 用途 | 默认值 |
|---|---|---|
jboss.home.dir |
WildFly 安装的根目录。 | 由domain.sh设置为$JBOSS_HOME |
jboss.domain.base.dir |
域内容的基目录。 | jboss.home.dir/domain |
jboss.domain.config.dir |
基配置目录。 | jboss.domain.base.dir/configuration |
jboss.domain.data.dir |
用于持久化数据文件存储的目录。 | jboss.domain.base.dir/data |
jboss.domain.log.dir |
包含host-controller.log和process-controller.log文件的目录 |
jboss.domain.base.dir/log |
jboss.domain.temp.dir |
用于临时文件存储的目录。 | jboss.domain.base.dir/tmp |
jboss.domain.deployment.dir |
用于存储已部署内容的目录。 | jboss.domain.data.dir/content |
jboss.domain.servers.dir |
包含托管服务器实例输出的目录。 | jboss.domain.data.dir/servers |
小贴士
主服务器应该单独运行,没有任何服务器。
奴隶
如前一段所述,当你以域模式启动 WildFly 时,默认情况下它会查找domain.xml和host.xml文件。由于我们正在运行奴隶,我们不希望加载domain.xml,因此我们将其移开(实际上我们将其重命名为domain.xml.unused)。因此,启动过程只选择host.xml文件,其中我们定义了我们的服务器以及它们所属的服务器组。
因此,基本上,在host.xml文件中,我们只定义了我们的服务器以及它们应该连接到的域控制器。
此外,在主机slave-2上,我们需要指定一个port-offset,因为我们已经在本地机器上运行了一个实例。SOAP-server-one和SOAP-server-two将增加它们的端口绑定到 150 和 250,因此第一个 http-connector 的端口号为8230,第二个 http-connector 的端口号为8330。同时,我们也必须更改管理接口的端口号。
最后,我们必须通过传递-Djboss.domain.master.address=127.0.0.1参数来指定奴隶将连接到的域控制器。
从管理员控制台看到的域概览如下所示:

管理控制台—域概览
通过 Web 控制台停止和启动服务器组
在这个食谱中,我们将学习如何使用 Web 控制台停止和启动整个服务器组。由于一个服务器可以属于多个服务器组,你可能想要一次性停止或启动所有服务器,因此停止或启动服务器组。
准备工作
为了能够跟随这个食谱,我们需要完成前面的一个。因此,在我们开始之前,我们需要启动主服务器和奴隶服务器slave-1和slave-2。在三个不同的终端中,导航到$WILDFLY_HOME,并运行以下命令:
-
$ ./bin/domain.sh -Djboss.domain.base.dir=master。 -
./bin/domain.sh -Djboss.domain.base.dir=slave-1 -Djboss.domain.master.address=127.0.0.1。 -
./bin/domain.sh -Djboss.domain.base.dir=slave-2 -Djboss.domain.master.address=127.0.0.1。
现在一切都已经启动并运行,让我们继续前进。
如何操作…
-
打开浏览器并将它指向管理员控制台:
http://localhost:9990。 -
输入用户名和密码(分别是
wildfly和cookbook.2015)并转到域选项卡。你应该会看到一个类似于以下截图的页面:![如何操作…]()
管理控制台—域概览
-
现在,将鼠标悬停在组框上时,命令链接将显示如下:
![如何操作…]()
服务器组:启动、停止和重启
-
此外,将鼠标悬停在服务器框上时,命令链接将显示如下:
![如何操作…]()
启动服务器组
-
现在尝试点击停止组命令链接并确认操作!
-
嘿嘿…很简单吧!页面更新了服务器实例图标,并在消息板上显示操作成功:
![如何操作…]()
停止服务器组
-
在日志中,您应该看到类似以下条目:
[Host Controller] 09:05:52,656 INFO [org.jboss.as.host.controller] (Host Controller Service Threads - 9) WFLYHC0024: Stopping server REST-server-one [Server:REST-server-one] 09:05:52,692 INFO [org.jboss.as.server] (ServerService Thread Pool -- 59) WFLYSRV0211: Suspending server 09:05:52,706 INFO [org.jboss.as.process.Server:REST-server-one.status] (ProcessController-threads - 4) WFLYPC0019: Stopping process 'Server:REST-server-one' [Server:REST-server-one] 09:05:52,714 INFO [org.jboss.as.server] (Thread-2) WFLYSRV0220: Server shutdown has been requested. [Server:REST-server-one] 09:05:52,732 INFO [org.jboss.as.connector.subsystems.datasources] (MSC service thread 1-13) WFLYJCA0010: Unbound data source [java:jboss/datasources/ExampleDS] [Server:REST-server-one] 09:05:52,735 INFO [org.jboss.as.connector.deployers.jdbc] (MSC service thread 1-9) WFLYJCA0019: Stopped Driver service with driver-name = h2 [Server:REST-server-one] 09:05:52,736 INFO [org.wildfly.extension.undertow] (MSC service thread 1-16) WFLYUT0019: Host default-host stopping [Server:REST-server-one] 09:05:52,745 INFO [org.wildfly.extension.undertow] (MSC service thread 1-3) WFLYUT0008: Undertow HTTP listener default suspending [Server:REST-server-one] 09:05:52,750 INFO [org.wildfly.extension.undertow] (MSC service thread 1-3) WFLYUT0007: Undertow HTTP listener default stopped, was bound to /127.0.0.1:8080 [Server:REST-server-one] 09:05:52,752 INFO [org.wildfly.extension.undertow] (MSC service thread 1-3) WFLYUT0004: Undertow 1.2.0.Beta10 stopping [Host Controller] 09:05:52,783 INFO [org.jboss.as.host.controller] (Remoting "slave-1:MANAGEMENT" task-11) WFLYHC0027: Unregistering server REST-server-one [Server:REST-server-one] 09:05:52,853 INFO [org.jboss.as] (MSC service thread 1-4) WFLYSRV0050: WildFly Full 9.0.0.Beta2 (WildFly Core 1.0.0.Beta2) stopped in 112ms [Server:REST-server-one] 09:05:52,902 INFO [org.jboss.as.process.Server:REST-server-one.status] (reaper for Server:REST-server-one) WFLYPC0011: Process 'Server:REST-server-one' finished with an exit status of 0 [Host Controller] 09:05:52,941 INFO [org.jboss.as.host.controller] (ProcessControllerConnection-thread - 2) WFLYHC0027: Unregistering server REST-server-one -
让我们重新启动服务器组,通过点击启动组命令链接,操作如下:
![如何操作…]()
-
重新启动后,WildFly 通过提供消息反馈和更新属于服务器组的关联状态图标来确认操作成功,如下所示:
![如何操作…]()
启动服务器组
-
slave-1的日志条目如下:[Host Controller] 09:08:46,327 INFO [org.jboss.as.host.controller] (Host Controller Service Threads - 13) WFLYHC0023: Starting server REST-server-one 09:08:46,404 INFO [org.jboss.as.process.Server:REST-server-one.status] (ProcessController-threads - 5) WFLYPC0018: Starting process 'Server:REST-server-one' [Server:REST-server-one] 09:08:46,859 INFO [org.jboss.modules] (main) JBoss Modules version 1.4.2.Final [Server:REST-server-one] 09:08:47,135 INFO [org.jboss.msc] (main) JBoss MSC version 1.2.4.Final [Server:REST-server-one] 09:08:47,243 INFO [org.jboss.as] (MSC service thread 1-8) WFLYSRV0049: WildFly Full 9.0.0.Beta2 (WildFly Core 1.0.0.Beta2) starting [Server:REST-server-one] 09:08:47,347 INFO [org.xnio] (MSC service thread 1-4) XNIO version 3.3.0.Final [Server:REST-server-one] 09:08:47,356 INFO [org.xnio.nio] (MSC service thread 1-4) XNIO NIO Implementation Version 3.3.0.Final [Server:REST-server-one] 09:08:47,383 INFO [org.jboss.remoting] (MSC service thread 1-4) JBoss Remoting version 4.0.8.Final [Host Controller] 09:08:48,065 INFO [org.jboss.as.domain.controller.mgmt] (Remoting "slave-1:MANAGEMENT" task-13) WFLYHC0021: Server [Server:REST-server-one] connected using connection [Channel ID 312ade53 (inbound) of Remoting connection 277898d9 to /127.0.0.1:34603] [Host Controller] 09:08:48,103 INFO [org.jboss.as.host.controller] (server-registration-threads - 1) WFLYHC0020: Registering server REST-server-one [Server:REST-server-one] 09:08:48,749 INFO [org.jboss.as.controller.management-deprecated] (ServerService Thread Pool -- 28) WFLYCTL0028: Attribute enabled is deprecated, and it might be removed in future version! [Server:REST-server-one] 09:08:48,845 WARN [org.jboss.as.txn] (ServerService Thread Pool -- 33) WFLYTX0013: Node identifier property is set to the default value. Please make sure it is unique. [Server:REST-server-one] 09:08:48,845 INFO [org.jboss.as.security] (ServerService Thread Pool -- 35) WFLYSEC0002: Activating Security Subsystem [Server:REST-server-one] 09:08:48,846 INFO [org.jboss.as.webservices] (ServerService Thread Pool -- 19) WFLYWS0002: Activating WebServices Extension [Server:REST-server-one] 09:08:48,850 INFO [org.jboss.as.security] (MSC service thread 1-15) WFLYSEC0001: Current PicketBox version=4.9.0.Beta2 [Server:REST-server-one] 09:08:49,427 INFO [org.jboss.as.clustering.infinispan] (ServerService Thread Pool -- 49) WFLYCLINF0001: Activating Infinispan subsystem. [Server:REST-server-one] 09:08:49,434 INFO [org.jboss.as.naming] (ServerService Thread Pool -- 41) WFLYNAM0001: Activating Naming Subsystem [Server:REST-server-one] 09:08:49,443 INFO [org.jboss.as.connector] (MSC service thread 1-13) WFLYJCA0009: Starting JCA Subsystem (IronJacamar 1.2.3.Final) [Server:REST-server-one] 09:08:49,464 INFO [org.jboss.as.jsf] (ServerService Thread Pool -- 43) WFLYJSF0007: Activated the following JSF Implementations: [main] [Server:REST-server-one] 09:08:49,498 INFO [org.wildfly.extension.io] (ServerService Thread Pool -- 50) WFLYIO001: Worker 'default' has auto-configured to 16 core threads with 128 task threads based on your 8 available processors [Server:REST-server-one] 09:08:49,537 INFO [org.wildfly.extension.undertow] (ServerService Thread Pool -- 32) WFLYUT0003: Undertow 1.2.0.Beta10 starting [Server:REST-server-one] 09:08:49,537 INFO [org.wildfly.extension.undertow] (MSC service thread 1-14) WFLYUT0003: Undertow 1.2.0.Beta10 starting [Server:REST-server-one] 09:08:49,550 INFO [org.jboss.as.connector.subsystems.datasources] (ServerService Thread Pool -- 53) WFLYJCA0004: Deploying JDBC-compliant driver class org.h2.Driver (version 1.3) [Server:REST-server-one] 09:08:49,566 INFO [org.jboss.as.connector.deployers.jdbc] (MSC service thread 1-8) WFLYJCA0018: Started Driver service with driver-name = h2 [Server:REST-server-one] 09:08:49,686 INFO [org.jboss.as.naming] (MSC service thread 1-15) WFLYNAM0003: Starting Naming Service [Server:REST-server-one] 09:08:49,698 INFO [org.jboss.as.mail.extension] (MSC service thread 1-7) WFLYMAIL0001: Bound mail session [java:jboss/mail/Default] [Server:REST-server-one] 09:08:49,777 INFO [org.wildfly.extension.undertow] (ServerService Thread Pool -- 32) WFLYUT0014: Creating file handler for path /home/wildfly/WFC/wildfly/welcome-content [Server:REST-server-one] 09:08:49,916 INFO [org.wildfly.extension.undertow] (MSC service thread 1-3) WFLYUT0012: Started server default-server. [Server:REST-server-one] 09:08:49,933 INFO [org.wildfly.extension.undertow] (MSC service thread 1-3) WFLYUT0018: Host default-host starting [Server:REST-server-one] 09:08:50,029 INFO [org.wildfly.extension.undertow] (MSC service thread 1-4) WFLYUT0006: Undertow HTTP listener default listening on /127.0.0.1:8080 [Server:REST-server-one] 09:08:50,170 INFO [org.jboss.as.connector.subsystems.datasources] (MSC service thread 1-16) WFLYJCA0001: Bound data source [java:jboss/datasources/ExampleDS] [Server:REST-server-one] 09:08:50,440 INFO [org.jboss.ws.common.management] (MSC service thread 1-10) JBWS022052: Starting JBoss Web Services - Stack CXF Server 5.0.0.Beta3 [Server:REST-server-one] 09:08:50,733 INFO [org.jboss.as] (Controller Boot Thread) WFLYSRV0025: WildFly Full 9.0.0.Beta2 (WildFly Core 1.0.0.Beta2) started in 4228ms - Started 193 of 370 services (207 services are lazy, passive or on-demand)
更多内容...
在停止和启动单个服务器实例时,相同的操作也适用。
在host.xml文件中,您可以定义auto-start属性,其默认值为true。将其设置为false将不会启动服务器,您将需要通过管理控制台或 CLI 手动启动它。
<server name="REST-server-one" group="server-group-REST-app" auto-start="true">
<jvm name="default">
<heap size="64m" max-size="256m"/>
</jvm>
</server>
关于这一点,让我们尝试 CLI,它更有趣!
通过 CLI 停止和启动您的服务器组
在本教程中,我们将学习如何使用 CLI 停止和启动整个服务器组。由于一个服务器可以属于多个服务器组,您可能希望一次性停止或启动所有服务器,从而停止或启动服务器组。
准备中
对于这个教程,域控制器和主机控制器都应该处于运行状态。对于它们的配置,请参考本章中的教程将主机控制器连接到域控制器。
如何操作…
-
打开您的终端并按照以下步骤操作:
![如何操作…]()
-
使用
ls命令,您可以列出您遍历的整个上下文。我们想要做的是“停止”名为server-group-REST-app的整个服务器组:[domain@localhost:9990 /] /server-group=server-group-REST-app:stop-servers() { "outcome" => "success", "result" => undefined, "server-groups" => undefined } [domain@localhost:9990 /] -
现在,让我们再次启动服务器组,通过调用
start-servers方法:[domain@localhost:9990 /] /server-group=server-group-REST-app:start-servers() { "outcome" => "success", "result" => undefined, "server-groups" => undefined } [domain@localhost:9990 /]
它是如何工作的…
在 CLI 中,您基本上重新映射了整个配置,因此,如果您查看domain.xml或host.xml文件,您会发现几乎相同的层次结构。
在我们的教程中,我们想要停止一个服务器组,所以如果您查看domain.xml,您可以确定上下文和命令——您可以通过在:或/旁边的Tab键查看可用命令。
通过 CLI 停止和启动您的服务器
在本教程中,我们将学习如何使用 CLI 停止和启动属于服务器组的一个单独服务器。因为属于服务器组的服务器可能分布在多个不同的机器上,拥有不同的资源,您可能需要停止单个服务器,而不停止整个服务器组来添加更多资源并重新启动它。幸运的是,WildFly 提供了这样的细粒度来停止服务器。
准备中
对于这个菜谱,域控制器和主机控制器都应该处于开启和运行状态。对于它们的配置,请参考本章中的菜谱将主机控制器连接到域控制器。
如何做…
-
打开你的终端并按照以下步骤操作:
$ ./bin/jboss-cli.sh --connect [domain@localhost:9990 /] -
我们想要做的是“停止”名为
REST-server-one的服务器,它属于名为slave-1的主机控制器。这是通过以下方式完成的:[domain@localhost:9990 /] /host=slave-1/server-config=REST-server-one:stop() { "outcome" => "success", "result" => "STOPPING" } [domain@localhost:9990 /]如你所见,服务器处于
STOPPING状态;这意味着它可能需要一段时间,具体取决于你绑定了多少资源以及/或者部署了多少。 -
要检查是否完成,尝试以下命令:
[domain@localhost:9990 /] /host=slave-1/server-config=REST-server-one:read-resource(include-runtime=true) { "outcome" => "success", "result" => { "auto-start" => true, "cpu-affinity" => undefined, "group" => "server-group-REST-app", "name" => "REST-server-one", "priority" => undefined, "socket-binding-group" => undefined, "socket-binding-port-offset" => 0, "status" => "STOPPED", "interface" => undefined, "jvm" => {"default" => undefined}, "path" => undefined, "system-property" => undefined } } [domain@localhost:9990 /] -
现在,让我们通过调用
start方法再次启动服务器:[domain@localhost:9990 /] /host=slave-1/server-config=REST-server-one:start() { "outcome" => "success", "result" => "STARTING" } [domain@localhost:9990 /]
它是如何工作的…
stop命令本身很简单,不需要太多解释。值得提到的是,要能够停止服务器,你首先需要知道它属于哪个主机/从机。你可以这样做,通过导航 WildFly 的结构,如下所示:

一旦你找到了你想要/需要停止的服务器,你可以发出stop命令。
更多内容…
在处理stop和start命令时,在server-group和server上,你可以添加blocking=true选项,这基本上会使命令挂起,直到完成。命令如下所示:
[domain@localhost:9990 /] /host=slave-1/server-config=REST-server-one:start(blocking=true)
这样我们就能知道服务器何时完成操作。想象一下你正在按顺序执行多个 CLI 命令,而某个命令需要先前的操作完成。如果没有启用阻塞标志,该命令将无法工作。
通过 Web 控制台将应用程序部署和取消部署到特定的服务器组
在这个菜谱中,我们将学习如何通过 Web 控制台部署和取消部署应用程序,并将其分配给服务器组。
准备工作
对于这个菜谱,域控制器和主机控制器都应该处于开启和运行状态。对于它们的配置,请参考本章中的菜谱将主机控制器连接到域控制器。
在这个和接下来的菜谱中,我们需要一个应用程序来测试我们的配置。对于这个菜谱,我们需要名为example的应用程序,你可以在我的 GitHub 仓库中找到它。如果你跳过了第二章中的"使用部署文件夹管理应用程序菜谱",以独立模式运行 WildFly,请参考它以下载你将需要的所有源代码和项目。
要构建应用程序,请输入以下内容:
$ cd $WILDFLY_HOME/github/wildfly-cookbook
$ cd example
$ mvn -e clean package
如何做…
-
打开你的浏览器并将其指向管理控制台:
http://localhost:9990。 -
输入用户名和密码(分别是
wildfly和cookbook.2015)并转到部署。你应该会看到一个类似于以下截图的页面:![如何做…]()
管理部署窗口
-
现在要部署应用程序,我们首先需要通过点击添加按钮并选择您的软件包来添加它,如图所示:
![如何操作…]()
部署 example.war 应用程序
-
现在只需按照向导说明操作。您应该最终将软件包部署,但未分配到服务器组,如图所示:
![如何操作…]()
应用程序已部署但未分配到任何服务器组
在域控制器的日志中,您应该看到如下所示的日志:
[Host Controller] 17:02:19,737 INFO [org.jboss.as.repository] (XNIO-1 task-1) WFLYDR0001: Content added at location /home/luigi/WFC/wildfly/master/data/content/21/7dd6250d5bc4afcabdffb0b25c99db92239b5a/content -
但您在主机控制器日志侧没有看到任何日志。这是因为我们还没有将应用程序分配到任何服务器组。要将应用程序分配到服务器组,只需点击分配按钮并选择服务器组,如图所示:
![如何操作…]()
将部署分配到服务器组
-
保存它,域控制器应将部署传播到所选服务器组所属的服务器,如图所示:
![如何操作…]()
部署分配到服务器组
您可以通过在主机控制器日志中查找类似以下条目来检查它:
[Server:REST-server-one] 17:09:13,813 INFO [org.jboss.as.server.deployment] (MSC service thread 1-2) WFLYSRV0027: Starting deployment of "example.war" (runtime-name: "example.war") [Server:REST-server-one] 17:09:14,406 INFO [org.wildfly.extension.undertow] (MSC service thread 1-1) WFLYUT0021: Registered web context: /example [Server:REST-server-one] 17:09:14,478 INFO [org.jboss.as.server] (ServerService Thread Pool -- 59) WFLYSRV0010: Deployed "example.war" (runtime-name : "example.war")在域控制器侧将不再找到更多条目。
-
如果您需要检查哪些部署属于哪个服务器组,请转到部署选项卡,选择服务器组选项卡,然后点击相应的查看命令链接,如图所示:
![如何操作…]()
选择服务器组
-
您应该看到所有成功的部署,如图所示:
![如何操作…]()
特定服务器组的部署
-
好的,现在是我们卸载应用程序的时候了。正如 Web 控制台所建议的,从部署概览中,您可以通过选择软件包并点击删除按钮来删除它(在 GUI 术语中是卸载),如图所示:
![如何操作…]()
卸载软件包
-
按照以下方式确认您的操作:
![如何操作…]()
软件包已卸载
现在查看日志,您应该找到以下条目(首先是
域控制器日志,然后是主机控制器日志):[Server:REST-server-one] 22:17:05,544 INFO [org.wildfly.extension.undertow] (MSC service thread 1-2) WFLYUT0022: Unregistered web context: /example [Server:REST-server-one] 22:17:05,573 INFO [org.hibernate.validator.internal.util.Version] (MSC service thread 1-4) HV000001: Hibernate Validator 5.1.3.Final [Server:REST-server-one] 22:17:05,612 INFO [org.jboss.as.server.deployment] (MSC service thread 1-4) WFLYSRV0028: Stopped deployment example.war (runtime-name: example.war) in 72ms [Server:REST-server-one] 22:17:05,659 INFO [org.jboss.as.server] (ServerService Thread Pool -- 70) WFLYSRV0009: Undeployed "example.war" (runtime-name: "example.war") [Server:REST-server-one] 22:17:05,663 INFO [org.jboss.as.repository] (ServerService Thread Pool -- 70) WFLYDR0002: Content removed from location /home/luigi/WFC/wildfl/domain/servers/REST-server-one/data/content/8d/b8b9b1cda547c7ec3fb493dbd5940cdb378ec0/content [Host Controller] 22:17:05,666 INFO [org.jboss.as.repository] (XNIO-1 task-8) WFLYDR0002: Content removed from location /home/luigi/WFC/wildfly/domain/data/content/8d/b8b9b1cda547c7ec3fb493dbd5940cdb378ec0/content
现在让我们在下一个食谱中使用 CLI 回顾所有这些内容以及更多内容。
通过 CLI 将应用程序部署和卸载到特定服务器组
在本食谱中,我们将学习如何使用 CLI 部署和卸载应用程序,并将其分配到特定的服务器组。本食谱的操作几乎与上一个食谱中执行的操作相同,但特定于 CLI。
准备工作
对于本食谱,域控制器和主机控制器都应该处于运行状态。有关它们的配置,请参阅本章中的食谱将主机控制器连接到域控制器。
在本食谱和接下来的食谱中,我们需要一个应用程序来测试我们的配置。对于这个食谱,我们需要名为example的应用程序,您可以在我的 GitHub 仓库中找到它。如果您跳过了第二章中关于“使用部署文件夹管理应用程序”的食谱,请参考它以下载您将需要的所有源代码和项目。
要构建应用程序,请按以下方式输入:
$ cd $WILDFLY_HOME/github/wildfly-cookbook
$ cd example
$ mvn -e clean package
一旦构建了应用程序,将其复制到$WILDFLY_HOME文件夹以方便使用。
如何做...
-
打开您的终端并像往常一样连接到数据中心:
$ ./bin/jboss-cli.sh --connect -
现在只需按Tab键即可查看可用的命令:
![如何做…]()
-
有一个可用的
deploy命令,这正是我们所需要的。让我们将我们的工件部署到两个服务器组,server-group-REST-app和server-group-SOAP-app。[domain@localhost:9990 /] deploy example.war --server-groups=server-group-SOAP-app,server-group-REST-app [domain@localhost:9990 /]注意
请记住,应用程序的路径相对于用于访问 CLI 的路径是相对的。所以,如果您在您的用户主目录中,
deploy命令要指定的路径应该是WFC/github/wildfly-cookbook/example/target/example.war。 -
让我们单独检查每个服务器组发生了什么:
[domain@localhost:9990 /] deployment-info --server-group=server-group-REST-app NAME RUNTIME-NAME STATE example.war example.war enabled [domain@localhost:9990 /] deployment-info --server-group=server-group-SOAP-app NAME RUNTIME-NAME STATE example.war example.war enabled [domain@localhost:9990 /]-
如果您不相信 CLI,检查各种日志中发生了什么:
主日志如下:
[Host Controller] 22:56:08,289 INFO [org.jboss.as.repository] (management-handler-thread - 2) WFLYDR0001: Content added at location /home/luigi/WFC/wildfly/master/data/content/21/7dd6250d5bc4afcabdffb0b25c99db92239b5a/content从属-1 日志如下:
[Server:REST-server-one] 22:56:08,557 INFO [org.jboss.as.server.deployment] (MSC service thread 1-7) WFLYSRV0027: Starting deployment of "example.war" (runtime-name: "example.war") [Server:REST-server-one] 22:56:08,779 INFO [org.wildfly.extension.undertow] (MSC service thread 1-2) WFLYUT0021: Registered web context: /example [Server:REST-server-one] 22:56:10,121 INFO [org.jboss.as.server] (ServerService Thread Pool -- 65) WFLYSRV0010: Deployed "example.war" (runtime-name : "example.war")从属-2 日志如下:
[Server:SOAP-server-one] 22:56:08,725 INFO [org.jboss.as.server.deployment] (MSC service thread 1-6) WFLYSRV0027: Starting deployment of "example.war" (runtime-name: "example.war") [Server:SOAP-server-two] 22:56:08,741 INFO [org.jboss.as.server.deployment] (MSC service thread 1-3) WFLYSRV0027: Starting deployment of "example.war" (runtime-name: "example.war") [Server:SOAP-server-one] 22:56:09,906 INFO [org.wildfly.extension.undertow] (MSC service thread 1-3) WFLYUT0021: Registered web context: /example [Server:SOAP-server-two] 22:56:10,004 INFO [org.wildfly.extension.undertow] (MSC service thread 1-7) WFLYUT0021: Registered web context: /example [Server:SOAP-server-one] 22:56:10,121 INFO [org.jboss.as.server] (ServerService Thread Pool -- 59) WFLYSRV0010: Deployed "example.war" (runtime-name : "example.war") [Server:SOAP-server-two] 22:56:10,134 INFO [org.jboss.as.server] (ServerService Thread Pool -- 59) WFLYSRV0010: Deployed "example.war" (runtime-name : "example.war")如您从日志中看到的那样,我们的工件已部署到所有声明的服务器组。
从属-2有两个运行实例,每个实例都有自己的工件副本。
-
-
现在要取消部署工件,就像做饼一样简单:
[domain@localhost:9990 /] undeploy example.war –server-groups=server-group-REST-app,server-group-SOAP-app-
让我们通过 CLI 检查我们是否已成功取消部署我们的工件:
[domain@localhost:9990 /] deployment-info --server-group=server-group-REST-app [domain@localhost:9990 /] deployment-info --server-group=server-group-SOAP-app [domain@localhost:9990 /]
再次,检查日志以查看发生了什么:
-
主日志如下:
[Host Controller] 22:59:35,225 INFO [org.jboss.as.repository] (management-handler-thread - 9) WFLYDR0002: Content removed from location /home/luigi/WFC/wildfly/master/data/content/21/7dd6250d5bc4afcabdffb0b25c99db92239b5a/content -
从属-1 日志如下:
[Server:REST-server-one] 22:59:35,005 INFO [org.wildfly.extension.undertow] (MSC service thread 1-7) WFLYUT0022: Unregistered web context: /example [Server:REST-server-one] 22:59:35,044 INFO [org.jboss.as.server.deployment] (MSC service thread 1-8) WFLYSRV0028: Stopped deployment example.war (runtime-name: example.war) in 40ms [Server:REST-server-one] 22:59:35,201 INFO [org.jboss.as.repository] (ServerService Thread Pool -- 69) WFLYDR0002: Content removed from location /home/luigi/WFC/wildfly/slave-1/servers/REST-server-one/data/content/21/7dd6250d5bc4afcabdffb0b25c99db92239b5a/content [Server:REST-server-one] 22:59:35,201 INFO [org.jboss.as.server] (ServerService Thread Pool -- 69) WFLYSRV0009: Undeployed "example.war" (runtime-name: "example.war") [Host Controller] 22:59:35,221 INFO [org.jboss.as.repository] (Host Controller Service Threads - 14) WFLYDR0002: Content removed from location /home/luigi/WFC/wildfly/slave-1/data/content/21/7dd6250d5bc4afcabdffb0b25c99db92239b5a/content [Host Controller] 22:59:42,048 INFO [org.jboss.as.repository] (Host Controller Service Threads - 4) WFLYDR0009: Content /home/luigi/WFC/wildfly/slave-1/data/content/8d/b8b9b1cda547c7ec3fb493dbd5940cdb378ec0 is obsolete and will be removed [Host Controller] 22:59:42,050 INFO [org.jboss.as.repository] (Host Controller Service Threads - 4) WFLYDR0002: Content removed from location /home/luigi/WFC/wildfly/slave-1/data/content/8d/b8b9b1cda547c7ec3fb493dbd5940cdb378ec0/content [Server:REST-server-one] 22:59:46,550 INFO [org.jboss.as.repository] (ServerService Thread Pool -- 59) WFLYDR0009: Content /home/luigi/WFC/wildfly/slave-1/servers/REST-server-one/data/content/8d/b8b9b1cda547c7ec3fb493dbd5940cdb378ec0 is obsolete and will be removed [Server:REST-server-one] 22:59:46,552 INFO [org.jboss.as.repository] (ServerService Thread Pool -- 59) WFLYDR0002: Content removed from location /home/luigi/WFC/wildfly/slave-1/servers/REST-server-one/data/content/8d/b8b9b1cda547c7ec3fb493dbd5940cdb378ec0/content -
从属-2 日志如下:
[Server:SOAP-server-two] 22:59:35,004 INFO [org.wildfly.extension.undertow] (MSC service thread 1-3) WFLYUT0022: Unregistered web context: /example [Server:SOAP-server-one] 22:59:35,017 INFO [org.wildfly.extension.undertow] (MSC service thread 1-2) WFLYUT0022: Unregistered web context: /example [Server:SOAP-server-two] 22:59:35,072 INFO [org.hibernate.validator.internal.util.Version] (MSC service thread 1-3) HV000001: Hibernate Validator 5.1.3.Final [Server:SOAP-server-one] 22:59:35,093 INFO [org.hibernate.validator.internal.util.Version] (MSC service thread 1-5) HV000001: Hibernate Validator 5.1.3.Final [Server:SOAP-server-two] 22:59:35,127 INFO [org.jboss.as.server.deployment] (MSC service thread 1-6) WFLYSRV0028: Stopped deployment example.war (runtime-name: example.war) in 127ms [Server:SOAP-server-one] 22:59:35,141 INFO [org.jboss.as.server.deployment] (MSC service thread 1-8) WFLYSRV0028: Stopped deployment example.war (runtime-name: example.war) in 128ms [Server:SOAP-server-two] 22:59:35,203 INFO [org.jboss.as.repository] (ServerService Thread Pool -- 64) WFLYDR0002: Content removed from location /home/luigi/WFC/wildfly/slave-2/servers/SOAP-server-two/data/content/21/7dd6250d5bc4afcabdffb0b25c99db92239b5a/content [Server:SOAP-server-two] 22:59:35,204 INFO [org.jboss.as.server] (ServerService Thread Pool -- 64) WFLYSRV0009: Undeployed "example.war" (runtime-name: "example.war") [Server:SOAP-server-one] 22:59:35,209 INFO [org.jboss.as.repository] (ServerService Thread Pool -- 63) WFLYDR0002: Content removed from location /home/luigi/WFC/wildfly/slave-2/servers/SOAP-server-one/data/content/21/7dd6250d5bc4afcabdffb0b25c99db92239b5a/content [Server:SOAP-server-one] 22:59:35,212 INFO [org.jboss.as.server] (ServerService Thread Pool -- 63) WFLYSRV0009: Undeployed "example.war" (runtime-name: "example.war") [Host Controller] 22:59:35,219 INFO [org.jboss.as.repository] (Host Controller Service Threads - 15) WFLYDR0002: Content removed from location /home/luigi/WFC/wildfly/slave-2/data/content/21/7dd6250d5bc4afcabdffb0b25c99db92239b5a/content [Host Controller] 22:59:49,597 INFO [org.jboss.as.repository] (Host Controller Service Threads - 7) WFLYDR0009: Content /home/luigi/WFC/wildfly/slave-2/data/content/8d/b8b9b1cda547c7ec3fb493dbd5940cdb378ec0 is obsolete and will be removed [Host Controller] 22:59:49,598 INFO [org.jboss.as.repository] (Host Controller Service Threads - 7) WFLYDR0002: Content removed from location /home/luigi/WFC/wildfly/slave-2/data/content/8d/b8b9b1cda547c7ec3fb493dbd5940cdb378ec0/content
-
还有更多...
实际上,您可以使用以下指令将工件部署到所有服务器组:
[domain@localhost:9990 /] deploy example.war --all-server-groups
取消部署也有快捷方式:
[domain@localhost:9990 /] undeploy example.war --all-relevant-server-groups
它基本上从分配给它的所有服务器组中取消部署工件。
通过 CLI 检查服务器状态
在这个食谱中,我们将学习如何使用 CLI 检查服务器状态。使用独立模式,不需要进行此类检查,因为您根本无法连接到 CLI。因此,在域模式下,在进行某些操作之前,您可能需要知道某个服务器是否正在运行。
准备中
对于这个食谱,域控制器和主机控制器都应该处于开启和运行状态。对于它们的配置,请参阅本章中的食谱“将主机控制器连接到域控制器”。
如何做...
打开您的终端并像往常一样连接到域控制器:
$ ./bin/jboss-cli.sh --connect
[domain@localhost:9990 /] /host=slave-1/server-config=REST-server-one:read-resource(include-runtime=true)
{
"outcome" => "success",
"result" => {
"auto-start" => true,
"cpu-affinity" => undefined,
"group" => "server-group-REST-app",
"name" => "REST-server-one",
"priority" => undefined,
"socket-binding-group" => undefined,
"socket-binding-port-offset" => 0,
"status" => "STARTED",
"interface" => undefined,
"jvm" => {"default" => undefined},
"path" => undefined,
"system-property" => undefined
}
}
[domain@localhost:9990 /]
第四章. 使用 CLI 管理日志子系统
在本章中,您将涵盖以下主题:
-
创建一个文件处理器日志
-
创建一个周期性旋转的文件处理器
-
创建一个大小旋转的文件处理器
-
定义异步处理器
-
创建一个 syslog 处理器
-
列出和读取日志文件
-
使用不同的日志实现
简介
日志子系统是您配置 WildFly 和应用程序的信息语句的地方,用于调试和审计目的。
WildFly 自带默认配置,可以在独立和域目录的configuration文件夹中找到。默认配置会自动在控制台和名为server.log的文件中记录信息。
正如您在本章中将学到的,WildFly 有七个不同的处理器来帮助您持久化应用程序日志:
-
控制台: 将应用程序日志写入标准输出
-
文件: 将应用程序日志写入文件
-
周期性: 根据时间旋转文件来写入应用程序日志
-
大小: 根据大小旋转文件来写入应用程序日志
-
异步: 包装一个或多个处理器以提供异步行为
-
自定义: 允许您使用自己的处理器,只要它扩展了
java.util.logging.Handler类 -
Syslog: 将应用程序日志写入默认的操作系统日志记录器
根据您的 WildFly 的操作模式,您有不同的默认日志文件路径设置。
域模式
在托管域中,我们有两种类型的日志文件:控制器和服务器日志。
从默认的domain.xml配置开始,域控制器组件和服务器都位于同一主机上:
| 进程 | 日志文件 |
|---|---|
[主机控制器] |
jboss.domain.log.dir/host-controller.log |
[进程控制器] |
jboss.domain.log.dir/process-controller.log |
[服务器一】 |
jboss.domain.servers.dir/server-one/log/server.log |
[服务器二】 |
jboss.domain.servers.dir/server-two/log/server.log |
[服务器三】 |
jboss.domain.servers.dir/server-three/log/server.log |
如果您想更改上述提到的属性之一,可以设置不同的值,如下所示:
$ ./bin/domain.sh -Djboss.domain.log.dir=/home/luigi/wildfly-domain-logs
独立模式
独立服务器的默认日志文件位于 WildFly 主目录的jboss.server.base.dir文件夹的日志子目录中:
| 进程 | 日志文件 |
|---|---|
[服务器] |
jboss.server.log.dir/server.log |
如果您想更改上述提到的属性,可以设置不同的值,如下所示:
$ ./bin/standalone.sh -Djboss.server.log.dir=/home/luigi/wildfly-standalone-logs
创建一个文件处理器日志
日志子系统是您配置应用程序和/或 WildFly 核心的输出信息语句的地方,用于调试或审计目的。在本食谱中,我们将学习如何将应用程序的日志语句输出指向文件处理器。
为了简化,我们将尝试在独立模式下运行的 WildFly 的食谱。
准备工作
首先,让我们为我们的菜谱创建一个独立的配置,如下所示:
$ cd $WILDFLY_HOME
$ cp -a standalone std-logging
为了测试我们的日志配置,我们需要一个应用程序来记录一些语句。在这种情况下,我们将使用名为logging的应用程序。要获取部署的工件,请参阅第一章中的软件先决条件菜谱,欢迎来到 WildFly!。
现在,让我们启动 WildFly,如下所示:
$ cd $WILDFLY_HOME
$ ./bin/standalone.sh -Djboss.server.base.dir=$WILDFLY_HOME/std-logging
如何做...
-
在 WildFly 服务器运行的情况下,打开你的命令行工具并连接到 CLI:
$ ./bin/jboss-cli.sh --connect [standalone@localhost:9990 /] -
现在执行以下命令:
[standalone@localhost:9990 /] /subsystem=logging/file-handler=wildflycookbook-fh:add(level=INFO, file={"relative-to"=>"jboss.server.log.dir", "path"=>"wildflycookbook-fh.log"}, append=false, autoflush=true) [standalone@localhost:9990 /] /subsystem=logging/logger=com.packtpub.wildflycookbook:add(use-parent-handlers=false,handlers=["wildflycookbook-fh"],level=INFO) -
现在部署
logging.war应用程序,查看log文件夹以查看是否存在文件wildflycookbook-fh.log。相应的测试 URL 如下http://127.0.0.1:8080/logging。
现在检查你的日志,你应该看到如下内容:

使用 wildflycookbook-fh 文件处理程序的日志语句。
它是如何工作的...
我们首先需要添加文件处理程序,然后创建一个类别来将适当的应用程序包映射到我们新的文件处理程序wildflycookbook-fh。
应用程序包可以是任何,甚至是你可能感兴趣并希望存储在单独文件中的 WildFly 内部包。
不管怎样,一旦你创建了文件处理程序,你可以在 CLI 中检查它的配置,如下所示:

查看standalone.xml文件,新的文件处理程序配置如下所示:
<file-handler name="wildflycookbook-fh" autoflush="true">
<level name="INFO"/>
<file relative-to="jboss.server.log.dir" path="wildflycookbook-fh.log"/>
<append value="false"/>
</file-handler>
<logger category="com.packtpub.wildflycookbook" use-parent-handlers="false">
<level name="INFO"/>
<handlers>
<handler name="wildflycookbook-fh"/>
</handlers>
</logger>
还有更多...
通常,每个环境都有其独特的特性,即使是日志子系统也不例外。在测试环境中,你可能需要更多信息来查看,因此你希望开发者信息被追踪到日志文件中。我们可以通过在处理程序和类别上启用DEBUG级别来实现这种行为。让我们看看这两个指令。
首先,我们为文件处理程序本身启用DEBUG级别:
[standalone@localhost:9990 /] /subsystem=logging/file-handler=wildflycookbook-fh:write-attribute(name=level,value=DEBUG)
然后我们在类别上启用DEBUG级别:
[standalone@localhost:9990 /] /subsystem=logging/logger=com.packtpub.wildflycookbook:write-attribute(name=level,value=DEBUG)
创建周期性轮转文件处理程序
在这个菜谱中,我们将学习如何将应用程序的日志语句输出定向到周期性文件处理程序。这与文件处理程序不同,因为当运行系统达到预定义的时间(即小时变化、日变化等)时,日志文件会滚动,备份自身并创建一个具有相同特性的新文件。
为了简单起见,我们将尝试在独立模式下运行的 WildFly 的菜谱。
准备工作
如果你没有遵循前面的菜谱,我们需要为我们的菜谱创建一个独立的配置,如下所示:
$ cd $WILDFLY_HOME
$ cp -a standalone std-logging
为了测试我们的日志配置,我们需要一个应用程序来记录一些语句。在这种情况下,我们将使用名为logging的应用程序。要获取部署的工件,请参阅第一章中的软件先决条件菜谱,欢迎来到 WildFly!。
现在,让我们启动 WildFly,如下所示:
$ cd $WILDFLY_HOME
$ ./bin/standalone.sh -Djboss.server.base.dir=$WILDFLY_HOME/std-logging
如何做…
-
在运行 WildFly 服务器的情况下,打开您的命令行工具并连接到 CLI:
$ ./bin/jboss-cli.sh --connect [standalone@localhost:9990 /] -
现在执行以下命令:
[standalone@localhost:9990 /] /subsystem=logging/periodic-rotating-file-handler=wildflycookbook-prfh:add(level=INFO, file={"relative-to"=>"jboss.server.log.dir", "path"=>"wildflycookbook-prfh.log"}, append=false, autoflush=true, suffix=".yyyy-MM-dd-HH-mm")在前面的配置中,我们设置了后缀和旋转算法为分钟,只是为了举例。
小贴士
在生产环境中,您应该使用每日旋转算法并安排一个
olding机制,以便将文件备份到其他位置,以避免文件系统满载。然而,如果您遵循了之前的配方,您只需将
periodic.rotating-file-handler添加到类别中,如下所示:[standalone@localhost:9990 /] /subsystem=logging/logger=com.packtpub.wildflycookbook:add-handler(name=wildflycookbook-prfh) -
相反,如果您跳过了 创建文件处理器日志 配方,您需要创建一个新的类别并将处理器引用到它,如下所示:
[standalone@localhost:9990 /] /subsystem=logging/logger=com.packtpub.wildflycookbook:add(use-parent-handlers=false,handlers=["wildflycookbook-prfh"], level=INFO) -
现在部署
logging.war,如果您还没有这样做,然后查看log文件夹以检查文件wildflycookbook-prfh.log是否存在。要触发日志语句到新文件,请访问以下地址:http://127.0.0.1:8080/logging。现在等一会儿,刷新页面几次。您应该在
jboss.server.base.dir/log文件夹中注意到至少另一个*-prfh*日志文件,如下所示:![如何做…]()
正如您所看到的,我们的日志文件已经旋转了一次。
如何工作…
我们首先需要创建文件处理器,然后创建一个类别来将适当的应用程序包映射到我们新的周期性旋转文件处理器 wildflycookbook-prfh。应用程序包可以是任何类型,甚至是您可能感兴趣并希望存储在单独文件中的 WildFly 内部包。
那么关于“零”字节的文件呢?该文件是在创建处理器后创建的。在我的情况下,我在运行 logging 应用程序之前等了几分钟,所以当是时候写入文件时,处理器首先检查分钟是否改变(它改变了),然后旋转文件。然后它创建了新文件并将内容写入其中。
无论如何,一旦您创建了周期性旋转文件处理器,您可以在 CLI 中检查其配置,如下所示:

查看 standalone.xml,新的周期性旋转文件处理器配置如下所示:
<periodic-rotating-file-handler name="wildflycookbook-prfh" autoflush="true">
<level name="INFO"/>
<file relative-to="jboss.server.log.dir" path="wildflycookbook-prfh.log"/>
<suffix value=".yyyy-MM-dd-HH-mm"/>
<append value="false"/>
</periodic-rotating-file-handler>
<logger category="com.packtpub.wildflycookbook" use-parent-handlers="false">
<level name="INFO"/>
<handlers>
<handler name="wildflycookbook-fh"/>
<handler name="wildflycookbook-prfh"/>
</handlers>
</logger>
wildflycookbook-fh 处理器存在是因为之前的配方。
创建大小旋转文件处理器
在这个配方中,我们将学习如何将应用程序的日志语句输出目标到一个大小旋转文件处理器。它与文件处理器不同,因为当文件本身在字节大小方面达到特定值时,它会自动备份并创建一个具有相同特性的新文件。这个机制默认只设置一个备份文件。这是为了防止您忘记设置,导致硬盘空间快速耗尽。
为了简化,我们将尝试在独立模式下运行的 WildFly 中的配方。
准备工作
如果您没有遵循之前的配方,我们需要为我们的配方创建一个独立配置,如下所示:
$ cd $WILDFLY_HOME
$ cp -a standalone std-logging
为了测试我们的日志配置,我们需要一个应用程序来记录一些语句。在这种情况下,我们将使用名为logging的应用程序。要获取部署的工件,请参阅第一章中的软件先决条件配方,欢迎来到 WildFly!。
现在,让我们启动 WildFly,如下所示:
$ cd $WILDFLY_HOME
$ ./bin/standalone.sh -Djboss.server.base.dir=$WILDFLY_HOME/std-logging
如何做…
-
在 WildFly 服务器运行时,打开你的命令行工具并连接到 CLI:
$ ./bin/jboss-cli.sh--connect [standalone@localhost:9990 /] -
现在执行以下命令:
[standalone@localhost:9990 /] /subsystem=logging/size-rotating-file-handler=wildflycookbook-srfh:add(level=INFO, file={"relative-to"=>"jboss.server.log.dir", "path"=>"wildflycookbook-srfh.log"}, append=false, autoflush=true, rotate-size=1k, max-backup-index=5)在前面的配置中,我们将
rotate-size设置为1KB。当文件达到该大小时,处理器将关闭当前文件并创建一个新的文件。旧的文件将被重命名并添加索引后缀。属性max-backup-index指定处理器需要维护的文件数量;在我们的例子中是五个。 -
然而,在创建文件处理器日志配方中,我们创建了一个日志类别。因此,如果你遵循了那个配方,你只需要将
size-rotating-file-handler处理器添加到类别中,如下所示:[standalone@localhost:9990 /] /subsystem=logging/logger=com.packtpub.wildflycookbook:add-handler(name=wildflycookbook-srfh) -
相反,如果你跳过了
创建文件处理器日志配方,你需要创建一个新的类别并将处理器引用到它,如下所示:[standalone@localhost:9990 /] /subsystem=logging/logger=com.packtpub.wildflycookbook:add(use-parent-handlers=false,handlers=["wildflycookbook-srfh"], level=INFO) -
现在部署
logging.war,如果你还没有做的话,然后查看log文件夹以检查是否存在文件wildflycookbook-srfh.log。要触发日志语句到新文件,请点击以下地址:http://127.0.0.1:8080/logging。 -
做三到四次,这样我们的日志文件就会增加大小。你应该注意
jboss.server.base.dir/log文件夹中的一些*-srfh.log日志文件,如下所示:![如何做…]()
它是如何工作的…
我们首先需要创建 size-rotating-file-handler,然后创建/选择一个类别来将适当的应用程序包映射到我们新的 size-rotating-file-handler,wildflycookbook-srfh。应用程序包可以是任何,甚至是 WildFly 内部包,你可能感兴趣并希望将其存储在单独的文件中。
如果我们达到max-backup-index会发生什么?
假设我们已经切换了当前日志文件五次,所以在我们log文件夹中有以下文件:
-
wildflycookbook-srfh.log -
wildflycookbook-srfh.log.1 -
wildflycookbook-srfh.log.2 -
wildflycookbook-srfh.log.3 -
wildflycookbook-srfh.log.4 -
wildflycookbook-srfh.log.5
索引文件的大小大约为 1 KB,而当前的wildflycookbook-srfh.log只需要额外的几个字节就可以滚动。
当我们再次点击应用程序时,当前文件会带上索引后缀 1,旧的.1变成.2,旧的.2变成.3,以此类推。.5文件会发生什么?它将被删除,.4文件将取而代之。
无论如何,一旦你创建了 size-rotating-file-handler,你可以在 CLI 中检查其配置,如下所示:

查看standalone.xml,新的 size-rotating-file-handler 配置如下所示:
<size-rotating-file-handler name="wildflycookbook-srfh" autoflush="true">
<level name="INFO"/>
<file relative-to="jboss.server.log.dir" path="wildflycookbook-srfh.log"/>
<rotate-size value="1k"/>
<max-backup-index value="5"/>
<append value="false"/>
</size-rotating-file-handler>
<logger category="com.packtpub.wildflycookbook" use-parent-handlers="false">
<level name="INFO"/>
<handlers>
<handler name="wildflycookbook-fh"/>
<handler name="wildflycookbook-prfh"/>
<handler name="wildflycookbook-srfh"/>
</handlers>
</logger>
由于之前的食谱,存在处理程序wildflycookbook-fh和wildflycookbook-prfh。
定义异步处理程序
在这个食谱中,你将学习如何将应用程序的日志语句输出指向你选择的处理程序,并以异步方式记录。为了简单起见,我们将尝试在独立模式下运行的 WildFly 上执行我们的食谱。
准备工作
为了测试我们的日志配置,我们需要一个应用程序来记录一些语句。在这种情况下,我们将使用名为logging的应用程序。要获取部署的工件,请参阅第一章中的软件先决条件食谱,欢迎来到 WildFly!。
此外,这个食谱依赖于本章中解释的创建文件处理程序日志和创建周期性旋转文件处理程序食谱所做的配置。
如何做到这一点...
-
在一个运行的 WildFly 服务器上,打开你的命令行工具并连接到 CLI:
$ ./bin/jboss-cli.sh--connect [standalone@localhost:9990 /] -
现在执行以下命令:
[standalone@localhost:9990 /] /subsystem=logging/async-handler=wildflycookbook-afh:add(level=INFO, queue-length=1024, overflow-action=BLOCK) {"outcome" => "success"} [standalone@localhost:9990 /] /subsystem=logging/async-handler=wildflycookbook-afh:assign-subhandler(name=wildflycookbook-prfh) {"outcome" => "success"} [standalone@localhost:9990 /] /subsystem=logging/async-handler=wildflycookbook-afh:assign-subhandler(name=wildflycookbook-fh) {"outcome" => "success"} [standalone@localhost:9990 /] -
要检查新的配置,只需发出以下命令:
[standalone@localhost:9990 /] /subsystem=logging/async-handler=wildflycookbook-afh:read-resource(recursive=true,include-runtime=true,include-defaults=true) { "outcome" => "success", "result" => { "enabled" => true, "filter" => undefined, "filter-spec" => undefined, "level" => "INFO", "name" => "wildflycookbook-afh", "overflow-action" => "BLOCK", "queue-length" => 1024, "subhandlers" => [ "wildflycookbook-prfh", "wildflycookbook-fh" ] } } [standalone@localhost:9990 /] -
好吧,现在我们有了新的异步处理程序以异步方式管理两个其他处理程序。要检查异步处理程序是否配置正确,请打开浏览器并将它指向以下 URL:
http://127.0.0.1:8080/logging
它是如何工作的...
由wildflycookbook-fh和wildflycookbook-prfh处理程序管理的文件仍然写入它们自己的日志文件。异步处理程序本身根本不会生成任何额外的文件;它只为其他处理程序添加异步行为。
异步处理程序用于提高日志吞吐量。
它抓取应用程序日志语句,将它们放入缓冲区,然后使用您定义的处理程序进行记录。此外,这个缓冲区不是无界的——它有一个限制。实际上,从如何做到这一点部分显示的命令中,我强调了几个关键点:queue-length和overflow-action。
简而言之,我们告诉异步处理程序缓冲区可以容纳多少日志语句,以及当它们没有更多空间时应该做什么。
我们将1024作为缓冲区中日志语句的数量,并将block操作作为超出限制的行为。这意味着日志语句处于等待状态,直到它在缓冲区中找到空间。否则,你可以选择discard操作,其后果是丢失你的日志。
你可以使用你想要的任何操作,但请记住,对于block操作,你应该调整缓冲区的大小,以避免存储过多的或过少的日志。
查看standalone.xml,新的异步处理程序配置看起来像这样:
<async-handler name="wildflycookbook-afh">
<level name="INFO"/>
<queue-length value="1024"/>
<overflow-action value="block"/>
<subhandlers>
<handler name="wildflycookbook-prfh"/>
<handler name="wildflycookbook-fh"/>
</subhandlers>
</async-handler>
还有更多...
异步处理程序更快,那么为什么你总是不使用它呢?好吧,不一定;有时使用异步处理程序可能会得到更差的表现。
如果你的应用程序大量使用 I/O 操作,那么你可以从中受益。另一方面,如果你的应用程序大量使用 CPU,那么使用异步处理器肯定不会给你带来好处,因为它会增加上下文切换。
创建 syslog 处理器
在这个菜谱中,我们将学习如何使用"syslog"。syslog 是一个用于消息记录的标准协议。这个标准已经在不同的操作系统上实现,例如 Linux、Unix 和 Windows。在 2009 年,互联网 工程任务组(IETF)标准化了"syslog"协议规范,可以在以下地址查看:
你可能会在集中式日志系统用于收集所有系统信息的环境中使用syslog-handler。
最后,对于这个菜谱,我将使用一个名为syslog-ng的 syslog 服务器。
-
要安装到 Fedora 21 系统,请按以下步骤操作:
$ sudo yum -y install rsyslog -
完成后,将以下指令启用到
/etc/rsyslog.conf中:$ModLoad imudp.so $UDPServerRun 514 -
然后使用以下命令启动
rsyslogd守护进程:$ sudo /usr/sbin/rsyslogd
注意
它的详细安装和配置超出了本书的范围。顺便说一句,你可以参考以下网站的官方文档:
准备工作
如果你没有遵循之前的菜谱,我们需要为我们的菜谱创建一个独立配置,如下所示:
$ cd $WILDFLY_HOME
$ cp -a standalone std-logging
为了测试我们的日志配置,我们需要一个应用程序来记录一些语句。在这种情况下,我们将使用名为logging的应用程序。要获取部署的工件,请参阅第一章中的软件先决条件菜谱,欢迎来到 WildFly!。
现在,让我们启动 WildFly,如下所示:
$ cd $WILDFLY_HOME
$ ./bin/standalone.sh -Djboss.server.base.dir=$WILDFLY_HOME/std-logging
如何操作…
-
在 WildFly 服务器运行后,打开你的命令行工具并连接到 CLI:
$ ./bin/jboss-cli.sh--connect [standalone@localhost:9990 /] -
现在执行以下命令:
[standalone@localhost:9990 /] /subsystem=logging/syslog-handler=wildfly-slh:add(server-address=localhost,hostname=foogaro,port=514,syslog-format=RFC5424,facility=user-level,level=INFO,app-name=wildfly-logging,enabled=true) {"outcome" => "success"} -
现在我们需要将处理器与日志记录器关联起来,我们将使用
com.packtpub.wildflycookbook(在本章之前的菜谱中使用过),如下所示:[standalone@localhost:9990 /] /subsystem=logging/logger=com.packtpub.wildflycookbook:add-handler(name=wildfly-slh) {"outcome" => "success"} [standalone@localhost:9990 /] -
在我们可以测试我们的新配置之前,打开一个新的终端窗口,通过以下命令
tail``syslog:$ journalctl -f这在 Fedora 21 上会起作用;在不同的 Linux 系统上,你可能发现你的
SysLog服务器将日志记录到/var/log/messages文件中。然而,根据你的操作系统指定文件。 -
准备就绪后,如果你还没有这样做,部署日志应用程序,并在浏览器中打开以下 URL:
http://localhost:8080/logging。 -
你应该看到以下类似的内容:
Message from syslogd@localhost at Apr 19 17:56:36 ... wildfly-logging[15343] Fatal message这是我们的日志应用程序捕获的致命消息。你知道这是因为它引用了
wildfly-logging,这与定义syslog-handler时属性app-name的值相对应。它成功了!
它是如何工作的…
首先,最好重复一下,“syslog”是一个协议,因此它有多个实现,这取决于硬件和软件。换句话说,它是操作系统依赖的。在我们的第一个命令中,当创建 syslog-handler 时,我们指定了许多参数。
让我们在以下表格中总结它们:
| 属性 | 描述 |
|---|---|
server-address |
这是 syslog 服务器所在的位置——默认是 localhost。 |
hostname |
这是发送消息的服务器的主机名。 |
port |
这是 syslog 服务器监听的端口——默认是 514。 |
syslog-format |
根据 RFC5424 规范用于记录消息的格式——默认是 RFC5424。 |
facility |
这是消息的“类别”,仍然基于 RFC5425 规范——默认是 user-level。 |
level |
这是日志级别——默认是 ALL。 |
app-name |
这应该对应于生成消息的应用程序。它基本上用于过滤。 |
enabled |
当设置为 false 时,禁用 syslog-handler——默认值是 true。 |
然而,无论何时创建处理器,你都必须将其引用到记录器中。否则,你将看不到任何消息。
注意
详细介绍“syslog”协议超出了本书的范围。有关更多信息,请参阅其规范,可在以下网站找到:
列出和读取日志文件
在这个菜谱中,我们将学习如何列出和读取日志文件。当你只能访问 CLI(可能是一个远程 CLI),而不能访问服务器本身或托管日志文件的文件系统时,这可能会很有帮助。
准备工作
为了完全理解这个菜谱,你应该遵循之前的某个菜谱,这些菜谱创建了各种日志文件。然而,标准的 WildFly 日志文件 server.log 将会存在,这已经足够了。
如何操作...
-
在运行 WildFly 服务器的情况下,打开你的命令行工具并连接到 CLI:
$ ./bin/jboss-cli.sh --connect [standalone@localhost:9990 /] -
在读取文件之前,我们需要知道哪些日志文件存在。要列出所有文件,请执行以下命令:
[standalone@localhost:9990 /] /subsystem=logging:list-log-files { "outcome" => "success", "result" => [ { "file-name" => "server.log", "file-size" => 24879L, "last-modified-date" => "2015-05-24T15:14:29.000+0200" }, { "file-name" => "wildflycookbook-fh.log", "file-size" => 0L, "last-modified-date" => "2015-05-24T15:14:13.000+0200" }, { "file-name" => "wildflycookbook-prfh.log", "file-size" => 0L, "last-modified-date" => "2015-05-24T15:14:13.000+0200" }, { "file-name" => "wildflycookbook-prfh.log.2015-05-24-14-42", "file-size" => 0L, "last-modified-date" => "2015-05-24T14:42:14.000+0200" }, { "file-name" => "wildflycookbook-prfh.log.2015-05-24-14-43", "file-size" => 440L, "last-modified-date" => "2015-05-24T14:43:06.000+0200" }, { "file-name" => "wildflycookbook-srfh.log", "file-size" => 0L, "last-modified-date" => "2015-05-24T15:14:13.000+0200" }, { "file-name" => "wildflycookbook-srfh.log.1", "file-size" => 1096L, "last-modified-date" => "2015-05-24T14:44:17.000+0200" } ] } [standalone@localhost:9990 /] -
由于我们之前的菜谱,我们有大量的文件要查看。为了这个菜谱的目的,我们将使用默认的日志文件,
server.log。要读取文件,我们可以尝试以下命令:[standalone@localhost:9990 /] /subsystem=logging:read-log-file(name=server.log) { "outcome" => "success", "result" => [ "2015-05-24 15:14:14,813 INFO [org.wildfly.extension.undertow] (MSC service thread 1-9) WFLYUT0006: Undertow HTTP listener default listening on /127.0.0.1:8080", "2015-05-24 15:14:14,964 INFO [org.jboss.as.connector.subsystems.datasources] (MSC service thread 1-15) WFLYJCA0001: Bound data source [java:jboss/datasources/ExampleDS]", "2015-05-24 15:14:15,021 INFO [org.jboss.as.server.deployment.scanner] (MSC service thread 1-5) WFLYDS0013: Started FileSystemDeploymentService for directory /Users/foogaro/wildfly-9.0.0.Beta2/std-logging/deployments", "2015-05-24 15:14:15,025 INFO [org.jboss.as.server.deployment] (MSC service thread 1-8) WFLYSRV0027: Starting deployment of \"logging.war\" (runtime-name: \"logging.war\")", "2015-05-24 15:14:15,116 INFO [org.jboss.ws.common.management] (MSC service thread 1-4) JBWS022052: Starting JBoss Web Services - Stack CXF Server 5.0.0.Beta3", "2015-05-24 15:14:15,363 INFO [org.wildfly.extension.undertow] (MSC service thread 1-11) WFLYUT0021: Registered web context: /logging", "2015-05-24 15:14:15,405 INFO [org.jboss.as.server] (ServerService Thread Pool -- 34) WFLYSRV0010: Deployed \"logging.war\" (runtime-name : \"logging.war\")", "2015-05-24 15:14:15,545 INFO [org.jboss.as] (Controller Boot Thread) WFLYSRV0060: Http management interface listening on http://127.0.0.1:9990/management", "2015-05-24 15:14:15,546 INFO [org.jboss.as] (Controller Boot Thread) WFLYSRV0051: Admin console listening on http://127.0.0.1:9990", "2015-05-24 15:14:15,546 INFO [org.jboss.as] (Controller Boot Thread) WFLYSRV0025: WildFly Full 9.0.0.Beta2 (WildFly Core 1.0.0.Beta2) started in 2328ms - Started 269 of 451 services (221 services are lazy, passive or on-demand)" ] } [standalone@localhost:9990 /]如你所见,输出仅显示了内容的一些最后几行。
-
如果你想从开头读取,可以指定
tail=false参数(默认设置为true),以及name参数,如下所示:[standalone@localhost:9990 /] /subsystem=logging:read-log-file(name=server.log,tail=false) { "outcome" => "success", "result" => [ "2015-05-24 14:38:38,059 INFO [org.jboss.modules] (main) JBoss Modules version 1.4.2.Final", "2015-05-24 14:38:38,216 INFO [org.jboss.msc] (main) JBoss MSC version 1.2.4.Final", "2015-05-24 14:38:38,266 INFO [org.jboss.as] (MSC service thread 1-6) WFLYSRV0049: WildFly Full 9.0.0.Beta2 (WildFly Core 1.0.0.Beta2) starting", "2015-05-24 14:38:38,267 DEBUG [org.jboss.as.config] (MSC service thread 1-6) Configured system properties:", "[Standalone] = ", "awt.toolkit = sun.lwawt.macosx.LWCToolkit", "file.encoding = UTF-8", "file.encoding.pkg = sun.io", "file.separator = /", "ftp.nonProxyHosts = local|*.local|169.254/16|*.169.254/16" ] } [standalone@localhost:9990 /] -
如果你想要查看更多行日志文件,只需指定你想要的行数,并添加
lines参数,如下所示:[standalone@localhost:9990 /] /subsystem=logging:read-log-file(name=server.log,lines=15) { "outcome" => "success", "result" => [ "2015-05-24 15:14:14,642 INFO [org.jboss.as.naming] (MSC service thread 1-16) WFLYNAM0003: Starting Naming Service", "2015-05-24 15:14:14,642 INFO [org.jboss.as.mail.extension] (MSC service thread 1-14) WFLYMAIL0001: Bound mail session [java:jboss/mail/Default]", "2015-05-24 15:14:14,703 INFO [org.wildfly.extension.undertow] (ServerService Thread Pool -- 55) WFLYUT0014: Creating file handler for path /Users/foogaro/wildfly-9.0.0.Beta2/welcome-content", "2015-05-24 15:14:14,728 INFO [org.wildfly.extension.undertow] (MSC service thread 1-5) WFLYUT0012: Started server default-server.", "2015-05-24 15:14:14,745 INFO [org.wildfly.extension.undertow] (MSC service thread 1-3) WFLYUT0018: Host default-host starting", "2015-05-24 15:14:14,813 INFO [org.wildfly.extension.undertow] (MSC service thread 1-9) WFLYUT0006: Undertow HTTP listener default listening on /127.0.0.1:8080", "2015-05-24 15:14:14,964 INFO [org.jboss.as.connector.subsystems.datasources] (MSC service thread 1-15) WFLYJCA0001: Bound data source [java:jboss/datasources/ExampleDS]", "2015-05-24 15:14:15,021 INFO [org.jboss.as.server.deployment.scanner] (MSC service thread 1-5) WFLYDS0013: Started FileSystemDeploymentService for directory /Users/foogaro/wildfly-9.0.0.Beta2/std-logging/deployments", "2015-05-24 15:14:15,025 INFO [org.jboss.as.server.deployment] (MSC service thread 1-8) WFLYSRV0027: Starting deployment of \"logging.war\" (runtime-name: \"logging.war\")", "2015-05-24 15:14:15,116 INFO [org.jboss.ws.common.management] (MSC service thread 1-4) JBWS022052: Starting JBoss Web Services - Stack CXF Server 5.0.0.Beta3", "2015-05-24 15:14:15,363 INFO [org.wildfly.extension.undertow] (MSC service thread 1-11) WFLYUT0021: Registered web context: /logging", "2015-05-24 15:14:15,405 INFO [org.jboss.as.server] (ServerService Thread Pool -- 34) WFLYSRV0010: Deployed \"logging.war\" (runtime-name : \"logging.war\")", "2015-05-24 15:14:15,545 INFO [org.jboss.as] (Controller Boot Thread) WFLYSRV0060: Http management interface listening on http://127.0.0.1:9990/management", "2015-05-24 15:14:15,546 INFO [org.jboss.as] (Controller Boot Thread) WFLYSRV0051: Admin console listening on http://127.0.0.1:9990", "2015-05-24 15:14:15,546 INFO [org.jboss.as] (Controller Boot Thread) WFLYSRV0025: WildFly Full 9.0.0.Beta2 (WildFly Core 1.0.0.Beta2) started in 2328ms - Started 269 of 451 services (221 services are lazy, passive or on-demand)" ] } [standalone@localhost:9990 /]
显然,一个持续的 tail -f 命令将会非常有用,但目前还没有这样的命令。
还有更多...
值得注意的是还有一个选项,那就是skip参数。它基本上根据您是从文件头部还是尾部开始读取来向上或向下移动行。
让我们尝试跳过 5 行来试试:
[standalone@localhost:9990 /] /subsystem=logging:read-log-file(name=server.log,skip=5)
{
"outcome" => "success",
"result" => [
"2015-05-24 15:14:14,642 INFO [org.jboss.as.naming] (MSC service thread 1-16) WFLYNAM0003: Starting Naming Service",
"2015-05-24 15:14:14,642 INFO [org.jboss.as.mail.extension] (MSC service thread 1-14) WFLYMAIL0001: Bound mail session [java:jboss/mail/Default]",
"2015-05-24 15:14:14,703 INFO [org.wildfly.extension.undertow] (ServerService Thread Pool -- 55) WFLYUT0014: Creating file handler for path /Users/foogaro/wildfly-9.0.0.Beta2/welcome-content",
"2015-05-24 15:14:14,728 INFO [org.wildfly.extension.undertow] (MSC service thread 1-5) WFLYUT0012: Started server default-server.",
"2015-05-24 15:14:14,745 INFO [org.wildfly.extension.undertow] (MSC service thread 1-3) WFLYUT0018: Host default-host starting",
"2015-05-24 15:14:14,813 INFO [org.wildfly.extension.undertow] (MSC service thread 1-9) WFLYUT0006: Undertow HTTP listener default listening on /127.0.0.1:8080",
"2015-05-24 15:14:14,964 INFO [org.jboss.as.connector.subsystems.datasources] (MSC service thread 1-15) WFLYJCA0001: Bound data source [java:jboss/datasources/ExampleDS]",
"2015-05-24 15:14:15,021 INFO [org.jboss.as.server.deployment.scanner] (MSC service thread 1-5) WFLYDS0013: Started FileSystemDeploymentService for directory /Users/foogaro/wildfly-9.0.0.Beta2/std-logging/deployments",
"2015-05-24 15:14:15,025 INFO [org.jboss.as.server.deployment] (MSC service thread 1-8) WFLYSRV0027: Starting deployment of \"logging.war\" (runtime-name: \"logging.war\")",
"2015-05-24 15:14:15,116 INFO [org.jboss.ws.common.management] (MSC service thread 1-4) JBWS022052: Starting JBoss Web Services - Stack CXF Server 5.0.0.Beta3"
]
}
[standalone@localhost:9990 /]
该命令仅删除了文件的最后 5 行,但它从 5 行之前开始显示输出。默认情况下,显示的总行数是 10 行——这是因为默认情况下,它从文件的末尾开始读取。
自 WildFly 版本 8.2 以来,日志子系统获得了一个名为log-file的新资源。该资源列出了在jboss.server.log.dir(或根据操作模式为jboss.domain.log.dir)中定义的所有日志文件,这些文件在子系统中被定义。在 WildFly 9 中,现在您可以通过管理接口下载日志文件。
-
打开浏览器并将其指向
http://localhost:9990/management/subsystem/logging/log-file/server.log?operation=attribute&name=stream&useStreamAsResponse。 -
或者,您可以使用如
curl这样的 HTTP 工具。使用curl工具,您可以获取日志文件内容,如下所示:$ curl --digest -o server.log -L -D - http://127.0.0.1:9990/management?useStreamAsResponse --header "Content-Type: application/json" -u wildfly:cookbook.2015 -d '{"operation":"read-attribute","address":[{"subsystem":"logging"},{"log-file":"server.log"}],"name":"stream"}'上述示例获取了
server.log的流并将其存储到同名的文件中(-o server.log指令实际上存储了输出)。 -
同时,随着 WildFly 9 的新版本发布,您可以直接从 Web 控制台读取日志文件。您可以通过点击运行时并选择左侧的日志查看器菜单项来查看所有可用的文件,如下图中所示:
![还有更多…]()
-
一旦您选择了要查看的日志文件,下载和查看按钮将被启用。以下截图显示了 Web 控制台中的日志文件外观:
![还有更多…]()
多么优雅啊!
使用不同的日志实现
在本关于日志的最后一道菜中,我们将学习如何使用不同的日志实现。在早期版本的 JBoss AS 中,您可以依赖java.util.logging(也称为 JUL)或log4j实现。WildFly 依赖于 JUL 日志实现。
准备中
为了管理您可以使用哪些日志实现,WildFly 为您提供了几个属性。
第一个名为add-logging-api-dependencies,可以设置为true(默认值)或false。当设置为true时,WildFly 日志默认实现将自动添加到所有部署中。
另一个属性名为use-deployment-logging-config,可以设置为true(默认值)或false。当设置为true时,它为部署提供了per-deployment logging功能,这实际上使得您的应用程序能够携带自己的日志配置文件。
以下配置文件是允许的:
-
logging.properties -
jboss-logging.properties -
log4j.properties -
log4j.xml -
jboss-log4j.xml
如果您正在部署一个 EAR 应用程序,该文件应放置在META-INF目录中。而在 WAR 和 JAR 捆绑包的情况下,该文件可以放置在META-INF或WEB-INF目录中。
现在让我们回到我们的配方。我们将看到如何使用不同的日志实现。我们将使用log4j实现。
为了测试我们的日志配置,我们需要一个应用程序来记录一些语句。在这种情况下,我们将使用名为log4j的应用程序。要获取部署的工件,请参阅第一章中的软件先决条件配方,欢迎来到 WildFly!。
现在是时候创建一个自定义的独立配置文件夹来测试我们的配方了,如下所示:
$ cd $WILDFLY_HOME
$ cp -a standalone std-log4j
$ ./bin/standalone.sh -Djboss.server.base.dir=$WILDFLY_HOME/std-log4j
如何操作...
-
在 WildFly 服务器运行时,打开你的命令行工具并连接到 CLI:
$ ./bin/jboss-cli.sh --connect [standalone@localhost:9990 /] -
现在我们移除了应用程序的自动日志记录依赖。请注意,此设置将适用于同一 WildFly 实例上运行的所有其他应用程序,如下面的命令所示:
[standalone@localhost:9990 /] /subsystem=logging:write-attribute(name=add-logging-api-dependencies,value=false) {"outcome" => "success"} -
现在需要部署的应用程序需要声明对
log4j的依赖,它已经作为模块提供给你,位于 WildFly 主目录的modules/system/layers/base/org/apache/log4j/main文件夹中。为此,您需要将一个名为jboss-deployment-structure.xml的文件添加到您的META-INF或WEB-INF文件夹中(取决于您是否有一个 EAR 或 WAR 应用程序),如下所示:<?xml version="1.0"?> <jboss-deployment-structure > <deployment> <dependencies> <module name="org.apache.log4j" /> </dependencies> </deployment> </jboss-deployment-structure> Now, let's go back to the CLI to tell WildFly that we have our own configuration file, which looks like the following: <log4j:configuration debug="true" xmlns:log4j='http://jakarta.apache.org/log4j/'> <appender name="fileAppender" class="org.apache.log4j.RollingFileAppender"> <param name="append" value="false" /> <param name="file" value="log4j.log" /> <layout class="org.apache.log4j.PatternLayout"> <param name="ConversionPattern" value="%d{ABSOLUTE} %-5p [%c{1}] %m%n" /> </layout> </appender> <root> <level value="DEBUG" /> <appender-ref ref="fileAppender" /> </root> </log4j:configuration> -
如前所述,
per-deployment-logging功能默认启用。然而,以下是一个启用它的 CLI 命令:[standalone@localhost:9990 /] /subsystem=logging:write-attribute(name=use-deployment-logging-config,value=true) { "outcome" => "success", "response-headers" => { "operation-requires-reload" => true, "process-state" => "reload-required" } }小贴士
完成后,请记得
重新加载服务器。 -
此外,如准备就绪部分所指出的,您可以提供自己的日志配置文件。以下是我
log4j应用程序附带的log4j.xml文件:<log4j:configuration debug="true" xmlns:log4j='http://jakarta.apache.org/log4j/'> <appender name="fileAppender" class="org.apache.log4j.RollingFileAppender"> <param name="append" value="false" /> <param name="file" value="log4j.log" /> <layout class="org.apache.log4j.PatternLayout"> <param name="ConversionPattern" value="%d{ABSOLUTE} %-5p [%c{1}] %m%n" /> </layout> </appender> <root> <level value="DEBUG" /> <appender-ref ref="fileAppender" /> </root> </log4j:configuration> -
现在我们可以构建和部署应用程序了。在部署应用程序时,请注意
log4j.log文件中的日志(在上述 XML 代码片段中定义),该文件位于£WILDFLY_HOME文件夹中。你应该会看到以下条目:15:32:20,835 ERROR [Startup] Error message 15:32:20,837 WARN [Startup] Warning message 15:32:20,837 INFO [Startup] Information message 15:32:20,837 DEBUG [Startup] Debug message
还有更多...
有另一种方法可以达到相同的结果。您可以保持add-logging-api-dependencies为true,并通过在jboss-deployment-structure.xml文件中排除logging子系统来避免隐式依赖,如下所示:
<?xml version="1.0"?>
<jboss-deployment-structure >
<deployment>
<exclude-subsystems>
<subsystem name="logging"/>
</exclude-subsystems>
<dependencies>
<module name="org.apache.log4j" />
</dependencies>
</deployment>
</jboss-deployment-structure>
这种方法过于保守,因为它不会影响可能依赖于日志子系统的其他应用程序。
此外,依赖项可以按照标准的 Java 方式编写,即通过放置在META-INF文件夹中的MANIFEST.MF文件,如下所示:
Manifest-Version: 1.0
Dependencies: org.apache.log4j
<empty line>
小贴士
请记住在行尾添加一个空行,以拥有一个有效的MANIFEST.MF文件。你也可以有更多的依赖模块,通过逗号,分隔它们。
参见
更多关于 WildFly 9 中日志工作原理的信息,可以在docs.jboss.org/author/display/WFLY9/Logging+Configuration查看。
第五章. 使用 CLI 管理数据源子系统
在本章中,您将涵盖以下主题:
-
准备非 JDBC-4 兼容的驱动程序
-
创建和删除数据源
-
检查数据源连接
-
读取数据源的统计信息
-
设置连接池
-
创建和删除 XA 数据源
简介
在本章中,您将学习如何使用 CLI 管理 WildFly 数据源子系统。这独立于 WildFly 的操作模式。因此,为了便于配置,我们将以独立模式运行 WildFly。
数据源是应用程序用于连接数据库的组件。数据源反过来使用驱动程序与底层数据库正确通信。因此,为了 WildFly 提供数据库集成,它需要一个驱动程序和一个数据源。
WildFly 自带默认配置,即ExampleDS数据源,绑定到“H2”驱动程序。H2 是一个 Java SQL 数据库,主要用于测试目的的内存数据库,并支持 SQL。
WildFly 自动识别任何符合 JDBC 4 规范的驱动程序。因此,驱动程序可以作为模块(即静态部署)安装,或者它可以作为任何正常应用程序部署。
在第一种方法中,您必须在所有需要此类驱动程序的应用程序和配置的主机上复制驱动模块安装。另一方面,通过在域模式下使用动态部署,只需一个命令或一个点击,就可以将驱动程序传播到所有服务器组,从而传播到所有可用的主机。
数据源也可以通过使用-ds.xml文件以传统方式部署。尽管这种替代方案在从 JBoss 5 和 JBoss 6 迁移时非常有帮助,但在配置生产环境时并不是最佳选择。这是因为数据源不能通过管理界面(如 CLI 和 Web 管理控制台)进行更改。此外,数据源不能配置为利用安全关注,如安全域和密码保险库(我们将在本书的后面讨论这些主题)。
准备非 JDBC-4 兼容的驱动程序
在本配方中,我们将学习如何使 JDBC 驱动程序符合版本 4。这是安装驱动程序并将其提供给您的数据源,以及您的应用程序所必需的。
准备工作
如果您已经有一个符合 JDBC 4 规范的驱动程序,您可以跳过此配方;否则,我假设您没有 JDBC 驱动程序,我将在整个配方中将其称为non-jdbc-4-driver.jar。
如何操作...
要使您的驱动程序符合 JDBC 4 规范,您只需按照以下步骤添加一个文件:
-
创建一个临时文件夹并进入其中。
-
将您的
non-jdbc-4-driver.jar驱动程序文件放入其中。 -
创建一个
META-INF/services目录。 -
创建一个名为
java.sql.Driver的文件并将其放置在第 3 步指定的文件夹中。 -
编辑文件
java.sqlDriver并输入一行包含实现驱动程序类的完全限定名称。 -
从步骤
1的空文件夹开始,使用 JAR 工具更新文件non-jdbc-4-driver.jar,如下所示:jar -uf non-jdbc-4-driver.jar META-INF/services/java.sql.Driver
现在,你已经准备好安装或部署你的新 JDBC 4 兼容驱动程序了。
创建和删除数据源
应用程序附带的最常用功能之一是将状态(如用户信息、订单等)持久化到数据库中的可能性。在这个菜谱中,我们将学习如何配置数据源和 JDBC 4 兼容驱动程序。
准备工作
在我们开始之前,我们需要在我们的计算机上安装一个运行的数据库,或者在远程服务器上。在这个菜谱中,我们将使用一个运行在本地的 MySQL 数据库,监听端口 3306——MySQL 数据库服务器的安装和配置超出了本书的范围。
声明一个新的数据源包括两个独立的步骤:
-
安装 JDBC 驱动程序。
-
配置数据源本身。
第一步可以有两种不同的方法。你可以通过部署它作为一个普通工件来安装 JDBC 驱动程序,或者你可以将其安装为 WildFly 模块。
首先,从 dev.mysql.com/downloads/connector/j/ 下载最新的 MySQL JDBC 连接器版本(根据本文写作时,版本是 "5.1.35"),并将其放置在你的 home 文件夹下的 WildFly Cookbook 目录 WFC 中。
要将 JDBC 驱动程序作为 WildFly 模块安装,我们需要执行以下操作:
-
进入模块文件夹
$WILDFLY_HOME/modules/system/layers/base并创建一个与你的模块名称匹配的子文件夹结构,如下所示:$ cd $WILDFLY_HOME/modules/system/layers/base $ mkdir -p jdbc/mysql/main -
将你之前下载的
~/WFC/mysql-connector-java-5.1.35-bin.jar文件放入main文件夹。 -
在
main文件夹中,创建一个名为module.xml的文件,内容如下:<module name="jdbc.mysql"> <resources> <resource-root path="mysql-connector-java-5.1.35-bin.jar"/> </resources> <dependencies> <module name="javax.api"/> <module name="javax.transaction.api"/> </dependencies> </module>如你所见,我已经强调了与子文件夹结构匹配的模块名称——除了主文件夹,它仅对应于
version。太好了!现在我们准备好将驱动程序添加到数据源子系统了。
-
启动 WildFly 并执行以下操作:
$ ./bin/jboss-cli.sh --connect [standalone@localhost:9990 /] /subsystem=datasources/jdbc-driver=mysql:add(driver-module-name=jdbc.mysql, driver-name=mysql) {"outcome" => "success"} [standalone@localhost:9990 /] -
在我们继续之前,请在你的运行 MySQL 服务器实例上创建一个名为
wildflycookbook的数据库,如下所示:CREATE DATABASE IF NOT EXISTS wildflycookbook
现在我们已经安装了 JDBC 驱动程序,我们准备好配置我们的数据源了。
如何做到这一点...
-
在一个运行的 WildFly 服务器上,打开你的命令行工具并连接到 CLI:
$ ./bin/jboss-cli.sh You are disconnected at the moment. Type 'connect' to connect to the server or 'help' for the list of supported commands. [disconnected /] connect [standalone@localhost:9990 /] -
现在执行以下命令:
[standalone@localhost:9990 /] /subsystem=datasources/data-source=WildFlyCookbookDS:add(jndi-name="java:jboss/datasources/WildFlyCookbookDS", connection-url="jdbc:mysql://localhost:3306/wildflycookbook", driver-name="mysql", user-name="wildflymysql", password="wildfly-mysql-password", prepared-statements-cache-size=128, share-prepared-statements=true, blocking-timeout-wait-millis=60000, idle-timeout-minutes=20) {"outcome" => "success"} [standalone@localhost:9990 /] -
现在,在列出可用的数据源时,你应该找到我们新创建的一个:
[standalone@localhost:9990 /] ls /subsystem=datasources/data-source ExampleDS WildFlyCookbookDS [standalone@localhost:9990 /] -
如果你需要删除一个数据源,在其旁边调用
remove,如下所示:[standalone@localhost:9990 /] /subsystem=datasources/data-source=WildFlyCookbookDS:remove() {"outcome" => "success"} [standalone@localhost:9990 /] ls /subsystem=datasources/data-source ExampleDS [standalone@localhost:9990 /]
它是如何工作的...
如你所见,配置数据源并不那么困难,但要达到这个目标需要做很多工作。数据源本身只是对数据库的引用,涉及到一个连接器,即驱动程序。实际上,在 DS 配置中,我强调了与我们在准备工作部分定义的驱动程序匹配的driver-name属性。此外,我还强调了应该根据你的数据库配置更改的password属性。
还有更多...
数据源配置包含更多参数,例如定义和调整连接池的大小,但我们将在本章的专用配方中看到这些。你还将了解到你可以有一个 XA-Datasource,这实际上可以在不同的事务系统中启用分布式事务。
检查数据源连接
有时,你可能会在你的日志应用中看到错误,这是因为你的持久化存储出现了问题。在这种情况下,首先要做的是检查数据库是否正在运行,通过测试其连接来确认;你甚至可能意识到你指向了一个错误的数据库或者拼写错误了连接 URL。
准备工作
这个配方基于之前的配方,其中我们配置了一个连接到本地 MySQL 数据库的数据源,因此我们将使用 WildFlyCookBookDS 数据源来测试我们的数据库连接。只要你提供了正确的配置,你也可以使用你选择的数据源进行测试。
如何操作...
-
在一个运行的 WildFly 服务器上,打开你的命令行工具并连接到 CLI:
$ ./bin/jboss-cli.sh --connect [standalone@localhost:9990 /] -
现在执行以下命令:
[standalone@localhost:9990 /] /subsystem=datasources/data-source=WildFlyCookbookDS:test-connection-in-pool() { "outcome" => "success", "result" => [true] } [standalone@localhost:9990 /]
就这样!不是很容易吗?
读取数据源的统计信息
在这个配方中,你将学习如何启用数据源的统计信息,以检查我们是否充分利用了它,或者是否需要调整数据源,或者在最坏的情况下,与数据库进行扩展。
准备工作
为了了解我们的数据源发生了什么,让我们向数据库生成一些流量。在这种情况下,我们将使用名为datasource-traffic-generator的应用程序。要获取部署的工件,请参考第一章中的软件先决条件配方,欢迎来到 WildFly!。此外,在应用程序的源代码中,你可以找到一个 Apache JMeter(也称为 JMeter)项目,位于以下路径:datasource-traffic-generator/src/main/resources/HTTP Request Defaults.jmx。
简而言之,Apache JMeter 是一个用于进行压力测试的测试工具;实际上,我们将对datasource-traffic-generator应用程序进行压力测试,该应用程序将一些数据插入到数据库中。
注意
你可以从jmeter.apache.org/download_jmeter.cgi下载 Apache JMeter 的二进制文件。
安装相当简单:
-
只需将下载的包
apache-jmeter-2.13.zip解压到~/WFC文件夹中。 -
要运行它,从命令行导航到 JMeter 文件夹并输入以下命令:
$ ./bin/jmeter.sh如果一切顺利,你应该会看到如图所示的 JMeter 工具:
![准备就绪]()
Apache JMeter
-
此外,我们将要使用的数据库是 MySQL。在那里,创建一个名为
wildflycookbook的数据库。 -
最后,创建一个名为
USER的表,如下所示:CREATE DATABASE IF NOT EXISTS wildflycookbook CREATE TABLE wildflycookbook.user ( id BIGINT NOT NULL AUTO_INCREMENT, firstname VARCHAR(100) NOT NULL, lastname VARCHAR(100) NOT NULL, email VARCHAR(255) NOT NULL, phone VARCHAR(50), nickname VARCHAR(50), PRIMARY KEY (id), CONSTRAINT user_email UNIQUE (email) ) ENGINE=InnoDB DEFAULT CHARSET=latin1
如何操作…
-
在运行中的 WildFly 服务器上,打开您的命令行工具并连接到 CLI:
$ ./bin/jboss-cli.sh --connect [standalone@localhost:9990 /] -
现在,按照常规部署
datasource-traffic-generator.war包。 -
在 CLI 上使用以下命令启用 JDBC 和池统计信息:
[standalone@localhost:9990 /] /subsystem=datasources/data-source=WildFlyCookbookDS/statistics=jdbc:write-attribute(name=statistics-enabled,value=true) {"outcome" => "success"} [standalone@localhost:9990 /] /subsystem=datasources/data-source=WildFlyCookbookDS/statistics=pool:write-attribute(name=statistics-enabled,value=true) {"outcome" => "success"} -
打开
datasource-traffic-generator/src/main/resources/HTTP Request Defaults.jmxJMeter 项目并点击启动按钮。 -
一旦你在 JMeter 中按下播放按钮,在 WildFLy CLI 中,执行以下命令:
/subsystem=datasources/data-source=WildFlyCookbookDS/statistics=jdbc:read-resource(include-runtime=true) /subsystem=datasources/data-source=WildFlyCookbookDS/statistics=pool:read-resource(include-runtime=true)结果如图所示:
![如何操作…]()
它是如何工作的…
如您所见,我们执行了两个命令,一个用于检索jdbc级别的信息,另一个用于检索数据源中配置的pool信息。
还有更多...
而不是在 CLI 中执行两个命令,您可以使用一个命令检索更多信息。这有助于在 CLI 外解析数据;同时请注意,输出非常类似于 JSON 格式。
实际上,您可以使用 bash 脚本、python 或 Java 在 CLI 外执行 CLI 命令,然后解析结果以检索所需的信息。
要使用 bash-shell 执行 CLI 命令,请输入以下命令:
$ ./bin/jboss-cli.sh -c --command="/subsystem=datasources/data-source=WildFlyCookbookDS:read-resource(include-runtime=true,recursive=true)"
设置连接池
如前所述的食谱,有时我们需要提取一些关于数据库集成行为的信息。很多时候,当您的应用程序的并发用户数量在数百或数千时,您必须同时服务多个数据库连接。这正是我们将在这个食谱中学习的内容,使用 CLI。
准备就绪
将连接池想象成一个预先填充了最小数量可供应用程序使用的连接的桶。还有一个上限限制,它定义了池可以保持的最大连接数。池的最小和最大大小默认值分别为0和20,默认情况下prefill属性设置为false。这意味着当数据源启动时,其连接池将创建0个活跃和有效的连接,并且它可以保持多达 20 个连接。
为什么你会使用连接池?因为创建一个连接涉及到很多幕后的事情,所以有它准备可以帮助你提高性能。
如何操作…
-
在运行中的 WildFly 服务器上,打开您的命令行工具并连接到 CLI:
$ ./bin/jboss-cli.sh You are disconnected at the moment. Type 'connect' to connect to the server or 'help' for the list of supported commands. [disconnected /] connect [standalone@localhost:9990 /] -
现在执行以下命令:
[standalone@localhost:9990 /] /subsystem=datasources/data-source=WildFlyCookbookDS:write-attribute(name="min-pool-size", value="10") {"outcome" => "success"} [standalone@localhost:9990 /] /subsystem=datasources/data-source=WildFlyCookbookDS:write-attribute(name="max-pool-size", value="20") {"outcome" => "success"} [standalone@localhost:9990 /]
它是如何工作的…
之前提到的命令为我们创建了一个连接池,这样我们就可以计算从 10 到 20 的一系列可用连接。
还有更多…
我们还可以使用的一个选项是让我们的连接池预先填充连接,以增强我们的数据库连接。
-
要实现这一点,我们需要将
pool-prefill属性设置为true,如下所示:[standalone@localhost:9990 /] /subsystem=datasources/data-source=WildFlyCookbookDS:write-attribute(name="pool-prefill", value="true") { "outcome" => "success", "response-headers" => { "operation-requires-reload" => true, "process-state" => "reload-required" } } [standalone@localhost:9990 /] -
如您所见,并非所有更改都是在运行时进行的;我们经常需要重新加载 WildFly 配置,就像在这个例子中一样。要重新加载服务器(我们为此有一个配方,无论是独立模式还是域模式),请执行以下命令:
[standalone@localhost:9990 /] :reload { "outcome" => "success", "result" => undefined } [standalone@localhost:9990 /]以下图像显示了带有其新池配置的
WildFlyCookbookDS数据源:![还有更多…]()
还有另一个重要方面,值得提及关于数据源连接池。
如果你预先填充你的池以有效连接,然后数据库崩溃或重新启动会发生什么?好吧,你的连接在池中可能看起来是有效的,但底层数据库的引用已经改变,因此它们将无法查询你的数据。幸运的是,有几个参数可以帮助你解决这个问题。
首先,你需要选择一个验证连接的机制。你可以选择基于时间的方法,或者决定每次使用连接时都进行检查。
第一种方法包括设置以下属性:
-
background-validation=true -
background-validation-millis=30000—默认是0
另一方面,第二种方法包括设置以下属性:
validate-on-match=true
无论你使用哪种方法,总是将另一个设置为false。
一旦你选择了验证机制,你需要指定如何检查连接是否有效。你可以通过使用以下属性之一来实现:
-
check-valid-connection-sql -
valid-connection-checker-class
这两个属性都是数据库特定的。第一个必须包含有效的 SQL 代码(例如SELECT 1或SELECT 1 FROM DUAL)。第二个将检查算法委托给一个类。WildFly 为最常用的数据库提供了检查类,如下所示:
| 检查类 |
|---|
org.jboss.jca.adapters.jdbc.extensions.db2.DB2ValidConnectionChecker |
org.jboss.jca.adapters.jdbc.extensions.mssql.MSSQLValidConnectionChecker |
org.jboss.jca.adapters.jdbc.extensions.mysql.MySQLReplicationValidConnectionChecker |
org.jboss.jca.adapters.jdbc.extensions.mysql.MySQLValidConnectionChecker |
org.jboss.jca.adapters.jdbc.extensions.novendor.GoodForSecondsValidConnectionChecker |
org.jboss.jca.adapters.jdbc.extensions.novendor.JDBC4ValidConnectionChecker |
org.jboss.jca.adapters.jdbc.extensions.novendor.NullValidConnectionChecker |
org.jboss.jca.adapters.jdbc.extensions.novendor.SQLExceptionValidConnectionChecker |
org.jboss.jca.adapters.jdbc.extensions.oracle.OracleValidConnectionChecker |
org.jboss.jca.adapters.jdbc.extensions.postgres.PostgreSQLValidConnectionChecker |
org.jboss.jca.adapters.jdbc.extensions.sybase.SybaseValidConnectionChecker |
最后,但同样重要的是,你可以依赖两个其他属性来帮助你清理连接:stale-connection-checker-class-name 和 exception-sorter-class-name。
第一个提供了你清理陈旧连接的简单方法;你可以依赖一个通用类,以及 DB2 和 Oracle 特定类,如下所示:
| 通用/特定类 |
|---|
org.jboss.jca.adapters.jdbc.extensions.novendor.AlwaysStaleConnectionChecker |
org.jboss.jca.adapters.jdbc.extensions.novendor.NullStaleConnectionChecker |
org.jboss.jca.adapters.jdbc.extensions.db2.DB2StaleConnectionChecker |
org.jboss.jca.adapters.jdbc.extensions.oracle.OracleStaleConnectionChecker |
第二个属性为你提供了清理抛出 FATAL 异常的连接的简单方法,并且相对于你的数据库,你可以依赖以下类:
| 类 |
|---|
org.jboss.jca.adapters.jdbc.extensions.db2.DB2ExceptionSorter |
org.jboss.jca.adapters.jdbc.extensions.informix.InformixExceptionSorter |
org.jboss.jca.adapters.jdbc.extensions.mysql.MySQLExceptionSorter |
org.jboss.jca.adapters.jdbc.extensions.mssql.MSSQLExceptionSorter |
org.jboss.jca.adapters.jdbc.extensions.novendor.AlwaysExceptionSorter |
org.jboss.jca.adapters.jdbc.extensions.novendor.NullExceptionSorter |
org.jboss.jca.adapters.jdbc.extensions.oracle.OracleExceptionSorter |
org.jboss.jca.adapters.jdbc.extensions.postgres.PostgreSQLExceptionSorter |
org.jboss.jca.adapters.jdbc.extensions.sybase.SybaseExceptionSorter |
创建和删除 XA-Datasource
XA-Datasources 与普通数据源类似,只是它们需要一个不同的 driver-class-name,并且支持跨异构事务系统的分布式事务。
想象一下在线商店的经典例子:用户购买商品,商品从库存数据库中移除,并且从用户的银行账户中扣除相应金额,这是一个外部系统。这两个操作必须成功才能进行支付和发货。
这只是一个例子,为了给你一个概念;我们不会进一步深入。
准备工作
本菜谱的前提是 Creating and removing a datasource 菜谱中的 Getting ready 部分。
如何操作…
-
在 WildFly 服务器运行时,打开你的命令行工具并连接到 CLI:
$ ./bin/jboss-cli.sh --connect [standalone@localhost:9990 /] -
现在执行以下命令:
[standalone@localhost:9990 /] batch [standalone@localhost:9990 / #] /subsystem=datasources/xa-data-source=XAWildFlyCookBookDS:add(driver-name=mysql,jndi-name=java:jboss/datasources/XAWildFlyCookBookDS,use-java-context=true,new-connection-sql="select 1 from dual",no-tx-separate-pool=true,valid-connection-checker-class-name="org.jboss.jca.adapters.jdbc.extensions.mysql.MySQLValidConnectionChecker",stale-connection-checker-class-name="org.jboss.jca.adapters.jdbc.extensions.mysql.MySQLStaleConnectionChecker",min-pool-size=10,max-pool-size=25,track-statements=true,prepared-statements-cache-size=25, xa-datasource-class="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource") [standalone@localhost:9990 / #] /subsystem=datasources/xa-data-source=XAWildFlyCookBookDS/xa-datasource-properties=URL:add(value="jdbc:mysql://localhost:3306/wildflycookbook") [standalone@localhost:9990 / #] /subsystem=datasources/xa-data-source=XAWildFlyCookBookDS/xa-datasource-properties=User:add(value="root") [standalone@localhost:9990 / #] /subsystem=datasources/xa-data-source=XAWildFlyCookBookDS/xa-datasource-properties=Password:add(value="password-root") [standalone@localhost:9990 / #] run-batch The batch executed successfully [standalone@localhost:9990 /] -
现在,在列出可用的数据源时,你应该找到我们新创建的一个:
[standalone@localhost:9990 /] ls /subsystem=datasources/xa-data-source XAWildFlyCookBookDS [standalone@localhost:9990 /] -
如果你需要删除一个数据源,请在其旁边调用
remove,如下面的图片所示:![如何操作…]()
它是如何工作的…
我们首先只使用所需信息创建了一个 XA-DataSource,然后以批量模式添加了其他信息。通过这样做,我们能够将配置过程拆分,并最终找出我们出错的地方。
此外,我们指定了xa-datasource-class类,因为 XA-DataSource 需要一个实现并支持分布式事务的特殊类。驱动程序是相同的,只需指定不同的驱动类实现。
第六章 WildFly 集群
本章我们将涵盖以下主题:
-
以独立模式创建集群
-
以独立模式创建单独的集群
-
以域模式创建集群
-
以域模式创建单独的集群
-
通过 TCP 创建集群
-
使用 JGroups 工具测试 UDP 协议
简介
在本章中,你将学习如何为分布在两个或更多 WildFly 节点上的 Web 应用程序创建集群。集群是在发生故障(即服务器崩溃)的情况下继续为客户端提供服务的能力,也称为故障转移。
注意
集群旨在在应用层而不是在操作系统层实现。
例如,假设你正在填写一个长表单,包含大量步骤(步骤指的是页面)。现在假设在最后一步,服务器或 WildFly 节点崩溃了,你将不得不重新填写所有信息。当然,如果你可以选择的话,你肯定不会再使用那个网站了。顺便问一下,你会如何解决这个问题?集群就是答案。
在集群中,用户的会话会被复制到你的集群节点。所以,在发生故障的情况下,在下一个 HTTP 请求中,你将落在不同的服务器/节点上,它将像什么都没发生一样继续为你提供服务——显然,最终用户不会看到他的/她的请求是由不同的服务器/节点处理的。
在 WildFly 中,我们有两个组件(从配置文件的角度来看,它们是子系统)来完成这项工作;它们是infinispan(用于缓存数据会话)和JGroups(用于在集群节点之间传播 HTTP 会话)。Infinispan 是存储数据的组件,而 JGroups 是协调构成应用集群的节点之间通信的组件。
我们将看到如何使用不同的协议实现集群:UDP(多播和默认协议)和 TCP(单播)。这可以在jgroups子系统中进行配置。默认的是 UDP。
为了完整性,我们将在两种操作模式中尝试我们的配置:独立模式和域模式。
注意
记住,集群是 WildFly 提供的一项服务,因此它是按需激活的。因此,你需要提供一个集群感知的应用程序来激活集群。这意味着在你的web.xml文件中需要有<distributable/> XML 标签。
在本章中,你需要一个标准的 WildFly 安装和一个设置好的管理用户。如果你从这里开始,请参阅第一章,欢迎来到 WildFly!
以独立模式创建集群
在这个菜谱中,你将学习如何在本地将两个 WildFly 节点进行集群化,即在你的 PC 上。我们将使用独立模式和ha配置文件来尝试这个操作。
准备工作
对于这个配方,我们需要名为 cluster-test 的 cluster-aware 应用程序,你可以在我的 GitHub 仓库中找到它。如果你跳过了 第二章 中的 使用部署文件夹管理应用程序 配方,以独立模式运行 WildFly,请参考它以下载你需要的所有源代码和项目。
要构建应用程序,请输入以下命令:
$ cd ~/WFC/github/wildfly-cookbook
$ cd cluster-test
$ mvn -e clean package
如何操作...
从 WildFly 安装目录 $WILDFLY_HOME,让我们创建两个文件夹,每个文件夹代表一个服务器节点:
-
打开一个终端并执行以下命令:
$ cd $WILDFLY_HOME $ cp -a standalone cl-std-node-1 $ cp -a standalone cl-std-node-2 -
现在,让我们将
cluster-test.war应用程序复制到我们刚刚创建的每个节点的deployments文件夹中。执行以下命令:$ cp ~/WFC/github/wildfly-cookbook/cluster-test/target/cluster-test.war cl-std-node-1/deployments/ $ cp ~/WFC/github/wildfly-cookbook/cluster-test/target/cluster-test.war cl-std-node-2/deployments/ -
我们几乎准备好测试我们的集群了。我们需要一些配置,但不需要编辑太多,我们只需将命令行参数传递给
standalone.sh脚本。让我们这么做:$ ./bin/standalone.sh -Djboss.server.base.dir=cl-std-node-1 --server-config=standalone-ha.xml -Djboss.socket.binding.port-offset=100 -Djboss.node.name=node-1 ... 02:26:22,755 INFO [org.jboss.as.server.deployment] (MSC service thread 1-14) WFLYSRV0027: Starting deployment of "cluster-test.war" (runtime-name: "cluster-test.war") 02:26:22,910 INFO [org.jboss.ws.common.management] (MSC service thread 1-2) JBWS022052: Starting JBoss Web Services - Stack CXF Server 5.0.0.Beta3 02:26:23,843 INFO [stdout] (MSC service thread 1-6) 02:26:23,843 INFO [stdout] (MSC service thread 1-6) ------------------------------------------------------------------- 02:26:23,843 INFO [stdout] (MSC service thread 1-6) GMS: address=node-1, cluster=ee, physical address=127.0.0.1:55300 02:26:23,843 INFO [stdout] (MSC service thread 1-6) ------------------------------------------------------------------- 02:26:27,145 INFO [org.infinispan.remoting.transport.jgroups.JGroupsTransport] (ServerService Thread Pool -- 64) ISPN000078: Starting JGroups channel web 02:26:27,154 INFO [org.infinispan.remoting.transport.jgroups.JGroupsTransport] (ServerService Thread Pool -- 64) ISPN000094: Received new cluster view for channel web: [node-1|0] (1) [node-1] 02:26:27,155 INFO [org.infinispan.remoting.transport.jgroups.JGroupsTransport] (ServerService Thread Pool -- 64) ISPN000079: Channel web local address is node-1, physical addresses are [127.0.0.1:55300] 02:26:27,163 INFO [org.infinispan.factories.GlobalComponentRegistry] (ServerService Thread Pool -- 64) ISPN000128: Infinispan version: Infinispan 'Hoptimus Prime' 7.1.1.Final 02:26:27,706 INFO [org.jboss.as.clustering.infinispan] (ServerService Thread Pool -- 64) WFLYCLINF0002: Started dist cache from web container 02:26:27,706 INFO [org.jboss.as.clustering.infinispan] (ServerService Thread Pool -- 62) WFLYCLINF0002: Started cluster-test.war cache from web container 02:26:27,970 INFO [org.wildfly.extension.undertow] (MSC service thread 1-16) WFLYUT0021: Registered web context: /cluster-test 02:26:28,006 INFO [org.jboss.as.server] (ServerService Thread Pool -- 36) WFLYSRV0010: Deployed "cluster-test.war" (runtime-name : "cluster-test.war") 02:26:28,182 INFO [org.jboss.as] (Controller Boot Thread) WFLYSRV0060: Http management interface listening on http://127.0.0.1:10090/management 02:26:28,183 INFO [org.jboss.as] (Controller Boot Thread) WFLYSRV0051: Admin console listening on http://127.0.0.1:10090 02:26:28,183 INFO [org.jboss.as] (Controller Boot Thread) WFLYSRV0025: WildFly Full 9.0.0.Beta2 (WildFly Core 1.0.0.Beta2) started in 8645ms - Started 334 of 516 services (285 services are lazy, passive or on-demand)在前面的命令中,我只强调了相关的输出;这是为了给你一个清晰的聚类服务视图。
-
让我们打开一个浏览器,将其指向 URL
http://127.0.0.1:8180/cluster-test。现在刷新页面几次。你应该会看到以下截图:![如何操作...]()
"node-1" 上运行的 "cluster-test" 应用程序
在日志中,你应该会找到以下语句:
15:20:25,118 INFO [stdout] (default task-3) ********************************+ 15:20:25,119 INFO [stdout] (default task-3) Visitor(s): 0 15:20:25,119 INFO [stdout] (default task-3) ********************************+ 15:20:25,228 INFO [stdout] (default task-4) ********************************+ 15:20:25,229 INFO [stdout] (default task-4) Visitor(s): 1 15:20:25,229 INFO [stdout] (default task-4) ********************************+ 15:20:25,291 INFO [stdout] (default task-5) ********************************+ 15:20:25,291 INFO [stdout] (default task-5) Visitor(s): 2 15:20:25,291 INFO [stdout] (default task-5) ********************************+ 15:20:25,315 INFO [stdout] (default task-6) ********************************+ 15:20:25,315 INFO [stdout] (default task-6) Visitor(s): 3 15:20:25,316 INFO [stdout] (default task-6) ********************************+ 15:20:25,608 INFO [stdout] (default task-7) ********************************+ 15:20:25,609 INFO [stdout] (default task-7) Visitor(s): 4 15:20:25,609 INFO [stdout] (default task-7) ********************************+现在一切顺利,让我们启动第二个节点并看看会发生什么。
-
在一个新的终端中,执行以下命令:
$ cd $JBOSS_HOME $ ./bin/standalone.sh -Djboss.server.base.dir=cl-std-node-2 --server-config=standalone-ha.xml -Djboss.socket.binding.port-offset=200 -Djboss.node.name=node-2 … 03:13:44,381 INFO [org.jboss.as.server.deployment] (MSC service thread 1-14) WFLYSRV0027: Starting deployment of "cluster-test.war" (runtime-name: "cluster-test.war") 03:13:44,548 INFO [org.jboss.ws.common.management] (MSC service thread 1-15) JBWS022052: Starting JBoss Web Services - Stack CXF Server 5.0.0.Beta3 03:13:45,075 INFO [stdout] (MSC service thread 1-6) 03:13:45,075 INFO [stdout] (MSC service thread 1-6) ------------------------------------------------------------------- 03:13:45,075 INFO [stdout] (MSC service thread 1-6) GMS: address=node-2, cluster=ee, physical address=127.0.0.1:55400 03:13:45,077 INFO [stdout] (MSC service thread 1-6) ------------------------------------------------------------------- 03:13:45,153 INFO [org.infinispan.remoting.transport.jgroups.JGroupsTransport] (Incoming-2,ee,node-1) ISPN000094: Received new cluster view for channel web: [node-1|1] (2) [node-1, node-2] 03:13:45,675 INFO [org.infinispan.remoting.transport.jgroups.JGroupsTransport] (ServerService Thread Pool -- 62) ISPN000078: Starting JGroups channel web 03:13:45,679 INFO [org.infinispan.remoting.transport.jgroups.JGroupsTransport] (ServerService Thread Pool -- 62) ISPN000094: Received new cluster view for channel web: [node-1|1] (2) [node-1, node-2] 03:13:45,680 INFO [org.infinispan.remoting.transport.jgroups.JGroupsTransport] (ServerService Thread Pool -- 62) ISPN000079: Channel web local address is node-2, physical addresses are [127.0.0.1:55400] 03:13:45,747 INFO [org.infinispan.factories.GlobalComponentRegistry] (ServerService Thread Pool -- 62) ISPN000128: Infinispan version: Infinispan 'Hoptimus Prime' 7.1.1.Final 03:13:46,187 INFO [org.infinispan.CLUSTER] (remote-thread--p3-t1) ISPN000310: Starting cluster-wide rebalance for cache cluster-test.war, topology CacheTopology{id=1, rebalanceId=1, currentCH=DefaultConsistentHash{ns = 80, owners = (1)[node-1: 80+0]}, pendingCH=DefaultConsistentHash{ns = 80, owners = (2)[node-1: 40+40, node-2: 40+40]}, unionCH=null, actualMembers=[node-1, node-2]} 03:13:46,187 INFO [org.infinispan.CLUSTER] (remote-thread--p3-t2) ISPN000310: Starting cluster-wide rebalance for cache dist, topology CacheTopology{id=1, rebalanceId=1, currentCH=DefaultConsistentHash{ns = 80, owners = (1)[node-1: 80+0]}, pendingCH=DefaultConsistentHash{ns = 80, owners = (2)[node-1: 40+40, node-2: 40+40]}, unionCH=null, actualMembers=[node-1, node-2]} 03:13:46,204 INFO [org.infinispan.CLUSTER] (transport-thread--p2-t10) ISPN000328: Finished local rebalance for cache cluster-test.war on node node-1, topology id = 1 03:13:46,209 INFO [org.infinispan.CLUSTER] (transport-thread--p2-t11) ISPN000328: Finished local rebalance for cache dist on node node-1, topology id = 1 03:13:46,416 INFO [org.infinispan.CLUSTER] (remote-thread--p3-t1) ISPN000328: Finished local rebalance for cache dist on node node-2, topology id = 1 03:13:46,431 INFO [org.infinispan.CLUSTER] (remote-thread--p3-t2) ISPN000328: Finished local rebalance for cache cluster-test.war on node node-2, topology id = 1 03:13:46,461 INFO [org.jboss.as.clustering.infinispan] (ServerService Thread Pool -- 62) WFLYCLINF0002: Started dist cache from web container 03:13:46,461 INFO [org.jboss.as.clustering.infinispan] (ServerService Thread Pool -- 64) WFLYCLINF0002: Started cluster-test.war cache from web container 03:13:46,662 INFO [org.wildfly.extension.undertow] (MSC service thread 1-3) WFLYUT0021: Registered web context: /cluster-test 03:13:46,700 INFO [org.jboss.as.server] (ServerService Thread Pool -- 36) WFLYSRV0010: Deployed "cluster-test.war" (runtime-name : "cluster-test.war") 03:13:46,863 INFO [org.jboss.as] (Controller Boot Thread) WFLYSRV0060: Http management interface listening on http://127.0.0.1:10190/management 03:13:46,864 INFO [org.jboss.as] (Controller Boot Thread) WFLYSRV0051: Admin console listening on http://127.0.0.1:10190 03:13:46,864 INFO [org.jboss.as] (Controller Boot Thread) WFLYSRV0025: WildFly Full 9.0.0.Beta2 (WildFly Core 1.0.0.Beta2) started in 5424ms - Started 334 of 516 services (285 services are lazy, passive or on-demand)在前面的命令行输出中,我只强调了相关的输出;这是为了给你一个清晰的聚类服务视图。与
node-1不同,我们可以看到现在集群由两个成员组成:node-1和node-2。 -
现在,让我们尝试将同一个浏览器窗口指向 URL
http://127.0.0.1:8280/cluster-test。你应该会看到如下内容:![如何操作...]()
"node-2" 上运行的 "cluster-test" 应用程序
如你所见,第二个节点从我们在
node-1中停止的地方继续计数。太好了,我们的集群正在工作!!
它是如何工作的...
让我们分析一下我们做了什么以及为什么它不需要太多配置就能工作。除了 node-1 的 standalone.sh 脚本外,我们还指定了一些参数,例如:
-
jboss.server.base.dir=cl-std-node-1:需要指定我们的基础目录作为起始文件夹以检索所有配置文件。 -
--server-config=standalone-ha.xml:需要指定具有ha配置文件的服务器配置文件。 -
jboss.socket.binding.port-offset=100:需要指定端口偏移量(对于node-2为200)。对于第一个节点,我们可以跳过这个步骤,但我喜欢看到序列:1、2、3、4...n,在这种情况下将是8180、8280、8380,以此类推。 -
jboss.node.name=node-1:需要唯一标识集群内的节点(显然,对于第二个节点是node-2)。
这就是我们构建集群所需做的全部工作。这是因为 WildFly 的默认配置,特别是子系统jgroups的配置。让我们看看它的默认设置:
<subsystem >
<channels default="ee">
<channel name="ee"/>
</channels>
<stacks default="udp">
<stack name="udp">
<transport type="UDP" socket-binding="jgroups-udp"/>
<protocol type="PING"/>
<protocol type="MERGE3"/>
<protocol type="FD_SOCK" socket-binding="jgroups-udp-fd"/>
<protocol type="FD_ALL"/>
<protocol type="VERIFY_SUSPECT"/>
<protocol type="pbcast.NAKACK2"/>
<protocol type="UNICAST3"/>
<protocol type="pbcast.STABLE"/>
<protocol type="pbcast.GMS"/>
<protocol type="UFC"/>
<protocol type="MFC"/>
<protocol type="FRAG2"/>
<protocol type="RSVP"/>
</stack>
<stack name="tcp">
<transport type="TCP" socket-binding="jgroups-tcp"/>
<protocol type="MPING" socket-binding="jgroups-mping"/>
<protocol type="MERGE3"/>
<protocol type="FD_SOCK" socket-binding="jgroups-tcp-fd"/>
<protocol type="FD"/>
<protocol type="VERIFY_SUSPECT"/>
<protocol type="pbcast.NAKACK2"/>
<protocol type="UNICAST3"/>
<protocol type="pbcast.STABLE"/>
<protocol type="pbcast.GMS"/>
<protocol type="MFC"/>
<protocol type="FRAG2"/>
<protocol type="RSVP"/>
</stack>
</stacks>
</subsystem>
因此,用于集群传输的默认协议是 UDP(见强调的代码)。此 UDP 设置在standalone-ha.xml文件中指定的socket-binding-group内有额外的配置,如下所示:
<socket-binding-group name="standard-sockets" default-interface="public" port-offset="${jboss.socket.binding.port-offset:0}">
<socket-binding name="management-http" interface="management" port="${jboss.management.http.port:9990}"/>
<socket-binding name="management-https" interface="management" port="${jboss.management.https.port:9993}"/>
<socket-binding name="ajp" port="${jboss.ajp.port:8009}"/>
<socket-binding name="http" port="${jboss.http.port:8080}"/>
<socket-binding name="https" port="${jboss.https.port:8443}"/>
<socket-binding name="jgroups-mping" port="0" multicast-address="${jboss.default.multicast.address:230.0.0.4}" multicast-port="45700"/>
<socket-binding name="jgroups-tcp" port="7600"/>
<socket-binding name="jgroups-tcp-fd" port="57600"/>
<socket-binding name="jgroups-udp" port="55200" multicast-address="${jboss.default.multicast.address:230.0.0.4}" multicast-port="45688"/>
<socket-binding name="jgroups-udp-fd" port="54200"/>
<socket-binding name="modcluster" port="0" multicast-address="224.0.1.105" multicast-port="23364"/>
<socket-binding name="txn-recovery-environment" port="4712"/>
<socket-binding name="txn-status-manager" port="4713"/>
<outbound-socket-binding name="mail-smtp">
<remote-destination host="localhost" port="25"/>
</outbound-socket-binding>
</socket-binding-group>
因此,默认情况下,集群中的每个成员都会在230.0.0.4地址上广播自己。此外,配置中指定的每个端口都会随着命令行脚本中指定的jboss.socket.binding.port-offset参数一起更改。
还有更多...
我们可以不使用port-offset指令来构建我们的集群,而是为每个节点使用不同的 IP 地址,但这不会正常工作。这是因为存储在 cookie 中的 HTTP 会话引用。一般来说,cookie 由一个名称(通常是JSESSIONID)、一个值(用于在服务器上引用 HTTP 会话的 ID)、一个域和一个上下文路径组成。
所有这些属性必须相同,才能向服务器上的同一 HTTP 会话发送请求,而绑定到不同 IP 的节点则不会是这样。IP 是 cookie 的域,因此它将不起作用——除非你平衡所有属性——但这将是下一章的主题。
参见
如果您在此配置中遇到任何问题,您可能存在网络问题,您可以使用本章的最后一个配方进行故障排除。
在独立模式下创建单独的集群
在这个配方中,您将学习如何配置不同且隔离的集群,在本地运行。我们将使用独立模式和ha配置文件来尝试这样做。
准备工作
对于这个配方,我们需要一个名为cluster-test的cluster-aware应用程序,您可以在我的 GitHub 仓库中找到它。如果您跳过了第二章中关于“使用部署文件夹管理应用程序”的配方,请参阅它以下载您将需要的所有源代码和项目。
要构建应用程序,请执行以下命令:
$ cd ~/WFC/github/wildfly-cookbook
$ cd cluster-test
$ mvn -e clean package
从 WildFly 安装目录$WILDFLY_HOME开始,让我们创建四个文件夹,每个文件夹代表一个服务器节点。
-
打开一个终端并执行以下命令:
$ cd $WILDFLY_HOME $ cp -a standalone cl-std-node-A1 $ cp -a standalone cl-std-node-A2 $ cp -a standalone cl-std-node-B1 $ cp -a standalone cl-std-node-B2 -
现在,让我们将
cluster-test.war应用程序复制到我们刚刚创建的每个节点的deployments文件夹中。请执行以下命令:$ cp ~/WFC/github/wildfly-cookbook/cluster-test/target/cluster-test.war cl-std-node-A1/deployments/ $ cp ~/WFC/github/wildfly-cookbook/cluster-test/target/cluster-test.war cl-std-node-A2/deployments/ $ cp ~/WFC/github/wildfly-cookbook/cluster-test/target/cluster-test.war cl-std-node-B1/deployments/ $ cp ~/WFC/github/wildfly-cookbook/cluster-test/target/cluster-test.war cl-std-node-B2/deployments/
我们几乎准备好测试我们的集群了。我们只需要一些配置传递给standalone.sh脚本,通过命令行。
节点-A1
在以下日志输出中,您可以看到已经形成了一个集群,并且一个名为node-A1的成员加入了它:
$ ./bin/standalone.sh -Djboss.server.base.dir=cl-std-node-A1 --server-config=standalone-ha.xml -Djboss.socket.binding.port-offset=100 -Djboss.node.name=node-A1
...
03:41:27,703 INFO [stdout] (MSC service thread 1-13)
03:41:27,703 INFO [stdout] (MSC service thread 1-13) -------------------------------------------------------------------
03:41:27,703 INFO [stdout] (MSC service thread 1-13) GMS: address=node-A1, cluster=ee, physical address=127.0.0.1:55300
03:41:27,704 INFO [stdout] (MSC service thread 1-13) -------------------------------------------------------------------
03:41:31,065 INFO [org.infinispan.remoting.transport.jgroups.JGroupsTransport] (ServerService Thread Pool -- 62) ISPN000078: Starting JGroups channel web
03:41:31,073 INFO [org.infinispan.remoting.transport.jgroups.JGroupsTransport] (ServerService Thread Pool -- 62) ISPN000094: Received new cluster view for channel web: [node-A1|0] (1) [node-A1]
03:41:31,075 INFO [org.infinispan.remoting.transport.jgroups.JGroupsTransport] (ServerService Thread Pool -- 62) ISPN000079: Channel web local address is node-A1, physical addresses are [127.0.0.1:55300]
...
节点-A2
在以下日志输出中,您可以看到一个名为node-A2的成员与另一个名为node-A1的成员一起加入了一个集群:
$ ./bin/standalone.sh -Djboss.server.base.dir=cl-std-node-A2 --server-config=standalone-ha.xml -Djboss.socket.binding.port-offset=200 -Djboss.node.name=node-A2
...
03:43:27,309 INFO [stdout] (MSC service thread 1-5)
03:43:27,309 INFO [stdout] (MSC service thread 1-5) -------------------------------------------------------------------
03:43:27,310 INFO [stdout] (MSC service thread 1-5) GMS: address=node-A2, cluster=ee, physical address=127.0.0.1:55400
03:43:27,310 INFO [stdout] (MSC service thread 1-5) -------------------------------------------------------------------
03:43:27,672 INFO [org.infinispan.remoting.transport.jgroups.JGroupsTransport] (ServerService Thread Pool -- 62) ISPN000078: Starting JGroups channel web
03:43:27,681 INFO [org.infinispan.remoting.transport.jgroups.JGroupsTransport] (ServerService Thread Pool -- 62) ISPN000094: Received new cluster view for channel web: [node-A1|1] (2) [node-A1, node-A2]
03:43:27,683 INFO [org.infinispan.remoting.transport.jgroups.JGroupsTransport] (ServerService Thread Pool -- 62) ISPN000079: Channel web local address is node-A2, physical addresses are [127.0.0.1:55400]
...
节点-B1
在以下日志输出中,你可以看到已经形成了一个集群,并且一个名为node-B1的成员加入了它。我们没有看到任何node-Ax成员,所以我们形成了一个不同的集群:
$ ./bin/standalone.sh -Djboss.server.base.dir=cl-std-node-B1 --server-config=standalone-ha.xml -Djboss.socket.binding.port-offset=300 -Djboss.node.name=node-B1 -Djboss.default.multicast.address=230.0.0.5
...
03:44:59,778 INFO [stdout] (MSC service thread 1-3)
03:44:59,778 INFO [stdout] (MSC service thread 1-3) -------------------------------------------------------------------
03:44:59,779 INFO [stdout] (MSC service thread 1-3) GMS: address=node-B1, cluster=ee, physical address=127.0.0.1:55500
03:44:59,779 INFO [stdout] (MSC service thread 1-3) -------------------------------------------------------------------
03:45:03,810 INFO [org.infinispan.remoting.transport.jgroups.JGroupsTransport] (ServerService Thread Pool -- 62) ISPN000078: Starting JGroups channel web
03:45:03,818 INFO [org.infinispan.remoting.transport.jgroups.JGroupsTransport] (ServerService Thread Pool -- 62) ISPN000094: Received new cluster view for channel web: [node-B1|0] (1) [node-B1]
03:45:03,819 INFO [org.infinispan.remoting.transport.jgroups.JGroupsTransport] (ServerService Thread Pool -- 62) ISPN000079: Channel web local address is node-B1, physical addresses are [127.0.0.1:55500]
...
节点-B2
在以下日志输出中,你可以看到一个名为node-B2的成员与另一个名为node-B1的成员一起加入了一个集群:
$ ./bin/standalone.sh -Djboss.server.base.dir=cl-std-node-B2 --server-config=standalone-ha.xml -Djboss.socket.binding.port-offset=400 -Djboss.node.name=node-B2 -Djboss.default.multicast.address=230.0.0.5
...
03:46:32,480 INFO [stdout] (MSC service thread 1-14)
03:46:32,481 INFO [stdout] (MSC service thread 1-14) -------------------------------------------------------------------
03:46:32,481 INFO [stdout] (MSC service thread 1-14) GMS: address=node-B2, cluster=ee, physical address=127.0.0.1:55600
03:46:32,481 INFO [stdout] (MSC service thread 1-14) -------------------------------------------------------------------
03:46:35,068 INFO [org.infinispan.remoting.transport.jgroups.JGroupsTransport] (ServerService Thread Pool -- 62) ISPN000078: Starting JGroups channel web
03:46:35,081 INFO [org.infinispan.remoting.transport.jgroups.JGroupsTransport] (ServerService Thread Pool -- 62) ISPN000094: Received new cluster view for channel web: [node-B1|1] (2) [node-B1, node-B2]
03:46:35,082 INFO [org.infinispan.remoting.transport.jgroups.JGroupsTransport] (ServerService Thread Pool -- 62) ISPN000079: Channel web local address is node-B2, physical addresses are [127.0.0.1:55600]
...
如何操作…
现在我们已经启动了所有 WildFly 节点并形成了两个不同的集群,让我们用我们的优秀cluster-test应用程序来测试它们:
-
打开你的浏览器,将其指向以下位置:
http://127.0.0.1:8180/cluster-test。 -
刷新页面几次。在浏览器窗口中,你应该会看到以下类似截图:
![如何操作…]()
在“node-A1”上运行的“cluster-test”应用程序
在
node-A1日志中,你应该找到以下语句:17:07:15,429 INFO [stdout] (default task-1) ********************************+ 17:07:15,429 INFO [stdout] (default task-1) Visitor(s): 0 17:07:15,430 INFO [stdout] (default task-1) ********************************+ 17:07:16,853 INFO [stdout] (default task-2) ********************************+ 17:07:16,854 INFO [stdout] (default task-2) Visitor(s): 1 17:07:16,854 INFO [stdout] (default task-2) ********************************+ 17:07:17,271 INFO [stdout] (default task-3) ********************************+ 17:07:17,273 INFO [stdout] (default task-3) Visitor(s): 2 17:07:17,273 INFO [stdout] (default task-3) ********************************+ 17:07:17,693 INFO [stdout] (default task-4) ********************************+ 17:07:17,694 INFO [stdout] (default task-4) Visitor(s): 3 17:07:17,695 INFO [stdout] (default task-4) ********************************+ 17:07:18,208 INFO [stdout] (default task-5) ********************************+ 17:07:18,209 INFO [stdout] (default task-5) Visitor(s): 4 17:07:18,209 INFO [stdout] (default task-5) ********************************+ -
现在,让我们尝试将相同的浏览器窗口指向 URL
http://127.0.0.1:8280/cluster-test。你应该会看到以下类似截图:![如何操作…]()
在“node-A2”上运行的“cluster-test”应用程序
如你所见,第二个节点从我们在
node-A1停止的地方继续计数。在node-A2日志中,你应该找到以下语句:17:10:29,776 INFO [stdout] (default task-1) ********************************+ 17:10:29,777 INFO [stdout] (default task-1) Visitor(s): 5 17:10:29,777 INFO [stdout] (default task-1) ********************************+好的,集群
A正在运行。
现在我们尝试节点B的其他 URL:
-
在同一个浏览器窗口中,指向地址
http://127.0.0.1:8380/cluster-test。在浏览器窗口中,你应该会看到以下类似图像:![如何操作…]()
在“node-B1”上运行的“cluster-test”应用程序
如你所见,应用程序从
0(零)开始计数。node-B1日志应该有如下语句:17:12:59,978 INFO [stdout] (default task-1) ********************************+ 17:12:59,979 INFO [stdout] (default task-1) Visitor(s): 0 17:12:59,980 INFO [stdout] (default task-1) ********************************+ -
现在,让我们尝试将相同的浏览器窗口指向以下 URL:
http://127.0.0.1:8480/cluster-test。你应该会看到以下截图:![如何操作…]()
在“node-B2”上运行的“cluster-test”应用程序
在
node-B2日志中,你应该找到以下语句:17:13:52,841 INFO [stdout] (default task-1) ********************************+ 17:13:52,841 INFO [stdout] (default task-1) Visitor(s): 1 17:13:52,842 INFO [stdout] (default task-1) ********************************+太好了,集群
B也正在运行!现在,尝试在不同的 URL 之间切换,看看集群是否正确响应。
它是如何工作的…
让我们分析我们所做的,以及为什么它不需要太多配置就能工作。除了node-A1和node-A2的standalone.sh脚本外,我们还指定了一些参数:
-
jboss.server.base.dir=cl-std-node-A1:需要指定我们的基础目录作为起始文件夹以检索所有配置文件(cl-std-node-A2用于node-A2) -
--server-config=standalone-ha.xml:需要指定具有ha配置文件的服务器配置文件 -
jboss.socket.binding.port-offset=100:需要指定端口偏移量(对于“node-2”是 200)
-
jboss.node.name=node-A1:需要唯一标识集群内的节点(显然对于第二个A节点是node-A2)
这就是我们为节点A创建集群所需做的所有事情。
为了创建节点B集群,我们需要指定几乎相同的参数,加上jboss.default.multicast.address,其值为230.0.0.5。默认的组播地址值为230.0.0.4,然后由A节点使用。这使得我们能够创建两个不同的集群:集群A的成员将通过230.0.0.4地址进行通信,而集群B的成员将通过230.0.0.5地址进行通信。
在域模式下创建集群
在这个菜谱中,你将学习如何在本地将两个 WildFly 节点进行集群,也就是说,在你的电脑上。我们将使用域模式和ha配置文件来尝试这个操作。
准备工作
对于这个菜谱,我们需要一个名为cluster-test的cluster-aware应用程序,你可以在我的 GitHub 仓库中找到它。如果你跳过了第二章中关于使用部署文件夹管理应用程序的菜谱,请参考它以下载你需要的所有源代码和项目。
要构建应用程序,请运行以下命令:
$ cd ~/WFC/github/wildfly-cookbook
$ cd cluster-test
$ mvn -e clean package
如何操作...
从 WildFly 安装目录$WILDFLY_HOME开始,让我们创建两个文件夹;一个代表domain-controller,另一个代表主机(我们将在同一主机内运行两个实例)。
打开一个终端并执行以下命令:
$ cd $WILDFLY_HOME
$ cp -a domain cl-dmn-master
$ cp -a domain cl-dmn-host-1
主机
现在,让我们使用放置在cl-dmn-master文件夹中的domain.xml和host.xml文件来配置我们的域控制器。
编辑domain.xml文件,并将<server-groups>...</server-groups>标签定义替换为以下内容:
<server-groups>
<server-group name="cluster-REST-app" profile="ha">
<jvm name="default">
<heap size="512m" max-size="512m"/>
</jvm>
<socket-binding-group ref="ha-sockets"/>
</server-group>
</server-groups>
再次,为了使用集群,我们需要使用ha配置文件,我在server-group的profile属性中提到了它。此外,我们还需要通过ref属性引用适当的socket-binding-group,在这种情况下值为ha-sockets。
小贴士
遵循以下规则:始终正确命名服务器组;不要将它们命名为"server-A"、"server-1"或类似名称,否则当你开始管理越来越多的服务器时,你会感到困惑。
现在,让我们编辑host.xml文件,以便只包含domain-controller而没有任何正在运行的主机。
以下是需要采取的步骤:
-
将主机命名为
master:<host name="master" > -
将
<domain-controller>...</domain-controller>标签定义替换为以下内容:<domain-controller> <local/> </domain-controller> -
删除
<servers>标签定义。好的,我们已经完成了
domain-controller的配置。让我们运行一下。 -
打开一个终端并执行以下命令:
$ cd $WILDFLY_HOME $ ./bin/domain.sh -Djboss.domain.base.dir=cl-dmn-master
现在我们可以配置将成为集群一部分的主机。
主机-1
首先,让我们禁用cl-dmn-host-1文件夹中现有的domain.xml。
打开一个终端并执行以下命令:
$ cd $WILDFLY_HOME
$ cd cl-dmn-host-1
$ mv configuration/domain.xml configuration/domain.xml.unused
通过这样做,文件在启动时将不会被读取。现在,让我们使用放置在cl-dmn-host-1文件夹中的host.xml文件来配置我们的主机控制器。
编辑host.xml文件并按照以下步骤操作:
-
将主机命名为
host-1:<host name="host-1" > -
将
<management-interfaces>...</management-interfaces>标签定义替换为<management>标签内的以下内容:<management-interfaces> <native-interface security-realm="ManagementRealm"> <socket interface="management" port="${jboss.management.native.port:19999}"/> </native-interface> <http-interface security-realm="ManagementRealm" http-upgrade-enabled="true"> <socket interface="management" port="${jboss.management.http.port:19990}"/> </http-interface> </management-interfaces> -
将
<domain-controller>...</domain-controller>标签定义替换为以下内容:<domain-controller> <remote security-realm="ManagementRealm"> <discovery-options> <static-discovery name="primary" protocol="${jboss.domain.master.protocol:remote}" host="${jboss.domain.master.address}" port="${jboss.domain.master.port:9999}"/> </discovery-options> </remote> </domain-controller> -
将
<servers>...</servers>标签定义替换为以下内容:<servers> <server name="REST-server-1" group="cluster-REST-app"> <jvm name="default"> <heap size="384m" max-size="384m"/> </jvm> <socket-bindings port-offset="100"/> </server> <server name="REST-server-2" group="cluster-REST-app"> <jvm name="default"> <heap size="384m" max-size="384m"/> </jvm> <socket-bindings port-offset="200"/> </server> </servers>
注意
在这种情况下,使用索引前缀命名服务器有助于提供更多信息。例如,如果你有五个服务器,每个服务器运行两个实例,并且你在日志文件中捕获到关于REST-server-7的错误语句,那么你知道你需要查看机器编号4,对吧?
打开终端并执行以下命令:
$ cd $WILDFLY_HOME
$ ./bin/domain.sh -Djboss.domain.base.dir=cl-dmn-host-1 -Djboss.domain.master.address=127.0.0.1
现在,如果你查看host-1的日志输出,你应该已经注意到关于我们的集群没有任何信息。为什么?(你应该知道;无论如何,答案将在稍后解释。)
现在一切都已经启动并运行,让我们部署我们的应用(你得到答案了吗?)。
-
打开终端并执行以下命令:
$ cd $WILDFLY_HOME $ ./bin/jboss-cli.sh You are disconnected at the moment. Type 'connect' to connect to the server or 'help' for the list of supported commands. [disconnected /] connect [domain@localhost:9990 /] deploy cluster-test.war --server-groups=cluster-REST-app -
让我们先检查日志。在
domain-controller中,你应该看到一条声明内容已上传的语句,如下所示:[Host Controller] 20:52:54,502 INFO [org.jboss.as.repository] (management-handler-thread - 7) WFLYDR0001: Content added at location /Users/foogaro/wildfly-9.0.0.Beta2/cl-dmn-master/data/content/ee/f4c936445881ec81ccb497cd2e7a500b95a92c/content在host-1你应该看到以下语句:
[Server:REST-server-2] 21:09:41,157 INFO [stdout] (MSC service thread 1-11) [Server:REST-server-2] 21:09:41,157 INFO [stdout] (MSC service thread 1-11) ------------------------------------------------------------------- [Server:REST-server-2] 21:09:41,157 INFO [stdout] (MSC service thread 1-11) GMS: address=REST-server-2, cluster=ee, physical address=127.0.0.1:55400 [Server:REST-server-2] 21:09:41,157 INFO [stdout] (MSC service thread 1-11) ------------------------------------------------------------------- [Server:REST-server-1] 21:09:41,167 INFO [stdout] (MSC service thread 1-14) [Server:REST-server-1] 21:09:41,167 INFO [stdout] (MSC service thread 1-14) ------------------------------------------------------------------- [Server:REST-server-1] 21:09:41,168 INFO [stdout] (MSC service thread 1-14) GMS: address=REST-server-1, cluster=ee, physical address=127.0.0.1:55300 [Server:REST-server-1] 21:09:41,168 INFO [stdout] (MSC service thread 1-14) ------------------------------------------------------------------- [Server:REST-server-2] 21:09:44,576 INFO [org.infinispan.remoting.transport.jgroups.JGroupsTransport] (ServerService Thread Pool -- 21) ISPN000078: Starting JGroups channel web [Server:REST-server-1] 21:09:44,576 INFO [org.infinispan.remoting.transport.jgroups.JGroupsTransport] (ServerService Thread Pool -- 21) ISPN000078: Starting JGroups channel web [Server:REST-server-1] 21:09:44,580 INFO [org.infinispan.remoting.transport.jgroups.JGroupsTransport] (ServerService Thread Pool -- 21) ISPN000094: Received new cluster view for channel web: [REST-server-1|0] (1) [REST-server-1] [Server:REST-server-1] 21:09:44,581 INFO [org.infinispan.remoting.transport.jgroups.JGroupsTransport] (ServerService Thread Pool -- 21) ISPN000079: Channel web local address is REST-server-1, physical addresses are [127.0.0.1:55300] [Server:REST-server-2] 21:09:44,581 INFO [org.infinispan.remoting.transport.jgroups.JGroupsTransport] (ServerService Thread Pool -- 21) ISPN000094: Received new cluster view for channel web: [REST-server-2|0] (1) [REST-server-2] [Server:REST-server-2] 21:09:44,582 INFO [org.infinispan.remoting.transport.jgroups.JGroupsTransport] (ServerService Thread Pool -- 21) ISPN000079: Channel web local address is REST-server-2, physical addresses are [127.0.0.1:55400] [Server:REST-server-1] 21:09:44,588 INFO [org.infinispan.factories.GlobalComponentRegistry] (ServerService Thread Pool -- 21) ISPN000128: Infinispan version: Infinispan 'Hoptimus Prime' 7.1.1.Final [Server:REST-server-2] 21:09:44,589 INFO [org.infinispan.factories.GlobalComponentRegistry] (ServerService Thread Pool -- 21) ISPN000128: Infinispan version: Infinispan 'Hoptimus Prime' 7.1.1.Final [Server:REST-server-1] 21:09:44,793 INFO [org.jboss.as.clustering.infinispan] (ServerService Thread Pool -- 31) WFLYCLINF0002: Started cluster-test.war cache from web container [Server:REST-server-1] 21:09:44,793 INFO [org.jboss.as.clustering.infinispan] (ServerService Thread Pool -- 21) WFLYCLINF0002: Started dist cache from web container [Server:REST-server-2] 21:09:44,798 INFO [org.jboss.as.clustering.infinispan] (ServerService Thread Pool -- 29) WFLYCLINF0002: Started cluster-test.war cache from web container [Server:REST-server-2] 21:09:44,798 INFO [org.jboss.as.clustering.infinispan] (ServerService Thread Pool -- 21) WFLYCLINF0002: Started dist cache from web container [Server:REST-server-1] 21:09:45,050 INFO [org.wildfly.extension.undertow] (MSC service thread 1-7) WFLYUT0021: Registered web context: /cluster-test [Server:REST-server-2] 21:09:45,052 INFO [org.wildfly.extension.undertow] (MSC service thread 1-12) WFLYUT0021: Registered web context: /cluster-test [Server:REST-server-1] 21:09:45,119 INFO [org.jboss.as.server] (ServerService Thread Pool -- 60) WFLYSRV0010: Deployed "cluster-test.war" (runtime-name : "cluster-test.war") [Server:REST-server-2] 21:09:45,119 INFO [org.jboss.as.server] (ServerService Thread Pool -- 24) WFLYSRV0010: Deployed "cluster-test.war" (runtime-name : "cluster-test.war")看看,现在我们有了关于集群的语句日志,我们知道答案:一旦应用程序需要,集群就会被激活。
是时候使用我们的应用程序来测试我们的集群了!
-
打开你的浏览器并将它指向以下位置:
http://127.0.0.1:8180/cluster-test。刷新页面几次。在浏览器窗口中,你应该会看到以下类似截图的内容:![Host-1]()
在“host-1”上运行的“cluster-test”应用与“REST-server-1”
在
host-1的日志中,你应该找到以下语句:[Server:REST-server-1] 16:05:47,393 INFO [stdout] (default task-1) ********************************+ [Server:REST-server-1] 16:05:47,394 INFO [stdout] (default task-1) Visitor(s): 0 [Server:REST-server-1] 16:05:47,394 INFO [stdout] (default task-1) ********************************+ [Server:REST-server-1] 16:05:50,266 INFO [stdout] (default task-3) ********************************+ [Server:REST-server-1] 16:05:50,267 INFO [stdout] (default task-3) Visitor(s): 1 [Server:REST-server-1] 16:05:50,267 INFO [stdout] (default task-3) ********************************+ [Server:REST-server-1] 16:05:50,529 INFO [stdout] (default task-4) ********************************+ [Server:REST-server-1] 16:05:50,530 INFO [stdout] (default task-4) Visitor(s): 2 [Server:REST-server-1] 16:05:50,531 INFO [stdout] (default task-4) ********************************+ [Server:REST-server-1] 16:05:50,800 INFO [stdout] (default task-5) ********************************+ [Server:REST-server-1] 16:05:50,800 INFO [stdout] (default task-5) Visitor(s): 3 [Server:REST-server-1] 16:05:50,801 INFO [stdout] (default task-5) ********************************+ [Server:REST-server-1] 16:05:51,405 INFO [stdout] (default task-6) ********************************+ [Server:REST-server-1] 16:05:51,406 INFO [stdout] (default task-6) Visitor(s): 4 [Server:REST-server-1] 16:05:51,407 INFO [stdout] (default task-6) ********************************+注意日志语句后缀指示的服务器名称。
-
现在,让我们尝试将同一个浏览器窗口指向 URL
http://127.0.0.1:8280/cluster-test。你应该会看到以下类似截图的内容:![Host-1]()
在“host-1”上运行的“cluster-test”应用与“REST-server-2”
注意
如你所见,第二个节点从我们在REST-server-1停止的地方继续计数。在host-1的日志中,你应该找到以下语句:
[Server:REST-server-2] 16:11:27,734 INFO [stdout] (default task-1) ********************************+
[Server:REST-server-2] 16:11:27,734 INFO [stdout] (default task-1) Visitor(s): 5
[Server:REST-server-2] 16:11:27,734 INFO [stdout] (default task-1) ********************************+
日志后缀已更改为REST-server-2。好的,我们的集群正在正常工作。
它是如何工作的...
跳过域模式配置的细节(见第三章,在域模式下运行 WildFly),让我们分析我们所做的工作以及为什么它不需要太多配置就能工作。除了master节点的domain.sh脚本外,我们还指定了-Djboss.domain.base.dir=cl-dmn-master参数,表示我们的基础目录作为获取整个配置文件的起始文件夹。
此外,在domain.xml中,我们在server-groups的定义中指定了对ha配置文件和ha-sockets的引用。这些配置启用了集群功能。记住,只有ha和full-ha配置文件启用了集群功能。
在查看host-1端时,除了domain.sh脚本外,我们还指定了-Djboss.domain.base.dir=cl-dmn-host-1和-Djboss.domain.master.address=127.0.0.1属性,将我们的基本目录设置为起始文件夹,以检索整个配置文件,以及相对的domain-controller地址。
注意
记住,域控制器通过主机控制器将配置推送到主机,这就是为什么我们在host-1中没有配置对应项。
这就是我们创建集群所需做的所有事情。这是因为默认的 WildFly 配置,特别是子系统jgroups的配置。让我们看看它的默认设置:
<subsystem >
<channels default="ee">
<channel name="ee"/>
</channels>
<stacks default="udp">
<stack name="udp">
<transport type="UDP" socket-binding="jgroups-udp"/>
<protocol type="PING"/>
<protocol type="MERGE3"/>
<protocol type="FD_SOCK" socket-binding="jgroups-udp-fd"/>
<protocol type="FD_ALL"/>
<protocol type="VERIFY_SUSPECT"/>
<protocol type="pbcast.NAKACK2"/>
<protocol type="UNICAST3"/>
<protocol type="pbcast.STABLE"/>
<protocol type="pbcast.GMS"/>
<protocol type="UFC"/>
<protocol type="MFC"/>
<protocol type="FRAG2"/>
<protocol type="RSVP"/>
</stack>
<stack name="tcp">
<transport type="TCP" socket-binding="jgroups-tcp"/>
<protocol type="MPING" socket-binding="jgroups-mping"/>
<protocol type="MERGE3"/>
<protocol type="FD_SOCK" socket-binding="jgroups-tcp-fd"/>
<protocol type="FD"/>
<protocol type="VERIFY_SUSPECT"/>
<protocol type="pbcast.NAKACK2"/>
<protocol type="UNICAST3"/>
<protocol type="pbcast.STABLE"/>
<protocol type="pbcast.GMS"/>
<protocol type="MFC"/>
<protocol type="FRAG2"/>
<protocol type="RSVP"/>
</stack>
</stacks>
</subsystem>
因此,用于集群传输的默认协议是 UDP(见强调的代码)。这个 UDP 设置在domain.xml文件中,在名为ha-sockets的socket-binding-group内,有额外的配置,如下所示:
<socket-binding-group name="ha-sockets" default-interface="public">
<socket-binding name="ajp" port="${jboss.ajp.port:8009}"/>
<socket-binding name="http" port="${jboss.http.port:8080}"/>
<socket-binding name="https" port="${jboss.https.port:8443}"/>
<socket-binding name="jgroups-mping" port="0" multicast-address="${jboss.default.multicast.address:230.0.0.4}" multicast-port="45700"/>
<socket-binding name="jgroups-tcp" port="7600"/>
<socket-binding name="jgroups-tcp-fd" port="57600"/>
<socket-binding name="jgroups-udp" port="55200" multicast-address="${jboss.default.multicast.address:230.0.0.4}" multicast-port="45688"/>
<socket-binding name="jgroups-udp-fd" port="54200"/>
<socket-binding name="modcluster" port="0" multicast-
address="224.0.1.105" multicast-port="23364"/>
<socket-binding name="txn-recovery-environment" port="4712"/>
<socket-binding name="txn-status-manager" port="4713"/>
<outbound-socket-binding name="mail-smtp">
<remote-destination host="localhost" port="25"/>
</outbound-socket-binding>
</socket-binding-group>
因此,默认情况下,集群的每个成员都会在230.0.0.4地址上广播自己。此外,配置中指定的每个端口都会随着host.xml文件中host-1服务器中指定的<socket-bindings port-offset="XXX"/>设置一起更改。
还有更多…
我们本可以不使用port-offset指令来创建我们的集群,而是为每个节点使用不同的 IP 地址,但这不会正常工作。这是因为存储在 cookie 中的 HTTP 会话引用。一般来说,cookie 由一个名称(通常是JSESSIONID)、一个值(用于在服务器上引用 HTTP 会话的 ID)、一个域和一个上下文路径组成。
所有这些属性必须相同,才能向服务器上的同一 HTTP 会话发出请求,而对于绑定到不同 IP 的节点来说,情况并非如此。IP 是 cookie 的域,因此它将不起作用——除非您进行负载均衡——但这将在下一章中介绍。
参见
如果您遇到此配置问题,您可能存在网络问题,您可以使用本章的使用 JGroups 工具测试 UDP 协议配方进行故障排除。
在域模式下创建单独的集群
在前面的配方中,我们学习了如何创建集群。如果我们需要管理更多应用程序,每个应用程序都有自己的集群怎么办?这正是您将在本配方中学到的。我们将学习如何使用ha配置文件管理更多应用程序。
准备工作
对于这个菜谱,我们需要一个名为example的cluster-aware应用程序,你可以在我的 GitHub 仓库中找到它。如果你跳过了第二章中关于“使用部署文件夹管理应用程序”的菜谱,请参考它以下载你需要的所有源代码和项目。
要构建应用程序,执行以下命令:
$ cd ~/WFC/github/wildfly-cookbook
$ cd example
$ mvn -e clean package
如何操作...
从 WildFly 安装目录$WILDFLY_HOME开始,让我们创建三个文件夹,一个用于域控制器(始终运行域控制器本身,不包含任何其他主机),以及两个代表两个不同主机及其自己的host-controller的文件夹。
打开终端并执行以下命令(如果你遵循了前一个菜谱中的步骤,你可以跳过前两个cp命令):
$ cd $WILDFLY_HOME
$ cp -a domain cl-dmn-master
$ cp -a domain cl-dmn-host-1
$ cp -a domain cl-dmn-host-2
主
现在,让我们使用放置在cl-dmn-master文件夹中的domain.xml和host.xml文件来配置我们的域控制器。这将与之前的菜谱完全相同,以防万一。
编辑domain.xml文件,将<server-groups>...</server-groups>标签定义替换为以下内容:
<server-groups>
<server-group name="cluster-REST-app" profile="ha">
<jvm name="default">
<heap size="512m" max-size="512m"/>
</jvm>
<socket-binding-group ref="ha-sockets"/>
</server-group>
<server-group name="cluster-SOAP-app" profile="ha">
<jvm name="default">
<heap size="512m" max-size="512m"/>
</jvm>
<socket-binding-group ref="ha-sockets"/>
</server-group>
</server-groups>
再次强调,要使用集群,我们需要使用ha配置文件,我在server-group元素的profile属性中引用了它。此外,我们还需要通过ref属性引用适当的socket-binding-group,在这种情况下值为ha-sockets。
小贴士
遵循以下规则:始终正确命名服务器组,不要将它们命名为“server-A”、“server-1”或类似名称。
现在让我们编辑host.xml文件,以便只包含domain-controller而没有任何运行的主机。
这里是要遵循的步骤:
-
将主机命名为
master:<host name="master" > -
将
<domain-controller>...</domain-controller>标签定义替换为以下内容:<domain-controller> <local/> </domain-controller> -
删除
<servers>标签定义。好的,我们已经完成了 domain-controller 的配置。现在让我们运行一下。
-
打开终端并执行以下命令:
$ cd $WILDFLY_HOME $ ./bin/domain.sh -Djboss.domain.base.dir=cl-dmn-master
现在,我们可以配置将成为集群一部分的主机。
主机-1
首先,让我们禁用cl-dmn-host-1文件夹中现有的domain.xml文件。
打开终端并执行以下命令:
$ cd $WILDFLY_HOME
$ cd cl-dmn-host-1
$ mv configuration/domain.xml configuration/domain.xml.unused
通过这样做,文件将在启动时不会被读取。现在,让我们使用放置在cl-dmn-host-1文件夹中的host.xml来配置我们的主机控制器。
编辑host.xml文件,按照以下步骤操作:
-
将主机命名为
host-1:<host name="host-1" > -
将
<management-interfaces>...</management-interfaces>标签定义替换为以下内容,位于<management>标签内部:<management-interfaces> <native-interface security-realm="ManagementRealm"> <socket interface="management" port="${jboss.management.native.port:19999}"/> </native-interface> <http-interface security-realm="ManagementRealm" http-upgrade-enabled="true"> <socket interface="management" port="${jboss.management.http.port:19990}"/> </http-interface> </management-interfaces>这只在同一服务器上运行了更多管理接口时才需要。
-
将
<domain-controller>...</domain-controller>标签定义替换为以下内容:<domain-controller> <remote security-realm="ManagementRealm"> <discovery-options> <static-discovery name="primary" protocol="${jboss.domain.master.protocol:remote}" host="${jboss.domain.master.address}" port="${jboss.domain.master.port:9999}"/> </discovery-options> </remote> </domain-controller>如你所见,
jboss.domain.master.address属性没有默认值,因此我们需要以某种方式传递它。 -
将
<servers>...</servers>标签定义替换为以下内容:<servers> <server name="REST-server-1" group="cluster-REST-app"> <socket-bindings port-offset="100"/> </server> <server name="SOAP-server-1" group="cluster-SOAP-app"> <socket-bindings port-offset="200"/> </server> </servers> -
打开终端并执行以下命令:
$ cd $WILDFLY_HOME $ ./bin/domain.sh -Djboss.domain.base.dir=cl-dmn-host-1 -Djboss.domain.master.address=127.0.0.1
注意
记住,集群是在需要时激活的,也就是说,在我们安装了cluster-aware应用程序之后。
主机-2
让我们对host-2进行完全相同的事情,只需做少许调整。
打开终端并执行以下命令:
$ cd $WILDFLY_HOME
$ cd cl-dmn-host-2
$ mv configuration/domain.xml configuration/domain.xml.unused
编辑host.xml文件并按照以下步骤操作:
-
将主机命名为
host-2:<host name="host-2" > -
将
<management>标签内的<management-interfaces>...</management-interfaces>标签定义替换为以下内容:<management-interfaces> <native-interface security-realm="ManagementRealm"> <socket interface="management" port="${jboss.management.native.port:29999}"/> </native-interface> <http-interface security-realm="ManagementRealm" http-upgrade-enabled="true"> <socket interface="management" port="${jboss.management.http.port:29990}"/> </http-interface> </management-interfaces> -
将
<domain-controller>...</domain-controller>标签定义替换为以下内容:<domain-controller> <remote security-realm="ManagementRealm"> <discovery-options> <static-discovery name="primary" protocol="${jboss.domain.master.protocol:remote}" host="${jboss.domain.master.address}" port="${jboss.domain.master.port:9999}"/> </discovery-options> </remote> </domain-controller> -
将
<servers>...</servers>标签定义替换为以下内容:<servers> <server name="REST-server-2" group="cluster-REST-app"> <socket-bindings port-offset="300"/> </server> <server name="SOAP-server-2" group="cluster-SOAP-app"> <socket-bindings port-offset="400"/> </server> </servers> -
打开终端并执行以下命令:
$ cd $WILDFLY_HOME $ ./bin/domain.sh -Djboss.domain.base.dir=cl-dmn-host-2 -Djboss.domain.master.address=127.0.0.1注意
记住,集群是在需要时激活的,也就是说,在我们安装了
cluster-aware应用程序之后。 -
按照以下方式部署应用程序:
$ cd $WILDFLY_HOME $ ./bin/jboss-cli.sh You are disconnected at the moment. Type 'connect' to connect to the server or 'help' for the list of supported commands. [disconnected /] connect [domain@localhost:9990 /] deploy cluster-test.war --all-server-groups
测试集群
现在所有主机都已启动并运行,让我们测试我们的两个集群!
打开浏览器并指向以下 URL,使用不同的窗口:
-
URL
http://127.0.0.1:8180/cluster-test将显示以下输出:![测试集群]()
-
URL
http://127.0.0.1:8280/cluster-test将显示以下输出:![测试集群]()
-
URL
http://127.0.0.1:8380/cluster-test将显示以下输出:![测试集群]()
-
URL
http://127.0.0.1:8480/cluster-test将显示以下输出:![测试集群]()
哎呀!我们没想到会出现这种情况,对吧?我们错过了什么?
-
好吧,我们得到了相同的应用程序,但仍然,集群应该已经工作。我的意思是两个独立的集群。我们为所有主机分配了不同的端口。那么问题出在哪里?
-
服务器组并不定义集群。集群是在网络层面上定义的。为了有一个第二个集群,即一个独立的集群,我们需要为想要形成第二个集群的服务器指定一个不同的多播地址。两个服务器组都在共享同一个套接字绑定组
ha-sockets,所以所有信息、缓存和集群 ping 都会发送到同一个网络。
我们该如何做呢?将多播地址作为参数与domain.sh脚本一起传递给host-2?不!那样做的话,我们会为REST-server-2服务器设置一个不同的多播地址,这个地址应该存在于第一个集群cluster-REST-app中。
我们需要为第一个集群定义一个多播地址,并为第二个集群定义另一个地址。我们的集群在逻辑上由cluster-REST-app服务器组和cluster-SOAP-app服务器组表示,我们可以在服务器组级别定义这些多播地址,因此是在domain.xml中。
现在,停止一切!
主
编辑domain.xml文件,将<server-groups>...</server-groups>标签定义替换为以下内容:
<server-groups>
<server-group name="cluster-REST-app" profile="ha">
<jvm name="default">
<heap size="512m" max-size="512m"/>
</jvm>
<socket-binding-group ref="ha-sockets"/>
<system-properties>
<property name="jboss.default.multicast.address" value="${rest.multicast.address}" />
</system-properties>
<deployments>
<deployment name="cluster-test.war" runtime-name="cluster-test.war"/>
</deployments>
</server-group>
<server-group name="cluster-SOAP-app" profile="ha">
<jvm name="default">
<heap size="512m" max-size="512m"/>
</jvm>
<socket-binding-group ref="ha-sockets"/>
<system-properties>
<property name="jboss.default.multicast.address" value="${soap.multicast.address}" />
</system-properties>
<deployments>
<deployment name="cluster-test.war" runtime-name="cluster-test.war"/>
</deployments>
</server-group>
</server-groups>
打开终端并执行以下命令:
$ cd $WILDFLY_HOME
$ ./bin/domain.sh -Djboss.domain.base.dir=cl-dmn-master
主机-1
打开终端并执行以下命令:
$ cd $WILDFLY_HOME
$ ./bin/domain.sh -Djboss.domain.base.dir=cl-dmn-host-1 -Djboss.domain.master.address=127.0.0.1 -Drest.multicast.address=230.0.0.4 -Dsoap.multicast.address=230.0.0.5
主机-2
打开终端并执行以下命令:
$ cd $WILDFLY_HOME
$ ./bin/domain.sh -Djboss.domain.base.dir=cl-dmn-host-2 -Djboss.domain.master.address=127.0.0.1 -Drest.multicast.address=230.0.0.4 -Dsoap.multicast.address=230.0.0.5
重新测试集群
让我们打开同一浏览器的两个窗口并将它们指向以下 URL:
-
URL
http://127.0.0.1:8180/cluster-test将显示以下输出:![重新测试集群]()
-
URL
http://127.0.0.1:8380/cluster-test将显示以下输出:![重新测试集群]()
现在打开另一个不同浏览器的两个其他窗口并将它们指向以下 URL:
-
URL
http://127.0.0.1:8280/cluster-test将显示以下输出:![重新测试集群]()
-
URL
http://127.0.0.1:8480/cluster-test将显示以下输出:![重新测试集群]()
我们开始了!
它是如何工作的…
我们在第一个测试过程中解释了出了什么问题。然而,第二个测试略有不同;似乎使用不同的浏览器起到了魔法般的效果。好吧,有点像。
首先,我们实际上将集群分成了两部分,每部分都有自己的网络,这正是设计和实现的要求。
需要不同的浏览器只有一个原因——因为我们正在同一 IP(但不同端口)上进行测试,并且因为主机名(即 IP)与浏览器会话 cookie 的域名匹配,如果四个窗口共享相同的 cookie,最终会导致完全错误的行为——从我们的角度来看。
让我们描述一下“同一四个窗口浏览器”的场景,看看它是否符合我们的思考:
-
客户端向服务器 8180 发起请求:它以包含名称
JSESSIONID,域名127.0.0.1,路径/cluster-test和值0o0hPhIZ73unAtIMDCb0zR2h.host-1:REST-server-1的set-cookie头部进行响应。访问者编号0。 -
客户端向服务器 8380 发起请求:服务器没有返回
set-cookie头部,因为浏览器在自己的机器上找到了 cookie 并将其发送到服务器,与请求一起。由于服务器8180和8380位于同一个集群中,并且 HTTP 会话在这些服务器之间进行了复制,服务器找到了会话并增加了我们的访问者数量。访问者编号1。 -
客户端向服务器 8280 发起请求:我们将其配置为位于不同的集群中。浏览器将 cookie 与请求本身一起发送。服务器找不到会话,因此它以包含新创建值的
set-cookie头部进行响应:名称JSESSIONID,域名127.0.0.1,路径/cluster-test和值8X1gLkCbr5RsmELxwTlI0izj.host-1:SOAP-server-1。访问者编号0。 -
客户端向服务器 8480 发起请求:浏览器将 cookie 与请求本身一起发送。服务器找不到会话,因此它以包含新创建值的
set-cookie头部进行响应。
如您所见,使用相同的浏览器——至少是相同会话的浏览器——是无法工作的。
参见
如果您在这个配置中遇到任何问题,您可能存在网络问题,您可以使用本章最后一个小节中的最后一个配方进行故障排除。
通过 TCP 创建集群
在许多情况下,尤其是在存在多个网络限制的企业和云环境中,你无法使用多播地址,即使在同一网络中。幸运的是,jgroups子系统通过提供一种简单的方法在 UDP 和 TCP 集群之间切换,这正是你将在本食谱中学到的。我们将使用带有ha配置文件的独立模式进行工作。
准备中
对于这个食谱,我们需要一个名为“example”的“集群感知”应用程序,你可以在我的 GitHub 仓库中找到它。如果你跳过了第二章中关于“使用部署文件夹管理应用程序”的食谱,请参阅它以下载你需要的所有源代码和项目。
要构建应用程序,请按以下步骤操作:
$ cd ~/WFC/github/wildfly-cookbook
$ cd example
$ mvn -e clean package
如何进行测试...
从 WildFly 安装目录$WILDFLY_HOME,让我们创建两个文件夹,每个文件夹代表一个服务器节点。
-
打开终端并执行以下命令:
$ cd $WILDFLY_HOME $ cp -a standalone cl-std-tcp-node-1 $ cp -a standalone cl-std-tcp-node-2 -
现在,让我们将
cluster-test.war应用程序复制到我们刚刚创建的每个节点的deployments文件夹中。运行以下命令:$ cp cluster-test.war cl-std-tcp-node-1/deployments/ $ cp cluster-test.war cl-std-tcp-node-2/deployments/
我们几乎准备好测试我们的集群了。我们只需要一些配置。
Node-1
编辑standalone-ha.xml文件,并将jgroups子系统替换为以下定义:
<subsystem >
<channels default="ee">
<channel name="ee"/>
</channels>
<stacks default="tcp">
<stack name="udp">
<transport type="UDP" socket-binding="jgroups-udp"/>
<protocol type="PING"/>
<protocol type="MERGE3"/>
<protocol type="FD_SOCK" socket-binding="jgroups-udp-fd"/>
<protocol type="FD_ALL"/>
<protocol type="VERIFY_SUSPECT"/>
<protocol type="pbcast.NAKACK2"/>
<protocol type="UNICAST3"/>
<protocol type="pbcast.STABLE"/>
<protocol type="pbcast.GMS"/>
<protocol type="UFC"/>
<protocol type="MFC"/>
<protocol type="FRAG2"/>
<protocol type="RSVP"/>
</stack>
<stack name="tcp">
<transport type="TCP" socket-binding="jgroups-tcp"/>
<protocol type="TCPPING">
<property name="initial_hosts">
127.0.0.1[7700],127.0.0.1[7800]
</property>
<property name="num_initial_members">
2
</property>
<property name="port_range">
0
</property>
<property name="timeout">
2000
</property>
</protocol>
<protocol type="MPING" socket-binding="jgroups-mping"/>
<protocol type="MERGE3"/>
<protocol type="FD_SOCK" socket-binding="jgroups-tcp-fd"/>
<protocol type="FD"/>
<protocol type="VERIFY_SUSPECT"/>
<protocol type="pbcast.NAKACK2"/>
<protocol type="UNICAST3"/>
<protocol type="pbcast.STABLE"/>
<protocol type="pbcast.GMS"/>
<protocol type="MFC"/>
<protocol type="FRAG2"/>
<protocol type="RSVP"/>
</stack>
</stacks>
</subsystem>
打开终端并执行以下命令:
$ cd $WILDFLY_HOME
$ ./bin/standalone.sh -Djboss.server.base.dir=cl-std-tcp-node-1 --server-config=standalone-ha.xml -Djboss.socket.binding.port-offset=100 -Djboss.node.name=node-1
Node-2
编辑standalone-ha.xml文件,并替换jgroups子系统,就像我们为node-1所做的那样。
打开终端并执行以下命令:
$ cd $WILDFLY_HOME
$ ./bin/standalone.sh -Djboss.server.base.dir=cl-std-tcp-node-2 --server-config=standalone-ha.xml -Djboss.socket.binding.port-offset=200 -Djboss.node.name=node-2
测试 TCP 集群
现在所有节点都已启动并运行,让我们测试我们的 TCP 集群!
-
打开浏览器并指向
http://127.0.0.1:8180/cluster-test。刷新页面几次,你应该会看到以下类似图像:![测试 TCP 集群]()
在 TCP 集群的独立模式“ha”配置文件下运行在“node-1”上的“cluster-test”应用程序
在
node-1的日志中,你应该会看到以下语句:15:04:57,966 INFO [stdout] (default task-1) ********************************+ 15:04:57,967 INFO [stdout] (default task-1) Visitor(s): 0 15:04:57,967 INFO [stdout] (default task-1) ********************************+ 15:04:59,762 INFO [stdout] (default task-3) ********************************+ 15:04:59,763 INFO [stdout] (default task-3) Visitor(s): 1 15:04:59,763 INFO [stdout] (default task-3) ********************************+ 15:05:00,105 INFO [stdout] (default task-4) ********************************+ 15:05:00,105 INFO [stdout] (default task-4) Visitor(s): 2 15:05:00,105 INFO [stdout] (default task-4) ********************************+ 15:05:00,462 INFO [stdout] (default task-5) ********************************+ 15:05:00,462 INFO [stdout] (default task-5) Visitor(s): 3 15:05:00,463 INFO [stdout] (default task-5) ********************************+ 15:05:01,080 INFO [stdout] (default task-6) ********************************+ 15:05:01,080 INFO [stdout] (default task-6) Visitor(s): 4 15:05:01,080 INFO [stdout] (default task-6) ********************************+ -
现在,尝试将浏览器指向
http://127.0.0.1:8280/cluster-test。你应该会看到以下类似图像:![测试 TCP 集群]()
在 TCP 集群的独立模式“ha”配置文件下运行在“node-2”上的“cluster-test”应用程序
并且在
node-2的日志中,你应该会看到以下语句:15:05:28,122 INFO [stdout] (default task-1) ********************************+ 15:05:28,123 INFO [stdout] (default task-1) Visitor(s): 5 15:05:28,123 INFO [stdout] (default task-1) ********************************+
我们的 TCP 集群正在运行!让我们也尝试看看它是否具有良好的扩展性:
-
打开终端并执行以下命令:
$ cd $WILDFLY_HOME $ cp -a standalone cl-std-tcp-node-3 -
现在,让我们将
cluster-test.war应用程序复制到node-3的deployments文件夹中,如下所示:$ cp cluster-test.war cl-std-tcp-node-3/deployments/ -
编辑
standalone-ha.xml文件,并替换jgroups子系统,就像我们为node-1和node-2所做的那样。 -
打开终端并执行以下命令:
$ cd $JBOSS_HOME $ ./bin/standalone.sh -Djboss.server.base.dir=cl-std-tcp-node-3 --server-config=standalone-ha.xml -Djboss.socket.binding.port-offset=300 -Djboss.node.name=node-3
在前两个节点的日志中,你应该会看到以下条目:
-
23:14:24,466 INFO [org.infinispan.remoting.transport.jgroups.JGroupsTransport] (Incoming-8,ee,node-1) ISPN000094: Received new cluster view for channel web: [node-1|2] (3) [node-1, node-2, node-3] -
23:14:24,468 INFO [org.infinispan.remoting.transport.jgroups.JGroupsTransport] (Incoming-9,ee,node-2) ISPN000094: Received new cluster view for channel web: [node-1|2] (3) [node-1, node-2, node-3]
让我们在浏览器中测试它,指向http://127.0.0.1:8380/cluster-test。你应该看到以下截图类似的内容:

在 TCP 集群中扩展运行在node-3上的“cluster-test”应用程序——使用“ha”配置文件的单机模式
好的,一切如预期工作!
它是如何工作的……
让我们分析我们所做的。
尽管standalone.sh脚本只有少量参数,但主要配置包括正确设置 JGroups 子系统的stack元素的default属性为 TCP。此外,我们还需要设置集群成员之间如何 ping 对方。默认是MPING协议(M代表多播)。因此,我们定义了 ping 协议,命名为TCPPING,并定义了已知主机(initial_hosts属性)。
概念是每个想要加入集群的主机都会向已知主机请求成员信息。如果这些主机没有运行,新节点将无法加入集群。顺便说一句,如果所有集群成员都处于运行状态,而已知主机关闭,集群不会有任何影响;它们只是被视为离开了集群的两个成员。
事实上,在测试通过添加node-3和停止前两个节点来扩展 TCP 集群时,我们只会看到node-3日志中的以下条目:
23:21:54,480 INFO [org.infinispan.remoting.transport.jgroups.JGroupsTransport] (Incoming-8,ee,node-3) ISPN000094: Received new cluster view for channel web: [node-3|5] (2) [node-3, node-2]
23:21:57,473 INFO [org.infinispan.remoting.transport.jgroups.JGroupsTransport] (Incoming-10,ee,node-3) ISPN000094: Received new cluster view for channel web: [node-3|6] (1) [node-3]
我们定义的与<protocol type="TCPPING">...</protocol>相关的属性如下:
-
num_initial_members:在集群被视为完整之前节点的数量。 -
port_range:在已知主机未响应的情况下尝试的端口号范围。例如,使用port_range为50和已知主机10.0.0.1[7600]时,新成员将尝试端口7600、7601、7602到7650。 -
timeout:成员在加入集群之前将等待的超时时间。
使用 JGroups 工具测试 UDP 协议
通常情况下,你需要验证和/或认证一个配置,在涉及 UDP 集群的问题时,首先要检查的是 UDP 是否正常工作。
在这个菜谱中,你将学习如何检查 UDP 是否工作,使用图形工具测试,并使用 Java 应用程序(因此没有 UI——这是企业环境的情况)。
如何做……
首先,让我们检查我们是否在我们的 WildFly 安装文件夹中有 JGroups 库。
打开你的命令行工具并执行以下命令:
$ find . -name "jgroups*.jar"
./modules/system/layers/base/org/jgroups/main/jgroups-3.6.2.Final.jar
太好了!现在我们可以测试它了。
图形测试
打开你的命令行工具。
-
现在执行以下命令:
$ java -cp modules/system/layers/base/org/jgroups/main/jgroups-3.6.2.Final.jar org.jgroups.demos.Draw ... ------------------------------------------------------------------- GMS: address=0-44172, cluster=draw-cluster, physical address=fe80:0:0:0:f2de:f1ff:fe99:b294%2:35420 ------------------------------------------------------------------- ** View=[0-44172|0] (1) [0-44172]你应该看到以下图像类似的应用程序:
![图形测试]()
第一个 JGroups 绘图应用程序
-
如果你因为 IPv6 而遇到网络问题,尝试通过添加以下参数来强制使用 IPv4:
-Djava.net.preferIPv4Stack=true -
现在,在另一个终端中,执行之前提到的相同命令:
$ java -cp $WILDFLY_HOME/modules/system/layers/base/org/jgroups/main/jgroups-3.6.2.Final.jar org.jgroups.demos.Draw ... ------------------------------------------------------------------- GMS: address=0-3647, cluster=draw-cluster, physical address=fe80:0:0:0:f2de:f1ff:fe99:b294%2:40803 ------------------------------------------------------------------- ** View=[0-44172|1] (2) [0-44172, 0-3647]从这个第二个命令中,我强调了集群视图,它正在计数两个成员,你现在可以看到相同的应用程序正在运行(括号内的数字也指示集群成员):
![图形测试]()
第二个 JGroups 绘图应用程序
-
在它上面画些东西,如果你的 UDP 工作正常,你的草图应该会出现在另一个画布上,就像我的这样:
![图形测试]()
JGroups 应用程序反映变化
如果你遇到图形测试问题,尝试通过命令行测试 UDP,如下一节所述。
Shell 测试
打开你的命令行工具:
-
现在执行以下命令:
$ java -cp $WILDFLY_HOME/modules/system/layers/base/org/jgroups/main/jgroups-3.6.2.Final.jar org.jgroups.tests.McastReceiverTest Socket=0.0.0.0/0.0.0.0:5555, bind interface=/172.17.42.1 Socket=0.0.0.0/0.0.0.0:5555, bind interface=/10.0.1.6 Socket=0.0.0.0/0.0.0.0:5555, bind interface=/10.0.1.5 Socket=0.0.0.0/0.0.0.0:5555, bind interface=/10.0.1.4 Socket=0.0.0.0/0.0.0.0:5555, bind interface=/10.0.1.3 Socket=0.0.0.0/0.0.0.0:5555, bind interface=/fe80:0:0:0:f2de:f1ff:fe99:b294%em1 Socket=0.0.0.0/0.0.0.0:5555, bind interface=/10.0.254.33 Socket=0.0.0.0/0.0.0.0:5555, bind interface=/0:0:0:0:0:0:0:1%lo Socket=0.0.0.0/0.0.0.0:5555, bind interface=/127.0.0.1这将启动一个 JGroups 应用程序作为接收器,因此它将监听传入的消息。
-
再次,如果你因为 IPv6 而遇到网络问题,尝试通过添加以下参数来强制使用 IPv4:
-Djava.net.preferIPv4Stack=true -
现在,在另一个终端中,我们可以称它为
sender,执行以下命令:$ java -cp $WILDFLY_HOME/modules/system/layers/base/org/jgroups/main/jgroups-3.6.2.Final.jar org.jgroups.tests.McastSenderTest Socket #1=0.0.0.0/0.0.0.0:5555, ttl=32, bind interface=/172.17.42.1 Socket #2=0.0.0.0/0.0.0.0:5555, ttl=32, bind interface=/10.0.1.6 Socket #3=0.0.0.0/0.0.0.0:5555, ttl=32, bind interface=/10.0.1.5 Socket #4=0.0.0.0/0.0.0.0:5555, ttl=32, bind interface=/10.0.1.4 Socket #5=0.0.0.0/0.0.0.0:5555, ttl=32, bind interface=/10.0.1.3 Socket #6=0.0.0.0/0.0.0.0:5555, ttl=32, bind interface=/fe80:0:0:0:f2de:f1ff:fe99:b294%em1 Socket #7=0.0.0.0/0.0.0.0:5555, ttl=32, bind interface=/10.0.254.33 Socket #8=0.0.0.0/0.0.0.0:5555, ttl=32, bind interface=/0:0:0:0:0:0:0:1%lo Socket #9=0.0.0.0/0.0.0.0:5555, ttl=32, bind interface=/127.0.0.1 > -
第二个终端将等待标准输入。由于我没有指定任何接口或多播地址,它将绑定到任何可用的接口,在我的情况下,有多个。顺便说一句,让我们尝试输入一些内容并按 Enter 键,如下所示:
> Ciao! > -
现在看看第一个终端,即我们启动
receiver应用程序的终端:Ciao! [sender=172.17.42.1:5555] Ciao! [sender=10.0.1.6:5555] Ciao! [sender=10.0.1.5:5555] Ciao! [sender=10.0.1.4:5555] Ciao! [sender=10.0.1.3:5555] Ciao! [sender=10.0.254.33:5555] Ciao! [sender=10.0.254.33:5555] Ciao! [sender=127.0.0.1:5555] Ciao! [sender=127.0.0.1:5555]
哟!我们收到了来自所有可用和配置的接口的所有内容。
第七章. WildFly 负载均衡
在本章中,我们将涵盖以下主题:
-
安装和配置 Apache HTTPD
-
为 Apache 安装和配置 mod_cluster
-
使用自动广告(UDP)平衡 WildFly
-
使用可用均衡器列表(TCP)平衡 WildFly
-
使用 HTTP 连接器而不是 AJP 进行平衡
-
在重启 Apache 时保留 WildFly 工作进程
-
平衡不同应用程序的相同上下文
-
滚动更新
简介
在本章中,你将学习如何负载均衡你的 WildFly 实例。负载均衡是将工作负载分配到多个节点的能力,在我们的案例中,是 WildFly 节点。这项技术用于优化资源、最小化响应时间以及最大化应用吞吐量。
要实现负载均衡,我们需要一个组件来作为 WildFly 节点的代理,并将工作负载分配到它们。一种常见的工作负载分配模式是以轮询方式转发客户端请求,以确保每个节点服务的请求数量相同。
那么,一个请求可以生成到节点的实际工作负载是什么?例如,一个长时间运行的请求或涉及繁重任务的请求会使节点比仅处理静态页面请求的其他节点忙碌得多。
这不是一个公平的工作负载分配!我们需要某种东西来更好地校准这个工作负载分配,这取决于节点实际上有多忙。
我们已经有了 Apache HTTP 服务器(也称为 HTTPD),它可以平衡到我们的 WildFly 节点。我们还在 HTTPD 和 WildFly 中有一个名为mod_cluster的组件,以获取实际的工作负载分布。
Apache HTTP 服务器的mod_cluster组件是一组模块,正如我们将在本章后面看到的那样,而 WildFly 的mod_cluster是一个名为mod_cluster的子系统。
注意
mod_cluster组件将前端与后端解耦。基本上,Apache HTTP 服务器没有对 WildFly 节点的任何引用,WildFly 也没有对 HTTPD 均衡器的任何引用——只要使用多播广告。此外,mod_cluster支持 AJP、HTTP 和 HTTPS 协议。更多信息可以通过阅读以下 URL 的官方文档获得:docs.jboss.org/mod_cluster/1.2.0/html/.
负载均衡不会影响你在其中运行 WildFly 的操作模式(域或独立)。你可以在运行在域模式和独立模式下的实例之间进行负载均衡。为了我们的目的,我们首先将使用独立模式,然后我们将切换到域模式。
安装和配置 Apache HTTPD
在这个菜谱中,你将学习如何安装和配置 Apache HTTPD。我们首先下载软件,然后将其安装到源中。如果你愿意,你可以选择使用包管理器(如 YUM)来获取 Apache HTTP Server,但我认为理解和学习如何从源构建它更有趣。
我们将使用 Apache HTTPD 版本 2.2.29,因为 2.2.x 版本是在生产环境中与 mod_cluster 一起运行的最常用版本之一。它已经包含了 APR 和 APR-Util 库,以及 PCRE。
准备工作
要下载 Apache HTTP Server,请访问 httpd.apache.org/download.cgi。当你第一次访问这个网站时,你可以选择两个稳定版本:2.2 和 2.4。显然,2.4 版本更新,它有一些新功能。它的配置文件语法也有所不同。
另一方面,2.2 版本已经存在很长时间了,它可能更稳定。尽管如此,2.2 包提供了 APR、APR-Util 和 PCRE 包,因此你不需要像 2.4 版本那样单独下载和安装它们。
说了这么多,我们将使用 Apache HTTP Server 版本 2.2.29。此外,我们还需要在您的系统中安装 openssl-devel(开发)版本。要安装它:
-
对于 Fedora 21,使用以下命令:
sudo yum-y install gcc openssl openssl-devel wget tar unzip -
对于 Fedora 22,使用以下命令:
sudo yum-y install gcc openssl openssl-devel wget tar unzip findutils
将所有软件下载到 ~/WFC/ 文件夹。
如何操作...
-
下载完成后,打开终端并执行以下命令:
$ cd ~/WFC $ sudo tar zxvf httpd-2.2.29.tar.gz现在我们已经解压了所有软件,我们准备配置、编译和构建我们的 Apache HTTPD。
-
执行以下命令以适用于 Fedora 21/22:
$ cd httpd-2.2.29 sudo ./configure --prefix=/home/<USER>/WFC/httpd --with-mpm=worker --enable-mods-shared=most --enable-maintainer-mode --with-expat=builtin --enable-ssl --enable-proxy --enable-proxy-http --enable-proxy-ajp --disable-proxy-balancer --with-included-apr在这里,
<USER>必须替换为你的用户名。在我的情况下,这是luigi;因此,--prefix指令将是--prefix=/home/luigi/WFC/httpd。所以,让我们开始执行以下命令:... $ sudo make ... $ sudo make install ... Installing configuration files mkdir /home/luigi/WFC/httpd/conf mkdir /home/luigi/WFC/httpd/conf/extra mkdir /home/luigi/WFC/httpd/conf/original mkdir /home/luigi/WFC/httpd/conf/original/extra Installing HTML documents mkdir /home/luigi/WFC/httpd/htdocs Installing error documents mkdir /home/luigi/WFC/httpd/error Installing icons mkdir /home/luigi/WFC/httpd/icons mkdir /home/luigi/WFC/httpd/logs Installing CGIs mkdir /home/luigi/WFC/httpd/cgi-bin Installing header files Installing build system files Installing man pages and online manual mkdir /home/luigi/WFC/httpd/man mkdir /home/luigi/WFC/httpd/man/man1 mkdir /home/luigi/WFC/httpd/man/man8 mkdir /home/luigi/WFC/httpd/manual -
好的,现在我们准备启动 Apache。导航到其
bin文件夹并执行它,如下所示:$ cd ~/WFC/httpd/bin $ ./httpd -k start -f ~/WFC/httpd/conf/httpd.conf httpd: Could not reliably determine the server's fully qualified domain name, using mylaptop for ServerName (13)Permission denied: make_sock: could not bind to address [::]:80 (13)Permission denied: make_sock: could not bind to address 0.0.0.0:80 no listening sockets available, shutting down Unable to open logs -
哎呀...Apache 监听端口
80,这是一个特权端口;我们到了 root。让我们用sudo再试一次:$ sudo ./httpd -k start -f /opt/httpd/conf/httpd.conf httpd: Could not reliably determine the server's fully qualified domain name, using mylaptop for ServerName注意
请记住,在生产环境中,出于安全原因,Apache HTTP Server 不应该以 root 用户身份运行。现在,即使我们有一个关于
ServerName指令未设置而发出的警告。让我们现在消除它。 -
编辑
/opt/httpd/conf/httpd.conf文件,查找以下注释行#ServerName www.example.com:80。将其替换为以下条目:ServerName balancer-one.com:80 -
现在,作为 root 用户,打开并编辑
/etc/hosts文件,并添加以下指令:127.0.0.1 balancer-one.com -
现在,回到 Apache 的
bin文件夹,再次运行httpd脚本:$ sudo ./httpd -k restart -f /opt/httpd/conf/httpd.conf -
警告消失了。让我们通过打开浏览器并将它指向
http://balancer-one.com来测试一切是否正常工作。你应该看到以下页面:![如何操作...]()
Apache HTTPD 运行并为其默认页面提供服务
参见
在安装 Apache HTTPD 软件时遇到的每个问题都可以通过阅读以下网站上的 Apache 文档来缓解httpd.apache.org/docs/2.2/。
为 Apache 安装和配置 mod_cluster
在这个食谱中,我们将学习如何在 Apache HTTPD 安装中安装和配置mod-cluster。这个食谱需要一个正在运行的 Apache HTTPD,并假设其安装目录是/opt/httpd。如果你在环境中没有安装 Apache,请遵循前面食谱中的说明。
准备工作
-
首先,从以下网站下载 mod-cluster 模块
mod-cluster.jboss.org/downloads/1-2-6-Final-bin。 -
将所有软件下载到
/opt/httpd/modules文件夹。 -
如果你愿意,你可以通过你的操作系统包管理器安装
mod_cluster,在类似 Fedora 的系统上,如下所示:sudo yum install mod_cluster
如何操作…
-
下载完成后,打开终端并执行以下命令:
$ cd /opt/httpd/modules $ tar zxvf mod_cluster-1.2.6.Final-linux2-x64-so.tar.gz mod_advertise.so mod_manager.so mod_proxy_cluster.so mod_slotmem.so现在我们已经解压了所有软件,我们准备配置
mod_cluster。为了更好地配置和管理mod_cluster,让我们创建一个用于内部通信的私有网络接口,即 Apache 和 WildFly 之间。 -
打开终端窗口并执行以下命令:
$ sudo ifconfig eth0:1 10.0.0.1 netmask 255.255.255.0注意
如果你没有遵循前面的食谱,你可能需要编辑
httpd.conf文件并禁用与mod_cluster模块冲突的proxy-balancer模块。 -
编辑
/opt/httpd/conf/httpd.conf文件,查找以下注释行LoadModule proxy_balancer_module modules/mod_proxy_balancer.so。将其替换为以下条目:#LoadModule proxy_balancer_module modules/mod_proxy_balancer.so -
在终端窗口中,创建一个名为
mod_cluster.conf的新文件,并将其放置在opt/httpd/conf/extra文件夹中,如下所示:$ touch /opt/httpd/conf/extra/mod_cluster.conf $ vim /opt/httpd/conf/extra/mod_cluster.conf -
现在添加以下代码:
LoadModule slotmem_module modules/mod_slotmem.so LoadModule manager_module modules/mod_manager.so LoadModule proxy_cluster_module modules/mod_proxy_cluster.so LoadModule advertise_module modules/mod_advertise.so Listen 10.0.0.1:80 Listen 10.0.0.1:6666 <VirtualHost 10.0.0.1:80> <Location /mcm> SetHandler mod_cluster-manager Order deny,allow Deny from all Allow from 10.0.0.1 </Location> </VirtualHost> <VirtualHost 10.0.0.1:6666> <Directory /> Order deny,allow Deny from all Allow from 10.0.0.1 </Directory> ServerAdvertise on http://10.0.0.1:6666 EnableMCPMReceive </VirtualHost> -
现在,让我们再次编辑
httpd.conf文件,并在末尾添加以下指令。它几乎是自我解释的:# mod_cluster settings Include conf/extra/mod_cluster.conf这将加载前面的文件,其中包含我们所有的
mod_cluster配置。 -
让我们使用以下命令启动我们的 Apache:
$ cd /opt/httpd/bin $ sudo ./httpd -k start -f /opt/httpd/conf/httpd.conf -
现在将你的浏览器指向以下 URL:
-
http://10.0.0.1 -
http://10.0.0.1/mcm
你应该看到以下截图所示的页面:
![如何操作…]()
在私有网络接口上运行的 Apache HTTPD 和 mod_cluster 管理器
-
它是如何工作的…
让我们分析到目前为止我们所做的工作:
-
首先,我们将所有的
mod_cluster模块复制到 Apache 的modules目录中。然后,通过在mod_cluster.conf文件中添加LoadModule指令来引用它们,以便加载模块。此外,我们通过Include指令将mod_cluster.conf文件引用到默认的 Apache 配置文件httpd.conf中。这为我们粘合 Apache 和
mod_cluster提供了粘合剂。 -
为什么我们创建了一个私有网络接口?这是由于设计和安全原因。设计可能是一个好的实践,但安全是必须的。内部通信应该暴露在私有网络上,而不是公共网络上。尽管如此,在企业环境中,您很可能会有两个网络接口的服务器来完成这个目的。因此,为了模拟企业环境并给您一个好的设计,我们只是创建了一个另一个网络接口。
-
因此,由于前一点中提到的理由,我们将
mod_cluster管理页面/mcm之间的私有接口以及 Apache 和 WildFly 之间的所有通信绑定。本质上,这是在 Apache 上的mod_cluster和 WildFly 上的mod_cluster组件之间交换消息。 -
这基本上提供了平衡;一个 WildFly
mod_cluster向10.0.0.1:6666多播地址发送信号,“嗨,我在这里”。在另一边,在 Apache 上,mod_cluster组件读取消息并通过平衡为新请求启用该 WildFly 节点。
在本章的后面部分,我们需要更改一些前面的设置,添加更多的指令。这里讨论的只是基本的设置,以开始使用。
使用自动广告平衡 WildFly – UDP
在这个菜谱中,我们将学习如何平衡以独立模式运行的两个 WildFly 节点。默认的mod_cluster设置提供自动广告功能,使用多播地址。此外,在这个菜谱中,我们还将使用集群配置,以提供更好的测试体验。如果您需要更多关于使用 WildFly 进行集群的信息,请阅读第六章,WildFly 集群。
本菜谱中使用的整个 WildFly 配置将不会依赖于任何之前的配置。相反,我们假设 Apache HTTPD 的安装和配置基于本章前两个菜谱。
准备工作
对于这个菜谱,我们需要一个名为balancing-test的应用程序,您可以在我的 GitHub 仓库中找到它。如果您跳过了第二章中的使用部署文件夹管理应用程序菜谱,请参阅它以下载您将需要的所有源代码和项目。
要构建应用程序,请执行以下命令:
$ cd ~/WFC/github/wildfly-cookbook
$ cd balancing-test
$ mvn clean package
如何做到这一点...
让我们从 WildFly 安装目录$WILDFLY_HOME中创建两个文件夹,每个文件夹代表一个服务器节点。
-
打开一个终端并执行以下命令:
$ cd $WILDFLY_HOME $ cp -a standalone bl-std-node-1 $ cp -a standalone bl-std-node-2 -
现在,让我们将
balancing-test.war应用程序复制到我们刚刚创建的每个节点的deployments文件夹中。操作如下:$ cp balancing-test.war bl-std-node-1/deployments/ $ cp balancing-test.war bl-std-node-2/deployments/现在,让我们为每个节点生成一个虚拟 IP:
$ sudo ifconfig eth0:2 10.0.1.1 netmask 255.255.255.0 $ sudo ifconfig eth0:3 10.0.1.2 netmask 255.255.255.0 -
如果它还没有运行,让我们通过执行以下命令来启动 Apache 及其日志:
$ cd /opt/httpd/bin $ sudo ./httpd -k start -f /opt/httpd/conf/httpd.conf $ tail -f ../logs/{access,error}_log ==> ../logs/access_log <== ==> ../logs/error_log <== [Sun Oct 12 16:10:29 2014] [notice] SIGHUP received. Attempting to restart [Sun Oct 12 16:10:29 2014] [notice] Digest: generating secret for digest authentication ... [ Sun Oct 12 16:10:29 2014] [notice] Digest: done [ Sun Oct 12 16:10:29 2014] [warn] httpd version 2.2.29 mismatch detected [ Sun Oct 12 16:10:29 2014] [notice] Advertise initialized for process 26675 [ Sun Oct 12 16:10:29 2014] [notice] Apache/2.2.29 (Unix) mod_ssl/2.2.29 OpenSSL/1.0.1e-fips DAV/2 mod_cluster/1.2.6.Final configured -- resuming normal operations -
好的,现在 Apache 已经启动。让我们通过在新终端窗口中输入以下命令来启动我们的第一个节点:
$ ./bin/standalone.sh -Djboss.server.base.dir=bl-std-node-1 --server-config=standalone-ha.xml -Djboss.bind.address=10.0.1.1 -Djboss.management.http.port=19990 -Djboss.node.name=node-1 ... 16:13:58,472 INFO [org.jboss.as] (Controller Boot Thread) JBAS015874: WildFly 8.1.0.Final "Kenny" started in 6531ms - Started 289 of 400 services (179 services are lazy, passive or on-demand) -
现在,在
node-1启动后的几秒钟内,你应该在 Apache 日志的access_log中看到以下条目:==> ../logs/access_log <== 10.0.0.1 - - [12/Oct/2014:16:14:05 +0200] "INFO / HTTP/1.1" 200 - 10.0.0.1 - - [12/Oct/2014:16:14:05 +0200] "CONFIG / HTTP/1.1" 200 - 10.0.0.1 - - [12/Oct/2014:16:14:05 +0200] "ENABLE-APP / HTTP/1.1" 200 - 10.0.0.1 - - [12/Oct/2014:16:14:05 +0200] "STATUS / HTTP/1.1" 200 46 10.0.0.1 - - [12/Oct/2014:16:14:15 +0200] "STATUS / HTTP/1.1" 200 46这些是
mod_cluster日志,表明它们接收到了一个信息消息,一个配置消息,然后它启用了 WildFlynode-1提供的应用程序。 -
现在,打开我们在
mod_cluster.conf文件中定义的mod_cluster-manager页面——Apache 端——在以下 URL:http://10.0.0.1/mcm你应该看到以下页面:
![如何做…]()
mod_cluster管理页面显示正在运行的节点及其上下文。 -
让我们在新的终端窗口中启动另一个节点
node-2,如下所示:$ ./bin/standalone.sh -Djboss.server.base.dir=bl-std-node-2 --server-config=standalone-ha.xml -Djboss.bind.address=10.0.1.2 -Djboss.management.http.port=29990 -Djboss.node.name=node-2 ... 16:13:58,472 INFO [org.jboss.as] (Controller Boot Thread) JBAS015874: WildFly 8.1.0.Final "Kenny" started in 6531ms - Started 289 of 400 services (179 services are lazy, passive or on-demand)现在,在
node-2启动后的几秒钟内,你应该在 Apache 日志的access_log中看到以下条目:... 10.0.0.1 - - [12/Oct/2014:16:23:45 +0200] "STATUS / HTTP/1.1" 200 46 10.0.0.1 - - [12/Oct/2014:16:23:55 +0200] "STATUS / HTTP/1.1" 200 46 10.0.0.1 - - [12/Oct/2014:16:24:05 +0200] "STATUS / HTTP/1.1" 200 46 10.0.0.1 - - [12/Oct/2014:16:24:15 +0200] "STATUS / HTTP/1.1" 200 46 10.0.0.1 - - [12/Oct/2014:16:24:21 +0200] "INFO / HTTP/1.1" 200 331 10.0.0.1 - - [12/Oct/2014:16:24:21 +0200] "CONFIG / HTTP/1.1" 200 - 10.0.0.1 - - [12/Oct/2014:16:24:21 +0200] "ENABLE-APP / HTTP/1.1" 200 - 10.0.0.1 - - [12/Oct/2014:16:24:21 +0200] "STATUS / HTTP/1.1" 200 46 10.0.0.1 - - [12/Oct/2014:16:24:25 +0200] "STATUS / HTTP/1.1" 200 46 -
让我们刷新
mcm页面:![如何做…]()
mod_cluster管理页面显示正在运行的节点及其上下文。现在一切都已经启动并运行,让我们来玩我们的神奇应用程序。
-
将你的浏览器指向以下 URL 并刷新页面几次:
http://10.0.0.1/balancing-test你应该看到一个像下面的页面:
![如何做…]()
Apache 在节点-1 上提供负载均衡测试应用程序
注意
在我的情况下,请求落在服务器
node-1上;你的可能落在node-2上。此外,请注意,WildFly 的mod_cluster附带一些默认的负载均衡设置,例如:-
sticky-session="true":如果会话仍然存活,则将后续相同会话的请求粘附到同一节点 -
sticky-session-force="true":这强制 Apache 在后续请求无法路由到同一节点时响应错误 -
sticky-session-remove="false":这表示如果后续请求无法路由到同一节点,Apache 是否应该移除粘附行为。因此,在刷新页面时,节点名称永远不会改变。顺便说一下,让我们停止服务节点并再次刷新页面。如果一切按预期工作,你应该会看到一个像以下截图中的页面:
![如何做…]()
Apache 在节点-2 上提供负载均衡测试应用程序
-
是的,我们做到了!
它是如何工作的…
如果你注意到了,我们在两个节点上都没有更改任何特定的mod_cluster设置。这是因为 WildFly 的mod_cluster配置依赖于与 Apache 端相同的默认设置。整个配置位于mod_cluster子系统以及 socket-binding-group 中。遵循以下配置:
<subsystem >
<mod-cluster-config advertise-socket="mod_cluster" advertise="true" connector="ajp">
<dynamic-load-provider>
<load-metric type="cpu"/>
</dynamic-load-provider>
</mod-cluster-config>
</subsystem>
以下代码描述了mod_cluster应该如何通知 Apache 中的对应部分有关其工作负载。默认情况下,工作负载是针对 CPU 计算的。
<socket-binding-group name="standard-sockets" default-interface="public" port-offset="${jboss.socket.binding.port-offset:0}">
<socket-binding name="management-http" interface="management" port="${jboss.management.http.port:9990}"/>
<socket-binding name="management-https" interface="management" port="${jboss.management.https.port:9993}"/>
<socket-binding name="ajp" port="${jboss.ajp.port:8009}"/>
<socket-binding name="http" port="${jboss.http.port:8080}"/>
<socket-binding name="https" port="${jboss.https.port:8443}"/>
<socket-binding name="jgroups-mping" port="0" multicast-address="${jboss.default.multicast.address:230.0.0.4}" multicast-port="45700"/>
<socket-binding name="jgroups-tcp" port="7600"/>
<socket-binding name="jgroups-tcp-fd" port="57600"/>
<socket-binding name="jgroups-udp" port="55200" multicast-address="${jboss.default.multicast.address:230.0.0.4}" multicast-port="45688"/>
<socket-binding name="jgroups-udp-fd" port="54200"/>
<socket-binding name="modcluster" port="0" multicast-address="224.0.1.105" multicast-port="23364"/>
<socket-binding name="txn-recovery-environment" port="4712"/>
<socket-binding name="txn-status-manager" port="4713"/>
<outbound-socket-binding name="mail-smtp">
<remote-destination host="localhost" port="25"/>
</outbound-socket-binding>
</socket-binding-group>
以下代码描述了mod_cluster如何与 Apache 中的对应部分通信,有关其可用性和工作负载。默认情况下,多播地址设置为224.0.1.105。
如果需要更改多播地址,出于任何原因,请记住在 Apache 中更改其对应设置,使用以下指令:
AdvertiseGroup http://MULTICAST_ADDR:PORT
分解进程
以下图像显示了由netstat工具提供的进程列表,你可以检查 IP 和端口绑定:

显示进程绑定 IP 和端口的 netstat 工具
当 Apache 启动时,实际上正在执行一个进程——PID9524(如netstat图所示),该进程监听 IP10.0.0.1的端口80和6666;该进程还监听任何接口(0.0.0.0)的端口23364。
当 WildFly 节点node-1启动时,实际上正在执行一个进程——PID9674(如netstat图所示),该进程监听多个端口和 IP:
-
端口
8080,绑定到 IP10.0.1.1,由 HTTP 连接器使用 -
端口
19990,绑定到 IP127.0.0.1,由主机控制器用于远程管理 -
端口
8009,绑定到 IP10.0.1.1,由 AJP 连接器使用 -
端口
23364,绑定到 IP224.0.1.105,由mod_cluster使用 -
端口
45688,绑定到 IP230.0.0.4,由jgroups用于形成集群
当 WildFly 节点node-2启动时,实际上正在执行一个进程——PID9853(如netstat图所示),该进程监听多个端口和 IP:
-
端口
8080,绑定到 IP10.0.1.2,由 HTTP 连接器使用 -
端口
29990,绑定到 IP127.0.0.1,由主机控制器用于远程管理 -
端口
8009,绑定到 IP10.0.1.2,由 AJP 连接器使用 -
端口
23364,绑定到 IP224.0.1.105,由mod_cluster使用 -
端口
45688,绑定到 IP230.0.0.4,由jgroups用于形成集群
有些绑定目前并不相关,所以我们不会讨论它们。你应该注意的是,WildFly 节点之间有相同的绑定。
-
224.0.1.105:23364由两者用于在 Apache 端向mod_cluster广播自己。 -
230.0.0.4:45688由两者用于创建 UDP 集群,因此具有相同的绑定使它们能够加入同一个集群。
分解通信
让我们分析参与负载均衡的所有组件如何相互通信。首先,看看以下这张价值千金的图像:

显示使用 UDP 进行平衡和集群通信配置的架构图
跳过客户端/用户请求资源时的步骤,以及当请求击中路由到 Apache 均衡器的交换机时的步骤,让我们集中关注 Apache 和 WildFly 之间通信的事件序列,看看中间发生了什么。
事件序列如下:
-
Apache 监听
224.0.1.105:23364以平衡工作进程 -
当 WildFly 节点启动时,它们在
224.0.1.105:23364上广播自己 -
Apache 收到以下消息:
10.0.0.1 - - [12/Oct/2014:16:24:21 +0200] "INFO / HTTP/1.1" 200 331 10.0.0.1 - - [12/Oct/2014:16:24:21 +0200] "CONFIG / HTTP/1.1" 200 - 10.0.0.1 - - [12/Oct/2014:16:24:21 +0200] "ENABLE-APP / HTTP/1.1" 200 - 10.0.0.1 - - [12/Oct/2014:16:24:21 +0200] "STATUS / HTTP/1.1" 200 46 -
Apache 通过 AJP 协议开始平衡节点,地址为
10.0.1.1/2:8009,具体取决于通过STATUS消息接收到的负载。 -
WildFly 节点接收请求并对其进行处理,然后响应 Apache。
-
同时,
jgroups在集群成员之间序列化和反序列化 http 会话 -
同时,WildFly 节点向 Apache 发送有关实际工作负载的
STATUS消息,等等
这基本上就是通过 UDP 平衡 WildFly 集群之外的事情。
您可以轻松地平衡一个非集群环境,这对于无状态服务来说很合适;配置相同,只是您将不得不使用一个“非高可用”配置文件。
还有更多…
如前所述,WildFly mod_cluster管理工作负载的计算,默认为 CPU。
以下是一些其他类型的负载指标:
| 类型 | 负载指标 |
|---|---|
cpu |
返回系统 CPU 负载 |
mem |
返回系统内存使用量 |
heap |
返回堆内存使用量作为最大堆大小的百分比 |
sessions |
给定一个容量,根据活动会话/容量返回百分比 |
requests |
返回每秒请求数量 |
send-traffic |
返回每秒 KB 的出站请求流量 |
receive-traffic |
返回每秒 KB 的入站请求 POST 流量 |
busyness |
返回线程池中忙于处理请求的连接器线程的百分比 |
connection-pool |
返回连接池中正在使用的连接的百分比 |
您可以将负载指标添加到node-1的 mod-cluster 子系统,如下所示:
$ ./bin/jboss-cli.sh
You are disconnected at the moment. Type 'connect' to connect to the server or 'help' for the list of supported commands.
[disconnected /] connect 127.0.0.1:19990
[standalone@127.0.0.1:19990 /] /subsystem=modcluster/mod-cluster-config=configuration:add-metric(type=mem, weight=2, capacity=1)
{
"outcome" => "success",
"response-headers" => {
"operation-requires-reload" => true,
"process-state" => "reload-required"
}
}
[standalone@127.0.0.1:19990 /] :reload()
{
"outcome" => "success",
"result" => undefined
}
[standalone@127.0.0.1:19990 /]
这基本上为系统内存使用量添加了一个新的负载指标。如果我们查看standalone-ha.xml文件,我们会发现以下内容:
<subsystem >
<mod-cluster-config advertise-socket="modcluster" advertise="true" connector="ajp">
<dynamic-load-provider>
<load-metric type="cpu"/>
<load-metric type="mem" weight="2" capacity="1"/>
</dynamic-load-provider>
</mod-cluster-config>
</subsystem>
因此,mod_cluster将根据这两个指标向其 Apache 对应方提供一个负载系数。如果您在异构环境中安装了 WildFly,这可能会非常有帮助,比如说一个具有不同资源能力的服务器——一个服务器可能比其他服务器少一些 RAM,或者比其他服务器少一些核心。因此,添加和删除指标让您有自由调整工作负载分配。
参见
对于集群配置,请参阅本书第五章第五章。使用 CLI 管理数据源子系统,使用 CLI 管理数据源子系统。
使用可用均衡器列表平衡 WildFly – TCP
在这个菜谱中,我们将学习如何使用 TCP 平衡平衡两个以独立模式运行的 WildFly 节点。与向多播地址发送信息消息不同,WildFly mod_cluster直接将所有内容发送到 Apache。
此外,在这个菜谱中,我们还将使用集群配置,以提供更好的测试感觉。如果你需要有关使用 WildFly 进行集群的更多信息,请参阅第五章,使用 CLI 管理数据源子系统。顺便说一下,用于此菜谱的整个 WildFly 配置将不依赖于任何之前的配置。相反,我们假设 Apache HTTPD 安装和配置基于本章的前两个菜谱。
准备工作
对于这个菜谱,我们需要一个名为balancing-test的应用程序,你可以在我的 GitHub 仓库中找到它。如果你跳过了第二章中关于使用部署文件夹管理应用程序的菜谱,请参阅它以下载你需要的所有源代码和项目。
要构建应用程序,请输入以下命令:
$ cd ~/WFC/github/wildfly-cookbook
$ cd balancing-test
$ mvn clean package
如何做到这一点...
从 WildFly 安装目录$WILDFLY_HOME,让我们创建两个文件夹,每个文件夹代表一个服务器节点。
-
打开终端并执行以下命令:
$ cd $WILDFLY_HOME $ cp -a standalone bl-tcp-std-node-1 $ cp -a standalone bl-tcp-std-node-2 -
现在,让我们将
balancing-test.war应用程序复制到我们刚刚创建的每个节点的deployments文件夹中。如下操作:$ cp balancing-test.war bl-tcp-std-node-1/deployments/ $ cp balancing-test.war bl-tcp-std-node-2/deployments/ -
现在,让我们为每个节点生成一个虚拟 IP:
$ sudo ifconfig eth0:2 10.0.1.1 netmask 255.255.255.0 $ sudo ifconfig eth0:3 10.0.1.2 netmask 255.255.255.0 -
现在,让我们回到 Apache 配置并编辑
mod_cluster.conf,如下禁用广告:<VirtualHost 10.0.0.1:6666> <Directory /> Order deny,allow Deny from all Allow from 10.0.0.1 </Directory> ServerAdvertise off EnableMCPMReceive </VirtualHost> -
如果它还没有运行,让我们通过执行以下命令启动 Apache 及其日志:
$ cd /opt/httpd/bin $ sudo ./httpd -k start -f /opt/httpd/conf/httpd.conf $ tail -f ../logs/{access,error}_log ==> ../logs/access_log <== ==> ../logs/error_log <== [Sat Oct 18 23:10:29 2014] [notice] SIGHUP received. Attempting to restart [Sat Oct 18 23:10:29 2014] [notice] Digest: generating secret for digest authentication ... [Sat Oct 18 23:10:29 2014] [notice] Digest: done [Sat Oct 18 23:10:29 2014] [warn] httpd version 2.2.29 mismatch detected [Sat Oct 18 23:10:29 2014] [notice] Advertise initialized for process 26675 [Sat Oct 18 23:10:29 2014] [notice] Apache/2.2.29 (Unix) mod_ssl/2.2.29 OpenSSL/1.0.1e-fips DAV/2 mod_cluster/1.2.6.Final configured -- resuming normal operations -
现在,我们需要通过向
standalone-ha.xml文件的mod_cluster子系统添加一些属性来告诉 WildFlymod_cluster直接与 Apache 通信,如下所示:<subsystem > <mod-cluster-config advertise-socket="modcluster" advertise="false" proxy-list="10.0.0.1:6666" connector="ajp"> <dynamic-load-provider> <load-metric type="cpu"/> </dynamic-load-provider> </mod-cluster-config> </subsystem> -
如前所述代码所强调的,我们正在告诉
mod_cluster禁用自动广告,并将自身广告到proxy-list属性中指定的代理。要指定多个代理,请使用逗号,作为分隔符:proxy-list="10.0.0.1:6666,10.0.0.2:6666" -
现在 Apache 已经启动并运行,并且我们已经配置了我们的节点,让我们在新的终端窗口中通过以下命令启动它:
$ cd $WILDFLY_HOME $ ./bin/standalone.sh -Djboss.server.base.dir=bl-tcp-std-node-1 --server-config=standalone-ha.xml -Djboss.bind.address=10.0.1.1 -Djboss.management.http.port=19990 -Djboss.node.name=node-1 ... 23:13:58,472 INFO [org.jboss.as] (Controller Boot Thread) JBAS015874: WildFly 8.1.0.Final "Kenny" started in 6531ms - Started 289 of 400 services (179 services are lazy, passive or on-demand) -
在
node-1启动后几秒钟,你应该会在 Apache 日志中看到以下条目,access_log:==> ../logs/access_log <== 10.0.0.1 - - [18/Oct/2014:23:14:05 +0200] "INFO / HTTP/1.1" 200 - 10.0.0.1 - - [18/Oct/2014:23:14:05 +0200] "CONFIG / HTTP/1.1" 200 - 10.0.0.1 - - [18/Oct/2014:23:14:05 +0200] "ENABLE-APP / HTTP/1.1" 200 - 10.0.0.1 - - [18/Oct/2014:23:14:05 +0200] "STATUS / HTTP/1.1" 200 46 10.0.0.1 - - [18/Oct/2014:23:14:15 +0200] "STATUS / HTTP/1.1" 200 46这些是
mod_cluster日志,表明它接收到了一个信息消息,一个配置消息,然后启用了 WildFlynode-1提供的应用程序。 -
将 WildFly
mod_cluster的配置复制到node-2,然后在新的终端窗口中启动它,如下所示:$ ./bin/standalone.sh -Djboss.server.base.dir=bl-tcp-std-node-2 --server-config=standalone-ha.xml -Djboss.bind.address=10.0.1.2 -Djboss.management.http.port=29990 -Djboss.node.name=node-2 ... 23:13:58,472 INFO [org.jboss.as] (Controller Boot Thread) JBAS015874: WildFly 8.1.0.Final "Kenny" started in 6531ms - Started 289 of 400 services (179 services are lazy, passive or on-demand) -
在
node-2启动后几秒钟,你应该会在 Apache 日志中看到以下条目,access_log:... 10.0.0.1 - - [18/Oct/2014:23:23:45 +0200] "STATUS / HTTP/1.1" 200 46 10.0.0.1 - - [18/Oct/2014:23:23:55 +0200] "STATUS / HTTP/1.1" 200 46 10.0.0.1 - - [18/Oct/2014:23:24:05 +0200] "STATUS / HTTP/1.1" 200 46 10.0.0.1 - - [18/Oct/2014:23:24:15 +0200] "STATUS / HTTP/1.1" 200 46 10.0.0.1 - - [18/Oct/2014:23:24:21 +0200] "INFO / HTTP/1.1" 200 331 10.0.0.1 - - [18/Oct/2014:23:24:21 +0200] "CONFIG / HTTP/1.1" 200 - 10.0.0.1 - - [18/Oct/2014:23:24:21 +0200] "ENABLE-APP / HTTP/1.1" 200 - 10.0.0.1 - - [18/Oct/2014:23:24:21 +0200] "STATUS / HTTP/1.1" 200 46 10.0.0.1 - - [18/Oct/2014:23:24:25 +0200] "STATUS / HTTP/1.1" 200 46 -
现在,打开我们在
mod_cluster.conf文件中定义的mod_cluster-manager页面—Apache 端—以下 URL:http://10.0.0.1/mcm你应该会看到以下页面:
![如何做到这一点...]()
mod_cluster 管理页面显示正在运行的节点及其上下文—TCP
现在一切都已经启动并运行,让我们玩一下我们奇妙的应用程序。
-
将你的浏览器指向以下 URL 并刷新页面几次:
http://10.0.0.1/balancing-test你应该会看到一个像以下截图中的页面:
![如何做到这一点...]()
Apache 在节点-1 上提供 balancing-test 应用程序
-
在我的情况下,请求落在服务器
node-1上,你的可能落在node-2上。让我们停止服务节点并刷新页面一次。如果一切如预期工作,你应该会看到一个像以下截图中的页面:![如何做到这一点...]()
Apache 在节点-2 上提供 balancing-test 应用程序
我们让它工作得和自动广告平衡时完全一样。
它是如何工作的…
在此情况下,与启用自动广告的平衡不同之处在于,mod_cluster 直接连接到 Apache 以通信其 STATUS 信息,如下面的图像所示:

显示使用 TCP 的平衡和集群通信配置的架构图
尽管如此,WildFly 以相同的方式扩展;你可以添加任意多的 WildFly 节点,它们将由 Apache 自动平衡。缺点是如果你添加了另一个 Apache 实例,你需要通过 mod_cluster 子系统的 proxy-list 属性将其引用添加到 WildFly 的 mod_cluster 配置中;这个更改最终需要重新加载服务器。
使用 HTTP 连接器而不是 AJP 进行平衡
在这个配方中,我们将学习如何使用 HTTP 连接器而不是二进制协议 AJP 来平衡 WildFly 节点。这个配方的目的是向你展示如何使用不同的连接器,而不是真正平衡 WildFly 提供的服务。因此,我们将只使用一个 WildFly 节点,由 Apache 前端。我们还假设 Apache HTTPD 的安装和配置基于本章的前两个配方。
准备工作
对于这个配方,我们需要一个名为 balancing-test 的应用程序,你可以在我的 GitHub 仓库中找到它。如果你跳过了 第二章 中关于 使用部署文件夹管理应用程序 的配方,请参阅它以下载你将需要的所有源代码和项目。
要构建应用程序,请输入以下命令:
$ cd ~/WFC/github/wildfly-cookbook
$ cd balancing-test
$ mvn clean package
如何做到这一点...
从 WildFly 安装目录 $WILDFLY_HOME,让我们创建一个代表服务器节点的文件夹。
-
打开一个终端并执行以下命令:
$ cd $WILDFLY_HOME $ cp -a standalone bl-http-std-node-1 -
现在,让我们将
balancing-test.war应用程序复制到我们刚刚创建的节点的deployments文件夹中。输入以下命令:$ cp balancing-test.war bl-http-std-node-1/deployments/ -
现在,让我们为节点生成一个虚拟 IP:
$ sudo ifconfig eth0:1 10.0.1.1 netmask 255.255.255.0 -
如果它还没有运行,让我们通过执行以下命令来启动 Apache 和其日志:
$ cd /opt/httpd/bin $ sudo ./httpd -k start -f /opt/httpd/conf/httpd.conf $ tail -f ../logs/{access,error}_log ==> ../logs/access_log <== ==> ../logs/error_log <== [Sun Oct 12 16:10:29 2014] [notice] SIGHUP received. Attempting to restart [Sun Oct 12 16:10:29 2014] [notice] Digest: generating secret for digest authentication ... [Sun Oct 12 16:10:29 2014] [notice] Digest: done [Sun Oct 12 16:10:29 2014] [warn] httpd version 2.2.29 mismatch detected [Sun Oct 12 16:10:29 2014] [notice] Advertise initialized for process 26675 [Sun Oct 12 16:10:29 2014] [notice] Apache/2.2.29 (Unix) mod_ssl/2.2.29 OpenSSL/1.0.1e-fips DAV/2 mod_cluster/1.2.6.Final configured -- resuming normal operations -
现在是时候配置我们的 WildFly 节点
node-1了。编辑bl-http-std-node-1/configuration文件夹中的standalone-ha.xml文件,并按以下方式配置 Undertow 子系统:<subsystem > <buffer-cache name="default"/> <server name="default-server"> <http-listener name="http" socket-binding="http" enabled="true"/> <ajp-listener name="ajp" socket-binding="ajp" enabled="false"/> <host name="default-host" alias="localhost"> <location name="/" handler="welcome-content"/> <filter-ref name="server-header"/> <filter-ref name="x-powered-by-header"/> </host> </server> <servlet-container name="default"> <jsp-config/> </servlet-container> <handlers> <file name="welcome-content" path="${jboss.home.dir}/welcome-content"/> </handlers> <filters> <response-header name="server-header" header- name="Server" header-value="WildFly/8"/> <response-header name="x-powered-by-header" header-name="X-Powered-By" header-value="Undertow/1"/> </filters> </subsystem> -
我们通过添加属性
enabled并将其设置为false来禁用了 AJP 监听器。基于这些更改,我们需要按以下方式更新mod_cluster子系统:<subsystem > <mod-cluster-config advertise-socket="modcluster" connector="http"> <dynamic-load-provider> <load-metric type="cpu"/> </dynamic-load-provider> </mod-cluster-config> </subsystem> -
我们将连接器引用从 AJP 更改为 HTTP。现在我们需要更新远程子系统中的连接器引用,该子系统使用其默认名称
default引用连接器。应用以下更改:<subsystem > <endpoint worker="default"/> <http-connector name="http-remoting-connector" connector-ref="default" security-realm="ApplicationRealm"/> </subsystem> -
就这样;我们的配置完成了。让我们通过在新终端窗口中输入以下命令来启动我们的节点:
$ ./bin/standalone.sh -Djboss.server.base.dir=bl-http-std-node-1 --server-config=standalone-ha.xml -Djboss.bind.address=10.0.1.1 -Djboss.management.http.port=19990 -Djboss.node.name=node-1 ... 18:03:57,734 INFO [org.jboss.as] (Controller Boot Thread) JBAS015874: WildFly 8.1.0.Final "Kenny" started in 6670ms - Started 287 of 399 services (179 services are lazy, passive or on-demand) -
现在,在
node-1启动后的几秒钟内,你应该会在 Apache 日志中看到以下条目,即在access_log中:==> ../logs/access_log <== ... 10.0.0.1 - - [16/Oct/2014:18:04:03 +0200] "INFO / HTTP/1.1" 200 - 10.0.0.1 - - [16/Oct/2014:18:04:03 +0200] "CONFIG / HTTP/1.1" 200 - 10.0.0.1 - - [16/Oct/2014:18:04:03 +0200] "ENABLE-APP / HTTP/1.1" 200 - 10.0.0.1 - - [16/Oct/2014:18:04:03 +0200] "STATUS / HTTP/1.1" 200 46 10.0.0.1 - - [16/Oct/2014:18:04:13 +0200] "STATUS / HTTP/1.1" 200 46这些是 mod-cluster 日志,表明它接收到了一个信息消息和一个配置消息,然后它启用了 WildFly
node-1提供的应用程序。 -
让我们通过打开浏览器并将它指向以下 URL 来测试我们的应用程序:
http://10.0.0.1/balancing-test你应该会看到一个像以下截图一样的页面:
![如何操作…]()
Apache 在节点 1 上通过 HTTP 连接器提供 balancing-test 应用程序
它是如何工作的…
如果你期待在 Apache 端进行更改,那么你错了。WildFly mod_cluster连接到 Apache 并说“嗨,我需要平衡。这是我的可达性”。因此,当 Apache 收到有关应用程序、拓扑和通信策略的所有信息时,它开始将请求分发给 WildFly。
这正是我们所做的。我们通过 HTTP 连接器配置了新的通信通道,禁用了 AJP 连接器,就这样!很简单,不是吗?
在重启 Apache 时保留 WildFly 工作进程
许多时候,你需要重启 Apache,而你所有的 WildFly 实例都会在几秒钟内不可用。因此,这个讨厌的404错误可能是一个问题。在这个菜谱中,我们将学习如何以简单的方式减轻这个问题。我们还假设 Apache HTTPD 的安装和配置基于本章前两个菜谱。
准备工作
对于这个菜谱,我们需要一个名为balancing-test的应用程序,你可以在我的 GitHub 仓库中找到它。如果你跳过了第二章中关于使用部署文件夹管理应用程序的菜谱,请参考它以下载你需要的所有源代码和项目。
要构建应用程序,请输入以下命令:
$ cd ~/WFC/github/wildfly-cookbook
$ cd balancing-test
$ mvn clean package
如何操作…
从 WildFly 安装目录$WILDFLY_HOME开始,让我们创建一个代表服务器节点的文件夹。
-
打开一个终端并执行以下命令:
$ cd $WILDFLY_HOME $ cp -a standalone bl-persist-std-node-1 -
现在,让我们将
balancing-test.war应用程序复制到我们刚刚创建的节点deployments文件夹中。输入以下命令:$ cp balancing-test.war bl-persist-std-node-1/deployments/ -
现在,让我们为节点生成一个虚拟 IP:
$ sudo ifconfig eth0:1 10.0.1.1 netmask 255.255.255.0 -
如果它尚未运行,让我们通过执行以下命令来启动 Apache 及其日志:
$ cd /opt/httpd/bin $ sudo ./httpd -k start -f /opt/httpd/conf/httpd.conf $ tail -f ../logs/{access,error}_log ==> ../logs/access_log <== ==> ../logs/error_log <== [Thu Oct 16 18:51:51 2014] [warn] Init: Session Cache is not configured [hint: SSLSessionCache] [Thu Oct 16 18:51:51 2014] [warn] httpd version 2.2.29 mismatch detected [Thu Oct 16 18:51:51 2014] [notice] Digest: generating secret for digest authentication ... [Thu Oct 16 18:51:51 2014] [notice] Digest: done [Thu Oct 16 18:51:51 2014] [warn] httpd version 2.2.29 mismatch detected [Thu Oct 16 18:51:51 2014] [notice] Advertise initialized for process 15525 [Thu Oct 16 18:51:51 2014] [notice] Apache/2.2.29 (Unix) mod_ssl/2.2.29 OpenSSL/1.0.1e-fips DAV/2 mod_cluster/1.2.6.Final configured -- resuming normal operations -
让我们在新的终端窗口中输入以下命令来启动我们的节点:
$ ./bin/standalone.sh -Djboss.server.base.dir=bl-persist-std-node-1 --server-config=standalone-ha.xml -Djboss.bind.address=10.0.1.1 -Djboss.management.http.port=19990 -Djboss.node.name=node-1 ... 18:55:37,613 INFO [org.jboss.as] (Controller Boot Thread) JBAS015874: WildFly 8.1.0.Final "Kenny" started in 6723ms - Started 289 of 400 services (179 services are lazy, passive or on-demand)现在,在
node-1启动后的几秒钟,您应该在 Apache 日志中看到以下条目,即access_log:==> ../logs/access_log <== ... 10.0.0.1 - - [16/Oct/2014:18:55:43 +0200] "INFO / HTTP/1.1" 200 - 10.0.0.1 - - [16/Oct/2014:18:55:43 +0200] "CONFIG / HTTP/1.1" 200 - 10.0.0.1 - - [16/Oct/2014:18:55:43 +0200] "ENABLE-APP / HTTP/1.1" 200 - 10.0.0.1 - - [16/Oct/2014:18:55:43 +0200] "STATUS / HTTP/1.1" 200 54 10.0.0.1 - - [16/Oct/2014:18:55:53 +0200] "STATUS / HTTP/1.1" 200 54这些是
mod_cluster日志,表明它接收到了一条信息消息和一条配置消息,然后启用了由 WildFly 提供的node-1应用。 -
现在,让我们通过执行以下命令来重启 Apache:
$ cd /opt/httpd/bin $ ./httpd -k restart -f /opt/httpd/conf/httpd.conf在 Apache 日志中,您应该看到类似以下的内容:
==> ../logs/error_log <== ... [Thu Oct 16 18:57:23 2014] [warn] manager_handler STATUS error: MEM: Can't read node ==> ../logs/access_log <== 10.0.0.1 - - [16/Oct/2014:18:57:23 +0200] "STATUS / HTTP/1.1" 500 535在 WildFly 日志中,您应该看到以下日志条目:
18:57:23,879 ERROR [org.jboss.modcluster] (UndertowEventHandlerAdapter - 1) MODCLUSTER000042: Error MEM sending STATUS command to 10.0.0.1/10.0.0.1:6666, configuration will be reset: MEM: Can't read node等待几秒钟,现在您应该看到 WildFly 按照以下方式向 Apache 发送消息:
==> ../logs/access_log <== 10.0.0.1 - - [16/Oct/2014:18:57:23 +0200] "STATUS / HTTP/1.1" 500 535 10.0.0.1 - - [16/Oct/2014:18:57:33 +0200] "INFO / HTTP/1.1" 200 - 10.0.0.1 - - [16/Oct/2014:18:57:33 +0200] "CONFIG / HTTP/1.1" 200 - 10.0.0.1 - - [16/Oct/2014:18:57:33 +0200] "ENABLE-APP / HTTP/1.1" 200 - 10.0.0.1 - - [16/Oct/2014:18:57:33 +0200] "STATUS / HTTP/1.1" 200 46 10.0.0.1 - - [16/Oct/2014:18:57:43 +0200] "STATUS / HTTP/1.1" 200 46在重启过程中,用户可能会看到您应用的NOT FOUND错误页面,如下所示:
![如何操作…]()
Apache 在重启后找不到应用程序上下文
-
因此,为了减轻这个问题,您可以在 Apache 关闭之前,以某种方式持久化 WildFly 节点信息,然后在启动时再次读取这些信息。
编辑
/opt/httpd/conf/extra文件夹中的mod_cluster.conf文件,并在虚拟主机声明之外添加以下指令:... Listen 10.0.0.1:80 Listen 10.0.0.1:6666 PersistSlots On <VirtualHost 10.0.0.1:80> ... -
让我们看看它是否工作。停止 Apache 和 WildFly,然后重启它们。几秒钟后,我们应该有以下条目:
==> ../logs/error_log <== [Thu Oct 16 19:14:39 2014] [warn] Init: Session Cache is not configured [hint: SSLSessionCache] [Thu Oct 16 19:14:39 2014] [warn] httpd version 2.2.29 mismatch detected [Thu Oct 16 19:14:39 2014] [notice] Digest: generating secret for digest authentication ... [Thu Oct 16 19:14:39 2014] [notice] Digest: done [Thu Oct 16 19:14:39 2014] [warn] httpd version 2.2.29 mismatch detected [Thu Oct 16 19:14:39 2014] [notice] Advertise initialized for process 16529 [Thu Oct 16 19:14:39 2014] [notice] Apache/2.2.29 (Unix) mod_ssl/2.2.29 OpenSSL/1.0.1e-fips DAV/2 mod_cluster/1.2.6.Final configured -- resuming normal operations ==> ../logs/access_log <== 10.0.0.1 - - [16/Oct/2014:19:15:01 +0200] "INFO / HTTP/1.1" 200 - 10.0.0.1 - - [16/Oct/2014:19:15:01 +0200] "CONFIG / HTTP/1.1" 200 - 10.0.0.1 - - [16/Oct/2014:19:15:01 +0200] "ENABLE-APP / HTTP/1.1" 200 - 10.0.0.1 - - [16/Oct/2014:19:15:01 +0200] "STATUS / HTTP/1.1" 200 55 -
现在重启 Apache 并查看其日志:
==> ../logs/error_log <== [Thu Oct 16 19:17:05 2014] [notice] SIGHUP received. Attempting to restart [Thu Oct 16 19:17:05 2014] [notice] Digest: generating secret for digest authentication ... [Thu Oct 16 19:17:05 2014] [notice] Digest: done [Thu Oct 16 19:17:05 2014] [warn] httpd version 2.2.29 mismatch detected [Thu Oct 16 19:17:05 2014] [notice] Advertise initialized for process 16529 [Thu Oct 16 19:17:05 2014] [notice] Apache/2.2.29 (Unix) mod_ssl/2.2.29 OpenSSL/1.0.1e-fips DAV/2 mod_cluster/1.2.6.Final configured -- resuming normal operations ==> ../logs/access_log <== 10.0.0.1 - - [16/Oct/2014:19:17:11 +0200] "STATUS / HTTP/1.1" 200 46 10.0.0.1 - - [16/Oct/2014:19:17:21 +0200] "STATUS / HTTP/1.1" 200 46
看看,不再有来自 WildFly 的INFO、CONFIG和ENABLE-APP消息!WildFly 日志没有捕捉到任何东西,我们的应用启动速度与 Apache 启动速度一样快。
平衡不同应用的同一种上下文
一个大型企业环境可能由几个提供大量应用的应用服务器组成。有时,当应用在不同的节点上运行时(在 WildFly 的域模式下,甚至可能是不同的服务器组),它们会有相同的上下文路径,但它们共享同一个均衡器,即 Apache。
在这个菜谱中,我们将学习如何平衡来自不同节点不同应用的同一种应用上下文。
准备中
对于这个菜谱,我们需要一个名为balancing-test的应用程序,您可以在我的 GitHub 仓库中找到它。如果您跳过了第二章中关于使用部署文件夹管理应用的菜谱,请参阅它以下载您需要的所有源代码和项目。
要构建应用,请输入以下命令:
$ cd ~/WFC/github/wildfly-cookbook
$ cd balancing-test
$ mvn clean package
如何操作…
从 WildFly 安装目录$WILDFLY_HOME开始,让我们创建两个文件夹,每个文件夹代表一个服务器节点。
-
打开终端并执行以下命令:
$ cd $WILDFLY_HOME $ cp -a standalone bl-std-node-bar $ cp -a standalone bl-std-node-foo -
现在,让我们将
deployments文件夹中的两个 Web 应用复制到相应的节点。输入以下命令:$ cp app-bar-with-this-context.war bl-std-node-bar/deployments/ $ cp app-foo-with-this-context.war bl-std-node-foo/deployments/ -
现在,让我们为每个节点生成一个虚拟 IP:
$ sudo ifconfig eth0:1 10.0.1.1 netmask 255.255.255.0 $ sudo ifconfig eth0:2 10.0.1.2 netmask 255.255.255.0 -
现在,让我们回到 Apache 配置并编辑
mod_cluster.conf。我们需要替换整个配置,如下所示:LoadModule slotmem_module modules/mod_slotmem.so LoadModule manager_module modules/mod_manager.so LoadModule proxy_cluster_module modules/mod_proxy_cluster.so LoadModule advertise_module modules/mod_advertise.so PersistSlots On ServerAdvertise Off Listen 10.0.0.3:80 <VirtualHost 10.0.0.3:80> ServerName mcm.com <Location /> SetHandler mod_cluster-manager Order deny,allow Deny from all Allow from all </Location> </VirtualHost> Listen 10.0.0.1:80 <VirtualHost 10.0.0.1:80> ServerName bar.com <Directory /> Order deny,allow Deny from all Allow from all </Directory> EnableMCPMReceive On CreateBalancers 1 ProxyPass / balancer://barBalancer/this </VirtualHost> Listen 10.0.0.2:80 <VirtualHost 10.0.0.2:80> ServerName foo.com <Directory /> Order deny,allow Deny from all Allow from all </Directory> EnableMCPMReceive On CreateBalancers 1 ProxyPass / balancer://fooBalancer/this </VirtualHost> -
如您所见,我们更改了虚拟主机绑定,因此让我们为它们创建适当的虚拟 IP:
$ sudo ifconfig eth0:3 10.0.0.1 netmask 255.255.255.0 $ sudo ifconfig eth0:4 10.0.0.2 netmask 255.255.255.0 $ sudo ifconfig eth0:5 10.0.0.3 netmask 255.255.255.0 -
现在,我们需要将
ServerName指令映射到/etc/hosts文件中,如下所示:10.0.0.3 mcm.com 10.0.0.1 bar.com 10.0.0.2 foo.com -
现在,让我们通过在新终端窗口中启动 Apache HTTPD 来检查我们的配置是否正确——至少是 Apache 配置——如下所示:
$ cd /opt/httpd/bin $ ./httpd -k restart -f /opt/httpd/conf/httpd.conf In the Apache logs, you should see entries similar to the following: ==> ../logs/access_log <== ==> ../logs/error_log <== [Fri Oct 17 00:12:57 2014] [warn] Init: Session Cache is not configured [hint: SSLSessionCache] [Fri Oct 17 00:12:57 2014] [warn] httpd version 2.2.29 mismatch detected [Fri Oct 17 00:12:57 2014] [notice] Digest: generating secret for digest authentication ... [Fri Oct 17 00:12:57 2014] [notice] Digest: done [Fri Oct 17 00:12:57 2014] [warn] httpd version 2.2.29 mismatch detected [Fri Oct 17 00:12:57 2014] [notice] Advertise initialized for process 23907 [Fri Oct 17 00:12:57 2014] [notice] Apache/2.2.29 (Unix) mod_ssl/2.2.29 OpenSSL/1.0.1e-fips DAV/2 mod_cluster/1.2.6.Final configured -- resuming normal operations -
打开浏览器并将它指向以下 URL:
http://mcm.com您应该看到以下
mod_cluster-manager页面:![如何做…]()
通过 mcm.com 地址由 Apache 提供的 mod_cluster 管理页面
-
现在,让我们配置我们的第一个节点,
node-bar。我们需要调整我们的mod_cluster子系统,如下所示:<subsystem > <mod-cluster-config advertise-socket="modcluster" advertise="false" proxy-list="${proxy.list}" connector="ajp" balancer="${balancer.name}"> <dynamic-load-provider> <load-metric type="cpu"/> </dynamic-load-provider> </mod-cluster-config> </subsystem> -
现在,我们可以通过执行以下命令来启动
node-bar:$ cd $WILDFLY_HOME $ ./bin/standalone.sh -Djboss.server.base.dir=bl-std-node-bar --server-config=standalone-ha.xml -Djboss.bind.address=10.0.1.1 -Djboss.management.http.port=19990 -Djboss.node.name=node-bar -Dbalancer.name=barBalancer -Dproxy.list=10.0.0.1:80 -Djboss.default.multicast.address=230.0.1.4 ... 00:24:20,643 INFO [org.wildfly.extension.undertow] (MSC service thread 1-1) WFLYUT0021: Registered web context: /this ...Apache 应该捕获以下日志条目:
10.0.0.1 - - [17/Oct/2014:00:24:16 +0200] "INFO / HTTP/1.1" 200 - 10.0.0.1 - - [17/Oct/2014:00:24:16 +0200] "CONFIG / HTTP/1.1" 200 - 10.0.0.1 - - [17/Oct/2014:00:24:16 +0200] "STATUS / HTTP/1.1" 200 48 10.0.0.1 - - [17/Oct/2014:00:24:20 +0200] "ENABLE-APP / HTTP/1.1" 200 - 10.0.0.1 - - [17/Oct/2014:00:24:20 +0200] "ENABLE-APP / HTTP/1.1" 200 - 10.0.0.1 - - [17/Oct/2014:00:24:26 +0200] "STATUS / HTTP/1.1" 200 48 -
现在,将我们对
mod_cluster子系统所做的相同更改应用到node-foo上。完成后,按照以下方式启动节点:$ cd $WILDFLY_HOME $ ./bin/standalone.sh -Djboss.server.base.dir=bl-std-node-foo --server-config=standalone-ha.xml -Djboss.bind.address=10.0.1.2 -Djboss.management.http.port=29990 -Djboss.node.name=node-foo -Dbalancer.name=fooBalancer -Dproxy.list=10.0.0.2:80 -Djboss.default.multicast.address=230.0.2.4 ... 00:24:39,689 INFO [org.wildfly.extension.undertow] (MSC service thread 1-5) WFLYUT0021: Registered web context: /this ...Apache 应该捕获以下日志条目:
10.0.0.1 - - [17/Oct/2014:00:24:32 +0200] "INFO / HTTP/1.1" 200 325 10.0.0.1 - - [17/Oct/2014:00:24:32 +0200] "CONFIG / HTTP/1.1" 200 - 10.0.0.1 - - [17/Oct/2014:00:24:32 +0200] "STATUS / HTTP/1.1" 200 48 10.0.0.1 - - [17/Oct/2014:00:24:36 +0200] "STATUS / HTTP/1.1" 200 48 ==> ../logs/error_log <== [Fri Oct 17 00:24:39 2014] [warn] ENABLE: context /this is in balancer fooBalancer and barBalancer ==> ../logs/access_log <== 10.0.0.1 - - [17/Oct/2014:00:24:39 +0200] "ENABLE-APP / HTTP/1.1" 200 - ==> ../logs/error_log <== [Fri Oct 17 00:24:39 2014] [warn] ENABLE: context /this is in balancer fooBalancer and barBalancer ==> ../logs/access_log <== 10.0.0.1 - - [17/Oct/2014:00:24:39 +0200] "ENABLE-APP / HTTP/1.1" 200 - 10.0.0.1 - - [17/Oct/2014:00:24:42 +0200] "STATUS / HTTP/1.1" 200 48 10.0.0.1 - - [17/Oct/2014:00:24:46 +0200] "STATUS / HTTP/1.1" 200 48如您所见,Apache 使用两个均衡器警告您关于上下文
/this,这是可以的;这正是我们想要的。 -
现在,让我们刷新
mcm.com网站:![如何做…]()
显示具有相同应用程序上下文路径的节点信息的 mod_cluster 管理页面
-
现在是时候查看我们的两个网站,
bar.com和foo.com是否被我们的 Apache 配置正确均衡和提供服务了:![如何做…]()
显示 bar.com 和 foo.com 网站的主页
为了确保一切正常工作,您应该在
node-bar日志中找到以下条目:00:37:42,053 INFO [stdout] (default task-1) ********************************+ 00:37:42,054 INFO [stdout] (default task-1) I'm serving your request and I'm "app-bar-with-this-context" 00:37:42,054 INFO [stdout] (default task-1) ********************************+对于
node-foo,您应该找到以下日志条目:00:37:43,709 INFO [stdout] (default task-1) ********************************+ 00:37:43,709 INFO [stdout] (default task-1) I'm serving your request and I'm "app-foo-with-this-context" 00:37:43,709 INFO [stdout] (default task-1) ********************************+
太好了,一切如预期工作!
它是如何工作的...
大部分配置都在 Apache 端,并且是关于我们如何配置虚拟主机。我们需要指定一个mod_cluster指令,即CreateBalancers。它们定义了在包含的虚拟主机中如何创建均衡器,因此您可以控制哪个均衡器处理您的请求(如我们的案例所示)。
此mod_cluster指令可以有以下值:
-
0:在 Apache HTTPD 中定义的所有虚拟主机中创建均衡器 -
1:不创建均衡器(至少需要一个ProxyPass/ProxyPassMatch来定义均衡器名称) -
2:仅创建名为myclsuter的主要均衡器,这是默认设置。
根据前面的定义,对于bar.com网站,我们使用了以下定义:
Listen 10.0.0.1:80
<VirtualHost 10.0.0.1:80>
ServerName bar.com
<Directory />
Order deny,allow
Deny from all
Allow from all
</Directory>
EnableMCPMReceive On
CreateBalancers 1
ProxyPass / balancer://barBalancer/this
</VirtualHost>
对于foo.com网站,我们使用了以下定义:
Listen 10.0.0.2:80
<VirtualHost 10.0.0.2:80>
ServerName foo.com
<Directory />
Order deny,allow
Deny from all
Allow from all
</Directory>
EnableMCPMReceive On
CreateBalancers 1
ProxyPass / balancer://fooBalancer/this
</VirtualHost>
因此,我们命令 Apache 使用均衡器barBalancer来处理bar.com请求,并使用fooBalancer来处理foo.com请求。在 WildFly 端,我们必须将mod_cluster子系统中的均衡器名称与相对节点匹配。
我们通过在mod_cluster子系统定义属性balancer来实现这一点。该属性被赋值为启动时传递的属性,名为balancer.name。以下是我们的配置:
<subsystem >
<mod-cluster-config advertise-socket="modcluster" advertise="false" proxy-list="${proxy.list}" connector="ajp" balancer="${balancer.name}">
<dynamic-load-provider>
<load-metric type="cpu"/>
</dynamic-load-provider>
</mod-cluster-config>
</subsystem>
我们还需要指定哪个 Apache 正在为我们提供服务,通过使用proxy-list属性,该属性以名为proxy.list的属性设置,我们在命令行中传递。
参见
为了更好地理解 Apache 指令,请查看以下网站的文档:
滚动更新
在这个配方中,您将学习如何使用滚动更新来更新您的应用程序,同时仍然提供服务可用性。为了实现这一点,我们需要从 Apache 到 WildFly 配置很多东西,并编写测试应用程序。我们还假设 Apache HTTPD 的安装和配置基于本章前两个配方。
准备工作
对于这个配方,我们需要名为rolling-test的应用程序,您可以在我的 GitHub 仓库中找到它。如果您跳过了第二章中关于使用部署文件夹管理应用程序的配方,请参阅它以下载您将需要的所有源代码和项目。
要构建应用程序,请按以下步骤操作:
$ cd ~/WFC/github/wildfly-cookbook
$ cd rolling-test
$ mvn -e clean package
如何操作…
让我们从 WildFly 安装目录$WILDFLY_HOME创建四个文件夹,每个文件夹代表一个服务器节点。
-
打开终端并执行以下命令:
$ cd $WILDFLY_HOME $ cp -a standalone bl-rolling-node-1 $ cp -a standalone bl-rolling-node-2 $ cp -a standalone bl-rolling-node-3 $ cp -a standalone bl-rolling-node-4 -
现在,让我们将
rolling-test-1.0.war网络应用程序复制到我们刚刚创建的每个节点的deployments文件夹中,如下所示:$ cp ~/WFC/github/wildfly-cookbook/rolling-test/target/rolling-test-1.0.war bl-rolling-node-1/deployments/ $ cp ~/WFC/github/wildfly-cookbook/rolling-test/target/rolling-test-1.0.war bl-rolling-node-2/deployments/ $ cp ~/WFC/github/wildfly-cookbook/rolling-test/target/rolling-test-1.0.war bl-rolling-node-3/deployments/ $ cp ~/WFC/github/wildfly-cookbook/rolling-test/target/rolling-test-1.0.war bl-rolling-node-4/deployments/ -
现在,让我们为每个节点生成一个虚拟 IP:
$ sudo ifconfig eth0:2 10.0.1.1 netmask 255.255.255.0 $ sudo ifconfig eth0:3 10.0.1.2 netmask 255.255.255.0 $ sudo ifconfig eth0:4 10.0.1.3 netmask 255.255.255.0 $ sudo ifconfig eth0:5 10.0.1.4 netmask 255.255.255.0 -
编辑每个 WildFly 节点的
standalone-ha.xml文件,并将infinispan子系统的名为web的cache-containerXML 元素替换为以下 XML 代码:<cache-container name="web" default-cache="repl" module="org.wildfly.clustering.web.infinispan"> <transport lock-timeout="60000"/> <replicated-cache name="repl" mode="SYNC"batching="true"> <locking isolation="REPEATABLE_READ"/> </replicated-cache> <distributed-cache name="dist" mode="ASYNC" batching="true" l1-lifespan="0" owners="2"> <file-store/> </distributed-cache> </cache-container> -
现在,让我们回到 Apache 配置并编辑
mod_cluster.conf。我们需要替换整个配置,如下所示:LoadModule slotmem_module modules/mod_slotmem.so LoadModule manager_module modules/mod_manager.so LoadModule proxy_cluster_module modules/mod_proxy_cluster.so LoadModule advertise_module modules/mod_advertise.so Listen 10.0.0.1:80 <VirtualHost 10.0.0.1:80> <Location /mcm> SetHandler mod_cluster-manager Order deny,allow Deny from all Allow from 10.0.0.1 </Location> </VirtualHost> Listen 10.0.0.1:6666 <VirtualHost 10.0.0.1:6666> <Directory /> Order deny,allow Deny from all Allow from 10.0.0.1 </Directory> ServerAdvertise on http://10.0.0.1:6666 EnableMCPMReceive </VirtualHost> Listen 10.0.0.2:80 <VirtualHost 10.0.0.2:80> ServerName rolling.com <Directory /> Order deny,allow Deny from all Allow from all </Directory> EnableMCPMReceive On RewriteEngine On RewriteCond %{REQUEST_URI} !^/(rolling/.*)$ RewriteRule ^/(.*)$ /rolling/$1 [P,L] ProxyPass / balancer://mycluster/rolling stickysession=JSESSIONID|jsessionid nofailover=Off ProxyPassReverse /rolling / ProxyPassReverseCookieDomain / rolling.com ProxyPassReverseCookiePath /rolling / </VirtualHost> -
为第二个虚拟主机添加另一个虚拟 IP,如下所示:
$ sudo ifconfig eth0:6 10.0.0.2 netmask 255.255.255.0 -
现在,作为 root 用户,编辑
/etc/hosts文件并添加以下指令:10.0.0.2 rolling.com -
现在我们已经准备好启动 Apache 和所有四个 WildFly 节点。
对于 Apache,请按以下步骤操作:
$ sudo ./httpd -k restart -f /opt/httpd/conf/httpd.conf $ tail -f ../logs/{access,error}_log ==> ../logs/access_log <== ==> ../logs/error_log <== [Fri Oct 17 15:22:34 2014] [warn] Init: Session Cache is not configured [hint: SSLSessionCache] [Fri Oct 17 15:22:34 2014] [warn] httpd version 2.2.29 mismatch detected [Fri Oct 17 15:22:34 2014] [notice] Digest: generating secret for digest authentication ... [Fri Oct 17 15:22:34 2014] [notice] Digest: done [Fri Oct 17 15:22:34 2014] [warn] httpd version 2.2.29 mismatch detected [Fri Oct 17 15:22:34 2014] [notice] Advertise initialized for process 26498 [Fri Oct 17 15:22:34 2014] [notice] Apache/2.2.29 (Unix) mod_ssl/2.2.29 OpenSSL/1.0.1e-fips DAV/2 mod_cluster/1.2.6.Final configured -- resuming normal operations -
对于 WildFly 节点,打开四个不同的终端窗口,并在每个窗口中执行以下操作:
-
对于
node-1:./bin/standalone.sh -Djboss.server.base.dir=bl-rolling-node-1 --server-config=standalone-ha.xml -Djboss.bind.address=10.0.1.1 -Djboss.management.http.port=19990 -Djboss.node.name=node-1 -
对于
node-2:./bin/standalone.sh -Djboss.server.base.dir=bl-rolling-node-2 --server-config=standalone-ha.xml -Djboss.bind.address=10.0.1.2 -Djboss.management.http.port=29990 -Djboss.node.name=node-2 -
对于
node-3:./bin/standalone.sh -Djboss.server.base.dir=bl-rolling-node-3 --server-config=standalone-ha.xml -Djboss.bind.address=10.0.1.3 -Djboss.management.http.port=39990 -Djboss.node.name=node-3 -
对于
node-4:./bin/standalone.sh -Djboss.server.base.dir=bl-rolling-node-4 --server-config=standalone-ha.xml -Djboss.bind.address=10.0.1.4 -Djboss.management.http.port=49990 -Djboss.node.name=node-4
-
-
打开浏览器并将其指向以下网站,刷新页面几次:
http://rolling.com您应该看到以下内容:
![如何操作…]()
Apache 通过服务器名
rolling.com提供滚动应用程序服务 -
不必花太多时间查看日志,让我们停止
node-1,或者你所在的任何节点。刷新页面几次:![如何操作…]()
由集群中的另一个成员提供的滚动应用程序,节点-2
好的,它工作了。现在假设我们的超级老板希望用我们伟大应用程序的最新版本进入生产。显然,他希望昨天就完成,显然他不想向客户提供一个服务不可用的状态。
你知道答案:是的,先生,是的。
-
编辑
rolling-test应用程序的index.jsp文件,并通过以下方式替换<h3>...</h3>HTML 标签的内容:<h3 style="color:#ccff00;">I'm running version 1.1</h3> -
现在我们需要增加应用程序版本;我们可以通过编辑
rolling-test项目的pom.xmlMaven 文件,并按照以下方式更改其<version>标签来完成:<version>1.1</version> -
现在,我们可以按照以下方式再次构建项目:
$ cd ~/WFC/github/wildfly-cookbook $ cd rolling-test $ mvn -e clean package -
停止
node-2并通过以下方式更新应用程序:移除旧包并复制新包到node-1和node-2:$ rm -rf /opt/wildfly/bl-rolling-node-1/deployments/rolling-test-1.0.war $ rm -rf /opt/wildfly/bl-rolling-node-2/deployments/rolling-test-1.0.war $ cp rolling-test-1.1.war /opt/wildfly/bl-rolling-node-1/deployments/ $ cp rolling-test-1.1.war /opt/wildfly/bl-rolling-node-2/deployments/小贴士
我们可以这样做,因为我们正在使用独立模式。不过,请记住,在生产环境中禁用热部署,以防万一。
-
现在,像往常一样重新启动两个节点:
$ ./bin/standalone.sh -Djboss.server.base.dir=bl-rolling-node-1 --server-config=standalone-ha.xml -Djboss.bind.address=10.0.1.1 -Djboss.management.http.port=19990 -Djboss.node.name=node-1 $ ./bin/standalone.sh -Djboss.server.base.dir=bl-rolling-node-2 --server-config=standalone-ha.xml -Djboss.bind.address=10.0.1.2 -Djboss.management.http.port=29990 -Djboss.node.name=node-2 -
返回浏览器并刷新页面;你应该得到我们超级老板希望的新功能,如下所示:
![如何操作…]()
具有新功能的滚动应用程序
并且是的,我们没有丢失会话。现在我们需要升级其他两个节点,
node-3和node-4,否则用户可能会通过访问应用程序得到以下页面:![如何操作…]()
集群中每个节点都需要更新滚动应用程序
-
因此,让我们停止
node-3和node-4,移除旧的艺术品,并按照以下方式部署新的:$ rm -rf /opt/wildfly/bl-rolling-node-3/deployments/rolling-test-1.0.war $ rm -rf /opt/wildfly/bl-rolling-node-4/deployments/rolling-test-1.0.war $ cp rolling-test-1.1.war /opt/wildfly/bl-rolling-node-3/deployments/ $ cp rolling-test-1.1.war /opt/wildfly/bl-rolling-node-4/deployments/ -
现在,像往常一样重新启动两个节点:
$ ./bin/standalone.sh -Djboss.server.base.dir=bl-rolling-node-3 --server-config=standalone-ha.xml -Djboss.bind.address=10.0.1.3 -Djboss.management.http.port=39990 -Djboss.node.name=node-3 $ ./bin/standalone.sh -Djboss.server.base.dir=bl-rolling-node-4 --server-config=standalone-ha.xml -Djboss.bind.address=10.0.1.4 -Djboss.management.http.port=49990 -Djboss.node.name=node-4 -
现在所有节点都有相同的应用程序,通过逐个停止节点并刷新页面,显然留下一个节点运行,我们应该有一个如下截图所示的场景:
![如何操作…]()
所有 4 个节点的滚动更新已完成
完美!让我们去老板的办公室告诉他我们已经完成了!
它是如何工作的…
嗯,实际上整个配置都在 Apache 端。在 WildFly 中我们没有做太多。正如你所看到的,我们配置了一个复制缓存,仅用于演示目的。复制缓存和分布式缓存之间的区别在于,在第一种情况下,所有信息都复制到所有节点,而在第二种情况下,会话仅复制到选定的节点。那些节点,如果没有其他指定,将随机选择。
从配置中,你可以通过owners属性告诉缓存将多少节点用于会话的分布。
-
我们还依赖于
standalone-ha配置文件,使用其默认值,并启动它,同时为每个节点添加一些自定义绑定。 -
我们创建了一个由四个节点组成的集群。每个节点都有自己的应用程序副本。
-
然后,我们停止了两个节点。停止两个节点允许用户使用我们的应用程序,因为他们的请求会在其他可用的节点上均衡。
注意
滚动更新可能更加困难;这完全取决于应用程序的功能和域模型的后向兼容性,这是会话中包含的内容。在我们的例子中,跨集群节点的数据对于应用程序的第一个版本和最后一个版本都是相同的。
还有更多...
在域模式下,情况大致相同:
-
首先,你需要逻辑上将集群分成两部分,就像我们在滚动更新期间所做的那样。为此,我们应该配置一个代表集群前半部分的服务器组——让我们称它为
sg-rolling-1——然后配置另一个服务器组,命名为sg-rolling-2,它将代表集群的后半部分。 -
在进行第一次滚动更新时,你会停止第一个服务器组,卸载旧的应用程序,然后部署新的应用程序。一旦部署完成,你可以重新启动服务器组。
-
当所有节点都启动并运行时,你可以为第二个服务器组重复此操作。这就是在域模式下你应该做的。
参见
如果你需要更深入地了解 WildFly 的聚类,请参阅第五章,使用 CLI 管理数据源子系统。
第八章:命令行界面(CLI)的使用
在本章中,您将学习以下食谱:
-
调用 CLI 命令
-
检查 WildFly 版本
-
检查 WildFly 操作模式
-
获取操作系统版本
-
获取 JVM 版本
-
检查 JVM 选项
-
检查 JVM 内存 - 堆大小等
-
检查服务器状态
-
检查 JNDI 树视图
-
调用外部文件中声明的 CLI 命令
简介
在本章中,您将学习如何使用 CLI 获取您所需的信息。CLI 还提供了一个方法来执行它之外的操作,通过指定要连接的 WildFly 和要执行的操作。此外,WildFly 还提供了一个 HTTP API,可以用来执行操作并检索信息。这个 API 的大部分用于系统监控。
由于 WildFly 可以以独立模式或域模式运行,每当有需要时,我们将使用这两种模式,因为您可能以不同的方式访问相同的信息。
在本章中,我们将模拟/使用远程 WildFly 实例,就像实际场景中您可以将以下食谱应用于连接到远程服务器一样。在没有看到任何身份验证和授权问题的情况下尝试localhost上的食谱是没有意义的。
要模拟远程服务器,您最终可以使用 VirtualBox 或 Docker,然后按照第一章中描述的方式安装 WildFly。
我将使用 Docker 工具版本 1.5 在 Linux 容器中运行的 WildFly。显然,您可以使用真实的远程服务器,效果是一样的;重要的是 WildFly 平台是否暴露出来。
顺便说一句,这本书的最后一章是关于使用 Docker 在 Linux 容器中运行 WildFly 的,所以如果您对 Docker 一无所知(您一直躲在哪里?),请查看这本书的最后一章,或者获取一本绝对优秀的 Docker 书籍,《Docker Book》,作者James Turnbull,www.dockerbook.com
所以,我的 Docker 配置如下:
DOCKER_HOST=tcp://192.168.59.103:2376
因此,我的远程 WildFly 实例将绑定到该 IP,以及 WildFly 的常用端口,如8080、9990和9999。相同的管理用户是本书中使用的:用户名为wildfly,密码为cookbook.2015。
调用 CLI 命令
在这个食谱中,您将学习如何直接从您的命令行调用 CLI 命令,而不需要访问 CLI 本身,比如说不是以交互式的方式。这种技术在您需要脚本一些过程时可能很有用,比如按顺序停止和启动服务器,只有在另一个应用程序已经部署的情况下才部署应用程序,等等。很多时候,您还需要监控某些状态,因此您只需要那个数字(通常在 Nagios 中看到的那样)。
准备工作
记住,我正在远程运行 WildFly,绑定到 IP 地址192.168.59.103。WildFly 已经启动并运行。
如何做到这一点…
在您的本地 PC 上,打开一个新的终端窗口并运行以下命令:
$ cd $WILDFLY_HOME
$ ./bin/jboss-cli.sh -c --controller=192.168.59.103:9990 --user=wildfly --password=cookbook.2015 --command=":read-attribute(name=release-codename)"
{
"outcome" => "success",
"result" => "Kenny"
}
显然,你可以通过在输出中使用awk命令提取你需要的信息(你可以使用你熟悉的任何工具),如下所示:
$ ./bin/jboss-cli.sh -c --controller=192.168.59.103:9990 --user=wildfly --password=cookbook.2015 --command=":read-attribute(name=release-codename)" | awk 'NR==3 { print $3 }'
"Kenny"
如果你在域模式下运行 WildFly,调用和结果都是相同的。
它是如何工作的...
实际上,没有太多可说的或解释的,只是jboss-cli.sh脚本调用了wildfly-cli-1.0.0.Beta2.jar库中的org.jboss.as.cli.CommandLineMain类,传递所有参数。--command基本上禁用了交互模式,执行语句,并在返回标准输入之前在标准输出中打印输出。
我们所做的基本上是以下操作:
$ cd $WILDFLY_HOME
$ ./bin/jboss-cli.sh
You are disconnected at the moment. Type 'connect' to connect to the server or 'help' for the list of supported commands.
[disconnected /] connect 192.168.59.103:9990
Authenticating against security realm: ManagementRealm
Username: wildfly
Password: [cookbook.2015]
[standalone@192.168.59.103:9990 /] :read-attribute(name=release-codename)
{
"outcome" => "success",
"result" => "Kenny"
}
[standalone@192.168.59.103:9990 /]
顺便说一下,你无法操作输出。
还有更多...
WildFly 提供了一个额外的 API,即 HTTP API。让我们用一些网络命令行工具,如curl来尝试一下。
curl
在一个运行的 WildFly 实例上,执行以下步骤:
-
在你的本地 PC 上,打开一个新的终端窗口并执行以下命令:
$ curl --verbose http://192.168.59.103:9990/management/?operation=attribute\&name=release—codename * Hostname was NOT found in DNS cache * Trying 192.168.59.103... * Connected to 192.168.59.103 (192.168.59.103) port 9990 (#0) > GET /management/?operation=attribute&name=release-codename HTTP/1.1 > User-Agent: curl/7.37.0 > Host: 192.168.59.103:9990 > Accept: */* > < HTTP/1.1 401 Unauthorized < Connection: keep-alive < WWW-Authenticate: Digest realm="ManagementRealm",domain="/management",nonce="it/3pAte8WkNMTQzMDkzNzkzNDYwN+FFS4e5sd6vPqf6T/M4bQI=",opaque="00000000000000000000000000000000",algorithm=MD5 < Content-Length: 77 < Content-Type: text/html < Date: Wed, 06 May 2015 18:45:34 GMT < * Connection #0 to host 192.168.59.103 left intact <html><head><title>Error</title></head><body>401 - Unauthorized</body></html>如你所见,
curl对ManagementRealm的Digest认证提出了抱怨,这是 WildFly 管理接口所使用的。 -
让我们把用户名
wildfly和密码cookbook.2015提供给命令,如下所示:$ curl --verbose --digest --user wildfly http://192.168.59.103:9990/management/?operation=attribute\&name=release-codename Enter host password for user 'wildfly': * Hostname was NOT found in DNS cache * Trying 192.168.59.103... * Connected to 192.168.59.103 (192.168.59.103) port 9990 (#0) * Server auth using Digest with user 'wildfly' > GET /management/?operation=attribute&name=release-codename HTTP/1.1 > User-Agent: curl/7.37.0 > Host: 192.168.59.103:9990 > Accept: */* > < HTTP/1.1 401 Unauthorized < Connection: keep-alive < WWW-Authenticate: Digest realm="ManagementRealm",domain="/management",nonce="6ofpOO62oNsNMTQzMDkzNzk2ODQzORxjucrlmU+bXpQSbl6Mkos=",opaque="00000000000000000000000000000000",algorithm=MD5 < Content-Length: 77 < Content-Type: text/html < Date: Wed, 06 May 2015 18:46:08 GMT < * Ignoring the response-body * Connection #0 to host 192.168.59.103 left intact * Issue another request to this URL: 'http://192.168.59.103:9990/management/?operation=attribute&name=release-codename' * Found bundle for host 192.168.59.103: 0x228bb70 * Re-using existing connection! (#0) with host 192.168.59.103 * Connected to 192.168.59.103 (192.168.59.103) port 9990 (#0) * Initializing NSS with certpath: sql:/etc/pki/nssdb * Server auth using Digest with user 'wildfly' > GET /management/?operation=attribute&name=release-codename HTTP/1.1 > Authorization: Digest username="wildfly", realm="ManagementRealm", nonce="6ofpOO62oNsNMTQzMDkzNzk2ODQzORxjucrlmU+bXpQSbl6Mkos=", uri="/management/?operation=attribute&name=release-codename", response="e52ce8f429808ef48a76da7193de27e9", opaque="00000000000000000000000000000000", algorithm="MD5" > User-Agent: curl/7.37.0 > Host: 192.168.59.103:9990 > Accept: */* > < HTTP/1.1 200 OK < Connection: keep-alive < Authentication-Info: nextnonce="tsPhgznQArENMTQzMDkzNzk2ODU0NSgg91cYeN3rLKUkBXYRMA8=" < Content-Type: application/json; charset=utf-8 < Content-Length: 7 < Date: Wed, 06 May 2015 18:46:08 GMT < * Connection #0 to host 192.168.59.103 left intact "Kenny" -
好的,它工作了;现在移除
--verbose标志并再次执行命令。你应该在输入密码后只得到Kenny。 -
如果你不想输入密码,你也可以将其作为参数传递,但这伴随着所有的安全担忧。为此,请按照以下操作进行:
$ curl --digest --user wildfly:cookbook.2015 http://192.168.59.103:9990/management/?operation=attribute\&name=release-codename "Kenny"
检查 WildFly 版本
在这个菜谱中,你将学习如何通过向 CLI 调用命令来检查你正在运行的 WildFly 版本。
准备工作
记住,我正在远程运行 WildFly,绑定到192.168.59.103作为 IP。WildFly 已经启动并运行。
如何操作...
在你的本地 PC 上,打开一个新的终端窗口并执行以下命令:
$ cd $WILDFLY_HOME
$ ./bin/jboss-cli.sh -c --controller=192.168.59.103:9990 --user=wildfly --password=cookbook.2015 --command=":read-attribute(name=product-version)"
{
"outcome" => "success",
"result" => "9.0.0.Beta2"
}
显然,你可以通过在输出中使用awk命令提取你需要的信息(你可以使用你熟悉的任何工具),如下所示:
$ ./bin/jboss-cli.sh -c --controller=192.168.59.103:9990 --user=wildfly --password=cookbook.2015 --command=":read-attribute(name=product-version)" | awk 'NR==3 { print $3 }'
"9.0.0.Beta2"
如果你在域模式下运行 WildFly,调用和结果将是相同的。
它是如何工作的...
这里我们告诉jboss-cli.sh脚本执行我们在--command参数中定义的命令,并将结果返回到标准输出。
使用 CLI,它将是这样的:
$ cd $WILDFLY_HOME
$ ./bin/jboss-cli.sh
You are disconnected at the moment. Type 'connect' to connect to the server or 'help' for the list of supported commands.
[disconnected /] connect 192.168.59.103
Authenticating against security realm: ManagementRealm
Username: wildfly
Password:
[standalone@192.168.59.103:9990 /] :read-attribute(name=product-version)
{
"outcome" => "success",
"result" => "9.0.0.Beta2"
}
[standalone@192.168.59.103:9990 /]
顺便说一下,你无法操作输出。
还有更多...
WildFly 提供了一个额外的 API,即 HTTP API。让我们用一些网络命令行工具,如curl来尝试一下。
curl
在一个运行的 WildFly 实例上,执行以下步骤:
-
打开一个新的终端窗口并运行以下命令:
$ curl --verbose http://192.168.59.103:9990/management/?operation=attribute\&name=product-version * Hostname was NOT found in DNS cache * Trying 192.168.59.103... * Connected to 192.168.59.103 (192.168.59.103) port 9990 (#0) > GET /management/?operation=attribute&name=product-version HTTP/1.1 > User-Agent: curl/7.37.1 > Host: 192.168.59.103:9990 > Accept: */* > < HTTP/1.1 401 Unauthorized < Connection: keep-alive < WWW-Authenticate: Digest realm="ManagementRealm",domain="/management",nonce="Z9fm45feS/QNMTQzMTk1ODE4OTA5OXjLOp5ZQcJ+ag+dmE6jbEM=",opaque="00000000000000000000000000000000",algorithm=MD5 < Content-Length: 77 < Content-Type: text/html < Date: Mon, 18 May 2015 14:09:49 GMT < * Connection #0 to host 192.168.59.103 left intact <html><head><title>Error</title></head><body>401 - Unauthorized</body></html>如你所见,
curl对ManagementRealm的摘要认证提出了抱怨,这是 WildFly 管理接口所使用的。 -
让我们把用户名设置为
wildfly,密码设置为cookbook.2015,如下所示:$ curl --verbose --digest --user wildfly http://192.168.59.103:9990/management/?operation=attribute\&name=product-version Enter host password for user 'wildfly': * Hostname was NOT found in DNS cache * Trying 192.168.59.103... * Connected to 192.168.59.103 (192.168.59.103) port 9990 (#0) * Server auth using Digest with user 'wildfly' > GET /management/?operation=attribute&name=product-version HTTP/1.1 > User-Agent: curl/7.37.1 > Host: 192.168.59.103:9990 > Accept: */* > < HTTP/1.1 401 Unauthorized < Connection: keep-alive < WWW-Authenticate: Digest realm="ManagementRealm",domain="/management",nonce="iRU9d5SaQI8NMTQzMTk1ODc1NTY2OAgdi5zSNYp+IAgtpOgBZRU=",opaque="00000000000000000000000000000000",algorithm=MD5 < Content-Length: 77 < Content-Type: text/html < Date: Mon, 18 May 2015 14:19:15 GMT < * Ignoring the response-body * Connection #0 to host 192.168.59.103 left intact * Issue another request to this URL: 'http://192.168.59.103:9990/management/?operation=attribute&name=product-version' * Found bundle for host 192.168.59.103: 0x7fc3a0400ed0 * Re-using existing connection! (#0) with host 192.168.59.103 * Connected to 192.168.59.103 (192.168.59.103) port 9990 (#0) * Server auth using Digest with user 'wildfly' > GET /management/?operation=attribute&name=product-version HTTP/1.1 > Authorization: Digest username="wildfly", realm="ManagementRealm", nonce="iRU9d5SaQI8NMTQzMTk1ODc1NTY2OAgdi5zSNYp+IAgtpOgBZRU=", uri="/management/?operation=attribute&name=product-version", response="e3f7a23441f44992d1d7e2c9fcc00cc2", opaque="00000000000000000000000000000000", algorithm="MD5" > User-Agent: curl/7.37.1 > Host: 192.168.59.103:9990 > Accept: */* > < HTTP/1.1 200 OK < Connection: keep-alive < Authentication-Info: nextnonce="45+yNlhzGZ8NMTQzMTk1ODc1NTY4MZP/ti6JYQlWeum3jKxWEao=" < Content-Type: application/json; charset=utf-8 < Content-Length: 13 < Date: Mon, 18 May 2015 14:19:15 GMT < * Connection #0 to host 192.168.59.103 left intact "9.0.0.Beta2"好的,它工作了;现在移除
--verbose标志并再次执行命令。输入密码后,你应该得到9.0.0.Beta2。 -
如果你不想输入密码,你也可以将其作为参数传递,但请注意这会带来所有的安全顾虑,如下所示:
$ curl --digest --user wildfly:cookbook.2015 http://192.168.59.103:9990/management/?operation=attribute\&name=product-version "9.0.0.Beta2"
检查 WildFly 操作模式
在这个菜谱中,你将学习如何通过向 CLI 发送命令来检查 WildFly 正在运行的操作模式。
准备工作
记住,我正在远程运行 WildFly,绑定到 IP 地址192.168.59.103。WildFly 已经启动并运行。
如何操作...
打开一个新的终端窗口并执行以下命令:
$ cd $WILDFLY_HOME
$ ./bin/jboss-cli.sh -c --controller=192.168.59.103:9990 --user=wildfly --password=cookbook.2015 --command=":read-attribute(name=launch-type)"
{
"outcome" => "success",
"result" => "STANDALONE"
}
显然,你可以使用awk命令从输出中提取所需的信息(你可以使用你熟悉的任何工具),如下所示:
$ ./bin/jboss-cli.sh -c --controller=192.168.59.103:9990 --user=wildfly --password=cookbook.2015 --command=":read-attribute(name=launch-type)" | awk 'NR==3 { print $3 }'
"STANDALONE"
如果你在域模式下运行 WildFly,调用方式相同,但结果如下:
"DOMAIN"
就这样!
它是如何工作的...
在这里,我们告诉jboss-cli.sh脚本执行我们在--command参数中定义的命令,并将结果输出到标准输出。
使用 CLI,如下所示:
$ cd $WILDFLY_HOME
$ ./bin/jboss-cli.sh
You are disconnected at the moment. Type 'connect' to connect to the server or 'help' for the list of supported commands.
[disconnected /] connect 192.168.59.103:9990
Authenticating against security realm: ManagementRealm
Username: wildfly
Password:
[standalone@192.168.59.103:9990 /] :read-attribute(name=launch-type)
{
"outcome" => "success",
"result" => "STANDALONE"
}
[standalone@192.168.59.103:9990 /]
顺便说一下,你不能操纵输出。
还有更多...
WildFly 提供了一个额外的 API,即 HTTP API。让我们用一些网络命令行工具,如curl来尝试它。
curl
在一个运行的 WildFly 实例中,执行以下步骤:
-
打开一个新的终端窗口并执行以下命令:
$ curl --verbose http://192.168.59.103:9990/management/?operation=attribute\&name=launch-type * Hostname was NOT found in DNS cache * Trying 192.168.59.103... * Connected to 192.168.59.103 (192.168.59.103) port 9990 (#0) > GET /management/?operation=attribute&name=launch-type HTTP/1.1 > User-Agent: curl/7.37.1 > Host: 192.168.59.103:9990 > Accept: */* > < HTTP/1.1 401 Unauthorized < Connection: keep-alive < WWW-Authenticate: Digest realm="ManagementRealm",domain="/management",nonce="oOubmxOIcS0NMTQzMTk1OTg2Mjk4Nqu/YSX7Gh368EZ1XPoG3Eg=",opaque="00000000000000000000000000000000",algorithm=MD5 < Content-Length: 77 < Content-Type: text/html < Date: Mon, 18 May 2015 14:37:42 GMT < * Connection #0 to host 192.168.59.103 left intact <html><head><title>Error</title></head><body>401 - Unauthorized</body></html>如你所见,
curl对ManagementRealm的摘要认证提出了抱怨,这是 WildFly 管理接口所使用的。 -
让我们把用户名和密码传递给命令,如下所示:
$ curl --verbose --digest --user wildfly http://192.168.59.103:9990/management/?operation=attribute\&name=launch-type Enter host password for user 'wildfly': * Hostname was NOT found in DNS cache * Trying 192.168.59.103... * Connected to 192.168.59.103 (192.168.59.103) port 9990 (#0) * Server auth using Digest with user 'wildfly' > GET /management/?operation=attribute&name=launch-type HTTP/1.1 > User-Agent: curl/7.37.1 > Host: 192.168.59.103:9990 > Accept: */* > < HTTP/1.1 401 Unauthorized < Connection: keep-alive < WWW-Authenticate: Digest realm="ManagementRealm",domain="/management",nonce="ma7nUTWjpCINMTQzMTk2MDA1MjIyOPhpd9+Np6mKrGK9OIhVD1A=",opaque="00000000000000000000000000000000",algorithm=MD5 < Content-Length: 77 < Content-Type: text/html < Date: Mon, 18 May 2015 14:40:52 GMT < * Ignoring the response-body * Connection #0 to host 192.168.59.103 left intact * Issue another request to this URL: 'http://192.168.59.103:9990/management/?operation=attribute&name=launch-type' * Found bundle for host 192.168.59.103: 0x7fb833d00c70 * Re-using existing connection! (#0) with host 192.168.59.103 * Connected to 192.168.59.103 (192.168.59.103) port 9990 (#0) * Server auth using Digest with user 'wildfly' > GET /management/?operation=attribute&name=launch-type HTTP/1.1 > Authorization: Digest username="wildfly", realm="ManagementRealm", nonce="ma7nUTWjpCINMTQzMTk2MDA1MjIyOPhpd9+Np6mKrGK9OIhVD1A=", uri="/management/?operation=attribute&name=launch-type", response="3ae09c2aaf9a1c29a0f2c5153d56d485", opaque="00000000000000000000000000000000", algorithm="MD5" > User-Agent: curl/7.37.1 > Host: 192.168.59.103:9990 > Accept: */* > < HTTP/1.1 200 OK < Connection: keep-alive < Authentication-Info: nextnonce="xWcBNZToWOoNMTQzMTk2MDA1MjIzNKGJWH9tRyHEBw/DEd3VE6w=" < Content-Type: application/json; charset=utf-8 < Content-Length: 12 < Date: Mon, 18 May 2015 14:40:52 GMT < * Connection #0 to host 192.168.59.103 left intact "STANDALONE" -
好的,它工作了;现在移除
--verbose标志并再次执行命令。输入密码后,你应该得到STANDALONE。 -
如果你不想输入密码,你也可以将其作为参数传递,但请注意这会带来所有的安全顾虑,如下所示:
$ curl --verbose --digest --user wildfly:cookbook.2015 http://192.168.59.103:9990/management/?operation=attribute\&name=launch-type "STANDALONE"
获取操作系统版本
在这个菜谱中,你将学习如何通过向 CLI 发送命令来获取 WildFly 正在运行的操作系统版本。
准备工作
记住,我正在远程运行 WildFly,绑定到 IP 地址192.168.59.103。WildFly 已经启动并运行。
如何操作...
-
打开一个新的终端窗口并运行以下命令:
$ cd $WILDFLY_HOME $ ./bin/jboss-cli.sh -c --controller=192.168.59.103:9990 --user=wildfly --password=cookbook.2015 --command="/core-service=platform-mbean/type=operating-system:read-resource(include-runtime=true,include-defaults=true)" { "outcome" => "success", "result" => { "name" => "Linux", "arch" => "amd64", "version" => "3.18.5-tinycore64", "available-processors" => 8, "system-load-average" => 0.0, "object-name" => "java.lang:type=OperatingSystem" } } -
显然,你可以使用输出中的
awk命令提取所需的信息(你可以使用你熟悉的任何工具),如下所示:$ ./bin/jboss-cli.sh -c --controller=192.168.59.103:9990 --user=wildfly --password=cookbook.2015 --command="/core-service=platform-mbean/type=operating-system:read-resource(include-runtime=true,include-defaults=true)" | awk 'NR==4 { print $3 }' "Linux", -
如果你在域模式下运行 WildFly,调用方式如下:
$ ./bin/jboss-cli.sh -c --controller=192.168.59.103:9990 --user=wildfly --password=cookbook.2015 --command="/host=master/server=server-one/core-service=platform-mbean/type=operating-system:read-resource(include-runtime=true,include-defaults=true)" { "outcome" => "success", "result" => { "name" => "Linux", "arch" => "amd64", "version" => "3.18.5-tinycore64", "available-processors" => 8, "system-load-average" => 0.0, "object-name" => "java.lang:type=OperatingSystem" } }
就这样!
它是如何工作的...
在这里,我们告诉jboss-cli.sh脚本执行我们在--command参数中定义的命令,并将结果输出到标准输出。
使用 CLI,如下所示:
$ cd $WILDFLY_HOME
$ ./bin/jboss-cli.sh
You are disconnected at the moment. Type 'connect' to connect to the server or 'help' for the list of supported commands.
[disconnected /] connect 192.168.59.103:9990
Authenticating against security realm: ManagementRealm
Username: wildfly
Password:
[standalone@192.168.59.103:9990 /] /core-service=platform-mbean/type=operating-system:read-resource(include-runtime=true,include-defaults=true)
{
"outcome" => "success",
"result" => {
"name" => "Linux",
"arch" => "amd64",
"version" => "3.16.4-tinycore64",
"available-processors" => 8,
"system-load-average" => 0.0,
"object-name" => "java.lang:type=OperatingSystem"
}
}
[standalone@192.168.59.103:9990 /]
顺便说一下,你不能操纵输出。
还有更多...
WildFly 提供了一个额外的 API,即 HTTP API。让我们用一些网络命令行工具,如curl来尝试它。
curl
在一个运行的 WildFly 实例中,执行以下步骤:
-
打开一个新的终端窗口并按照以下步骤操作:
$ curl --verbose http://192.168.59.103:9990/management/core-service/platform-mbean/type/operating-system?operation=resource\&include-runtime=true\&include-defaults=true * Hostname was NOT found in DNS cache * Trying 192.168.59.103... * Connected to 192.168.59.103 (192.168.59.103) port 9990 (#0) > GET /management/core-service/platform-mbean/type/operating-system?operation=resource&include-runtime=true&include-defaults=true HTTP/1.1 > User-Agent: curl/7.37.1 > Host: 192.168.59.103:9990 > Accept: */* > < HTTP/1.1 401 Unauthorized < Connection: keep-alive < WWW-Authenticate: Digest realm="ManagementRealm",domain="/management",nonce="vgtjzIheBzkNMTQzMTk2MDkwMzkyMPnsADViafwy586SMaSftDU=",opaque="00000000000000000000000000000000",algorithm=MD5 < Content-Length: 77 < Content-Type: text/html < Date: Mon, 18 May 2015 14:55:03 GMT < * Connection #0 to host 192.168.59.103 left intact <html><head><title>Error</title></head><body>401 - Unauthorized</body></html>如你所见,
curl对ManagementRealm的摘要认证提出了抱怨,这是 WildFly 管理接口所使用的。 -
让我们把用户名和密码提供给命令,如下所示:
$ curl --verbose --digest --user wildfly http://192.168.59.103:9990/management/core-service/platform-mbean/type/operating-system?operation=resource\&include-runtime=true\&include-defaults=true Enter host password for user 'wildfly': * Hostname was NOT found in DNS cache * Trying 192.168.59.103... * Connected to 192.168.59.103 (192.168.59.103) port 9990 (#0) * Server auth using Digest with user 'wildfly' > GET /management/core-service/platform-mbean/type/operating-system?operation=resource&include-runtime=true&include-defaults=true HTTP/1.1 > User-Agent: curl/7.37.1 > Host: 192.168.59.103:9990 > Accept: */* > < HTTP/1.1 401 Unauthorized < Connection: keep-alive < WWW-Authenticate: Digest realm="ManagementRealm",domain="/management",nonce="Z7R7/j+3b5oNMTQzMTk2MTA3NjQ1N4lD5fc075kCb3a0afe4pCg=",opaque="00000000000000000000000000000000",algorithm=MD5 < Content-Length: 77 < Content-Type: text/html < Date: Mon, 18 May 2015 14:57:56 GMT < * Ignoring the response-body * Connection #0 to host 192.168.59.103 left intact * Issue another request to this URL: 'http://192.168.59.103:9990/management/core-service/platform-mbean/type/operating-system?operation=resource&include-runtime=true&include-defaults=true' * Found bundle for host 192.168.59.103: 0x7ff722414af0 * Re-using existing connection! (#0) with host 192.168.59.103 * Connected to 192.168.59.103 (192.168.59.103) port 9990 (#0) * Server auth using Digest with user 'wildfly' > GET /management/core-service/platform-mbean/type/operating-system?operation=resource&include-runtime=true&include-defaults=true HTTP/1.1 > Authorization: Digest username="wildfly", realm="ManagementRealm", nonce="Z7R7/j+3b5oNMTQzMTk2MTA3NjQ1N4lD5fc075kCb3a0afe4pCg=", uri="/management/core-service/platform-mbean/type/operating-system?operation=resource&include-runtime=true&include-defaults=true", response="d71b0f677bfb476226880ae0f55b899c", opaque="00000000000000000000000000000000", algorithm="MD5" > User-Agent: curl/7.37.1 > Host: 192.168.59.103:9990 > Accept: */* > < HTTP/1.1 200 OK < Connection: keep-alive < Authentication-Info: nextnonce="xEfjYw89nD8NMTQzMTk2MTA3NjQ3MSkjdHABRCjFARHS2aR438k=" < Content-Type: application/json; charset=utf-8 < Content-Length: 176 < Date: Mon, 18 May 2015 14:57:56 GMT < * Connection #0 to host 192.168.59.103 left intact {"name" : "Linux", "arch" : "amd64", "version" : "3.18.5-tinycore64", "available-processors" : 8, "system-load-average" : 0.0, "object-name" : "java.lang:type=OperatingSystem"} -
好的,它工作了;现在移除
--verbose标志并再次执行命令。你应该在输入密码后只得到以下 JSON 输出:{"name" : "Linux", "arch" : "amd64", "version" : "3.16.4-tinycore64", "available-processors" : 8, "system-load-average" : 0.0, "object-name" : "java.lang:type=OperatingSystem"}很整洁,但看起来相当丑陋!
-
如果你想要美化 JSON 输出并且不想输入密码(你也可以将其作为参数传递,尽管这会带来所有的安全担忧),你可以这样做:
$ curl --digest --user wildfly:cookbook.2015 http://192.168.59.103:9990/management/core-service/platform-mbean/type/operating-system?operation=resource\&include-runtime=true\&include-defaults=true\&json.pretty=true { "name" : "Linux", "arch" : "amd64", "version" : "3.18.5-tinycore64", "available-processors" : 8, "system-load-average" : 0.0, "object-name" : "java.lang:type=OperatingSystem" }
这就是我喜欢的样子!
获取 JVM 版本
在这个菜谱中,你将学习如何通过向 CLI 发送命令来获取 WildFly 正在运行的 JVM。
准备工作
记住我正在远程运行 WildFly,绑定到 IP 192.168.59.103。WildFly 已经启动并运行。
如何做到这一点……
-
打开一个新的终端窗口并执行以下命令:
$ cd $WILDFLY_HOME $ ./bin/jboss-cli.sh -c --controller=192.168.59.103:9990 --user=wildfly --password=cookbook.2015 --command="/core-service=platform-mbean/type=runtime:read-attribute(name=spec-version)" { "outcome" => "success", "result" => "1.8" } -
显然,你可以通过使用输出中的
awk命令(你可以使用你熟悉的任何工具)来提取你需要的信息,如下所示:$ ./bin/jboss-cli.sh -c --controller=192.168.59.103:9990 --user=wildfly --password=cookbook.2015 --command="/core-service=platform-mbean/type=runtime:read-attribute(name=spec-version)" | awk 'NR==3 { print $3 }' "1.8" -
如果你是在域模式下运行 WildFly,调用方式如下:
$ ./bin/jboss-cli.sh -c --controller=192.168.59.103:9990 --user=wildfly --password=cookbook.2015 --command="/host=master/server=server-one/core-service=platform-mbean/type=runtime:read-attribute(name=spec-version)" | awk 'NR==3 { print $3 }' "1.8"
就这样!
它是如何工作的……
我们在这里告诉 jboss-cli.sh 脚本执行我们在 --command 参数中定义的命令,并将结果返回到标准输出。
使用 CLI,它将是这样的:
$ cd $WILDFLY_HOME
$ ./bin/jboss-cli.sh
You are disconnected at the moment. Type 'connect' to connect to the server or 'help' for the list of supported commands.
[disconnected /] connect 192.168.59.103:9990
Authenticating against security realm: ManagementRealm
Username: wildfly
Password:
[standalone@192.168.59.103:9990 /] /core-service=platform-mbean/type=runtime:read-attribute(name=spec-version)
{
"outcome" => "success",
"result" => "1.8"
}
[standalone@192.168.59.103:9990 /]
顺便说一下,你无法操作输出。
还有更多……
WildFly 提供了一个额外的 API,即 HTTP API。让我们用一些网络命令行工具,如 curl 来试试。
curl
在一个运行的 WildFly 实例中,执行以下步骤:
-
打开一个新的终端窗口并运行以下命令:
$ curl --verbose http://192.168.59.103:9990/management/core-service/platform-mbean/type/runtime?operation=attribute\&name=spec-version * Hostname was NOT found in DNS cache * Trying 192.168.59.103... * Connected to 192.168.59.103 (192.168.59.103) port 9990 (#0) > GET /management/core-service/platform-mbean/type/runtime?operation=attribute&name=spec-version HTTP/1.1 > User-Agent: curl/7.37.1 > Host: 192.168.59.103:9990 > Accept: */* > < HTTP/1.1 401 Unauthorized < Connection: keep-alive < WWW-Authenticate: Digest realm="ManagementRealm",domain="/management",nonce="wvo9g8raGFsNMTQzMTk2MTgwNjk1NE1gqeXx9Q8BkyC5OMS5Dy8=",opaque="00000000000000000000000000000000",algorithm=MD5 < Content-Length: 77 < Content-Type: text/html < Date: Mon, 18 May 2015 15:10:06 GMT < * Connection #0 to host 192.168.59.103 left intact <html><head><title>Error</title></head><body>401 - Unauthorized</body></html>如你所见,
curl对ManagementRealm的摘要认证提出了抱怨,这是 WildFly 管理接口所使用的。 -
让我们把用户名和密码提供给命令,如下所示:
$ curl --verbose --digest --user wildfly http://192.168.59.103:9990/management/core-service/platform-mbean/type/runtime?operation=attribute\&name=spec-version Enter host password for user 'wildfly': * Hostname was NOT found in DNS cache * Trying 192.168.59.103... * Connected to 192.168.59.103 (192.168.59.103) port 9990 (#0) * Server auth using Digest with user 'wildfly' > GET /management/core-service/platform-mbean/type/runtime?operation=attribute&name=spec-version HTTP/1.1 > User-Agent: curl/7.37.1 > Host: 192.168.59.103:9990 > Accept: */* > < HTTP/1.1 401 Unauthorized < Connection: keep-alive < WWW-Authenticate: Digest realm="ManagementRealm",domain="/management",nonce="lq4EWazA3IUNMTQzMTk2MTgzMjI3NM2iKbJNsltAipmEDWj2zBs=",opaque="00000000000000000000000000000000",algorithm=MD5 < Content-Length: 77 < Content-Type: text/html < Date: Mon, 18 May 2015 15:10:32 GMT < * Ignoring the response-body * Connection #0 to host 192.168.59.103 left intact * Issue another request to this URL: 'http://192.168.59.103:9990/management/core-service/platform-mbean/type/runtime?operation=attribute&name=spec-version' * Found bundle for host 192.168.59.103: 0x7fb35ac14a40 * Re-using existing connection! (#0) with host 192.168.59.103 * Connected to 192.168.59.103 (192.168.59.103) port 9990 (#0) * Server auth using Digest with user 'wildfly' > GET /management/core-service/platform-mbean/type/runtime?operation=attribute&name=spec-version HTTP/1.1 > Authorization: Digest username="wildfly", realm="ManagementRealm", nonce="lq4EWazA3IUNMTQzMTk2MTgzMjI3NM2iKbJNsltAipmEDWj2zBs=", uri="/management/core-service/platform-mbean/type/runtime?operation=attribute&name=spec-version", response="b91b7944f1c95154b5a0a97e2c87cace", opaque="00000000000000000000000000000000", algorithm="MD5" > User-Agent: curl/7.37.1 > Host: 192.168.59.103:9990 > Accept: */* > < HTTP/1.1 200 OK < Connection: keep-alive < Authentication-Info: nextnonce="EP8IM2uluIcNMTQzMTk2MTgzMjI4NeTU7FBtoaXVQ/6HR5zlVzw=" < Content-Type: application/json; charset=utf-8 < Content-Length: 5 < Date: Mon, 18 May 2015 15:10:32 GMT < * Connection #0 to host 192.168.59.103 left intact "1.8" -
好的,它工作了;现在移除
--verbose标志并再次执行命令。你应该在输入密码后只得到以下输出:"1.8" -
如果你不想输入密码,你也可以将其作为参数传递,尽管这会带来所有的安全担忧,如下所示:
$ curl --digest --user wildfly:cookbook.2015 http://192.168.59.103:9990/management/core-service/platform-mbean/type/runtime?operation=attribute\&name=spec-version "1.8"
这就是我喜欢的样子!
更多关于运行时类型的信息
我们在前面步骤中使用的 runtime 类型提供了很多非常有用的信息。以下是在漂亮的 JSON 格式中你可以拥有的所有信息的完整列表:
{ "name" : "1145@e0d713e81636",
"vm-name" : "Java HotSpot(TM) 64-Bit Server VM",
"vm-vendor" : "Oracle Corporation",
"vm-version" : "25.40-b25",
"spec-name" : "Java Virtual Machine Specification",
"spec-vendor" : "Oracle Corporation",
"spec-version" : "1.8",
"management-spec-version" : "1.2",
"class-path" : "/home/wildfly/WFC/wildfly/jboss-modules.jar",
"library-path" : "/usr/java/packages/lib/amd64:/usr/lib64:/lib64:/lib:/usr/lib",
"boot-class-path-supported" : true,
"boot-class-path" : "/home/wildfly/WFC/jdk8/jre/lib/resources.jar:/home/wildfly/WFC/jdk8/jre/lib/rt.jar:/home/wildfly/WFC/jdk8/jre/lib/sunrsasign.jar:/home/wildfly/WFC/jdk8/jre/lib/jsse.jar:/home/wildfly/WFC/jdk8/jre/lib/jce.jar:/home/wildfly/WFC/jdk8/jre/lib/charsets.jar:/home/wildfly/WFC/jdk8/jre/lib/jfr.jar:/home/wildfly/WFC/jdk8/jre/classes",
"input-arguments" : [
"-D[Standalone]",
"-XX:+UseCompressedOops",
"-XX:+UseCompressedOops",
"-Xms64m",
"-Xmx512m",
"-XX:MaxPermSize=256m",
"-Djava.net.preferIPv4Stack=true",
"-Djboss.modules.system.pkgs=org.jboss.byteman",
"-Djava.awt.headless=true",
"-Dorg.jboss.boot.log.file=/home/wildfly/WFC/wildfly/standalone/log/server.log",
"-Dlogging.configuration=file:/home/wildfly/WFC/wildfly/standalone/configuration/logging.properties"
],
"start-time" : 1431960757217,
"system-properties" : {
"[Standalone]" : "",
"awt.toolkit" : "sun.awt.X11.XToolkit",
"file.encoding" : "ANSI_X3.4-1968",
"file.encoding.pkg" : "sun.io",
"file.separator" : "/",
"java.awt.graphicsenv" : "sun.awt.X11GraphicsEnvironment",
"java.awt.headless" : "true",
"java.awt.printerjob" : "sun.print.PSPrinterJob",
"java.class.path" : "/home/wildfly/WFC/wildfly/jboss-modules.jar",
"java.class.version" : "52.0",
"java.endorsed.dirs" : "/home/wildfly/WFC/jdk8/jre/lib/endorsed",
"java.ext.dirs" : "/home/wildfly/WFC/jdk8/jre/lib/ext:/usr/java/packages/lib/ext",
"java.home" : "/home/wildfly/WFC/jdk8/jre",
"java.io.tmpdir" : "/tmp",
"java.library.path" : "/usr/java/packages/lib/amd64:/usr/lib64:/lib64:/lib:/usr/lib",
"java.naming.factory.url.pkgs" : "org.jboss.as.naming.interfaces:org.jboss.ejb.client.naming",
"java.net.preferIPv4Stack" : "true",
"java.runtime.name" : "Java(TM) SE Runtime Environment",
"java.runtime.version" : "1.8.0_40-b26",
"java.specification.name" : "Java Platform API
Specification",
"java.specification.vendor" : "Oracle Corporation",
"java.specification.version" : "1.8",
"java.util.logging.manager" : "org.jboss.logmanager.LogManager",
"java.vendor" : "Oracle Corporation",
"java.vendor.url" : "http://java.oracle.com/",
"java.vendor.url.bug" : "http://bugreport.sun.com/bugreport/",
"java.version" : "1.8.0_40",
"java.vm.info" : "mixed mode",
"java.vm.name" : "Java HotSpot(TM) 64-Bit Server VM",
"java.vm.specification.name" : "Java Virtual Machine Specification",
"java.vm.specification.vendor" : "Oracle Corporation",
"java.vm.specification.version" : "1.8",
"java.vm.vendor" : "Oracle Corporation",
"java.vm.version" : "25.40-b25",
"javax.management.builder.initial" : "org.jboss.as.jmx.PluggableMBeanServerBuilder",
"javax.xml.datatype.DatatypeFactory" : "__redirected.__DatatypeFactory",
"javax.xml.parsers.DocumentBuilderFactory" : "__redirected.__DocumentBuilderFactory",
"javax.xml.parsers.SAXParserFactory" :
"__redirected.__SAXParserFactory",
"javax.xml.stream.XMLEventFactory" :
"__redirected.__XMLEventFactory",
"javax.xml.stream.XMLInputFactory" : "__redirected.__XMLInputFactory",
"javax.xml.stream.XMLOutputFactory" : "__redirected.__XMLOutputFactory",
"javax.xml.transform.TransformerFactory" : "__redirected.__TransformerFactory",
"javax.xml.validation.SchemaFactory:http://www.w3.org/2001/XMLSchema" : "__redirected.__SchemaFactory",
"javax.xml.xpath.XPathFactory:http://java.sun.com/jaxp/xpath/dom" : "__redirected.__XPathFactory",
"jboss.bind.address" : "0.0.0.0",
"jboss.bind.address.management" : "0.0.0.0",
"jboss.home.dir" : "/home/wildfly/WFC/wildfly",
"jboss.host.name" : "e0d713e81636",
"jboss.modules.dir" : "/home/wildfly/WFC/wildfly/modules",
"jboss.modules.system.pkgs" : "org.jboss.byteman",
"jboss.node.name" : "e0d713e81636",
"jboss.qualified.host.name" : "e0d713e81636",
"jboss.server.base.dir" : "/home/wildfly/WFC/wildfly/standalone",
"jboss.server.config.dir" : "/home/wildfly/WFC/wildfly/standalone/configuration",
"jboss.server.data.dir" : "/home/wildfly/WFC/wildfly/standalone/data",
"jboss.server.deploy.dir" : "/home/wildfly/WFC/wildfly/standalone/data/content",
"jboss.server.log.dir" : "/home/wildfly/WFC/wildfly/standalone/log",
"jboss.server.name" : "e0d713e81636",
"jboss.server.persist.config" : "true",
"jboss.server.temp.dir" : "/home/wildfly/WFC/wildfly/standalone/tmp",
"line.separator" : "\n",
"logging.configuration" : "file:/home/wildfly/WFC/wildfly/standalone/configuration/logging.properties",
"module.path" : "/home/wildfly/WFC/wildfly/modules",
"org.apache.xml.security.ignoreLineBreaks" : "true",
"org.jboss.boot.log.file" : "/home/wildfly/WFC/wildfly/standalone/log/server.log",
"org.jboss.resolver.warning" : "true",
"org.jboss.security.context.ThreadLocal" : "true",
"org.xml.sax.driver" : "__redirected.__XMLReaderFactory",
"os.arch" : "amd64",
"os.name" : "Linux",
"os.version" : "3.18.5-tinycore64",
"path.separator" : ":",
"sun.arch.data.model" : "64",
"sun.boot.class.path" : "/home/wildfly/WFC/jdk8/jre/lib/resources.jar:/home/wildfly/WFC/jdk8/jre/lib/rt.jar:/home/wildfly/WFC/jdk8/jre/lib/sunrsasign.jar:/home/wildfly/WFC/jdk8/jre/lib/jsse.jar:/home/wildfly/WFC/jdk8/jre/lib/jce.jar:/home/wildfly/WFC/jdk8/jre/lib/charsets.jar:/home/wildfly/WFC/jdk8/jre/lib/jfr.jar:/home/wildfly/WFC/jdk8/jre/classes",
"sun.boot.library.path" : "/home/wildfly/WFC/jdk8/jre/lib/amd64",
"sun.cpu.endian" : "little",
"sun.cpu.isalist" : "",
"sun.io.unicode.encoding" : "UnicodeLittle",
"sun.java.command" : "/home/wildfly/WFC/wildfly/jboss-modules.jar -mp /home/wildfly/WFC/wildfly/modules org.jboss.as.standalone -Djboss.home.dir=/home/wildfly/WFC/wildfly -Djboss.server.base.dir=/home/wildfly/WFC/wildfly/standalone -b 0.0.0.0 -bmanagement 0.0.0.0",
"sun.java.launcher" : "SUN_STANDARD",
"sun.jnu.encoding" : "ANSI_X3.4-1968",
"sun.management.compiler" : "HotSpot 64-Bit Tiered Compilers",
"sun.nio.ch.bugLevel" : "",
"sun.os.patch.level" : "unknown",
"user.country" : "US",
"user.dir" : "/home/wildfly/WFC/wildfly",
"user.home" : "/home/wildfly",
"user.language" : "en",
"user.name" : "wildfly",
"user.timezone" : "America/New_York"
},
"uptime" : 1393273,
"object-name" : "java.lang:type=Runtime"
}
如你所见,有很多信息,并且使用 operation=attribute 指令,你可以直接获取你需要的属性。否则使用 awk 命令。
检查 JVM 选项
在这个菜谱中,你将学习如何通过向 CLI 发送命令来获取运行 WildFly 所使用的 JVM 选项。
准备工作
记住我正在远程运行 WildFly,绑定到 IP 地址192.168.59.103。WildFly 已经启动并运行。
如何操作…
-
打开一个新的终端窗口并执行以下命令:
$ cd WILDFLY_HOME $ ./bin/jboss-cli.sh -c --controller=192.168.59.103:9990 --user=wildfly --password=cookbook.2015 --command="/core-service=platform-mbean/type=runtime:read-attribute(name=input-arguments,include-defaults=true)" { "outcome" => "success", "result" => [ "-D[Standalone]", "-Xms64m", "-Xmx512m", "-XX:MaxPermSize=256m", "-Djava.net.preferIPv4Stack=true", "-Djboss.modules.system.pkgs=org.jboss.byteman", "-Djava.awt.headless=true", "-Dorg.jboss.boot.log.file=/home/wildfly/WFC/wildfly/standalone/log/server.log", "-Dlogging.configuration=file:home/wildfly/WFC/wildfly/standalone/configuration/logging.properties" ] } -
显然,您可以使用输出中的
awk命令提取所需的信息(您可以使用您熟悉的任何工具),如下所示:$ ./bin/jboss-cli.sh -c --controller=192.168.59.103:9990 --user=wildfly --password=cookbook.2015 --command="/core-service=platform-mbean/type=runtime:read-attribute(name=input-arguments,include-defaults=true)" | awk 'NR==6 { print $1 }' "-Xmx521m", -
如果您在域模式下运行 WildFly,调用方式如下:
$ ./bin/jboss-cli.sh -c --controller=192.168.59.103:9990 --user=wildfly --password=cookbook.2015 --command="/host=master/server=server-one/core-service=platform-mbean/type=runtime:read-attribute(name=input-arguments,include-defaults=true)" | awk 'NR==6 { print $1 }' "-Xmx521m",
就这样!
它是如何工作的…
这里我们告诉jboss-cli.sh脚本来执行我们在--command参数中定义的命令,并将结果返回到标准输出。
使用 CLI,操作如下:
$ cd $WILDFLY_HOME
$ ./bin/jboss-cli.sh
You are disconnected at the moment. Type 'connect' to connect to the server or 'help' for the list of supported commands.
[disconnected /] connect 192.168.59.103:9990
Authenticating against security realm: ManagementRealm
Username: wildfly
Password:
[standalone@192.168.59.103:9990 /] /core-service=platform-mbean/type=runtime:read-attribute(name=input-arguments,include-defaults=true)
{
"outcome" => "success",
"result" => [
"-D[Standalone]",
"-Xms64m",
"-Xmx512m",
"-XX:MaxPermSize=256m",
"-Djava.net.preferIPv4Stack=true",
"-Djboss.modules.system.pkgs=org.jboss.byteman",
"-Djava.awt.headless=true",
"-Dorg.jboss.boot.log.file=/home/wildfly/WFC/wildfly/standalone/log/server.log",
"-Dlogging.configuration=file:/home/wildfly/WFC/wildfly/standalone/configuration/logging.properties"
]
}
[standalone@192.168.59.103:9990 /]
记住,您不能操作输出。
还有更多…
WildFly 提供了一个额外的 API,即 HTTP API。让我们用一些网络命令行工具,如curl来尝试它。
curl
在一个运行的 WildFly 实例中,执行以下步骤:
-
打开一个新的终端窗口并按照以下步骤操作:
$ curl --verbose http://192.168.59.103:9990/management/core-service/platform-mbean/type/runtime?operation=attribute\&name=input-arguments\&include-defaults=true * Hostname was NOT found in DNS cache * Trying 192.168.59.103... * Connected to 192.168.59.103 (192.168.59.103) port 9990 (#0) > GET /management/core-service/platform-mbean/type/runtime?operation=attribute&name=input-arguments&include-defaults=true HTTP/1.1 > User-Agent: curl/7.37.1 > Host: 192.168.59.103:9990 > Accept: */* > < HTTP/1.1 401 Unauthorized < Connection: keep-alive < WWW-Authenticate: Digest realm="ManagementRealm",domain="/management",nonce="Hipx4QD6g24NMTQzMTk2MjY2ODcyNhXQ5pLbS5A/ZruXxypB1gM=",opaque="00000000000000000000000000000000",algorithm=MD5 < Content-Length: 77 < Content-Type: text/html < Date: Mon, 18 May 2015 15:24:28 GMT < * Connection #0 to host 192.168.59.103 left intact <html><head><title>Error</title></head><body>401 - Unauthorized</body></html>如您所见,
curl对ManagementRealm的摘要认证提出了抱怨,这是 WildFly 管理接口所使用的。 -
让我们把用户名和密码提供给命令,如下所示:
$ curl --verbose --digest --user wildfly http://192.168.59.103:9990/management/core-service/platform-mbean/type/runtime?operation=attribute\&name=input-arguments\&include-defaults=true Enter host password for user 'wildfly': * Hostname was NOT found in DNS cache * Trying 192.168.59.103... * Connected to 192.168.59.103 (192.168.59.103) port 9990 (#0) * Server auth using Digest with user 'wildfly' > GET /management/core-service/platform-mbean/type/runtime?operation=attribute&name=input-arguments&include-defaults=true HTTP/1.1 > User-Agent: curl/7.37.1 > Host: 192.168.59.103:9990 > Accept: */* > < HTTP/1.1 401 Unauthorized < Connection: keep-alive < WWW-Authenticate: Digest realm="ManagementRealm",domain="/management",nonce="QVV7pMN+yIYNMTQzMTk2Mjg1MzAwNIEcpzHTEA2YeGu3jANV1bI=",opaque="00000000000000000000000000000000",algorithm=MD5 < Content-Length: 77 < Content-Type: text/html < Date: Mon, 18 May 2015 15:27:33 GMT < * Ignoring the response-body * Connection #0 to host 192.168.59.103 left intact * Issue another request to this URL: 'http://192.168.59.103:9990/management/core-service/platform-mbean/type/runtime?operation=attribute&name=input-arguments&include-defaults=true' * Found bundle for host 192.168.59.103: 0x7fd4c0500e00 * Re-using existing connection! (#0) with host 192.168.59.103 * Connected to 192.168.59.103 (192.168.59.103) port 9990 (#0) * Server auth using Digest with user 'wildfly' > GET /management/core-service/platform-mbean/type/runtime?operation=attribute&name=input-arguments&include-defaults=true HTTP/1.1 > Authorization: Digest username="wildfly", realm="ManagementRealm", nonce="QVV7pMN+yIYNMTQzMTk2Mjg1MzAwNIEcpzHTEA2YeGu3jANV1bI=", uri="/management/core-service/platform-mbean/type/runtime?operation=attribute&name=input-arguments&include-defaults=true", response="8d550a343d3a56cfcd7afdd6ca0c1664", opaque="00000000000000000000000000000000", algorithm="MD5" > User-Agent: curl/7.37.1 > Host: 192.168.59.103:9990 > Accept: */* > < HTTP/1.1 200 OK < Connection: keep-alive < Authentication-Info: nextnonce="UkyiLKmVGuYNMTQzMTk2Mjg1MzAwN08EKUU+895ECweZBtXoCdQ=" < Content-Type: application/json; charset=utf-8 < Content-Length: 402 < Date: Mon, 18 May 2015 15:27:33 GMT < * Connection #0 to host 192.168.59.103 left intact ["-D[Standalone]","-XX:+UseCompressedOops","-XX:+UseCompressedOops","-Xms64m","-Xmx512m","-XX:MaxPermSize=256m","-Djava.net.preferIPv4Stack=true","-Djboss.modules.system.pkgs=org.jboss.byteman","-Djava.awt.headless=true","-Dorg.jboss.boot.log.file=/home/wildfly/WFC/wildfly/standalone/log/server.log","-Dlogging.configuration=file:/home/wildfly/WFC/wildfly/standalone/configuration/logging.properties"] -
好的,它工作了;现在移除
--verbose标志并再次执行命令。在输入密码后,您应该只得到以下 JSON 输出,如下所示:["-D[Standalone]","-XX:+UseCompressedOops","-XX:+UseCompressedOops","-Xms64m","-Xmx512m","-XX:MaxPermSize=256m","-Djava.net.preferIPv4Stack=true","-Djboss.modules.system.pkgs=org.jboss.byteman","-Djava.awt.headless=true","-Dorg.jboss.boot.log.file=/home/wildfly/WFC/wildfly/standalone/log/server.log","-Dlogging.configuration=file:/home/wildfly/WFC/wildfly/standalone/configuration/logging.properties"]很整洁,但看起来很丑!
-
如果您想美化 JSON 输出并且不想输入密码(您也可以将其作为参数传递,尽管这会带来所有的安全问题),您可以这样做:
$ curl --digest --user wildfly:cookbook.2015 http://192.168.59.103:9990/management/core-service/platform-mbean/type/runtime?operation=attribute\&name=input-arguments\&include-defaults=true\&json.pretty=true[ "-D[Standalone]", "-XX:+UseCompressedOops", "-XX:+UseCompressedOops", "-Xms64m", "-Xmx512m", "-XX:MaxPermSize=256m", "-Djava.net.preferIPv4Stack=true", "-Djboss.modules.system.pkgs=org.jboss.byteman", "-Djava.awt.headless=true", "-Dorg.jboss.boot.log.file=/home/wildfly/WFC/wildfly/standalone/log/server.log", "-Dlogging.configuration=file:/home/wildfly/WFC/wildfly/standalone/configuration/logging.properties" ]
这就是我喜欢的样子!
检查 JVM 内存 - 堆大小和所有
在这个菜谱中,我们将学习如何通过调用 CLI 命令来获取 JVM 内存信息,包括堆大小、非堆大小、元空间大小(Java 7 之前的 PermGen)、eden、old 和 survivor 区域,如下所示。每个都有对应的命令。
准备工作
记住我正在远程运行 WildFly,绑定到 IP 地址192.168.59.103。WildFly 已经启动并运行。
如何操作…
我们将分别走过"heap"、"non-heap"、"metaspace"(Java 7 之前的 PermGen)、"eden"、"old"和"survivor"区域内存。
堆
要获取堆内存信息,请执行以下步骤:
-
打开一个新的终端窗口并按照以下步骤操作:
$ cd $WILDFLY_HOME $ ./bin/jboss-cli.sh -c --controller=192.168.59.103:9990 --user=wildfly --password=cookbook.2015 --command="/core-service=platform-mbean/type=memory:read-attribute(name=heap-memory-usage,include-defaults=true)" { "outcome" => "success", "result" => { "init" => 67108864L, "used" => 90009368L, "committed" => 199753728L, "max" => 477626368L } } -
显然,您可以使用输出中的
awk命令提取所需的信息(您可以使用您熟悉的任何工具),如下所示:$ ./bin/jboss-cli.sh -c --controller=192.168.59.103:9990 --user=wildfly --password=cookbook.2015 --command="/core-service=platform-mbean/type=memory:read-attribute(name=heap-memory-usage,include-defaults=true)" | awk 'NR==7 { print $0 }' "max" => 477626368L小贴士
如果您只需要数字,它是一个
long数据类型,请在print语句中使用$3。 -
如果您在域模式下运行 WildFly,调用方式如下:
$ ./bin/jboss-cli.sh --connect --controller=192.168.59.103:9990 --user=wildfly --password=cookbook.2015 --command="/host=master/server=server-one/core-service=platform-mbean/type=memory:read-attribute(name=heap-memory-usage,include-defaults=true)" { "outcome" => "success", "result" => { "init" => 67108864L, "used" => 78141544L, "committed" => 147324928L, "max" => 477626368L } }
就这样!
非堆
要获取非堆内存信息,请执行以下步骤:
-
打开一个新的终端窗口并执行以下命令:
$ cd $WILDFLY_HOME $ ./bin/jboss-cli.sh -c --controller=192.168.59.103:9990 --user=wildfly --password=cookbook.2015 --command="/core-service=platform-mbean/type=memory:read-attribute(name=non-heap-memory-usage,include-defaults=true)" { "outcome" => "success", "result" => { "init" => 2555904L, "used" => 55613336L, "committed" => 62128128L, "max" => -1L } } -
显然,您可以使用输出中的
awk命令提取所需的信息(您可以使用您熟悉的任何工具),如下所示:$ ./bin/jboss-cli.sh -c --controller=192.168.59.103:9990 --user=wildfly --password=cookbook.2015 --command="/core-service=platform-mbean/type=memory:read-attribute(name=non-heap-memory-usage,include-defaults=true)" | awk 'NR==5 { print $0 }' "used" => 55613336L, -
如果您在域模式下运行 WildFly,调用方式如下:
$ ./bin/jboss-cli.sh -c --controller=192.168.59.103:9990 --user=wildfly --password=cookbook.2015 --command="/host=master/server=server-one/core-service=platform-mbean/type=memory:read-attribute(name=non-heap-memory-usage,include-defaults=true)" { "outcome" => "success", "result" => { "init" => 2555904L, "used" => 65766592L, "committed" => 73310208L, "max" => -1L } }
就这样!
Metaspace 或 PermGen
Metaspace 自 Java 1.8 起可用,而 PermGen 直到 Java 1.7 可用。对于元空间内存信息,请执行以下步骤:
-
打开一个新的终端窗口并按照以下步骤操作:
$ cd $WILDFLY_HOME $ ./bin/jboss-cli.sh -c --controller=192.168.59.103:9990 --user=wildfly --password=cookbook.2015 --command="/core-service=platform-mbean/type=memory-pool/name=Metaspace:read-resource(include-runtime=true,include-defaults=true)" { "outcome" => "success", "result" => { "name" => "Metaspace", "type" => "NON_HEAP", "valid" => true, "memory-manager-names" => ["Metaspace_Manager"], "usage-threshold-supported" => true, "collection-usage-threshold-supported" => false, "usage-threshold" => 0L, "collection-usage-threshold" => undefined, "usage" => { "init" => 0L, "used" => 42415280L, "committed" => 47185920L, "max" => -1L }, "peak-usage" => { "init" => 0L, "used" => 42415280L, "committed" => 47185920L, "max" => -1L }, "usage-threshold-exceeded" => false, "usage-threshold-count" => 0L, "collection-usage-threshold-exceeded" => undefined, "collection-usage-threshold-count" => undefined, "collection-usage" => undefined, "object-name" => "java.lang:type=MemoryPool,name=Metaspace" } } -
显然,您可以通过使用输出中的
awk命令来提取所需的信息(您可以使用您熟悉的任何工具)。然而,在这种情况下,您最好使用read-attribute语法,然后使用awk命令提取您想要的信息,如下所示:$ ./bin/jboss-cli.sh -c --controller=192.168.59.103:9990 --user=wildfly --password=cookbook.2015 --command="/core-service=platform-mbean/type=memory-pool/name=Metaspace:read-attribute(name=usage,include-defaults=true)" { "outcome" => "success", "result" => { "init" => 0L, "used" => 42512536L, "committed" => 47448064L, "max" => -1L } } -
如果您在域模式下运行 WildFly,调用方式如下:
$ ./bin/jboss-cli.sh --connect --controller=192.168.59.103:9990 --user=wildfly --password=cookbook.2015 --command="/host=master/server=server-one/core-service=platform-mbean/type=memory-pool/name=Metaspace:read-resource(include-runtime=true,include-defaults=true)" { "outcome" => "success", "result" => { "name" => "Metaspace", "type" => "NON_HEAP", "valid" => true, "memory-manager-names" => ["Metaspace_Manager"], "usage-threshold-supported" => true, "collection-usage-threshold-supported" => false, "usage-threshold" => 0L, "collection-usage-threshold" => undefined, "usage" => { "init" => 0L, "used" => 51111424L, "committed" => 56885248L, "max" => -1L }, "peak-usage" => { "init" => 0L, "used" => 51111424L, "committed" => 56885248L, "max" => -1L }, "usage-threshold-exceeded" => false, "usage-threshold-count" => 0L, "collection-usage-threshold-exceeded" => undefined, "collection-usage-threshold-count" => undefined, "collection-usage" => undefined, "object-name" => "java.lang:type=MemoryPool,name=Metaspace" } }
就这样!
Eden
要获取 eden 内存信息,请执行以下步骤:
-
打开一个新的终端窗口并输入以下命令:
$ cd /opt/wildfly $ ./bin/jboss-cli.sh -c --controller=192.168.59.103:9990 --user=wildfly --password=cookbook.2015 --command="/core-service=platform-mbean/type=memory-pool/name=PS_Eden_Space:read-resource(include-runtime=true,include-defaults=true)" { "outcome" => "success", "result" => { "name" => "PS_Eden_Space", "type" => "HEAP", "valid" => true, "memory-manager-names" => [ "PS_MarkSweep", "PS_Scavenge" ], "usage-threshold-supported" => false, "collection-usage-threshold-supported" => true, "usage-threshold" => undefined, "collection-usage-threshold" => 0L, "usage" => { "init" => 16777216L, "used" => 61270456L, "committed" => 147849216L, "max" => 147849216L }, "peak-usage" => { "init" => 16777216L, "used" => 67108864L, "committed" => 147849216L, "max" => 173539328L }, "usage-threshold-exceeded" => undefined, "usage-threshold-count" => undefined, "collection-usage-threshold-exceeded" => false, "collection-usage-threshold-count" => 0L, "collection-usage" => { "init" => 16777216L, "used" => 0L, "committed" => 147849216L, "max" => 147849216L }, "object-name" => "java.lang:type=MemoryPool,name=\"PS Eden Space\"" } } -
显然,您可以通过使用输出中的
awk命令来提取所需的信息(您可以使用您熟悉的任何工具)。然而,在这种情况下,您最好使用read-attribute语法,然后awk您想要的信息,如下所示:$ ./bin/jboss-cli.sh --connect --controller=192.168.59.103:9990 --user=wildfly --password=cookbook.2015 --command="/core-service=platform-mbean/type=memory-pool/name=PS_Eden_Space:read-attribute(name=usage,include-defaults=true)" { "outcome" => "success", "result" => { "init" => 16777216L, "used" => 70648432L, "committed" => 147849216L, "max" => 147849216L } } -
如果您在域模式下运行 WildFly,调用方式如下:
$ ./bin/jboss-cli.sh --connect --controller=192.168.59.103:9990 --user=wildfly --password=cookbook.2015 --command="/host=master/server=server-one/core-service=platform-mbean/type=memory-pool/name=PS_Eden_Space:read-resource(include-runtime=true,include-defaults=true)" { "outcome" => "success", "result" => { "name" => "PS_Eden_Space", "type" => "HEAP", "valid" => true, "memory-manager-names" => [ "PS_MarkSweep", "PS_Scavenge" ], "usage-threshold-supported" => false, "collection-usage-threshold-supported" => true, "usage-threshold" => undefined, "collection-usage-threshold" => 0L, "usage" => { "init" => 16777216L, "used" => 40625992L, "committed" => 58720256L, "max" => 144703488L }, "peak-usage" => { "init" => 16777216L, "used" => 58720256L, "committed" => 58720256L, "max" => 173539328L }, "usage-threshold-exceeded" => undefined, "usage-threshold-count" => undefined, "collection-usage-threshold-exceeded" => false, "collection-usage-threshold-count" => 0L, "collection-usage" => { "init" => 16777216L, "used" => 0L, "committed" => 58720256L, "max" => 144703488L }, "object-name" => "java.lang:type=MemoryPool,name=\"PS Eden Space\"" } }
就这样!
旧
要获取旧内存信息,请执行以下步骤:
-
打开一个新的终端窗口并执行以下操作:
$ cd /opt/wildfly $ ./bin/jboss-cli.sh -c --controller=192.168.59.103:9990 --user=wildfly --password=cookbook.2015 --command="/core-service=platform-mbean/type=memory-pool/name=PS_Old_Gen:read-resource(include-runtime=true,include-defaults=true)"{ "outcome" => "success", "result" => { "name" => "PS_Old_Gen", "type" => "HEAP", "valid" => true, "memory-manager-names" => ["PS_MarkSweep"], "usage-threshold-supported" => true, "collection-usage-threshold-supported" => true, "usage-threshold" => 0L, "collection-usage-threshold" => 0L, "usage" => { "init" => 45088768L, "used" => 17330048L, "committed" => 59244544L, "max" => 358088704L }, "peak-usage" => { "init" => 45088768L, "used" => 17330048L, "committed" => 59244544L, "max" => 358088704L }, "usage-threshold-exceeded" => false, "usage-threshold-count" => 0L, "collection-usage-threshold-exceeded" => false, "collection-usage-threshold-count" => 0L, "collection-usage" => { "init" => 45088768L, "used" => 17330048L, "committed" => 59244544L, "max" => 358088704L }, "object-name" => "java.lang:type=MemoryPool,name=\"PS Old Gen\"" } } -
显然,您可以通过使用输出中的
awk命令来提取所需的信息(您可以使用您熟悉的任何工具)。然而,在这种情况下,您最好使用read-attribute语法,然后awk您想要的信息,如下所示:$ ./bin/jboss-cli.sh --connect --controller=192.168.59.103:9990 --user=wildfly --password=cookbook.2015 --command="/core-service=platform-mbean/type=memory-pool/name=PS_Old_Gen:read-attribute(name=usage,include-defaults=true)"{ "outcome" => "success", "result" => { "init" => 45088768L, "used" => 17330048L, "committed" => 59244544L, "max" => 358088704L } } -
如果您在域模式下运行 WildFly,调用方式如下:
$ ./bin/jboss-cli.sh --connect --controller=192.168.59.103:9990 --user=wildfly --password=cookbook.2015 --command="/host=master/server=server-one/core-service=platform-mbean/type=memory-pool/name=PS_Old_Gen:read-resource(include-runtime=true,include-defaults=true)" { "outcome" => "success", "result" => { "name" => "PS_Old_Gen", "type" => "HEAP", "valid" => true, "memory-manager-names" => ["PS_MarkSweep"], "usage-threshold-supported" => true, "collection-usage-threshold-supported" => true, "usage-threshold" => 0L, "collection-usage-threshold" => 0L, "usage" => { "init" => 45088768L, "used" => 21521968L, "committed" => 70254592L, "max" => 358088704L }, "peak-usage" => { "init" => 45088768L, "used" => 21521968L, "committed" => 70254592L, "max" => 358088704L }, "usage-threshold-exceeded" => false, "usage-threshold-count" => 0L, "collection-usage-threshold-exceeded" => false, "collection-usage-threshold-count" => 0L, "collection-usage" => { "init" => 45088768L, "used" => 18670576L, "committed" => 70254592L, "max" => 358088704L }, "object-name" => "java.lang:type=MemoryPool,name=\"PS Old Gen\"" } }
就这样!
幸存者
要获取幸存者内存信息,请执行以下步骤:
-
打开一个新的终端窗口并输入以下命令:
$ cd /opt/wildfly $ ./bin/jboss-cli.sh -c --controller=192.168.59.103:9990 --user=wildfly --password=cookbook.2015 --command="/core-service=platform-mbean/type=memory-pool/name=PS_Survivor_Space:read-resource(include-runtime=true,include-defaults=true)" { "outcome" => "success", "result" => { "name" => "PS_Survivor_Space", "type" => "HEAP", "valid" => true, "memory-manager-names" => [ "PS_MarkSweep", "PS_Scavenge" ], "usage-threshold-supported" => false, "collection-usage-threshold-supported" => true, "usage-threshold" => undefined, "collection-usage-threshold" => 0L, "usage" => { "init" => 2621440L, "used" => 0L, "committed" => 15728640L, "max" => 15728640L }, "peak-usage" => { "init" => 2621440L, "used" => 12809608L, "committed" => 15728640L, "max" => 15728640L }, "usage-threshold-exceeded" => undefined, "usage-threshold-count" => undefined, "collection-usage-threshold-exceeded" => false, "collection-usage-threshold-count" => 0L, "collection-usage" => { "init" => 2621440L, "used" => 0L, "committed" => 15728640L, "max" => 15728640L }, "object-name" => "java.lang:type=MemoryPool,name=\"PS Survivor Space\"" } } -
显然,您可以通过使用输出中的
awk命令来提取所需的信息(您可以使用您熟悉的任何工具)。然而,在这种情况下,您最好使用read-attribute语法,然后使用awk命令提取您想要的信息,如下所示:$ ./bin/jboss-cli.sh --connect --controller=192.168.59.103:9990 --user=wildfly --password=cookbook.2015 --command="/core-service=platform-mbean/type=memory-pool/name=PS_Survivor_Space:read-attribute(name=usage,include-defaults=true)" { "outcome" => "success", "result" => { "init" => 2621440L, "used" => 0L, "committed" => 15728640L, "max" => 15728640L } } -
如果您在域模式下运行 WildFly,调用方式如下:
$ ./bin/jboss-cli.sh --connect --controller=192.168.59.103:9990 --user=wildfly --password=cookbook.2015 --command="/host=master/server=server-one/core-service=platform-mbean/type=memory-pool/name=PS_Survivor_Space:read-resource(include-runtime=true,include-defaults=true)" { "outcome" => "success", "result" => { "name" => "PS_Survivor_Space", "type" => "HEAP", "valid" => true, "memory-manager-names" => [ "PS_MarkSweep", "PS_Scavenge" ], "usage-threshold-supported" => false, "collection-usage-threshold-supported" => true, "usage-threshold" => undefined, "collection-usage-threshold" => 0L, "usage" => { "init" => 2621440L, "used" => 15199856L, "committed" => 15204352L, "max" => 15204352L }, "peak-usage" => { "init" => 2621440L, "used" => 15199856L, "committed" => 15204352L, "max" => 15204352L }, "usage-threshold-exceeded" => undefined, "usage-threshold-count" => undefined, "collection-usage-threshold-exceeded" => false, "collection-usage-threshold-count" => 0L, "collection-usage" => { "init" => 2621440L, "used" => 15199856L, "committed" => 15204352L, "max" => 15204352L }, "object-name" => "java.lang:type=MemoryPool,name=\"PS Survivor Space\"" } }
就这样!
它是如何工作的……
在这里,我们告诉jboss-cli.sh脚本来执行我们在--command参数中定义的命令,并将结果返回到标准输出。
使用 CLI,它将是这样的:
$ ./bin/jboss-cli.sh
You are disconnected at the moment. Type 'connect' to connect to the server or 'help' for the list of supported commands.
[disconnected /] connect 192.168.59.103:9990
Authenticating against security realm: ManagementRealm
Username: wildfly
Password:
[standalone@192.168.59.103:9990 /] /core-service=platform-mbean/type=memory:read-attribute(name=heap-memory-usage,include-defaults=true)
{
"outcome" => "success",
"result" => {
"init" => 67108864L,
"used" => 76422344L,
"committed" => 175112192L,
"max" => 477626368L
}
}
[standalone@192.168.59.103:9990 /]
顺便说一下,您无法操作输出。
您可以尝试自己替换感兴趣的内存名称,以尝试其他内存空间。
还有更多……
WildFly 提供另一个 API,即 HTTP API。让我们用一些网络命令行工具,如curl来尝试它。
这里我们将以 JVM 堆内存为例进行考察。
Curl
使用 curl 执行以下步骤:
-
在一个运行的 WildFly 实例中,打开一个新的终端窗口并执行以下命令:
$ curl --verbose http://192.168.59.103:9990/management/core-service/platform-mbean/type/memory?operation=attribute\&name=heap-memory-usage * Hostname was NOT found in DNS cache * Trying 192.168.59.103... * Connected to 192.168.59.103 (192.168.59.103) port 9990 (#0) > GET /management/core-service/platform-mbean/type/memory?operation=attribute&name=heap-memory-usage HTTP/1.1 > User-Agent: curl/7.37.1 > Host: 192.168.59.103:9990 > Accept: */* > < HTTP/1.1 401 Unauthorized < Connection: keep-alive < WWW-Authenticate: Digest realm="ManagementRealm",domain="/management",nonce="SwXvsbP7j0UNMTQzMjAxNjUxODY4Ml1gpwERAQYWFmaOqeygbx4=",opaque="00000000000000000000000000000000",algorithm=MD5 < Content-Length: 77 < Content-Type: text/html < Date: Tue, 19 May 2015 06:21:58 GMT < * Connection #0 to host 192.168.59.103 left intact <html><head><title>Error</title></head><body>401 - Unauthorized</body></html>如您所见,
curl对ManagementRealm的摘要认证提出了抱怨,这是 WildFly 管理接口所使用的。 -
让我们给命令提供用户名和密码,如下所示:
$ curl --verbose --digest --user wildfly http://192.168.59.103:9990/management/core-service/platform-mbean/type/memory?operation=attribute\&name=heap-memory-usage Enter host password for user 'wildfly': * Hostname was NOT found in DNS cache * Trying 192.168.59.103... * Connected to 192.168.59.103 (192.168.59.103) port 9990 (#0) * Server auth using Digest with user 'wildfly' > GET /management/core-service/platform-mbean/type/memory?operation=attribute&name=heap-memory-usage HTTP/1.1 > User-Agent: curl/7.37.1 > Host: 192.168.59.103:9990 > Accept: */* > < HTTP/1.1 401 Unauthorized < Connection: keep-alive < WWW-Authenticate: Digest realm="ManagementRealm",domain="/management",nonce="47ky6duDTAANMTQzMjAxNjY2NTc1NDQr1KuuN1GzJ50GQ0PUuCs=",opaque="00000000000000000000000000000000",algorithm=MD5 < Content-Length: 77 < Content-Type: text/html < Date: Tue, 19 May 2015 06:24:25 GMT < * Ignoring the response-body * Connection #0 to host 192.168.59.103 left intact * Issue another request to this URL: 'http://192.168.59.103:9990/management/core-service/platform-mbean/type/memory?operation=attribute&name=heap-memory-usage' * Found bundle for host 192.168.59.103: 0x7fa919d00dd0 * Re-using existing connection! (#0) with host 192.168.59.103 * Connected to 192.168.59.103 (192.168.59.103) port 9990 (#0) * Server auth using Digest with user 'wildfly' > GET /management/core-service/platform-mbean/type/memory?operation=attribute&name=heap-memory-usage HTTP/1.1 > Authorization: Digest username="wildfly", realm="ManagementRealm", nonce="47ky6duDTAANMTQzMjAxNjY2NTc1NDQr1KuuN1GzJ50GQ0PUuCs=", uri="/management/core-service/platform-mbean/type/memory?operation=attribute&name=heap-memory-usage", response="f04575d464d4151697dc6468610ec989", opaque="00000000000000000000000000000000", algorithm="MD5" > User-Agent: curl/7.37.1 > Host: 192.168.59.103:9990 > Accept: */* > < HTTP/1.1 200 OK < Connection: keep-alive < Authentication-Info: nextnonce="kFsM2zG0/XANMTQzMjAxNjY2NTc2MP4TtqHrlyzAujuq4ZLn9ek=" < Content-Type: application/json; charset=utf-8 < Content-Length: 82 < Date: Tue, 19 May 2015 06:24:25 GMT < * Connection #0 to host 192.168.59.103 left intact {"init" : 67108864, "used" : 62314024, "committed" : 222298112, "max" : 477626368} -
好的,它工作了;现在移除
--verbose标志并再次执行命令。输入密码后,你应该只得到以下 JSON 输出:{"init" : 67108864, "used" : 66257248, "committed" : 222298112, "max" : 477626368}很酷,但看起来很丑!
-
如果你想要美化 JSON 输出并且不想输入密码(你也可以作为参数传递,但会带来所有的安全顾虑),你可以执行以下命令:
$ curl --digest --user wildfly:cookbook.2015 http://192.168.59.103:9990/management/core-service/platform-mbean/type/memory?operation=attribute\&name=heap-memory-usage\&json.pretty=true{ "init" : 67108864, "used" : 67826544, "committed" : 222298112, "max" : 477626368 }
这就是我喜欢的样子!
检查服务器状态
在这个菜谱中,我们将学习如何通过调用 CLI 命令来检查运行中的 WildFly 实例的状态。
准备中
记住我正在远程运行 WildFly,绑定到 IP 地址192.168.59.103。WildFly 已经启动并运行。
如何操作…
打开一个新的终端窗口并执行以下命令:
$ cd $WILDFLY_HOME
$ ./bin/jboss-cli.sh -c --controller=192.168.59.103:9990 --user=wildfly --password=cookbook.2015 --command=":read-attribute(name=server-state)"
{
"outcome" => "success",
"result" => "running"
}
你可以通过在输出中使用awk命令提取所需的信息(你可以使用你熟悉的任何工具),如下所示:
$ ./bin/jboss-cli.sh -c --controller=192.168.59.103:9990 --user=wildfly --password=cookbook.2015 --command=":read-attribute(name=server-state)" | awk 'NR==3 { print $3 }'
"running"
如果你以域模式运行 WildFly,调用方式如下:
$ ./bin/jboss-cli.sh -c --controller=192.168.59.103:9990 --user=wildfly --password=cookbook.2015 --command="/host=master/server=server-one:read-attribute(name=server-state)" | awk 'NR==3 { print $3 }'
"running"
就这样了!
它是如何工作的…
在这里,我们告诉jboss-cli.sh脚本来执行我们在--command参数中定义的命令,并将结果输出到标准输出。
使用 CLI,操作如下:
$ cd $WILDFLY_HOME
$ ./bin/jboss-cli.sh
You are disconnected at the moment. Type 'connect' to connect to the server or 'help' for the list of supported commands.
[disconnected /] connect 192.168.59.103:9990
Authenticating against security realm: ManagementRealm
Username: wildfly
Password:
[standalone@192.168.59.103:9990 /] :read-attribute(name=server-state)
{
"outcome" => "success",
"result" => "running"
}
[standalone@192.168.59.103:9990 /]
顺便说一下,你不能操纵输出。
还有更多…
WildFly 提供另一个 API,即 HTTP API。让我们用一些网络命令行工具,如curl来尝试它。
curl
在一个运行的 WildFly 实例中,执行以下步骤:
-
打开一个新的终端窗口并执行以下命令:
$ curl --verbose http://192.168.59.103:9990/management/?operation=attribute\&name=server-state * Hostname was NOT found in DNS cache * Trying 192.168.59.103... * Connected to 192.168.59.103 (192.168.59.103) port 9990 (#0) > GET /management/?operation=attribute&name=server-state HTTP/1.1 > User-Agent: curl/7.37.1 > Host: 192.168.59.103:9990 > Accept: */* > < HTTP/1.1 401 Unauthorized < Connection: keep-alive < WWW-Authenticate: Digest realm="ManagementRealm",domain="/management",nonce="kv+FBaxYgDINMTQzMjAxNzUzMTYyNbC9h3WcIBNOnUH1mBkR1vY=",opaque="00000000000000000000000000000000",algorithm=MD5 < Content-Length: 77 < Content-Type: text/html < Date: Tue, 19 May 2015 06:38:51 GMT < * Connection #0 to host 192.168.59.103 left intact <html><head><title>Error</title></head><body>401 - Unauthorized</body></html>如你所见,
curl对ManagementRealm的摘要认证提出了抱怨,这是 WildFly 管理接口所使用的。 -
让我们给命令提供用户名和密码,如下所示:
$ curl --verbose --digest --user wildfly http://192.168.59.103:9990/management/?operation=attribute\&name=server-state Enter host password for user 'wildfly': * Hostname was NOT found in DNS cache * Trying 192.168.59.103... * Connected to 192.168.59.103 (192.168.59.103) port 9990 (#0) * Server auth using Digest with user 'wildfly' > GET /management/?operation=attribute&name=server-state HTTP/1.1 > User-Agent: curl/7.37.1 > Host: 192.168.59.103:9990 > Accept: */* > < HTTP/1.1 401 Unauthorized < Connection: keep-alive < WWW-Authenticate: Digest realm="ManagementRealm",domain="/management",nonce="Pl2q17HyCSsNMTQzMjAxNzU0NTUxOC3ZiDpK6wbWOysSkGTtFd8=",opaque="00000000000000000000000000000000",algorithm=MD5 < Content-Length: 77 < Content-Type: text/html < Date: Tue, 19 May 2015 06:39:05 GMT < * Ignoring the response-body * Connection #0 to host 192.168.59.103 left intact * Issue another request to this URL: 'http://192.168.59.103:9990/management/?operation=attribute&name=server-state' * Found bundle for host 192.168.59.103: 0x7f897b500c70 * Re-using existing connection! (#0) with host 192.168.59.103 * Connected to 192.168.59.103 (192.168.59.103) port 9990 (#0) * Server auth using Digest with user 'wildfly' > GET /management/?operation=attribute&name=server-state HTTP/1.1 > Authorization: Digest username="wildfly", realm="ManagementRealm", nonce="Pl2q17HyCSsNMTQzMjAxNzU0NTUxOC3ZiDpK6wbWOysSkGTtFd8=", uri="/management/?operation=attribute&name=server-state", response="7b740a1695f01a9041a3d0c61cf35c91", opaque="00000000000000000000000000000000", algorithm="MD5" > User-Agent: curl/7.37.1 > Host: 192.168.59.103:9990 > Accept: */* > < HTTP/1.1 200 OK < Connection: keep-alive < Authentication-Info: nextnonce="3gZb5DbobbENMTQzMjAxNzU0NTUzOD7wAHJMcsZDujeJP3F/N9M=" < Content-Type: application/json; charset=utf-8 < Content-Length: 9 < Date: Tue, 19 May 2015 06:39:05 GMT < * Connection #0 to host 192.168.59.103 left intact "running" -
好的,它工作了;现在移除
--verbose标志并再次执行命令。输入密码后,你应该只得到running。 -
如果你不想输入密码,你也可以作为参数传递,但会带来所有的安全顾虑。为此,执行以下命令:
$ curl --verbose --digest --user wildfly:cookbook.2015 http://192.168.59.103:9990/management/?operation=attribute \&name=server-state "running"
检查 JNDI 树视图
在这个菜谱中,我们将学习如何通过调用 CLI 命令来获取你的 WildFly 实例的 JNDI 树视图。这可能在你需要检查某些应用程序上下文是否存在、需要知道数据源 JNDI 名称或需要查找 EJB 时很有用。
准备中
记住我正在远程运行 WildFly,绑定到 IP 地址192.168.59.103。WildFly 已经启动并运行。
如何操作…
-
打开一个新的终端窗口并执行以下命令:
$ cd $WILDFLY_HOME $ ./bin/jboss-cli.sh -c --controller=192.168.59.103:9990 --user=wildfly --password=cookbook.2015 –command="/subsystem=naming:jndi-view" { "outcome" => "success", "result" => { "java: contexts" => { "java:" => {"TransactionManager" => { "class-name" => "com.arjuna.ats.jbossatx.jta.TransactionManagerDelegate", "value" => "com.arjuna.ats.jbossatx.jta.TransactionManagerDelegate@16b89a30" }}, "java:jboss" => { "TransactionManager" => { "class-name" => "com.arjuna.ats.jbossatx.jta.TransactionManagerDelegate", "value" => "com.arjuna.ats.jbossatx.jta.TransactionManagerDelegate@16b89a30" }, "TransactionSynchronizationRegistry" => { "class-name" => "org.jboss.as.txn.service.internal.tsr.TransactionSynchronizationRegistryWrapper", "value" => "org.jboss.as.txn.service.internal.tsr.TransactionSynchronizationRegistryWrapper@315ee6c0" }, "UserTransaction" => { "class-name" => "javax.transaction.UserTransaction", "value" => "UserTransaction" }, "jaas" => { "class-name" => "com.sun.proxy.$Proxy11", "value" => "java:jboss/jaas/ Context proxy" }, "ee" => { "class-name" => "javax.naming.Context", "children" => {"concurrency" => { "class-name" => "javax.naming.Context", "children" => { "scheduler" => { "class-name" => "javax.naming.Context", "children" => {"default" => { "class-name" => "java.lang.Object", "value" => "?" }} }, "factory" => { "class-name" => "javax.naming.Context", "children" => {"default" => { "class-name" => "java.lang.Object", "value" => "?" }} }, "executor" => { "class-name" => "javax.naming.Context", "children" => {"default" => { "class-name" => "java.lang.Object", "value" => "?" }} }, "context" => { "class-name" => "javax.naming.Context", "children" => {"default" => { "class-name" => "java.lang.Object", "value" => "?" }} } } }} }, "datasources" => { "class-name" => "javax.naming.Context", "children" => {"ExampleDS" => { "class-name" => "org.jboss.as.connector.subsystems.datasources.WildFlyDataSource", "value" => "org.jboss.as.connector.subsystems.datasources.WildFlyDataSource@59b69704" }} }, "mail" => { "class-name" => "javax.naming.Context", "children" => {"Default" => { "class-name" => "javax.mail.Session", "value" => "javax.mail.Session@25c2acd6" }} } }, "java:jboss/exported" => undefined, "java:global" => undefined }, "applications" => undefined } }显然,你可以通过在输出中使用
awk命令提取所需的信息(你可以使用你熟悉的任何工具),如下所示: -
如果你以域模式运行 WildFly,调用方式如下:
$ cd $WILDFLY_HOME $ ./bin/jboss-cli.sh -c --controller=192.168.59.103:9990 --user=wildfly --password=cookbook.2015 --command='/host=master/server=server-one/subsystem=naming:jndi-view' { "outcome" => "success", "result" => { "java: contexts" => { ... }, "applications" => {"example.war" => { "java:app" => { "AppName" => { "class-name" => "java.lang.String", "value" => "example" }, "env" => { "class-name" => "org.jboss.as.naming.NamingContext", "value" => "env" } }, "modules" => undefined }} } }
就这样了!
它是如何工作的…
在这里,我们告诉jboss-cli.sh脚本来执行我们在--command参数中定义的命令,并将结果输出到标准输出。
使用 CLI,操作如下:
$ cd $WILDFLY_HOME
$ ./bin/jboss-cli.sh
You are disconnected at the moment. Type 'connect' to connect to the server or 'help' for the list of supported commands.
[disconnected /] connect 192.168.59.103:9990
Authenticating against security realm: ManagementRealm
Username: wildfly
Password:
[standalone@192.168.59.103:9990 /] /subsystem=naming:jndi-view
....
输出将与本食谱中如何操作部分的图像显示相同。
调用声明在外部文件中的 CLI 命令
在本食谱中,我们将学习如何使用jboss-cli.sh脚本执行在单独文件中声明的命令。
准备工作
记住,我正在远程运行 WildFly,绑定到 IP 地址192.168.59.103。WildFly 已经启动并运行。
创建一个名为wildfly-cookbook.cli的文件,并将列表命令ls插入其中。将文件放置在你的本地$WILDFLY_HOME文件夹中。
现在是时候通过 CLI 调用我们的命令了!
如何操作…
打开一个新的终端窗口并执行以下操作:
$ cd $WILDFLY_HOME
$ ./bin/jboss-cli.sh -c --controller=192.168.59.103:9990 --user=wildfly --password=cookbook.2015 --file=wildfly-cookbook.cli
core-service
deployment
deployment-overlay
extension
interface
path
socket-binding-group
subsystem
system-property
launch-type=STANDALONE
management-major-version=3
management-micro-version=0
management-minor-version=0
name=7536a491dba6
namespaces=[]
process-type=Server
product-name=WildFly Full
product-version=9.0.0.Beta2
profile-name=undefined
release-codename=Kenny
release-version=1.0.0.Beta2
running-mode=NORMAL
schema-locations=[]
server-state=running
suspend-state=RUNNING
它是如何工作的…
在这里,我们告诉jboss-cli.sh脚本通过--file指令读取wildfly-cookbook.2015文件内定义的命令。CLI 读取我们的wildfly-cookbook.cli文件并执行其命令。
使用文件来存储配置你的 WildFly 实例所需的命令非常有用,可以自动化配置。
想象一个需要自动扩展系统的场景。假设你的所有应用程序源代码都在版本控制软件仓库中,例如git,以及.cli配置文件。下载你的应用程序,构建它,使用.cli文件配置 WildFly 实例,部署应用程序,并启动服务器将变得轻而易举!
在下一章中,我们将讨论如何停止和启动服务器,使用 CLI 部署应用程序,以及更多内容。
第九章. 克服 CLI
在本章中,你将学习以下食谱:
-
调用服务器停止、启动和重新加载
-
调用服务器组停止、启动、重启和重新加载
-
创建服务器组
-
创建服务器
-
管理应用程序 – 部署、卸载
简介
在本章中,你将学习如何使用 CLI 来改变系统的状态。也就是说,改变不同的设置,如部署和创建新服务器。在前一章中,我们看到了如何从 CLI 中获取信息。CLI 还提供了一个方法来执行它外的命令,通过指定要连接的 WildFly 和要执行的命令。
此外,WildFly 为你提供了一个 HTTP API,可以用来执行操作和检索信息(大多数这些 API 用于执行系统监控分析)。在前一章中,我们使用 CLI 来获取信息;因此,通过 HTTP,我们使用了 HTTP 协议的 GET 动词。在下面的食谱中,我们将使用 POST 动词以及 WildFly HTTP API 需要的参数来改变服务器的状态。HTTP API 只接受 JSON 数据,因此我们需要以这种方式发送数据。
当有道理时,我们将使用两种操作模式中的方法——独立模式和域模式,因为关于操作模式,你可能有不同的入口点。
在本章中,我们将模拟/使用远程 WildFly 实例,就像在实际场景中一样,你可以应用以下食谱来连接到远程服务器。在没有看到任何身份验证和授权问题的情况下,尝试localhost上的食谱是没有意义的。
为了模拟远程服务器,你最终可以使用 VirtualBox 或 Docker,然后按照第一章中描述的方式安装 WildFly。
我将使用 Docker 工具版本 1.5 在 Linux 容器上运行的 WildFly。显然,你可以使用真实的远程服务器——那将是相同的;真正重要的是 WildFly 平台是否被暴露。
顺便说一句,这本书的最后一章全是关于在 Linux 容器中使用 Docker 运行 WildFly 的内容。所以如果你对 Docker 一无所知(你一直躲在哪里?),可以看看这本书的最后一章,或者获取优秀的 Docker 书籍,《Docker Book》,作者詹姆斯·特布尔,www.dockerbook.com。
我的 Docker 配置如下:
DOCKER_HOST=tcp://192.168.59.103:2376
因此,我的远程 WildFly 实例将绑定到该 IP,以及 WildFly 的常用端口,如8080、9990和9999。本书中管理用户保持不变,用户名为wildfly,密码为cookbook.2015。
调用服务器停止、启动和重新加载
在这个食谱中,我们将学习如何通过向 CLI 发出命令来停止、启动和重新加载一个 WildFly 实例。你可能需要手动停止服务器来纠正错误配置或重新部署应用程序。因此,了解如何停止、启动和重新加载服务器是必须的。
准备工作
记住,我正在远程运行 WildFly,绑定到192.168.59.103作为 IP。WildFly 已经启动并运行。
如何做到这一点...
我们将分别介绍stop、start、restart和reload命令,以更好地解释它们的调用方式,并最终解释它们之间的差异。例如,start命令只在域模式下有意义,因为在独立模式下启动 WildFly 是一个手动操作。
停止
打开一个新的终端窗口并执行以下命令:
$ cd $WILDFLY_HOME
$ ./bin/jboss-cli.sh -c --controller=192.168.59.103:9990 --user=wildfly --password=cookbook.2015 --command=":shutdown(restart=false)"
{"outcome" => "success"}
显然,在这个命令之后,你需要手动启动 WildFly。这是因为主机控制器进程与 WildFly 实例一起关闭了,因此,你无法访问管理界面来控制你的实例。
如果你正在域模式下运行 WildFly,你首先需要确定你想要停止的主机和服务器,然后调用停止方法,如下所示:
$ ./bin/jboss-cli.sh --connect --controller=192.168.59.103:9990 --user=wildfly --password=cookbook.2015 --command="/host=master/server-config=server-one:stop()"
{
"outcome" => "success",
"result" => "STOPPING"
}
启动
这个命令只在域模式下有意义,其中你连接到域控制器。然后你可以向各个活动的主机控制器推送命令,如以下所示,来启动或关闭 WildFly 实例。
打开一个新的终端窗口并执行以下命令:
$ cd $WILDFLY_HOME
$ ./bin/jboss-cli.sh --connect --controller=192.168.59.103:9990 --user=wildfly --password=cookbook.2015 --command="/host=master/server-config=server-one:start()"
{
"outcome" => "success",
"result" => "STARTING"
}
重新启动
打开一个新的终端窗口并运行以下代码:
$ cd $WILDFLY_HOME
$ ./bin/jboss-cli.sh -c --controller=192.168.59.103:9990 --user=wildfly --password=cookbook.2015 --command=":shutdown(restart=true)"
{"outcome" => "success"}
在前面的stop部分,你可能想知道,将restart参数设置为true传递给shutdown方法可能会重新启动 WildFly 实例,实际上它确实这样做了。
顺便说一句,没有与域模式对应的模式。它只适用于服务器组,这在本章后面会讨论。
重新加载
打开一个新的终端窗口并执行以下操作:
$ cd $WILDFLY_HOME
$ ./bin/jboss-cli.sh -c --controller=192.168.59.103:9990 --user=wildfly --password=cookbook.2015 --command=":reload()"
{
"outcome" => "success",
"result" => undefined
}
reload方法只是关闭所有活动的 WildFly 服务,解绑资源,然后再次启动它们,重新加载配置。
如果你正在域模式下运行 WildFly,调用的目的仍然是相同的——重新加载服务器配置;你可以这样做:
$ ./bin/jboss-cli.sh --connect --controller=192.168.59.103:9990 --user=wildfly --password=cookbook.2015 --command=":reload-servers()"
{
"outcome" => "success",
"result" => undefined,
"server-groups" => undefined
}
它是如何工作的...
在这里,我们正在告诉jboss-cli.sh脚本来执行我们在--command参数中定义的命令,并将结果返回到标准输出。
还有更多...
WildFly 提供另一个 API,即 HTTP API。让我们用一些网络命令行工具,如curl来尝试它。
Curl
-
在一个运行的 WildFly 实例中,打开一个新的终端窗口并输入以下命令:
$ curl --verbose http://192.168.59.103:9990/management/ -X POST -H "Content-Type: application/json" -d '{"operation":"reload"}' -
你应该得到以下输出:
* Hostname was NOT found in DNS cache * Trying 192.168.59.103... * Connected to 192.168.59.103 (192.168.59.103) port 9990 (#0) > POST /management/ HTTP/1.1 > User-Agent: curl/7.37.1 > Host: 192.168.59.103:9990 > Accept: */* > Content-Type: application/json > Content-Length: 22 > * upload completely sent off: 22 out of 22 bytes < HTTP/1.1 401 Unauthorized < Connection: keep-alive < WWW-Authenticate: Digest realm="ManagementRealm",domain="/management",nonce="//unXxm6vjoNMTQzNTA1MTg1NDUyNtkzOlQPF6rdPZp9cTcQnhY=",opaque="00000000000000000000000000000000",algorithm=MD5 < Content-Length: 77 < Content-Type: text/html < Date: Tue, 23 Jun 2015 09:30:44 GMT < * Connection #0 to host 192.168.59.103 left intact <html><head><title>Error</title></head><body>401 - Unauthorized</body></html>正如你所看到的,
curl对ManagementRealm的摘要认证提出了抱怨,这是 WildFly 管理接口所使用的。 -
让我们给命令提供用户名和密码,如下所示:
$ curl --verbose --digest --user wildfly http://192.168.59.103:9990/management/ -X POST -H "Content-Type: application/json" -d '{"operation":"reload"}' -
你应该得到以下输出:
Enter host password for user 'wildfly': * Hostname was NOT found in DNS cache * Trying 192.168.59.103... * Connected to 192.168.59.103 (192.168.59.103) port 9990 (#0) * Server auth using Digest with user 'wildfly' > POST /management/ HTTP/1.1 > User-Agent: curl/7.37.1 > Host: 192.168.59.103:9990 > Accept: */* > Content-Type: application/json > Content-Length: 0 > < HTTP/1.1 401 Unauthorized < Connection: keep-alive < WWW-Authenticate: Digest realm="ManagementRealm",domain="/management",nonce="urmPLpGMJgkNMTQzNTA1MjEwMTI4NJYIEw20Br2YWefE0GbBBk8=",opaque="00000000000000000000000000000000",algorithm=MD5 < Content-Length: 77 < Content-Type: text/html < Date: Tue, 23 Jun 2015 09:30:44 GMT < * Ignoring the response-body * Connection #0 to host 192.168.59.103 left intact * Issue another request to this URL: 'http://192.168.59.103:9990/management/' * Found bundle for host 192.168.59.103: 0x7fc1104149d0 * Re-using existing connection! (#0) with host 192.168.59.103 * Connected to 192.168.59.103 (192.168.59.103) port 9990 (#0) * Server auth using Digest with user 'wildfly' > POST /management/ HTTP/1.1 > Authorization: Digest username="wildfly", realm="ManagementRealm", nonce="urmPLpGMJgkNMTQzNTA1MjEwMTI4NJYIEw20Br2YWefE0GbBBk8=", uri="/management/", response="0bbe505bc1af3907ff3a8ca960387829", opaque="00000000000000000000000000000000", algorithm="MD5" > User-Agent: curl/7.37.1 > Host: 192.168.59.103:9990 > Accept: */* > Content-Type: application/json > Content-Length: 22 > * upload completely sent off: 22 out of 22 bytes < HTTP/1.1 200 OK < Connection: keep-alive < Authentication-Info: nextnonce="oIOIlHtymVgNMTQzNTA1MjEwMTI4OZI07WcGItS8EhmrBTUgrCU=" < Content-Type: application/json; charset=utf-8 < Content-Length: 40 < Date: Tue, 23 Jun 2015 09:30:44 GMT < * Connection #0 to host 192.168.59.103 left intact {"outcome" : "success", "result" : null} -
好的,它工作了;现在移除
--verbose标志并再次执行命令。在输入密码后,你应该得到以下 JSON 输出:$ curl --digest --user wildfly:cookbook.2015 http://192.168.59.103:9990/management/ -X POST -H "Content-Type: application/json" -d '{"operation":"reload"}' {"outcome" : "success", "result" : null} $注意
我选择
reload方法只是为了向你展示如何使用 HTTP API 来做这件事。
调用服务器组停止、启动、重启和重新加载
在这个菜谱中,我们将学习如何通过向 CLI 发送命令来停止、启动、重启和重新加载 WildFly 服务器组。正如你所知,你可以停止、启动、重启和重新加载属于服务器组的单个服务器。我们将使用 CLI 查看所有这些命令。
由于我们正在讨论服务器组,这个菜谱只有在 WildFly 以域模式运行时才有意义。
准备工作
记住,我正在远程运行 WildFly,绑定到 IP 192.168.59.103。WildFly 已经启动并运行。
如何操作...
我们将分别介绍 stop、start、restart 和 reload 命令,以更好地解释它们的调用和最终差异。
停止
打开一个新的终端窗口并执行以下代码:
$ cd $WILDFLY_HOME
$ ./bin/jboss-cli.sh -c --controller=192.168.59.103:9990 --user=wildfly --password=cookbook.2015 --command="/server-group=main-server-group:stop-servers()"
{
"outcome" => "success",
"result" => undefined,
"server-groups" => undefined
}
开始
打开一个新的终端窗口并运行以下命令:
$ cd $WILDFLY_HOME
$ ./bin/jboss-cli.sh -c --controller=192.168.59.103:9990 --user=wildfly --password=cookbook.2015 --command="/server-group=main-server-group:start-servers()"
{
"outcome" => "success",
"result" => undefined,
"server-groups" => undefined
}
重启
打开一个新的终端窗口并执行以下命令:
$ cd $WILDFLY_HOME
$ ./bin/jboss-cli.sh -c --controller=192.168.59.103:9990 --user=wildfly --password=cookbook.2015 --command="/server-group=main-server-group:restart-servers()"
{
"outcome" => "success",
"result" => undefined,
"server-groups" => undefined
}
重新加载
打开一个新的终端窗口并运行以下代码:
$ cd $WILDFLY_HOME
$ ./bin/jboss-cli.sh -c --controller=192.168.59.103:9990 --user=wildfly --password=cookbook.2015 --command="/server-group=main-server-group:reload-servers()"
{
"outcome" => "success",
"result" => undefined,
"server-groups" => undefined
}
它是如何工作的...
在这里,我们正在告诉 jboss-cli.sh 脚本执行我们在 --command 参数中定义的命令,并将结果返回到标准输出。
使用 CLI,操作如下:
$ ./bin/jboss-cli.sh
You are disconnected at the moment. Type 'connect' to connect to the server or 'help' for the list of supported commands.
[disconnected /] connect 192.168.59.103:9990
Authenticating against security realm: ManagementRealm
Username: wildfly
Password:
[standalone@192.168.59.103:9990 /] /server-group=main-server-group:stop-servers()
{
"outcome" => "success",
"result" => undefined,
"server-groups" => undefined
}
[standalone@192.168.59.103:9990 /]
同样的,其他方法也适用:start-servers()、restart-servers() 和 reload-servers()。
更多内容...
WildFly 提供了一个额外的 API,即 HTTP API。让我们用一些网络命令行工具,如 curl 来尝试它。
Curl
在一个运行的 WildFly 实例中,打开一个新的终端窗口并执行以下代码:
$ curl --digest --user wildfly:cookbook.2015 http://192.168.59.103:9990/management/ -X POST -H "Content-Type: application/json" -d '{"operation":"restart-servers","address":[{"server-group":"main-server-group"}]}'
{"outcome" : "success", "result" : null, "server-groups" : null}
就这样!
创建服务器组
在这个菜谱中,你将学习如何通过向 CLI 发送命令来创建服务器组。这仅适用于域模式。
准备工作
记住,我正在远程运行 WildFly,绑定到 IP 192.168.59.103。WildFly 已经启动并运行。
如何操作...
打开一个新的终端窗口并运行以下代码:
$ cd $WILDFLY_HOME
$ ./bin/jboss-cli.sh -c --controller=192.168.59.103:9990 --user=wildfly --password=cookbook.2015 --command="/server-group=next-server-group:add(profile=ha,socket-binding-group=ha-sockets)"
{
"outcome" => "success",
"result" => undefined,
"server-groups" => undefined
}
注意
服务器组已准备好使用,无需重新加载或重启。这意味着可以将其用于添加服务器。目前,它只是一个空的服务器组。在本章的后面部分,我们将看到如何使用 CLI 将服务器添加到服务器组。
如果需要删除服务器组,只需使用其上的 remove 方法,如下所示:
$ ./bin/jboss-cli.sh -c --controller=192.168.59.103:9990 --user=wildfly --password=cookbook.2015 --command="/server-group=next-server-group:remove()"
{
"outcome" => "success",
"result" => undefined,
"server-groups" => undefined
}
它是如何工作的...
在这里,我们正在告诉 jboss-cli.sh 脚本执行我们在 --command 参数中定义的命令,并将结果返回到标准输出。
使用 CLI,操作如下:
$ ./bin/jboss-cli.sh
You are disconnected at the moment. Type 'connect' to connect to the server or 'help' for the list of supported commands.
[disconnected /] connect 192.168.59.103:9990
Authenticating against security realm: ManagementRealm
Username: wildfly
Password:
[domain@192.168.59.103:9990 /] /server-group=next-server-group:add(profile=ha,socket-binding-group=ha-sockets)
{
"outcome" => "success",
"result" => undefined,
"server-groups" => undefined
}
[domain@192.168.59.103:9990 /]
更多内容...
WildFly 提供了一个额外的 API,即 HTTP API。让我们用一些网络命令行工具,如 curl 来尝试它。
在一个运行的 WildFly 实例中,打开一个新的终端窗口并运行以下代码:
$ curl --digest --user wildfly:cookbook.2015 http://192.168.59.103:9990/management/ -X POST -H "Content-Type: application/json" -d '{"operation" : "composite", "address" : [], "steps" : [{"operation" : "add", "address" : {"server-group" : "next-server-group"}, "profile" : "ha", "socket-binding-group" : "ha-sockets"}]}'
{"outcome" : "success", "result" : {"step-1" : {"outcome" : "success"}}, "server-groups" : null}
创建服务器
在这个菜谱中,我们将学习如何通过向 CLI 发送命令来创建服务器。这仅适用于域模式。
准备工作
记住,我正在远程运行 WildFly,绑定到 IP 192.168.59.103。WildFly 已经启动并运行。
如何操作...
打开一个新的终端窗口并执行以下命令:
$ cd $WILDFLY_HOME
$ ./bin/jboss-cli.sh -c --controller=192.168.59.103:9990 --user=wildfly --password=cookbook.2015 --command="/host=master/server-config=server-four:add(group=main-server-group, auto-start=true, socket-binding-port-offset=450)"
{
"outcome" => "success",
"result" => undefined,
"server-groups" => undefined
}
现在服务器已准备就绪,你只需手动启动它。这可以通过启动你的新服务器所属的服务器组的服务来实现,在这种情况下,main-server-group。运行以下代码:
$ ./bin/jboss-cli.sh -c --controller=192.168.59.103:9990 --user=wildfly --password=cookbook.2015 --command=/server-group=main-server-group:start-servers()
{
"outcome" => "success",
"result" => undefined,
"server-groups" => undefined
}
上述命令只是启动了处于 STOPPED 状态的服务器。
它是如何工作的...
这里,我们正在告诉 jboss-cli.sh 脚本执行我们在 --command 参数中定义的命令,并将结果输出到标准输出。
使用 CLI,它将是这样的:
$ ./bin/jboss-cli.sh
You are disconnected at the moment. Type 'connect' to connect to the server or 'help' for the list of supported commands.
[disconnected /] connect 192.168.59.103:9990
Authenticating against security realm: ManagementRealm
Username: wildfly
Password:
[standalone@192.168.59.103:9990 /] /host=master/server-config=server-four:add(group=main-server-group, auto-start=true, socket-binding-port-offset=450)
{
"outcome" => "success",
"result" => undefined,
"server-groups" => undefined
}
[standalone@192.168.59.103:9990 /]
更多内容...
WildFly 提供了一个额外的 API,即 HTTP API。让我们用一些网络命令行工具,如 curl 来试试。
cURL
在一个正在运行的 WildFly 实例中,打开一个新的终端窗口并执行以下命令:
$ curl --digest --user wildfly:cookbook.2015 http://192.168.59.103:9990/management/ -X POST -H "Content-Type: application/json" -d '{"operation" : "composite", "address" : [], "steps" : [{"operation" : "add", "address" : [{"host" : "master"},{"server-config" : "server-four"}], "group" : "main-server-group", "auto-start" : "true", "socket-binding-port-offset" : "450"}]}'
{"outcome" : "success", "result" : {"step-1" : {"outcome" : "success"}}, "server-groups" : null}
管理应用程序 – 部署、卸载
在这个食谱中,我们将学习如何在运行中的 WildFly 实例上通过向 CLI 发送命令来部署和卸载应用程序,并检查其状态。
准备工作
记住,我正在远程运行 WildFly,绑定到 IP 192.168.59.103。WildFly 已经启动并运行。
对于这个食谱,我们需要一个名为 example 的应用程序,你可以在我的 GitHub 仓库中找到它。如果你跳过了第二章中关于“使用部署文件夹管理应用程序”的食谱,请参阅它以下载你需要的所有源代码和项目。
要构建应用程序,请给出以下命令:
$ cd ~/WFC/github/wildfly-cookbook
$ cd example
$ mvn -e clean package
完成后,将工件 example.war 复制到你的本地 $WILDFLY_HOME 文件夹。
如何做到这一点...
我们将分别介绍 deploy、status 和 undeploy 命令,以更好地解释它们在处理独立模式和域模式时的区别。
部署
打开一个新的终端窗口并运行以下命令:
$ cd $WILDFLY_HOME
$ ./bin/jboss-cli.sh -c --controller=192.168.59.103:9990 --user=wildfly --password=cookbook.2015 --command="deploy example.war"
如果你以域模式运行 WildFly,调用方式如下:
$ ./bin/jboss-cli.sh -c --controller=192.168.59.103:9990 --user=wildfly --password=cookbook.2015 --command="deploy example.war --server-groups=main-server-group"
状态
如果你需要检查部署,可以执行以下代码:
$ ./bin/jboss-cli.sh -c --controller=192.168.59.103:9990 --user=wildfly --password=cookbook.2015 --command="/deployment=example.war:read-attribute(name=status)"
{
"outcome" => "success",
"result" => "OK"
}
如果你以域模式运行 WildFly,我们首先需要知道应用程序所属的服务器组。我们可以通过以下命令来实现:
$ ./bin/jboss-cli.sh -c --controller=192.168.59.103:9990 --user=wildfly --password=cookbook.2015 --command="deployment-info --name=example.war"
NAME RUNTIME-NAME
example.war example.war
SERVER-GROUP STATE
main-server-group enabled
other-server-group not added
如果你已经知道服务器组,你可以直接通过调用以下命令来检查部署状态,以查看它所属的服务器:
$ ./bin/jboss-cli.sh -c --controller=192.168.59.103:9990 --user=wildfly --password=cookbook.2015 --command="/host=master/server=server-one/deployment=example.war:read-attribute(name=status)"
{
"outcome" => "success",
"result" => "OK"
}
卸载
如果你需要卸载你的应用程序,只需按下以下命令:
$ ./bin/jboss-cli.sh -c --controller=192.168.59.103:9990 --user=wildfly --password=cookbook.2015 --command="undeploy example.war"
如果你以域模式运行 WildFly,调用方式如下:
$ ./bin/jboss-cli.sh -c --controller=192.168.59.103:9990 --user=wildfly --password=cookbook.2015 --command="undeploy example.war --all-relevant-server-groups"
就这样!
它是如何工作的...
这里,我们正在告诉 jboss-cli.sh 脚本执行我们在 --command 参数中定义的命令,并将结果输出到标准输出。
部署
按照以下方式使用 CLI:
$ ./bin/jboss-cli.sh
You are disconnected at the moment. Type 'connect' to connect to the server or 'help' for the list of supported commands.
[disconnected /] connect 192.168.59.103:9990
Authenticating against security realm: ManagementRealm
Username: wildfly
Password:
[standalone@192.168.59.103:9990 /] deploy example.war
[standalone@192.168.59.103:9990 /]
在域模式下,你将拥有以下命令:
[domain@192.168.59.103:9990 /] deploy example.war --server-groups=main-server-group
[domain@192.168.59.103:9990 /]
顺便说一下,你不能操作输出。
状态
按照以下方式使用 CLI:
$ ./bin/jboss-cli.sh
You are disconnected at the moment. Type 'connect' to connect to the server or 'help' for the list of supported commands.
[disconnected /] connect 192.168.59.103:9990
Authenticating against security realm: ManagementRealm
Username: wildfly
Password:
[standalone@192.168.59.103:9990 /] deployment-info example.war
{
"outcome" => "success",
"result" => "OK"
}
[standalone@192.168.59.103:9990 /]
Within the domain mode, you will have the following command:
[domain@192.168.59.103:9990 /] /host=master/server=server-one/deployment=example.war:read-attribute(name=status)
{
"outcome" => "success",
"result" => "OK"
}
[domain@192.168.59.103:9990 /]
卸载
按照以下方式使用 CLI:
$ ./bin/jboss-cli.sh
You are disconnected at the moment. Type 'connect' to connect to the server or 'help' for the list of supported commands.
[disconnected /] connect 192.168.59.103:9990
Authenticating against security realm: ManagementRealm
Username: wildfly
Password:
[standalone@192.168.59.103:9990 /] undeploy example.war
[standalone@192.168.59.103:9990 /]
在域模式下,请执行以下命令:
[domain@192.168.59.103:9990 /] undeploy example.war --all-relevant-server-groups
[domain@192.168.59.103:9990 /]
顺便说一下,你无法操作输出。
还有更多……
WildFly 提供了一个额外的 API,即 HTTP API。让我们用一些网络命令行工具,如 curl 来试试。
Curl 部署
让我们使用 curl 进行部署:
-
在运行 WildFly 实例的情况下,打开一个新的终端窗口并执行以下命令:
$ curl --digest --user wildfly:cookbook.2015 -F "file=@example.war" http://192.168.59.103:9990/management/add-content {"outcome" : "success", "result" : { "BYTES_VALUE" : "eqfGfLVOCv+p1gz5gjDgwX79ERk=" }}注意
文件
example.war应该放在你调用命令的同一文件夹中。 -
好的,它工作了。但我们只是上传了文件,还没有部署。要部署,我们需要获取
upload命令产生的哈希码,并在下一个命令中使用它,如下所示:$ curl --digest --user wildfly:cookbook.2015 http://192.168.59.103:9990/management -X POST -H "Content-Type: application/json" -d '{"content":[{"hash": {"BYTES_VALUE" : "eqfGfLVOCv+p1gz5gjDgwX79ERk="}}], "address": [{"deployment":"example.war"}], "operation":"add", "enabled":"true"}' {"outcome" : "success"}看这里!我们已经成功部署了我们的星际网络应用程序。
-
如果你以域模式运行 WildFly,上传方法相同;因此,为了有效地部署应用程序,你可以执行以下命令:
$ curl --digest --user wildfly:cookbook.2015 http://192.168.59.103:9990/management -X POST -H "Content-Type: application/json" -d '{"content":[{"hash": {"BYTES_VALUE" : "eqfGfLVOCv+p1gz5gjDgwX79ERk="}}], "address": [{"deployment":"example.war"}], "server-groups":"main-server-group", "runtime-name":"example.war", "operation":"add", "enabled":"true"}' {"outcome" : "success", "result" : null, "server-groups" : null}
就这样了!
状态
要检查部署状态,请执行以下命令:
$ curl --digest --user wildfly:cookbook.2015 http://192.168.59.103:9990/management -X POST -H "Content-Type: application/json" -d '{"address": [{"deployment":"example.war"}],"operation":"read-attribute","name":"status"}'
{"outcome" : "success", "result" : "OK"}
如果你以域模式运行 WildFly,你可以运行以下代码:
$ curl --digest --user wildfly:cookbook.2015 http://192.168.59.103:9990/management -X POST -H "Content-Type: application/json" -d '{"operation" : "read-resource", "address" : [{"deployment":"example.war"}], "json.pretty":1}'
{
"outcome" : "success",
"result" : {
"content" : [{"hash" : {
"BYTES_VALUE" : "eqfGfLVOCv+p1gz5gjDgwX79ERk="
}}],
"name" : "example.war",
"runtime-name" : "example.war"
}
}
就这些了。
Curl 卸载
让我们使用 curl 进行卸载:
$ curl --digest --user wildfly:cookbook.2015 http://192.168.59.103:9990/management/ -X POST -H "Content-Type: application/json" -d '{"address" : [{"deployment":"example.war"}],"operation":"undeploy"}'
{"outcome" : "success"}
就这样了!
第十章。强化 WildFly 通信
在本章中,你将学习以下食谱:
-
使用 HTTPS 保护 WildFly
-
使用 HTTPS 保护特定应用程序
-
使用 HTTPS 保护 WildFly 控制台
-
使用 HTTPS 保护域和主机控制器之间的通信
简介
在本章中,你将学习如何从通信通道的角度来保护你的 WildFly 系统,即 HTTPS 协议。如果系统安全是一个关注点,你需要提供这样的功能。顺便说一句,在任何一个层面对系统进行安全保护时,都要关注性能,因为它可能会造成一些开销。
我们将学习如何在 WildFly 系统的不同阶段提供安全性,即:
-
保护你的应用程序通信访问协议
-
保护 WildFly 管理控制台
-
保护域控制器与所有主机控制器之间的通信安全
尽管最后两点对性能的影响不大,因为它们是管理工具,但第一个可能会影响你的性能。顺便说一句,在企业环境中,通常情况下,你的 WildFly 中间件平台位于反向代理(即 Apache HTTPD)之后,进入非军事区(DMZ)。因此,你应该最终保护反向代理,而不是 Apache 和 WildFly 之间的流量,这只会造成 CPU 开销。如果你的网络基础设施已经在 DMZ 内提供了安全,那么在 Apache 和 WildFly 之间避免使用 HTTPS。
好的,现在我已经与我的良心和解,让我们开始吧!
使用 HTTPS 保护 WildFly
在这个食谱中,我们将学习如何使用安全通道为你的应用程序提供服务,这是通过使用 HTTPS 协议来实现的。如果你对隐私有顾虑,这个食谱将展示你如何保护你的数据流量。顺便说一句,保护你的应用程序需要不同的考虑和方面,这些问题应该由开发人员和运维团队解决。
准备工作
要开始,让我们首先创建一个ad-hoc文件夹来运行我们的 WildFly。在终端窗口中执行以下命令:
$ cd $WILDFLY_HOME
$ cp -a standalone sec-std-node-1
现在是时候创建我们的密钥库,它用于加密数据流量。我们将使用一个密码来打开密钥库文件本身,并使用一个密码来加载别名。
-
打开一个新的终端窗口并输入以下命令:
$ cd $WILDFLY_HOME $ cd sec-std-node-1/configuration $ keytool -v -genkey -alias wildfly.ssl -keypass alias.2015 -keyalg RSA -keysize 2048 -sigalg SHA1withRSA -keystore wildfly.ssl.keystore -storepass keystore.2015 What is your first and last name? [Unknown]: WildFly Cookbook What is the name of your organizational unit? [Unknown]: Packt Publishing What is the name of your organization? [Unknown]: packtpub.com What is the name of your City or Locality? [Unknown]: Birmingham What is the name of your State or Province? [Unknown]: GB What is the two-letter country code for this unit? [Unknown]: UK Is CN=WildFly Cookbook, OU=Packt Publishing, O=packtpub.com, L=Birmingham, ST=GB, C=UK correct? [no]: yes Generating 2,048 bit RSA key pair and self-signed certificate (SHA1withRSA) with a validity of 90 days for: CN=WildFly Cookbook, OU=Packt Publishing, O=packtpub.com, L=Birmingham, ST=GB, C=UK [Storing wildfly.ssl.keystore] -
好的,现在我们已经创建了用于加密 HTTP 消息的密钥库。让我们通过执行以下命令来检查其完整性:
$ keytool -list -v -keystore wildfly.ssl.keystore Enter keystore password: Keystore type: JKS Keystore provider: SUN Your keystore contains 1 entry Alias name: wildfly.ssl Creation date: Nov 21, 2014 Entry type: PrivateKeyEntry Certificate chain length: 1 Certificate[1]: Owner: CN=WildFly Cookbook, OU=Packt Publishing, O=packtpub.com, L=Birmingham, ST=GB, C=UK Issuer: CN=WildFly Cookbook, OU=Packt Publishing, O=packtpub.com, L=Birmingham, ST=GB, C=UK Serial number: 5514d1d1 Valid from: Fri Nov 21 10:36:31 CET 2014 until: Thu Feb 19 10:36:31 CET 2015 Certificate fingerprints: MD5: D3:FA:9D:20:6D:79:DF:83:C3:50:B1:AC:23:B2:7D:9F SHA1: 89:AB:67:27:23:A4:89:85:06:10:FF:70:C6:6B:05:3D:14:DA:2D:AD SHA256: 7C:DD:A7:E3:44:B4:C5:9F:05:EE:87:2D:75:35:C2:3C:90:00:1B:DF:67:89:9B:13:33:F7:58:55:74:89:F7:7C Signature algorithm name: SHA1withRSA Version: 3 Extensions: #1: ObjectId: 2.5.29.14 Criticality=false SubjectKeyIdentifier [ KeyIdentifier [ 0000: 1A F5 F7 EC D8 88 D9 80 DE 34 E7 A9 00 75 6A 74 .........4...ujt 0010: E8 60 56 D4 .`V. ] ] ******************************************* *******************************************
好的,一切正常!!!
我们现在已经准备好配置 WildFly 以通过 HTTPS 公开自身和我们的应用程序。
如何做到这一点...
我们现在将对 WildFly 的配置文件进行一些修改,以实现我们的目标。我们将看到两种操作模式:独立模式和域模式。
独立模式
-
首先,像往常一样启动一个 WildFly 实例:
$ cd $WILDFLY_HOME $ ./bin/standalone.sh -Djboss.server.base.dir=sec-std-node-1 -
在不同的终端窗口中,连接到 WildFly CLI 并运行以下代码:
$ ./bin/jboss-cli.sh --connect [standalone@localhost:9990 /] /core-service=management/security-realm=SSLRealm:add() {"outcome" => "success"} [standalone@localhost:9990 /] /core-service=management/security-realm=SSLRealm/server-identity=ssl:add(keystore-path=wildfly.ssl.keystore, keystore-relative-to=jboss.server.config.dir,keystore-password=keystore.2015, alias=wildfly.ssl, key-password=alias.2015) { "outcome" => "success", "response-headers" => { "operation-requires-reload" => true, "process-state" => "reload-required" } } [standalone@localhost:9990 /] :reload() { "outcome" => "success", "result" => undefined } [standalone@localhost:9990 /] /subsystem=undertow/server=default-server/https-listener=https:add(socket-binding=https, security-realm=SSLRealm) {"outcome" => "success"} [standalone@localhost:9990 /]好的,我们已经完成了配置。
-
让我们通过打开浏览器并将它指向
https://localhost:8443/来测试一切:![独立]()
浏览器警告用户关于自签名证书,因此不受信任
-
首先,浏览器会警告你关于一个安全问题;只需点击 添加异常 按钮,然后,在下一个弹出窗口中,点击 确认安全异常 按钮。一旦确认,浏览器将显示我们的 WildFy 实例正在通过 HTTPS 运行,如下所示:
![独立]()
浏览器通过 HTTPS 显示 WildFly
域
-
首先创建一个
ad-hoc文件夹以在域模式下操作:$ cd $WILDFLY_HOME $ cp -a domain sec-dmn-1 -
从本食谱的 准备就绪 部分中,看看您如何创建密钥库,或者从
sec-std-node-1文件夹中复制它(如果您遵循了 独立 部分的步骤),如下所示:$ cp sec-std-node-1/configuration/wildfly.ssl.keystore sec-dmn-1/configuration -
现在按照以下方式以域模式启动 WildFly:
$ cd $WILDFLY_HOME $ ./bin/domain.sh -Djboss.domain.base.dir=sec-dmn-1 -
接下来,在另一个终端窗口中,连接到 WildFly CLI 并执行以下操作:
$ ./bin/jboss-cli.sh --connect [domain@localhost:9990 /] /host=master/core-service=management/security-realm=SSLRealm:add() { "outcome" => "success", "result" => undefined, "server-groups" => undefined } [domain@localhost:9990 /] /host=master/core-service=management/security-realm=SSLRealm/server-identity=ssl:add(keystore-path=wildfly.ssl.keystore, keystore-relative-to=jboss.domain.config.dir,keystore-password=keystore.2015, alias=wildfly.ssl, key-password=alias.2015) { "outcome" => "success", "response-headers" => { "operation-requires-reload" => true, "process-state" => "reload-required" }, "result" => undefined, "server-groups" => undefined } [domain@localhost:9990 /] :reload-servers { "outcome" => "success", "result" => undefined, "server-groups" => undefined, "response-headers" => {"process-state" => "reload-required"} } [domain@localhost:9990 /] -
如我们所知,WildFly 默认域配置提供了两个服务器组,一个绑定到
full配置文件,另一个绑定到full-ha配置文件(这个没有活动服务器),我们可以为full配置文件启用 HTTPS 协议。在同一个 CLI 控制台中,运行以下代码:[domain@localhost:9990 /] /profile=full/subsystem=undertow/server=default-server/https-listener=https:add(socket-binding=https, security-realm=SSLRealm) { "outcome" => "success", "result" => undefined, "server-groups" => {"main-server-group" => {"host" => {"master" => { "server-one" => {"response" => {"outcome" => "success"}}, "server-two" => {"response" => {"outcome" => "success"}} }}}}, "response-headers" => {"process-state" => "reload-required"} } [domain@localhost:9990 /]请记住,一旦配置已重新加载,对配置文件的操作将对引用
full配置文件的所有服务器组中的所有服务器传播更改。好的,我们已经完成了配置。
-
让我们通过打开浏览器并将它指向
https://localhost:8443/来测试一切:![域]()
浏览器警告用户关于自签名证书,因此不受信任
-
首先,浏览器会警告你关于一个安全问题;只需点击 添加异常 按钮,然后,在下一个弹出窗口中,点击 确认安全异常 按钮。一旦确认,浏览器将显示我们的 WildFy 实例正在通过 HTTPS 运行,如下所示:
![域]()
浏览器通过 HTTPS 显示 WildFly
它是如何工作的...
在 WildFly 方面,我们在 management 部分中声明了一个新的域,命名为 SSLRealm。
在新的域中,我们声明了密钥库,其中包含用于 HTTPS 协议加密数据的证书。
最后,对于两种操作模式,我们在 Undertow 子系统中添加了 https-listener,引用了新创建的域和 https 套接字绑定。
这就是 WildFly 通过安全通道(即 HTTPS)提供服务所需的所有内容。
还有更多...
如您从配置文件 standalone.xml 和 domain.xml 中注意到的那样,我们保留了 http-listener。事实上,我们的应用程序也通过 HTTP 可用,这是显而易见的,所以以下两个 URL 都会提供我们的 WildFly 欢迎页面以及任何应用程序:
-
http://localhost:8080 -
https://localhost:8443
如果您看到以下页面,那么是因为您正确地输入了主机名和端口号(即localhost和8443),但您使用了 HTTP 方案,而不是 HTTPS 方案:

浏览器尝试使用 US-ASCII 编码显示加密数据
然而,如果您只想提供安全通道,请从配置中删除http-listener声明,这样您就会得到安全保障。
请记住,禁用http-listener需要一点注意,因为它被其他子系统(如ejb3和webservices)引用(这两个都与http-remoting-connector相关,它绑定到http-listener)。
这样做,您的应用程序将无法通过 HTTP 访问,如下面的截图所示:

浏览器无法通过 HTTP 访问应用程序
参见…
- 要深入了解 keytool 命令,请参阅 Oracle 官方文档,链接为
docs.oracle.com/javase/8/docs/technotes/tools/unix/keytool.html。
使用 HTTPS 保护特定应用程序
在这个菜谱中,我们将学习如何为特定应用程序使用安全通道。这与您的应用程序如何被访问有关,而不是与如何保护您的数据模型、功能以及应用程序附带的所有功能有关。保护应用程序需要不同的考虑和方面,这些通常需要在开发端解决。
准备工作
要开始,让我们首先创建一个ad-hoc文件夹来运行我们的 WildFly。在终端窗口中执行以下命令:
$ cd $WILDFLY_HOME
$ cp -a standalone sec-std-node-2
现在是时候创建我们的密钥库了,它用于加密特定应用程序的数据流量:
-
打开一个新的终端窗口,并运行以下代码:
$ cd $WILDFLY_HOME $ cd sec-std-node-2/configuration $ keytool -v -genkey -alias wildfly.ssl.app -keyalg RSA -keysize 2048 -sigalg SHA1withRSA -keystore wildfly.ssl.app.keystore -storepass keystore.2015 -keypass alias.2015 What is your first and last name? [Unknown]: WildFly Cookbook What is the name of your organizational unit? [Unknown]: Packt Publishing What is the name of your organization? [Unknown]: packtpub.com What is the name of your City or Locality? [Unknown]: Birmingham What is the name of your State or Province? [Unknown]: GB What is the two-letter country code for this unit? [Unknown]: UK Is CN=WildFly Cookbook, OU=Packt Publishing, O=packtpub.com, L=Birmingham, ST=GB, C=UK correct? [no]: yes Generating 2,048 bit RSA key pair and self-signed certificate (SHA1withRSA) with a validity of 90 days for: CN=WildFly Cookbook, OU=Packt Publishing, O=packtpub.com, L=Birmingham, ST=GB, C=UK [Storing wildfly.ssl.app.keystore] -
好的,现在我们已经创建了用于加密 HTTP 消息的密钥库。让我们通过执行以下命令来检查其完整性:
$ keytool -list -v -keystore wildfly.ssl.app.keystore Enter keystore password: Keystore type: JKS Keystore provider: SUN Your keystore contains 1 entry Alias name: wildfly.ssl.app Creation date: Nov 21, 2014 Entry type: PrivateKeyEntry Certificate chain length: 1 Certificate[1]: Owner: CN=WildFly Cookbook, OU=Packt Publishing, O=packtpub.com, L=Birmingham, ST=GB, C=UK Issuer: CN=WildFly Cookbook, OU=Packt Publishing, O=packtpub.com, L=Birmingham, ST=GB, C=UK Serial number: 17c96347 Valid from: Fri Nov 21 16:04:21 CET 2014 until: Thu Feb 19 16:04:21 CET 2015 Certificate fingerprints: MD5: 17:F1:E5:1D:93:4D:FE:AD:43:5A:7A:D6:79:9E:3A:6A SHA1: 90:7B:26:B0:07:6D:B2:E3:AD:A1:81:D2:F1:AA:47:C0:8D:0B:6D:43 SHA256: C9:F3:AC:23:B7:54:45:AC:84:D7:D6:D7:A7:5D:B9:7C:ED:99:95:EC:9C:B9:9C:E0:47:68:30:C0:48:9D:D8:BD Signature algorithm name: SHA1withRSA Version: 3 Extensions: #1: ObjectId: 2.5.29.14 Criticality=false SubjectKeyIdentifier [ KeyIdentifier [ 0000: 8C 21 34 9C F6 30 39 BD 21 43 CF 34 C4 31 A1 B7 .!4..09.!C.4.1.. 0010: 81 7E E6 D1 .... ] ] ******************************************* *******************************************好的,一切正常!
我们现在准备好配置 WildFly 以通过 HTTPS 公开我们的特定应用程序。
-
要测试 HTTPS 配置,我们需要两个名为
ssl-example和no-ssl-example的应用程序,您可以在我的 GitHub 仓库中找到它们。如果您跳过了第二章中的使用部署文件夹管理应用程序菜谱,请参阅它以下载您将需要的所有源代码和项目。 -
要构建应用程序,请执行以下操作:
$ cd ~/WFC/github/wildfly-cookbook $ mvn -e clean package -f ssl-example/pom.xml $ mvn -e clean package -f no-ssl-example/pom.xml -
完成后,将
no-ssl-example.war和ssl-example.war(位于它们各自的target文件夹下)复制到您的本地$WILDFLY_HOME文件夹中。
如何做到这一点…
现在我们将对 WildFly 的配置文件进行一些修改,以实现我们的目标。我们将看到两种操作模式:独立模式和域模式。
独立模式
首先,按照以下方式启动一个 WildFly 实例:
$ cd $WILDFLY_HOME
$ ./bin/standalone.sh -Djboss.server.base.dir=sec-std-node-2
在另一个终端窗口中,连接到 WildFly CLI 并执行以下操作:
$ cd $WILDFLY_HOME
$ ./bin/jboss-cli.sh --connect
[standalone@localhost:9990 /] /core-service=management/security-realm=AppSSLRealm:add()
{"outcome" => "success"}
[standalone@localhost:9990 /] /core-service=management/security-realm=AppSSLRealm/server-identity=ssl:add(keystore-path=wildfly.ssl.app.keystore, keystore-relative-to=jboss.server.config.dir,keystore-password=keystore.2015, alias=wildfly.ssl.app, key-password=alias.2015)
{
"outcome" => "success",
"response-headers" => {
"operation-requires-reload" => true,
"process-state" => "reload-required"
}
}
[standalone@localhost:9990 /] :reload()
{
"outcome" => "success",
"result" => undefined
}
[standalone@localhost:9990 /] /subsystem=undertow/server=secure-server:add()
{
"outcome" => "success",
"response-headers" => {
"operation-requires-reload" => true,
"process-state" => "reload-required"
}
}
[standalone@localhost:9990 /] :reload
{
"outcome" => "success",
"result" => undefined
}
[standalone@localhost:9990 /] /subsystem=undertow/server=secure-server/https-listener=https:add(socket-binding=https, security-realm=AppSSLRealm)
{"outcome" => "success"}
[standalone@localhost:9990 /] /subsystem=undertow/server=secure-server/host=secure-host:add()
{"outcome" => "success"}
[standalone@localhost:9990 /] /subsystem=undertow/server=secure-server:write-attribute(name=default-host,value=secure-host)
{
"outcome" => "success",
"response-headers" => {
"operation-requires-reload" => true,
"process-state" => "reload-required"
}
}
[standalone@localhost:9990 /] :reload
{
"outcome" => "success",
"result" => undefined
}
[standalone@localhost:9990 /]
好的,我们已经完成了配置。
测试
-
我们现在需要使用 CLI 部署我们的应用程序,如下所示:
[standalone@localhost:9990 /] deploy no-ssl-example.war [standalone@localhost:9990 /] deploy ssl-example.war [standalone@localhost:9990 /] -
要测试配置,请打开您的浏览器并将其指向
http://localhost:8080/no-ssl-example。 -
您应该会看到一个类似于以下页面:
![Testing]()
-
现在,将浏览器指向
https://localhost:8443/ssl-example。 -
在安全警告之后,你应该会看到一个类似于以下页面:
![Testing]()
现在如果你尝试将模式、端口和上下文应用程序混合,你会看到ssl-example应用程序只能通过 HTTPS 访问,而no-ssl-example只能通过 HTTP 访问。
域
-
首先,创建一个
ad-hoc文件夹以在域模式下操作:$ cd $WILDFLY_HOME $ cp -a domain sec-dmn-2 -
从本食谱的准备就绪部分,看看您如何创建密钥库,或者从
sec-std-node-2文件夹中复制它(如果您遵循了独立部分的步骤),如下所示:$ cp sec-std-node-2/configuration/wildfly.ssl.app.keystore sec-dmn-2/configuration -
现在以域模式启动 WildFly,如下所示:
$ cd $WILDFLY_HOME $ ./bin/domain.sh -Djboss.domain.base.dir=sec-dmn-2 -
接下来,在另一个终端窗口中,连接到 WildFly CLI 并执行以下命令:
[domain@localhost:9990 /] /host=master/core-service=management/security-realm=AppSSLRealm:add() { "outcome" => "success", "result" => undefined, "server-groups" => undefined } [domain@localhost:9990 /] /host=master/core-service=management/security-realm=AppSSLRealm/server-identity=ssl:add(keystore-path=wildfly.ssl.app.keystore,keystore-relative-to=jboss.domain.config.dir,keystore-password=keystore.2015,alias=wildfly.ssl.app,key-password=alias.2015) { "outcome" => "success", "response-headers" => { "operation-requires-reload" => true, "process-state" => "reload-required" }, "result" => undefined, "server-groups" => undefined } [domain@localhost:9990 /] reload --host=master [domain@localhost:9990 /]如我们所知,WildFly 默认域配置提供了两个服务器组,一个绑定到
full配置文件,另一个绑定到full-ha配置文件(这个没有活动服务器),我们可以为full配置文件启用 HTTPS 协议。 -
在相同的 CLI 控制台中,执行以下命令:
[domain@localhost:9990 /] /profile=full/subsystem=undertow/server=secure-server:add() { "outcome" => "success", "result" => undefined, "server-groups" => {"main-server-group" => {"host" => {"master" => { "server-one" => {"response" => { "outcome" => "success", "response-headers" => { "operation-requires-reload" => true, "process-state" => "reload-required" } }}, "server-two" => {"response" => { "outcome" => "success", "response-headers" => { "operation-requires-reload" => true, "process-state" => "reload-required" } }} }}}} } [domain@localhost:9990 /] reload --host=master [domain@localhost:9990 /] /profile=full/subsystem=undertow/server=secure-server/https-listener=https:add(socket-binding=https,security-realm=AppSSLRealm) { "outcome" => "success", "result" => undefined, "server-groups" => {"main-server-group" => {"host" => {"master" => { "server-one" => {"response" => {"outcome" => "success"}}, "server-two" => {"response" => {"outcome" => "success"}} }}}} } [domain@localhost:9990 /] /profile=full/subsystem=undertow/server=secure-server/host=secure-host:add() { "outcome" => "success", "result" => undefined, "server-groups" => {"main-server-group" => {"host" => {"master" => { "server-one" => {"response" => {"outcome" => "success"}}, "server-two" => {"response" => {"outcome" => "success"}} }}}} } [domain@localhost:9990 /] /profile=full/subsystem=undertow/server=secure-server:write-attribute(name=default-host,value=secure-host) { "outcome" => "success", "result" => undefined, "server-groups" => {"main-server-group" => {"host" => {"master" => { "server-one" => {"response" => { "outcome" => "success", "response-headers" => { "operation-requires-reload" => true, "process-state" => "reload-required" } }}, "server-two" => {"response" => { "outcome" => "success", "response-headers" => { "operation-requires-reload" => true, "process-state" => "reload-required" } }} }}}} } [domain@localhost:9990 /] reload --host=master [domain@localhost:9990 /]
请记住,一旦配置已重新加载,对配置文件的操作将影响所有属于引用full配置文件的服务器组中的服务器。
好的,我们已经完成了配置。
测试
我们现在需要使用 CLI 部署我们的应用程序,如下所示:
[domain@localhost:9990 /] deploy no-ssl-example.war --server-groups=main-server-group
[domain@localhost:9990 /] deploy ssl-example.war --server-groups=main-server-group
[domain@localhost:9990 /]
要测试配置,请打开您的浏览器并将其指向以下 URL:
-
http://localhost:8080/no-ssl-example -
https://localhost:8443/ssl-example
您应该遵循与独立模式中描述的相同步骤,以及相同的最终页面。
它是如何工作的...
在 WildFly 端,使用 CLI,我们在management部分创建了一个新的域,命名为AppSSLRealm。在新域中,我们声明了密钥库,其中包含用于 HTTPS 协议加密数据的证书。
在Undertow子系统上工作,我们添加了一个名为secure-server的服务器。然后我们向其添加了https-listener,将监听器绑定到https套接字绑定配置和AppSSLRealm安全域。
最后,我们定义了一个名为secure-host的主机,并将其设置为我们的secure-server的默认主机。
等一下!我们是如何将ssl-example.war应用程序与在Undertow子系统声明的secure-host配置匹配的?
匹配发生在应用程序级别。在我们的应用程序WEB-INF文件夹中的jboss-web.xml中,您需要声明以下内容:
<?xml version="1.0" encoding="UTF-8"?>
<jboss-web>
<server-instance>secure-server</server-instance>
<virtual-host>secure-host</virtual-host>
</jboss-web>
上述 XML 代码指示 WildFly,该应用程序需要绑定到名为secure-server的服务器,以及名为secure-host的主机。
这样 WildFly(实际上是 Undertow)将使用该特定主机来提供服务。
让我们同时查看这两个配置,以便更好地理解匹配:
| WildFly – Undertow | 应用程序 |
|---|
|
<server name="secure-server" default-host="secure-host ">
<https-listener name="https" socket-binding="https" security-realm="SSLRealm"/>
<host name="secure-host">
<filter-ref name="server-header"/>
<filter-ref name="x-powered-by-header"/>
</host>
</server>
|
<?xml version="1.0" encoding="UTF-8"?>
<jboss-web>
<server-instance>secure-server</server-instance>
<virtual-host>secure-host</virtual-host>
</jboss-web>
|
在server声明中,还有一个名为default-host的属性设置为secure-host,但它只是用来指示如果有多于一个主机时使用哪个主机。
还有更多……
如果你尝试通过浏览器混合我们的配置,你会注意到使用http-listener配置找不到ssl-example应用程序。对于使用https-listener配置的example应用程序,情况也是如此。
如果你尝试打开浏览器并将它指向http://localhost:8080/ssl-example,你应该会看到一个404 – Not Found页面,如下截图所示:

如果你访问https://localhost:8443/no-ssl-example,同样适用。
参见……
- 要深入了解
keytool命令,请参阅 Oracle 官方文档docs.oracle.com/javase/8/docs/technotes/tools/unix/keytool.html。
使用 HTTPS 保护 WildFly 控制台
在这个菜谱中,我们将学习如何在通信协议级别保护 WildFly 管理控制台。为了实现这种配置,我们需要创建一个证书,该证书将用于加密所有流量。
准备工作
要开始,让我们首先创建一个ad-hoc文件夹来运行我们的 WildFly。在终端窗口中运行以下命令:
$ cd $WILDFLY_HOME
$ cp -a standalone sec-std-node-mgmt
现在是时候创建我们的密钥库,该密钥库用于加密数据传输。
-
打开一个新的终端窗口并执行以下命令:
$ cd $WILDFLY_HOME $ cd sec-std-node-mgmt/configuration $ keytool -v -genkey -alias wildfly.management -keyalg RSA -keysize 2048 -sigalg SHA1withRSA -keystore wildfly.management.keystore -storepass keystore.2015 -keypass alias.2015 What is your first and last name? [Unknown]: WildFly Cookbook What is the name of your organizational unit? [Unknown]: Packt Publishing What is the name of your organization? [Unknown]: packtpub.com What is the name of your City or Locality? [Unknown]: Birmingham What is the name of your State or Province? [Unknown]: GB What is the two-letter country code for this unit? [Unknown]: UK Is CN=WildFly Cookbook, OU=Packt Publishing, O=packtpub.com, L=Birmingham, ST=GB, C=UK correct? [no]: yes Generating 2,048 bit RSA key pair and self-signed certificate (SHA1withRSA) with a validity of 90 days for: CN=WildFly Cookbook, OU=Packt Publishing, O=packtpub.com, L=Birmingham, ST=GB, C=UK [Storing wildfly.management.keystore] -
好的,现在我们已经创建了用于加密 HTTP 消息的密钥库。让我们通过执行以下命令来检查其完整性:
$ keytool -list -v -keystore wildfly.management.keystore Enter keystore password: Keystore type: JKS Keystore provider: SUN Your keystore contains 1 entry Alias name: wildfly.management Creation date: Nov 19, 2014 Entry type: PrivateKeyEntry Certificate chain length: 1 Certificate[1]: Owner: CN=WildFly Cookbook, OU=Packt Publishing, O=packtpub.com, L=Birmingham, ST=GB, C=UK Issuer: CN=WildFly Cookbook, OU=Packt Publishing, O=packtpub.com, L=Birmingham, ST=GB, C=UK Serial number: 3bdf9d9 Valid from: Wed Nov 19 15:26:50 CET 2014 until: Tue Feb 17 15:26:50 CET 2015 Certificate fingerprints: MD5: C6:D1:87:5D:93:FC:C4:55:9D:7E:77:A4:9F:94:C1:68 SHA1: DF:B4:E6:96:D4:08:2C:58:A9:62:F1:B7:6F:F8:5E:3E:47:43:06:6F SHA256: E2:B9:47:D4:22:32:D7:D3:6A:A9:38:FF:E2:1F:FC:4E:A3:1A:5D:53:77:95:1E:5C:8E:A7:26:5E:89:6D:BE:44 Signature algorithm name: SHA1withRSA Version: 3 Extensions: #1: ObjectId: 2.5.29.14 Criticality=false SubjectKeyIdentifier [ KeyIdentifier [ 0000: 42 20 64 A6 07 50 7D 05 16 0F 21 25 78 1A 66 06 B d..P....!%x.f. 0010: 97 8C B3 F2 .... ] ] ******************************************* *******************************************
好的,一切正常!
我们现在已准备好配置 WildFly,通过 HTTPS 公开其管理控制台。
如何做……
我们现在将对 WildFly 的配置文件进行一些修改,以达到我们的目标。我们将看到两种操作模式:独立模式和域模式。
独立模式
首先,由于我们将创建并使用一个新的管理域(命名为SecureManagementRealm),我们需要向其中添加一个新的管理用户(命名为securewildfly)。
-
打开一个新的终端窗口并执行以下命令:
$ cd $WILDFLY_HOME $ java -cp modules/system/layers/base/org/jboss/sasl/main/jboss-sasl-1.0.5.Final.jar org.jboss.sasl.util.UsernamePasswordHashUtil securewildfly SecureManagementRealm cookbook.2015 >> sec-std-node-mgmt/configuration/secure-mgmt-users.properties小贴士
确保在运行前面的命令后清除 OS 历史命令,因为密码也会显示。要清除历史记录,在相同终端中调用
history -c命令。 -
现在我们可以继续进行有效配置。以下是如何启动 WildFly:
$ cd $WILDFLY_HOME $ ./bin/standalone.sh -Djboss.server.base.dir=sec-std-node-mgmt -
现在,在另一个终端窗口中连接到 WildFly CLI 并运行以下命令:
$ cd $WILDFLY_HOME $ ./bin/jboss-cli.sh --connect [standalone@localhost:9990 /] batch [standalone@localhost:9990 / #] /core-service=management/security-realm=SecureManagementRealm:add() [standalone@localhost:9990 / #] /core-service=management/security-realm=SecureManagementRealm/authentication=local:add(skip-group-loading=true, default-user="$local") [standalone@localhost:9990 / #] /core-service=management/security-realm=SecureManagementRealm/authentication=properties:add(path=secure-mgmt-users.properties, relative-to=jboss.server.config.dir) [standalone@localhost:9990 / #] /core-service=management/security-realm=SecureManagementRealm/authorization=properties:add(path=mgmt-groups.properties, relative-to=jboss.server.config.dir) [standalone@localhost:9990 / #] /core-service=management/security-realm=SecureManagementRealm:write-attribute(name=map-groups-to-roles,value=false) [standalone@localhost:9990 / #] /core-service=management/security-realm=SecureManagementRealm/server-identity=ssl:add(keystore-path=wildfly.management.keystore,keystore-relative-to=jboss.server.config.dir,keystore-password=keystore.2015,alias=wildfly.management, key-password=alias.2015) [standalone@localhost:9990 / #] /core-service=management/management-interface=http-interface:write-attribute(name=security-realm,value=SecureManagementRealm) [standalone@localhost:9990 / #] /core-service=management/management-interface=http-interface:write-attribute(name=secure-socket-binding,value=management-https) [standalone@localhost:9990 / #] :reload() [standalone@localhost:9990 / #] run-batch The batch executed successfully [standalone@localhost:9990 /]
好的,我们已经完成了配置。
测试
打开您的浏览器,将其指向https://localhost:9993:

浏览器警告用户关于不受信任的证书
首先,浏览器会警告您存在安全问题;只需点击添加异常按钮,然后在下一个弹出窗口中点击确认安全异常按钮。
浏览器将提示您输入凭证以访问SecureManagementRealm的 WildFly 管理控制台;只需输入securewildfly作为用户名,cookbook.2015作为密码。
你现在应该已经进入了 Web 控制台,如下图中所示:

通过 HTTPS 的 WildFly 管理控制台
太好了,我们完成了!
顺便说一句,通过 HTTPS 保护控制台本身可能没问题,但仍然,有凭证的人可以登录并对其进行操作。为了更好地保护您的管理控制台,您应该专注于 WildFly 中可用的基于角色的访问控制(RBAC)功能,本书稍后讨论,它为您提供了更细粒度的控制,谁可以做什么。
域
-
首先,创建一个
ad-hoc文件夹以在域模式下操作:$ cd $WILDFLY_HOME $ cp -a domain sec-dmn-mgmt -
从本食谱的
准备就绪部分,看看您如何创建密钥库,或者从sec-std-node-mgmt文件夹中复制它(如果您遵循了独立部分的步骤),如下所示:$ cp sec-std-node-mgmt/configuration/wildfly.management.keystore sec-dmn-mgmt/configuration/接下来,由于我们将创建并使用一个新的管理域(命名为
SecureManagementRealm),我们需要向其中添加一个新的管理用户(命名为securewildfly)。 -
打开一个新的终端窗口并执行以下命令:
$ cd $WILDFLY_HOME $ java -cp modules/system/layers/base/org/jboss/sasl/main/jboss-sasl-1.0.5.Final.jar org.jboss.sasl.util.UsernamePasswordHashUtil securewildfly SecureManagementRealm cookbook.2015 >> sec-dmn-mgmt/configuration/secure-mgmt-users.properties小贴士
确保在运行上述命令后清除 OS 历史命令,因为密码也会显示出来。要清除历史记录,在同一终端中调用
history -c命令。 -
现在按照以下方式以域模式启动 WildFly:
$ cd $WILDFLY_HOME $ ./bin/domain.sh -Djboss.domain.base.dir=sec-dmn-mgmt -
接下来,在另一个终端窗口中,连接到 WildFly CLI 并运行以下命令:
$ ./bin/jboss-cli.sh --connect [domain@localhost:9990 /] batch [domain@localhost:9990 / #] /host=master/core-service=management/security-realm=SecureManagementRealm:add() [domain@localhost:9990 / #] /host=master/core-service=management/security-realm=SecureManagementRealm/authentication=local:add(skip-group-loading=true, default-user="$local") [domain@localhost:9990 / #] /host=master/core-service=management/security-realm=SecureManagementRealm/authentication=properties:add(path=secure-mgmt-users.properties, relative-to=jboss.domain.config.dir) [domain@localhost:9990 / #] /host=master/core-service=management/security-realm=SecureManagementRealm/authorization=properties:add(path=mgmt-groups.properties, relative-to=jboss.domain.config.dir) [domain@localhost:9990 / #] /host=master/core-service=management/security-realm=SecureManagementRealm:write-attribute(name=map-groups-to-roles,value=false) [domain@localhost:9990 / #] /host=master/core-service=management/security-realm=SecureManagementRealm/server-identity=ssl:add(keystore-path=wildfly.management.keystore,keystore-relative-to=jboss.domain.config.dir,keystore-password=keystore.2015,alias=wildfly.management, key-password=alias.2015) [domain@localhost:9990 / #] /host=master/core-service=management/management-interface=http-interface:write-attribute(name=security-realm,value=SecureManagementRealm) [domain@localhost:9990 / #] /host=master/core-service=management/management-interface=http-interface:write-attribute(name=secure-port,value="${jboss.management.https.port:9993}") [domain@localhost:9990 / #] run-batch The batch executed successfully process-state: reload-required [domain@localhost:9990 /] reload --host=master Failed to establish connection in 6058ms: WFLYPRT0053: Could not connect to http-remoting://localhost:9990\. The connection failed: XNIO000816: Redirect encountered establishing connection [disconnected /]
我们被踢出控制台,因为它不再可以通过端口9990访问,而是通过默认的端口9993。
测试
当您打开浏览器并将指针指向https://localhost:9993 URL 时,您将看到与独立模式中描述的相同内容:一个关于自签名证书的安全警告,登录弹出窗口,最后是 WildFly 管理控制台。
它是如何工作的...
首先,我们通过 CLI 定义了安全域,并将其添加到主机,在本例中是master。在新域中,我们声明了 HTTPS 协议所使用的密钥库来加密数据。
接下来,我们将新创建的SecureManagementRealm引用到management-interface的http-interface部分。为此,我们还需要指定secure-port,否则我们会将整个管理接口绑定到默认端口9990,这会导致我们遇到ssl_error_rx_record_too_long浏览器错误。
那就是 WildFly 通过 HTTPS 提供管理控制台所需的所有内容。
更多内容...
在这个菜谱中,我们已经创建并使用了一个新的领域来保护管理控制台。尽管如此,我们也可以使用默认的ManagementRealm,这是可以的。
使用不同的领域将使我们能够根据需要切换领域。但更重要的是,当你使用add-user.sh脚本创建用户时,生成的密码包含用户名、领域名称和密码的字符串,然后使用 MD5 进行哈希处理,并以十六进制值解码。
因此,在您的配置的*-user.properties文件中,当您看到username=SOMETHING时,您应该这样阅读:
username=HEX(MD5('username':'realm':'password'))
参见
- 要深入了解
keytool命令,请参阅 Oracle 官方文档docs.oracle.com/javase/8/docs/technotes/tools/unix/keytool.html。
使用 HTTPS 确保域和主机控制器通信安全
在这个菜谱中,我们将学习如何确保域控制器和主机控制器之间的通信安全。为了实现这一点,我们需要为每个控制器创建一个密钥库和证书,包括域。
准备工作
要开始,让我们首先创建ad-hoc文件夹来运行我们的 WildFly 实例:一个主服务器和两个主机。在终端窗口中执行以下命令:
$ cd $WILDFLY_HOME
$ cp -a domain sec-dmn-master
$ cp -a domain sec-dmn-node-1
$ cp -a domain sec-dmn-node-2
我们还应该准备配置文件夹,以便拥有适当的配置文件,使用预安装的文件作为模板并执行以下命令:
$ cd $WILDFLY_HOME
$ mv sec-dmn-master/configuration/host-master.xml sec-dmn-master/configuration/host.xml
$ mv sec-dmn-node-1/configuration/domain.xml sec-dmn-node-1/configuration/domain.xml.unused
$ mv sec-dmn-node-1/configuration/host-slave.xml sec-dmn-node-1/configuration/host.xml
$ mv sec-dmn-node-2/configuration/domain.xml sec-dmn-node-2/configuration/domain.xml.unused
$ mv sec-dmn-node-2/configuration/host-slave.xml sec-dmn-node-2/configuration/host.xml
现在我们可以继续创建证书。
如何操作…
我们将首先为每个服务器创建密钥库;然后从中提取证书,最后将主机的证书导入到域控制器密钥库中,并将域证书导入到主机的密钥库中。很快就会变得清楚。
-
打开一个新的终端窗口并运行以下命令:
$ cd $WILDFLY_HOME $ keytool -genkeypair -alias sec-dmn-master -keyalg RSA -keysize 1024 -validity 365 -keystore sec-dmn-master.jks -dname "CN=sec-dmn-master,OU=Packt Publishing,O=packtpub.com,L=Birmingham,ST=GB,C=UK" -keypass "cookbook.2015" -storepass "cookbook.2015" $ keytool -genkeypair -alias sec-dmn-node-1 -keyalg RSA -keysize 1024 -validity 365 -keystore sec-dmn-node-1.jks -dname "CN=sec-dmn-node-1,OU=Packt Publishing,O=packtpub.com,L=Birmingham,ST=GB,C=UK" -keypass "cookbook.2015" -storepass "cookbook.2015" $ keytool -genkeypair -alias sec-dmn-node-2 -keyalg RSA -keysize 1024 -validity 365 -keystore sec-dmn-node-2.jks -dname "CN=sec-dmn-node-2,OU=Packt Publishing,O=packtpub.com,L=Birmingham,ST=GB,C=UK" -keypass "cookbook.2015" -storepass "cookbook.2015" -
现在我们需要将证书从每个密钥库中导出,并存储在文件中。在之前的
keytool命令相同的终端中,执行以下命令:$ keytool -exportcert -keystore sec-dmn-master.jks -alias sec-dmn-master -keypass "cookbook.2015" -storepass "cookbook.2015" -file sec-dmn-master.cer $ keytool -exportcert -keystore sec-dmn-node-1.jks -alias sec-dmn-node-1 -keypass "cookbook.2015" -storepass "cookbook.2015" -file sec-dmn-node-1.cer $ keytool -exportcert -keystore sec-dmn-node-2.jks -alias sec-dmn-node-2 -keypass "cookbook.2015" -storepass "cookbook.2015" -file sec-dmn-node-2.cer -
现在如果您查看
$WILDFLY_HOME文件夹,您应该看到以下文件:-
sec-dmn-master.cer -
sec-dmn-master.jks -
sec-dmn-node-1.cer -
sec-dmn-node-1.jks -
sec-dmn-node-2.cer -
sec-dmn-node-2.jks
-
-
现在我们需要将主机的证书导入到
domain密钥库中,如下所示:$ keytool -importcert -keystore sec-dmn-master.jks -storepass "cookbook.2015" -trustcacerts -alias sec-dmn-node-1 -file sec-dmn-node-1.cer Owner: CN=sec-dmn-node-1, OU=Packt Publishing, O=packtpub.com, L=Birmingham, ST=GB, C=UK Issuer: CN=sec-dmn-node-1, OU=Packt Publishing, O=packtpub.com, L=Birmingham, ST=GB, C=UK Serial number: 47cc055 Valid from: Mon Nov 24 15:48:33 CET 2014 until: Tue Nov 24 15:48:33 CET 2015 Certificate fingerprints: MD5: BB:31:D0:6F:20:78:FB:07:70:B7:E4:68:DB:EC:2C:83 SHA1: 83:DE:B0:D5:01:F4:8F:8C:5D:06:5E:6F:78:D1:28:A9:BF:C4:AE:18 SHA256: B4:4C:BC:D0:C6:EC:5E:11:D0:0E:BB:5F:84:74:D4:8B:9C:EA:13:17:A6:E2:6E:1B:C2:65:DC:16:9B:F0:0D:D4 Signature algorithm name: SHA256withRSA Version: 3 Extensions: #1: ObjectId: 2.5.29.14 Criticality=false SubjectKeyIdentifier [ KeyIdentifier [ 0000: F2 12 C7 78 60 40 26 A3 7D 43 E3 14 0F 76 46 B0 ...x`@&..C...vF. 0010: 62 A8 52 40 b.R@ ] ] Trust this certificate? [no]: yes Certificate was added to keystore -
完成后,让我们为其他主机做同样的操作:
$ keytool -importcert -keystore sec-dmn-master.jks -storepass "cookbook.2015" -trustcacerts -alias sec-dmn-node-2 -file sec-dmn-node-2.cer Owner: CN=sec-dmn-node-2, OU=Packt Publishing, O=packtpub.com, L=Birmingham, ST=GB, C=UK Issuer: CN=sec-dmn-node-2, OU=Packt Publishing, O=packtpub.com, L=Birmingham, ST=GB, C=UK Serial number: 4cc64451 Valid from: Mon Nov 24 15:48:34 CET 2014 until: Tue Nov 24 15:48:34 CET 2015 Certificate fingerprints: MD5: 29:CD:32:78:13:CD:63:7E:16:CE:AE:FC:4A:00:48:7D SHA1: 7D:19:1B:C9:B8:61:72:10:C1:9A:80:98:36:6F:8F:D6:B9:87:F9:83 SHA256: 0A:5E:12:4D:EF:41:BC:AB:4C:7F:56:23:7B:80:E0:00:6C:D0:AC:7C:37:B8:FA:51:ED:2A:70:98:39:67:F7:4B Signature algorithm name: SHA256withRSA Version: 3 Extensions: #1: ObjectId: 2.5.29.14 Criticality=false SubjectKeyIdentifier [ KeyIdentifier [ 0000: 6E 82 DC 55 F3 91 29 55 25 E7 B5 88 96 F5 1F 42 n..U..)U%......B 0010: 0A 52 7F 64 .R.d ] ] Trust this certificate? [no]: yes Certificate was added to keystore -
现在是主机导入
domain证书的时候了,如下所示:$ keytool -importcert -keystore sec-dmn-node-1.jks -storepass "cookbook.2015" -trustcacerts -alias sec-dmn-master -file sec-dmn-master.cer Owner: CN=sec-dmn-master, OU=Packt Publishing, O=packtpub.com, L=Birmingham, ST=GB, C=UK Issuer: CN=sec-dmn-master, OU=Packt Publishing, O=packtpub.com, L=Birmingham, ST=GB, C=UK Serial number: 337c6e75 Valid from: Mon Nov 24 15:48:33 CET 2014 until: Tue Nov 24 15:48:33 CET 2015 Certificate fingerprints: MD5: 20:B4:7F:FB:E6:E5:6C:A8:29:82:19:2B:F7:56:90:B8 SHA1: 1F:1D:64:49:F5:B5:A4:CC:B7:CA:4C:15:3C:E6:75:C4:E6:03:09:F7 SHA256: C0:66:C8:FF:E3:B8:CD:5B:6D:99:61:1D:6B:05:19: F0:05:B1:28:D0:4D:96:CB:AC:B4:89:FB:2B:73:01:7D:04 Signature algorithm name: SHA256withRSA Version: 3 Extensions: #1: ObjectId: 2.5.29.14 Criticality=false SubjectKeyIdentifier [ KeyIdentifier [ 0000: 3B 84 5D 1C 5D C1 F6 EF 8C 2B AF C6 80 D7 03 89 ;.].]....+...... 0010: F9 0A 6D CE ..m. ] ] Trust this certificate? [no]: yes Certificate was added to keystore -
完成后,让我们为其他主机做同样的操作:
$ keytool -importcert -keystore sec-dmn-node-2.jks -storepass "cookbook.2015" -trustcacerts -alias sec-dmn-master -file sec-dmn-master.cer Owner: CN=sec-dmn-master, OU=Packt Publishing, O=packtpub.com, L=Birmingham, ST=GB, C=UK Issuer: CN=sec-dmn-master, OU=Packt Publishing, O=packtpub.com, L=Birmingham, ST=GB, C=UK Serial number: 337c6e75 Valid from: Mon Nov 24 15:48:33 CET 2014 until: Tue Nov 24 15:48:33 CET 2015 Certificate fingerprints: MD5: 20:B4:7F:FB:E6:E5:6C:A8:29:82:19:2B:F7:56:90:B8 SHA1: 1F:1D:64:49:F5:B5:A4:CC:B7:CA:4C:15:3C:E6:75:C4:E6:03:09:F7 SHA256: C0:66:C8:FF:E3:B8:CD:5B:6D:99:61:1D:6B:05:19:F0:05:B1:28:D0:4D:96:CB:AC:B4:89:FB:2B:73:01:7D:04 Signature algorithm name: SHA256withRSA Version: 3 Extensions: #1: ObjectId: 2.5.29.14 Criticality=false SubjectKeyIdentifier [ KeyIdentifier [ 0000: 3B 84 5D 1C 5D C1 F6 EF 8C 2B AF C6 80 D7 03 89 ;.].]....+...... 0010: F9 0A 6D CE ..m. ] ] Trust this certificate? [no]: yes Certificate was added to keystore -
好的,我们已经完成了。让我们将每个密钥库复制到其
ad-hoc配置文件夹中,如下所示:$ cd $WILDFLY_HOME $ cp sec-dmn-master.jks sec-dmn-master/configuration/ $ cp sec-dmn-node-1.jks sec-dmn-node-1/configuration/ $ cp sec-dmn-node-2.jks sec-dmn-node-2/configuration/ -
现在,我们需要对默认域配置做一些调整。打开
sec-dmn-master/configuration/domain.xml文件,并将default配置文件设置为文件中声明的所有server-groups。同时,将socket-binding-group引用设置为standard-sockets,再次为所有声明的服务器组——我们这样做只是为了避免由于full和full-ha配置文件导致的复杂配置,这些配置涉及消息传递和集群。 -
接下来,打开
sec-dmn-node-1/configuration/host.xml并按照以下方式更改主机名:<host name="sec-dmn-node-1" > -
确保为每个配置的服务器正确设置端口偏移量属性(默认配置提供了两个名为
server-one和server-two的服务器),如下所示:server-one - <socket-bindings port-offset="100"/> server-two - <socket-bindings port-offset="150"/> -
让我们对另一个主机也做同样的操作。打开
sec-dmn-node-2/configuration/host.xml并按照以下方式更改主机名:<host name="sec-dmn-node-2" > -
在这里,也要确保为每个配置的服务器正确设置端口偏移量属性,如下所示:
server-one – <socket-bindings port-offset="200"/> server-two - <socket-bindings port-offset="250"/>我们还没有完成。我们需要配置我们的密钥库,我们将使用 CLI,因此所有服务器都必须启动并运行。
-
让我们在单独的终端窗口中执行以下每个命令来启动它们:
$ ./bin/domain.sh -Djboss.domain.base.dir=sec-dmn-master $ ./bin/domain.sh -Djboss.domain.base.dir=sec-dmn-node-1 -Djboss.management.native.port=19999 -Djboss.domain.master.address=127.0.0.1 $ ./bin/domain.sh -Djboss.domain.base.dir=sec-dmn-node-2 -Djboss.management.native.port=29999 -Djboss.domain.master.address=127.0.0.1 -
在这一点上,我们需要将我们的密钥库声明到 WildFly 配置文件中。再次,在另一个终端窗口中,按照以下方式连接到 CLI:
$ cd $WILDFLY_HOME $ ./bin/jboss-cli.sh -c batch /host=master/core-service=management/security-realm=DCHCSecureRealm:add() /host=master/core-service=management/security-realm=DCHCSecureRealm/server-identity=ssl:add(alias=sec-dmn-master,keystore-relative-to=jboss.domain.config.dir,keystore-path=sec-dmn-master.jks,keystore-password=cookbook.2015) /host=master/core-service=management/security-realm=DCHCSecureRealm/authentication=truststore:add(keystore-relative-to=jboss.domain.config.dir,keystore-path=sec-dmn-master.jks,keystore-password=cookbook.2015) /host=master/core-service=management/security-realm=DCHCSecureRealm/authentication=local:add(default-user=\$local) /host=master/core-service=management/security-realm=DCHCSecureRealm/authentication=properties:add(relative-to=jboss.domain.config.dir,path=mgmt-users.properties) /host=master/core-service=management/management-interface=native-interface:write-attribute(name=security-realm,value=DCHCSecureRealm) /host=sec-dmn-node-1/core-service=management/security-realm=DCHCSecureRealm:add() /host=sec-dmn-node-1/core-service=management/security-realm=DCHCSecureRealm/server-identity=ssl:add(alias=sec-dmn-node-1,keystore-relative-to=jboss.domain.config.dir,keystore-path=sec-dmn-node-1.jks,keystore-password=cookbook.2015) /host=sec-dmn-node-1/core-service=management/security-realm=DCHCSecureRealm/authentication=truststore:add(keystore-relative-to=jboss.domain.config.dir,keystore-path=sec-dmn-node-1.jks,keystore-password=cookbook.2015) /host=sec-dmn-node-1/core-service=management/security-realm=DCHCSecureRealm/authentication=local:add(default-user="\$local") /host=sec-dmn-node-1/core-service=management/security-realm=DCHCSecureRealm/authentication=properties:add(relative-to=jboss.domain.config.dir,path=mgmt-users.properties) /host=sec-dmn-node-1/core-service=management/management-interface=native-interface:write-attribute(name=security-realm,value=DCHCSecureRealm) /host=sec-dmn-node-1:write-remote-domain-controller(security-realm=DCHCSecureRealm) /host=sec-dmn-node-2/core-service=management/security-realm=DCHCSecureRealm:add() /host=sec-dmn-node-2/core-service=management/security-realm=DCHCSecureRealm/server-identity=ssl:add(alias=sec-dmn-node-2,keystore-relative-to=jboss.domain.config.dir,keystore-path=sec-dmn-node-2.jks,keystore-password=cookbook.2015) /host=sec-dmn-node-2/core-service=management/security-realm=DCHCSecureRealm/authentication=truststore:add(keystore-relative-to=jboss.domain.config.dir,keystore-path=sec-dmn-node-2.jks,keystore-password=cookbook.2015) /host=sec-dmn-node-2/core-service=management/security-realm=DCHCSecureRealm/authentication=local:add(default-user="\$local") /host=sec-dmn-node-2/core-service=management/security-realm=DCHCSecureRealm/authentication=properties:add(relative-to=jboss.domain.config.dir,path=mgmt-users.properties) /host=sec-dmn-node-2/core-service=management/management-interface=native-interface:write-attribute(name=security-realm,value=DCHCSecureRealm) /host=sec-dmn-node-2:write-remote-domain-controller(security-realm=DCHCSecureRealm) run-batch reload --host=master目前,请不要在意错误;我们还需要再走一步。
-
停止域控制器以及主机控制器,并按照以下方式启动它们:
$ ./bin/domain.sh -Djboss.domain.base.dir=sec-dmn-master -Djavax.net.ssl.trustStore=$WILDFLY_HOME/sec-dmn-master/configuration/sec-dmn-master.jks $ ./bin/domain.sh -Djboss.domain.base.dir=sec-dmn-node-1 -Djboss.management.native.port=19999 -Djboss.domain.master.address=127.0.0.1 -Djavax.net.ssl.trustStore=$WILDFLY_HOME/sec-dmn-node-1/configuration/sec-dmn-node-1.jks $ ./bin/domain.sh -Djboss.domain.base.dir=sec-dmn-node-2 -Djboss.management.native.port=29999 -Djboss.domain.master.address=127.0.0.1 -Djavax.net.ssl.trustStore=$WILDFLY_HOME/sec-dmn-node-2/configuration/sec-dmn-node-2.jks
现在,域控制器正在使用 HTTPS 与主机控制器通信。
它是如何工作的...
让我们用言语来解释我们需要做什么以及我们已经做了什么。
我们需要加密域控制器和主机控制器之间的流量。为了实现这一点,我们需要一个证书。因此,由于域和主机控制器之间的通信需要认证,我们还需要在它们之间创建一种可信的通信方式。所有这些机制都可以使用 Java 密钥库实现。
我们首先为域和主机控制器创建密钥库:
$ keytool -genkeypair -alias sec-dmn-master -keyalg RSA -keysize 1024 -validity 365 -keystore sec-dmn-master.jks -dname "CN=sec-dmn-master,OU=Packt Publishing,O=packtpub.com,L=Birmingham,ST=GB,C=UK" -keypass "cookbook.2015" -storepass "cookbook.2015"
$ keytool -genkeypair -alias sec-dmn-node-1 -keyalg RSA -keysize 1024 -validity 365 -keystore sec-dmn-node-1.jks -dname "CN=sec-dmn-node-1,OU=Packt Publishing,O=packtpub.com,L=Birmingham,ST=GB,C=UK" -keypass "cookbook.2015" -storepass "cookbook.2015"
$ keytool -genkeypair -alias sec-dmn-node-2 -keyalg RSA -keysize 1024 -validity 365 -keystore sec-dmn-node-2.jks -dname "CN=sec-dmn-node-2,OU=Packt Publishing,O=packtpub.com,L=Birmingham,ST=GB,C=UK" -keypass "cookbook.2015" -storepass "cookbook.2015"
我们然后从每个密钥库中提取一个证书,并将其存储在一个cer文件中:
$ keytool -exportcert -keystore sec-dmn-master.jks -alias sec-dmn-master -keypass "cookbook.2015" -storepass "cookbook.2015" -file sec-dmn-master.cer
$ keytool -exportcert -keystore sec-dmn-node-1.jks -alias sec-dmn-node-1 -keypass "cookbook.2015" -storepass "cookbook.2015" -file sec-dmn-node-1.cer
$ keytool -exportcert -keystore sec-dmn-node-2.jks -alias sec-dmn-node-2 -keypass "cookbook.2015" -storepass "cookbook.2015" -file sec-dmn-node-2.cer
由于我们需要域控制器远程连接到主机控制器,反之亦然,因此我们需要在密钥库中创建一个链接,从而将主机控制器的证书导入域控制器密钥库:
$ keytool -importcert -keystore sec-dmn-master.jks -storepass "cookbook.2015" -trustcacerts -alias sec-dmn-node-1 -file sec-dmn-node-1.cer
$ keytool -importcert -keystore sec-dmn-master.jks -storepass "cookbook.2015" -trustcacerts -alias sec-dmn-node-2 -file sec-dmn-node-2.cer
这样,sec-dmn-master.jks密钥库文件将同时作为信任库使用,其中包含主机控制器的证书。实际上,在检查sec-dmn-master.jks密钥库时,我们应该在其中找到三个条目:
$ keytool -list -v -keystore sec-dmn-master.jks
Enter keystore password:
Keystore type: JKS
Keystore provider: SUN
Your keystore contains 3 entries
Alias name: sec-dmn-node-2
...
Alias name: sec-dmn-node-1
...
Alias name: sec-dmn-master
...
同样的机制也适用于主机控制器,它们只需导入域控制器的证书:
$ keytool -importcert -keystore sec-dmn-node-1.jks -storepass "cookbook.2015" -trustcacerts -alias sec-dmn-master -file sec-dmn-master.cer
$ keytool -importcert -keystore sec-dmn-node-2.jks -storepass "cookbook.2015" -trustcacerts -alias sec-dmn-master -file sec-dmn-master.cer
为了简洁,我将不会展示这两个密钥库的检查清单;顺便说一句,你可以按照以下方式发出命令:
keytool -list -v -keystore sec-dmn-node-1.jks
keytool -list -v -keystore sec-dmn-node-2.jks
它们都应该包含两个条目。
在所有这些准备工作之后,我们必须启动所有服务器以更新我们的配置。这是因为在我们处于域模式时,我们只能看到正在运行的主机,而不是声明的主机。然后我们运行了 CLI 并在批处理模式下执行了一系列命令。
在 run-batch 命令之后,该命令实际上运行了每个命令并提交,然后我们对 host=master 运行 reload 命令,即域控制器。
当域控制器启动时,它会将其配置推送到所有连接的主机控制器,但在这个情况下,我们的主机控制器由于以下错误出现在 server.log 中而断开连接:
-
以下错误是针对
sec-dmn-master的:[Host Controller] ERROR [org.jboss.remoting.remote.connection] JBREM000200: Remote connection failed: javax.net.ssl.SSLException: Received fatal alert: certificate_unknown -
以下错误是针对
sec-dmn-node-1的:[Host Controller] WARN [org.jboss.as.host.controller] JBAS010914: Connection to remote host-controller closed. [Host Controller] INFO [org.jboss.as.host.controller] JBAS016584: Trying to reconnect to master host controller. [Host Controller] WARN [org.jboss.as.host.controller] JBAS010900: Could not connect to remote domain controller at remote://127.0.0.1:9999 -- java.net.ConnectException: JBAS012174: Could not connect to remote://127.0.0.1:9999\. The connection failed [Host Controller] WARN [org.jboss.as.host.controller] JBAS016581: No domain controller discovery options remain. [Host Controller] INFO [org.jboss.as.host.controller] JBAS016584: Trying to reconnect to master host controller. [Host Controller] WARN [org.jboss.as.host.controller] JBAS010900: Could not connect to remote domain controller at remote://127.0.0.1:9999 -- java.lang.IllegalStateException: JBAS016509: Unable to connect due to SSL failure. -
以下错误是针对
sec-dmn-node-2的:[Host Controller] WARN [org.jboss.as.host.controller] JBAS010914: Connection to remote host-controller closed. [Host Controller] INFO [org.jboss.as.host.controller] JBAS016584: Trying to reconnect to master host controller. [Host Controller] WARN [org.jboss.as.host.controller] JBAS010900: Could not connect to remote domain controller at remote://127.0.0.1:9999 -- java.net.ConnectException: JBAS012174: Could not connect to remote://127.0.0.1:9999\. The connection failed [Host Controller] WARN [org.jboss.as.host.controller] JBAS016581: No domain controller discovery options remain. [Host Controller] INFO [org.jboss.as.host.controller] JBAS016584: Trying to reconnect to master host controller. [Host Controller] WARN [org.jboss.as.host.controller] JBAS010900: Could not connect to remote domain controller at remote://127.0.0.1:9999 -- java.lang.IllegalStateException: JBAS016509: Unable to connect due to SSL failure.
域控制器启用了 SSL 通信,而主机控制器没有传递它们自己的证书,因为默认的 JVM cacert 信任存储文件被传递。这就是我们为什么必须停止一切的原因。
当重启域和主机控制器时,我们必须添加 -Djavax.net.ssl.trustStore 属性(指定启动控制器的正确密钥库),以便 SSL 握手阶段能够成功,因为在这个时候,每个控制器都会通过其相对密钥库。
按照以下命令启动域和主机控制器:
./bin/domain.sh -Djboss.domain.base.dir=sec-dmn-master -Djavax.net.ssl.trustStore=$WILDFLY_HOME/sec-dmn-master/configuration/sec-dmn-master.jks
./bin/domain.sh -Djboss.domain.base.dir=sec-dmn-node-1 -Djboss.management.native.port=19999 -Djboss.domain.master.address=127.0.0.1 -Djavax.net.ssl.trustStore=$WILDFLY_HOME/sec-dmn-node-1/configuration/sec-dmn-node-1.jks
./bin/domain.sh -Djboss.domain.base.dir=sec-dmn-node-2 -Djboss.management.native.port=29999 -Djboss.domain.master.address=127.0.0.1 -Djavax.net.ssl.trustStore=$WILDFLY_HOME/sec-dmn-node-2/configuration/sec-dmn-node-2.jks
参见……
-
要深入了解
keytool命令,请参阅 Oracle 官方文档docs.oracle.com/javase/8/docs/technotes/tools/unix/keytool.html。 -
关于
SSL协议的更多详细信息,请从en.wikipedia.org/wiki/Transport_Layer_Security开始查找。
第十一章。强化 WildFly 配置
在本章中,你将学习以下食谱:
-
使用属性文件交付你的配置
-
保护你的配置哈希密码
-
使用保险库来保护和加密密码
简介
在本章中,你将学习如何使用不同的方法来保护你的 WildFly 系统配置。保护配置意味着隐藏敏感数据,如密码,以防止其他人以某种方式参与你的项目或系统。
这个目标可以通过不同的方式实现:
-
使用属性文件外部化动态参数,例如绑定、凭证等
-
哈希密码——这是一种相当常见的技巧
-
将密码存储在保险库中——这是你可以用来保护密码的最安全的方法
第一种方法并不完全安全,因此它是一种自定义设置的清洁方法。尽管如此,它仍然可以给你分发配置文件的自由,无需担心安全问题,因为其中只会有默认设置,没有更多。此外,你的每个 WildFly 基础设施环境都可以依赖于不同的属性文件。配置是相同的;你只需交付不同的属性文件。
最后两种方法更侧重于混淆密码。哈希使用哈希算法来加密密码。因此,无论谁在 XML 配置文件中看到密码,都只会看到其哈希值,而不是真正的密码。
将密码存储在保险库中要复杂一些,但提供了更好的保护,因为证书用于加密和解密密码。
使用属性文件交付你的配置
在这个食谱中,你将学习如何使用属性文件交付你的配置。如果你不想将所有设置硬编码到 XML 文件中,从而需要为每个环境更改它们,这可能会很有用。这样,你可以通过 XML 文件提供通用配置,并通过属性文件提供特定环境的特定设置。
多亏了 WildFly 提供的属性替换功能,你可以在 XML 文件(standalone.xml、domain.xml和host.xml)中使用${your.property.goes.here}语法。
准备工作
要开始,我们首先创建一个ad-hoc文件夹来运行我们的 WildFly。在终端窗口中运行以下命令:
$ cd $WILDFLY_HOME
$ cp -a standalone sec-std-cfg-node-1
现在是时候创建一些属性了!
如何操作…
-
首先,让我们创建一个名为
wildflycookbook.properties的属性文件,并添加以下属性和值:jboss.bind.address=10.0.0.1 -
现在在你的系统中创建前面的虚拟 IP。在 Linux 中,你将打开一个终端窗口并运行以下命令:
$ sudo ifconfig eth0:1 10.0.0.1 netmask 255.255.255.0其中
eth0是你的网络接口名称。 -
现在,在终端窗口中,执行以下命令:
$ cd $WILDFLY_HOME $ ./bin/standalone.sh -Djboss.server.base.dir=sec-std-cfg-node-1 -P wildflycookbook.properties ... 17:24:05,588 INFO [org.wildfly.extension.undertow] (MSC service thread 1-16) WFLYUT0006: Undertow HTTP listener default listening on /10.0.0.1:8080 ... 17:24:06,019 INFO [org.jboss.as] (Controller Boot Thread) WFLYSRV0025: WildFly Full 9.0.0.Beta2 (WildFly Core 1.0.0.Beta2) started in 2822ms - Started 202 of 379 services (210 services are lazy, passive or on-demand)如您从日志输出中看到的,强调的字符,属性已从 WildFly 读取并在运行时放置到其配置中。因此,现在我们的 WildFly 实例绑定到
http://10.0.0.1:8080。
它是如何工作的...
这种方法相当简单直接;除了它的用法之外,没有太多可说的。前面的例子只是给你一个关于你可以用它做什么的初步想法。
-P file.property指令是 WildFly 的一个功能,可以从属性文件中加载大量系统属性。WildFly 通过为其配置提供属性替换来利用这种机制。
想象一下以下的数据源配置:
<subsystem >
<datasources>
<datasource jndi-name="java:jboss/MySqlDS" pool-name="MySqlDS">
<connection-url>jdbc:mysql://mysql-prod-cluster-node-1:3306/store</connection-url>
<driver>mysql</driver>
<security>
<user-name>root</user-name>
<password>1password</password>
</security>
</datasource>
<drivers/>
</datasources>
</subsystem>
如您所见,连接到数据库的参数,如服务器名称、服务器端口、数据库名称和凭证,都硬编码到文件中。
现在假设你需要将 WildFly 配置复制到其他地方,但实际工作将由你公司外的人来完成。你会给这个人所有这些信息吗?我想不会。
拥有一个属性文件可以在某种程度上保护你不泄露这样的信息,如下所示:
<subsystem >
<datasources>
<datasource jndi-name="java:jboss/MySqlDS" pool-name="MySqlDS">
<connection-url>${db.prod.conn.url}</connection-url>
<driver>mysql</driver>
<security>
<user-name>${db.prod.uid}</user-name>
<password>${db.prod.pwd}</password>
</security>
</datasource>
<drivers/>
</datasources>
</subsystem>
显然,需要找到前面的属性并将其匹配到属性文件中,内容如下:
db.prod.conn.url=jdbc:mysql://mysql-uat-cluster-node-1:3306/store
db.prod.uid=uat
dp.prod.pwd=uat-password
更好!
还有更多...
请记住,在 Java 中传递属性或属性文件时,属性文件具有优先级。因此,在指定属性文件后使用-D符号传递属性将优先于相同的属性。最后一个获胜!
保护配置哈希密码
在这个菜谱中,你将学习如何将密码作为配置文件进行掩码处理,这样它们就不会被看到,或者更好,对查看它们的人来说没有意义。
你可能听说过将应该保密或私密的文本(如密码)进行转换。很多时候,术语如编码、加密和哈希被不加区分地使用,但它们并不相同。在我们继续之前,让我们澄清这些概念。
编码文本是将它转换为可读的,并使其适用于不同的格式(如 HTML 中的&符号应转换为&)。
加密是将文本转换为使其保密和没有意义的过程。这种转换基于一个密钥和一个算法(如 AES、RSA 和 Blowfish),这样密钥和算法的组合使得文本与其原始内容完全不同。只有知道密钥和算法的客户才能将加密的文本恢复到原始值。
哈希是关于完整性的。这意味着它用于检查一个消息(文本、密码、文件等)在到达目的地期间是否被更改。哈希是不可逆的。给定一个输入值,你将始终得到相同的哈希输出。如果你对你的源进行微小的更改,你将得到一个完全不同的哈希输出。有几种哈希函数算法,如 MD5、SHA-3 等。
让我们尝试对一个小的文本片段进行 MD5 校验和,如下所示:
$ md5sum <<< abcdef
5ab557c937e38f15291c04b7e99544ad -
$ md5sum <<< abcdeF
7ab0aeb4ce14fd8efa00f3c903f72cf5 -
如你所见,只需将最后一个字符从f(小写)改为F(大写),MD5 校验和散列函数就会给出完全不同的输出。
顺便说一下,在本章中,我们将只使用术语散列,无论是通过编码、加密还是散列转换后的密码。
好的,我希望我已经给你提供了一些关于这类真正需要深入解释的话题的更多信息。除了你可以自己找到的大量技术书籍之外,还有一本相当有趣的书籍名为《密码之书》,作者是西蒙·辛格。
让我们回到 WildFly。
准备工作
要开始,让我们首先创建一个ad-hoc文件夹来运行我们的 WildFly。在终端窗口中执行以下命令:
$ cd $WILDFLY_HOME
$ cp -a standalone sec-std-cfg-node-2
现在是时候为我们的密码创建散列了。
如何操作…
-
打开一个新的终端窗口并运行以下命令:
$ cd $WILDFLY_HOME $ java -cp modules/system/layers/base/org/picketbox/main/picketbox-4.9.0.Beta2.jar org.picketbox.datasource.security.SecureIdentityLoginModule cookbook.2015 Encoded password: 2663ecfd3089b80f99cabb669aa2636e前述脚本的输出就是你的散列密码。
-
为了完成这个任务,我们依赖于安全域。创建一个如下所示的安全域:
<security-domain name="encrypted-security-domain" cache-type="default"> <authentication> <login-module code="org.picketbox.datasource.security.SecureIdentityLoginModule" flag="required"> <module-option name="username" value="root"/> <module-option name="password" value="2663ecfd3089b80f99cabb669aa2636e"/> </login-module> </authentication> </security-domain> -
现在,让我们回到我们的数据源定义,并按照以下方式引用安全域:
<subsystem > <datasources> <datasource jndi-name="java:jboss/MySqlDS" pool- name="MySqlDS"> <connection-url>jdbc:mysql://mysql-prod-cluster-node-1:3306/store</connection-url> <driver>mysql</driver> <security> <security-domain>encrypted-security-domain</security-domain> </security> </datasource> <drivers/> </datasources> </subsystem>
现在它工作了。真不错!
它是如何工作的…
你所要做的就是使用 WildFly 提供的 Pickbox 安全框架生成散列,并使用它的SecurityIdentityLoginModule。
之后,你需要在 WildFly 配置中创建一个安全域,该安全域使用与生成密码相同的程序(即SecurityIdentityLoginModule类)。
接下来,每次你需要提供一个散列密码时,只需引用该安全域即可完成。在这个过程中,安全域首先为接收到的密码生成一个散列,然后将生成的散列与你在配置中存储的散列进行匹配。其余的只是一个匹配/不匹配的问题。
要能在配置文件中使用那个密码散列,我们需要定义一个使用与生成散列完全相同功能的安全域,否则将很难区分明文密码和加密密码。
例如,在数据源定义中,你不能有以下的 XML 代码片段:
<subsystem >
<datasources>
<datasource jndi-name="java:jboss/MySqlDS" pool-name="MySqlDS">
<connection-url>jdbc:mysql://mysql-prod-cluster-node-1:3306/store</connection-url>
<driver>mysql</driver>
<security>
<user-name>root</user-name>
<password>2663ecfd3089b80f99cabb669aa2636e</password>
</security>
</datasource>
<drivers/>
</datasources>
</subsystem>
也就是说,使用密码散列代替的方法是不可行的!谁能判断出这个密码是明文还是散列的?
更多…
这种方法不仅适用于数据源,甚至还可以用于login-module本身、JMS 队列和主题。
使用保险库保护密码
在这个菜谱中,你将学习如何保护我们的密码,同时仍然将它们提供给我们的 WildFly 配置。保险库是一个存储密码的地方,使用密钥库进行加密。在我们的菜谱中,我们将创建一个密钥库并存储用于连接 MySQL 数据库的密码。MySQL 安装不在此书的范围之内;如果你需要更多信息,请参阅 MySQL 文档网站dev.mysql.com/doc/refman/5.5/en/installing.html。
准备工作
要开始,让我们首先创建一个ad-hoc文件夹来运行我们的 WildFly:
-
在终端窗口中运行以下命令:
$ cd $WILDFLY_HOME $ cp -a standalone sec-std-cfg-node-3现在是时候创建我们的密钥库了。
-
在同一个终端窗口中,执行以下操作:
$ cd $WILDFLY_HOME $ cd sec-std-cfg-node-3/configuration $ mkdir vault $ cd vault $ keytool -v -genkey -alias wildfly.vault -keyalg RSA -keysize 2048 -sigalg SHA1withRSA -keystore wildfly.vault.keystore Enter keystore password: [vault.2015] Re-enter new password: [vault.2015] What is your first and last name? [Unknown]: WildFly Cookbook What is the name of your organizational unit? [Unknown]: Packt Publishing What is the name of your organization? [Unknown]: packtpub.com What is the name of your City or Locality? [Unknown]: Birmingham What is the name of your State or Province? [Unknown]: GB What is the two-letter country code for this unit? [Unknown]: UK Is CN=WildFly Cookbook, OU=Packt Publishing, O=packtpub.com, L=Birmingham, ST=GB, C=UK correct? [no]: yes Generating 2,048 bit RSA key pair and self-signed certificate (SHA1withRSA) with a validity of 90 days for: CN=WildFly Cookbook, OU=Packt Publishing, O=packtpub.com, L=Birmingham, ST=GB, C=UK Enter key password for <wildfly.vault> (RETURN if same as keystore password): [Storing wildfly.vault.keystore] -
好的,现在我们已经创建了密钥库来加密我们的密码。让我们通过执行以下命令来检查其完整性:
$ keytool -list -v -keystore wildfly.vault.keystore Enter keystore password: Keystore type: JKS Keystore provider: SUN Your keystore contains 1 entry Alias name: wildfly.vault Creation date: Dec 6, 2014 Entry type: PrivateKeyEntry Certificate chain length: 1 Certificate[1]: Owner: CN=WildFly Cookbook, OU=Packt Publishing, O=packtpub.com, L=Birmingham, ST=GB, C=UK Issuer: CN=WildFly Cookbook, OU=Packt Publishing, O=packtpub.com, L=Birmingham, ST=GB, C=UK Serial number: 6cfc82e9 Valid from: Sat Dec 06 14:43:16 CET 2014 until: Fri Mar 06 14:43:16 CET 2015 Certificate fingerprints: MD5: FA:A6:5F:E6:7F:04:70:7E:70:FA:56:E7:9C:8A:B8:95 SHA1: D2:18:BE:44:58:B1:57:54:0A:69:F9:E2:DB:3F:A7:82:7D:DE:DB:6B SHA256: 8D:DF:16:64:3D:F6:08:08:71:8A:5F:4C:16:27:82:C8:20:75:FC:67:DD:9D:68:0E:0C:6F:08:7F:FA:E8:5D:18 Signature algorithm name: SHA1withRSA Version: 3 Extensions: #1: ObjectId: 2.5.29.14 Criticality=false SubjectKeyIdentifier [ KeyIdentifier [ 0000: 68 21 C0 CE C1 A5 A0 11 3D 5B EE E8 18 92 72 ED h!......=[....r. 0010: C4 28 46 2B .(F+ ] ] ******************************************* *******************************************
好的,一切正常!我们现在准备好加密我们的密码并将它们存储到保险库中。
如何做到这一点...
要生成一个保险库,WildFly 在bin文件夹内提供了一个脚本,名为vault.sh。
作为示例,我们将生成一个存储用于连接数据库的密码的保险库:
-
打开一个终端窗口并执行以下操作:
$ cd $WILDFLY_HOME/sec-std-cfg-node-3/configuration $ $WILDFLY_HOME/bin/vault.sh -a PASSWORD -x cookbook.2015 -b DB-PROD -i 50 -k vault/wildfly.vault.keystore -p vault.2015 -s 86427531 -v wildfly.vault ... Dec 06, 2014 10:28:20 PM org.picketbox.plugins.vault.PicketBoxSecurityVault init INFO: PBOX000361: Default Security Vault Implementation Initialized and Ready Secured attribute value has been stored in Vault. Please make note of the following: ******************************************** Vault Block:DB-PROD Attribute Name:PASSWORD Configuration should be done as follows: VAULT::DB-PROD::PASSWORD::1 ******************************************** Vault Configuration in WildFly configuration file: ******************************************** ... </extensions> <vault> <vault-option name="KEYSTORE_URL" value="vault/wildfly.vault.keystore"/> <vault-option name="KEYSTORE_PASSWORD" value="MASK-1cn1ENbx4KLXxve5VvGlPy"/> <vault-option name="KEYSTORE_ALIAS" value="wildfly.vault"/> <vault-option name="SALT" value="86427531"/> <vault-option name="ITERATION_COUNT" value="50"/> <vault-option name="ENC_FILE_DIR" value="vault/"/> </vault><management> ... ********************************************上述脚本是
vault.sh的输出,它描述了以下要点:-
如何引用保险库中存储的密码
-
在
vault文件夹内名为VAULT.dat的文件 -
一个要放置到
standalone.xml或host.xml文件中的 XML 代码片段
-
-
我们现在可以在数据源配置中使用保险库,如下所示:
<datasource jndi-name="java:jboss/datasources/VaultDS" pool-name="VaultDS" enabled="true" use-java-context="true"> <connection-url>jdbc:mysql://192.168.59.103:3306/test </connection-url> <driver>mysql</driver> <security> <user-name>root</user-name> <password>${VAULT::DB-PROD::PASSWORD::1}</password> </security> </datasource>为了测试我们的配置,我们需要一个正在运行的 MySQL 服务器。在我的情况下,它绑定到了运行在
192.168.59.103:3306的服务器上。此外,我们需要配置连接池,以便立即将有效的连接放入其中,作为证明。 -
因此,数据源配置如下:
<datasource jndi-name="java:jboss/datasources/VaultDS" pool-name="VaultDS" enabled="true"> <connection-url>jdbc:mysql://192.168.59.103:3306/test</connection- url> <driver>mysql</driver> <pool> <min-pool-size>5</min-pool-size> <max-pool-size>5</max-pool-size> <prefill>true</prefill> </pool> <security> <user-name>root</user-name> <password>${VAULT::DB-PROD::PASSWORD::1}</password> </security> </datasource> -
让我们启动我们的 WildFly 实例并查看日志。运行以下命令:
$ ./bin/standalone.sh -Djboss.server.base.dir=sec-std-cfg-node-3 ... 15:32:35,607 INFO [org.jboss.as.server.deployment.scanner] (MSC service thread 1-4) JBAS015012: Started FileSystemDeploymentService for directory /opt/wildfly/sec-std-cfg-node-2/deployments 15:32:35,636 INFO [org.jboss.as.connector.subsystems.datasources] (MSC service thread 1-1) JBAS010400: Bound data source [java:jboss/datasources/VaultDS] 15:32:35,636 INFO [org.jboss.as.connector.subsystems.datasources] (MSC service thread 1-13) JBAS010400: Bound data source [java:jboss/datasources/ExampleDS] 15:32:35,676 INFO [org.jboss.ws.common.management] (MSC service thread 1-12) JBWS022052: Starting JBoss Web Services - Stack CXF Server 4.2.4.Final 15:32:35,722 INFO [org.jboss.as] (Controller Boot Thread) JBAS015961: Http management interface listening on http://127.0.0.1:9990/management 15:32:35,723 INFO [org.jboss.as] (Controller Boot Thread) JBAS015951: Admin console listening on http://127.0.0.1:9990 15:32:35,723 INFO [org.jboss.as] (Controller Boot Thread) JBAS015874: WildFly 8.1.0.Final "Kenny" started in 3854ms - Started 203 of 251 services (81 services are lazy, passive or on-demand) -
在日志中,我已经强调了指出
VaultDS数据源的声明,该数据源已成功绑定。现在打开一个新的终端窗口并连接到 CLI 以检查我们的可用连接,如下所示:$ ./bin/jboss-cli.sh You are disconnected at the moment. Type 'connect' to connect to the server or 'help' for the list of supported commands. [disconnected /] connect [standalone@localhost:9990 /] /subsystem=datasources/data-source=VaultDS/statistics=pool:read-resource(include-runtime=true) { "outcome" => "success", "result" => { "ActiveCount" => "5", "AvailableCount" => "5", "AverageBlockingTime" => "0", "AverageCreationTime" => "36", "AverageGetTime" => "0", "BlockingFailureCount" => "0", "CreatedCount" => "5", "DestroyedCount" => "0", "IdleCount" => "5", "InUseCount" => "0", "MaxCreationTime" => "43", "MaxGetTime" => "0", "MaxUsedCount" => "1", "MaxWaitCount" => "0", "MaxWaitTime" => "0", "TimedOut" => "0", "TotalBlockingTime" => "0", "TotalCreationTime" => "180", "TotalGetTime" => "0", "WaitCount" => "0" } } [standalone@localhost:9990 /]太好了!从前面的输出中,我们可以得出结论,我们的
VaultDS数据源连接池已填充了五个可用和活动的连接。 -
现在为了证明我们做得很好,让我们尝试添加两个更多连接到同一数据库的数据源;一个使用明文密码,另一个使用错误的保险库定义。添加以下数据源定义:
<datasource jndi-name="java:jboss/datasources/UnsecureDS" pool-name="UnsecureDS" enabled="true"> <connection-url>jdbc:mysql://192.168.59.103:3306/test</connection-url> <driver>mysql</driver> <pool> <min-pool-size>2</min-pool-size> <max-pool-size>2</max-pool-size> <prefill>true</prefill> </pool> <security> <user-name>root</user-name> <password>cookbook.2015</password> </security> </datasource> <datasource jndi-name="java:jboss/datasources/WrongVaultDS" pool-name="WrongVaultDS" enabled="true"> <connection-url>jdbc:mysql://192.168.59.103:3306/test</connection-url> <driver>mysql</driver> <pool> <min-pool-size>1</min-pool-size> <max-pool-size>1</max-pool-size> <prefill>true</prefill> </pool> <security> <user-name>root</user-name> <password>${VAULT::DB-TEST::PASSWORD::1}</password> </security> </datasource> -
UnsecureDS数据源使用明文密码连接到数据库,而WrongVaultDS使用错误的vault-block,即我们未创建的DB-TEST。让我们再次启动 WildFly 并捕获错误日志。执行以下命令:$ ./bin/standalone.sh -Djboss.server.base.dir=sec-std-cfg-node-3 ... 15:52:08,452 ERROR [org.jboss.as.controller.management-operation] (ServerService Thread Pool -- 27) JBAS014612: Operation ("add") failed - address: ([ ("subsystem" => "datasources"), ("data-source" => "WrongVaultDS") ]): java.lang.SecurityException: JBAS013311: Security Exception at org.jboss.as.security.vault.RuntimeVaultReader.retrieveFromVault(RuntimeVaultReader.java:104) at org.jboss.as.server.RuntimeExpressionResolver.resolvePluggableExpression(RuntimeExpressionResolver.java:45) ... 15:52:08,499 INFO [org.wildfly.extension.undertow] (MSC service thread 1-2) JBAS017519: Undertow HTTP listener default listening on /127.0.0.1:8080 15:52:08,499 INFO [org.wildfly.extension.undertow] (MSC service thread 1-4) JBAS017519: Undertow AJP listener ajp listening on /127.0.0.1:8009 15:52:08,631 ERROR [org.jboss.as.controller.management-operation] (Controller Boot Thread) "JBAS014784: Failed executing subsystem datasources boot operations" 15:52:08,633 ERROR [org.jboss.as.controller.management-operation] (Controller Boot Thread) JBAS014613: Operation ("parallel-subsystem-boot") failed - address: ([]) - failure description: "\"JBAS014784: Failed executing subsystem datasources boot operations\"" 15:52:08,670 INFO [org.jboss.as.server.deployment.scanner] (MSC service thread 1-1) JBAS015012: Started FileSystemDeploymentService for directory /Users/foogaro/wildfly-8.1.0.Final/sec-std-cfg-node-2/deployments 15:52:08,705 INFO [org.jboss.ws.common.management] (MSC service thread 1-5) JBWS022052: Starting JBoss Web Services - Stack CXF Server 4.2.4.Final 15:52:08,706 ERROR [org.jboss.as.controller.management-operation] (Controller Boot Thread) JBAS014613: Operation ("add") failed - address: ([ ("subsystem" => "datasources"), ("data-source" => "WrongVaultDS") ]) - failure description: "JBAS014749: Operation handler failed: JBAS013311: Security Exception" 15:52:08,748 INFO [org.jboss.as] (Controller Boot Thread) JBAS015961: Http management interface listening on http://127.0.0.1:9990/management 15:52:08,748 INFO [org.jboss.as] (Controller Boot Thread) JBAS015951: Admin console listening on http://127.0.0.1:9990 15:52:08,749 INFO [org.jboss.as] (Controller Boot Thread) JBAS015874: WildFly 8.1.0.Final "Kenny" started in 3150ms - Started 183 of 231 services (81 services are lazy, passive or on-demand)如您所见,日志显示错误并抱怨关于
WrongVaultDS的SecurityException。因此,所有的数据源子系统都处于错误状态且未初始化。 -
尝试从数据源定义中移除
WrongVaultDS,再次启动 WildFly,你应该会找到以下日志条目:15:57:07,585 INFO [org.jboss.as.connector.subsystems.datasources] (MSC service thread 1-11) JBAS010400: Bound data source [java:jboss/datasources/VaultDS] 15:57:07,585 INFO [org.jboss.as.connector.subsystems.datasources] (MSC service thread 1-16) JBAS010400: Bound data source [java:jboss/datasources/UnsecureDS]
如您所见,VaultDS和UnsecureDS都正确地绑定了。
它是如何工作的…
keytool命令是由 Java SE 平台提供的工具,但理解其用法和参数超出了本书的范围。然而,在食谱的另请参阅...部分提供了一个由 Oracle 提供的官方文档链接。
让我们分析一下 WildFly 为您提供的这个食谱中使用的 vault 脚本。让我们首先通过在终端窗口中执行以下命令来查看前一个脚本提供了哪些选项:
$ ./bin/vault.sh --help
usage: vault.sh <empty> | [-a <arg>] [-b <arg>] -c | -h | -x <arg> [-e
<arg>] [-i <arg>] [-k <arg>] [-p <arg>] [-s <arg>] [-v <arg>]
-a,--attribute <arg> Attribute name
-b,--vault-block <arg> Vault block
-c,--check-sec-attr Check whether the secured attribute
already exists in the Vault
-e,--enc-dir <arg> Directory containing encrypted files
-h,--help Help
-i,--iteration <arg> Iteration count
-k,--keystore <arg> Keystore URL
-p,--keystore-password <arg> Keystore password
-s,--salt <arg> 8 character salt
-v,--alias <arg> Vault keystore alias
-x,--sec-attr <arg> Secured attribute value (such as
password)to store
现在检查我们是如何调用脚本的:
$ cd $WILDFLY_HOME/sec-std-cfg-node-3/configuration
$ $WILDFLY_HOME/bin/vault.sh -a PASSWORD -x cookbook.2015 -b DB-PROD -i 50 -k vault/wildfly.vault.keystore -p vault.2015 -s 86427531 -v wildfly.vault
因此,我们使用 -a(减号 a)标记创建了一个名为 PASSWORD 的属性,然后使用 -x(减号 x)标记将其值设置为 cookbook.2015。接着,我们使用 -b(减号 b)标记创建了一个 vault-block 来存储该属性及其值。其余的参数用于绑定加密属性值的密钥库,这就是我们的密码。
vault.sh 脚本为我们生成一个有用的输出,用于调用和引用 vault 和 vault-block,它们映射到我们的密码。使用它所需的信息,WildFly 能够自动提取密码并将其传递给请求它的组件。
还有更多...
只需提供不同的 vault-block,vault 就可以存储更多的密码。
例如,在这个食谱的 如何做… 部分的末尾,我们尝试使用不同的 vault-block 绑定一个数据源(WrongVaultDS);我们称之为 DB-TEST。结果,我们无法绑定该数据源,因此整个数据源子系统处于错误状态。现在让我们尝试添加相同的数据库定义,但这次,我们还将 DB-TEST vault-block 提供给我们的 vault 文件:
-
打开一个终端窗口,并执行以下命令:
$ cd $WILDFLY_HOME/sec-std-cfg-node-2/configuration $ $WILDFLY_HOME/bin/vault.sh -a PASSWORD -x cookbook.2015 -b DB-TEST -i 50 -k vault/wildfly.vault.keystore -p vault.2015 -s 86427531 -v wildfly.vault … Dec 08, 2014 4:20:17 PM org.picketbox.plugins.vault.PicketBoxSecurityVault init INFO: PBOX000361: Default Security Vault Implementation Initialized and Ready Secured attribute value has been stored in Vault. Please make note of the following: ******************************************** Vault Block:DB-TEST Attribute Name:PASSWORD Configuration should be done as follows: VAULT::DB-TEST::PASSWORD::1 ******************************************** Vault Configuration in WildFly configuration file: ******************************************** ... </extensions> <vault> <vault-option name="KEYSTORE_URL" value="vault/wildfly.vault.keystore"/> <vault-option name="KEYSTORE_PASSWORD" value="MASK-1cn1ENbx4KLXxve5VvGlPy"/> <vault-option name="KEYSTORE_ALIAS" value="wildfly.vault"/> <vault-option name="SALT" value="86427531"/> <vault-option name="ITERATION_COUNT" value="50"/> <vault-option name="ENC_FILE_DIR" value="vault/"/> </vault><management> ... ******************************************** -
现在编辑
standalone.xml文件并添加以下 XML 代码片段:<datasource jndi-name="java:jboss/datasources/WrongVaultDS" pool-name="WrongVaultDS" enabled="true"> <connection-url>jdbc:mysql://192.168.59.103:3306/test</connection-url> <driver>mysql</driver> <pool> <min-pool-size>1</min-pool-size> <max-pool-size>1</max-pool-size> <prefill>true</prefill> </pool> <security> <user-name>root</user-name> <password>${VAULT::DB-TEST::PASSWORD::1}</password> </security> </datasource> -
现在,像往常一样启动 WildFly 并查看日志。运行以下命令:
$ ./bin/standalone.sh -Djboss.server.base.dir=sec-std-cfg-node-3 ... 16:33:02,063 INFO [org.jboss.as.connector.subsystems.datasources] (MSC service thread 1-3) JBAS010400: Bound data source [java:jboss/datasources/WrongVaultDS] 16:33:02,063 INFO [org.jboss.as.connector.subsystems.datasources] (MSC service thread 1-6) JBAS010400: Bound data source [java:jboss/datasources/ExampleDS] 16:33:02,063 INFO [org.jboss.as.connector.subsystems.datasources] (MSC service thread 1-2) JBAS010400: Bound data source [java:jboss/datasources/VaultDS] 16:33:02,063 INFO [org.jboss.as.connector.subsystems.datasources] (MSC service thread 1-9) JBAS010400: Bound data source [java:jboss/datasources/UnsecureDS] 16:33:02,064 INFO [org.jboss.ws.common.management] (MSC service thread 1-5) JBWS022052: Starting JBoss Web Services - Stack CXF Server 4.2.4.Final 16:33:02,118 INFO [org.jboss.as] (Controller Boot Thread) JBAS015961: Http management interface listening on http://127.0.0.1:9990/management 16:33:02,119 INFO [org.jboss.as] (Controller Boot Thread) JBAS015951: Admin console listening on http://127.0.0.1:9990 16:33:02,120 INFO [org.jboss.as] (Controller Boot Thread) JBAS015874: WildFly 8.1.0.Final "Kenny" started in 1992ms - Started 203 of 251 services (81 services are lazy, passive or on-demand)
成功了!正如你所看到的,只要声明不同的 vault-block,你就可以在 vault 中定义和存储所有你想要的密码。
参见
- 要深入了解 keytool 命令,请参阅 Oracle 官方文档,链接为
docs.oracle.com/javase/8/docs/technotes/tools/unix/keytool.html
第十二章. 使用 WildFly 进行基于角色的访问控制
在本章中,你将学习以下菜谱:
-
在简单和 RBAC 提供者之间切换
-
管理用户、组和它们的角色映射
-
将预定义的角色设置给所有已认证用户
-
通过服务器组或主机范围授予用户访问权限 - 范围角色
-
与 OpenLDAP 集成
简介
在本章中,你将学习如何通过提供细粒度的访问控制机制来改进 WildFly 管理控制台。WildFly 附带两个内置提供者:一个是称为 simple,它是默认的;另一个称为 RBAC——正如你所猜到的,它代表基于角色的访问控制。
切换到 RBAC 提供者意味着你可以控制用户可以做什么和看到什么,以及用户被允许执行的操作类型。使用 RBAC 提供者,你基本上是在与不同用户分担责任。你可能希望一些用户只进行部署,其他人偶尔监控整个系统,等等。此外,我们将看到如何将 RBAC 与一个提供用户和组的 LDAP 系统(WildFly 角色)集成。
WildFly 附带一组预定义的角色,每个角色都有特定的权限。这些角色如下:
-
监控员:正如其名可能暗示的,这个角色只能查看服务器状态,但不能查看敏感数据,如密码。
-
操作员:这个角色具有与监控器提供的相同权限,还可以停止和启动服务器,或者暂停运行时服务,如 JMS 目的地。通常,你会将这样的角色授予负责管理特定服务器实例的人员。然而,操作员无法查看敏感数据,如密码。
-
维护者:这个角色有权查看和编辑所有运行时状态和所有配置,除了敏感数据。具有这种角色的用户可以管理几乎整个系统。
-
部署者:这个角色类似于监控器角色,还可以读取和修改部署的配置和状态。
-
审计员:这个角色类似于监控器角色,还可以查看敏感数据。它还可以访问审计日志子系统。
-
管理员:这个角色可以做任何事情,查看和修改一切,除了审计日志子系统。管理员还可以配置 RBAC 提供者。
-
超级用户:这是最强大的角色。它可以查看和修改资源、状态和敏感数据。这个角色是在引入 RBAC 之前使用的。
这些角色可以组合在一起形成一个角色组,该角色组可以被特定用户使用。
在简单和 RBAC 提供者之间切换
在这个菜谱中,我们将学习如何从简单提供者切换到 RBAC 提供者。RBAC 为我们提供了细粒度的访问控制。
准备工作
要开始,让我们首先创建一个ad-hoc文件夹来运行我们的 WildFly。在终端窗口中,执行以下命令:
$ cd $WILDFLY_HOME
$ cp -a standalone rbac-std-node-1
现在是时候运行我们的 WildFly 了!!!
如何操作…
执行以下步骤:
-
打开一个终端窗口并输入以下命令:
$ cd $WILDFLY_HOME $ ./bin/standalone.sh -Djboss.server.base.dir=rbac-std-node-1 -
一旦启动,在新终端窗口中,连接到 CLI 并切换到 RBAC 提供者,如下所示:
$ cd $WILDFLY_HOME $ ./bin/jboss-cli.sh You are disconnected at the moment. Type 'connect' to connect to the server or 'help' for the list of supported commands. [disconnected /] connect [standalone@localhost:9990 /] /core-service=management/access=authorization:write-attribute(name=provider,value=rbac) { "outcome" => "success", "response-headers" => { "operation-requires-reload" => true, "process-state" => "reload-required" } } [standalone@localhost:9990 /] :reload() { "outcome" => "success", "result" => undefined } [standalone@localhost:9990 /]太好了,我们成功切换到了 RBAC 提供者。
-
在我们的
standalone.xml文件中,我们应该找到以下定义:<access-control provider="rbac"> <role-mapping> <role name="SuperUser"> <include> <user name="$local"/> </include> </role> </role-mapping> </access-control> -
要切换回
simple提供者,请按以下操作:standalone@localhost:9990 /] /core-service=management/access=authorization:write-attribute(name=provider,value=simple) { "outcome" => "success", "response-headers" => { "operation-requires-reload" => true, "process-state" => "reload-required" } } [standalone@localhost:9990 /] :reload() { "outcome" => "success", "result" => undefined } [standalone@localhost:9990 /] -
在我们的
standalone.xml文件中,我们应该找到以下定义:<access-control provider="simple"> <role-mapping> <role name="SuperUser"> <include> <user name="$local"/> </include> </role> </role-mapping> </access-control>
就这样,到目前为止相当简单!!!
它是如何工作的…
这种方法相当简单直接;除了它的用法之外,没有太多可说的。前面的例子只是第一步。实际上,如果您在切换到RBAC提供者后尝试访问控制台,您将无法登录。
尝试一下!!!打开浏览器,将其指向http://localhost:9990。输入我们在本书开头指定的用户名和密码:用户名为wildfly,密码为cookbook.2015。
看吧,您无法登录。如何解决这个问题?
从前面的 XML 代码片段中,您应该已经注意到了指向名为$local用户的include指令。在这种情况下,$local意味着在ManagementRealm和ApplicationRealm领域中定义的用户,但没有任何权限访问控制台。
我们应该做的是,在include XML 元素中添加我们的wildfly用户从ManagementRealm,如下所示:
[standalone@localhost:9990 /] /core-service=management/access=authorization/role-mapping=SuperUser/include=wildfly:add(name=wildfly,realm=ManagementRealm,type=USER)
{"outcome" => "success"}
[standalone@localhost:9990 /]
之前的命令将以下 XML 代码生成到我们的standalone.xml文件中:
<access-control provider="rbac">
<role-mapping>
<role name="SuperUser">
<include>
<user name="$local"/>
<user alias="wildfly" realm="ManagementRealm" name="wildfly"/>
</include>
</role>
</role-mapping>
</access-control>
现在如果我们像往常一样尝试访问控制台,我们应该能够成功登录。
还有更多…
无论您选择了哪种提供者,一旦登录,您都可以通过点击显示在 Web 控制台右上角的用户名来查看您用户的角色:

显示当前登录用户的角色
如您所见,我们有一个名为SuperUser的角色。再深入一点,有一个有趣的链接以…运行,这可能会引起您的兴趣。让我们点击它!

以角色选择角色弹出窗口运行
尝试玩转它,以更好地理解不同的角色和您获得的权限。例如,尝试使用Monitor角色部署一个应用程序,或者检查您是否可以查看或编辑数据源的用户名和密码。保持好奇心!
管理用户、组和它们的角色映射
在这个菜谱中,您将学习如何管理用户、组和它们与 WildFly 角色的相对映射。我们将从 Web 控制台内部执行大多数任务。
准备工作
要开始,让我们首先创建一个ad-hoc文件夹来运行我们的 WildFly。在终端窗口中,执行以下命令:
$ cd $WILDFLY_HOME
$ cp -a standalone rbac-std-node-2
现在是时候运行我们的 WildFly 了。
如何操作…
-
打开一个终端窗口并输入以下命令:
$ cd $WILDFLY_HOME $ ./bin/standalone.sh -Djboss.server.base.dir=rbac-std-node-2 -
一旦启动,连接到 CLI 并在新终端窗口中切换到 RBAC 提供者,就像我们之前菜谱中所做的那样:
$ cd $WILDFLY_HOME $ ./bin/jboss-cli.sh You are disconnected at the moment. Type 'connect' to connect to the server or 'help' for the list of supported commands. [disconnected /] connect [standalone@localhost:9990 /] /core-service=management/access=authorization:write-attribute(name=provider,value=rbac) { "outcome" => "success", "response-headers" => { "operation-requires-reload" => true, "process-state" => "reload-required" } } [standalone@localhost:9990 /] :reload() { "outcome" => "success", "result" => undefined } [standalone@localhost:9990 /] -
现在我们已经成功切换到 RBAC 提供者,我们需要将我们的本地管理用户(如果您一直在跟随这本书,您会知道它是wildfly)映射到超级用户角色,如下所示:
[standalone@localhost:9990 /] /core-service=management/access=authorization/role-mapping=SuperUser/include=wildfly:add(name=wildfly,realm=ManagementRealm,type=USER) {"outcome" => "success"} [standalone@localhost:9990 /]
好的,我们现在可以访问 Web 控制台,并查看我们如何管理用户、组和它们的相关映射。
用户
让我们先添加一些用户:
-
在一个新的终端窗口中,执行以下命令:
$ cd $WILDFLY_HOME $ ./bin/add-user.sh --silent=true devguy devguy.2015 -sc rbac-std-node-2/configuration $ ./bin/add-user.sh --silent=true opsguy opsguy.2015 -sc rbac-std-node-2/configuration $ ./bin/add-user.sh --silent=true devopsguy devopsguy.2015 -sc rbac-std-node-2/configuration $ ./bin/add-user.sh --silent=true pmguy pmguy.2015 -sc rbac-std-node-2/configuration -
好的,现在让我们打开浏览器并将它指向
http://localhost:9990。 -
以wildfly用户登录后,点击管理选项卡,您应该会看到一个类似于以下页面的页面:
![用户]()
显示 RBAC 用户
-
我们的wildfly用户在那里。让我们通过点击标记为添加的按钮,并按照以下方式填写表单来添加用户
devguy:![用户]()
添加用户及其角色的 RBAC 表单
-
通过点击保存按钮进行确认。现在我们应该已经将新用户添加到列表中,如下一张图片所示:
![用户]()
显示新添加的用户 devguy
-
如果由于任何原因我们无法访问 Web 控制台,我们可以依赖 CLI,并指定以下命令:
[standalone@localhost:9990 /] /core-service=management/access=authorization/role-mapping=Deployer:add {"outcome" => "success"} [standalone@localhost:9990 /] /core-service=management/access=authorization/role-mapping=Deployer/include=devguy:add(name=devguy,type=USER,realm=ManagementRealm) {"outcome" => "success"} [standalone@localhost:9990 /] /core-service=management/access=authorization/role-mapping=Operator:add {"outcome" => "success"} [standalone@localhost:9990 /] /core-service=management/access=authorization/role-mapping=Operator/include=devguy:add(name=devguy,type=USER,realm=ManagementRealm) {"outcome" => "success"} [standalone@localhost:9990 /] -
让我们添加其他用户,
opsguy、devopsguy和pmguy,并赋予他们适当的角色:-
opsguy:维护者和操作者的角色 -
devopsguy:管理者的角色 -
pmguy:监控者的角色
-
-
您应该有以下列表:
![用户]()
用户列表
现在我们已经输入了所有用户,让我们尝试其中一个。
-
因此,注销并使用用户
pmguy重新登录。一旦我们回来,让我们转到部署选项卡。您会看到一个类似于以下页面的页面:![用户]()
没有任何按钮可以更改部署的部署页面
如您所见,我们无法管理我们的部署,无法删除或禁用它们,也无法部署新的应用程序。
-
让我们回到我们的管理选项卡,看看我们是否可以添加一个更强大的角色:
![用户]()
管理页面关于授权所需的警告
我们不能;只有具有管理员和超级用户角色的用户才能。
组
正如我们所见,每个角色都有特定的权限,关于你可以做什么和不可以做什么,以及你可以看到什么和不可以看到什么。尽管用户角色映射很简单,但你可以在特定组下管理和组合多个角色。
让我们创建两个组:development和operations:
-
如果你仍然以
pmguy用户登录,请注销并使用wildfly用户重新登录。 -
到达那里后,转到管理选项卡并点击组:
![组]()
显示 RBAC 组
-
通过点击添加按钮添加两个组,如下所示:
![组]()
创建开发组
-
在前面的屏幕截图中,我们正在从 WildFly 内置角色列表中选择部署者和操作员角色,并将它们赋予开发组。
![组]()
创建操作组
-
在前面的屏幕截图中,我们正在从 WildFly 内置角色列表中选择维护者和操作员角色,并将它们分配给操作组。
-
现在,我们的
group-roles表中应该有以下条目:![组]()
组列表
-
我们的
standalone.xml文件应该有如下定义:<access-control provider="rbac"> <role-mapping> <role name="SuperUser"> <include> <user name="$local"/> <user alias="wildfly" realm="ManagementRealm" name="wildfly"/> </include> </role> <role name="Deployer"> <include> <user realm="ManagementRealm" name="devguy"/> <group name="Development"/> </include> </role> <role name="Operator"> <include> <user realm="ManagementRealm" name="devguy"/> <user realm="ManagementRealm" name="opsguy"/> <group name="Development"/> <group name="Operations"/> </include> </role> <role name="Maintainer"> <include> <user realm="ManagementRealm" name="opsguy"/> <group name="Operations"/> </include> </role> <role name="Administrator"> <include> <user realm="ManagementRealm" name="devopsguy"/> </include> </role> <role name="Monitor"> <include> <user realm="ManagementRealm" name="pmguy"/> </include> </role> </role-mapping> </access-control>
如您所见,组也已包含在适当的角色中。现在我们需要给一个用户分配一个组。根据我们的配置,我们可以将开发组分配给devguy用户,将操作组分配给opsguy。
由于我们正在处理属于管理域的用户以从命令行完成此任务,我们可以遵循两种不同的方法:
-
使用
add-user.sh脚本再次插入用户,并传递组,如下所示:$ ./bin/add-user.sh --silent=true devguy devguy.2015 -g Development -sc rbac-std-node-2/configuration -
直接将用户添加到
mgmt-groups.properties文件中,如下所示:$ echo "opsguy=Operations" >> rbac-std-node-2/configuration/mgmt-groups.properties注意
由于我们可能潜在地有数十、数百甚至数千名用户,我们可能不想为每个用户分配一个角色,而是将一组角色映射到一个组中,并将用户映射到该组。这种场景在拥有其用户存储在
Active Directory或 LDAP 存储的企业中很常见——我们将在本章后面深入探讨集成 LDAP 存储。
话虽如此,让我们移除用户devguy和opsguy的角色映射。
-
我们可以通过从 Web 控制台选择用户并点击移除按钮来完成此操作。
-
完成后,我们应该有以下场景:
![组]()
用户列表
-
现在,我们应该能够使用用户
devguy注销并重新登录。 -
完成后,让我们点击页面右上角的
devguy链接,我们应该看到他的角色,如下所示:![组]()
显示用户的角色
让我们用另一个用户opsguy试一试。如果一切顺利,我们应该也能看到他的角色。
它是如何工作的...
用户和组管理通过 Web 控制台简化;尽管如此,我们仍然需要在 Web 控制台之外完成一些任务,例如将用户映射到组。顺便说一句,这里的主要过程是:
-
我们需要一个能够访问 RBAC 管理页面并添加/删除用户和组的用户——就像我们的 WildFly 超级用户一样。
-
我们需要添加用户或组并将它们映射到适当的角色
-
用户必须存在于存储中,如属性文件和 LDAP(WildFly 支持这两个存储);我们使用
add-user.sh脚本来静默添加用户,包括他们的域。
为所有已认证用户设置预定义的角色
在这个菜谱中,你将学习如何将预定义的角色分配给所有已认证的用户。当你的用户基数很大,且无法完全查看身份存储中所有用户和组时,这个设置可能很有用。因此,与其不授权某些用户,你可能会想给他们最简单和最少的权限。
准备工作
要开始,让我们首先创建一个ad-hoc文件夹来运行我们的 WildFly。在终端窗口中执行以下命令:
$ cd $WILDFLY_HOME
$ cp -a standalone rbac-std-node-3
现在是时候运行我们的 WildFly 了!
如何操作…
-
打开一个终端窗口并输入以下命令:
$ cd $WILDFLY_HOME $ ./bin/standalone.sh -Djboss.server.base.dir=rbac-std-node-3 -
一旦启动,在新终端窗口中连接到 CLI 并切换到 RBAC 提供者,就像我们之前的方法一样:
$ cd $WILDFLY_HOME $ ./bin/jboss-cli.sh You are disconnected at the moment. Type 'connect' to connect to the server or 'help' for the list of supported commands. [disconnected /] connect [standalone@localhost:9990 /] /core-service=management/access=authorization:write-attribute(name=provider,value=rbac) { "outcome" => "success", "response-headers" => { "operation-requires-reload" => true, "process-state" => "reload-required" } } [standalone@localhost:9990 /] :reload() { "outcome" => "success", "result" => undefined } [standalone@localhost:9990 /] -
现在我们已经成功切换到 RBAC 提供者,我们需要将我们的本地管理用户(如果您正在遵循本书,本地管理用户是
wildfly)映射到SuperUser角色,如下所示:[standalone@localhost:9990 /] /core-service=management/access=authorization/role-mapping=SuperUser/include=wildfly:add(name=wildfly,realm=ManagementRealm,type=USER) {"outcome" => "success"} [standalone@localhost:9990 /] -
现在我们需要添加一些用户,只是为了模拟我们没有考虑为系统提供访问权限的用户;在新的终端窗口中,执行以下命令:
$ cd $WILDFLY_HOME $ ./bin/add-user.sh --silent=true luigi luigi.2015 -sc rbac-std-node-3/configuration $ ./bin/add-user.sh --silent=true fabio fabio.2015 -sc rbac-std-node-3/configuration $ ./bin/add-user.sh --silent=true stefano stefano.2015 -sc rbac-std-node-3/configuration好的,我们现在可以访问 Web 控制台并查看我们如何通过给他们默认角色来管理这类用户。
-
让我们打开浏览器并将它指向
http://localhost:9990。 -
使用
wildfly用户进行认证,一旦登录,点击管理标签,然后点击角色部分。 -
然后,选择Monitor角色,点击编辑链接,并标记包含所有复选框,如下所示:
![如何操作…]()
为所有已认证用户分配默认角色
-
一旦您点击保存,所选角色将被分配给所有已认证用户的默认角色。
![如何操作…]()
角色列表
-
如果我们没有访问 Web 控制台,我们仍然可以依赖我们的 CLI 并发出以下命令:
[standalone@localhost:9990 /] /core-service=management/access=authorization/role-mapping=Monitor:write-attribute(name=include-all,value=true) {"outcome" => "success"} [standalone@localhost:9990 /] -
好的,让我们通过注销并使用用户
luigi重新登录来尝试我们的设置。打开浏览器并将它指向http://localhost:9990。 -
将
luigi作为用户名,将luigi.2015作为密码;我们应该得到以下场景:![如何操作…]()
用户 luigi 映射到默认角色 Monitor
很棒!
它是如何工作的…
由于过程相当直接,没有太多可说的。让我们回顾一下我们做了什么以及我们是如何做的。
首先,我们需要切换提供者,使用 CLI 将simple切换到rbac。
其次,我们需要将我们的管理用户wildfly添加到SuperUser角色中,这导致了以下配置:
<access-control provider="rbac">
<role-mapping>
<role name="SuperUser">
<include>
<user name="$local"/>
<user alias="wildfly" realm="ManagementRealm" name="wildfly"/>
</include>
</role>
</role-mapping>
</access-control>
然后,我们使用add-user.sh脚本将潜在的非管理用户添加到ManagementRealm域中:
$ cd $WILDFLY_HOME
$ ./bin/add-user.sh --silent=true luigi luigi.2015 -sc rbac-std-node-3/configuration
$ ./bin/add-user.sh --silent=true fabio fabio.2015 -sc rbac-std-node-3/configuration
$ ./bin/add-user.sh --silent=true stefano stefano.2015 -sc rbac-std-node-3/configuration
--silent=true命令基本上意味着非交互模式;然后我们传递用户名和密码,然后我们指定存储用户的路径。默认情况下,存储由mgmt-users.properties文件表示,对应于 WildFly 侧的ManagementRealm。
如果我们想要将用户存储在ApplicationRealm中,我们应该在脚本中传递了选项-a。此外,如果我们想要将用户的凭据存储到不同的文件中,我们可以传递选项-up /path/filename.properties。
要获取所有可用选项的完整列表,请运行add-user.sh脚本并传递--help指令,如下所示:
$ ./bin/add-user.sh –help

最后,我们将监控角色设置为所有已认证用户的默认角色。
通过服务器组或主机授予用户访问权限 - 作用域角色
在这个食谱中,您将学习如何仅授予用户在特定服务器组或主机上的访问权限。显然,此功能仅在域模式下可用。这个特性被称为作用域角色,当您需要与多个团队(如开发团队)共享环境时非常有用。这样,每个团队实际上都可以在其服务器组或专用主机上工作,而不会干扰其他服务器组或主机。
准备工作
要开始,让我们首先创建一个ad-hoc文件夹来运行我们的 WildFly。在终端窗口中输入以下命令:
$ cd $WILDFLY_HOME
$ cp -a domain rbac-dmn-scp-roles
现在是时候运行我们的 WildFly 了。
如何操作…
-
打开一个终端窗口并执行以下命令:
$ cd $WILDFLY_HOME $ ./bin/domain.sh -Djboss.domain.base.dir=rbac-dmn-scp-roles -
一旦开始,在一个新的终端窗口中连接到 CLI,并切换到 RBAC 提供者,就像我们在前面的食谱中所做的那样:
$ cd $WILDFLY_HOME $ ./bin/jboss-cli.sh You are disconnected at the moment. Type 'connect' to connect to the server or 'help' for the list of supported commands. [disconnected /] connect [domain@localhost:9990 /] /core-service=management/access=authorization:write-attribute(name=provider,value=rbac) { "outcome" => "success", "response-headers" => { "operation-requires-reload" => true, "process-state" => "reload-required" }, "result" => undefined, "server-groups" => {"main-server-group" => {"host" => {"master" => { "server-one" => {"response" => { "outcome" => "success", "response-headers" => { "operation-requires-reload" => true, "process-state" => "reload-required" } }}, "server-two" => {"response" => { "outcome" => "success", "response-headers" => { "operation-requires-reload" => true, "process-state" => "reload-required" } }} }}}} } [domain@localhost:9990 /] reload --host=master [domain@localhost:9990 /] -
现在我们已经成功切换到 RBAC 提供者,我们需要将我们的
wildfly用户映射到超级用户角色,如下所示:[domain@localhost:9990 /] /core-service=management/access=authorization/role-mapping=SuperUser/include=wildfly:add(name=wildfly,realm=ManagementRealm,type=USER) { "outcome" => "success", "result" => undefined, "server-groups" => {"main-server-group" => {"host" => {"master" => { "server-one" => {"response" => {"outcome" => "success"}}, "server-two" => {"response" => {"outcome" => "success"}} }}}} } [domain@localhost:9990 /]现在,我们可以登录到我们的 WildFly 控制台并创建所谓的
scoped-roles。 -
登录到 WildFly Web 控制台并转到管理选项卡。然后选择角色子选项卡,并选择作用域角色部分。页面如下所示:
![如何操作…]()
作用域角色的管理页面
-
现在,点击添加按钮并填写以下截图所示的信息:
![如何操作…]()
作用域角色添加表单
-
一旦您通过点击保存按钮提交表单,作用域角色将被插入并按照以下列表显示:
![如何操作…]()
显示 dev-team-one 作用域角色的列表
现在,是时候创建一个用户并将其映射到之前步骤中创建的作用域角色了。
-
打开一个终端并执行以下命令:
$ cd $WILDFLY_HOME $ ./bin/add-user.sh --silent=true user-dev-1 devguy.2015 -sc rbac-dmn-scp-roles/configuration现在,我们可以将该用户添加到
dev-team-one作用域角色中。 -
让我们回到 WildFly Web 控制台,选择管理选项卡,然后选择用户子选项卡,并点击添加按钮。填写以下图像所示的信息:
![如何操作…]()
-
保存您的更改后,您应该有如下列表:
![如何操作…]()
-
现在,我们可以使用
user-dev-1用户和密码devguy.2015注销并重新登录。现在,如果您转到域选项卡,在概览部分(左侧的菜单项),您将只看到您所属的服务器组的拓扑结构,如下截图所示:
![如何操作…]()
就这样!
它是如何工作的…
关于这个过程没有太多可说的,因为它相当直接。让我们回顾一下我们做了什么以及我们是如何做的。
首先,我们需要切换提供者,从simple切换到rbac提供者,使用 CLI。
其次,我们需要将我们的管理用户wildfly添加到SuperUser角色,这导致了以下配置:
<access-control provider="rbac">
<role-mapping>
<role name="SuperUser">
<include>
<user name="$local"/>
<user alias="wildfly" realm="ManagementRealm" name="wildfly"/>
</include>
</role>
</role-mapping>
</access-control>
然后,我们以用户wildfly的身份登录到 WildFly Web 控制台,并添加了我们的第一个范围角色。我们将范围角色映射到名为main-server-group的服务器组,其基本角色为Deployer。
我们还可以通过在类型选择组件中指定Host,将我们的范围角色映射到主机,如下所示:

如您所见,角色的范围将与我们的配置中可用的主机名相匹配,即在我们的情况下是master。
然后,我们使用add-user.sh脚本添加了一个用户:
$ cd $WILDFLY_HOME
$ ./bin/add-user.sh --silent=true user-dev-1 devguy.2015 -sc rbac-dmn-scp-roles/configuration
--silent=true命令基本上意味着非交互模式;然后我们传递用户名和密码,然后指定存储用户的路径。默认情况下,存储由mgmt-users.properties文件表示,对应于 WildFly 侧的ManagementRealm。
最后,我们将user-dev-1映射到范围角色,并使用它登录。
因此,WildFly Web 控制台仅对指定在范围角色中的服务器组可用,相应地,它被分配了Deployer角色。
与 OpenLDAP 集成
在这个配方中,你将学习如何集成 OpenLDAP 为我们的 RBAC 提供者提供一个用户基础。在企业环境中,你通常会发现公司存储其用户的数据库。这些数据库通常具有某种结构,为每个用户定义角色、配置文件和/或组。
例如,连接到服务器的用户可能看到或不看到某些文件或目录。访问应用程序的用户可能找到或不找到特定的功能;这可能就是 WildFly Web 控制台的情况。LDAP 是一种由许多数据库使用的协议,用于存储这些用户的权限。有许多这样的数据库,例如 Microsoft Active Directory,但为了保持我们与开源世界的同步,我们将查看和使用 OpenLDAP 工具。
准备工作
对于这个配方,你将使用一个现成的 OpenLDAP 服务器,因为 OpenLDAP 服务器的安装超出了本书的范围。
尽管如此,我们仍将使用以下 LDIF 文件中定义的 LDAP 结构:
顺便说一下,如果您想深入了解 OpenLDAP,您可以依靠优秀的书籍《Mastering OpenLDAP: Configuring, Securing and Integrating Directory Services》,作者:Matt Butcher,出版社:Packt Publishing。
要开始,我们首先创建一个ad-hoc文件夹来运行我们的 WildFly。在终端窗口中输入以下命令:
$ cd $WILDFLY_HOME
$ cp -a standalone rbac-std-ldap
现在是时候运行我们的 WildFly 了。
如何做到这一点…
-
打开一个终端窗口并执行以下命令:
$ cd $WILDFLY_HOME $ ./bin/standalone.sh -Djboss.server.base.dir=rbac-std-ldap -
一旦启动,在一个新的终端窗口中,连接到 CLI 并切换到 RBAC 提供者,就像我们在之前的菜谱中所做的那样:
$ cd $WILDFLY_HOME $ ./bin/jboss-cli.sh You are disconnected at the moment. Type 'connect' to connect to the server or 'help' for the list of supported commands. [disconnected /] connect [standalone@localhost:9990 /] /core-service=management/access=authorization:write-attribute(name=provider,value=rbac) { "outcome" => "success", "response-headers" => { "operation-requires-reload" => true, "process-state" => "reload-required" } } [standalone@localhost:9990 /] :reload() { "outcome" => "success", "result" => undefined } [standalone@localhost:9990 /] -
现在我们已经成功切换到 RBAC 提供者,我们需要将我们的本地管理用户(如果你在遵循这本书,你会知道它是
wildfly)映射到超级用户角色,如下所示:[standalone@localhost:9990 /] /core-service=management/access=authorization/role-mapping=SuperUser/include=wildfly:add(name=wildfly,realm=ManagementRealm,type=USER) {"outcome" => "success"} [standalone@localhost:9990 /] -
现在我们已经完全切换到 RBAC 提供者,我们需要配置 LDAP 连接及其分组过滤器。停止 WildFly,打开
standalone.xml文件,然后添加以下security-realm:<security-realm name="LDAPRealm"> <authentication> <ldap connection="ldap" base-dn="ou=Users,dc=example,dc=com"> <username-filter attribute="uid"/> </ldap> </authentication> <authorization> <ldap connection="ldap"> <group-search group-name="SIMPLE" iterative="true" group-dn-attribute="dn" group-name-attribute="cn"> <group-to-principal base- dn="ou=Groups,dc=example,dc=com" recursive="true" search-by="DISTINGUISHED_NAME"> <membership-filter principal-attribute="uniqueMember" /> </group-to-principal> </group-search> </ldap> </authorization> </security-realm> -
现在,就在
</security-realms>XML 关闭标签之后,添加以下内容:<outbound-connections> <ldap name="ldap" url="ldap://localhost:389" search-dn="cn=Manager,dc=example,dc=com" search-credential="ldap.2015"/> </outbound-connections>
将区分名称(dn属性)映射到适合您当前 LDAP 系统的任何内容。对于表示admin用户密码的search-credential属性(在dn属性的开头指定):cn=Manager。
还要注意,在生产环境中,你会使用 SSL 上的 LDAP 协议,以安全的方式传输凭据。因此,URL 将是ldaps://secureldap.intranet:636。
保持standalone.xml文件打开,并应用以下更改:
-
找到以下定义:
<management-interfaces> <http-interface security-realm="ManagementRealm" http-upgrade-enabled="true"> <socket-binding http="management-http"/> </http-interface> </management-interfaces> -
用以下内容替换它:
<management-interfaces> <http-interface security-realm="LDAPRealm" http- upgrade-enabled="true"> <socket-binding http="management-http"/> </http-interface> </management-interfaces>
要将 LDAP 与 RBAC 集成到我们的管理控制台,我们只需做这些。
我已经使用rbac.ldif文件填充了 OpenLDAP 服务器,并将user1引用到了 OpenLDAP 本身的自定义组中。以下是一个此类配置的提取:
# Development Group:
dn: cn=Developers,ou=Groups,dc=example,dc=com
cn: Developers
ou: Groups
description: Users who are Developers
uniqueMember: uid=user1,ou=Users,dc=example,dc=com
uniqueMember: uid=user3,ou=Users,dc=example,dc=com
uniqueMember: uid=user5,ou=Users,dc=example,dc=com
objectClass: groupOfUniqueNames
# Operational Group:
dn: cn=Operational,ou=Groups,dc=example,dc=com
cn: Operational
ou: Groups
description: Users who are Operational
uniqueMember: uid=user1,ou=Users,dc=example,dc=com
uniqueMember: uid=user2,ou=Users,dc=example,dc=com
objectClass: groupOfUniqueNames
现在我们需要将 OpenLDAP 组映射到 WildFly 的内置角色,如管理用户、组和它们的角色映射菜谱中所述,以便user1将拥有部署者、操作员和维护者的角色。请按照以下图像所示绑定组和角色:

现在,如果你尝试使用用户user1注销并重新登录到 WildFly Web 控制台,你将首先被提示输入 LDAPRealm 的凭据,如图所示:

接下来,你将使用适当的角色登录,如图所示:

如您所见,用户user1以操作员、维护者和部署者的角色登录。
它是如何工作的…
首先,我们需要切换提供者,使用 CLI 从simple切换到rbac。
其次,我们需要将我们的管理用户wildfly包含到SuperUser角色中,这导致了以下配置:
<access-control provider="rbac">
<role-mapping>
<role name="SuperUser">
<include>
<user name="$local"/>
<user alias="wildfly" realm="ManagementRealm" name="wildfly"/>
</include>
</role>
</role-mapping>
</access-control>
然后,我们添加了一个名为 LDAPRealm 的新安全域。在其中,我们定义了身份验证和授权过程,这涉及到设置适当的 LDAP 区分名称。
为了连接到 LDAP,我们必须使用 outbound-connections 指令创建一个连接引用。
最后,我们将 HTTP 管理接口,即 WildFly 控制台,映射到 LDAPRealm 域。
由于rbac.ldif文件的架构,其中用户所属的组映射到该组本身,我们不得不指导使用group-search元素而不是user-to-dn元素。
第十三章。使用 WildFly 进行消息传递
在本章中,您将学习以下配方:
-
使用 HornetQ 运行消息系统
-
向/从 JMS 队列目的地发送和接收消息
-
使用共享存储进行 HornetQ 集群
-
使用消息复制进行 HornetQ 集群
简介
在本章中,您将学习如何配置 WildFly 中的 HornetQ 嵌入,以便为我们的应用程序提供 JMS 功能。由于 WildFly 是 Java EE 7 认证的应用服务器,它实现了 JMS 规范版本 2.0。
HornetQ 是一种 面向消息的中间件(MOM),用于在集群和异步系统中交换消息。它可以独立运行(我说的不是 WildFly 独立模式)或嵌入,即在 WildFly 等应用程序服务器内部,通过使用 Java EE 连接器架构(JCA)。
因为 HornetQ 使用 JCA 与 WildFly 集成,它还提供了开箱即用的连接池、JTA 事务和容器管理安全支持,这些功能简化了开发者的工作。因此,将您的 EJB 应用程序与 JMS 系统集成不需要额外的工作。
HornetQ 使用两个基本概念,即接受者和连接器。接受者确定 HornetQ 服务器如何接受传入的连接,而连接器确定如何与其他 HornetQ 服务器或 JMS 客户端建立连接。
存在两种类型的接受者和连接器:
-
invm:同一 JVM 内部的连接(显然性能更好)
-
netty: 与远程 JVM 的连接
每个配置的连接器仅用于到达服务器,如果另一服务器上配置了相同类型的接受者。换句话说,如果我们使用 invm 连接器建立连接,另一服务器必须配置了 invm 接受者。另一方面,如果我们使用 netty 连接器建立连接,另一服务器必须配置了 netty 接受者。
HornetQ 使用一个主服务器和一个备份服务器提供高可用性(HA)功能。备份服务器处于空闲模式;只有在主服务器失败时才会工作。
实际上,所有消息都是不断从主服务器复制到备份服务器。这种同步通过两种方式实现:共享存储和消息复制。共享存储本质上意味着主服务器和备份服务器共享相同的文件系统,其中存储消息。另一方面,消息复制通过网络流量工作。然后每个服务器将在其自己的本地文件系统中持久化消息。
注意
只有持久消息才会被复制;因此,它们在服务器崩溃后仍然存在。
HornetQ 有自己的持久性,它基于高性能的日志文件系统。HornetQ 使用三种不同的日志:
-
绑定日志
-
JMS 日志
-
消息日志
HornetQ 支持两种不同的共享存储配置:
-
在 SAN 上的 GFS2,使用 AsyncIO 日志类型
-
使用 AsyncIO 或 NIO 日志类型的 NFSv4
注意
由于性能问题,NFS 应仅用于开发环境。
如果我们在独立模式下运行,我们可以在 WildFly 配置文件中找到嵌入的 HornetQ 配置,更确切地说是在standalone-full.xml和standalone-full-ha.xml中。然而,如果我们运行在域模式下,我们可以在 WildFly 提供的配置文件中找到适当的 HornetQ 配置,例如full和full-ha。full配置启用了消息系统,而full-ha增加了集群和负载均衡功能。
对于本章,假设您熟悉 JMS 概念,如队列、主题、MDB、死信队列、过期队列、点对点和发布/订阅,因为我们不会深入探讨 JMS 规范细节。如果您想开始学习 HornetQ,我强烈建议您阅读由 Piero Giacomelli 撰写的优秀书籍《HornetQ 消息开发指南》,由 Packt Publishing 出版,该书深入探讨了框架的开发方面和 JMS 规范。
HornetQ 也有替代品,例如 ActiveMQ。要深入了解 ActiveMQ,请参阅官方文档activemq.apache.org。
小贴士
构建一个消息系统是一项艰难且复杂的工作;它涉及许多方面(内存、性能、网络、存储、集群等),并且没有一种适合所有情况的配置。每个消息系统都需要进行专门的配置、测试和多次调整,才能投入生产。
使用 HornetQ 运行消息系统
在这个食谱中,您将学习如何配置和运行由 HornetQ 提供的 WildFly 消息系统。这是一个热身食谱,只是为了让您做好准备。
准备工作
要开始,让我们首先创建一个adhoc文件夹来运行我们的 WildFly。在终端窗口中输入以下命令:
$ cd $WILDFLY_HOME
$ cp -a standalone jms-std-node-1
现在是时候运行我们的 WildFly 了!!!
如何做到这一点...
-
打开一个终端窗口并执行以下命令:
$ cd $WILDFLY_HOME $ ./bin/standalone.sh -Djboss.server.base.dir=jms-std-node-1 --server-config=standalone-full.xml -
现在,如果我们打开 Web 控制台,我们应该也能找到消息子系统,如下面的截图所示:
![如何做到这一点…]()
管理控制台显示消息子系统
好吧,这很简单!让我们尝试本章接下来的更令人兴奋的食谱。
它是如何工作的...
首先,您应该已经注意到我们指定了--server-config指令,它覆盖了默认的配置文件名,即独立模式下的standalone.xml,以及域模式下的host.xml。
指定的配置文件名是预先配置了消息系统的那个。如果您还记得这本书前面的食谱,在 WildFly(同样在 JBoss AS 和 JBoss EAP)中,对于不同的配置文件,我们有以下内容:
-
default: 应用于通用 Web 应用程序(包括 REST 后端) -
full: 应用于具有 JMS 功能的应用程序 -
ha:此配置应用于需要集群和平衡能力的常见 Web 应用程序 -
full-ha:此配置应用于需要 JMS、集群和平衡能力的应用程序
因此,standalone-full.xml文件为我们提供了运行具有 JMS 功能的应用程序的正确配置。
启动启动脚本后,日志显示各种条目。以以下条目为例:
07:49:49,151 WARN [org.jboss.as.messaging] (MSC service thread 1-11) WFLYMSG0075: AIO wasn't located on this platform, it will fall back to using pure Java NIO. Your platform is Linux, install LibAIO to enable the AIO journal.
它在抱怨使用本地 API 访问文件系统而不是使用 Java NIO API。这是因为,如介绍中提到的,HornetQ 在文件系统上持久化 JMS 消息,所以更高效的 API 是首选。
其他有趣的日志条目如下:
07:49:50,249 INFO [org.hornetq.core.server] (ServerService Thread Pool -- 68) HQ221043: Adding protocol support CORE
07:49:50,262 INFO [org.hornetq.core.server] (ServerService Thread Pool -- 68) HQ221043: Adding protocol support AMQP
07:49:50,268 INFO [org.hornetq.core.server] (ServerService Thread Pool -- 68) HQ221043: Adding protocol support STOMP
07:49:50,535 INFO [org.hornetq.core.server] (ServerService Thread Pool -- 68) HQ221034: Waiting to obtain live lock
07:49:50,536 INFO [org.hornetq.core.server] (ServerService Thread Pool -- 68) HQ221035: Live Server Obtained live lock
07:49:50,585 INFO [org.jboss.messaging] (MSC service thread 1-12) WFLYMSG0016: Registered HTTP upgrade for hornetq-remoting protocol handled by http-acceptor-throughput acceptor
07:49:50,585 INFO [org.jboss.messaging] (MSC service thread 1-3) WFLYMSG0016: Registered HTTP upgrade for hornetq-remoting protocol handled by http-acceptor acceptor
07:49:50,710 INFO [org.hornetq.core.server] (ServerService Thread Pool -- 68) HQ221007: Server is now live
07:49:50,711 INFO [org.hornetq.core.server] (ServerService Thread Pool -- 68) HQ221001: HornetQ Server version 2.4.5.FINAL (Wild Hornet, 124) [48a0b5e3-1b30-11e5-838f-0fbc7481b7aa]
前面的条目与 HornetQ 提供的多协议互操作性相关(它不仅仅实现了 JMS 规范)。这意味着服务器(即 HornetQ)现在已准备好生产和消费消息,以及所有相关内容。
最后但同样重要的是,以下条目描述了预先配置的资源:
07:49:50,740 INFO [org.jboss.as.messaging] (ServerService Thread Pool -- 68) WFLYMSG0002: Bound messaging object to jndi name java:jboss/exported/jms/RemoteConnectionFactory
07:49:50,765 INFO [org.hornetq.core.server] (ServerService Thread Pool -- 70) HQ221003: trying to deploy queue jms.queue.DLQ
07:49:50,849 INFO [org.jboss.as.messaging] (ServerService Thread Pool -- 71) WFLYMSG0002: Bound messaging object to jndi name java:/ConnectionFactory
07:49:50,851 INFO [org.hornetq.core.server] (ServerService Thread Pool -- 69) HQ221003: trying to deploy queue jms.queue.ExpiryQueue
07:49:50,878 INFO [org.jboss.as.connector.deployment] (MSC service thread 1-7) WFLYJCA0007: Registered connection factory java:/JmsXA
07:49:50,930 INFO [org.hornetq.ra] (MSC service thread 1-7) HornetQ resource adaptor started
07:49:50,931 INFO [org.jboss.as.connector.services.resourceadapters.ResourceAdapterActivatorService$ResourceAdapterActivator] (MSC service thread 1-7) IJ020002: Deployed: file://RaActivatorhornetq-ra
07:49:50,937 INFO [org.jboss.as.connector.deployment] (MSC service thread 1-13) WFLYJCA0002: Bound JCA ConnectionFactory [java:/JmsXA]
07:49:50,938 INFO [org.jboss.as.messaging] (MSC service thread 1-16) WFLYMSG0002: Bound messaging object to jndi name java:jboss/DefaultJMSConnectionFactory
我们有连接工厂,用于连接服务器并从服务器获取会话,以便发送和接收消息。
我们还有两个目的地:jms.queue.DLQ和jms.queue.ExpiryQueue。
第一个是一个死信队列,所有无法成功处理(消费)的消息都会进入其中。当使用消息驱动 Bean(MDB)消费消息时出现错误,消息将被回滚并直接进入 DLQ。尽管如此,还是有重传策略可以尝试再次发送我们的消息。
所有过期消息都会进入名为ExpiryQueue的队列。
注意
如果您在 Mac 上运行并使用full-ha配置文件,请注意您可能会遇到以下网络问题:
HQ224033:广播连接器configs失败;无法分配请求的地址
由于full-ha配置提供了额外的集群和平衡能力,它为其 HornetQ 服务器提供自动发现,这是通过多播地址实现的。通常,UDP 流量会被丢弃,所以请确保您所在的环境接受 UDP 流量。
例如,在我们的案例中,我们可能正在尝试在家中的食谱,并且 UDP 流量被转发到丢弃此类流量的 ISP。我们需要做的是创建一个本地路由,将 UDP 流量重定向到我们的回环接口,即我们的 localhost,127.0.0.1 IP 地址。
您可以将多播路由添加到回环接口,如下所示:
sudo route add 224.0.0.0 127.0.0.1 -netmask 240.0.0.0
注意,在生产环境中运行时,添加此回环路由将阻止所有多播消息到达其他服务器。
参见
- 若要深入了解 HornetQ 服务器原理,请参阅官方文档,链接为
hornetq.jboss.org。
向/从 JMS 队列目标发送和接收消息
在这个菜谱中,我们将学习如何生产和消费 JMS 队列消息。我们将使用两个小应用程序,一个用于生成消息,另一个用于消费消息。
准备工作
-
要开始,让我们首先创建一个
adhoc文件夹来运行我们的 WildFly。在终端窗口中输入以下命令:$ cd $WILDFLY_HOME $ cp -a standalone jms-std-node-2 -
在这个菜谱中,我们需要一个应用程序来测试我们的配置。为此菜谱,我们需要名为
jms-producer和jms-consumer的应用程序,您可以在我的 GitHub 仓库中找到它们。如果您跳过了 第二章 中关于 使用部署文件夹管理应用程序 的菜谱,请参阅它以下载您将需要的所有源代码和项目。 -
要构建应用程序,请输入以下命令:
$ cd $WILDFLY_HOME/github/wildfly-cookbook $ cd jms-producer $ mvn clean package
现在是时候运行我们的 WildFly 了!!!
如何操作…
首先,我们需要向 ApplicationRealm 域添加一个用户,这是远程子系统使用的域,因此也是消息子系统。
-
打开一个终端窗口并执行以下操作:
$ ./bin/add-user.sh -sc jms-std-node-2/configuration What type of user do you wish to add? a) Management User (mgmt-users.properties) b) Application User (application-users.properties) (a): b Enter the details of the new user to add. Using realm 'ApplicationRealm' as discovered from the existing property files. Username : jmsuser Password recommendations are listed below. To modify these restrictions edit the add-user.properties configuration file. - The password should not be one of the following restricted values {root, admin, administrator} - The password should contain at least 8 characters, 1 alphabetic character(s), 1 digit(s), 1 non-alphanumeric symbol(s) - The password should be different from the username Password : [jmsuser.2015] Re-enter Password : [jmsuser.2015] What groups do you want this user to belong to? (Please enter a comma separated list, or leave blank for none)[ ]: guest About to add user 'jmsuser' for realm 'ApplicationRealm' Is this correct yes/no? yes Added user 'jmsuser' to file '/opt/wildfly/jms-std-node-2/configuration/application-users.properties' Added user 'jmsuser' with groups guest to file '/opt/wildfly/jms-std-node-2/configuration/application-roles.properties' Is this new user going to be used for one AS process to connect to another AS process? e.g. for a slave host controller connecting to the master or for a Remoting connection for server to server EJB calls. yes/no? no -
现在是时候按照以下方式运行我们的 WildFly 了:
$ cd $WILDFLY_HOME $ ./bin/standalone.sh -Djboss.server.base.dir=jms-std-node-2 --server-config=standalone-full.xml -
一旦开始,让我们在新终端中连接到 CLI 并创建队列,如下所示:
$ cd $WILDFLY_HOME $ ./bin/jboss-cli.sh You are disconnected at the moment. Type 'connect' to connect to the server or 'help' for the list of supported commands. [disconnected /] connect [standalone@localhost:9990 /] jms-queue add --queue-address=WildFlyCookbookQueue --entries=queue/test,java:jboss/exported/jms/queue/test [standalone@localhost:9990 /] -
在我们的
server.log中,我们应该找到以下条目:12:02:52,183 INFO [org.hornetq.core.server] (ServerService Thread Pool -- 60) HQ221003: trying to deploy queue jms.queue.WildFlyCookbookQueue太棒了,我们成功创建了我们的队列目标!!!
-
现在是时候运行我们的
jms-producer并向我们的WildFlyCookbookQueue队列发送消息了。让我们编译并运行它,如下所示:$ cd $WILDFLY_HOME/github/wildfly-cookbook/jms-producer $ mvn clean compile exec:java上述命令的结果如图所示:
![如何操作…]()
我们的生成器发送了包含以下文本的十条文本消息:
WildFly Cookbook 的一条消息。 -
因此,如果我们现在连接到 CLI,我们可以计算我们的
WildFlyCookbookQueue目标中的消息数量,如下所示:$ cd $WILDFLY_HOME $ ./bin/jboss-cli.sh You are disconnected at the moment. Type 'connect' to connect to the server or 'help' for the list of supported commands. [disconnected /] connect [standalone@localhost:9990 /] jms-queue count-messages --queue-address=WildFlyCookbookQueue 10L -
上述 CLI 命令实际上显示了我们所期望的消息数量。让我们通过执行以下命令来尝试我们的消费者:
$ cd $WILDFLY_HOME/github/wildfly-cookbook/jms-consumer $ mvn clean compile exec:java上述命令的结果如图所示:
![如何操作…]()
太棒了,我们成功消费了存储在队列中的消息!!!
此外,当使用 MDB 消费消息时,如果您的 MDB 出现错误,或者由于任何原因抛出异常,其进程将被回滚;因此,消息将进入死信队列(DLQ)。
它是如何工作的…
为了能够理解我们所做的一切以及为什么,我们需要查看默认消息子系统配置。为了本菜谱的目的,我们将分析一个特定的设置,例如 security-setting:
<security-settings>
<security-setting match="#">
<permission type="send" roles="guest"/>
<permission type="consume" roles="guest"/>
<permission type="createNonDurableQueue" roles="guest"/>
<permission type="deleteNonDurableQueue" roles="guest"/>
</security-setting>
</security-settings>
上面的 XML 代码片段定义了任何(“#”符号是一个通配符,表示任何)目标都需要为特定的权限指定特定的角色,例如发送和消费消息。默认设置指定了“guest”角色,用于从任何队列发送和消费消息。
正因如此,我们需要添加一个用户,使用add-user脚本并指定角色guest。此外,为什么我们还需要用户呢?
由于我们正在使用http-remoting://localhost:8080地址远程连接到目的地,WildFly 使用远程子系统来处理远程连接,其配置如下:
<subsystem >
<endpoint worker="default"/>
<http-connector name="http-remoting-connector" connector-ref="default" security-realm="ApplicationRealm"/>
</subsystem>
远程子系统引用了ApplicationRealm域,这就是为什么我们将jmsuser用户添加到该域的原因。
在 Java 代码方面,我们添加了用户的引用如下:
final Properties env = new Properties();
env.put(Context.INITIAL_CONTEXT_FACTORY, INITIAL_CONTEXT_FACTORY);
env.put(Context.PROVIDER_URL, System.getProperty(Context.PROVIDER_URL, PROVIDER_URL));
env.put(Context.SECURITY_PRINCIPAL, System.getProperty("username", DEFAULT_USERNAME));
env.put(Context.SECURITY_CREDENTIALS, System.getProperty("password", DEFAULT_PASSWORD));
context = new InitialContext(env);
如果我们没有在 Java 代码中指定用户(实际上省略了SECURITY_PRINCIPAL和SECURITY_CREDENTIALS属性),或者甚至添加到域中,我们最终会得到以下错误:
Attempting to acquire connection factory "jms/RemoteConnectionFactory"
Found connection factory "jms/RemoteConnectionFactory" in JNDI
Attempting to acquire destination "jms/queue/test"
Found destination "jms/queue/test" in JNDI
javax.jms.JMSSecurityException: HQ119031: Unable to validate user: null
at org.hornetq.core.protocol.core.impl.ChannelImpl.sendBlocking(ChannelImpl.java:394)
at org.hornetq.core.client.impl.ClientSessionFactoryImpl.createSessionInternal(ClientSessionFactoryImpl.java:891)
at org.hornetq.core.client.impl.ClientSessionFactoryImpl.createSessionInternal(ClientSessionFactoryImpl.java:800)
at org.hornetq.core.client.impl.ClientSessionFactoryImpl.createSession(ClientSessionFactoryImpl.java:337)
at org.hornetq.jms.client.HornetQConnection.authorize(HornetQConnection.java:719)
at org.hornetq.jms.client.HornetQConnectionFactory.createConnectionInternal(HornetQConnectionFactory.java:762)
at org.hornetq.jms.client.HornetQConnectionFactory.createConnection(HornetQConnectionFactory.java:112)
at org.hornetq.jms.client.HornetQConnectionFactory.createConnection(HornetQConnectionFactory.java:107)
at com.packtpub.wildflycookbook.JMSProducer.main(JMSProducer.java:59)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:483)
at org.codehaus.mojo.exec.ExecJavaMojo$1.run(ExecJavaMojo.java:283)
at java.lang.Thread.run(Thread.java:745)
Caused by: HornetQSecurityException[errorType=SECURITY_EXCEPTION message=HQ119031: Unable to validate user: null]
... 15 more
还有更多…
在前面的示例中看到的逻辑也适用于主题。在 CLI 中,我们添加topic的命令几乎相同:
[standalone@localhost:9990 /] jms-topic add --topic-address=WildFlyCookbookTopic --entries=topic/test,java:jboss/exported/jms/topic/test
参见
-
关于 JMS 规范的信息可以在
jcp.org/aboutJava/communityprocess/final/jsr343/index.html找到。 -
要深入了解 HornetQ 服务器原理,请参阅官方文档
hornetq.jboss.org
使用共享存储进行 HornetQ 集群
在这个菜谱中,你将学习如何通过配置 WildFly 实例来配置 HornetQ 以提供集群功能;一个作为实时HornetQ 服务器,另一个作为备份HornetQ 服务器。
整体配置可以表示如下图像所示:

共享存储配置
在菜谱中,我们将通过为每个实时和备份服务器对提供相同的data目录来使用本地共享存储。这种配置在开发和测试环境中也非常有用,可以轻松模拟高可用性和故障转移功能。
在生产环境中,你应该通过拥有更多对实时和备份服务器,并使用以下图像中所示的正确文件系统来提供高可用性:

高可用性共享存储配置
准备工作
要开始,让我们首先创建两个adhoc文件夹来运行我们的 WildFly。
-
在终端窗口中,运行以下命令:
$ cd $WILDFLY_HOME $ cp -a standalone hq-live $ cp -a standalone hq-backup -
对于这个菜谱,我们需要两个名为
jms-producer和jms-consumer的应用程序,你可以在我的 GitHub 仓库中找到它们。如果你跳过了第二章中关于使用部署文件夹管理应用程序的菜谱,以独立模式运行 WildFly,请参考它以下载你需要的所有源代码和项目。 -
要构建应用程序,请执行以下命令:
$ cd $WILDFLY_HOME/github/wildfly-cookbook $ cd jms-producer $ mvn clean package
如何操作…
首先,我们需要将一个用户添加到ApplicationRealm域中,这是远程子系统使用的域,因此也是消息子系统使用的域:
-
打开一个终端窗口,并输入以下命令:
$ cd $WILDFLY_HOME $ ./bin/add-user.sh --silent -a -u jmsuser -p jmsuser.2015 -g guest -sc hq-live/configuration $ ./bin/add-user.sh --silent -a -u jmsuser -p jmsuser.2015 -g guest -sc hq-backup/configuration -
现在编辑
hq-live 服务器的standalone-full-ha.xml文件,并将messaging子系统替换为以下内容:<subsystem > <hornetq-server> <clustered>true</clustered> <persistence-enabled>true</persistence-enabled> <cluster-user>us3r</cluster-user> <cluster-password>p@ssw0rd</cluster-password> <backup>false</backup> <allow-failback>true</allow-failback> <failover-on-shutdown>true</failover-on-shutdown> <check-for-live-server>true</check-for-live-server> <backup-group-name>backup-group-2</backup-group-name> <shared-store>true</shared-store> <journal-type>NIO</journal-type> <journal-min-files>2</journal-min-files> <journal-file-size>102400</journal-file-size> <paging-directory path="hq-data/paging" relative-to="user.home"/> <bindings-directory path="hq-data/bindings" relative-to="user.home"/> <journal-directory path="hq-data/journal" relative-to="user.home"/> <large-messages-directory path="hq-data/large-messages" relative-to="user.home"/> <connectors> <http-connector name="http-connector" socket-binding="http"> <param key="http-upgrade-endpoint" value="http-acceptor"/> </http-connector> <http-connector name="http-connector-throughput" socket-binding="http"> <param key="http-upgrade-endpoint" value="http-acceptor-throughput"/> <param key="batch-delay" value="50"/> </http-connector> <in-vm-connector name="in-vm" server-id="0"/> </connectors> <acceptors> <http-acceptor name="http-acceptor" http-listener="default"/> <http-acceptor name="http-acceptor-throughput" http-listener="default"> <param key="batch-delay" value="50"/> <param key="direct-deliver" value="false"/> </http-acceptor> <in-vm-acceptor name="in-vm" server-id="0"/> </acceptors> <broadcast-groups> <broadcast-group name="bg-group1"> <socket-binding>messaging-group</socket-binding> <connector-ref>http-connector</connector-ref> </broadcast-group> </broadcast-groups> <discovery-groups> <discovery-group name="dg-group1"> <socket-binding>messaging-group</socket-binding> </discovery-group> </discovery-groups> <cluster-connections> <cluster-connection name="my-cluster"> <address>jms</address> <connector-ref>http-connector</connector-ref> <discovery-group-ref discovery-group-name="dg-group1"/> </cluster-connection> </cluster-connections> <security-settings> <security-setting match="#"> <permission type="send" roles="guest"/> <permission type="consume" roles="guest"/> <permission type="createNonDurableQueue" roles="guest"/> <permission type="deleteNonDurableQueue" roles="guest"/> </security-setting> </security-settings> <address-settings> <!--default for catch all--> <address-setting match="#"> <dead-letter-address>jms.queue.DLQ</dead-letter-address> <expiry- address>jms.queue.ExpiryQueue</expiry- address> <redistribution-delay>1000</redistribution-delay> <max-size-bytes>10485760</max-size-bytes> <page-size-bytes>2097152</page-size-bytes> <message-counter-history-day-limit>10</message-counter-history-day- limit> </address-setting> </address-settings> <jms-connection-factories> <connection-factory name="InVmConnectionFactory"> <connectors> <connector-ref connector-name="in-vm"/> </connectors> <entries> <entry name="java:/ConnectionFactory"/> </entries> </connection-factory> <connection-factory name="RemoteConnectionFactory"> <connectors> <connector-ref connector-name="http-connector"/> </connectors> <entries> <entry name="java:jboss/exported/jms/RemoteConnectionFactory"/> </entries> <ha>true</ha> <block-on-acknowledge>true</block-on-acknowledge> <retry-interval>500</retry-interval> <retry-interval-multiplier>1.5</retry-interval-multiplier> <max-retry-interval>60000</max-retry-interval> <reconnect-attempts>-1</reconnect-attempts> </connection-factory> <pooled-connection-factory name="hornetq-ra"> <transaction mode="xa"/> <connectors> <connector-ref connector-name="in-vm"/> </connectors> <entries> <entry name="java:/JmsXA"/> <!-- Global JNDI entry used to provide a default JMS Connection factory to EE application --> <entry name="java:jboss/DefaultJMSConnectionFactory"/> </entries> <ha>true</ha> <block-on-acknowledge>true</block-on-acknowledge> <retry-interval>500</retry-interval> <retry-interval-multiplier>1.5</retry-interval-multiplier> <max-retry-interval>60000</max-retry-interval> <reconnect-attempts>1000</reconnect-attempts> </pooled-connection-factory> </jms-connection-factories> <jms-destinations> <jms-queue name="ExpiryQueue"> <entry name="java:/jms/queue/ExpiryQueue"/> </jms-queue> <jms-queue name="DLQ"> <entry name="java:/jms/queue/DLQ"/> </jms-queue> <jms-queue name="WildFlyCookbookQueue"> <entry name="queue/test"/> <entry name="java:jboss/exported/jms/queue/test"/> <durable>true</durable> </jms-queue> </jms-destinations> </hornetq-server> </subsystem> -
现在是时候运行我们的 WildFly
live实例了,如下所示:$ cd $WILDFLY_HOME $ ./bin/standalone.sh -Djboss.server.base.dir=hq-live --server-config=standalone-full-ha.xml -Djboss.socket.binding.port-offset=100 -
在启动
live服务器时,编辑hq-backup服务器的standalone-full-ha.xml文件,并将messaging子系统替换为以下内容:<subsystem > <hornetq-server> <clustered>true</clustered> <persistence-enabled>true</persistence-enabled> <cluster-user>us3r</cluster-user> <cluster-password>p@ssw0rd</cluster-password> <backup>true</backup> <allow-failback>true</allow-failback> <failover-on-shutdown>true</failover-on-shutdown> <check-for-live-server>true</check-for-live-server> <backup-group-name>backup-group-2</backup-group-name> <shared-store>true</shared-store> <journal-type>NIO</journal-type> <journal-min-files>2</journal-min-files> <journal-file-size>102400</journal-file-size> <paging-directory path="hq-data/paging" relative-to="user.home"/> <bindings-directory path="hq-data/bindings" relative-to="user.home"/> <journal-directory path="hq-data/journal" relative-to="user.home"/> <large-messages-directory path="hq-data/large-messages" relative-to="user.home"/> <connectors> <http-connector name="http-connector" socket-binding="http"> <param key="http-upgrade-endpoint" value="http-acceptor"/> </http-connector> <http-connector name="http-connector-throughput" socket-binding="http"> <param key="http-upgrade-endpoint" value="http-acceptor-throughput"/> <param key="batch-delay" value="50"/> </http-connector> <in-vm-connector name="in-vm" server-id="0"/> </connectors> <acceptors> <http-acceptor name="http-acceptor" http-listener="default"/> <http-acceptor name="http-acceptor-throughput" http-listener="default"> <param key="batch-delay" value="50"/> <param key="direct-deliver" value="false"/> </http-acceptor> <in-vm-acceptor name="in-vm" server-id="0"/> </acceptors> <broadcast-groups> <broadcast-group name="bg-group1"> <socket-binding>messaging-group</socket-binding> <connector-ref>http-connector</connector-ref> </broadcast-group> </broadcast-groups> <discovery-groups> <discovery-group name="dg-group1"> <socket-binding>messaging-group</socket- binding> </discovery-group> </discovery-groups> <cluster-connections> <cluster-connection name="my-cluster"> <address>jms</address> <connector-ref>http-connector</connector-ref> <discovery-group-ref discovery-group-name="dg-group1"/> </cluster-connection> </cluster-connections> <security-settings> <security-setting match="#"> <permission type="send" roles="guest"/> <permission type="consume" roles="guest"/> <permission type="createNonDurableQueue" roles="guest"/> <permission type="deleteNonDurableQueue" roles="guest"/> </security-setting> </security-settings> <address-settings> <!--default for catch all--> <address-setting match="#"> <dead-letter-address>jms.queue.DLQ</dead-letter-address> <expiry-address>jms.queue.ExpiryQueue</expiry-address> <redistribution-delay>1000</redistribution-delay> <max-size-bytes>10485760</max-size-bytes> <page-size-bytes>2097152</page-size-bytes> <message-counter-history-day-limit>10</message-counter-history-day-limit> </address-setting> </address-settings> <jms-connection-factories> <connection-factory name="InVmConnectionFactory"> <connectors> <connector-ref connector-name="in-vm"/> </connectors> <entries> <entry name="java:/ConnectionFactory"/> </entries> </connection-factory> <connection-factory name="RemoteConnectionFactory"> <connectors> <connector-ref connector-name="http-connector"/> </connectors> <entries> <entry name="java:jboss/exported/jms/RemoteConnectionFactory"/> </entries> <ha>true</ha> <block-on-acknowledge>true</block-on-acknowledge> <retry-interval>500</retry-interval> <retry-interval-multiplier>1.5</retry-interval-multiplier> <max-retry-interval>60000</max-retry-interval> <reconnect-attempts>1000</reconnect-attempts> </connection-factory> <pooled-connection-factory name="hornetq-ra"> <transaction mode="xa"/> <connectors> <connector-ref connector-name="in-vm"/> </connectors> <entries> <entry name="java:/JmsXA"/> <!-- Global JNDI entry used to provide a default JMS Connection factory to EE application --> <entry name="java:jboss/DefaultJMSConnectionFactory"/> </entries> <ha>true</ha> </pooled-connection-factory> </jms-connection-factories> <jms-destinations> <jms-queue name="ExpiryQueue"> <entry name="java:/jms/queue/ExpiryQueue"/> </jms-queue> <jms-queue name="DLQ"> <entry name="java:/jms/queue/DLQ"/> </jms-queue> <jms-queue name="WildFlyCookbookQueue"> <entry name="queue/test"/> <entry name="java:jboss/exported/jms/queue/test"/> <durable>true</durable> </jms-queue> </jms-destinations> </hornetq-server> </subsystem> -
你现在可以按照以下方式启动 WildFly
backup实例:/bin/standalone.sh -Djboss.server.base.dir=hq-backup -c standalone-full-ha.xml -Djboss.socket.binding.port-offset=200 -
在实时服务器的日志中,你应该找到以下条目:
... 16:48:42,918 INFO [org.hornetq.core.server] (ServerService Thread Pool -- 68) HQ221034: Waiting to obtain live lock 16:48:42,918 INFO [org.hornetq.core.server] (ServerService Thread Pool -- 68) HQ221035: Live Server Obtained live lock 16:48:43,073 INFO [org.hornetq.core.server] (ServerService Thread Pool -- 68) HQ221007: Server is now live 16:48:43,073 INFO [org.hornetq.core.server] (ServerService Thread Pool -- 68) HQ221001: HornetQ Server version 2.4.5.FINAL (Wild Hornet, 124) [adcd3a5c-1e60-11e5-877d-c36458d7eaba] ... 16:48:43,326 INFO [org.jboss.as] (Controller Boot Thread) WFLYSRV0060: Http management interface listening on http://127.0.0.1:10090/management 16:48:43,327 INFO [org.jboss.as] (Controller Boot Thread) WFLYSRV0051: Admin console listening on http://127.0.0.1:10090 16:48:43,327 INFO [org.jboss.as] (Controller Boot Thread) WFLYSRV0025: WildFly Full 9.0.0.Beta2 (WildFly Core 1.0.0.Beta2) started in 3298ms - Started 249 of 481 services (282 services are lazy, passive or on-demand) -
当你在备份服务器的日志中时,你应该找到以下条目:
16:51:21,298 INFO [org.hornetq.core.server] (HQ119000: Activation for server HornetQServerImpl::serverUUID=adcd3a5c-1e60-11e5-877d-c36458d7eaba) HQ221032: Waiting to become backup node 16:51:21,301 INFO [org.hornetq.core.server] (HQ119000: Activation for server HornetQServerImpl::serverUUID=adcd3a5c-1e60-11e5-877d-c36458d7eaba) HQ221033: ** got backup lock 16:51:21,329 INFO [org.hornetq.core.server] (HQ119000: Activation for server HornetQServerImpl::serverUUID=adcd3a5c-1e60-11e5-877d-c36458d7eaba) HQ221013: Using NIO Journal 16:51:21,636 INFO [org.hornetq.core.server] (HQ119000: Activation for server HornetQServerImpl::serverUUID=adcd3a5c-1e60-11e5-877d-c36458d7eaba) HQ221043: Adding protocol support CORE 16:51:21,641 INFO [org.hornetq.core.server] (HQ119000: Activation for server HornetQServerImpl::serverUUID=adcd3a5c-1e60-11e5-877d-c36458d7eaba) HQ221043: Adding protocol support AMQP 16:51:21,645 INFO [org.hornetq.core.server] (HQ119000: Activation for server HornetQServerImpl::serverUUID=adcd3a5c-1e60-11e5-877d-c36458d7eaba) HQ221043: Adding protocol support STOMP 16:51:21,667 INFO [org.hornetq.core.server] (HQ119000: Activation for server HornetQServerImpl::serverUUID=adcd3a5c-1e60-11e5-877d-c36458d7eaba) HQ221109: HornetQ Backup Server version 2.4.5.FINAL (Wild Hornet, 124) [adcd3a5c-1e60-11e5-877d-c36458d7eaba] started, waiting live to fail before it gets active 16:51:21,742 INFO [org.jboss.as] (Controller Boot Thread) WFLYSRV0060: Http management interface listening on http://127.0.0.1:10190/management 16:51:21,742 INFO [org.jboss.as] (Controller Boot Thread) WFLYSRV0051: Admin console listening on http://127.0.0.1:10190 16:51:21,742 INFO [org.jboss.as] (Controller Boot Thread) WFLYSRV0025: WildFly Full 9.0.0.Beta2 (WildFly Core 1.0.0.Beta2) started in 3001ms - Started 235 of 475 services (281 services are lazy, passive or on-demand) 16:51:23,288 INFO [org.hornetq.core.server] (Thread-0 (HornetQ-server-HornetQServerImpl::serverUUID=adcd3a5c-1e60-11e5-877d-c36458d7eaba-640469128)) HQ221031: backup announced实时和备份服务器的日志条目都证实了我们的配置:实时服务器已经获得了数据目录的锁,而备份正在等待获取锁,并已宣布自己为备份。
现在是时候进行一些测试了。
测试
为了测试我们的配置,我们首先需要生成一些消息,然后检查消息是否已存储,然后消费它们。在这种情况下,按照以下方式编译jms-producer项目:
-
让我们使用
jms-producer项目按照以下方式生成一些消息:$ cd $WILDFLY_HOME/github/wildfly-cookbook $ cd jms-producer $ mvn clean package $ mvn exec:java ... Attempting to acquire connection factory "jms/RemoteConnectionFactory" Found connection factory "jms/RemoteConnectionFactory" in JNDI Attempting to acquire destination "jms/queue/test" Found destination "jms/queue/test" in JNDI Sending messages with content: A message by WildFly Cookbook Sending messages with content [0]: A message by WildFly Cookbook Sending messages with content [1]: A message by WildFly Cookbook Sending messages with content [2]: A message by WildFly Cookbook Sending messages with content [3]: A message by WildFly Cookbook Sending messages with content [4]: A message by WildFly Cookbook Sending messages with content [5]: A message by WildFly Cookbook Sending messages with content [6]: A message by WildFly Cookbook Sending messages with content [7]: A message by WildFly Cookbook Sending messages with content [8]: A message by WildFly Cookbook Sending messages with content [9]: A message by WildFly Cookbook [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 11.234 s [INFO] Finished at: 2015-06-29T16:03:33+02:00 [INFO] Final Memory: 15M/298M [INFO] ------------------------------------------------------------------------ -
现在,如果你检查消息计数,你将在实时服务器上找到十条消息。要检查,请执行以下操作:
[standalone@127.0.0.1:10090 /] jms-queue count-messages --queue-address=WildFlyCookbookQueue 10L [standalone@127.0.0.1:10090 /] -
如果你将前面的命令在备份服务器上执行,你会得到以下错误:
[standalone@127.0.0.1:10190 /] jms-queue count-messages --queue-address=WildFlyCookbookQueue WFLYMSG0066: Resource at the address [ ("subsystem" => "messaging"), ("hornetq-server" => "default"), ("jms-queue" => "WildFlyCookbookQueue") ] can not be managed, the hornetq-server is in backup mode [standalone@127.0.0.1:10190 /] -
如果你停止实时服务器,备份服务器应该成为新的实时服务器。你应该在备份服务器上找到以下日志条目:
17:58:09,698 WARN [org.hornetq.core.client] (Thread-1 (HornetQ-client-global-threads-1744891225)) HQ212037: Connection failure has been detected: HQ119015: The connection was disconnected because of server shutdown [code=DISCONNECTED] 17:58:09,898 INFO [org.jboss.messaging] (MSC service thread 1-10) WFLYMSG0016: Registered HTTP upgrade for hornetq-remoting protocol handled by http-acceptor acceptor 17:58:09,898 INFO [org.jboss.messaging] (MSC service thread 1-16) WFLYMSG0016: Registered HTTP upgrade for hornetq-remoting protocol handled by http-acceptor-throughput acceptor 17:58:09,900 INFO [org.hornetq.core.server] (ServerService Thread Pool -- 72) HQ221003: trying to deploy queue jms.queue.ExpiryQueue 17:58:09,905 INFO [org.hornetq.core.server] (ServerService Thread Pool -- 76) HQ221003: trying to deploy queue jms.queue.WildFlyCookbookQueue 17:58:09,916 INFO [org.jboss.as.messaging] (ServerService Thread Pool -- 75) WFLYMSG0002: Bound messaging object to jndi name java:jboss/exported/jms/RemoteConnectionFactory 17:58:09,916 INFO [org.hornetq.core.server] (ServerService Thread Pool -- 74) HQ221003: trying to deploy queue jms.queue.DLQ 17:58:09,918 INFO [org.jboss.as.messaging] (ServerService Thread Pool -- 73) WFLYMSG0002: Bound messaging object to jndi name java:/ConnectionFactory 17:58:09,918 INFO [org.hornetq.core.server] (HQ119000: Activation for server HornetQServerImpl::serverUUID=adcd3a5c-1e60-11e5-877d-c36458d7eaba) HQ221010: Backup Server is now live 17:58:09,956 INFO [org.jboss.as.connector.deployment] (MSC service thread 1-2) WFLYJCA0007: Registered connection factory java:/JmsXA 17:58:09,971 INFO [org.hornetq.ra] (MSC service thread 1-2) HornetQ resource adaptor started 17:58:09,971 INFO [org.jboss.as.connector.services.resourceadapters.ResourceAdapterActivatorService$ResourceAdapterActivator] (MSC service thread 1-2) IJ020002: Deployed: file://RaActivatorhornetq-ra 17:58:09,973 INFO [org.jboss.as.connector.deployment] (MSC service thread 1-4) WFLYJCA0002: Bound JCA ConnectionFactory [java:/JmsXA] 17:58:09,973 INFO [org.jboss.as.messaging] (MSC service thread 1-11) WFLYMSG0002: Bound messaging object to jndi name java:jboss/DefaultJMSConnectionFactory -
如果你再次在旧备份服务器上调用计数消息命令,你会看到它包含了之前产生的所有消息:
[standalone@127.0.0.1:10190 /] jms-queue count-messages --queue-address=WildFlyCookbookQueue 10L [standalone@127.0.0.1:10190 /]
如何工作…
故障转移背后的机制相当简单;因为 HornetQ 基于文件系统进行持久化,它会在数据存储文件夹上创建一个锁。基本上,对于相同的文件夹,第一个到达的服务器拥有锁,因此获胜。第二个服务器会继续尝试获取相同数据目录的锁,直到获得它,这意味着实时服务器已关闭或崩溃。
当备份服务器获得锁时,它会读取数据目录中存储的所有消息;它可用于存储更多消息,并且客户端会消费它们。
如果实时服务器在客户端生成或消费消息时崩溃会发生什么?
由于故障转移和高可用性配置(在<connection-factory name="RemoteConnectionFactory">和<pooled-connection-factory name="hornetq-ra">中的<ha>true</ha>),客户端会自动重新连接到第一个可用的实时服务器。客户端具有实时和备份服务器上的拓扑。
在生成消息时,尝试停止实时服务器。你应该得到以下图像中所示类似的输出:

在发送消息时,客户端重新连接到第一个可用的活动服务器
在消费消息时也适用。以下是一张显示您应该得到的内容的图片:

在消费消息时,客户端重新连接到第一个可用的活动服务器
参见
-
如介绍中所述,有很多关于 HornetQ、JMS 和 MOM 的一般性话题可以讨论,但这超出了本书的范围。因此,我建议您阅读 HornetQ 消息开发人员指南,作者 Piero Giacomelli,Packt Publishing,它深入探讨了框架的开发方面和 JMS 规范。
-
此外,jboss.org 社区在为我们所有人提供关于 HornetQ 的信息方面做得非常出色,这些信息可以在
hornetq.jboss.org免费获取。 -
最后,但同样重要的是,JMS 规范可在
jcp.org/aboutJava/communityprocess/final/jsr343/index.html获取。
使用消息复制来集群 HornetQ
在这个配方中,我们将学习如何配置一个包含活动 HornetQ 服务器和备份 HornetQ 服务器的集群。
根据之前的配方,可以通过使用共享存储(活动服务器和备份服务器共享相同的数据目录)或通过在网络层发生的消息复制来实现 HornetQ 集群环境——可以通过以下设置实现消息复制模式:
<shared-store>false</shared-store>
我们最终的配置应提供以下架构:

消息复制配置
准备工作
要开始,让我们首先创建一个 adhoc 文件夹来运行我们的 WildFly。
-
在终端窗口中,输入以下命令:
$ cd $WILDFLY_HOME $ cp -a standalone hq-node-1 $ cp -a standalone hq-node-2 -
为发送和接收消息的服务器提供应用程序用户,如下所示:
$ cd $WILDFLY_HOME $ ./bin/add-user.sh --silent -a -u jmsuser -p jmsuser.2015 -g guest -sc hq-node-1/configuration $ ./bin/add-user.sh --silent -a -u jmsuser -p jmsuser.2015 -g guest -sc hq-node-1/configuration -
对于这个配方,我们需要一个名为
cluster-jms-replication的应用程序,您可以在我的 GitHub 仓库中找到它。如果您跳过了 第二章 中关于 使用部署文件夹管理应用程序 的配方,请参阅它以下载您将需要的所有源代码和项目。 -
要构建应用程序,请运行以下命令:
$ cd $WILDFLY_HOME/github/wildfly-cookbook $ cd cluster-jms-replication $ mvn clean package
现在是时候配置我们的 HornetQ 服务器在 WildFly 中了!!!
如何做到这一点...
为了配置我们的服务器,我们将依赖于我 GitHub 仓库中现有的配置,命名为 wildfly-cookbook。在那里您可以找到一个名为 cluster-jms-replication 的项目,该项目由审稿人 Kylin Soong 提供。
-
在您下载仓库的文件夹(
~/WFC/github/wildfly-cookbook)中,有一个名为cluster-jms-replication的项目。在项目文件夹内执行以下操作:$ mvn clean install $ cp standalone-full-ha-1.xml $WILDFLY_HOME/node1/configuration/ $ cp standalone-full-ha-2.xml $WILDFLY_HOME/node2/configuration/ $ cp queue-jms.xml $WILDFLY_HOME/node1/deployments/ $ cp queue-jms.xml $WILDFLY_HOME/node2/deployments/ $ cp target/cluster-demo-jms.jar $WILDFLY_HOME/node1/deployments/ $ cp target/cluster-demo-jms.jar $WILDFLY_HOME/node2/deployments/ -
现在我们已经准备好以下启动我们的服务器:
$ ./bin/standalone.sh -c standalone-full-ha-1.xml -Djboss.server.base.dir=node1 $ ./bin/standalone.sh -c standalone-full-ha-2.xml -Djboss.server.base.dir=node2 -Djboss.socket.binding.port-offset=100 -
服务器启动后,转到
cluster-jms-replication项目文件夹并执行以下命令:$ mvn exec:java -
这将向 HornetQ 集群发送十条消息。在
node1服务器日志条目中,你应该找到以下内容:12:07:02,323 INFO [stdout] (Thread-7 (HornetQ-client-global-threads-1050658442)) 3: WildFly 9 HornetQ Messaging High Available 12:07:12,334 INFO [stdout] (Thread-7 (HornetQ-client-global-threads-1050658442)) 5: WildFly 9 HornetQ Messaging High Available 12:07:22,344 INFO [stdout] (Thread-7 (HornetQ-client-global-threads-1050658442)) 7: WildFly 9 HornetQ Messaging High Available 12:07:32,355 INFO [stdout] (Thread-7 (HornetQ-client-global-threads-1050658442)) 9: WildFly 9 HornetQ Messaging High Available在
node2服务器日志条目中,你应该找到以下内容:12:06:52,350 INFO [stdout] (Thread-7 (HornetQ-client-global-threads-589728564)) 2: WildFly 9 HornetQ Messaging High Available 12:07:02,358 INFO [stdout] (Thread-7 (HornetQ-client-global-threads-589728564)) 4: WildFly 9 HornetQ Messaging High Available 12:07:12,363 INFO [stdout] (Thread-7 (HornetQ-client-global-threads-589728564)) 6: WildFly 9 HornetQ Messaging High Available 12:07:22,370 INFO [stdout] (Thread-7 (HornetQ-client-global-threads-589728564)) 8: WildFly 9 HornetQ Messaging High Available 12:07:32,379 INFO [stdout] (Thread-7 (HornetQ-client-global-threads-589728564)) 10: WildFly 9 HornetQ Messaging High Available -
现在,我们将通过将前几条消息发送到
node1并停止它来进行故障转移测试(正常关闭或终止都一样)。再次进入cluster-jms-replication项目文件夹,并执行以下命令:$ mvn exec:java -
现在,一旦你在服务器
node1上收到前两条消息,就停止它。在过程结束时,你应该得到以下日志条目:-
node112:15:44,592 INFO [stdout] (Thread-13 (HornetQ-client-global-threads-1050658442)) 1: WildFly 9 HornetQ Messaging High Available 12:15:54,602 INFO [stdout] (Thread-13 (HornetQ-client-global-threads-1050658442)) 3: WildFly 9 HornetQ Messaging High Available -
node212:15:44,599 INFO [stdout] (Thread-12 (HornetQ-client-global-threads-589728564)) 2: WildFly 9 HornetQ Messaging High Available 12:15:54,606 INFO [stdout] (Thread-12 (HornetQ-client-global-threads-589728564)) 4: WildFly 9 HornetQ Messaging High Available 12:16:04,613 INFO [stdout] (Thread-12 (HornetQ-client-global-threads-589728564)) 6: WildFly 9 HornetQ Messaging High Available ... Errors and Warnings ... 12:16:14,622 INFO [stdout] (Thread-12 (HornetQ-client-global-threads-589728564)) 8: WildFly 9 HornetQ Messaging High Available 12:16:24,629 INFO [stdout] (Thread-12 (HornetQ-client-global-threads-589728564)) 10: WildFly 9 HornetQ Messaging High Available 12:16:34,642 INFO [stdout] (Thread-12 (HornetQ-client-global-threads-589728564)) 5: WildFly 9 HornetQ Messaging High Available 12:16:44,653 INFO [stdout] (Thread-12 (HornetQ-client-global-threads-589728564)) 7: WildFly 9 HornetQ Messaging High Available 12:16:54,663 INFO [stdout] (Thread-12 (HornetQ-client-global-threads-589728564)) 9: WildFly 9 HornetQ Messaging High Available
-
它是如何工作的…
有很多事情需要解释。请注意,只有持久消息会被复制到备份服务器。因此,非持久消息不会进行故障转移。在实时配置中,我们添加了以下指令:
<backup>false</backup>
<failover-on-shutdown>true</failover-on-shutdown>
<check-for-live-server>true</check-for-live-server>
第一条非常明显;它定义了服务器将是实时服务器。failover-on-shutdown 表示即使在正常服务器关闭的情况下,服务器也会切换到备份服务器,就像我们在测试中所做的那样。
check-for-live-server 指令与备份服务器配置中的 allow-failback 一起使用:
<backup>true</backup>
<failover-on-shutdown>true</failover-on-shutdown>
<allow-failback>true</allow-failback>
基本上,当实时服务器恢复运行时,它会通过联系其他备份服务器并对其当前实时服务器发出关闭指令来检查是否发生了故障转移。
参见
-
如介绍中所述,有很多关于 HornetQ、JMS 和 MOM 的一般性话题可以讨论,但这些都超出了本书的范围。因此,我建议你阅读《HornetQ 消息开发人员指南》一书,作者 Piero Giacomelli,出版社 Packt Publishing,该书深入探讨了框架的开发方面和 JMS 规范。
-
此外,jboss.org 社区为我们所有人提供了关于 HornetQ 的信息,这些信息可在
hornetq.jboss.org免费获取。 -
最后但同样重要的是,JMS 规范可在
jcp.org/aboutJava/communityprocess/final/jsr343/index.html获取。
第十四章:使用 OpenShift 将 WildFly 迁移到云端
在本章中,你将学习以下食谱:
-
注册 OpenShift Online
-
为我们的第一次部署安装 WildFly 卡式盒
-
通过 SSH 控制你的 WildFly 服务器
-
将你的代码部署到 OpenShift Online
简介
在本章中,你将了解 OpenShift 是什么,以及如何通过将你的应用程序直接部署到云上的互联网来利用它。
首先,OpenShift 是一个平台即服务,代表著名的缩写PaaS。OpenShift 是 Red Hat 的 PaaS,你可以找到三个不同版本的它:
-
OpenShift Origin:这是一个 OpenShift 的免费开源版本。你可以在 GitHub 上找到其代码流,网址为
github.com/openshift/origin。 -
OpenShift Online:OpenShift Online (OSO) 是本章我们将使用的版本。目前你只需要知道它是一个免费的 PaaS,你可以在其中根据你喜欢的环境部署你的应用程序。我们将在本章后面讨论它。OpenShift Online 可在
www.openshift.com免费使用。 -
OpenShift Enterprise:OpenShift Enterprise (OSE) 是 Red Hat 在企业级别提供支持的版本。
如同书中《OpenShift 入门》所述,O'Reilly:
OSE 是为那些想要稳定性和开箱即用的生产就绪安装的客户设计的。由于稳定性至关重要,Origin 或 Online 中的一些功能可能不会在 Enterprise 版本中存在一两个版本。
无论如何,OpenShift 到底是什么?OpenShift 有很多东西,但根据我的经验,我认为“自助配置”这个词更适合它。开发人员和/或系统管理员可以访问系统,并即时为自己配置环境。资源、运行时、网络:所有你需要的东西都在一键之遥。
OpenShift 还能为你做另一件事,那就是自动扩展。如果你在 OpenShift 上部署应用程序并标记“自动扩展”选项,每当你的网站流量增加时,你的基础设施就会扩展;为了处理越来越多的请求,会添加更多的“节点”。然而,“自动”地,当你的应用程序流量减少时,OpenShift 会回收额外的资源。
在 OpenShift 中,你需要了解三个主要概念:
-
齿轮:你可以将其视为一个提供你所需资源的服务器,例如 RAM、CPU 和磁盘空间。在 OpenShift Online 中,你可以找到三种类型的齿轮:小型、中型和大型。它们都有 1GB 的磁盘空间。小型齿轮有 512MB 的 RAM,中型齿轮有 1GB 的 RAM,大型齿轮有 2GB 的 RAM。
小贴士
OpenShift Online 免费提供三个小型齿轮。如果你想购买更多或想获取不同类型的齿轮,你需要购买它们。
-
卡式:把它想象成一个插件。卡式是你的服务或应用程序的主机。例如,如果你有一个 Java 网络应用程序,你的卡式可能是 WildFly、JBoss AS 或 Tomcat。卡式有两种类型:
-
独立:这就像 WildFly、JBoss AS 和 Vert.x;基本上是应用程序运行在上的应用服务器
-
嵌入式:这是一个功能卡,例如数据库卡
-
-
应用:无需过多转身,你的应用程序是在 OpenShift 上运行的。OpenShift 为传入流量暴露了三个端口:22 号端口上的 SSH,80 号端口上的 HTTP,以及 443 号端口上的 HTTPS。
这基本上是你需要了解的基本知识,以便与 OpenShift Online 一起工作。一个加分项是对 Git(一种分布式版本控制软件)的基础有所了解,但我们需要在需要时再讨论。
让我们开始!
注册 OpenShift Online
在这个菜谱中,你将学习如何注册免费的 OpenShift Online 账户。这是一个热身菜谱,将使我们能够熟悉整体基础设施。
准备就绪
由于我们将在网上操作,你接下来 10 分钟只需要有互联网连接。
如何操作…
-
打开你选择的浏览器,将其指向
www.openshift.com。 -
一旦到达那里,点击左边的在线框(如图所示):
![如何操作…]()
OpenShift 主页
-
下一步是填写注册表单,提供有效的、可工作的电子邮件 ID 并选择一个密码,如下面的截图所示:
![如何操作…]()
OpenShift Online 注册表单
-
完成后,点击注册按钮并等待确认邮件,如下面的消息所警告的:
![如何操作…]()
确认邮件的消息警告
-
你可能需要等待几分钟,甚至检查一下垃圾邮件文件夹。邮件应该看起来像下面的截图:
![如何操作…]()
OpenShift Online 发送的电子邮件
-
点击标有验证您的账户的链接以激活您的账户。
-
你将被提示接受OpenShift 服务协议和Red Hat 门户使用条款,如下面的图片所示:
![如何操作…]()
OpenShift 服务协议和 Red Hat 门户使用条款接受表
-
如果你愿意,你也可以勾选注册 OpenShift 电子邮件并点击我接受按钮。
太好了,你现在可以部署到云端并向世界提供你的应用程序了!
如何工作…
这个过程非常直接且快捷。再次值得提一下的是你获得的账户类型。
一旦你登录到 OpenShift Online 账户,屏幕右上角你可以找到带有向下箭头的用户名。如果你点击它,你会看到一个菜单;点击第一个标有我的账户的项目。
它应该为你提供一个如下所示的页面:

OpenShift Online “我的账户”页面
在该页面上有四件事情需要注意:
-
在左侧是你的登录电子邮件和账户类型;总是检查一下为好。
-
屏幕右上角有一个灰色粗体标签,指定了免费计划。
-
在页面底部中央是信息,目前你正在使用三台(3)中的零台(0)齿轮,并且它们是免费的。同样,在同一位置,还有一个提示,你可以拥有最多 16 Gears,不能再多了!这是你在选择 OpenShift Online 和 OpenShift Enterprise(这是一个完全可定制的版本)时应该考虑的事情。
-
最后,但同样重要的是,是立即升级按钮,它为你提供了升级到青铜或银色计划所需的所有信息。
参考以下内容
-
关于 OpenShift Online 提供的所有服务和其工作原理,请参阅
www.openshift.com/walkthrough/how-it-works上的文档。 -
如果你正在寻找从开发者的角度来看的文档,你可以访问
developers.openshift.com。
为我们的第一次部署安装 WildFly 卡包
在这个菜谱中,你将学习如何使用你的 OpenShift Online 账户配置 WildFly 卡包。如果你还没有账户,请参阅本章的第一个菜谱。
准备工作
由于我们大部分时间都会在线操作,你需要有一个互联网连接。另一方面,我们本地只需要一个要部署的 Web 应用程序。
-
因此,请从我的 GitHub 账户下载名为
wildfly-cookbook-oso的整个仓库,网址为github.com/foogaro/wildfly-cookbook-oso.git。 -
你可以
git-clone项目或仅将其作为 ZIP 存档下载。无论哪种方式,将源放置在我们的~/WFC/github路径下。在那里你可以找到一个名为openshift-welcome的项目。要编译项目,请执行以下命令:$ cd ~/WFC/github/wildfly-cookbook-oso $ cd openshift-welcome $ mvn clean package -
在由 Maven 生成的
target文件夹中,你应该找到准备部署的openshift-welcome.war工件。
现在让我们真正开始吧!
如何操作…
-
首先,登录到你的 OSO 账户
openshift.redhat.com/app/console。 -
使用注册时选择的电子邮件和密码,如下面的图像所示:
![如何操作…]()
OpenShift Online 登录表单
-
一旦进入,首先要做的是创建一个应用程序,如下面的截图所示:
![如何操作…]()
OpenShift Online 欢迎页面
-
点击立即创建第一个应用程序链接。在下一页,选择 WildFly 卡包,如下面的图像所示:
![如何操作…]()
OpenShift Online 最常用卡包列表
注意
在编写本书时,WildFly 最新可用的稳定卡式版本是 8.2.0.Final。显然,这是我们提供应用程序的应用服务器。
-
接下来,我们需要配置我们的命名空间,所有我们的应用程序都将属于该命名空间,并配置应用程序名称;这两个配置项共同构成了公共 URL,如下图中所示:
![如何操作…]()
OpenShift Online—应用程序设置表单
-
在前面的表单中,您需要更改的是域名,它位于连字符符号(-)的右侧;我选择了
wildflycookbook。域名必须在rhcloud.com的 OpenShift Online 中是唯一的。因此,请选择您自己的域名,所有您的应用程序名称都将属于该域名。 -
对于这次第一次尝试,请保持所有默认设置不变,并点击创建应用程序按钮。完成所有必要的步骤可能需要几分钟时间——对我来说是 4 分钟。
-
然后您将被询问是否要更改代码:
![如何操作…]()
-
目前,我们只需跳过此步骤,并点击标签为现在不,继续的链接——我们将在本章的后面看到具有特定食谱的另一个选项。
-
下一页将是我们的总结页面,它将给我们提供所有管理 WildFly 实例的信息,例如部署我们的应用程序。
![如何操作…]()
-
在前面的截图中的绿色框中,描述了管理员用户的用户名和密码——基本上,这是一个绑定到
ManagementRealm并具有管理员权限的用户。顺便说一句,为了访问 Web 控制台,我们需要使用一个名为 RHC 的工具,它可以为我们转发所有请求到我们新 OpenShift Online 环境中的管理控制台的 9990 端口。还有一件事要做,那就是安装名为 RHC 的客户端工具,它将为我们提供一个命令行界面,以便从我们的 PC 上本地管理 OSO。
-
打开一个终端窗口并运行以下命令:
$ sudo yum -y install rubygems git $ sudo gem install rhc -
现在所有软件都已安装,我们需要设置 RHC 工具,如下所示:
$ rhc setup OpenShift Client Tools (RHC) Setup Wizard This wizard will help you upload your SSH keys, set your application namespace, and check that other programs like Git are properly installed. If you have your own OpenShift server, you can specify it now. Just hit enter to use the server for OpenShift Online: openshift.redhat.com. Enter the server hostname: |openshift.redhat.com| You can add more servers later using 'rhc server'. Login to openshift.redhat.com: luigi@foogaro.com Password: ******** OpenShift can create and store a token on disk which allows to you to access the server without using your password. The key is stored in your home directory and should be kept secret. You can delete the key at any time by running 'rhc logout'. Generate a token now? (yes|no) yes Generating an authorization token for this client ... lasts about 1 month Saving configuration to /home/luigi/.openshift/express.conf ... done No SSH keys were found. We will generate a pair of keys for you. Created: /home/luigi/.ssh/id_rsa.pub Your public SSH key must be uploaded to the OpenShift server to access code. Upload now? (yes|no) yes Since you do not have any keys associated with your OpenShift account, your new key will be uploaded as the 'default' key. Uploading key 'default' ... done Checking for git ... found git version 2.1.0 Checking common problems .. done Checking for a domain ... wildflycookbook Checking for applications ... found 1 openshiftwelcome http://openshiftwelcome-wildflycookbook.rhcloud.com/ You are using 1 of 3 total gears The following gear sizes are available to you: small Your client tools are now configured.如您所见,输出符合我的配置。您的也应该类似。
好的,让我们回到我们之前的地方。我们准备部署我们的应用程序,但我们需要访问管理控制台。
-
现在我们可以通过首先提供以下 RHC 命令来完成它:
$ rhc port-forward openshiftwelcome Checking available ports ... done Forwarding ports ... To connect to a service running on OpenShift, use the Local address Service Local OpenShift ------- -------------- ---- ---------------- java 127.0.0.1:3528 => 127.7.222.1:3528 java 127.0.0.1:8080 => 127.7.222.1:8080 java 127.0.0.1:9990 => 127.7.222.1:9990 Press CTRL-C to terminate port forwarding如最后一行所述,请保持此过程运行。
-
现在,让我们打开浏览器并将它指向 OpenShift Online 管理控制台,即
http://127.0.0.1:9990。 -
当需要时,在绿色框中插入我们在创建第一个应用程序时提供的凭据。
您现在应该看到以下页面:
![如何操作…]()
OpenShift Online 上的 WildFly 管理控制台
-
我们可以通过点击部署标签来部署我们的应用程序:
![如何操作…]()
部署管理
-
点击添加按钮,选择我们之前使用 maven 命令编译的
openshift-welcome.war工件。一旦我们选择了它,按照以下方式启用并确认:![如何做…]()
部署
openshift-welcome.war应用程序 -
一旦我们成功部署了应用程序,让我们通过在
openshiftwelcome-wildflycookbook.rhcloud.com打开它来尝试它。这是我们首次在 OpenShift Online 上运行的 WildFly 应用:
![如何做…]()
OpenShift-welcome 应用程序启动并运行
该页面设计得尽可能全面;我还使用了粗体字体风格!
如何工作…
好吧,这里由 OpenShift Online 完成了艰难的工作。我们本质上所做的唯一一件事是安装 Ruby 编程语言环境,以便能够安装和运行 RHC 工具,这是用于管理 OpenShift Online 环境的客户端工具。
让我们回顾一下在云中部署我们的第一个应用程序期间我们所做的工作。
首先,我们选择了卡式盒。正如我们所见,有许多卡式盒可供我们的齿轮使用:WildFly、JBoss AS、Tomcat、Drupal、WordPress、Node.js 和MongoDB Express Angular Node(MEAN)。有很多,你可以在 OpenShift Online 网站上找到它们,网址为openshift.redhat.com/app/console/application_types。
一旦我们选择了 WildFly 卡式盒,平台会要求我们提供用于发布应用程序的公共 URL,如下图所示:

发布应用程序的公共 URL
URL,除了后缀.rhcloud.com之外,是由应用程序名称和命名空间组成的。最后一个基本上是你的域名,所有应用程序都驻留在那里。记住,它必须是唯一的!在我的情况下,我选择了命名空间wildflycookbook。
实际上,下次你创建应用程序时,你将只被要求提供应用程序名称,如下所示:

定义要用于公共 URL 的应用程序名称
仍然停留在同一页面上,我们还可以选择另一个选项——扩展,如下图所示:

OpenShift Online 中的扩展选项
这个选项将使我们能够自动扩展应用程序。简而言之,如介绍中已提到的,当传入流量增加时,OpenShift 将创建额外的 WildFly 齿轮(只要它们在你的计划中可用)和一个均衡器齿轮,以实现水平扩展。
下一个选项将是区域,即服务器将运行您的齿轮的位置:美国、欧洲或亚洲。
任何大小的所有齿轮都可以在美国运行;只有生产齿轮可以部署到欧洲地区(small.highcpu、medium 和 large)。亚洲地区保留用于专用节点服务。
因此,在免费计划下,我们只能选择美国地区,这没问题。
如你所猜测的地区名称(aws-us-east-1)所示,OpenShift 依赖于 Amazon AWS 来创建服务器。由于 Amazon 的“只为你使用的付费”政策,每当你的网站流量减少时,OpenShift 就会收回额外的资源。
除了配置设置之外,我还通过WEB-INF/jboss-web.xml JBoss 特定描述符文件将一个设置放入了应用程序中,它看起来像这样:
<jboss-web>
<context-root>/</context-root>
</jboss-web>
上述配置将应用程序的上下文根设置为/,因此我们可以通过只提供 URL openshiftwelcome-wildflycookbook.rhcloud.com 来访问它,而不需要任何额外的上下文路径。
还有更多…
这个菜谱有点长,你可能已经以不同的方式完成了它。如果是这样,你可能遇到过这种情况:

空闲状态中的应用程序
这基本上意味着你的 WildFly 实例已经停止。你的应用程序将不会收到流量,甚至 WildFly 管理控制台的访问也将无法工作:
$ rhc port-forward openshiftwelcome
Checking available ports ... none
There are no available ports to forward for this application. Your application may be stopped or idled.
前进唯一的方法是重新启动应用程序。
小贴士
请记住,在 48 小时的不活跃状态或完全没有流量后,OpenShift Online 会将你的应用程序恢复到空闲状态。为了防止你的应用程序进入空闲状态,你可以设置一个类型的 cron 作业,每天对你的网站进行一次简单的 HTTP GET 请求。
参见
-
关于 OpenShift Online 提供的服务及其工作原理,请参阅
www.openshift.com/walkthrough/how-it-works文档。 -
如果你正在寻找从开发者的角度的文档,请访问
developers.openshift.com。
通过 SSH 控制你的 WildFly 服务器
在这个菜谱中,你将学习如何访问运行 WildFly 的服务器。访问服务器会让你感到一种安心,因为我想,这是我们通常工作的方式。在那里,你可以看到 WildFly 进程、其日志、其目录结构等等。
准备工作
在你能够有效地完成这个菜谱之前,你应该有一个活跃的 OpenShift Online 账户以及一个应用程序。如果你需要,你可以按照第一个菜谱进行注册过程,并按照本章的为首次部署安装 WildFly 卡顿菜谱创建你的第一个应用程序。
在这个菜谱中,我将使用在创建应用程序 openshiftwelcome 时生成的环境;你的可能不同,但概念上应该是相同的。
如何做…
-
首先,让我们登录到你的 OSO 账户,在
openshift.redhat.com/app/console。 -
然后输入你在注册过程中选择的用户名和密码。
-
登录后,选择你创建的应用程序,或者根据第二个菜谱创建一个新的应用程序。我将选择我的
openshiftwelcome应用程序。以下是我的申请详情:
![如何操作…]()
申请详情
在前面的屏幕截图中,右侧有两个名为源代码和远程访问的部分。
-
在远程访问部分,点击想要登录到你的应用程序?标签。它描述了如何登录到托管你的应用程序的服务器,显然还包括它的卡式盒。
-
打开一个终端窗口,复制粘贴页面提供的
ssh命令。我的如下所示:$ ssh 54c0350afcf933691e0000dc@openshiftwelcome-wildflycookbook.rhcloud.com ********************************************************************* You are accessing a service that is for use only by authorized users. If you do not have authorization, discontinue use at once. Any use of the services is subject to the applicable terms of the agreement which can be found at: https://www.openshift.com/legal ********************************************************************* Welcome to OpenShift shell This shell will assist you in managing OpenShift applications. !!! IMPORTANT !!! IMPORTANT !!! IMPORTANT !!! Shell access is quite powerful and it is possible for you to accidentally damage your application. Proceed with care! If worse comes to worst, destroy your application with "rhc app delete" and recreate it !!! IMPORTANT !!! IMPORTANT !!! IMPORTANT !!! Type "help" for more info. [openshiftwelcome-wildflycookbook.rhcloud.com 54c0350afcf933691e0000dc]\>到那里后,你可以查看 WildFly 日志并查看你想要查看的一切。
注意
小心,如果你删除了不应该删除的东西,你需要重新创建你的应用程序。
让我们看看 WildFly 的日志。
-
执行以下操作:
[openshiftwelcome-wildflycookbook.rhcloud.com 54c0350afcf933691e0000dc]\> tail -f wildfly/standalone/log/server.log 2015-01-23 11:17:20,864 INFO [org.jboss.as.server] (XNIO-1 task-4) JBAS018559: Deployed "openshift-welcome.war" (runtime-name : "openshift-welcome.war") 2015-01-23 11:20:21,227 INFO [org.wildfly.extension.undertow] (MSC service thread 1-2) JBAS017535: Unregistered web context: /openshift-welcome 2015-01-23 11:20:21,379 INFO [org.hibernate.validator.internal.util.Version] (MSC service thread 1-1) HV000001: Hibernate Validator 5.1.3.Final 2015-01-23 11:20:21,590 INFO [org.jboss.as.server.deployment] (MSC service thread 1-2) JBAS015877: Stopped deployment openshift-welcome.war (runtime-name: openshift-welcome.war) in 386ms 2015-01-23 11:20:21,676 INFO [org.jboss.as.server] (XNIO-1 task-9) JBAS018558: Undeployed "openshift-welcome.war" (runtime-name: "openshift-welcome.war") 2015-01-23 11:20:21,677 INFO [org.jboss.as.repository] (XNIO-1 task-9) JBAS014901: Content removed from location /var/lib/openshift/54c0350afcf933691e0000dc/wildfly/standalone/data/content/6a/2481425c24beff9c840891145f6ea0ae5d1058/content 2015-01-23 11:20:34,820 INFO [org.jboss.as.repository] (XNIO-1 task-4) JBAS014900: Content added at location /var/lib/openshift/54c0350afcf933691e0000dc/wildfly/standalone/data/content/1b/65c52cbd69dc9f2fd0dbab59f1fb82c3c05038/content 2015-01-23 11:20:35,195 INFO [org.jboss.as.server.deployment] (MSC service thread 1-2) JBAS015876: Starting deployment of "openshift-welcome.war" (runtime-name: "openshift-welcome.war") 2015-01-23 11:20:35,305 INFO [org.wildfly.extension.undertow] (MSC service thread 1-2) JBAS017534: Registered web context: / 2015-01-23 11:20:35,418 INFO [org.jboss.as.server] (XNIO-1 task-8) JBAS018559: Deployed "openshift-welcome.war" (runtime-name : "openshift-welcome.war") -
显然,你不仅限于查看日志;你可以查看过程,如下所示:
[openshiftwelcome-wildflycookbook.rhcloud.com 54c0350afcf933691e0000dc]\> ps -efa | grep java | grep -v grep 4028 94579 94303 0 Jan23 ? 00:08:07 /var/lib/openshift/54c0350afcf933691e0000dc/wildfly/usr/lib/jvm/jdk1.8.0_05/bin/java -D[Standalone] -server -Xmx256m -XX:MaxPermSize=102m -XX:+AggressiveOpts -Dorg.apache.tomcat.util.LOW_MEMORY=true -DOPENSHIFT_APP_UUID=54c0350afcf933691e0000dc -Dorg.jboss.resolver.warning=true -Djava.net.preferIPv4Stack=true -Dfile.encoding=UTF-8 -Djboss.node.name=openshiftwelcome-wildflycookbook.rhcloud.com -Djgroups.bind_addr= -Dorg.apache.coyote.http11.Http11Protocol.COMPRESSION=on -Dorg.jboss.boot.log.file=/var/lib/openshift/54c0350afcf933691e0000dc/wildfly//standalone/log/server.log -Dlogging.configuration=file:/var/lib/openshift/54c0350afcf933691e0000dc/wildfly//standalone/configuration/logging.properties -jar /var/lib/openshift/54c0350afcf933691e0000dc/wildfly//jboss-modules.jar -mp /var/lib/openshift/54c0350afcf933691e0000dc/app-root/runtime/repo//.openshift/config/modules:/var/lib/openshift/54c0350afcf933691e0000dc/wildfly//modules org.jboss.as.standalone -Djboss.home.dir=/var/lib/openshift/54c0350afcf933691e0000dc/wildfly/ -Djboss.server.base.dir=/var/lib/openshift/54c0350afcf933691e0000dc/wildfly//standalone -b 127.7.222.1 -bmanagement=127.7.222.1 -
或者,你可以通过执行
htop命令来查看服务器资源:![如何操作…]()
请记住,只要你的用户(标识应用程序的哈希值;我的为54c0350afcf933691e0000dc)有权限,你就可以查看和控制一切。
它是如何工作的…
每次你创建一个应用程序时,实际上你是在创建一个环境,以及一个允许你的应用程序运行的卡式盒。OpenShift Online 为你创建一切,并为整个过程分配一个哈希值。这个哈希值将成为你登录服务器的用户名,它也标识了你的应用程序。OpenShift 创建事物的其余过程超出了本书的范围。
如果你想了解更多关于内部工作原理的信息,我建议你从这本书开始:Learning OpenShift,Grant Shipley,Packt Publishing。
相关内容
- 请务必参考官方文档,在这种情况下,可以在
docs.openshift.org/找到。
将你的代码部署到 OpenShift Online
在这个菜谱中,你将学习如何在源代码更新时重新部署应用程序。OpenShift Online 会自动为你完成这项工作,每次将提交应用到你的代码时,都会触发编译和部署。
准备工作
-
首先,我们需要一个 GitHub 上的项目,我们可以用它来进行测试。因此,我在我的 GitHub 仓库中创建了一个名为
openshiftcoding-wildflycookbook的项目,可在github.com/foogaro/openshiftcoding-wildflycookbook找到。 -
如果您在 GitHub 上没有账户,这可能是一个注册账户的好理由。迟早您都会需要它。为了快速开始,请访问 GitHub 网站
github.com并注册。注册过程完成后,您将准备好创建一个仓库,该仓库将托管您应用程序的源代码。如果您没有应用程序,您可以借用我的。 -
不管怎样,这次您不需要下载或
git-clone我的 GitHub 仓库到本地,因为它将被用于为下一个 OpenShift 应用程序提供数据。但我们需要安装git客户端工具。如果您使用的是类似 Red Hat 的操作系统,请运行以下命令:$ sudo yum -y install git -
完成后,您可以通过以下命令检查是否已成功安装所有内容:
$ git version git version 2.1.0
好的,现在我们准备开始了!
如何操作…
-
首先,您需要访问以下地址的 OpenShift Online 账户
openshift.redhat.com/app/console。 -
使用注册时选择的电子邮件和密码,如下面的截图所示:
![如何操作…]()
OpenShift Online 登录表单
小贴士
如果您还没有 OpenShift Online 账户,请遵循本章第一道菜谱中描述的注册过程。
-
登录后,在主页上,点击添加应用程序…按钮创建一个新的应用程序:
![如何操作…]()
添加新应用程序的 OpenShift Online 初始控制台
-
接下来,我们需要选择 WildFly 卡顿。由于有数十种卡顿,我们可以通过提供标准筛选器来搜索它,如下面的截图所示:
![如何操作…]()
通过筛选选择 WildFly 卡顿
-
之后,选择卡顿并填写以下详细信息的表单:
![如何操作…]()
设置应用程序详情
在我的情况下,我把我应用程序命名为
openshiftcoding。这次我指定了我的 GitHub 仓库,其中包含我的应用程序,即github.com/foogaro/openshiftcoding-wildflycookbook.git。您需要指定您的。 -
通过保留其他默认值并点击创建应用程序按钮完成表单。完成可能需要几分钟。
-
一旦过程完成,您应该会看到一个像下面的页面:
![如何操作…]()
新创建的应用程序概览
如前图所示,有一个标记为制作代码更改的部分,它描述了我们需要做什么来修改我们应用程序的源代码,并在我们修改代码时自动更新。
因此,打开一个终端窗口并输入git clone命令,指定你的 OpenShift Online 应用程序仓库——它不是 GitHub 上的那个,但会被克隆到 OpenShift 为你创建的服务器上。
这些是我现在输入的命令:
$ cd ~/WFC/github
$ mkdir git-openshift
$ cd git-openshift
$ git clone ssh://54c422b2fcf9338699000146@openshiftcoding-wildflycookbook.rhcloud.com/~/git/openshiftcoding.git/
Cloning into 'openshiftcoding'...
Warning: Permanently added the RSA host key for IP address '54.234.243.133' to the list of known hosts.
remote: Counting objects: 24, done.
remote: Compressing objects: 100% (18/18), done.
remote: Total 24 (delta 4), reused 24 (delta 4)
Receiving objects: 100% (24/24), done.
Resolving deltas: 100% (4/4), done.
Checking connectivity... done.
我们现在可以更改源代码,提交它,并查看 OpenShift 直接编译和部署我们的应用程序到我们的 WildFly 实例。为了看到这一切,我们需要查看日志。
-
打开一个终端窗口并输入以下命令:
$ ssh 54c422b2fcf9338699000146@openshiftcoding-wildflycookbook.rhcloud.com ********************************************************************* You are accessing a service that is for use only by authorized users. If you do not have authorization, discontinue use at once. Any use of the services is subject to the applicable terms of the agreement which can be found at: https://www.openshift.com/legal ********************************************************************* Welcome to OpenShift shell This shell will assist you in managing OpenShift applications. !!! IMPORTANT !!! IMPORTANT !!! IMPORTANT !!! Shell access is quite powerful and it is possible for you to accidentally damage your application. Proceed with care! If worse comes to worst, destroy your application with "rhc app delete" and recreate it !!! IMPORTANT !!! IMPORTANT !!! IMPORTANT !!! Type "help" for more info. [openshiftcoding-wildflycookbook.rhcloud.com 54c422b2fcf9338699000146]\> tail -f wildfly/standalone/log/server.log 2015-01-24 17:56:23,264 INFO [org.jboss.as.connector.deployment] (MSC service thread 1-5) JBAS010406: Registered connection factory java:/JmsXA 2015-01-24 17:56:23,337 INFO [org.hornetq.ra] (MSC service thread 1-5) HornetQ resource adaptor started 2015-01-24 17:56:23,338 INFO [org.jboss.as.connector.services.resourceadapters.ResourceAdapterActivatorService$ResourceAdapterActivator] (MSC service thread 1-5) IJ020002: Deployed: file://RaActivatorhornetq-ra 2015-01-24 17:56:23,355 INFO [org.jboss.as.messaging] (MSC service thread 1-7) JBAS011601: Bound messaging object to jndi name java:jboss/DefaultJMSConnectionFactory 2015-01-24 17:56:23,355 INFO [org.jboss.as.connector.deployment] (MSC service thread 1-5) JBAS010401: Bound JCA ConnectionFactory [java:/JmsXA] 2015-01-24 17:56:23,835 INFO [org.wildfly.extension.undertow] (MSC service thread 1-4) JBAS017534: Registered web context: / 2015-01-24 17:56:24,069 INFO [org.jboss.as.server] (ServerService Thread Pool -- 32) JBAS018559: Deployed "openshift-coding.war" (runtime-name : "openshift-coding.war") 2015-01-24 17:56:24,199 INFO [org.jboss.as] (Controller Boot Thread) JBAS015961: Http management interface listening on http://127.7.132.129:9990/management 2015-01-24 17:56:24,201 INFO [org.jboss.as] (Controller Boot Thread) JBAS015951: Admin console listening on http://127.7.132.129:9990 2015-01-24 17:56:24,201 INFO [org.jboss.as] (Controller Boot Thread) JBAS015874: WildFly 8.2.0.Final "Tweek" started in 10428ms - Started 294 of 425 services (178 services are lazy, passive or on-demand) -
如您从前面强调的日志中看到的,我们的应用程序已经在那里;实际上,如果我们打开创建应用程序时指定的公共 URL,我们可以看到它正在工作,如下所示:
![如何操作…]()
应用程序需要更新
好的,让我们更新源代码。在我的情况下,我只有一个 JSP,其代码如下:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xml:lang="en" lang="en"> <head> <title>WildFly::Cookbook</title> </head> <body> <b>We need to change some code here!</b> </body> </html> -
让我们在文本中添加一些颜色,如下所示:
<b style="color:red;">We need to change some code here!</b> -
现在,我们可以从第一个终端窗口(我们从 OpenShift 克隆 Git 仓库的地方)更新我们的代码到 Git 仓库,如下所示:
$ cd ~/WFC/github/git-openshift/openshiftcoding $ git status On branch master Your branch is up-to-date with 'origin/master'. Changes not staged for commit: (use "git add <file>..." to update what will be committed) (use "git checkout -- <file>..." to discard changes in working directory) modified: src/main/webapp/index.jsp no changes added to commit (use "git add" and/or "git commit -a") $ git add . $ git commit -m "Added red color to the text" [master c89467c] Added red color to the text 1 file changed, 1 insertion(+), 1 deletion(-) -
现在,我们需要推送一切。在这样做之前,让我们获取另一个带有可见 WildFly 日志的终端。现在我们可以进行魔法操作…
$ git push warning: push.default is unset; its implicit value has changed in Git 2.0 from 'matching' to 'simple'. To squelch this message and maintain the traditional behavior, use: git config --global push.default matching To squelch this message and adopt the new behavior now, use: git config --global push.default simple When push.default is set to 'matching', git will push local branches to the remote branches that already exist with the same name. Since Git 2.0, Git defaults to the more conservative 'simple' behavior, which only pushes the current branch to the corresponding remote branch that 'git pull' uses to update the current branch. See 'git help config' and search for 'push.default' for further information. (the 'simple' mode was introduced in Git 1.7.11\. Use the similar mode 'current' instead of 'simple' if you sometimes use older versions of Git) Counting objects: 6, done. Delta compression using up to 8 threads. Compressing objects: 100% (5/5), done. Writing objects: 100% (6/6), 517 bytes | 0 bytes/s, done. Total 6 (delta 2), reused 0 (delta 0) remote: Stopping wildfly cart remote: Sending SIGTERM to wildfly:455867 ... remote: Building git ref 'master', commit c89467c remote: Found pom.xml... attempting to build with 'mvn -e clean package -Popenshift -DskipTests' remote: Apache Maven 3.0.4 (r1232336; 2012-12-18 14:36:37-0500) remote: Maven home: /usr/share/java/apache-maven-3.0.4 remote: Java version: 1.8.0_05, vendor: Oracle Corporation remote: Java home: /var/lib/openshift/54c422b2fcf9338699000146/wildfly/usr/lib/jvm/jdk1.8.0_05/jre remote: Default locale: en_US, platform encoding: ANSI_X3.4-1968 remote: OS name: "linux", version: "2.6.32-504.3.3.el6.x86_64", arch: "i386", family: "unix" remote: [INFO] Scanning for projects... remote: [INFO] remote: [INFO] ------------------------------------------------------------------------ remote: [INFO] Building openshift-coding 1.0 remote: [INFO] ------------------------------------------------------------------------ remote: [INFO] remote: [INFO] --- maven-clean-plugin:2.4.1:clean (default-clean) @ openshift-coding --- remote: [INFO] remote: [INFO] --- maven-resources-plugin:2.5:resources (default-resources) @ openshift-coding --- remote: [debug] execute contextualize remote: [INFO] Using 'UTF-8' encoding to copy filtered resources. remote: [INFO] Copying 1 resource remote: [INFO] remote: [INFO] --- maven-compiler-plugin:2.3.2:compile (default-compile) @ openshift-coding --- remote: [INFO] No sources to compile remote: [INFO] remote: [INFO] --- maven-resources-plugin:2.5:testResources (default-testResources) @ openshift-coding --- remote: [debug] execute contextualize remote: [INFO] Using 'UTF-8' encoding to copy filtered resources. remote: [INFO] skip non existing resourceDirectory /var/lib/openshift/54c422b2fcf9338699000146/app-root/runtime/repo/src/test/resources remote: [INFO] remote: [INFO] --- maven-compiler-plugin:2.3.2:testCompile (default-testCompile) @ openshift-coding --- remote: [INFO] No sources to compile remote: [INFO] remote: [INFO] --- maven-surefire-plugin:2.10:test (default-test) @ openshift-coding --- remote: [INFO] Tests are skipped. remote: [INFO] remote: [INFO] --- maven-war-plugin:2.4:war (default-war) @ openshift-coding --- remote: [INFO] Packaging webapp remote: [INFO] Assembling webapp [openshift-coding] in [/var/lib/openshift/54c422b2fcf9338699000146/app-root/runtime/repo/target/openshift-coding] remote: [INFO] Processing war project remote: [INFO] Copying webapp resources [/var/lib/openshift/54c422b2fcf9338699000146/app-root/runtime/repo/src/main/webapp] remote: [INFO] Webapp assembled in [79 msecs] remote: [INFO] Building war: /var/lib/openshift/54c422b2fcf9338699000146/app-root/runtime/repo/deployments/openshift-coding.war remote: [INFO] ------------------------------------------------------------------------ remote: [INFO] BUILD SUCCESS remote: [INFO] ------------------------------------------------------------------------ remote: [INFO] Total time: 5.855s remote: [INFO] Finished at: Sat Jan 24 18:19:48 EST 2015 remote: [INFO] Final Memory: 7M/79M remote: [INFO] ------------------------------------------------------------------------ remote: Preparing build for deployment remote: Deployment id is 9aac8674 remote: Activating deployment remote: Deploying WildFly remote: Starting wildfly cart remote: Found 127.7.132.129:8080 listening port remote: Found 127.7.132.129:9990 listening port remote: /var/lib/openshift/54c422b2fcf9338699000146/wildfly/standalone/deployments /var/lib/openshift/54c422b2fcf9338699000146/wildfly remote: /var/lib/openshift/54c422b2fcf9338699000146/wildfly remote: CLIENT_MESSAGE: Artifacts deployed: ./openshift-coding.war remote: ------------------------- remote: Git Post-Receive Result: success remote: Activation status: success remote: Deployment completed with status: success To ssh://54c422b2fcf9338699000146@openshiftcoding-wildflycookbook.rhcloud.com/~/git/openshiftcoding.git/ f2eba48..c89467c master -> master如果你熟悉
git,你会知道这次推送比预期的要长一些,但它做了很多事情:-
停止了我们的 WildFly
-
它更新了代码
-
它执行了 maven 编译,将我们的应用程序打包为
war -
它将我们的新应用程序复制到了 WildFly 的
deployments文件夹 -
启动了 WildFly
-
-
如果你已经查看了 WildFly 日志,你应该已经注意到了以下条目:
... 2015-01-24 18:19:36,758 INFO [org.jboss.as] (MSC service thread 1-1) JBAS015950: WildFly 8.2.0.Final "Tweek" stopped in 1273ms ... 2015-01-24 18:20:13,374 INFO [org.wildfly.extension.undertow] (MSC service thread 1-5) JBAS017534: Registered web context: / 2015-01-24 18:20:13,673 INFO [org.jboss.as.server] (ServerService Thread Pool -- 32) JBAS018559: Deployed "openshift-coding.war" (runtime-name : "openshift-coding.war") 2015-01-24 18:20:13,871 INFO [org.jboss.as] (Controller Boot Thread) JBAS015961: Http management interface listening on http://127.7.132.129:9990/management 2015-01-24 18:20:13,879 INFO [org.jboss.as] (Controller Boot Thread) JBAS015951: Admin console listening on http://127.7.132.129:9990 2015-01-24 18:20:13,879 INFO [org.jboss.as] (Controller Boot Thread) JBAS015874: WildFly 8.2.0.Final "Tweek" started in 23656ms - Started 294 of 425 services (178 services are lazy, passive or on-demand) -
让我们看看一切是否正常工作。打开浏览器并访问
openshiftcoding-wildflycookbook.rhcloud.com。你应该看到如下页面:
![如何操作…]()
应用程序通过 git push 更新
太好了,它成功了!
它是如何工作的…
OpenShift Online 在幕后隐藏并做了很多事情,使我们的生活更加轻松,容易得多。我们实际上在开发方面所做的只是很小的一部分。
为了让 OpenShift Online 轻松编译、打包和部署我们的应用程序,我们需要指导它如何编译源代码、如何打包它以及部署到何处。我们可以通过 maven 项目文件pom.xml给出这些指令。
以下是在openshiftcoding-wildflycookbook项目中的完整pom.xml文件(也可在github.com/foogaro/openshiftcoding-wildflycookbook/blob/master/pom.xml找到):
<project
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.packtpub.wildfly-cookbook</groupId>
<artifactId>openshift-coding</artifactId>
<packaging>war</packaging>
<version>1.0</version>
<name>openshift-coding</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<profiles>
<profile>
<id>openshift</id>
<build>
<finalName>${project.name}</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>2.4</version>
<configuration>
<failOnMissingWebXml>false</failOnMissingWebXml>
<outputDirectory>deployments</outputDirectory>
<warName>${project.name}</warName>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>
这里的关键是名为openshift的配置文件。默认情况下,OSO 使用 Maven 编译源代码,并要求使用openshift配置文件。它找到的配置文件将与其设置一起使用;否则,代码将被编译和打包,但 OSO 将不知道部署到何处。
我提供的openshift配置文件,定义了(使用build标签)在 Maven 构建阶段要使用的设置。特别是,我使用了标准的maven-war-plugin并将outputDirectory设置为deployments,这是 WildFly 使用的目录。
参见
从开发者的角度来看,我建议您参考以下网站上的官方文档:
第十五章:使用 Docker 与 WildFly
在本章中,您将学习以下食谱:
-
设置 Docker 环境
-
在 Docker 中运行 WildFly
-
在 Docker 外记录 WildFly
-
在 Docker 中使用不同的 Docker 容器以域模式运行 WildFly
简介
在本章中,我们将开始学习 Docker 的基础知识,以及您如何通过部署应用程序及其环境来利用它。
Docker 是一个开源平台,用于开发、运输和运行应用程序。Docker 的基本思想是消除当您将应用程序(实际上整个运行环境)从环境到环境进行推广时出现的问题;也就是说,从开发到测试,到预生产,到质量,直到生产。涉及到的变量如此之多,即使你尽力而为,在传统的软件生命周期中也可能出现问题。
使用 Docker,您可以在每个阶段复制您的整个环境。单词“复制”可能不足以很好地解释这个概念;“重新提交”可能更好。
Docker 由以下组成:
-
镜像:Docker 镜像就像一个模板。假设您有一个包含 Fedora、Java 8、WildFly 和已安装应用程序的镜像。镜像可以通过名为
Dockerfile的描述文件下载或构建。从镜像中,您可以运行容器。 -
仓库:这些是存储镜像的地方。可以有私有和公共仓库,例如 Docker Hub (
hub.docker.com). -
容器:它们实际上是 Docker 的运行组件。基本上,它是一个 Docker 镜像的运行实例。容器可以被启动、停止、删除等。每个容器都与运行它的主机以及其他容器隔离。
顺便问一下,我们如何通过 Docker 在各个阶段推广我们的应用程序时避免出现任何坏惊喜的结果?将 Docker 视为创建可插拔层堆栈的工具。第一层是操作系统层,然后是您的环境层(可能是您的特定 Java 运行时版本),然后是您的应用程序层——您可以根据需要/想要拥有尽可能多的层。
一旦您的堆栈全部填满,您得到的就是您的 Docker 镜像,它已准备好提交到仓库。之后,您抓取您的镜像并在生产硬件上安装该精确镜像。然后您得到的是当一切运行顺利时的东西——希望您能理解这个意思。
为了更好地理解 Docker 是什么,我强烈建议您阅读以下 URLs 上的默认文档和用户指南:
在本章中,我们将主要使用适用于不同操作系统的 Docker 客户端,这是与 Docker 守护进程交互的工具。有如此多的设置和功能可以讨论,以至于需要另一本书来详述。因此,我将专注于安装客户端工具,并解释我们将在我们的食谱中使用的命令和参数,即在一些基本的 WildFly 场景中。
设置 Docker 环境
在这个食谱中,你将学习如何安装 Docker 并熟悉基本命令。你将看到如何基于 Fedora 创建和构建镜像,以及如何运行它。
准备工作
为了下载和安装 Docker 及其依赖项,我们需要访问互联网。
如何做…
-
要在 Fedora 上安装 Docker,请执行以下命令:
$ sudo yum -y remove docker $ sudo yum -y install docker-io安装应该看起来像以下截图:
![如何做…]()
-
好的,现在 Docker 已经安装好了,让我们来运行它:
$ sudo systemctl start docker -
如果你想在启动时启动 Docker,请使用以下命令:
$ sudo systemctl enable docker Created symlink from /etc/systemd/system/multi-user.target.wants/docker.service to /usr/lib/systemd/system/docker.service. -
此外,确保你的用户属于
docker组,通过执行以下命令:$ groups luigi wheel docker -
如果
docker组未列出,请使用以下命令添加:$ sudo usermod -a -G docker luigi显然,将用户
luigi替换为你自己的用户名。 -
通过以下命令检查 Docker 的版本:
$ docker version Client version: 1.6.0 Client API version: 1.18 Go version (client): go1.4.2 Git commit (client): 350a636/1.6.0 OS/Arch (client): linux/amd64 Server version: 1.6.0 Server API version: 1.18 Go version (server): go1.4.2 Git commit (server): 350a636/1.6.0太好了,我们现在准备好将任何东西 docker 化!
-
要这样做,首先我们需要检查是否有任何可用的镜像,如下所示:
$ docker images REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE -
由于没有可用的镜像,我们需要通过以下命令查找它:
$ docker search fedora NAME DESCRIPTION STARS OFFICIAL AUTOMATED fedora Official Fedora 21 base image and semi-off... 128 [OK] fedora/apache 28 [OK] fedora/couchdb 25 [OK] fedora/mariadb 22 [OK] fedora/memcached 19 [OK] fedora/earthquake 17 [OK] fedora/ssh 16 [OK] ... -
我们想要的 Docker 镜像是官方的 Fedora 21。我们可以按照以下方式下载它:
$ docker pull fedora:21 fedora:latest: The image you are pulling has been verified 511136ea3c5a: Pull complete 00a0c78eeb6d: Pull complete 834629358fe2: Downloading [> ] 2.681 MB/250 MB 3m20s -
当所有拉取完成时,我们得到以下输出:
$ docker pull fedora:21 fedora:latest: The image you are pulling has been verified 511136ea3c5a: Pull complete 00a0c78eeb6d: Pull complete 834629358fe2: Pull complete Status: Downloaded newer image for fedora:latest -
我们现在可以使用我们的第一个 Fedora Docker 镜像,通过执行以下命令:
$ docker run -it --rm fedora /bin/bash bash-4.3# ls -la total 60 drwxr-xr-x. 18 root root 4096 Jan 29 09:52 . drwxr-xr-x. 18 root root 4096 Jan 29 09:52 .. -rwxr-xr-x. 1 root root 0 Jan 29 09:52 .dockerenv -rwxr-xr-x. 1 root root 0 Jan 29 09:52 .dockerinit lrwxrwxrwx. 1 root root 7 Aug 16 09:24 bin -> usr/bin dr-xr-xr-x. 3 root root 4096 Dec 3 00:56 boot drwxr-xr-x. 5 root root 380 Jan 29 09:52 dev drwxr-xr-x. 47 root root 4096 Jan 29 09:52 etc drwxr-xr-x. 2 root root 4096 Aug 16 09:24 home lrwxrwxrwx. 1 root root 7 Aug 16 09:24 lib -> usr/lib lrwxrwxrwx. 1 root root 9 Aug 16 09:24 lib64 -> usr/lib64 drwx------. 2 root root 4096 Dec 3 00:56 lost+found drwxr-xr-x. 2 root root 4096 Aug 16 09:24 media drwxr-xr-x. 2 root root 4096 Aug 16 09:24 mnt drwxr-xr-x. 2 root root 4096 Aug 16 09:24 opt dr-xr-xr-x. 233 root root 0 Jan 29 09:52 proc dr-xr-x---. 2 root root 4096 Dec 3 00:58 root drwxr-xr-x. 2 root root 4096 Dec 3 00:56 run lrwxrwxrwx. 1 root root 8 Aug 16 09:24 sbin -> usr/sbin drwxr-xr-x. 2 root root 4096 Aug 16 09:24 srv dr-xr-xr-x. 13 root root 0 Jan 19 06:57 sys drwxrwxrwt. 7 root root 4096 Dec 3 00:58 tmp drwxr-xr-x. 12 root root 4096 Dec 3 00:56 usr drwxr-xr-x. 18 root root 4096 Dec 3 00:56 var bash-4.3# env HOSTNAME=141b250d4361 TERM=xterm PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin PWD=/ SHLVL=1 HOME=/root _=/usr/bin/env bash-4.3#
如何工作…
使用 Fedora 启动容器需要多长时间?毫秒?在传统的虚拟化环境中启动一个 Fedora 虚拟机需要多长时间?数十秒?整体安装又是如何?使用 Docker,一切都会变得极其快速。
注意
我们最终安装了 Docker 工具并下载了最新的 Fedora 镜像(在撰写本书时为版本 21)。我们可以将其用作我们下一个食谱,即在 Docker 中运行 WildFly的基础。
当你第一次安装 Docker 时,没有可用的镜像,你需要从 Docker 注册库中获取它们。默认注册库是线上的 Docker Hub,在那里你也可以免费注册并拥有自己的空间来分享你的 Docker 镜像。
在你能够拉取(即下载)Docker 镜像之前,你需要至少知道它的名称。通过执行docker search IMAGE_NAME命令,你实际上是在过滤所有存储在 DockerHub 中且名称或描述中包含指定过滤器的镜像。
一旦你拉取了所选的镜像,你可以使用它来运行容器,如下命令所示:
$ docker run -i -t fedora /bin/bash
bash-4.3#
在前面的命令中,我们正在使用 Docker 客户端工具与 Docker 守护进程通信,该守护进程在我们的系统中是活跃并监听的。
具体来说,我们指示 Docker 基于fedora镜像run一个容器,并最终在其中执行/bin/bash命令。
我们还指定了以下标志:
-
-i:启用 STDIN -
-t:分配一个伪终端,即终端
在 Docker 中运行 WildFly
在本食谱中,您将学习如何通过创建一个Dockerfile来在 Docker 容器中运行 WildFly,该文件描述了镜像应该如何构建。为了完成这个食谱,您需要有一个工作的 Docker 安装,以及一个 Fedora 21 镜像;如果您还没有安装这些,请遵循本章的第一个食谱。
准备中
在本食谱中,您将需要一个互联网连接来直接从容器中下载 WildFly。此外,我们还需要一个 Java 网络应用程序来测试我们的 WildFly 安装。如果您愿意,可以使用我 GitHub 账户中的一个项目,网址为github.com/foogaro/wildfly-cookbook.git。
您可以git-clone仓库或仅将其作为 ZIP 存档下载:
-
将源放置在
~/WFC/github。 -
在那里,您将找到一个名为
docker-example的项目。要编译该项目,请运行以下命令:$ cd ~/WFC/github $ cd wildfly-cookbook $ cd docker-example $ mvn clean package在 Maven 生成的
target文件夹中,您应该找到准备部署的docker-example.war工件。 -
接下来,创建一个文件夹,我们将放置所有将创建的 Docker 文件,如下所示:
$ cd ~/WFC && mkdir -p docker/wildfly
让我们开始使用 Docker 吧!
如何做到这一点…
我们需要做的第一件事是在~/WFC/docker/wildfly文件夹中创建Dockerfile文件。
-
打开一个文本编辑器,创建一个名为
Dockerfile的文件。现在复制并粘贴以下代码:FROM fedora:latest MAINTAINER Luigi Fugaro l.fugaro@gmail.com RUN yum -y install java-1.8.0-openjdk RUN yum -y install tar net-tools RUN cd /opt && curl http://download.jboss.org/wildfly/9.0.0.Beta2/wildfly-9.0.0.Beta2.tar.gz | tar zx RUN ln -s /opt/wildfly-9.0.0.Beta2 /opt/wildfly RUN groupadd -r cookbook -g 12345 && useradd -u 54321 -r -g cookbook -d /opt/wildfly -s /sbin/nologin -c "WildFly user" wildfly RUN /opt/wildfly/bin/add-user.sh wildfly cookbook.2015 --silent RUN chown -R wildfly:cookbook /opt/wildfly/* EXPOSE 8080 9990 USER wildfly CMD ["/opt/wildfly/bin/standalone.sh", "-b", "0.0.0.0", "-bmanagement", "0.0.0.0"]将其保存在之前提到的文件夹中。
-
现在,有了
Dockerfile,我们可以构建一个针对 WildFly 的特定镜像,并将其命名为foogaro/wildfly,如下所示:$ cd ~/WFC/docker/wildfly $ docker build -t foogaro/wildfly . Sending build context to Docker daemon 5.12 kB Sending build context to Docker daemon Step 0 : FROM fedora:latest ---> 834629358fe2 Step 1 : MAINTAINER Luigi Fugaro l.fugaro@gmail.com ---> Running in 29c6d2ecbe12 ---> 54af5e1a15b6 Removing intermediate container 29c6d2ecbe12 Step 2 : RUN yum -y install java-1.8.0-openjdk ---> Running in 7d73ef1137ea Resolving Dependencies --> Running transaction check ---> Package java-1.8.0-openjdk.x86_64 1:1.8.0.31-3.b13.fc21 will be installed ... Complete! Step 3 : RUN yum -y install tar net-tools ---> Running in 6a147261f3a7 Resolving Dependencies --> Running transaction check ---> Package net-tools.x86_64 0:2.0-0.31.20141124git.fc21 will be installed ---> Package tar.x86_64 2:1.27.1-7.fc21 will be installed ... Complete! Step 4 : RUN cd /opt && curl http://download.jboss.org/wildfly/9.0.0.Beta2/wildfly-9.0.0.Beta2.tar.gz | tar zx ---> Running in 90738a7cb6c0 % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 112M 100 112M 0 0 771k 0 0:02:29 0:02:29 --:--:-- 778k ---> 5e1138497058 Removing intermediate container 90738a7cb6c0 Step 5 : RUN ln -s /opt/wildfly-9.0.0.Beta2 /opt/wildfly ---> Running in 34d760c4ba59 ---> 5c9b207bd2aa Removing intermediate container 34d760c4ba59 Step 6 : RUN /opt/wildfly/bin/add-user.sh wildfly cookbook.2015 --silent ---> Using cache ---> 4cf96ff92355 Step 7 : EXPOSE 8080 9990 ---> Running in 51703bccf71e ---> 7fbb535ab85a Removing intermediate container 51703bccf71e Step 8 : CMD /opt/wildfly/bin/standalone.sh -b 0.0.0.0 -bmanagement 0.0.0.0 ---> Running in e8537f97615a ---> 56d5fea9c4ff Removing intermediate container e8537f97615a Successfully built 56d5fea9c4ff -
太好了!我们已经成功创建了第一个名为
foogaro/wildfly的 Docker 镜像。现在尝试执行以下命令:$ docker images REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE foogaro/wildfly latest 56d5fea9c4ff 3 minutes ago 745.8 MB fedora latest 834629358fe2 4 weeks ago 250.2 MB如您所见,除了其他镜像之外,我们还有刚刚创建的镜像。
-
现在,让我们按照以下方式运行 WildFly:
$ docker run -it -p 8080:8080 -p 9990:9990 --rm foogaro/wildfly您应该得到以下输出:
============================================================== JBoss Bootstrap Environment JBOSS_HOME: /opt/wildfly JAVA: java JAVA_OPTS: -server -Xms64m -Xmx512m -XX:MaxPermSize=256m -Djava.net.preferIPv4Stack=true -Djboss.modules.system.pkgs=org.jboss.byteman -Djava.awt.headless=true ============================================================== OpenJDK 64-Bit Server VM warning: ignoring option MaxPermSize=256m; support was removed in 8.0 19:24:17,226 INFO [org.jboss.modules] (main) JBoss Modules version 1.3.3.Final 19:24:17,448 INFO [org.jboss.msc] (main) JBoss MSC version 1.2.2.Final 19:24:17,514 INFO [org.jboss.as] (MSC service thread 1-5) JBAS015899: WildFly 8.2.0.Final "Tweek" starting 19:24:18,673 INFO [org.jboss.as.server] (Controller Boot Thread) JBAS015888: Creating http management service using socket-binding (management-http) 19:24:18,694 INFO [org.xnio] (MSC service thread 1-5) XNIO version 3.3.0.Final 19:24:18,726 INFO [org.xnio.nio] (MSC service thread 1-5) XNIO NIO Implementation Version 3.3.0.Final 19:24:18,752 INFO [org.jboss.as.clustering.infinispan] (ServerService Thread Pool -- 32) JBAS010280: Activating Infinispan subsystem. 19:24:18,768 INFO [org.wildfly.extension.io] (ServerService Thread Pool -- 31) WFLYIO001: Worker 'default' has auto-configured to 8 core threads with 64 task threads based on your 4 available processors 19:24:18,788 INFO [org.jboss.as.naming] (ServerService Thread Pool -- 40) JBAS011800: Activating Naming Subsystem 19:24:18,818 INFO [org.jboss.as.connector.subsystems.datasources] (ServerService Thread Pool -- 27) JBAS010403: Deploying JDBC-compliant driver class org.h2.Driver (version 1.3) 19:24:18,840 INFO [org.jboss.as.jsf] (ServerService Thread Pool -- 38) JBAS012615: Activated the following JSF Implementations: [main] 19:24:18,848 WARN [org.jboss.as.txn] (ServerService Thread Pool -- 46) JBAS010153: Node identifier property is set to the default value. Please make sure it is unique. 19:24:18,865 INFO [org.jboss.as.security] (ServerService Thread Pool -- 45) JBAS013171: Activating Security Subsystem 19:24:18,871 INFO [org.jboss.as.connector.logging] (MSC service thread 1-8) JBAS010408: Starting JCA Subsystem (IronJacamar 1.1.9.Final) 19:24:18,914 INFO [org.jboss.as.connector.deployers.jdbc] (MSC service thread 1-8) JBAS010417: Started Driver service with driver-name = h2 19:24:18,916 INFO [org.jboss.as.security] (MSC service thread 1-3) JBAS013170: Current PicketBox version=4.0.21.Final 19:24:18,927 INFO [org.jboss.as.webservices] (ServerService Thread Pool -- 48) JBAS015537: Activating WebServices Extension 19:24:19,017 INFO [org.wildfly.extension.undertow] (MSC service thread 1-7) JBAS017502: Undertow 1.1.0.Final starting 19:24:19,023 INFO [org.jboss.as.mail.extension] (MSC service thread 1-2) JBAS015400: Bound mail session [java:jboss/mail/Default] 19:24:19,027 INFO [org.wildfly.extension.undertow] (ServerService Thread Pool -- 47) JBAS017502: Undertow 1.1.0.Final starting 19:24:19,034 INFO [org.jboss.remoting] (MSC service thread 1-5) JBoss Remoting version 4.0.6.Final 19:24:19,050 INFO [org.jboss.as.naming] (MSC service thread 1-4) JBAS011802: Starting Naming Service 19:24:19,155 INFO [org.wildfly.extension.undertow] (ServerService Thread Pool -- 47) JBAS017527: Creating file handler for path /opt/wildfly/welcome-content 19:24:19,183 INFO [org.wildfly.extension.undertow] (MSC service thread 1-7) JBAS017525: Started server default-server. 19:24:19,217 INFO [org.wildfly.extension.undertow] (MSC service thread 1-1) JBAS017531: Host default-host starting 19:24:19,473 INFO [org.wildfly.extension.undertow] (MSC service thread 1-3) JBAS017519: Undertow HTTP listener default listening on /0.0.0.0:8080 19:24:19,622 INFO [org.jboss.as.server.deployment.scanner] (MSC service thread 1-3) JBAS015012: Started FileSystemDeploymentService for directory /opt/wildfly/standalone/deployments 19:24:19,730 INFO [org.jboss.as.connector.subsystems.datasources] (MSC service thread 1-6) JBAS010400: Bound data source [java:jboss/datasources/ExampleDS] 19:24:19,887 INFO [org.jboss.ws.common.management] (MSC service thread 1-1) JBWS022052: Starting JBoss Web Services - Stack CXF Server 4.3.2.Final 19:24:20,033 INFO [org.jboss.as] (Controller Boot Thread) JBAS015961: Http management interface listening on http://0.0.0.0:9990/management 19:24:20,033 INFO [org.jboss.as] (Controller Boot Thread) JBAS015951: Admin console listening on http://0.0.0.0:9990 19:24:20,034 INFO [org.jboss.as] (Controller Boot Thread) JBAS015874: WildFly 8.2.0.Final "Tweek" started in 3113ms - Started 184 of 234 services (82 services are lazy, passive or on-demand) -
现在,尝试打开您的浏览器,并将其指向
http://127.0.0.1:9990/。 -
浏览器应提示您输入 WildFly
ManagementRealm的用户名和密码;只需输入以下凭据:-
用户名:
wildfly -
密码:
cookbook.2015上述凭据是用于构建镜像的
Dockerfile文件中指定的凭据。
-
好了;您现在就在您的 docker 化 WildFly 实例中了!
现在,我们有相当多的选项来部署我们的docker-example应用程序。我们可以转到 WildFly 管理控制台的部署页面,添加我们的工件,上传它,并启用它。或者,我们可以将应用程序与 Docker 本身一起打包,这将更好,您很快就会了解到:
-
从准备就绪部分,您应该在路径中安装我的 GitHub
wildfly-cookbook仓库,我将其命名为CODE_PATH。从那里,您应该在docker-example文件夹中创建另一个Dockerfile。在Dockerfile中填写以下代码:FROM foogaro/wildfly:latest MAINTAINER Luigi Fugaro l.fugaro@gmail.com COPY target/docker-example.war /opt/wildfly/standalone/deployments/ EXPOSE 8080 9990 CMD ["/opt/wildfly/bin/standalone.sh", "-b", "0.0.0.0", "-bmanagement", "0.0.0.0"] -
再次,通过执行以下命令从它创建一个 Docker 镜像:
$ docker build -t foogaro/wildfly-docker-example . Sending build context to Docker daemon 33.28 kB Sending build context to Docker daemon Step 0 : FROM foogaro/wildfly:latest ---> 56d5fea9c4ff Step 1 : MAINTAINER Luigi Fugaro l.fugaro@gmail.com ---> Using cache ---> ab0e63c8c1a9 Step 2 : COPY target/docker-example.war /opt/wildfly/standalone/deployments/ ---> 3b46d10fde74 Removing intermediate container 919ef5d6bc45 Step 3 : EXPOSE 8080 9990 ---> Running in 7a33da460750 ---> b741119c54bb Removing intermediate container 7a33da460750 Step 4 : CMD /opt/wildfly/bin/standalone.sh -b 0.0.0.0 -bmanagement 0.0.0.0 ---> Running in 71e0fff696e4 ---> 0668770878ab Removing intermediate container 71e0fff696e4 Successfully built 0668770878ab -
太好了!我们已经成功创建了前面的 Docker 镜像,其中包含 WildFly 以及我们的
docker-example应用程序。我们还可以看到它作为 Docker 镜像被列出,如下所示:$ docker images REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE foogaro/wildfly-docker-example latest 0668770878ab 7 seconds ago 745.8 MB foogaro/wildfly latest 56d5fea9c4ff 34 minutes ago 745.8 MB fedora latest 834629358fe2 4 weeks ago 250.2 MB -
现在我们只需要在一个容器中运行这个镜像并测试我们的应用程序,如下所示:
$ docker run -i -t -p 8080:8080 -p 9990:9990 --rm foogaro/wildfly-docker-example您应该得到以下输出:
============================================================== JBoss Bootstrap Environment JBOSS_HOME: /opt/wildfly JAVA: java JAVA_OPTS: -server -Xms64m -Xmx512m -XX:MaxPermSize=256m -Djava.net.preferIPv4Stack=true -Djboss.modules.system.pkgs=org.jboss.byteman -Djava.awt.headless=true ============================================================== OpenJDK 64-Bit Server VM warning: ignoring option MaxPermSize=256m; support was removed in 8.0 19:52:10,808 INFO [org.jboss.modules] (main) JBoss Modules version 1.3.3.Final 19:52:11,037 INFO [org.jboss.msc] (main) JBoss MSC version 1.2.2.Final 19:52:11,109 INFO [org.jboss.as] (MSC service thread 1-6) JBAS015899: WildFly 8.2.0.Final "Tweek" starting 19:52:12,288 INFO [org.jboss.as.server] (Controller Boot Thread) JBAS015888: Creating http management service using socket-binding (management-http) 19:52:12,308 INFO [org.xnio] (MSC service thread 1-1) XNIO version 3.3.0.Final 19:52:12,320 INFO [org.xnio.nio] (MSC service thread 1-1) XNIO NIO Implementation Version 3.3.0.Final 19:52:12,379 INFO [org.jboss.remoting] (MSC service thread 1-1) JBoss Remoting version 4.0.6.Final 19:52:12,388 INFO [org.wildfly.extension.io] (ServerService Thread Pool -- 31) WFLYIO001: Worker 'default' has auto-configured to 8 core threads with 64 task threads based on your 4 available processors 19:52:12,424 INFO [org.jboss.as.clustering.infinispan] (ServerService Thread Pool -- 32) JBAS010280: Activating Infinispan subsystem. 19:52:12,453 INFO [org.jboss.as.jsf] (ServerService Thread Pool -- 38) JBAS012615: Activated the following JSF Implementations: [main] 19:52:12,465 INFO [org.jboss.as.naming] (ServerService Thread Pool -- 40) JBAS011800: Activating Naming Subsystem 19:52:12,467 INFO [org.jboss.as.security] (ServerService Thread Pool -- 45) JBAS013171: Activating Security Subsystem 19:52:12,478 WARN [org.jboss.as.txn] (ServerService Thread Pool -- 46) JBAS010153: Node identifier property is set to the default value. Please make sure it is unique. 19:52:12,494 INFO [org.jboss.as.security] (MSC service thread 1-1) JBAS013170: Current PicketBox version=4.0.21.Final 19:52:12,518 INFO [org.jboss.as.connector.logging] (MSC service thread 1-4) JBAS010408: Starting JCA Subsystem (IronJacamar 1.1.9.Final) 19:52:12,537 INFO [org.jboss.as.webservices] (ServerService Thread Pool -- 48) JBAS015537: Activating WebServices Extension 19:52:12,552 INFO [org.jboss.as.connector.subsystems.datasources] (ServerService Thread Pool -- 27) JBAS010403: Deploying JDBC-compliant driver class org.h2.Driver (version 1.3) 19:52:12,573 INFO [org.jboss.as.connector.deployers.jdbc] (MSC service thread 1-4) JBAS010417: Started Driver service with driver-name = h2 19:52:12,617 INFO [org.wildfly.extension.undertow] (MSC service thread 1-7) JBAS017502: Undertow 1.1.0.Final starting 19:52:12,618 INFO [org.wildfly.extension.undertow] (ServerService Thread Pool -- 47) JBAS017502: Undertow 1.1.0.Final starting 19:52:12,687 INFO [org.jboss.as.naming] (MSC service thread 1-1) JBAS011802: Starting Naming Service 19:52:12,689 INFO [org.jboss.as.mail.extension] (MSC service thread 1-2) JBAS015400: Bound mail session [java:jboss/mail/Default] 19:52:12,814 INFO [org.wildfly.extension.undertow] (ServerService Thread Pool -- 47) JBAS017527: Creating file handler for path /opt/wildfly/welcome-content 19:52:12,835 INFO [org.wildfly.extension.undertow] (MSC service thread 1-5) JBAS017525: Started server default-server. 19:52:12,997 INFO [org.wildfly.extension.undertow] (MSC service thread 1-4) JBAS017531: Host default-host starting 19:52:13,060 INFO [org.wildfly.extension.undertow] (MSC service thread 1-5) JBAS017519: Undertow HTTP listener default listening on /0.0.0.0:8080 19:52:13,348 INFO [org.jboss.as.server.deployment.scanner] (MSC service thread 1-8) JBAS015012: Started FileSystemDeploymentService for directory /opt/wildfly/standalone/deployments 19:52:13,356 INFO [org.jboss.as.server.deployment] (MSC service thread 1-1) JBAS015876: Starting deployment of "docker-example.war" (runtime-name: "docker-example.war") 19:52:13,413 INFO [org.jboss.as.connector.subsystems.datasources] (MSC service thread 1-8) JBAS010400: Bound data source [java:jboss/datasources/ExampleDS] 19:52:13,616 INFO [org.jboss.ws.common.management] (MSC service thread 1-2) JBWS022052: Starting JBoss Web Services - Stack CXF Server 4.3.2.Final 19:52:14,008 INFO [org.wildfly.extension.undertow] (MSC service thread 1-4) JBAS017534: Registered web context: /docker-example 19:52:14,082 INFO [org.jboss.as.server] (ServerService Thread Pool -- 28) JBAS018559: Deployed "docker-example.war" (runtime-name : "docker-example.war") 19:52:14,157 INFO [org.jboss.as] (Controller Boot Thread) JBAS015961: Http management interface listening on http://0.0.0.0:9990/management 19:52:14,157 INFO [org.jboss.as] (Controller Boot Thread) JBAS015951: Admin console listening on http://0.0.0.0:9990 19:52:14,158 INFO [org.jboss.as] (Controller Boot Thread) JBAS015874: WildFly 8.2.0.Final "Tweek" started in 3640ms - Started 249 of 304 services (92 services are lazy, passive or on-demand) -
通过阅读最后几条日志条目,我们可以看到我们的
docker-example应用程序已经被部署。要查看应用程序的实际运行情况,请打开浏览器并将它指向http://127.0.0.1:8080/docker-example。 -
您应该看到以下页面:
![如何操作…]()
docker-example 应用程序在 Docker 容器中运行
太好了,我们刚刚在运行 WildFly 容器的 Docker 容器中部署并运行了我们的第一个应用程序!
它是如何工作的…
除了 Docker 工具本身及其附带的所有内容外,还有一些相关的事情需要讨论。
当在docker run命令中运行 WildFly 容器时,我们使用了一个新的标志-p,后面跟着一些可疑的数字。标志-p用于将容器暴露的端口映射到本地端口;也就是说,主机的端口:
$ docker run -i -t -p 8080:8080 -p 9990:9990 --rm foogaro/wildfly
在这种情况下,我们将端口8080和9990映射到与主机相同的端口。这就是为什么我们可以通过本地地址访问 WildFly 管理控制台。在访问docker-example应用程序时也发生了同样的事情。
在前面的命令中还有一个用于的标志:--rm。
--rm标志指示 Docker 守护进程在容器停止时将其从容器历史记录列表中删除。要查看所有容器,包括非运行中的容器,您可以给出以下命令:
$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
2f8fd2fbd0a0 ab0e63c8c1a9 "/bin/sh -c '#(nop) 38 hours ago hungry_brown
794901deea3c 1816b615e0ce "/bin/sh -c 'cd /opt 39 hours ago Exited (2) 39 hours ago drunk_pike
3fdc9d5c1680 1816b615e0ce "/bin/sh -c 'cd /opt 39 hours ago Exited (2) 39 hours ago adoring_mcclintock
5035b7e0a76c 5200fb462c18 "/bin/sh -c 'cd /opt 39 hours ago Exited (127) 39 hours ago prickly_hawking
981dd5f92e24 5200fb462c18 "/bin/sh -c 'cd /opt 39 hours ago Exited (127) 39 hours ago elegant_hawking
如您所见,过了一会儿,列表可能会变得非常长。最终,您可以手动删除不再需要的旧容器,通过以下命令执行:
$ docker rm 981dd5f92e24
981dd5f92e24
再次列出所有容器,ID 为981dd5f92e24的容器应该已经消失,如下所示:
$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
2f8fd2fbd0a0 ab0e63c8c1a9 "/bin/sh -c '#(nop) 38 hours ago hungry_brown
794901deea3c 1816b615e0ce "/bin/sh -c 'cd /opt 39 hours ago Exited (2) 39 hours ago drunk_pike
3fdc9d5c1680 1816b615e0ce "/bin/sh -c 'cd /opt 39 hours ago Exited (2) 39 hours ago adoring_mcclintock
5035b7e0a76c 5200fb462c18 "/bin/sh -c 'cd /opt 39 hours ago Exited (127) 39 hours ago prickly_hawking
因此,如果我们尝试在没有--rm标志的情况下运行我们的foogaro/wildfly容器,当我们停止容器时,它应该出现在容器历史记录列表中。
还有另一件事情需要讨论,那就是我们创建的第二个 Dockerfile,它是用来创建foogaro/wildfly-docker-example镜像的。
文件中的结构和使用的命令相当简单:
-
FROM:它用于声明基础或起始镜像。
-
MAINTAINER:它用于声明 Dockerfile 的所有者;即镜像。
-
RUN:它用于在容器内部运行命令。
-
EXPOSE:它用于从容器中公开一组端口。
-
CMD:通常用作启动容器时运行服务/命令的最终指令。这个指令基本上是一个参数数组,其中要执行的命令本身也是一个参数。
注意
你可以使用更多指令,它们都在 Docker 网站上详细解释和提供,请访问docs.docker.com/reference/builder。
请记住,Dockerfile 中的每条指令都是顺序执行且是原子的。每条指令都会启动自己的临时容器,执行它必须完成的任务,完成后提交其工作,然后销毁其临时容器。下一条指令将执行同样的操作,依此类推。
这是一个很棒的功能,因为如果在构建镜像时出现问题,一旦错误被修复,你不需要重新启动;之前的错误指令被缓存,因此你可以快速测试并再次运行你的构建。
参考信息
请记住,如今,DockerHub 注册表中有很多官方镜像,例如官方的 WildFly Docker 镜像。为了获取并在此镜像上工作,你可以简单地从存储库中搜索并拉取它,如下面的图像所示:

在 Docker 外记录 WildFly 日志
当处理应用程序服务器以及因此网页应用程序时,我们通常真的需要查看日志。正如我们在前面的菜谱中看到的那样,我们已经在一个容器中运行了 WildFly,并且由于终端标志被启用(在执行docker run命令时使用-t),我们自动查看了日志。
如果没有启用终端标志,我们就需要访问容器(使用docker attach CONTAINER_ID或docker logs CONTAINER_ID命令)。这不是查看日志最舒适的方式,我们希望将日志存储在本地,在我们的主机上,并将它们分组。
在这个菜谱中,你将学习如何将你的应用程序日志存储在容器外部,并存储在主机上。
准备工作
为了能够跟随这个菜谱,你需要遵循前面的一个菜谱,它是关于在 Docker 中运行 WildFly 的。
如何操作…
首先,我们需要在主机上创建一个目录来存储我们的日志,并启用容器可写权限以访问该目录:
-
打开一个终端窗口并执行以下命令:
$ mkdir -p /opt/docker/wildfly/logs $ groupadd -r cookbook -g 12345 $ useradd -u 54321 -r -g cookbook -s /sbin/nologin -c "WildFly user" wildfly $ chcon -t svirt_sandbox_file_t /opt/docker/wildfly/logs -
现在,在另一个终端窗口中,使用我们的
foogaro/wildflyDocker 镜像运行一个容器,如下所示:$ docker run -d -v /opt/docker/wildfly/logs:/opt/wildfly/standalone/log -p 8080:8080 -p 9990:9990 --rm foogaro/wildfly -
在创建
/opt/docker/wildfly/logs文件夹的终端中,按照以下方式列出文件夹的内容:$ cd /opt/docker/wildfly/logs $ ls -la total 32 drwxr-xr-x. 2 wildfly cookbook 4096 Feb 5 17:32 . drwxr-xr-x. 4 wildfly cookbook 4096 Feb 5 12:43 .. -rw-r--r--. 1 wildfly cookbook 10261 Feb 5 17:36 server.log
太好了,我们的server.log文件就在那里!!!
显然,这并不是处理日志的最佳方式;你应该考虑与syslog、rsyslog、logstash和其他具有更强大和更高性能特性的工具集成。
注意
以这个食谱为例来处理容器外的文件夹。然而,在开发环境中,人们可能希望将日志存储在他们的个人电脑上。
它是如何工作的…
首先,我们需要创建一个目录来存储 WildFly 日志,然后添加我们在构建foogaro/wildflyDocker 镜像时使用的相同用户和组。实际上,在Dockerfile中有以下指令:
RUN groupadd -r cookbook -g 12345 && useradd -u 54321 -r -g cookbook -d /opt/wildfly -s /sbin/nologin -c "WildFly user" wildfly
上述代码在容器环境中添加了一个组和用户,并使用固定的组 ID 和用户 ID 创建它们。因此,为了允许容器对主机有读写权限,我们需要使用与目录相同的用户和组。
一切配置完成后,我们使用带有-v标志的docker run命令启动,该标志是关于使用from:to模式挂载卷。也就是说,主机from(在指定路径上)的所有内容都将出现在容器环境中的指定路径to。此外,在指定文件夹中进行的每次更新都将持久保存在主机文件夹中。
在 Docker 中使用不同的 Docker 容器在域模式下运行 WildFly
在这个食谱中,您将学习如何使用容器在域模式下运行 WildFly。我们将使用一个容器作为域控制器,另外两个容器,每个作为服务器组的服务器。
准备工作
为了正确地跟随和理解本食谱中讨论的主题,我们需要了解 WildFly 域模式是什么,以及其原则。您还可以参考第三章,使用 XAML 进行操作,该章节是关于在域模式下运行 WildFly。
此外,您需要遵循本章的前两个食谱,即拥有一个工作的 Docker 安装和一个可用的 WildFly 镜像。
然而,为了简化我们的食谱,我们将依赖于 WildFly 的默认配置文件:domain.xml、host-master.xml和host-slave.xml。
如何操作...
-
首先,我们需要基于
foogaro/wildfly镜像创建一个新的Dockerfile,它将包含运行 WildFly 在域模式下所需的全部配置。以下是整个文件:FROM foogaro/wildfly MAINTAINER Luigi Fugaro l.fugaro@gmail.com RUN sed -i '/secret value/d' /opt/wildfly/domain/configuration/host-slave.xml RUN sed -i '/<server-identities>/a <secret value="Y29va2Jvb2suMjAxNQ==" \/>' /opt/wildfly/domain/configuration/host-slave.xml RUN sed -i 's/remote host/remote username="wildfly" host/' /opt/wildfly/domain/configuration/host-slave.xml RUN sed -i 's/jboss.domain.master.address/env.DOMAIN_CONTROLLER_PORT_9999_TCP_ADDR/' /opt/wildfly/domain/configuration/host-slave.xml RUN sed -i 's/jboss.domain.master.port/env.DOMAIN_CONTROLLER_PORT_9999_TCP_PORT/' /opt/wildfly/domain/configuration/host-slave.xml EXPOSE 8080 9990 9999 USER wildfly ENTRYPOINT ["/opt/wildfly/bin/domain.sh"] CMD ["--host-config", "host-master.xml", "-b", "0.0.0.0", "-bmanagement", "0.0.0.0"] -
要构建镜像,请使用以下命令:
$ docker build -t foogaro/wildfly-domain . -
一旦构建过程完成,我们就可以在容器中运行
foogaro/wildfly-domain镜像。我们将首先运行的是没有任何操作服务器的域控制器,如下所示:$ docker run -i -t -p 9990:9990 -p 9999:9999 --name=DC --rm foogaro/wildfly-domain ... [Host Controller] 10:05:17,590 INFO [org.jboss.as] (Controller Boot Thread) WFLYSRV0025: WildFly Full 9.0.0.Beta2 (WildFly Core 1.0.0.Beta2) (Host Controller) started in 2830ms - Started 50 of 50 services (13 services are lazy, passive or on-demand) -
现在,您可以从主机访问管理控制台,地址为
http://localhost:9990/。 -
如以下图像所示,在域 | 拓扑部分,没有正在运行的服务器:
![如何操作…]()
在 Docker 容器中运行域模式下的 WildFly
让我们运行另一个容器,使用由
host-slave.xml文件提供的 WildFly 默认配置。 -
打开一个新的终端窗口并执行以下命令:
$ docker run -i -t -p 8080:8080 --link=DC:DOMAIN_CONTROLLER --name=HC-1 --rm foogaro/wildfly-domain --host-config host-slave.xml -b 0.0.0.0 -bmanagement 0.0.0.0 -
一旦容器成功启动 WildFly,刷新我们之前加载的 Admin Console 中的拓扑概述部分,你现在应该会看到两个正在运行的服务器,如下面的图像所示:
![如何操作…]()
显示两个在不同 Docker 容器上运行的 WildFly 域模式拓扑
它是如何工作的…
在这个菜谱中,我们看到了一些新的 Dockerfile 指令,例如 ENTRYPOINT。这个指令几乎和 CMD 一样;它用于运行服务。ENTRYPOINT 指令使用 CMD 指令来持有命令参数。实际上,你可以使用选项指定这两个指令,以覆盖命令行中的 CMD 指令,这正是我们在运行主机控制器时所做的。
我们使用 sed 命令修改了 host-slave.xml 文件;关于此命令的更多信息超出了本书的范围。
正如你应该已经知道的,域控制器和主机控制器之间的通信需要验证。因此,我插入了 ManagementRealm 中 wildfly 用户的哈希密码——第一个和第二个 sed。第三个 sed 基本上指示主机控制器使用 wildfly 用户验证域控制器。
小贴士
记住,如果你没有在 remote XML 元素中指定 username 属性,将使用主机名 <host name="slave"> 代替。
最后两个 sed 命令指示使用哪个地址和端口连接到域控制器。由于我们不知道容器会有哪个 IP 地址,我们可以依赖 Docker 的环境变量,它在启动时会自动设置。这个特定的映射是由于在启动第二个容器时使用的 --link 标志。
$ docker run -i -t -p 8080:8080 --link=DC:DOMAIN_CONTROLLER --name=HC-1 --rm foogaro/wildfly-domain --host-config host-slave.xml -b 0.0.0.0 -bmanagement 0.0.0.0
在第一个容器中,我们使用 --name 标志给它一个有意义的名称,比如 DC。然后我们使用相同的名称与 --link 标志将这个容器与第二个容器绑定,并将其映射到 DOMAIN_CONTROLLER 别名。
更多内容…
当运行容器时,你可以指定它将拥有的主机名。在我们的例子中,我们可以运行从属节点,如下所示:
$ docker run -i -t -p 8080:8080 -h host-1 --link=DC:DOMAIN_CONTROLLER --name=HC-1 --rm foogaro/wildfly-domain --host-config host-slave.xml -b 0.0.0.0 -bmanagement 0.0.0.0
在拓扑概述中,我们会看到以下页面:

在 Docker 容器中运行的有意义的名称主机-1
这是一个很实用的功能,可以避免尝试记住哪个哈希属于哪个主机。
总之,在这个菜谱中用于配置和运行域模式的相同机制也可以用来运行 WildFly 集群。
参见
- 更多关于链接 Docker 容器的信息可以在
docs.docker.com/userguide/dockerlinks/找到。






























































































































浙公网安备 33010602011771号