控制檯打印:
javax.jms.JMSException: Failed to build body from content. Serializable class not available to broker.
Reason: java.lang.ClassNotFoundException: Forbidden class com.javaliao.portal.model.TbLogVisit!
This class is not trusted to be serialized as ObjectMessage payload. Please take a look at
http://activemq.apache.org/objectmessage.html for more information on how to configure trusted classes.
at org.apache.activemq.util.JMSExceptionSupport.create(JMSExceptionSupport.java:36)
at org.apache.activemq.command.ActiveMQObjectMessage.getObject(ActiveMQObjectMessage.java:213)
at com.javaliao.portal.listener.LogVisitListener.consumePaymentResult(LogVisitListener.java:50)
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:497)
at org.springframework.messaging.handler.invocation.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:171)
出現這個問題是因爲:
儘管通常不鼓勵使用ObjectMessage,因爲它在生產者和消費者之間引入了類路徑的耦合,但ActiveMQ支持它們作爲JMS規範的一部分。
安防
objectMessage對象依賴於marshal/unmarshal對象負載的Java序列化。此進程通常被認爲是不安全的,因爲惡意負載可以利用主機系統進行攻擊。這就是爲什麼從版本5.12.2和5.13.0開始,ActiveMQ強制用戶顯式地列出可以使用ObjectMessages交換的包的白名單。
如果需要交換對象消息,則需要添加應用程序正在使用的包。通過使用org.apache.activemq.serializable_packages系統屬性(由代理和activemq客戶機庫解釋)可以做到這一點。您可以將此係統屬性添加到${activemq_home}/bin/env腳本中的activemq_opts變量。
例如
-dorg.apache.activemq.serializable_packages=java.lang,javax.security,java.util,org.apache.activemq,org.fusesource.hawtbuf,com.thoughtworks.xstream.mapper,com.mycompany.myapp
將com.mycompany.myapp包添加到受信任包列表中注意,這裏列出的其他包在默認情況下是啓用的,因爲它們是常規代理工作所必需的。如果您想簡化此機制,可以使用*通配符來信任所有包,如
-dorg.apache.activemq.serializable_包=*
客戶
在客戶機端,您需要使用與在objectMessage.getObject()調用上反序列化惡意代碼相同的機制,從而破壞應用程序的環境。您可以在代理上使用相同的配置機制,並使用系統屬性配置受信任的類但是,這在客戶端應用程序中通常不方便,因此在5.12.2和5.13.1中,我們引入了使用activemqconnectionfactory的附加配置機制。定義了兩種附加方法:
setTrustedPackages()方法允許您設置要取消序列化的受信任包的列表,如
activemqconnectionfactory=new activemqconnectionfactory(“tcp://localhost:61616”);
factory.setTrustedPackages(新的ArrayList(Arrays.asList(“org.apache.activemq.test,org.apache.camel.test.split(“,”)));
setTrustAllPackages()允許關閉安全檢查並信任所有類。它對測試很有用。
activemqconnectionfactory=new activemqconnectionfactory(“tcp://localhost:61616”);
factory.setTrustAllPackages(真);
您可以在camel上下文中設置相同的屬性,如:```
org.apache.activemq.test org.apache.camel.test測試
或
```如果設置了系統屬性,則此配置將覆蓋這些屬性。
我的代碼:
發消息代碼:
@Autowired
ActiveMQUtil activeMQUtil;
@Override
public void insertLogVisit(TbLogVisit tbLogVisit) {
//使用信息隊列發信息異步執行保存到數據庫中
try {
// 連接消息服務器
Connection connection = activeMQUtil.getConnection();
connection.start();
//第一個值表示是否使用事務,如果選擇true,第二個值相當於選擇0
Session session = connection.createSession(true, Session.SESSION_TRANSACTED);
// 發送消息
Queue testqueue = session.createQueue("LOG_VISIT_QUEUE");
MessageProducer producer = session.createProducer(testqueue);
ObjectMessage objectMessage = session.createObjectMessage();
/*AMQ_SCHEDULED_DELAY long 延遲投遞的時間
AMQ_SCHEDULED_PERIOD long 重複投遞的時間間隔
AMQ_SCHEDULED_REPEAT int 重複投遞次數
AMQ_SCHEDULED_CRON String Cron表達式*/
//設置時間爲30s
objectMessage.setLongProperty(ScheduledMessage.AMQ_SCHEDULED_DELAY,1000*30);
objectMessage.setObject(tbLogVisit);
// 設置持久化傳輸
producer.setDeliveryMode(DeliveryMode.PERSISTENT);
producer.send(objectMessage);
session.commit();// 事務型消息,必須提交後才生效
connection.close();
} catch (JMSException e) {
e.printStackTrace();
}
}
連接工具類:
package com.javaliao.portal.util;
import org.apache.activemq.ActiveMQConnectionFactory;
import org.apache.activemq.pool.PooledConnectionFactory;
import javax.jms.Connection;
import javax.jms.JMSException;
import java.util.ArrayList;
import java.util.Arrays;
public class ActiveMQUtil {
PooledConnectionFactory pooledConnectionFactory=null;
public void init(String brokerUrl){
ActiveMQConnectionFactory activeMQConnectionFactory=new ActiveMQConnectionFactory(brokerUrl);
//設置白名單包
activeMQConnectionFactory.setTrustedPackages(
new ArrayList(Arrays.asList("org.apache.activemq.test,org.apache.camel.test".split(","))));
activeMQConnectionFactory.setTrustAllPackages(true);
pooledConnectionFactory = new PooledConnectionFactory(activeMQConnectionFactory);
pooledConnectionFactory.setExpiryTimeout(2000);
pooledConnectionFactory.setMaximumActiveSessionPerConnection(10);
pooledConnectionFactory.setMaxConnections(30);
pooledConnectionFactory.setReconnectOnException(true);
}
public Connection getConnection(){
Connection connection = null;
try {
connection = pooledConnectionFactory.createConnection();
} catch (JMSException e) {
e.printStackTrace();
}
return connection;
}
}
配置類:
package com.javaliao.portal.config;
import com.javaliao.portal.util.ActiveMQUtil;
import org.apache.activemq.ActiveMQConnectionFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jms.config.DefaultJmsListenerContainerFactory;
import javax.jms.JMSException;
import javax.jms.Session;
@Configuration
public class ActiveMQConfig {
@Value("${spring.activemq.broker-url:disabled}")
String brokerURL ;
@Value("${activemq.listener.enable:disabled}")
String listenerEnable;
@Bean
public ActiveMQUtil getActiveMQUtil() throws JMSException {
if(brokerURL.equals("disabled")){
return null;
}
ActiveMQUtil activeMQUtil=new ActiveMQUtil();
activeMQUtil.init(brokerURL);
return activeMQUtil;
}
@Bean
public ActiveMQConnectionFactory activeMQConnectionFactory ( ){
/* if((url==null||url.equals(""))&&!brokerURL.equals("disabled")){
url=brokerURL;
}*/
ActiveMQConnectionFactory activeMQConnectionFactory =
new ActiveMQConnectionFactory( brokerURL);
return activeMQConnectionFactory;
}
//定義一個消息監聽器連接工廠,這裏定義的是點對點模式的監聽器連接工廠
@Bean(name = "jmsQueueListener")
public DefaultJmsListenerContainerFactory jmsQueueListenerContainerFactory(ActiveMQConnectionFactory activeMQConnectionFactory ) {
DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
if(!listenerEnable.equals("true")){
return null;
}
factory.setConnectionFactory(activeMQConnectionFactory);
//設置併發數
factory.setConcurrency("5");
//重連間隔時間
factory.setRecoveryInterval(5000L);
factory.setSessionTransacted(false);
factory.setSessionAcknowledgeMode(Session.CLIENT_ACKNOWLEDGE);
return factory;
}
}
實體類:
import java.io.Serializable;
import java.util.Date;
public class TbLogVisit implements Serializable {
private static final long serialVersionUID = 2659644940173539668L;
private Long id;
private String visitIpAddress;
private String visitHostName;
private String visitChannel;
private String visitDescription;
private String visitApi;
private String visitParams;
private String visitUrl;
private String visitTimeConsuming;
private String visitResult;
private Long visitNum;
private String visitThrowingErro;
private Date visitStartTime;
private Date visitEndTime;
private Date createTime;
private Date updateTime;
接收消息:
package com.javaliao.portal.listener;
import com.javaliao.portal.log4j.BaseLogger;
import com.javaliao.portal.model.TbLogVisit;
import com.javaliao.portal.service.ActionService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.annotation.JmsListener;
import org.springframework.stereotype.Component;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.ObjectMessage;
@Component
public class LogVisitListener {
@Autowired
ActionService actionService;
/**
* 爲了體現差距,專門做了個很大的class,使用json轉換後大概35MB左右
* 使用json傳輸的情況,單位均爲ms:
* 總時間:17366
* 傳輸時間:220
* 發送者把object轉爲json的時間:6271
* 發送總共時間:10000
* 接收者把message轉換爲textMessage時間:0
* 接收者把json轉換爲object時間:7146。
* ****************************************
* 使用ObjectMessage進行傳輸的情況:
* 總時間:6742
* 傳輸時間:173
* 發送總時間:4836
* 接收者把message轉換爲ObjectMessage時間:1733
* ******************************************
* 結論
* 雖然沒做壓力測試,雖然只測了一次,雖然測試環境僅僅是我的筆記本,但我想我已經可以做出結論了。
* 在server之間的異步通信時,object優於json。
* 優勢主要集中於java序列化和對象之間轉換的效率遠高於json盒對象轉換的效率,
* 另外序列化後對象的大小比json的小也是有利傳輸的原因。
*/
@JmsListener(containerFactory = "jmsQueueListener" ,destination = "LOG_VISIT_QUEUE")
public void consumePaymentResult(Message mapMessage){
try {
ObjectMessage tbLogVisitObject = (ObjectMessage) mapMessage;
TbLogVisit object = (TbLogVisit) tbLogVisitObject.getObject();
int count = actionService.insertLog(object);
if(count < 1){
BaseLogger.info("日誌更新失敗");
}
} catch (JMSException e) {
e.printStackTrace();
}
}
}
application.preperties:
#activemq信息隊列
spring.activemq.broker-url=tcp://192.168.134.100:61616
activemq.listener.enable=true
配置那邊:
我的是配置在linux系統下
然並卵,沒什麼用,然後強迫我使用String類型,改後代碼:
@Override
public void insertLogVisit(TbLogVisit tbLogVisit) {
try {
// 連接消息服務器
Connection connection = activeMQUtil.getConnection();
connection.start();
//第一個值表示是否使用事務,如果選擇true,第二個值相當於選擇0
Session session = connection.createSession(true, Session.SESSION_TRANSACTED);
// 發送消息
Queue testqueue = session.createQueue("LOG_VISIT_QUEUE");
MessageProducer producer = session.createProducer(testqueue);
MapMessage mapMessage=new ActiveMQMapMessage();
String toString = JSONObject.fromObject(tbLogVisit).toString();
mapMessage.setString("tbLogVisit",toString);
producer.setDeliveryMode(DeliveryMode.PERSISTENT);
producer.send(mapMessage);
session.commit();// 事務型消息,必須提交後才生效
connection.close();
} catch (JMSException e) {
e.printStackTrace();
}
}
接收消息:
@JmsListener(containerFactory = "jmsQueueListener" ,destination = "LOG_VISIT_QUEUE")
public void consumeLogResult(MapMessage mapMessage){
try {
String object = mapMessage.getString("tbLogVisit");
JSONObject jsonObject = new JSONObject().fromObject(object);
TbLogVisit logVisit = (TbLogVisit) JSONObject.toBean(jsonObject, TbLogVisit.class);
int count = actionService.insertLog(logVisit);
if(count < 1){
BaseLogger.info("日誌更新失敗");
}
} catch (JMSException e) {
e.printStackTrace();
}
}
然後控制檯: