44、JMX与JMS技术:系统集成的关键利器

JMX与JMS:系统集成关键技术

JMX与JMS技术:系统集成的关键利器

1. JMX基础操作

在使用JMX技术时,有几个关键步骤需要完成。首先,从属性列表中选择 NotifyDiffer NotifyMatch 。接着,调用 start() 操作来启动监控。完成这些操作后,你就可以切换到 wrox.ch12.jmx:type-Worker,number=2 MBean并更改 Status 属性,此时你应该能看到 WorkMonitorMBean 生成了一条通知。

2. Java消息服务(JMS)概述

JMS是用于与面向消息的中间件(MOM)交互的标准Java API。在JMS 1.0之前,每个消息传递供应商都会开发自己的API,这使得系统集成商只能依赖专有解决方案。而JMS的出现,使得软件组件可以进行位置无关的处理,非常适合系统集成和分布式处理系统。在消息系统中,软件组件通过消息目的地或端点之间的信息发送和接收来进行通信。

2.1 端点:队列和主题

队列和主题的区别仅在于消息从端点被消费(即移除)的方式。
- 队列模型 :每条消息仅被接收一次。可以类比在银行排队,当柜员有空时,依次处理客户的请求。添加更多消费者到队列中,每个消费者的工作量会减少。
- 主题模型 :每个注册到主题的组件都会收到消息的副本。就像送报员的送报路线,房子越多,送报的工作量就越大。

如果需要让多个系统了解相同的信息,应使用主题;如果要分解并处理一组请求,则使用队列。幸运的是,JMS 1.1发布后,JMS API中的大多数类适用于这两种类型的目的地,这简化了应用程序设计,还允许在队列和主题之间使用相同的消息事务发送消息。

以下是JMS API中一些重要类的描述:
| 类 | 描述 |
| — | — |
| Destination | 队列或主题的通用术语,即消息端点。通常在设计时作为管理过程的一部分创建,但也可以在运行时为特定目的创建和销毁。 |
| ConnectionFactory | 用于创建与JMS服务器连接的管理对象。其实现绑定到JNDI注册表,供客户端在运行时查找。 |
| Connection | 允许与JMS服务器创建消息会话。 |
| Session | 消息交换的核心,定义了消息事务支持和确认机制。也是创建消费者、生产者和消息类型的工厂接口。 |
| MessageConsumer | 允许客户端同步或异步地从JMS服务器接收消息。 |
| MessageProducer | 用于向JMS目的地发送消息。 |
| Message | 向目的地发送信息和从目的地接收信息的基础接口。有几个子类描述了消息可以包含的有效负载类型,如序列化对象、字节流或文本。 |

2.2 发送和接收消息示例

通过JMS发送和接收消息的示例包含三个组件:
| 组件 | 目的 |
| — | — |
| MessageClient | 展示如何连接到JMS服务器并通过JMS API发送消息。 |
| JMSWorker | 作为MBean部署的异步消息客户端。 |
| JMSAgent | 定义MBeanServer以公开JMSWorker MBean进行管理。 |

3. 配置JMS服务器

本示例使用JMS 1.1兼容的服务器JBoss 4.03。配置步骤如下:
1. 从 JBoss官网 下载并运行安装程序,然后启动JBoss,命令为: C:\jboss-4.0.3\bin\run.bat
2. 创建示例所需的目的地。打开 %jboss_home%\server\default\deploy\jms 目录下的 jbossmq-destinations-service.xml 文件,在文件末尾的 </server> 标签之前添加以下代码:

<mbean code="org.jboss.mq.server.jmx.Queue"
       name="jboss.mq.destination:service=Queue,name=work-start">
</mbean>
<mbean code="org.jboss.mq.server.jmx.Queue"
       name="jboss.mq.destination:service=Queue,name=work-complete">
</mbean>
  1. 配置客户端应用程序以与JMS服务器通信。将 jndi.properties 文件放在类路径中,文件内容如下:
java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory
java.naming.factory.url.pkgs=org.jboss.naming:org.jnp.interfaces
java.naming.provider.url=localhost:1099

确保 org.jnp.interfaces.NamingContextFactory org.jboss.naming:org.jnp.interfaces 资源在应用程序的类路径中,可从 jbossall-client.jar 文件中获取。

4. 代码实现
4.1 MessageClient类
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.MessageProducer;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.naming.InitialContext;
import javax.naming.NamingException;

public class MessageClient {
    Destination destination = null;
    ConnectionFactory factory = null;

    public MessageClient(String destinationName) {
        InitialContext ic = null;
        try {
            ic = new InitialContext();
            destination = (Destination) ic.lookup(destinationName);
            factory = (ConnectionFactory) ic.lookup("ConnectionFactory");
        } catch (NamingException e) {
            e.printStackTrace();
        }
    }

    public void send(String text) throws JMSException {
        Connection con = factory.createConnection();
        Session session = con.createSession(false, Session.AUTO_ACKNOWLEDGE);
        MessageProducer producer = session.createProducer(destination);
        TextMessage message = session.createTextMessage(text);
        System.out.println("message ready to send: " + message);
        producer.send(message);
        producer.close();
        session.close();
        con.close();
    }

    public static void main(String[] args) {
        try {
            MessageClient ms = new MessageClient("queue/work-start");
            ms.send("<document>Welcome to JMS</document>");
        } catch (JMSException e) {
            e.printStackTrace();
        }
    }
}

运行客户端的命令为: C:\>java –cp jbossall-client.jar wrox.ch12.jms.MessageClient

4.2 JMSWorkerMBean接口
package wrox.ch12.jms;

public interface JMSWorkerMBean {
    public void start();
    public void stop(); 
    public boolean isRunning(); 
    public void setRunning(boolean running); 
    public void setDestination(String name); 
    public String getDestination(); 
}
4.3 JMSWorker类
package wrox.ch12.jms;

import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageConsumer;
import javax.jms.MessageListener;
import javax.jms.MessageProducer;
import javax.jms.Session;
import javax.naming.InitialContext;
import javax.naming.NamingException;

public class JMSWorker implements MessageListener, JMSWorkerMBean {
    private ConnectionFactory factory;
    private Connection connection;
    Session session;
    private Destination destination;
    private boolean running = true;
    private String destinationName = null;

    public JMSWorker(String destinationName) {
        this.destinationName = destinationName; 
        try {
            InitialContext ic = new InitialContext();
            factory = (ConnectionFactory) ic.lookup("ConnectionFactory");
            destination = (Destination) ic.lookup(destinationName);
        } catch (NamingException e) {
            e.printStackTrace();
        }
    }

    public void onMessage(Message message) {
        try {
            System.out.println("message received:" + message.toString());
        } catch (JMSException e) {
            e.printStackTrace();
        }
    }

    public boolean isRunning() {
        return running;
    }

    public void setRunning(boolean running) {
        this.running = running;
    }

    public String getDestination() {
        return destinationName;
    }

    public void setDestination(String name) {
        this.destinationName = name;
    }

    public void start() {
        try {
            connection = factory.createConnection();
            session = connection.createSession(false, Session.CLIENT_ACKNOWLEDGE);
            MessageConsumer consumer = session.createConsumer(destination);
            consumer.setMessageListener(this);
            connection.start();
        } catch (JMSException e) {
            running = false;
            e.printStackTrace();
        }
        running = true;
    }

    public void stop() {
        try {
            connection.stop();
        } catch (JMSException e) {
            e.printStackTrace();
        }
    }
}
4.4 JMSAgent类
package wrox.ch12.jms;

import java.lang.management.ManagementFactory;
import javax.management.MBeanServer;
import javax.management.ObjectName;

public class JMSAgent {
    public static void main(String[] args) {
        JMSWorker worker = new JMSWorker("queue/work-start");
        MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
        try {
            ObjectName name = new ObjectName("wrox.ch12.jmx:type=Worker");
            mbs.registerMBean(worker, name);
            mbs.invoke(name, "start", null, null);
            System.out.println("JMS Agent Started..");
            Thread.sleep(Long.MAX_VALUE);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

启动JMS代理的命令为: c:\>Java -Dcom.sun.management.jmxremote wrox.ch12.jms.JMSAgent

当JMSWorker接收到消息时,会将消息打印到标准输出。JMS消息由Header和Body两部分组成,Header包含与消息相关的属性。以下是一些Header属性的用途:
| 属性 | 用途 |
| — | — |
| jmsMessageID | 每条消息分配的唯一标识符,提供唯一的跟踪机制。 |
| jmsCorrelationID | 用于关联消息,可看作是父子关系。例如,一条消息被拆分为子消息时,可使用该ID识别原始消息。 |
| jmsTimeStamp | 消息创建的时间。 |
| jmsReplyTo | 消息的返回地址,可用于指定特定消息交换的临时目的地。 |
| jmstype | 可与消息过滤器结合使用,注册只接收指定类型消息的MessageConsumer。 |

在之前的示例中,将MessageListener JMSWorker部署为MBean,但这不是必需的。J2EE 1.2 EJB容器定义了消息驱动的企业bean,如果消息具有事务性质,推荐使用EJB解决方案。而JMX方法在监控方面具有一定的灵活性,适用于与无法进行事务处理的遗留系统交互的场景。

5. 系统集成模式

在软件集成中,有一些常见的模式可以提高系统的可扩展性和灵活性。

5.1 处理链模式

消息组件在分布式处理链中表现出色。使用这种方法,将每个过程分解为一系列步骤,每个步骤从输入目的地读取数据,进行处理,然后将结果发送到输出目的地。这是一种非常可扩展的解决方案。

可以创建一个接口来将目的地作为管理属性公开:

public interface ProcessorMBean {
    public void setInputDestination(String queue ); 
    public void setOutputDestination(String queue );
}

处理链在后端系统中很常见,也适用于服务实现。通过将问题分解为一系列离散的过程或事件,可以更有效地进行负载均衡。

mermaid格式流程图如下:

graph LR
    A[输入目的地] --> B[步骤1处理]
    B --> C[输出目的地/输入目的地2]
    C --> D[步骤2处理]
    D --> E[输出目的地2]
5.2 请求 - 回复模式

在基于Web的编程中常见的请求 - 回复场景中,JMS也非常有用。使用这种架构模式时,使用临时队列来收集结果。对MessageClient和JMSWorker类进行一些修改即可实现此场景。

修改MessageClient以创建临时队列,并将其设置为要发送消息的ReplyTo属性:

Session session = conn.createSession(false, Session.AUTO_ACKNOWLEDGE);
Destination temp = session.createTemporaryQueue();
TextMessage request = session.createTextMessage(); 
request.setText("<message>please respond</message>");
request.setJMSReplyTo(temp);

消息发送后,客户端在临时队列上等待,直到请求被处理或超时:

MessageProducer mp = session.createProducer(queue);
mp.send(request);
mp.close();
MessageConsumer mc = session.createConsumer(temp);
TextMessage reply = (TextMessage) mc.receive(WAIT_THRESHHOLD);

修改JMSWorker类以处理响应:

public void onMessage(Message message) {
    try {
        System.out.println("message received:" + message.toString());
        if (message.getJMSReplyTo() != null) {
            System.out.println("responding message");
            MessageProducer producer = session.createProducer(message.getJMSReplyTo());
            producer.send(message);
            producer.close();
        }
    } catch (JMSException e) {
        e.printStackTrace();
    }
}

mermaid格式流程图如下:

graph LR
    A[MessageClient] -->|发送请求消息| B[JMSWorker]
    B -->|处理请求| C[生成响应消息]
    C -->|发送响应消息| D[临时队列]
    D -->|接收响应消息| A

通过理解JMX和JMS的基础知识,并运用这些系统集成模式,可以构建支持多种集成场景的组件。

JMX与JMS技术:系统集成的关键利器

6. 处理链模式的优势与应用场景深入分析

处理链模式在系统集成中有着独特的优势和广泛的应用场景。在分布式系统中,各个子系统之间的协作往往需要高效的任务分配和处理机制,处理链模式正好满足了这一需求。

从优势方面来看,它的可扩展性是其核心亮点。由于每个步骤都是独立的,并且通过输入和输出目的地进行衔接,因此可以很方便地添加或移除步骤,以适应不同的业务需求。例如,在一个电商系统的订单处理流程中,最初可能只有订单创建、库存检查和支付处理三个步骤。随着业务的发展,需要增加优惠券验证和物流分配的步骤,此时只需要在处理链中添加相应的步骤即可,而不会影响到其他已有的步骤。

在应用场景方面,处理链模式在数据处理和消息传递系统中尤为常见。在数据处理系统中,数据可能需要经过清洗、转换、分析等多个步骤才能得到最终的结果。每个步骤都可以看作是处理链中的一个环节,通过将数据从一个环节传递到下一个环节,实现数据的逐步处理。在消息传递系统中,消息可能需要经过路由、过滤、转换等操作才能到达最终的目的地。处理链模式可以很好地组织这些操作,确保消息的准确传递和处理。

7. 请求 - 回复模式的优化与注意事项

请求 - 回复模式在实际应用中需要进行一些优化,以提高系统的性能和可靠性。

在性能优化方面,合理设置临时队列的大小和生命周期是关键。临时队列的大小直接影响到系统的内存使用和消息处理能力。如果队列设置得太小,可能会导致消息丢失;如果设置得太大,会占用过多的系统资源。因此,需要根据实际的业务需求和系统负载来合理设置队列的大小。同时,临时队列的生命周期也需要进行管理。在请求处理完成后,及时销毁临时队列,以释放系统资源。

在可靠性方面,需要考虑消息丢失和超时的情况。消息在传输过程中可能会因为网络故障或系统故障而丢失,因此需要实现消息的重试机制。当消息发送失败时,系统可以自动重试发送,直到达到最大重试次数。另外,设置合理的超时时间也是很重要的。如果请求在规定的时间内没有得到响应,系统应该能够及时处理,避免长时间的等待。

8. JMX与JMS结合的最佳实践

JMX和JMS是两种强大的技术,将它们结合使用可以实现更高效的系统管理和集成。

在系统管理方面,JMX可以用于监控和管理JMS组件。通过JMX,可以实时获取JMS服务器的状态信息,如队列的长度、消息的处理速度等。同时,还可以通过JMX对JMS组件进行配置和管理,如修改队列的属性、启动或停止消息消费者等。

在系统集成方面,JMX和JMS可以协同工作,实现不同系统之间的消息传递和交互。例如,一个系统可以通过JMS发送消息到另一个系统,同时使用JMX监控消息的发送和接收情况。如果出现异常情况,JMX可以及时发出警报,并通过JMX接口对系统进行调整。

以下是一个简单的示例,展示如何使用JMX监控JMS队列的长度:

import javax.management.MBeanServerConnection;
import javax.management.ObjectName;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;

public class JMSQueueMonitor {
    public static void main(String[] args) throws Exception {
        JMXServiceURL url = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://localhost:9999/jmxrmi");
        JMXConnector connector = JMXConnectorFactory.connect(url);
        MBeanServerConnection mbeanServerConnection = connector.getMBeanServerConnection();

        ObjectName queueObjectName = new ObjectName("jboss.mq.destination:service=Queue,name=work-start");
        Integer queueSize = (Integer) mbeanServerConnection.getAttribute(queueObjectName, "QueueSize");
        System.out.println("Queue size: " + queueSize);

        connector.close();
    }
}
9. 总结

JMX和JMS技术在系统集成中扮演着重要的角色。JMX提供了强大的管理和监控功能,而JMS则实现了高效的消息传递和异步处理。通过合理运用这两种技术,可以构建出高可扩展性、高可靠性的系统。

处理链模式和请求 - 回复模式是系统集成中常用的模式,它们可以帮助我们更好地组织和管理系统中的各个组件。在实际应用中,需要根据具体的业务需求和系统特点,选择合适的模式和技术,并进行相应的优化和调整。

同时,我们还需要注意系统的安全性和性能问题。在使用JMS时,要确保消息的加密和认证,防止消息被篡改和泄露。在使用JMX时,要合理设置访问权限,避免未经授权的访问。

通过不断地学习和实践,我们可以更好地掌握JMX和JMS技术,为系统集成提供更有效的解决方案。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值