Spring JMS Integration Gateway Example 12 minute read
A detailed step-by-step tutorial on how to connect to a JMS broker using a Spring Integration Gateway and Spring Boot.
??xml version="1.0" encoding="utf-8" standalone="yes"?> In our first lesson, you will get introduced to the concepts of Enterprise Application Integration. You will learn about the and Enterprise integration patterns that can be applied to simplify integration between different platforms and the Integration strategies that can be followed for this purpose. Finally, we will discuss how and why to implement a Message driven architecture and how to achieve both Synchronous and asynchronous communication among nodes. In this lesson, you will get to understand how Spring Integration works under the hood. The core concepts of Spring Integration messaging system (like message channels and endpoints) will be introduced. Additionally, the components that build the framework will be discussed, including the channel adapters, transformers, filters, routers etc. Finally, the two distinct methods of communication (synchronous and asynchronous) are explained and the lesson ends with a discussion on error handling. In this lesson, we will focus on the integration with external web services. Spring Integration comes with the necessary functionality (adapters, channels etc.) to support web services out of the box. A full example is built from scratch in order to better understand the topic. In this lesson, we will focus on integrating our application with JMS messaging. For this purpose, we will use Active MQ, which will be our broker. We will show examples of sending and receiving JMS messages by using the Spring Integration JMS channel adapters. Following these examples, we will see some ways of customizing these invocations by configuring message conversion and destination resolution. In this lesson, we will wrap everything up by providing a complete application that uses several of the components provided by Spring Integration in order to provide a service to its users. We will discuss the system architecture, the actual implementation and the relevant error handling. In this lesson, we will examine different mechanisms of monitoring or gathering more information about what is going on within the messaging system. Some of these mechanisms consist of managing or monitoring the application through MBeans, which are part of the JMX specification. Another mechanism discussed in this chapter is how we will implement the EIP idempotent receiver pattern using a metadata store. Finally, the last mechanism described is the control bus. This will let us send messages that will invoke operations on components in the application context. Enterprise integration is too complex to be solved with a simple 'cookbook' approach. Instead, patterns can provide guidance by documenting the kind of experience that usually lives only in architects' heads: they are accepted solutions to recurring problems within a given context. Patterns are abstract enough to apply to most integration technologies, but specific enough to provide hands-on guidance to designers and architects. Patterns also provide a vocabulary for developers to efficiently describe their solution. Patterns are not 'invented'; they are harvested from repeated use in practice. If you have built integration solutions, it is likely that you have used some of these patterns, maybe in slight variations and maybe calling them by a different name. The purpose of this site is not to "invent" new approaches, but to present a coherent collection of relevant and proven patterns, which in total form an integration pattern language. Despite the 700+ pages, our book covers only a fraction of patterns (and the problems to be solved) in the integration space. The current patterns focus on Messaging, which forms the basis of most other integration patterns. We have started to harvest more patterns but are realizing (once again) how much work documenting these patterns really is. So please stay tuned. We have documented 65 messaging patterns, organized as follows: A detailed step-by-step tutorial on how to connect to a JMS broker using a Spring Integration Gateway and Spring Boot. A detailed step-by-step tutorial on how to connect to Apache ActiveMQ Artemis using Spring JMS and Spring Boot. A detailed step-by-step tutorial on how to publish/subscribe to a JMS topic using Spring JMS and Spring Boot. A detailed step-by-step tutorial on how to connect to an ActiveMQ JMS broker using Spring Integration and Spring Boot. A detailed step-by-step tutorial on how a Spring JMS listener works in combination with Spring Boot. A detailed step-by-step tutorial on how to use JmsTemplate in combination with Spring JMS and Spring Boot. A detailed step-by-step tutorial on how to implement a message selector using Spring JMS and Spring Boot. A detailed step-by-step tutorial on how to implement a message converter using Spring JMS and Spring Boot. A detailed step-by-step tutorial on how to use a Spring Boot admin UI to manage Spring Batch jobs. A detailed step-by-step tutorial on how to implement a Spring Batch Tasklet using Spring Boot. A detailed step-by-step tutorial on how to implement a Hello World Spring Batch job using Spring Boot.
q个问题的方案则是集选主Q一个集中Q只有一个LEADERQ由LEADER负责执行定时d工作。当LEADER被取消时Q会在剩下的实例中再选LEADER?br />
持有分布式锁的实例则是LEADER?br />
SPRING INTEGRATION JDBC 则已提供相关功能?br />
pom.xml
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId>
</dependency>
<dependency>
<groupId>org.mariadb.jdbc</groupId>
<artifactId>mariadb-java-client</artifactId>
</dependency>
LeaderElectionIntegrationConfig.java
import java.util.concurrent.CopyOnWriteArrayList;
import javax.sql.DataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.integration.jdbc.lock.DefaultLockRepository;
import org.springframework.integration.jdbc.lock.JdbcLockRegistry;
import org.springframework.integration.jdbc.lock.LockRepository;
import org.springframework.integration.support.leader.LockRegistryLeaderInitiator;
import com.paul.integration.leader.ControlBusGateway;
import com.paul.integration.leader.MyCandidate;
@Configuration
public class LeaderElectionIntegrationConfig {
@Bean
public List<String> needToStartupAdapterList(){
return new CopyOnWriteArrayList<>();
}
@Bean
public DefaultLockRepository defaultLockRepository(DataSource dataSource){
DefaultLockRepository defaultLockRepository =
new DefaultLockRepository(dataSource);
// defaultLockRepository.setTimeToLive(60_000);
return defaultLockRepository;
}
@Bean
public JdbcLockRegistry jdbcLockRegistry(LockRepository lockRepository){
return new JdbcLockRegistry(lockRepository);
}
@Bean
public MyCandidate myCandidate(
ControlBusGateway controlBusGateway,
List<String> needToStartupAdapterList
) {
return new MyCandidate(controlBusGateway, needToStartupAdapterList);
}
@Bean
public LockRegistryLeaderInitiator leaderInitiator() {
return new LockRegistryLeaderInitiator(
jdbcLockRegistry(null), myCandidate(null, null)
);
}
}
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.integration.leader.Context;
import org.springframework.integration.leader.DefaultCandidate;
import com.novacredit.mcra.mcracommon.integration.gateway.ControlBusGateway;
public class MyCandidate extends DefaultCandidate{
private static final Logger LOG = LoggerFactory.getLogger(MyCandidate.class);
private List<String> needToStartupAdapterList;
private ControlBusGateway controlBusGateway;
public MyCandidate(
ControlBusGateway controlBusGateway,
List<String> needToStartupAdapterList
) {
this.controlBusGateway = controlBusGateway;
this.needToStartupAdapterList = needToStartupAdapterList;
}
@Override
public void onGranted(Context context) {
super.onGranted(context);
LOG.info("*** Leadership granted ***");
LOG.info("STARTING MONGODB POLLER");
needToStartupAdapterList
.forEach(
c -> {
// c = "@'testIntegrationFlow.org.springframework.integration.config."
// + "SourcePollingChannelAdapterFactoryBean#0'";
String command = c + ".start()";
LOG.info("-----{}", command);
controlBusGateway.sendCommand(command);
}
);
LOG.info("STARTUP MESSAGE SENT");
}
@Override
public void onRevoked(Context context) {
super.onRevoked(context);
LOG.info("*** Leadership revoked ***");
LOG.info("STOPPING MONGODB POLLER");
needToStartupAdapterList
.forEach(
c -> {
// c = "@'testIntegrationConfig.testIntegrationFlow."
// + "mongoMessageSource.inboundChannelAdapter'";
String command = c + ".stop()";
LOG.info("-----{}", command);
// controlBusGateway.sendCommand(command);
}
);
LOG.info("SHUTDOWN MESSAGE SENT");
}
}
ControlBusIntegrationConfig.java
import org.springframework.context.annotation.Configuration;
import org.springframework.integration.dsl.IntegrationFlow;
import org.springframework.integration.dsl.IntegrationFlows;
import org.springframework.integration.dsl.MessageChannels;
import org.springframework.integration.gateway.GatewayProxyFactoryBean;
import org.springframework.integration.handler.LoggingHandler;
import org.springframework.messaging.MessageChannel;
import com.paul.integration.gateway.ControlBusGateway;
@Configuration
public class ControlBusIntegrationConfig {
@Bean
public MessageChannel controlBusChannel() {
return MessageChannels.direct().get();
}
@Bean
public IntegrationFlow controlBusFlow() {
return IntegrationFlows.from(controlBusChannel())
.log(LoggingHandler.Level.INFO, "controlBusChannel")
.controlBus()
.get();
}
@Bean
public GatewayProxyFactoryBean controlBusGateway() {
GatewayProxyFactoryBean gateway = new GatewayProxyFactoryBean(ControlBusGateway.class);
gateway.setDefaultRequestChannel(controlBusChannel());
gateway.setDefaultRequestTimeout(300l);
gateway.setDefaultReplyTimeout(300l);
return gateway;
}
}
ControlBusGateway.java
public void sendCommand(String command);
}
各个应用实例q行Ӟ其中的LockRegistryLeaderInitiator会自动运行,抢夺LEADER数据Q最l只有一个实例夺取。之后再执行MyCandidate中的代码?br />
]]>
引入了新名词QSupplier、Function与Consumer。实际上q几个类可视为AdapterQ如果之前已l有存在的Servicec,且方法名为各U各P可以重新包装成Supplier、Function与ConsumerQƈ在固定的Ҏ名:apply/get/accept中调用Service的方法?br />Supplier
当在配置文g中注入此cd的BeanQƈ在spring.cloud.stream.function.definition加入此Bean的名UͼSPRING CLOUD STREAM׃帮你生成一个Output MessageChannelQƈq接上此BeanQ后l只需要在BINDDING中加入对应的Destination NameQ即可向BROKER发消息了?br />Consumer
当在配置文g中注入此cd的BeanQƈ在spring.cloud.stream.function.definition加入此Bean的名UͼSPRING CLOUD STREAM׃帮你生成一个Input MessageChannelQƈq接上此BeanQ后l只需要在BINDDING中加入对应的Destination NameQ即可收到BROKER推送关于此Destination的消息了?br />Function
当在配置文g中注入此cd的BeanQƈ在spring.cloud.stream.function.definition加入此Bean的名UͼSPRING CLOUD STREAM׃帮你生成一个Input和Output MessageChannelQƈq接上此BeanQ后l只需要在BINDDING中分别对Input和Output MessageChannel加入对应的Destination Name1/Name2Q即可收到BROKER推送关于此Destination的消息,也可以向BROKER发消息了?br />与SPRING INTEGRATION的整?/h2>如果要对消息q行复杂处理Q如拆分消息、聚合消息、IF ELSE消息{,p借助SPRING INTEGRATION了?br />
public IntegrationFlow upperCaseFlow(LoanService loanService) {
return IntegrationFlows
//turn this IntegrationFlow as a gateway, here is a Function interface
//with loadCheckerFunction as bean name
.from(LoadCheckerFunction.class, gateway -> gateway.beanName("loadCheckerFunction"))
.handle(loanService, "check")
.logAndReply(LoggingHandler.Level.WARN);
}
public interface LoadCheckerFunction extends Function<Loan, Loan>{
}
IntegrationFlows.from(Class<?> serviceInterface)是可以将本IntegrationFlow包装成serviceInterface的实现类Q如果调用此接口Q最l会q回IntegrationFlow最后一个步骤的实体Q如果这个serviceInterface是Function的话Q刚好和SPRING CLOUD STREAMҎ上?br />
后箋在spring.cloud.stream.function.definition加入此Bean的名UloadCheckerFunctionQSPRING CLOUD STREAM׃帮你生成一个Input和Output MessageChannelQƈq接上此BeanQ再在BINDDING中分别对Input和Output MessageChannel加入对应的Destination Name1/Name2Q即可收到BROKER推送关于此Destination的消息,也可以向BROKER发消息?br />
application.yaml
# spring.cloud.stream.poller.fixed-delay=1000
# This setting can control which function method in our code will be triggered if there are multiple
# spring.cloud.function.definition=supplyLoan
# Give the autogenerated binding a friendlier name
spring:
application:
name: loan-check-rabbit
banner:
location: classpath:/banner-rabbit.txt
cloud:
stream:
function.definition: loadCheckerFunction
#BindingProperties
bindings:
loadCheckerFunction-in-0:
destination: queue.pretty.log.messages
binder: local_rabbit
loadCheckerFunction-out-0:
destination: queue.pretty.approved.messages
binder: local_rabbit
#BinderProperties
binders:
local_rabbit:
type: rabbit
environment:
spring:
rabbitmq:
host: 10.80.27.69
port: 5672
username: guest
password: guest
virtual-host: my-virtual-hostReference
https://spring.io/blog/2019/10/25/spring-cloud-stream-and-spring-integration
]]>
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpMethod;
import org.springframework.integration.dsl.IntegrationFlow;
import org.springframework.integration.dsl.IntegrationFlows;
import org.springframework.integration.dsl.Transformers;
import org.springframework.integration.http.dsl.Http;
import org.springframework.web.client.RestTemplate;
@SpringBootApplication
public class SpringIntegrationEnricherApplication {
public static void main(String[] args) {
SpringApplication.run(SpringIntegrationEnricherApplication.class, args);
}
@Bean
public IntegrationFlow jsonEnricherFlow(RestTemplate restTemplate) {
return IntegrationFlows.from(Function.class)
.transform(Transformers.fromJson(Map.class))
.enrich((enricher) -> enricher
.<Map<String, ?>>requestPayload((message) ->
((List<?>) message.getPayload().get("attributeIds"))
.stream()
.map(Object::toString)
.collect(Collectors.joining(",")))
.requestSubFlow((subFlow) ->
subFlow.handle(
Http.outboundGateway("/attributes?id={ids}", restTemplate)
.httpMethod(HttpMethod.GET)
.expectedResponseType(Map.class)
.uriVariable("ids", "payload")))
.propertyExpression("attributes", "payload.attributes"))
.<Map<String, ?>, Map<String, ?>>transform(
(payload) -> {
payload.remove("attributeIds");
return payload;
})
.transform(Transformers.toJson())
.get();
}
}
https://stackoverflow.com/questions/58205432/spring-integration-enrich-transform-message-using-rest-call
https://www.tabnine.com/web/assistant/code/rs/5c781b6ae70f87000197ab9f#L312
]]>
public class SpringIntegrationDslHttpRetryApplication {
@SuppressWarnings("unchecked")
public static void main(String[] args) {
ConfigurableApplicationContext applicationContext =
SpringApplication.run(SpringIntegrationDslHttpRetryApplication.class, args);
Function<Object, Object> function = applicationContext.getBean(Function.class);
function.apply("foo");
}
@Bean
public IntegrationFlow httpRetryFlow() {
return IntegrationFlows.from(Function.class)
.handle(Http.outboundGateway("http://localhost:11111")
.httpMethod(HttpMethod.GET)
.expectedResponseType(String.class),
e -> e.advice(retryAdvice()))
.get();
}
@Bean
public RequestHandlerRetryAdvice retryAdvice() {
return new RequestHandlerRetryAdvice();
}
}
logging.level.org.springframework.retry=debug
Reference:
https://docs.spring.io/spring-integration/reference/html/handler-advice.html#retry-advice
https://stackoverflow.com/questions/49784360/configure-error-handling-and-retry-for-http-outboundgateway-spring-dsl
https://stackoverflow.com/questions/50262862/requesthandlerretryadvice-with-httprequestexecutingmessagehandler-not-working
https://stackoverflow.com/questions/63689856/spring-integration-http-outbound-gateway-retry-based-on-reply-content
https://blog.csdn.net/cunfen8879/article/details/112552420
]]>
只要在JAVA BEAN中需要验证的字段加@NotNullq种标签Q然后在SERVISE中的输入参数中加@Valid标签Q则激z验证流E?br />也可以编E的方式自己验证Q?br />
//@Validated
public class MqMessageCcdValidator {
private static final Logger LOGGER = LoggerFactory.getLogger(MqMessageCcdValidator.class);
@Autowired
private Validator validator;
@ServiceActivator
public MqMessage<CcdRequest> validate(/* @Valid */ Message<MqMessage<CcdRequest>> requestMessage) {
Set<ConstraintViolation<MqMessage<CcdRequest>>> set = validator.validate(requestMessage.getPayload());
if(CollectionUtils.isNotEmpty(set)) {
CompositeException compositeException = new CompositeException();
set.forEach(
constraintViolation -> {
LOGGER.info("{}", constraintViolation);
ReqInfoValidationException exception =
new ReqInfoValidationException(constraintViolation.getMessage());
compositeException.addException(exception);
}
);
throw new MessageHandlingException(requestMessage, compositeException);
}
return requestMessage.getPayload();
}
}
自定义验证规?br />可用标签来做Q以下ؓ验证手机L规则Q?br />
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import javax.validation.Constraint;
import javax.validation.Payload;
import javax.validation.ReportAsSingleViolation;
import javax.validation.constraints.Pattern;
@Retention(RUNTIME)
@Target(value = { ElementType.FIELD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE })
@Constraint(validatedBy = {})
@ReportAsSingleViolation
@Pattern(regexp = "^1[3-9]\\d{9}$")
public @interface ChinaPhone {
String message() default "Invalid Chinese mobile No.";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
如果比较复杂的验证规则,则参见:
https://reflectoring.io/bean-validation-with-spring-boot/#implementing-a-custom-validator
How to use Java Bean Validation in Spring Boot
https://nullbeans.com/how-to-use-java-bean-validation-in-spring-boot/
Complete Guide to Validation With Spring Boot
https://reflectoring.io/bean-validation-with-spring-boot/
Spring JMS Validate Messages using JSR-303 Bean Validation
https://memorynotfound.com/spring-jms-validate-messages-jsr-303-bean-validation/
Spring REST Validation Example
https://mkyong.com/spring-boot/spring-rest-validation-example/
Spring Boot 整合 Bean Validation 校验数据
https://blog.csdn.net/wangzhihao1994/article/details/108403732
]]>
有一个主程、三个子程和一个聚合流E,聚合程会聚合三个子程的物,通知LE,再往下走?br />q且LE会感知子流E的错误Qƈ会交l相应错误处理流E处理,且将l果再交l聚合流E?br />
对应SPRING INTEGRATION 的SCATTERGATHER模式Q?br />
public IntegrationFlow scatterGatherAndExecutorChannelSubFlow(TaskExecutor taskExecutor) {
return f -> f
.scatterGather(
scatterer -> scatterer
.applySequence(true)
.recipientFlow(f1 -> f1.transform(p -> "Sub-flow#1"))
.recipientFlow(f2 -> f2
.channel(c -> c.executor(taskExecutor))
.transform(p -> {
throw new RuntimeException("Sub-flow#2");
})),
null,
s -> s.errorChannel("scatterGatherErrorChannel"));
}
@ServiceActivator(inputChannel = "scatterGatherErrorChannel")
public Message<?> processAsyncScatterError(MessagingException payload) {
return MessageBuilder.withPayload(payload.getCause().getCause())
.copyHeaders(payload.getFailedMessage().getHeaders())
.build();
}
https://github.com/adnanmamajiwala/spring-integration-sample/tree/master/dsl-scatter-gather/src/main/java/com/si/dsl/scattergather
https://docs.spring.io/spring-integration/docs/5.1.x/reference/html/#scatter-gather
]]>
https://stackoverflow.com/questions/50608415/cwsia0112e-the-property-name-keep-alive-is-not-a-valid-java-identifier
]]>
public IntegrationFlow provisionUserFlow() {
return
IntegrationFlows.from("input.channel")
.publishSubscribeChannel(Executors.newCachedThreadPool(),
s -> s.applySequence(true)
.subscribe(f -> f.enrichHeaders(e -> e.header(MessageHeaders.ERROR_CHANNEL, "errorChannel", true))
.handle(provisionerA, "provision")
.channel("aggregatorChannel")
)
.subscribe(f -> f.enrichHeaders(e -> e.header(MessageHeaders.ERROR_CHANNEL, "errorChannel", true))
.handle(provisionerB, "provision")
.channel("aggregatorChannel"))
)
.get();
}
@Bean
public IntegrationFlow aggregateFlow() {
return IntegrationFlows.from("aggregatorChannel")
.channel( aggregatorChannel)
.aggregate( a -> a.processor( collect, "aggregatingMethod"))
.get();
}
@Transformer( inputChannel = "errorChannel", outputChannel = "aggregatorChannel")
public Message<?> errorChannelHandler(ErrorMessage errorMessage) throws RuntimeException {
Message<?> failedMessage = ((MessagingException) errorMessage.getPayload()).getFailedMessage();
Exception exception = (Exception) errorMessage.getPayload();
return MessageBuilder.withPayload( exception.getMessage())
.copyHeadersIfAbsent( failedMessage.getHeaders() )
.build();
}
https://stackoverflow.com/q/46495127/11790720
]]>
public IntegrationFlow parallelSplitRouteAggregateFlow() {
return IntegrationFlows
.from(Http.inboundGateway("/trigger"))
.handle((p, h) -> Arrays.asList(1, 2, 3))
.split()
.channel(MessageChannels.executor(Executors.newCachedThreadPool()))
.<Integer, Boolean>route(o -> o % 2 == 0, m -> m
.subFlowMapping(true, sf -> sf.gateway(oddFlow()))
.subFlowMapping(false, sf -> sf.gateway(evenFlow())))
.aggregate()
.get();
}
@Bean
public IntegrationFlow oddFlow() {
return flow -> flow.<Integer>handle((payload, headers) -> "odd");
}
@Bean
public IntegrationFlow evenFlow() {
return flow -> flow.<Integer>handle((payload, headers) -> "even");
}
https://stackoverflow.com/questions/50121384/spring-integration-parallel-split-route-aggregate-flow-fails-due-to-one-way-mess
]]>
import java.util.Random;
import javax.jms.ConnectionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.integration.annotation.IntegrationComponentScan;
import org.springframework.integration.channel.PublishSubscribeChannel;
import org.springframework.integration.core.MessageSource;
import org.springframework.integration.dsl.IntegrationFlow;
import org.springframework.integration.dsl.IntegrationFlows;
import org.springframework.integration.dsl.core.Pollers;
import org.springframework.integration.dsl.jms.Jms;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.messaging.MessagingException;
/**
* Show how to handle error in spring integration flow.
* Please note, errorChannel in spring integration only applicable to
* error thrown in asynch component.
*
* @author zakyalvan
*/
@SpringBootApplication
@IntegrationComponentScan
public class ErrorHandlingApplication {
public static void main(String[] args) throws Exception {
ConfigurableApplicationContext applicationContext = new SpringApplicationBuilder(ErrorHandlingApplication.class)
.web(false)
.run(args);
Runtime.getRuntime().addShutdownHook(new Thread(() -> applicationContext.close()));
System.out.println("Pres enter key to exit");
System.in.read();
System.exit(0);
}
@Autowired
private ConnectionFactory connectionFactory;
@Bean
public MessageSource<Integer> randomIntegerMessageSource() {
return () -> MessageBuilder.withPayload(new Random().nextInt()).build();
}
@Bean
public IntegrationFlow withErrorFlow() {
return IntegrationFlows.from(randomIntegerMessageSource(), spec -> spec.poller(Pollers.fixedDelay(1000)))
.handle(Jms.outboundGateway(connectionFactory)
.requestDestination("processor.input")
.replyContainer(spec -> spec.sessionTransacted(true)))
.get();
}
@Autowired
@Qualifier("errorChannel")
private PublishSubscribeChannel errorChannel;
@Bean
public IntegrationFlow errorHandlingFlow() {
return IntegrationFlows.from(errorChannel)
.handle(message -> System.out.println("@@@@@@@@@@@@@@@@@@@@@" + ((MessagingException) message.getPayload()).getFailedMessage().getPayload()))
.get();
}
}
]]>
q等型,同一个MESSAGEQ如MESSAGE ID都一P在MESSAGINGpȝ中不运行多次Q结果都一P为啥Q因为重复的MESSAGEQ都被忽略了?br />
ҎQ?br />
消息被处理后Q从消息中取出IDQ放入META-DATA-STORE中,后箋处理消息Ӟ要从META-DATA-STORE中检查是否有倹{?br />
下面q个ҎQID的存储和判断是否重复消息都在一个INTERCEPTOR中搞定?br />
https://stackoverflow.com/questions/50401460/spring-integration-dsl-configure-idempotent-receiver-to-identify-duplicates
https://www.javacodegeeks.com/2015/09/monitoring-and-management.html
claim-check
MESSAGE的PAYLOAD存在STORE中,q回一个IDQ这个ID即claim-checkQ如果需要取MESSAGE的DETAIlӞ可从STORE中取出MESSAGE?br />https://github.com/spring-projects/spring-integration/blob/master/src/reference/asciidoc/claim-check.adoc
]]>Introduction to Enterprise Application Integration
Spring Integration Fundamentals
Spring Integration and Web Services
Enterprise Messaging
Spring Integration Full Example
Monitoring and Management
]]>
]]>
Spring Integration 中文手册 (2)
]]>
SPRING INTEGRATION是实CEIP模式的一U框Ӟ即用CHANNEL和JMS-INBOUND-ADAPTER、JMS-OUTBOUND-ADAPTERQ完全脱MJmsTemplate的API?br />
如果需要实现这U场景:从BROKER取一条消息,处理消息Q且处理途中不要再从BROKER再取消息Q处理完后再取消息,再处理?br />
q样要求手动开始和停止JMS LISTENERQ即手动开始和停止JMS-INBOUND-ADAPTER、JMS-OUTBOUND-ADAPTER?br />
@InboundChannelAdapter(value = "loaderResponseChannel")
public MessageSource loaderResponseSource() throws Exception {
return Jms
.inboundAdapter(oracleConnectionFactory())
.configureJmsTemplate(
t -> t.deliveryPersistent(true)
.jmsMessageConverter(jacksonJmsMessageConverter())
).destination(jmsInbound).get();
}
当用@InboundChannelAdapterӞ会自动注册一个SourcePollingChannelAdapter Q但q个名字比较长:configrationName.loaderResponseSource.inboundChannelAdapter?br />
呼叫q个实例的start()和stop()Ҏ卛_?br />
public IntegrationFlow controlBusFlow() {
return IntegrationFlows.from("controlBus")
.controlBus()
.get();
}
Message operation = MessageBuilder.withPayload("@configrationName.loaderResponseSource.inboundChannelAdapter.start()").build();
operationChannel.send(operation)
https://stackoverflow.com/questions/45632469/shutdown-spring-integration-with-jms-inboundadapter
https://docs.spring.io/spring-integration/docs/5.0.7.RELEASE/reference/html/system-management-chapter.html#control-bus
https://github.com/spring-projects/spring-integration-java-dsl/blob/master/src/test/java/org/springframework/integration/dsl/test/jms/JmsTests.java
https://stackoverflow.com/questions/50428552/how-to-stop-or-suspend-polling-after-batch-job-fail
]]>Messaging Patterns
https://www.enterpriseintegrationpatterns.com/patterns/messaging/index.html
]]>Spring JMS Integration Gateway Example 12 minute read
Spring JMS Artemis Example 6 minute read
Spring JMS Topic Example 5 minute read
Spring JMS Integration Example12 minute read
Spring JMS Listener Example 7 minute read
Spring JMS JmsTemplate Example 7 minute read
Spring JMS Message Selector Example 5 minute read
Spring JMS Message Converter Example5 minute read
Spring Batch Admin Example 11 minute read
Spring Batch Tasklet Example 7 minute read
Spring Batch Example 11 minute read
]]>
Let's take a look on the samples howto use that based on ActiveMQ JMS.
https://bitbucket.org/tomask79/spring-integration-java-dsl/src/master/
1.spring integration 's architecture
主要提供两个功能Q?/p>
在系l内提供实现轻量U、事仉动交互行为的框架
在系l间提供一U基于适配器的q_Q以支持灉|的系l间交互
2.spring integration对于企业集成模式的支?/p>
2.1MessageQ一个信息的单元Q通常有消息头QheaderQ和消息内容QpayloadQ组?/p>
2.2Message channelQ消息处理节点之间的q接Q负责将Message从生产者传输到消费者?/p>
Ҏ消费者的多少Q可分ؓpoint to point和publish-subscribe两种
Ҏ消息传输方式的不同,分ؓ同步和异步两U?/p>
2.3Message EndpointQ消息处理节点,消息从节点进入通道Q也是从节点d通道
几个常见的Message EndPointQ?/p>
CHANNEL ADAPTERQ用于连接该适配器的特点是单向消息流的,要么是消息通过该适配器进入通道Q要么是消息通过该适配器离开通道
MESSAGING GATEWAYQ处理的消息和Channel Adapter不同Q不是单向的Q即有进入该节点的消息,也会从该节点发出消息?br />
SERVICE ACTIVATORQ该节点调用服务来处理输入的消息Qƈ服务返回的数据发送到输出通道。在spring integration中,调用的方法被限定为本地方法调用?br />
ROUTERQ\由器Q将输入的消息\由到某个输出通道?/p>
SPLITTERQ将输入的消息拆分成子消?br />
AGGREGATORQ将输入的多个消息合qؓ一个消?br />
3.观看书中例子hello-world思?/p>
试gatewayӞ下面代码向通道names内放入消息worldQ?/p>
然后service-activator从names通道中获得消息worldQ调用方法sayHelloq回值到lgatewayQ?/p>
解释Qgateway有一个serviceQinterface的属性,q个属性指向一个interface。当我们用一个接口声明一个gatewayӞspring integration会自动帮我们生成该接口的代理c,q样当我们往gateway发送消息时Qspring integration会通过代理cL消息转发到defaultQrequestQchannel中去