* MQ伺服器的配置參加上篇*
4-綁定模式連接配接
Java應用程式必須和MQ伺服器安裝在同一個機器上,通過程序間通信機制,不通過TCP/IP進行通信,減少網絡開銷。
MQ提供32和64位版本的MQ JNI庫,預設在/opt/mqm/java/lib/和/opt/mqm/java/lib64/目錄下:
mqjbnd.so: 該庫為應用提供綁定模式連接配接MQ;
修改代碼:其實變動更小,隻要将主機位址和端口去掉即可,因為綁定模式不需要網絡通信,此外也不需要TCP通道參數。
import java.io.IOException;
import com.ibm.mq.MQEnvironment;
import com.ibm.mq.MQException;
import com.ibm.mq.MQGetMessageOptions;
import com.ibm.mq.MQMessage;
import com.ibm.mq.MQPutMessageOptions;
import com.ibm.mq.MQQueue;
import com.ibm.mq.MQQueueManager;
import com.ibm.mq.constants.CMQC;
import com.ibm.mq.constants.MQConstants;
public class MQTest2 {
public static void main(String[] args) throws MQException, IOException
{
//發送消息給隊列
put();
//從隊列讀取消息
get();
}
static void put() throws MQException, IOException
{
//配置MQ伺服器連接配接參數
//使用者名和密碼
MQEnvironment.userID = "mquser1";
MQEnvironment.password = "mqtest2016";
//設定應用名稱,友善伺服器MQ 檢視應用連接配接
MQEnvironment.properties.put(MQConstants.APPNAME_PROPERTY, "MQ Test By Java");
//設定綁定模式通信
MQEnvironment.properties.put(CMQC.TRANSPORT_PROPERTY,CMQC.TRANSPORT_MQSERIES_BINDINGS);
//建立執行個體,連接配接隊列管理器
MQQueueManager queueManager = new MQQueueManager("JAVA.QUEUE.MANAGER.1");
//以可寫的方式通路隊列管理器已定義的隊列QUEUE1,當然也可以建立隊列
MQQueue putQueue = queueManager.accessQueue("QUEUE1", CMQC.MQOO_OUTPUT);
//建立并發送消息給隊列
MQMessage myMessage = new MQMessage();
String name = "MePlusPlus's 部落格2";
myMessage.writeUTF(name);
//使用預設的消息選項
MQPutMessageOptions pmo = new MQPutMessageOptions();
//發送消息
putQueue.put(myMessage, pmo);
putQueue.close();
//斷開連接配接
queueManager.disconnect();
}
static void get() throws MQException, IOException
{
//配置MQ伺服器連接配接參數
MQEnvironment.userID = "mquser1";
MQEnvironment.password = "mqtest2016";
//設定應用名稱,友善伺服器MQ 檢視應用連接配接
MQEnvironment.properties.put(MQConstants.APPNAME_PROPERTY, "MQ Test By Java");
MQEnvironment.properties.put(CMQC.TRANSPORT_PROPERTY, CMQC.TRANSPORT_MQSERIES_BINDINGS);
//建立執行個體,連接配接隊列管理器
MQQueueManager queueManager = new MQQueueManager("JAVA.QUEUE.MANAGER.1");
//以可讀的方式通路隊列管理器已定義的隊列QUEUE1
MQQueue getQueue = queueManager.accessQueue("QUEUE1", CMQC.MQOO_INPUT_AS_Q_DEF);
//從隊列讀取消息
MQMessage theMessage = new MQMessage();
MQGetMessageOptions gmo = new MQGetMessageOptions();
getQueue.get(theMessage, gmo);
String name = theMessage.readUTF();
System.out.println(name);
getQueue.close();
//斷開連接配接
queueManager.disconnect();
}
}
編譯運作:
# cd /home/mq
# /home/jdk1.8.0_102/bin/javac -cp /opt/mqm/java/lib/com.ibm.mq.allclient.jar MQTest2.java
# /home/jdk1.8.0_102/bin/java -cp /opt/mqm/java/lib/com.ibm.mq.allclient.jar:/home/mq MQTest2
出現以下錯誤:
Exception in thread "main" com.ibm.mq.MQException: MQJE001: 完成代碼為 '2',原因為 '2495'。
at com.ibm.mq.MQSESSION.(MQSESSION.java:2065)
at com.ibm.mq.MQSESSION.getSession(MQSESSION.java:2105)
at com.ibm.mq.MQManagedConnectionJ11.(MQManagedConnectionJ11.java:210)
at com.ibm.mq.MQBindingsManagedConnectionFactoryJ11._createManagedConnection(MQBindingsManagedConnectionFactoryJ11.java:187)
at com.ibm.mq.MQBindingsManagedConnectionFactoryJ11.createManagedConnection(MQBindingsManagedConnectionFactoryJ11.java:233)
at com.ibm.mq.StoredManagedConnection.(StoredManagedConnection.java:96)
at com.ibm.mq.MQSimpleConnectionManager.allocateConnection(MQSimpleConnectionManager.java:194)
at com.ibm.mq.MQQueueManagerFactory.obtainBaseMQQueueManager(MQQueueManagerFactory.java:767)
at com.ibm.mq.MQQueueManagerFactory.procure(MQQueueManagerFactory.java:715)
at com.ibm.mq.MQQueueManagerFactory.constructQueueManager(MQQueueManagerFactory.java:678)
at com.ibm.mq.MQQueueManagerFactory.createQueueManager(MQQueueManagerFactory.java:148)
at com.ibm.mq.MQQueueManager.(MQQueueManager.java:675)
at MQTest2.put(MQTest2.java:38)
at MQTest2.main(MQTest2.java:18)
Caused by: com.ibm.mq.jmqi.local.LocalMQ$3: CC=2;RC=2495;AMQ8598: 未能裝入 WebSphere MQ 本機 JNI 庫“'mqjbnd'”。
at com.ibm.mq.jmqi.local.LocalMQ.loadLib(LocalMQ.java:1236)
at com.ibm.mq.jmqi.local.LocalMQ$1.run(LocalMQ.java:280)
at java.security.AccessController.doPrivileged(Native Method)
at com.ibm.mq.jmqi.local.LocalMQ.initialise_inner(LocalMQ.java:268)
at com.ibm.mq.jmqi.local.LocalMQ.initialise(LocalMQ.java:231)
at com.ibm.mq.jmqi.local.LocalMQ.(LocalMQ.java:1318)
at com.ibm.mq.jmqi.local.LocalServer.(LocalServer.java:229)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
at com.ibm.mq.jmqi.JmqiEnvironment.getInstance(JmqiEnvironment.java:672)
at com.ibm.mq.jmqi.JmqiEnvironment.getMQI(JmqiEnvironment.java:606)
at com.ibm.mq.MQSESSION.(MQSESSION.java:2058)
... 13 more
Caused by: java.lang.UnsatisfiedLinkError: no mqjbnd in java.library.path
at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1867)
at java.lang.Runtime.loadLibrary0(Runtime.java:870)
at java.lang.System.loadLibrary(System.java:1122)
at com.ibm.mq.jmqi.local.LocalMQ.loadLib(LocalMQ.java:1208)
... 26 more
很明顯,上述錯誤,是因為找不到MQ JNI庫,libmqjbnd.so,重新運作,指明該庫位置(注意本機是64位作業系統)
# /home/jdk1.8.0_102/bin/java -Djava.library.path=/opt/mqm/java/lib64 -cp /opt/mqm/java/lib/com.ibm.mq.allclient.jar:/home/mq MQTest2
再次出錯
Exception in thread "main" com.ibm.mq.MQException: MQJE001: 完成代碼為 '2',原因為 '2035'。
at com.ibm.mq.MQManagedConnectionJ11.(MQManagedConnectionJ11.java:251)
at com.ibm.mq.MQBindingsManagedConnectionFactoryJ11._createManagedConnection(MQBindingsManagedConnectionFactoryJ11.java:187)
at com.ibm.mq.MQBindingsManagedConnectionFactoryJ11.createManagedConnection(MQBindingsManagedConnectionFactoryJ11.java:233)
at com.ibm.mq.StoredManagedConnection.(StoredManagedConnection.java:96)
at com.ibm.mq.MQSimpleConnectionManager.allocateConnection(MQSimpleConnectionManager.java:194)
at com.ibm.mq.MQQueueManagerFactory.obtainBaseMQQueueManager(MQQueueManagerFactory.java:767)
at com.ibm.mq.MQQueueManagerFactory.procure(MQQueueManagerFactory.java:715)
at com.ibm.mq.MQQueueManagerFactory.constructQueueManager(MQQueueManagerFactory.java:678)
at com.ibm.mq.MQQueueManagerFactory.createQueueManager(MQQueueManagerFactory.java:148)
at com.ibm.mq.MQQueueManager.(MQQueueManager.java:675)
at MQTest2.put(MQTest2.java:38)
at MQTest2.main(MQTest2.java:18)
···
很明顯,表明MQ通路的權限錯誤,說明在綁定模式下,MQ未能充分授權,可能是隊列管理器、通道或者隊列對象。
經過搜尋和閱讀文檔,發現如下幾種方式,嘗試了以下方法,最終通過最後一種方法解決:
第一種嘗試(**該方法不起效果**):仔細檢查伺服器端隊列管理器和通道等配置發現,
> DEFINE CHANNEL(JAVA.CLIENT.CHANNEL1) CHLTYPE(SVRCONN) TRPTYPE(TCP)
> SET CHLAUTH(JAVA.CLIENT.CHANNEL1) TYPE(ADDRESSMAP) ADDRESS('127.0.0.1') MCAUSER('mquser1')
這裡的channel配置是針對IP位址進行過濾和限制通路的,建立的TCP通道,是以需要新增另一種類型的通道。
需要建立一個通道,非TCP通道,命名為JAVA.CLIENT.CHANNEL2
此外發現錯誤:少配置了 channel的參數,加入代碼:
MQEnvironment.channel = "JAVA.CLIENT.CHANNEL2";
# su - mqm
~ cd /opt/mqm/bin
~ source setmqenv -s
啟動腳本執行器,進行設定
~ runmqsc JAVA.QUEUE.MANAGER.1
DEFINE CHANNEL(JAVA.CLIENT.CHANNEL2) CHLTYPE(SVRCONN)
SET CHLAUTH('JAVA.CLINET.CHANNEL2') TYPE(USERMAP) ACTION(ADD) CLNTUSER('mquser1') USERSRC(MAP) MCAUSER('mquser1')
**仍然出錯,其實通過綁定模式,不需要通道參數,因為它不通過網絡進行通信,是利用程序間通信機制進行消息傳遞,上述方法無法解決**
第二種方法:(**該方法有效**)
在尋找文檔和查找MQ Java API文檔以後,發現了以下說明,進而找到解決方法,主要是因為執行MQTest2時,
需要切換到特定使用者,本文定義隊列管理器和隊列時授權給mquser1。
此外,在類 com.ibm.mq.MQEnvironment的
[文檔](https://www.ibm.com/support/knowledgecenter/SSFKSJ_7.5.0/com.ibm.mq.javadoc.doc/WMQJavaClasses/com/ibm/mq/MQEnvironment.html)
中有該表述:
> All the methods and attributes of this class apply to the WebSphere MQ classes for Java client connections,
> but only enableTracing(), disableTracing(), properties, version_notice, userID, connOptions and connTag apply to bindings connections.
翻譯出來,很簡單,對于綁定模式,MQEnvironment類中隻有 enableTracing(), disableTracing(), properties,
version_notice, userID, connOptions and connTag 這幾個方法和屬性有效,是以不用設定通道,主機名等屬性,
此外,properties裡面很多key也就是重寫了類中方法和屬性而已。
是以需要修改MQTest2.java檔案,删除不必要的屬性,修改後的代碼如下:
```java
import java.io.IOException;
import com.ibm.mq.MQEnvironment;
import com.ibm.mq.MQException;
import com.ibm.mq.MQGetMessageOptions;
import com.ibm.mq.MQMessage;
import com.ibm.mq.MQPutMessageOptions;
import com.ibm.mq.MQQueue;
import com.ibm.mq.MQQueueManager;
import com.ibm.mq.constants.CMQC;
import com.ibm.mq.constants.MQConstants;
public class MQTest2 {
public static void main(String[] args) throws MQException, IOException
{
//發送消息給隊列
put();
//從隊列讀取消息
get();
}
static void put() throws MQException, IOException
{
//設定應用名稱,友善伺服器MQ 檢視應用連接配接
MQEnvironment.properties.put(MQConstants.APPNAME_PROPERTY, "MQ Test By Java");
//設定綁定模式通信
MQEnvironment.properties.put(CMQC.TRANSPORT_PROPERTY,CMQC.TRANSPORT_MQSERIES_BINDINGS);
//建立執行個體,連接配接隊列管理器
MQQueueManager queueManager = new MQQueueManager("JAVA.QUEUE.MANAGER.1");
//以可寫的方式通路隊列管理器已定義的隊列QUEUE1,當然也可以建立隊列
MQQueue putQueue = queueManager.accessQueue("QUEUE1", CMQC.MQOO_OUTPUT);
//建立并發送消息給隊列
MQMessage myMessage = new MQMessage();
String name = "MePlusPlus's 部落格2";
myMessage.writeUTF(name);
//使用預設的消息選項
MQPutMessageOptions pmo = new MQPutMessageOptions();
//發送消息
putQueue.put(myMessage, pmo);
putQueue.close();
//斷開連接配接
queueManager.disconnect();
}
static void get() throws MQException, IOException
{
//設定應用名稱,友善伺服器MQ 檢視應用連接配接
MQEnvironment.properties.put(MQConstants.APPNAME_PROPERTY, "MQ Test By Java");
MQEnvironment.properties.put(CMQC.TRANSPORT_PROPERTY, CMQC.TRANSPORT_MQSERIES_BINDINGS);
//建立執行個體,連接配接隊列管理器
MQQueueManager queueManager = new MQQueueManager("JAVA.QUEUE.MANAGER.1");
//以可讀的方式通路隊列管理器已定義的隊列QUEUE1
MQQueue getQueue = queueManager.accessQueue("QUEUE1", CMQC.MQOO_INPUT_AS_Q_DEF);
//從隊列讀取消息
MQMessage theMessage = new MQMessage();
MQGetMessageOptions gmo = new MQGetMessageOptions();
getQueue.get(theMessage, gmo);
String name = theMessage.readUTF();
System.out.println(name);
getQueue.close();
//斷開連接配接
queueManager.disconnect();
}
}
編譯運作:
# cd /home/mq
# /home/jdk1.8.0_102/bin/javac -cp /opt/mqm/java/lib/com.ibm.mq.allclient.jar:/home/mq MQTest2.java
切換到mquser1執行:(**這個是關鍵**)
# su - mquser1
~ /home/jdk1.8.0_102/bin/java -Djava.library.path=/opt/mqm/java/lib64 -cp /opt/mqm/java/lib/com.ibm.mq.allclient.jar:/home/mq MQTest2
5-MQEnvironment類中的userID和password屬性作用初探
MQEnvironment類中的userID和password屬性:用來認證MQ應用的使用者身份。
如果設定了securityExit屬性,應用特定的安全驗證流程,則userID和password屬性将不起作用被忽略;
預設情況下,如果userID為空時,将預設發送JRE 屬性(user.name)作為該屬性值;如果設定了,則發送設定值;
此外,目前SecurityExit該接口已經過期,對于綁定模式(binding mode),SecurityExit接口不支援。
是以,userID和password表明應用身份,要和MQ伺服器端的安全設定配合使用,本文的示例程式中并沒有用上該屬性。
上述MQ設定:
SET AUTHREC PROFILE(QUEUE1) OBJTYPE(QUEUE) PRINCIPAL('mquser1') AUTHADD(PUT,GET)
principal屬性表明 使用者mquser1對于該隊列管理器中的隊列具有put,get權限。
SET AUTHREC OBJTYPE(QMGR) PRINCIPAL('mquser1') AUTHADD(CONNECT)
principal屬性表明mquser1對于隊列管理器具有連接配接的權限
DEFINE CHANNEL(JAVA.CLIENT.CHANNEL1) CHLTYPE(SVRCONN) TRPTYPE(TCP)
SET CHLAUTH(JAVA.CLIENT.CHANNEL1) TYPE(ADDRESSMAP) ADDRESS('127.0.0.1') MCAUSER('mquser1')
這條很重要,表明通過IP位址進行連接配接過濾,表明127.0.0.1這台機器可以通過通道JAVA.CLIENT.CHANNEL1,并以mquser1的身份通路隊列管理器。
也就是說127.0.0.1這台機器無需傳遞任何身份資訊。
那麼該userID和password作用有哪些?
實驗:對于上述執行個體中的TCP方式通路的代碼,1.不設定該屬性,正常運作;2.設定為mquser1和正确的password,正常運作;
3.設定mquser1和不設定password屬性,正常運作
4.設定為mquser1和錯誤的密碼無法通路 5.設定不存在的使用者mquser2,也無法通路; 6.添加使用者mquser2,正常運作
說明MQ伺服器對于應用傳遞過來的userID和password屬性并沒有忽略,而是一種方式在驗證,從上看出是以驗證作業系統使用者的方式在驗證。
此外配合MQ的權限設定,可以
SET CHLAUTH(' generic-channel-name ') TYPE (USERMAP) CLNTUSER(client-user-name) USERSRC(MAP) MCAUSER(user)
通過使用者映射,将應用傳遞過來的userID(也就是client-user-name) 映射成 系統的user(MCAUSER屬性)