Spring Integration 项目是Sring对Enterprise Patterns(企业集成模式)内容的实现,这些实现扩展了原有的Spring消息编程模型。
EIP内部提出了很多概念,比如消息的过滤、聚合、点对点发送、发布/订阅模式、消息转换、TCP/UDP、JMS、E-mail等内容。spring-messaging模块仅仅是对Spring Integration项目消息部分关键内容进行抽象和简单实现,Spring Integration项目是真正复杂的实现。
核心组件概述
-
MessageDipatcher:消息分发器。消息分发给MessageHandler的策略接口。BroadcastingDipatcher(广播模式):每次消费分发给所有的MessageHandler。UnicastingDispatcher(单播模式):每次消费只能让一个MessageHandler处理,单播模式分发给哪个MessageHandler,涉及负载均衡策略LoadBalancingStrategy。
-
LoadBalancingStrategy:默认只有轮询实现RoundRobinLoadBalancingStrategy。 -
Transformer:消息转换器。可以吧Message A转换成Message B,在转换过程中,比如可以过滤消息内HEADER或Payload信息,仅仅保留重要部分。 -
MessageSelector:消息选择器。决定是否选择(accept)消息,与MessageFilter配合进行消息过滤。 -
MessageRouter:消息路由。获取MessageHandler中的消息,并根据不同的条件发送给不同的MessageHandler。 -
Aggregator:消息集合器。把一组消息根据一些条件聚合成一条消息。 -
Splitter:消息分割器。把一条消息根据一些条件分割成多个消息。 -
ChannelAdapter:通道适配器。应用跟MessageChannel之间的桥梁。分为OutboundChannelAdapter和InboundChannelAdapter。 -
MessagingGateway:消息网关。以HTTP网关的形式将消息的操作暴露出去,用户通过Rest请求就可与消息系统进行交互。
核心组件使用
@Filter注解可以用来过滤消息
@Filter(inputChannel = "input", discardChannel = "discard", outputChannel = "output")
public boolean receiveByFilter(String msg) {
if (msg.contains("keywords")) {
return false;
}
return true;
}
过滤掉的消息会被发送到名称为
discard的MessageHandler中,没被过滤的消息则被发送到名称为output的MessageHandler中。
@Transformer注解用于消息的转换
@Transformer(inputChannel = "input", outputChannel = "output")
public Message receiveByTransformer(Message msg) {
msg.getHeaders().remove("secret");
return msg;
}
会删除
HEADER中key为secret的内容
@Splitter注解用于消息分割
@Splitter(inputChannel = "input", outputChannel = "output")
public String[] receiveByFilter(String msg) {
return msg.split("-");
}
会把一个消息内的内容根据“-”分割成多个消息。
@MessageGateway定义一个消息网关接口
@MessagingGateway(name = "testGateway", defaultRequestChannel = "order")
public interface TestGateway {
@Gateway(requestChannel = "order", replyTimeout = 2, requestTimeout = 200)
String echo(OrderMsg orderMsg);
}
可以注入
@Autowired并进行调用,底层通过动态代理的方式进行消息的发送。
@Poller用于消息的轮询消费
@Bean
@InboundChannelAdapter(value = "order", poller = @Poller(fixedDelay = "10000", maxMessagesPerPoll = "1"))
public MessageSource<OrderMsg> orderMessageSource() {
return () -> {
String randomGoods = new ArrayList<>(goodsCount.keySet()).get(random.nextInt(goodsCount.size()));
return MessageBuilder.withPayload(new OrderMsg(randomGoods, random.nextInt(5))).build();
};
}
跟
ChannelAdapter合作进行消息的消费(每次最多拉取1条消息,间隔10s操作一次)
-
MessageChannel实现DirectChannel:单播模式的消息通道,默认使用轮询均衡策略。ExecutorChannel:基于线程池的单播模式的消息通道,默认使用轮询负载均衡策略。PublishSubscribeChannel:基于线程池的广播模式的消息通道。QueueChannel:PollableChannel接口的实现类,基于队列和信号量完成。PriorityChannel:PollableChannel接口的实现类,基于优先队列。
spring-integration-amqp(RabbitMQ)和spring-integration-kafka(Kafka)这些项目是针对不同消息中间件的适配。
示例代码
@SpringBootApplication
@EnableIntegration
public class IntegrationApplication {
public static void main(String[] args) {
SpringApplication.run(IntegrationApplication.class, args);
}
private static Random random = new Random();
private static BigDecimal totalMoney = new BigDecimal(0);
private static Map<String, Integer> goodsCount = new HashMap();
private static Map<String, BigDecimal> goodsMoney = new HashMap();
static {
goodsCount.put("apple", 10);
goodsCount.put("book", 100);
goodsCount.put("clothes", 50);
goodsMoney.put("apple", new BigDecimal(4000));
goodsMoney.put("book", new BigDecimal(30));
goodsMoney.put("clothes", new BigDecimal(500));
}
@RestController
class MessagingController {
@Autowired
TestGateway testGateway;
@GetMapping("/order")
void order() {
testGateway.echo(new OrderMsg("book", 10));
}
@GetMapping("/result")
String result() {
return "totalMoney: " + totalMoney + ", goods count info: " + goodsCount;
}
}
@MessagingGateway(name = "testGateway", defaultRequestChannel = "order")
public interface TestGateway {
@Gateway(requestChannel = "order", replyTimeout = 2, requestTimeout = 200)
String echo(OrderMsg orderMsg);
}
@Bean
MessageChannel order() {
return new DirectChannel();
}
@Bean
MessagingTemplate messagingTemplate() {
return new MessagingTemplate(order());
}
@Bean
CommandLineRunner runner() {
return args -> {
messagingTemplate().send(MessageBuilder.withPayload(
new OrderMsg("apple", 5)).build());
messagingTemplate().send(MessageBuilder.withPayload(
new OrderMsg("book", 30)).build());
messagingTemplate().send(MessageBuilder.withPayload(
new OrderMsg("clothes", 1)).build());
};
}
@Bean
@InboundChannelAdapter(value = "order", poller = @Poller(fixedDelay = "10000", maxMessagesPerPoll = "1"))
public MessageSource<OrderMsg> orderMessageSource() {
return () -> {
String randomGoods = new ArrayList<>(goodsCount.keySet()).get(random.nextInt(goodsCount.size()));
return MessageBuilder.withPayload(new OrderMsg(randomGoods, random.nextInt(5))).build();
};
}
@ServiceActivator(inputChannel = "order", outputChannel = "errorTopic")
Message receive(@Payload OrderMsg order) {
if (goodsCount.containsKey(order.getGoods()) && order.getCount() > 0) {
int newCount = goodsCount.get(order.getGoods()) - order.getCount();
if (newCount < 0) {
return MessageBuilder.withPayload("goods: " + order.getGoods() + " count is not enough").build();
} else {
goodsCount.put(order.getGoods(), newCount);
totalMoney = totalMoney.add(
goodsMoney.get(order.getGoods()).multiply(new BigDecimal(order.getCount())));
return MessageBuilder.withPayload("goods: " + order.getGoods() + " handle successful").build();
}
} else {
return MessageBuilder.withPayload("goods: " + order.getGoods() + " count is not enough or count is invalid")
.build();
}
}
@ServiceActivator(inputChannel = "errorTopic")
void result(String msg) {
System.out.println(msg);
}
static class OrderMsg {
String goods;
Integer count;
public OrderMsg() {
}
public OrderMsg(String goods, Integer count) {
this.goods = goods;
this.count = count;
}
public String getGoods() {
return goods;
}
public void setGoods(String goods) {
this.goods = goods;
}
public Integer getCount() {
return count;
}
public void setCount(Integer count) {
this.count = count;
}
}
}