Spring 启动 - 与单独的服务共享嵌入式 JMS 代理
Spring boot - sharing embedded JMS broker with separate service
我有两个服务应该通过 ActiveMQ 相互通信。当我在一项服务中有接收方和发送方时,一切都很好,但是当我拆分它们时,我遇到了一个奇怪的 activemq 异常。
这是服务A的配置:
@EnableScheduling
@SpringBootApplication
@EnableJms
public class App extends SpringBootServletInitializer {
private static final Logger log = LoggerFactory.getLogger(App.class);
@Autowired
private static JmsTemplate jms;
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
正在发送消息:
@Autowired
private JmsTemplate jms;
public void sendTicket(Reservation reservation) {
log.debug("---------------sending message----------------");
// Send a message
jms.send("mailbox-destination", new MessageCreator() {
public ObjectMessage createMessage(Session session) throws JMSException {
ObjectMessage message = session.createObjectMessage();
message.setObject(reservation);
return message;
}
});
}
JMS 配置为内存队列:
spring.activemq.in-memory=true
spring.activemq.pooled=false
服务 B 类似,但 id 未定义 JmsContainerFactory。它只有接收器:
@Component
public class Receiver {
private static final Logger log = LoggerFactory.getLogger(Receiver.class);
/**
* Receive a message with reservation and print it out as a e-ticket.
*
* @param reservation
*/
@JmsListener(destination = "mailbox-destination")
public void receiveMessage(Reservation reservation) {
log.info("Received <" + reservation.getId() + ">");
}
}
服务 A 具有 JMS 和 ActiveMQ 代理作为 Maven 依赖项:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jms</artifactId>
</dependency>
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-broker</artifactId>
</dependency>
服务 B 只有 JMS 依赖。
你能告诉我如何在两个服务之间共享 bean 并在它们之间发送消息吗?我对这个话题完全陌生。
收到消息时出现以下异常
org.springframework.jms.UncategorizedJmsException: Uncategorized exception occured during JMS processing; nested exception is javax.jms.JMSException: Could not create Transport. Reason: javax.management.InstanceAlreadyExistsException: org.apache.activemq:type=Broker,brokerName=localhost
编辑:如果我从其中一项服务中删除代理依赖项,Tomcat 甚至不会启动:
java.util.concurrent.ExecutionException: org.apache.catalina.LifecycleException: Failed to start component [StandardEngine[Catalina].StandardHost[localhost]]
at java.util.concurrent.FutureTask.report(FutureTask.java:122)
at java.util.concurrent.FutureTask.get(FutureTask.java:192)
at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:917)
at org.apache.catalina.core.StandardEngine.startInternal(StandardEngine.java:262)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
at org.apache.catalina.core.StandardService.startInternal(StandardService.java:439)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
at org.apache.catalina.core.StandardServer.startInternal(StandardServer.java:769)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
at org.apache.catalina.startup.Catalina.start(Catalina.java:625)
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.apache.catalina.startup.Bootstrap.start(Bootstrap.java:351)
at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:485)
Caused by: org.apache.catalina.LifecycleException: Failed to start component [StandardEngine[Catalina].StandardHost[localhost]]
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:154)
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1409)
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1399)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
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: org.apache.catalina.LifecycleException: A child container failed during start
at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:925)
at org.apache.catalina.core.StandardHost.startInternal(StandardHost.java:871)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
... 6 common frames omitted
JMS 服务器在历史上是单独部署的独立应用程序,消费者和消息的侦听器都与该服务器通信。所以 JMS 方程中通常有三个独立的应用程序。
现在 Spring Boot 允许将 Active MQ JMS 代理(JMS 服务器)嵌入到您的 Spring Boot 应用程序中。但是,如果您为每个 Spring 引导服务配置自己的嵌入式 JMS 代理,那么这两个 JMS 代理是完全独立的,彼此不了解,也没有以任何方式连接。
如果您想在生产环境中使用 JMS,最好避免使用 Spring 引导嵌入式 JMS 代理并单独托管它。因此 3 节点设置将是 PROD 的首选。
编辑:
我怀疑我以前共享嵌入式 JMS 代理的方式有误。查看 Spring 文档,似乎无法使用 ActiveMQ:
spring.activemq.broker-url= # URL of the ActiveMQ broker.
Auto-generated by default. For instance tcp://localhost:61616
spring.activemq.in-memory=true # Specify if the default broker URL
should be in memory. Ignored if an explicit broker has been specified.
我在做these Spring Enterprise examples的时候用HornetQ试过,但是现在找不到这样的配置了。所以我敢打赌我遇到了问题并最终使用了单独的第三个节点。
如果你想进一步尝试,我会
- 切换到 HornetQ 或 Artemis
- 从服务 B 中删除 broker 依赖,只留下 spring-jms 依赖(这样就只有 JMS 客户端依赖可用)
尝试使用这些 HornetQ 属性来获取通过端口公开的嵌入式实例:
spring.hornetq.mode=embedded
spring.hornetq.port=5445
但是正如我所提到的,我之前没有让它工作,不确定是否可行。 在 Spring Boot docs 显式消息中找不到它不适用于此组合。
我怀疑嵌入式 Spring 引导 JMS 代理背后的想法是只允许本地内存集成测试,而不是向外界公开嵌入式 JMS 代理。
我有两个服务应该通过 ActiveMQ 相互通信。当我在一项服务中有接收方和发送方时,一切都很好,但是当我拆分它们时,我遇到了一个奇怪的 activemq 异常。
这是服务A的配置:
@EnableScheduling
@SpringBootApplication
@EnableJms
public class App extends SpringBootServletInitializer {
private static final Logger log = LoggerFactory.getLogger(App.class);
@Autowired
private static JmsTemplate jms;
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
正在发送消息:
@Autowired
private JmsTemplate jms;
public void sendTicket(Reservation reservation) {
log.debug("---------------sending message----------------");
// Send a message
jms.send("mailbox-destination", new MessageCreator() {
public ObjectMessage createMessage(Session session) throws JMSException {
ObjectMessage message = session.createObjectMessage();
message.setObject(reservation);
return message;
}
});
}
JMS 配置为内存队列:
spring.activemq.in-memory=true
spring.activemq.pooled=false
服务 B 类似,但 id 未定义 JmsContainerFactory。它只有接收器:
@Component
public class Receiver {
private static final Logger log = LoggerFactory.getLogger(Receiver.class);
/**
* Receive a message with reservation and print it out as a e-ticket.
*
* @param reservation
*/
@JmsListener(destination = "mailbox-destination")
public void receiveMessage(Reservation reservation) {
log.info("Received <" + reservation.getId() + ">");
}
}
服务 A 具有 JMS 和 ActiveMQ 代理作为 Maven 依赖项:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jms</artifactId>
</dependency>
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-broker</artifactId>
</dependency>
服务 B 只有 JMS 依赖。
你能告诉我如何在两个服务之间共享 bean 并在它们之间发送消息吗?我对这个话题完全陌生。
收到消息时出现以下异常
org.springframework.jms.UncategorizedJmsException: Uncategorized exception occured during JMS processing; nested exception is javax.jms.JMSException: Could not create Transport. Reason: javax.management.InstanceAlreadyExistsException: org.apache.activemq:type=Broker,brokerName=localhost
编辑:如果我从其中一项服务中删除代理依赖项,Tomcat 甚至不会启动:
java.util.concurrent.ExecutionException: org.apache.catalina.LifecycleException: Failed to start component [StandardEngine[Catalina].StandardHost[localhost]]
at java.util.concurrent.FutureTask.report(FutureTask.java:122)
at java.util.concurrent.FutureTask.get(FutureTask.java:192)
at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:917)
at org.apache.catalina.core.StandardEngine.startInternal(StandardEngine.java:262)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
at org.apache.catalina.core.StandardService.startInternal(StandardService.java:439)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
at org.apache.catalina.core.StandardServer.startInternal(StandardServer.java:769)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
at org.apache.catalina.startup.Catalina.start(Catalina.java:625)
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.apache.catalina.startup.Bootstrap.start(Bootstrap.java:351)
at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:485)
Caused by: org.apache.catalina.LifecycleException: Failed to start component [StandardEngine[Catalina].StandardHost[localhost]]
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:154)
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1409)
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1399)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
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: org.apache.catalina.LifecycleException: A child container failed during start
at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:925)
at org.apache.catalina.core.StandardHost.startInternal(StandardHost.java:871)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
... 6 common frames omitted
JMS 服务器在历史上是单独部署的独立应用程序,消费者和消息的侦听器都与该服务器通信。所以 JMS 方程中通常有三个独立的应用程序。
现在 Spring Boot 允许将 Active MQ JMS 代理(JMS 服务器)嵌入到您的 Spring Boot 应用程序中。但是,如果您为每个 Spring 引导服务配置自己的嵌入式 JMS 代理,那么这两个 JMS 代理是完全独立的,彼此不了解,也没有以任何方式连接。
如果您想在生产环境中使用 JMS,最好避免使用 Spring 引导嵌入式 JMS 代理并单独托管它。因此 3 节点设置将是 PROD 的首选。
编辑:
我怀疑我以前共享嵌入式 JMS 代理的方式有误。查看 Spring 文档,似乎无法使用 ActiveMQ:
spring.activemq.broker-url= # URL of the ActiveMQ broker. Auto-generated by default. For instance
tcp://localhost:61616
spring.activemq.in-memory=true # Specify if the default broker URL should be in memory. Ignored if an explicit broker has been specified.
我在做these Spring Enterprise examples的时候用HornetQ试过,但是现在找不到这样的配置了。所以我敢打赌我遇到了问题并最终使用了单独的第三个节点。
如果你想进一步尝试,我会
- 切换到 HornetQ 或 Artemis
- 从服务 B 中删除 broker 依赖,只留下 spring-jms 依赖(这样就只有 JMS 客户端依赖可用)
尝试使用这些 HornetQ 属性来获取通过端口公开的嵌入式实例:
spring.hornetq.mode=embedded spring.hornetq.port=5445
但是正如我所提到的,我之前没有让它工作,不确定是否可行。 在 Spring Boot docs 显式消息中找不到它不适用于此组合。
我怀疑嵌入式 Spring 引导 JMS 代理背后的想法是只允许本地内存集成测试,而不是向外界公开嵌入式 JMS 代理。