Spring Boot-与单独的服务共享嵌入式JMS代理 [英] Spring boot - sharing embedded JMS broker with separate service

查看:218
本文介绍了Spring Boot-与单独的服务共享嵌入式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应用程序中.但是,如果您分别使用自己的嵌入式JMS代理对Spring Boot服务进行配置,则这两个JMS代理是完全独立的,彼此之间并不了解,也无法以任何方式连接.

如果您想在生产中使用JMS,最好避免使用Spring Boot嵌入式JMS代理并将其单独托管.因此,对于PROD,首选3节点设置.

修改:

我怀疑使用以前的共享嵌入式JMS代理的方式时我错了.查看Spring Docs,ActiveMQ似乎无法实现:

spring.activemq.broker-url =#ActiveMQ代理的URL. 默认情况下自动生成.例如tcp://localhost:61616

spring.activemq.in-memory = true#指定默认代理URL是否 应该在内存中.忽略是否​​已指定显式代理.

当我在这些Spring Enterprise示例中工作时,我正在HornetQ上进行尝试,但是我现在找不到这样的配置.所以我敢打赌,我遇到了问题,最终使用了单独的第三个节点.

如果您想进一步尝试,我会

  1. 切换到HornetQ或Artemis
  2. 从服务B删除代理依赖项,仅保留spring-jms依赖项(以便仅提供JMS客户端依赖项)
  3. 尝试使用以下HornetQ属性进行戳戳,以尝试通过端口公开嵌入式实例:

    spring.hornetq.mode=embedded
    spring.hornetq.port=5445
    

但是正如我所提到的,我之前没有使它工作,并且不确定是否有可能.在Spring Boot文档中没有发现显式消息,它在这种组合下不起作用. /p>

我怀疑嵌入式Spring Boot JMS代理背后的想法是仅允许本地内存集成测试,而不是将嵌入式JMS代理暴露给外界.

I have two services which should communicate with each other via ActiveMQ. Everything works nicely when I have the receiver and sender in one service but when I split them, I got a strange activemq exception.

This is configuration of service 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);
  }

}

Sending messages:

  @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;
      }
    });
  }

The JMS is configured as in-memory queue:

spring.activemq.in-memory=true
spring.activemq.pooled=false

Service B is similar but id does not define the JmsContainerFactory. It only has the receiver:

@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() + ">");
  }
}

Service A have JMS and ActiveMQ broker as maven dependencies:

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jms</artifactId>
        </dependency>

        <dependency>
            <groupId>org.apache.activemq</groupId>
            <artifactId>activemq-broker</artifactId>
        </dependency>

Service B has only the JMS dependency.

Could you give me a hint how to share the bean between both services and send messages between them? I am completely new to this topic.

I am getting the following exception when receiving a message

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

EDIT: If I remove the broker dependency from one of the services, the Tomcat does not even start:

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 server were historically standalone applications deployed separately and consumer as well as listener of the message were communicating with this server. So there are typically three separate applications in JMS equation.

Now Spring Boot allows embed Active MQ JMS broker (JMS server) into your Spring Boot application. But if you configure your Spring Boot service each one with it's own embedded JMS broker, these two JMS brokers are totally separate, don't know about each other and are not connected in any way.

If you want to use JMS in production, it would be much wiser to avoid using Spring Boot embedded JMS brokers and host it separately. So 3 node setup would be preferred for PROD.

Edit:

I suspect I was wrong when with my previous way to share embedded JMS broker. Looking at Spring Docs it doesn't seem to be possible with 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.

I was trying it with HornetQ when I was working on these Spring Enterprise examples, but I can't find such config now. So I bet I had problems and ended up using separate third node.

If you want to further try it, I would

  1. Switch to HornetQ or Artemis
  2. Remove broker dependency from service B and leave there only spring-jms dependency (so that there would be only JMS client dependencies available)
  3. Try to poke with these HornetQ properties to try to have embedded instance that is exposed via port:

    spring.hornetq.mode=embedded
    spring.hornetq.port=5445
    

But As I mentioned I didn't make this working before and not sure if it is possible. Didn't find in Spring Boot docs explicit message it doesn't work in this combination.

I suspect the idea behind embedded Spring Boot JMS broker is to allow local in memory integration testing only, not to expose embedded JMS brokers to the outside world.

这篇关于Spring Boot-与单独的服务共享嵌入式JMS代理的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆