MQ全稱爲Message Queue, 消息隊列(MQ)是一種應用程序對應用程序的通信方法。應用程序通過讀寫出入隊列的消息(針對應用程序的數據)來通信,而無需專用連接來鏈接它們。
消息傳遞指的是程序之間通過在消息中發送數據進行通信,而不是通過直接調用彼此來通信,直接調用通常是用於諸如遠程過程調用的技術。
排隊指的是應用程序通過 隊列來通信。隊列的使用除去了接收和發送應用程序同時執行的要求。其中較爲成熟的MQ產品有IBM WEBSPHERE MQ。
MQ是消費-生產者模型的一個典型的代表,一端往消息隊列中不斷寫入消息,而另一端則可以讀取或者訂閱隊列中的消息。MQ和JMS類似,但不同的是JMS是SUN JAVA消息中間件服務的一個標準和API定義,而MQ則是遵循了AMQP協議的具體實現和產品。
在項目中,將一些無需即時返回且耗時的操作提取出來,進行了異步處理,而這種異步處理的方式大大的節省了服務器的請求響應時間,從而提高了系統的吞吐量。
RabbitMQ是一個在AMQP基礎上完整的,可複用的企業消息系統。他遵循Mozilla Public License開源協議。
現在公司項目需要,要安裝RabbitMQ,在安裝之前,要確定依賴的環境要有:erlang、python、simplejson,下面將我安裝過程過記錄如下。
一、安裝erlang
依次執行如下命令:
tar -zxf otp_src_R16B.tar.gz #具體版本 cd otp_src_R16B ./configure --prefix=/opt/ make make install
在安裝Erlang過程中可能會出現如下問題:
checking for kstat_open in -lkstat... no checking for tgetent in -lncurses... no checking for tgetent in -lcurses... no checking for tgetent in -ltermcap... no checking for tgetent in -ltermlib... no configure: error: No curses library functions found configure: error: /bin/sh '/home/niewf/software/erlang_R13B01/erts/configure' failed for erts
在網上找了一下,發現該問題在安裝Mysql的時候也可能發生。
具體原因爲:缺少ncurses安裝包。因此要先安裝ncurses,安裝過程如下:
a)、如果你的系統是RedHat系列:
yum list|grep ncurses yum -y install ncurses-devel yum install ncurses-devel
b)、如果你的系統是Ubuntu或Debian:
apt-cache search ncurses apt-get install libncurses5-dev
安裝之後,驗證是否安裝成功,執行以下命令並顯示結果:
[root@sckf otp_src_R16B]# erl Erlang R16B (erts-5.10.1) [source] [64-bit] [smp:24:24] [async-threads:10] [hipe] [kernel-poll:false] Eshell V5.10.1 (abort with ^G)
表示erlang已經安裝成功。
二、安裝python
在操作系統安裝時要選擇上python安裝,CRM5.7的OS是6.2,所以python不需升級。
如果機器的python版本過低,編譯simplejson時會出錯,需安裝python較新版本,我這裏使用的是Python-3.4.1.tgz。
tar -zxf Python-3.4.1.tgz cd Python-3.4.1 ./configure make make install mv/usr/bin/python /usr/bin/python-old ln -s /usr/local/bin/python /usr/bin/python
安裝完成後驗證python版本
[root@sckf ~]# python -V Python 3.4.1 [root@sckf ~]# which python /usr/bin/python [root@sckf~]#
三、安裝simplejson
一般都未安裝simplejson,如果機器未安裝,在安裝過程中會出現出現如下提示:
You don't appear to have simplejson.py installed
這時需先安裝simplejson,這裏安裝最新的simplejson:simplejson-3.3.1.tar.gz。
然後執行以下命令:
tar xvzf simplejson-2.6.1.tar.gz cd simplejson-2.6.1 python setup.py install
四、安裝rabbitmq
在http://www.rabbitmq.com/download.html下載相應安裝包,請使用當前最新的穩定版本。
我這裏使用的版本是:rabbitmq-server-generic-unix-3.2.2.tar.gz
(注意:請不要在內核是pae版本的os上安裝,因爲rabbitmq的可用內存會因爲pae而大大減小。)
1. 直接使用generic版本(推薦的方式)
[root@localhost rabbitmq]# tar -zxfrabbitmq-server-generic-unix-3.2.2.tar.gz [root@localhost rabbitmq]# ll 總計 3804 drwxr-xr-x 8 root root 4096 11-07 18:54 rabbitmq_server-3.2.2 -rw-r--r-- 1 root root 3886276 11-20 11:23rabbitmq-server-generic-unix-3.2.2.tar.gz [root@localhost rabbitmq]# cd rabbitmq -bash: cd: rabbitmq: 沒有那個文件或目錄 [root@localhostrabbitmq]# cd rabbitmq_server-3.2.2/
2. 啓動rabbitmqserver
./rabbitmq-server -detached
(可能會因爲沒有日誌文件夾而啓用失敗,只需要創建相應文件夾後重新運行該命令即可)
執行一下命令:
make TARGET_DIR=/opt/rabbitmq_server-3.2.2 SBIN_DIR=/opt/rabbitmq_server-3.2.2l/sbin MAN_DIR=/opt/rabbitmq_server-3.2.2/man make install
3. 啓用web管理界面
$rabbimq_home$/sbin/ ./rabbitmq-plugins enable rabbitmq_management
啓用web管理插件時會由於在/etc下無rabbitmq目錄而失敗,請建立/etc/rabbitmq目錄;
之後我們就可以通過http://localhsot:15672/來訪問web管理界面了
交易日誌中間件需要啓用stomp插件(看應用是否需要再啓用)
$rabbimq_home$/sbin/ ./rabbitmq-plugins enable rabbitmq_stomp
五、集羣配置(單節點部署不需要這部分)
假設有兩個節點10.109.1.149,10.109.1.158
(1)修改機器的hostname,例:
10.109.1.149 : export HOSTNAME=”server149” 10.109.1.158 : export HOSTNAME=”server158”
注意:
這裏只是會話級修改,所以必須修改/etc/sysconfig/network文件才行。
(2)修改/etc/hosts文件,使得DNS可解析HOSTNAME,例:
10.109.1.149 server149 10.109.1.158 server158
(3)保證兩臺機器的.erlang.cookie文件相同,以便他們之間能互相通信
將141的文件(/root/.erlang.cookie)scp至143,然後執行如下命令:
cat erlang.cookie.141 >>.erlang.cookie
再將.erlang.cookie拷貝至其他機器即可,其目的是要將集羣裏的所有節點的.erlang.cookie文件cat到一箇中,然後再拷貝至其他所有的節點,這樣所有節點的.erlang.cookie文件一致,他們才能夠互信。
(4)假設158作爲磁盤節點,149作爲內存節點
啓動158的rabbitmqserver
$rabbimq_home$/sbin/ ./rabbitmq-server -detached
(5)啓動149的rabbitmqserver並以內存節點的形式加入集羣
$rabbimq_home$/sbin/ ./rabbitmq-server -detached $rabbimq_home$/sbin/ ./rabbitmqctl stop_app $rabbimq_home$/sbin/ ./rabbitmqctl reset $rabbimq_home$/sbin/ ./rabbitmqctl join_cluster --ram rabbit@server158 $rabbimq_home$/sbin/ ./rabbitmqctl start_app
至此集羣環境搭建成功,可以使用rabbitmqctl cluster_status來查看集羣狀態。
六、測試實例
ReceiveTest:
package com.asiainfo.mq.rabbitmq.rabbitmqtest; import java.util.Date; import com.rabbitmq.client.ConnectionFactory; import com.rabbitmq.client.Connection; import com.rabbitmq.client.Channel; import com.rabbitmq.client.QueueingConsumer; public class ReceiveTest { private final static String QUEUE_NAME = "hello"; public static void main(String[] argv) throws java.io.IOException, java.lang.InterruptedException { ConnectionFactory factory = new ConnectionFactory(); factory.setHost("10.5.1.11"); Connection connection = factory.newConnection(); Channel channel = connection.createChannel(); channel.queueDeclare(QUEUE_NAME, false, false, false, null); System.out.println(" [*] Waiting for messages. To exit press CTRL+C"); QueueingConsumer consumer = new QueueingConsumer(channel); channel.basicConsume(QUEUE_NAME, true, consumer); Date nowTime = new Date(); while (true) { QueueingConsumer.Delivery delivery = consumer.nextDelivery(); String message = new String(delivery.getBody()); System.out.println("RecieveTime: " + nowTime); System.out.println(" [x] Received '" + message + "'"); } } }
SendTest:
package com.asiainfo.mq.rabbitmq.rabbitmqtest; import java.util.Date; import com.rabbitmq.client.ConnectionFactory; import com.rabbitmq.client.Connection; import com.rabbitmq.client.Channel; public class SendTest { private final static String QUEUE_NAME = "hello"; public static void main(String[] argv) throws java.io.IOException { try { ConnectionFactory factory = new ConnectionFactory(); factory.setHost("10.5.1.11"); Connection connection = factory.newConnection(); Channel channel = connection.createChannel(); channel.queueDeclare(QUEUE_NAME, false, false, false, null); Date nowTime = new Date(); String message = "Hello World! I'm RabbitMQ!!" + " SendTime: " + nowTime; channel.basicPublish("", QUEUE_NAME, null, message.getBytes()); System.out.println(" [x] Sent '" + message + "'"); channel.close(); connection.close(); System.out.println("Over Send."); } catch (Exception e) { // TODO: handle exception e.printStackTrace(); } } }