60分钟

第6章 中间件技术

【学习目标】

1.知识目标

理解中间件概念及分类。

了解中间件产品及其开源产品。

掌握Linux系统下JDK和Tomcat的安装与配置。

掌握RabbitMQ的安装与配置。

掌握RabbitMQ的应用。

理解RabbitMQ的消息发布及订阅系统。

2.技能目标

JDK安装与部署。

Tomcat的安装与部署。

RabbitMQ的安装与部署。

RabbitMQ的消息发布与订阅系统。

【认证考点】

了解中间件的概念、分类及产品。

掌握Linux系统下JDK和Tomcat的安装与配置。

掌握RabbitMQ的安装与配置。

掌握RabbitMQ的应用。

理解RabbitMQ的消息发布及订阅系统。

项目引导:实现消息发布及订阅系统开发

【项目描述】

场景1:用户注册后,需要发注册邮件和注册短信这种现象,传统的做法有注册信息写入数据库后,发送注册邮件,再发送注册短信,三个任务全部完成后才返回给客户端用户注册成功,但实际情况是邮件、短信不是必须,它仅仅是一个通知。场景2:人们经常会在双11购物节大量囤货,用户下单后,订单系统通知库存系统,但库存系统出现故障时,订单就会失败。

如何解决上述存在的问题?经常采用消息队列技术来解决。如用户注册场景,引入消息队列后,发送邮件、短信不是必须的业务逻辑,可以将他们进行异步处理 。如双11购物节,当用户下单后将消息写入消息队列,返回用户订单下单成功,而库存系统则通过订阅下单的消息,获取下单消息。在消息队列软件中,常用的软件为RabbitMQ,通过RabbitMQ进行信息发布与订阅。

知识储备

6.1 中间件技术

中间件(Middleware)是处于操作系统和应用程序之间的软件,它使用系统软件所提供的基础服务,衔接网络上应用系统的各个部分或不同的应用,能够达到资源共享、功能共享的目的。

6.1.1中间件的概念

随着计算机技术的快速发展,更多的应用软件被要求在许多不同的网络协议、不同的硬件生产厂商以及不一样的网络平台和环境上运营。这导致了软件开发者需要面临数据离散、操作困难、系统匹配程度低,以及需要开发多种应用程序来达到运营的目的。所以,中间件技术的产生,在极大程度上减轻了开发者的负担,使得网络的运行更有效率。

中间件是一种独立的系统软件服务程序,分布式应用软件借助这种软件在不同的技术之间共享资源,中间件位于客户机服务器的操作系统之上,管理计算资源和网络通信。

中间件是基础软件的一大类,属于可复用软件的范畴。顾名思义,中间件处于操作系统软件与用户的应用软件的中间。中间件在操作系统、网络和数据库之上,应用软件的下层,它的作用是为处于自己上层的应用软件提供运行与开发的环境,帮助用户灵活、高效地开发和集成复杂的应用软件。

1.为什么要使用中间件

中间件屏蔽了底层操作系统的复杂性,使程序开发人员面对一个简单而统一的开发环境,减少程序设计的复杂性,将注意力集中在自己的业务上,不必再为程序在不同系统软件上的移植而重复工作,从而大大减少了技术上的负担。中间件带给应用系统的,不只是开发的简便、开发周期的缩短,也减少了系统的维护、运行和管理的工作量,还减少了计算机总体费用的投入。

2.中间件的基本功能

中间件是独立的系统级软件,连接操作系统层和应用程序层,将不同操作系统提供应用的接口标准化,协议统一化,屏蔽具体操作的细节。

(1)通信支持

中间件为其所支持的应用软件提供平台化的运行环境,该环境屏蔽底层通信之间的接口差异,实现互操作,早期应用与分布式的中间件交互主要的通信方式为远程调用和消息两种方式。通信模块中,远程调用通过网络进行通信,通过支持数据的转换和通信服务,从而屏蔽不同的操作系统和网络协议。远程调用是提供给予过程的服务访问,为上层系统只提供非常简单的编程接口或过程调用模型。消息提供异步交互的机制。

(2)应用支持

中间件的目的就是服务上层应用,提供应用层不同服务之间的互操作机制。它为上层应用开发提供统一的平台和运行环境,并封装不同操作系统提供API接口,向应用提供统一的标准接口,使应用的开发和运行与操作系统无关,实现其独立性。

(3)公共服务

公共服务是对应用软件中共性功能或约束的提取。将这些共性的功能或者约束分类实现,并支持复用,作为公共服务,提供给应用程序使用。通过提供标准、统一的公共服务,可减少上层应用的开发工作量,缩短应用的开发时间,并有助于提供应用软件的质量。

6.1.2 中间件分类

中间件所包括的范围十分广泛,针对不同的应用需求涌现出多种各具特色的中间件产品。

1.消息中间件

消息中间件适用于进行网络通讯的系统,建立网络通讯的通道,进行数据和文件的传送。典型的产品有ActiveMQ、ZeroMQ、RabbitMQ、IBM webSphere MQ等。

2.交易中间件

交易中间件管理分布于不同操作系统的数据,实现数据一致性,保证系统的负载均衡。典型的产品有IBM CICS、Bea tuxedo等。

3.对象中间件

对象中间件保证不同厂家的软件之间的交互访问。典型的产品有IBM componentbroker、 iona orbix、borland visibroker等。

4.应用服务器

应用服务器是用来构造Internet/Intranet应用和其它分布式构件应用。典型的产品有IBM Websphere、Bea weblogic等。

5.安全中间件

安全中间件以公钥基础设施(PKI)为核心的、建立在一系列相关国际安全标准之上的一个开放式应用开发平台。典型的产品有Entrust等。

6.应用集成服务器

应用集成服务器把工作流和应用开发技术如消息及分布式构件结合在一起,使处理能方便自动地和构件、Script应用、工作流行为结合在一起,同时集成文档和电子邮件。典型的产品有LSS Flowman、IBM Flowmark、Vitria Businessagiliti等。

6.1.3 中间件的开源产品

国际上关于中间件技术的开发思路有两种:企业专有模式与开源开发模式。目前,企业专有模式已经取得很大的成绩,如BEA公司的WebLogic套件包、IBM公司的WebSphere套件包、还有HP、SUN和Oracle等公司推出的专有中间件产品。

目前开源应用服务器有两种,分别为JBOSS应用服务器和JOnAS应用服务器。JOnAS项目为“JAVA开放应用服务器”的缩写,其开发活动由法国ObjectWeb所主持。

ObjectWeb的发展思路是“通过联合做强、做大”,它的雄心是联合一切力量,不仅联合一切开发者和广大用户,而且也联合一切相关的开源开发项目。ObjectWeb联合体的最终目标就是在开放标准的指引下,为电子商务、EAI(企业应用集成)、家庭自动化、电信以及数据仓库的连接、网格计算、企业信息处理和微内核设计等广大的软件开发领域提供传统商业化解决方案的“开源替代物”。

用JAVA语言开发运行在服务器上的应用程序,必须遵循SUN公司提出的J2EE规范,也就是说,这种规范给出了在分布式环境下开发和部署面向“组件”的Java应用程序应当遵循的一些具体规则。典型的J2EE应用程序由两部分构成分别是表现组件(也叫Web组件有Servlets与JSP)和企业组件(EJB,Enterprise JavaBeans),他们定义事务处理逻辑和应用数据。J2EE服务器提供两种“容器”,一种是负责处理Web组件;另一种是负责处理企业组件。JBOSS和JOnAS开源应用服务器是“J2EE服务器”,它们分别在2004年6月19日和2005年2月1日通过了J2EE测试认证。

6.2 JDK与Tomcat应用服务器

JDK(Java Development Kit)是JAVA 语言的软件开发工具包,主要用于移动设备、嵌入式设备上的JAVA应用程序。Tomcat服务器是一个免费的开放源代码的Web应用服务器,属于轻量级应用服务器,在中小型系统和并发访问用户不是很多的场合下被普遍使用,是开发和调试JSP程序的首选。

6.2.1 JDK安装与配置

JDK是整个Java开发的核心,它包含了JAVA的运行环境(JVM+Java系统类库)和JAVA工具。

1.JDK发展

SE(Java SE)标准版,从JDK 5.0开始,改名为Java SE。EE(Java EE)企业版,使用这种JDK开发J2EE应用程序,从JDK 5.0开始,改名为Java EE,在2018年2月26日开始,J2EE改名为Jakarta EE 。ME(J2ME)主要用于移动设备、嵌入式设备上的Java应用程序,从JDK 5.0开始,改名为Java ME。如果没有JDK的话,无法编译Java程序(Java源码.java文件),如果想只运行Java程序(指class或jar或其它归档文件),要确保已安装相应的JRE。

2. Window系统安装JDK

在官网上下载Java开发工具包JDK,根据安装向导进行安装,在安装过程中也同时安装JRE。以JDK8版本为例,讲解JDK的安装,如图6-2-1所示。

图6-2-1 Windows下安装JDK

(1)环境变量配置

点击“电脑”右键,选择“属性”→“高级系统设置”→“环境变量”,如下图6-2-2所示。

图6-2- 2环境变量

设置环境变量值,在“环境变量”面板中找到“系统变量”,选择“新建”。

①设置JAVA_HOME环境变量值

在“新建系统变量”对话框中,变量名一栏输入“JAVA_HOME”,变量值为JDK的安装路径,如图6-2-3所示。

JDK的安装路径

图6-2-3配置JAVA_HOME环境变量

②编辑path变量值

系统变量中找到“path”变量,点击“编辑”,新建值设置为“%JAVA_HOME%\bin”,点击“确定”按钮,如图6-2-4所示。

图6-2-4编辑path变量值

③测试JDK

配置好环境变量之后,查看是否配置成功。打开DOS命令窗口,输入“java-version”命令,查看JDK的版本,如图6-2-5所示。

图6-2-5 JDK测试

3.Linux系统安装JDK

在Linux系统下安装JDK,以Centos(安装桌面“GNOME”)系统为例。安装过程如下:

【步骤1】卸载系统自带的OpenJDK以及相关的Java文件。在Linux上一般会安装OpenJDK,用“java -version”命令测试Java版本号,如图6-2-6所示。

图6-2-6 OpenJDK版本

采用“rpm -qa”命令查询安装包,如查询Java安装包,命令为“rpm -qa | grep java”,查询Java的安装包,如图6-2-7所示。命令中“rpm”表示管理套件“-qa ”表示使用询问模式,查询所有套件,“grep”表示查找文件里符合条件的字符串,“java ”表示查找包含java字符串的文件。

图6-2- 7 查找Java相关的安装包

在图6-2-7以上文件中OpenJDK的相关套件可以删除,noarch文件可以不用删除。用删除命令“rpm -e --nodeps文件名”,删除OpenJDK相关的文件。命令中“rpm ”表示为管理套件 ,“-e”删除指定的套件,“--nodeps”表示不验证套件档的相互关联性。代码如下:

rpm -e --nodeps java-1.7.0-openjdk-1.7.0.111-2.6.7.8.el7.x86_64

输入代码后,但是会出错,因为在普通用户模式下,并没有操作这几个文件的权限,进入root用户,可以有权限操作这几个文件。进入root用户命令如下:

su root

【注意】下面操作在CentOS系统的桌面界面,进入CentOS的桌面界面,输入“startx”命令。

删除OpenJDK相关文件,如图6-2-8所示。

图6-2-8 产出OpenJDK文件

检查是否已经删除成功,输入命令“java -version”,如图6-2-9所示。

图6-2- 9 验证删除OpenSSL

【步骤2】下载JDK。进入JDK的官网,根据系统下载相对应的JDK版本,这里下载的Linux 64位的JDK版本。如图6-2-10和图6-2-11所示。

图6-2- 10 下载Linux64位的JDK
图6-2- 11 下载JDK

如果当前登陆用户是root,那么文件下载到“/root/download/jdk-8u144-linux-x64.tar.gz”。在“/usr/local/”目录下创建“jdk”目录,将文件拷贝到该目录下,如图6-2-12所示。代码如下:

cp jdk-8u261-linux-x64.tar.gz /usr/local/jdk

命令说明:“cp”表示复制文件或目录,“jdk-8u261-linux-x64.tar.gz”表示要复制的文件名,“/user/local/jdk”表示要复制的目标目录。

图6-2-12 把JDK安装包考到jdk文件下

【步骤3】解压JDK。采用“tar -zxvf ”进行解压,代码如下:

tar -zxvf jdk-8u261-linux-x64.tar.gz

解压命令“tar”,“-zxvf”表示备份文件。解压后文件放置“jdk1.8.0_261”文件夹,如图6-2-13所示。

图6-2-13 解压后

【步骤4】配置JDK环境变量,编辑全局变量。在root权限下输入命令“vim /etc/profile”,“vim”表示文本编辑,“/etc/profile”表示/etc/目录下的全局变量文件。进入文本编辑状态下,光标走到文件最后一行,键盘按下“i”。

进入插入状态,在“/profile”配置文件最后一行中添加配置内容,如“JAVA_HOME=/usr/local/jdk/jdk1.8.0_261”,其中“/usr/local/jdk/”表示JDK的路径,如图6-2-14所示,代码如下:

图6-2-14配置环境变量

输入“:wq”退出文本编辑器。

【步骤5】测试环境变量。设置的环境变量生效,需要输入“source /etc/profile”命令,然后用“java _version”命令查看,如果输出JDK的版本号,则说明JDK安装成功,如图6-2-15所示。

图6-2-15 测试环境变量配置是否成功

6.2.2 Tomcat安装与配置

Tomcat是Apache软件基金会的Jakarta项目中的一个核心项目,由Apache、Sun和其他一些公司及个人共同开发而成,由于Sun的参与和支持,最新的Servlet 和JSP规范在Tomcat 中得到体现,Tomcat 5支持Servlet 2.4和JSP 2.0规范。由于Tomcat技术先进、性能稳定,而且免费,因而深受JAVA爱好者的喜爱并得到了部分软件开发商的认可,成为目前比较流行的Web应用服务器。

Tomcat服务器是一个免费的开放源代码的Web应用服务器,属于轻量级应用服务器,在中小型系统和并发访问用户不是很多的场合下被普遍使用,是开发和调试JSP程序的首选。Tomcat是Servlet和JSP容器,实现了JSP规范的Servlet容器,Tomcat在Servlet生命周期包括了包容、装载、运行和停止Servlet容器。

这里主要讲解在Linux系统下安装Tomcat服务器,以CentOS 7(安装桌面“GNOME”)系统为例。安装过程如下:

【步骤1】在“/usr/local”目录下创建“tomcat”目录,将下载的Tomcat安装包放在该目录下。代码如下:

cd /usr/local

mkdir tomcat

cd tomcat

【步骤2】下载Tomcat,进入Tomcat官网,找到与操作系统对应的安装包,直接下载,如图6-2-16所示。

图6-2-16 下载Tomcat

Tomcat下载完毕后,会存放在“HOME”/“Downloads”目录中,将下载好的Tomcat复制到预设的“/usr/local/tomcat”文件中,如图6-2-17所示。用命令“cd”进入到“/usr/local/tomcat”目录,用“ls”命令查看该目录中是否有Tomcat安装包文件,如图6-2-18所示。

图6-2- 17 下载的Tomcat存放的目录

图6-2-18 查看目录

【步骤3】安装Tomcat,在安装Tomcat前,先要安装JDK。解压Tomcat安装包,并重新命名为“tomcatfile”,如图6-2-19所示,代码如下:

tar -zxv -f apache-tomcat-8.5.37.tar.gz

mv apache-tomcat-8.5.37 tomcatfile

cd tomcatfile

图6-2-19 重命名

【步骤4】启动Tomcat。解压完后可以启动Tomcat,检查是否安装成功,进入安装“Tomcat”目录下“bin”,运行startup.sh文件,就可以启动Tomcat服务器,如图6-2-20所示,代码如下:

./bin/startup.sh
图6-2- 20 启动Tomcat服务器

【步骤5】访问服务器。如图6-2-20中已经成功启动Tomcat服务器,现在开始访问服务器,通过http://ip:8080访问服务器。如IP地址为:192.168.1.99,访问服务器为http://192.168.1.19:8080,如图6-2-21所示。

图6-2-21 正常访问服务器

如果能正常访问,那就不用配置防火墙,如果不能访问,就需要配置防火墙,开放8080端口。

项目实施

在用户注册场景中,通过消息队列发送邮件,短信来完成异步处理 ;在双11购物节场景中,将用户下单后的消息写入消息队列,返回用户订单下单成功,而库存系统则通过订阅下单的消息,获取下单消息。如何实现消息队列?技术人员常采用RabbitMQ中间件来完成信息发布与订阅。

需要完成的任务:

RabbitMQ消息代理软件。

消息发布及订阅系统开发。

6.3任务1:RabbitMQ安装与配置

消息队列(Message Queue,MQ)是应用程序和应用程序之间的通信方法。MQ是消息通信的模型,实现MQ的大致有两种主流方式,分别为高级消息队列协议AMQP和Java消息服务(Java Message Service)应用程序接口JMS。目前市面上成熟主流的MQ有Kafka 、RocketMQ、RabbitMQ。本章以RabbitMQ产品为例详细讲解。

6.3.1 RabbitMQ简介

RabbitMQ是实现了高级消息队列协议AMQP的开源消息代理软件,也叫面向消息的中间件。

1.RabbitMQ概述

RabbitMQ是使用Erlang编写的一个开源的消息队列,本身支持很多的协议,如AMQP、XMPP、SMTP、STOMP等。基于此RabbitMQ变的非常重量级,适合于企业级的开发,它实现了Broker架构,其核心思想是生产者不会将消息直接发送给队列,消息在发送给客户端时先在中心队列排队,除此RabbitMQ对路由、负载均衡、数据持久化都有很好的支持。

RabbitMQ服务能支持多种类型的操作系统,如Linux系列、Windows系列、MAC OS、Solaris、FreeBSD等。RabbitMQ也支持多种开发语言,如Python、Java、Ruby、PHP、C#、JavaScript、Go、Elixir、Objective-C、Swift等。

2.RabbitMQ的工作模式

RabbitMQ提供了6种工作模式,分别为简单模式、Work模式、Publish/Subscribe发布与订阅模式、Routing路由模式、Topics主题模式、RPC远程调用模式。其中RPC远程调用模式不算消息队列,这里不进行讲解。RabbitMQ的6种工作模式,如图6-3-1所示。

图6-3-1RabbitMQ的6种模式

(1)简单模式

简单模式是指消息产生者将消息放入队列,消息的消费者监听消息队列,如果队列中有消息,就消费掉,消息被拿走后,自动从队列中删除。

简单模式的应用场景,如聊天等场景。

(2)Work模式

Work模式是指消息产生者将消息放入队列,消费者可以有多个,如消费者1、消费者2等。消费者同时监听同一个队列,消息被消费者1、消费者2等共同争抢当前的消息队列中的内容,谁先拿到谁负责消费消息。

Work模式的应用场景,如红包、大项目中的资源调度等。

(3)Publish/Subscribe发布与订阅模式

Publish/Subscribe发布与订阅模式如图6-3-2所示,X代表交换机RabbitMQ内部组件,Erlang消息产生者是由代码完成,代码的执行效率不高,消息产生者将消息放入交换机,交换机发布订阅把消息发送到所有消息队列中,对应消息队列的消费者拿到消息进行消费。

Publish/Subscribe发布与订阅模式的适用场景,如邮件群发、群聊天、广播等。

图6-3- 2Publish/Subscribe发布与订阅模式

(4)Routing路由模式

Routing路由模式是指消息生产者将消息发送给交换机,交换机按照路由进行判断。路由是字符串构成,当前产生的消息中携带路由字符,交换机根据路由的Key,找到匹配上路由Key对应的消息队列,这个对应的消费者才能消费消息。

(5)Topics主题模式

Topics主题模式是指根据路由功能添加模糊匹配,模糊匹配主要通过“”、“#”来完成,其中“”表示通配符代表多个单词,“#”表示代表一个单词。消息产生者产生消息,把消息交给交换机,交换机根据Key的规则模糊匹配到对应的队列,由队列的监听消费者接收消息消费。

3.RabbitMQ的使用场合

(1)异步处理场合

场景说明,当用户注册后,需要发注册邮件和注册短信,传统的做法有两种,分别为串行方式 和并行方式。采用RabbitMQ后可以用消息队列解决异步处理问题。

①串行方式是将注册信息写入数据库后,发送注册邮件,再发送注册短信,以上三个任务全部完成后才返回给客户端。采用这种方式存有一个问题,这个问题就是邮件、短信并不是必须的,它只是一个通知。

②并行方式是将注册信息写入数据库后,在发送邮件的同时也发送短信,以上三个任务完成后,返回给客户端,并行的方式能提高处理的时间。假设三个业务节点分别使用50ms,串行方式使用时间150ms,并行使用时间100ms。虽然并行方式已经提高了处理时间,但是,邮件和短信对正常的使用网站没有任何影响,客户端没有必要等着其发送完成才显示注册成功,应该是写入数据库后就返回注册成功。

③消息队列 。引入消息队列后,发送邮件、短信不是必须的业务逻辑,可以将他们进行异步处理 ,如图6-3-2所示。

图6-3- 3消息队列异步处理

(2)应用解耦长街

场景说明,双11是购物狂节,用户下单后,订单系统需要通知库存系统,传统的做法就是订单系统调用库存系统的接口。采用这种方法存在一个缺点,当库存系统出现故障时,订单就会失败。

可以消息队列将订单系统和库存系统进行高耦合。在订单系统中,当用户下单后,订单系统完成持久化处理,将消息写入消息队列,返回用户订单下单成功。在库存系统中,订阅下单的消息,获取下单消息,进行库操作。 采用消息队列方法就算库存系统出现故障,消息队列也能保证消息的可靠投递,不会导致消息丢失。

(3)流量削峰场景

场景说明,流量削峰一般在秒杀活动中应用广泛 。秒杀活动,一般会因为流量过大,导致应用挂掉,为了解决这个问题,一般在应用前端加入消息队列。

消息队列在削峰场景中起到的作用有, 可以控制活动人数,超过此一定阀值的订单直接丢弃,可以缓解短时间的高流量压垮应用。

6.3.2 RabbitMQ安装与配置

1.安装RabbitMQ

通过官方地址下载RabbitMQ软件。由于RabbitMQ是基于Erlang语言开发的,所以必须先安装Erlang,然后再安装RabbitMQ。以CentOS操作系统为例,采用源码安装RabbitMQ。

(1)安装Erlang

【步骤1】下载Erlang。使用“wget”命令从官网中下载Erlang。在“/usr/local”目录下创建“erlang”目录,将下载的Erlang放在该目录下,因为Erlang编译安装默认是装在“/usr/local”下的bin和lib中,这里将它统一装到“/usr/local/erlang”中,方便查找和使用。如图6-3-4所示,代码如下:

cd /usr/local/
mkdir erlang
cd erlang
wget http://erlang.org/download/otp_src_21.3.tar.gz    
图6-3-4 下载erlang

【注意】如果Erlang文件不再预定的目录下,可以用Linux复制文件命令 “cp -rp old/ new/”。另外下载Erlang时间较长,需要耐心等待。

【步骤2】解压Erlang,使用tar命令进行解压,代码如下:

tar -zxvf otp_src_21.3.tar.gz

【步骤3】安装依赖包。在编译Eelang之前,必须安装以下依赖包,代码如下:

yum install libtool

yum install libtool-ltdl-devel

yum install gcc-c++

yum install erlang-doc

yum install erlang-jinterface

【备注】依赖包较多,要逐一检查,除此Erlang的编译需要用到Java环境,如果没有安装JDK,则会报错。

【步骤4】编译与安装。进入Erlang解压后的目录进行编译,执行命令“./configure”进行编译,如图6-3-5所示。编译成功如图6-3-6所示。

./configure --prefix=/usr/local/erlang  
图6-3- 5编译

图6-3-6忽略错误

直接执行make或者makeinstall命令进行编译安装,这个过程需要几分钟时间。代码如下:

make 

#或者

make install

【步骤5】查看是否安装成功。进入安装路径的“bin”目录,如Erlang安装路径为“/usr/local/erlang/otp_src_21.3”,在该目录中有“cerl”文件,就说明安装成功,如图6-3-7所示。

图6-3- 7进步bin目录

【步骤6】添加环境变量。然后将“/usr/local/erlang/bin/otp_src_21.1”目录加入到环境变量中,代码如下:

vim /etc/profile

PATH=$PATH:/usr/local/erlang/bin

\#或者

echo 'export PATH=$PATH:/usr/local/erlang/opt_src_21.1/bin' >> /etc/profile

刷新环境变量,代码如下:

source /etc/profile

测试安装是否成功,进入Erlang安装目录的“bin”目录下,直接输入./erl命令,如图6-3-8所示,则说明Erlang安装成功。命令如下:

./bin/erl

图6-3-8 输入erl命令

【步骤7】退出Erlang。输入“halt().”命令退出来(点号别忘记)。代码如下:

halt().

(2)安装RabbitMQ

【步骤1】下载RabbitMQ。在安装RabbitMQ之前,需要去官网查看一下RabbitMQ版本对Erlang版本的支持情况,如图6-3-9所示。

图6-3-9 RabbitMQ与Erlang版本对应

安装的Erlang是21版本,RabbitMQ对应的版本3.7.19、3.7.20和3.8.0。在官网上,直接下载该版本的安装包,如图6-3-10所示。

图6-3-10下载rabbitmq

【步骤2】解压安装包。将安装包放在“/usr/local/rabbitmq”目录下,进行解压。如果解压过程中出现错误,可能原因是由于压缩包是“tar.xz”格式的所以需要用到xz,没有就先安装 。安装xz代码如下:

yum install -y xz

解压用“tar -xvf”命令解压,如图6-3-11所示,代码如下:

tar –xvf rabbitmq-unix-3.7.19.tar
图6-3- 11解压RabbitMQ

为了便于记忆,将名字重新命名为rabbitmq3.7.19,如图6-3-12所示。

mv /usr/local/rabbitmq/rabbitmq_server-3.7.19 rabbitmq3.7.19
图6-3-12 重命名

【步骤3】配置环境变量。将安装的RabbitMQ下的“sbin”目录,写入环境变量,代码如下:

echo 'export PATH=$PATH:/usr/local/RabbitMQ/rabbitmq-3.8/sbin' >> /etc/profile

刷新环境变量,代码如下:

source /etc/profile

【步骤4】创建配置目录,代码如下:

mkdir /etc/rabbitmq

【步骤5】测试RabbitMQ是否安装成功,在root权限下,命令如下:

#开启rabbitmq服务

rabbitmq-server -detached

#查看服务状态

rabbitmqctl status

#开启rabbitmq

rabbitmqctl start_app

【步骤6】开启管理插件,命令为“ rabbitmq-plugins enable rabbitmq_management”,如图6-3-13所示。

图6-3- 13 开启管理插件

运用“rabbitmq-plugins list”命令查看插件集合,如图6-3-14所示。

图6-3- 14查看插件集合

通过“IP:15672”访问可视化界面,如IP为“192.168.1.4”,访问可视化界面,如图6-3-15所示。到这里RabbitMQ就安装成功。

图6-3-15 访问可视化界面

6.3.3 RabbitMQ的基本应用

RabbitMQ基本使用包括启动服务、创建用户、启用Web管理及开机启动。

1. 创建一个用户

RabbitMQ其自带了“guest/guest”的用户名和密码,也可以创建自定义用户。

创建账号为“admin”,密码为“admin”,如图6-3-16所示,代码如下:

图6-3- 16 创建用户

将admin用户赋予超级管理员权限。

rabbitmqctl set_user_tags admin administrator

rabbitmqctl set_permissions -p "/" admin "." "." ".*"

2.启用Web管理

启用Web管理,命令如下:

rabbitmq-plugins enable rabbitmq_management

如果是通过Java连接使用的是5672端口,通过浏览器访问“IP:15672”端口来登录管理平台,以浏览器访问为例,账号密码就是之前创建的“admin”,如图6-3-17所示。点击图6-3-17中的“Login”进入到RabbitMQ的管理界面,如图6-3-18所示。

图6-3- 17 浏览器访问
图6-3- 18 RadditMQ管理界面

6.4 任务2:消息发布及订阅系统开发

RabbitMQ的设计理念是只要有接收消息的队列,消息就会存放到队列里,直到订阅人取走,如果没有可以接收这个消息的消息队列,默认是抛弃这个消息的。基于这样的设计理念,RabbitMQ可以实现消息发布和订阅系统。

6.4.1 RabbitMQ Web端的使用

通过“IP:15672”访问RabbitMQ Web端的管理界面,如IP为“182.168.1.4:15672”,首先认识RabbitMQ Web端的各种选项卡。

1.“Overview”概览选项卡

“Overview”选项卡表示概览,在该选项卡“Totals”项这里展示的是统计信息,对队列中的消息进行统计,如图6-4-1。在“Node”项显示的是RabbitMQ的服务节点,目前有一个节点,这里可以有多个服务节点,如图6-4-2所示。

图6-4- 1 Tatals消息统计
图6-4- 2Nodes节点信息

“Ports and contexts”展示的是端口信息,如图6-4-3所示。这里一共有三个端口,其中5672是amqp协议的端口,15672是RabbitMQ的管理工具端口,25672是做集群的端口。如果Java程序要与RabbitMQ进行交互,需要5672端口交互,因为Java客户端需要与RabbitMQ服务进行数据交互,必须要遵循amqp协议,所以要走5672端口。15672是RabbitMQ的管理工具的端口,与服务无关,仅仅是管理工具运行的端口。

图6-4- 3 端口信息

2.“Connections”连接选项卡

在“Connections”选项卡中可以看到客户端连接RabbitMQ服务的信息,目前尚未有客户端连接,所以上面看不到连接信息,如图6-4-4所示。

图6-4- 4 connetions客户端信息

3.“Channels”通道选项卡

“Channels”通道选项卡,如图6-4-5所示。

图6-4- 5“Channels”通道选项卡

4.“Exchanges”通道选项卡

“Exchanges”通道选项卡,如图6-4-6所示。

图6-4- 6“Exchanges”通道选项卡

5.“Queues” 队列选项卡

“Queues”队列选项卡,如图6-4-7所示,可以在“Add a new queue”手动添加新的队列。

图6-4- 7“Queues”队列选项卡

6.4.1RabbitMQ用户及Virtual Hosts配置

RabbitMQ中用户有超级管理员(Admin)、监控者(Monitoring)、策略制定者(Policymaker)、普通管理者(Management)、其他等。

1.用户介绍

超级管理员(Admin)可登陆管理控制台,可查看所有的信息,并且可以对用户、策略进行操作。

监控者(Monitoring),可登陆管理控制台,同时可以查看RabbitMQ节点的相关信息(进程数、内存使用情况、磁盘使用情况等)。

策略制定者(Policymaker),可登陆管理控制台,同时可以对策略进行管理。但无法查看节点的相关信息。

普通管理者(Management),仅可登陆管理控制台,无法看到节点信息,也无法对策略进行管理。

其他角色,无法登陆管理控制台,通常就是普通的生产者和消费者。

2.增加用户

以超级管理员用户登录RabbitMQ管理平台,可以添加用户。点击“Admin”选项,选择“All users”→“Add a user”,填写用户名和密码,设置用户权限,最后点击“Add user”按钮,这样用户就添加成功,如图6-4-8所示。

图6-4-8添加用户

3.用户管理

以超级管理员用户登录RabbitMQ管理平台,可以对用户进行管理,如查看用户信息、查找用户、设置用户信息等。如图6-4-9所示。

图6-4- 9 管理用户

4.Virtual Hosts配置

RabbitMQ能像MySQL拥有可以指定用户对库和表等操作的权限管理。在RabbitMQ中有虚拟消息服务器Virtual Host,每个Virtual Hosts相当于一个相对独立的RabbitMQ服务器,每个Virtual Host之间是相互隔离的,exchange、queue、message不能互通。

(1)创建Virtual Hosts

【步骤1】选择“Vritual Hosts”。点击“Admin”→“Vritual Hosts”,如图6-4-10所示。

图6-4- 10选择“Vritual Hosts”

【步骤2】设置Virtual Hosts的名字。点击“Add a new virtual hosts”,设置虚拟消息服务器的名字,一般以“/”开头,然后点击“Add virtual hosts”按钮,如图6-4-11所示。

图6-4- 11设置Virtual Hosts的名字

【步骤3】设置Virtual Hosts权限。在“Admin”→“Virtual Hosts”→“All virtual hosts”中,查看所有的创建好的Virtual Hosts的列表,如图6-4-12所示。

图6-4-12Virtual Hosts的列表

选择要设置权限的Virtual Hosts的名称,如图6-4-12中的“rabbit2”,弹出Virtual Hosts的权限设置页面,选择指定账号,如图6-4-13所示。

图6-4-13 设置指定账号权限

图6-4-13中参数说明,“User”表示用户名,“Exchange”表示交互,“Write regexp”一个正则表达式,用户对符合该正则表达式的所有资源拥有写操作的权限,“Read regexp”表示一个正则表达式,用户对符合该正则表达式的所有资源拥有读操作的权限。

6.4.2 RabbitMQ的消息发布及订阅系统开发

RabbitMQ能实现消息发布和订阅,需要Exchange、消息的发布者、消费者、消息队列等。具体实现思路为消息的生产者发出消息,然后经过Exchange转换,消息队列(Queue)需要绑定Exchange,Exchange把消息发送到各个消息队列中,然后,各个消费者从消息队列中取到发布者发布的消息。简单来说发布/订阅模式即生产者将消息发送给多个消费者。

在消息队列的一发一收中,RabbitMQ是如何发送消息和接收消息?RabbitMQ收发的过程分为发消息过程和收消息过程。

1.生成者发消息的过程

①首先连接到RabbitMQ Borker,建立一个连接(Connection),开启一个信道(Channel)。

②声明交换机(Exchange)。

③声明队列(Queue)。

④通过路由键(Binding Key)将交换机与路由器绑定。

⑤发送消息(消息包含路由键(Routing Key)和交换机等内容)到RabbitMQ Borke。

⑥交换机根据接收到路由键去匹配到相应的队列中,如果找到则放入到对应的队列中,找不到则退回(这里是根据配置信息来的)。

⑦关闭。

2.消费者收消息的过程

①连接到RabbitMQ Borker,建立一个连接,开启一个信道。

②请求接收RabbitMQ Borker中队列的消息。

③等待RabbitMQ Borker回应返回队列中相应的消息。

④消费者接收到消息,返回确认(ack)。

⑤RabbitMQ移除队列中对应的消息。

⑥关闭。

【实例6-4-1】采用Java开发语言实现RabbitMQ的消息发布与订阅。采用Java语言作为开发语言,分别编写发布者类代码和消息接收类代码。

1.准备Java开发工具软件

Eclipse是著名的跨平台的自由集成开发环境(IDE),主要用来Java语言开发。以CentOS系统为例,安装Eclipse软件。

【步骤1】下载Eclipse软件。用“eget”命令到官网下载与操作系统匹配的Eclipse软件,如操作系统是64位,则需要下载64位的Eclipse。

【步骤2】解压Eclipse软件,用“tar”命令解压。

【步骤3】启动Eclipse软件,通过“./eclipse-inst”,就可以直接启动了,如图6-4-14所示。

图6-4- 14启动Eclipse

2.编写发布者类

用Java语言编写Emitlog类文件,取名为“Emitlog.java”文件,代码如下:

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
 // 用rabbitMQ实现发布订阅
public class Emitlog {
private static final String EXCHANGE_NAME = "logs";
public static void main(String args[]) throws Exception{
    ConnectionFactory connectionFactory = new ConnectionFactory();
    connectionFactory.setHost("localhost");
    Connection connection = connectionFactory.newConnection();
    Channel channel = connection.createChannel();
    channel.exchangeDeclare(EXCHANGE_NAME,"fanout");
    String msg = "发布最新消息XXXXXXXXX";
    channel.basicPublish(EXCHANGE_NAME,"",null,msg.getBytes());
    System.out.println("[send] msg: " +msg);
    channel.close();
    connection.close();
  }
}

3.编写消息接收类

用Java语言编写ReceiveLogs类文件,取名为“ReceiveLogs.java”文件,代码如下:

import com.rabbitmq.client.*;
import java.io.IOException;
// RabbitMQ实现发布订阅的功能订阅类
public class ReceiveLogs {
  private static final String EXCHANGE_NAME = "logs";
  public static void main(String[] args) throws Exception{
    ConnectionFactory factory = new ConnectionFactory();
    factory.setHost("localhost");
    Connection connection = factory.newConnection();
    Channel channel = connection.createChannel();
    channel.exchangeDeclare(EXCHANGE_NAME,"fanout");
    String queueName = channel.queueDeclare().getQueue();
    channel.queueBind(queueName,EXCHANGE_NAME,"");
    System.out.print("[*] waiting for message");
    DefaultConsumer consume = new DefaultConsumer(channel){
      @Override
      public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
        String message = new String(body,"UTF-8");
         System.out.println("[x] receive message :" + message);
      }
    };
    channel.basicConsume(queueName,true,consume);
  }
}

4.运行发布者和接收消息类

分别运行发布者类ReceiveLogs和接收消息类Emitlog。

Emitlog类运行结果为:

[send] msg: "发布最新消息XXXXXXXXX";

Process finished with exit code 0

ReceiveLogs类运行结果:

[*] waiting for message[x] receive message : 发布最新消息XXXXXXXXX

【实例6-4-2】RabbitMQ/Java(发布/订阅模式),关于日志系统的完整例子,实例中RabbitMQ服务器的IP地址为“172.168.1.4”。

1.生成者发送端代码

生成者发送端代码采用Java语言,编写EmitLog.java,代码如下:

package sublog;
import java.io.IOException;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
public class EmitLog {
  private final static String EXCHANGE_NAME = "logs";
  public static void main(String[] args) throws IOException {
    // 创建连接连接到MabbitMQ
    ConnectionFactory factory = new ConnectionFactory();
    // 设置MabbitMQ所在主机ip或者主机名
    factory.setHost("115.159.181.204");
    factory.setPort(5672);
    factory.setUsername("admin");
    factory.setPassword("admin");
    // 创建一个连接
    Connection connection = factory.newConnection(); 
    // 创建一个频道 
    Channel channel = connection.createChannel(); 
    // 指定转发——广播
    ((com.rabbitmq.client.Channel) channel).exchangeDeclare(EXCHANGE_NAME, "fanout");
    for(int i=0;i<3;i++){
      // 发送的消息
      String message = "Hello World!";
      ((com.rabbitmq.client.Channel) channel).basicPublish(EXCHANGE_NAME, "", null, message.getBytes());
      System.out.println(" [x] Sent '" + message + "'");
    }
    // 关闭频道和连接
    channel.close();
    connection.close();
  }
}

2.消费者1代码

消费者1代码采用Java语言,编写 ReceiveLogs2Console.java,代码如下:

package sublog;
import java.io.IOException;
import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.AMQP.Connection;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
public class ReceiveLogs2Console {
  private static final String EXCHANGE_NAME = "logs";
  public static void main(String[] argv) throws IOException, InterruptedException {
    ConnectionFactory factory = new ConnectionFactory();
//设置主机IP,登录用户名和端口
    factory.setHost("172.168.1.4");
    factory.setPort(5672);
    factory.setUsername("admin");
    factory.setPassword("admin");
    // 打开连接和创建频道,与发送端一样
    com.rabbitmq.client.Connection connection =factory.newConnection();
    final Channel channel = connection.createChannel();
    channel.exchangeDeclare(EXCHANGE_NAME, "fanout");
    // 声明一个随机队列
    String queueName = channel.queueDeclare().getQueue();
    channel.queueBind(queueName, EXCHANGE_NAME, "");
    System.out.println(" [*] Waiting for messages. To exit press CTRL+C");
     // 创建队列消费者
    final Consumer consumer = new DefaultConsumer(channel) {
       @Override
       public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
        String message = new String(body, "UTF-8");
        System.out.println(" [x] Received '" + message + "'");
       }
      };
      channel.basicConsume(queueName, true, consumer);
  }
}

3.消费者2代码

消费者2采用Java语言,消费者2 ReceiveLogs2File.java代码如下:

package sublog;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.AMQP.Connection;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;

public class ReceiveLogs2File {
  private static final String EXCHANGE_NAME = "logs";
  public static void main(String[] argv) throws IOException, InterruptedException {
    ConnectionFactory factory = new ConnectionFactory();
     //设置主机IP,登录用户名和端口
    factory.setHost("192.168.1.4");
    factory.setPort(5672);
    factory.setUsername("admin");
    factory.setPassword("admin");
    // 打开连接和创建频道,与发送端一样
    com.rabbitmq.client.Connection connection = factory.newConnection();
    final Channel channel = connection.createChannel();
    channel.exchangeDeclare(EXCHANGE_NAME, "fanout");
    // 声明一个随机队列
    String queueName = channel.queueDeclare().getQueue();
    channel.queueBind(queueName, EXCHANGE_NAME, "");
    System.out.println(" [*] Waiting for messages. To exit press CTRL+C");
    // 创建队列消费者
    final Consumer consumer = new DefaultConsumer(channel) {
       @Override
       public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
        String message = new String(body, "UTF-8");
        print2File(message);
//        System.out.println(" [x] Received '" + message + "'");
       }
       };
      channel.basicConsume(queueName, true, consumer);
  }
    private static void print2File(String msg) {
    try {
String dir = ReceiveLogs2File.class.getClassLoader().getResource("").getPath();
String logFileName = new SimpleDateFormat("yyyy-MM-dd").format(new Date());
      File file = new File(dir, logFileName + ".log");
      FileOutputStream fos = new FileOutputStream(file, true);
      fos.write((new SimpleDateFormat("HH:mm:ss").format(new Date())+" - "+msg + "\r\n").getBytes());
      fos.flush();
      fos.close();
    } catch (FileNotFoundException e) {
      e.printStackTrace();
    } catch (IOException e) {
      e.printStackTrace();
    }
  } 
}

实例中用一个生产者用于发送Log消息,设置了两个消费者,一个用于打印接收到的消息,另一个除了打印接收到的消息还写有日志信息的文件。

生产者声明了一个广播模式的转换器,订阅这个转换器的消费者都可以收到每一条消息。可以看到在生产者中,没有声明队列,生产者其实只关心Exchange,至于Exchange会把消息转发给哪些队列,并不是生产者关心的。

两个消费者,一个打印日志,一个写入文件。声明一下广播模式的转换器,而队列则是随机生成的,消费者实例启动后,会创建一个随机实例,这个在管理页面可以看到,如图6-4-15所示。而实例关闭后,随机队列也会自动删除。最后将队列与转发器绑定。

图6-4- 15创建随机实例

运行的时候要先运行两个消费者实例,然后在运行生产者实例。否则获取不到实例,如图6-4-16所示。

图6-4- 16运行消费者

本章小结

本章主要讲解了中间件技术、JDK与Tomcat引用服务器、RabbitMQ安装与配置、消息发布与订阅系统开发。通过本章的学习,读者应理解中间件的基本概念、中间件的分类、了解常见中间件的开源产品、能在Linux系统下安装与配置JDK、Tomcat、能在Linux系统下安装与配置RabbitMQ、能使用RabbitMQ、能实现RabbitMQ的消息发布与订阅系统的开发等知识与技能。

本章习题

一、单项选择题

1.下列关于中间件的描述正确的是( )。

A.中间件是应用型软件

B.中间件是操作系统中的一种

C.中间件处于操作系统和应用程序之间的软件

D.中间件是虚拟化底层的软件

2.RabbitMQ中间件属于( )类中间件。

A.交易中间件

B.消息中间件

C.对象中间件

D.应用服务器

3.JDK(Java Development Kit)是( )语言的软件开发工具包。

A.PHP

B.Python

C.C++

D.Java

4.在JDK中设置JAVA_HOME环境变量值( )。

A.JDK的安装路径

B.JDK的安装路径下Bin目录

C.JDK的安装路径下Jre目录

D.JDK的安装路径下Src目录

5.在Centos系统中安装Tomcat时,在( )用户下安装。

A.个人用户

B.Root用户

6.Tomcat服务器默认的端口是( )。

A.8080

B.80

C.8091

7.由于RabbitMQ是基于( )语言开发的。

A.Java

B.Erlang

C.C

D.Python

8.运行Erlang时,直接输入( )命令。

A.erl

B.echo

C.make

D.source

9.通过“IP”访问RabbitMQ可视化界面,其端口为( )。

A.8089

B.3316

C.15672

D.5672

二、多选题

1.中间件的基本功能( )。

A. 通信支持

B. 应用支持

C. 公共服务

D. 数据存储

2.中间件的分类有那些( )。

A. 消息中间件

B. 交易中间件

C. 对象中间件

D. 应用服务器

3.RabbitMQ提供了6种工作模式,下列哪些选项是RabibitMQ的工作模式。( )

A.Work模式

B.Publish/Subscribe发布与订阅模式

C.Routing路由模式

D.RPC远程调用模式