Idempotent Consumer

36 篇文章 1 订阅

一、Idempotent Consumer

The Idempotent Consumer from the EIP patterns is used to filter out duplicate messages.

This pattern is implemented using the IdempotentConsumer class. This uses an Expression to calculate a unique message ID string for a given message exchange; this ID can then be looked up in the IdempotentRepository to see if it has been seen before; if it has the message is consumed; if its not then the message is processed and the ID is added to the repository.

The Idempotent Consumer essentially acts like a Message Filter to filter out duplicates.

Camel will add the message id eagerly to the repository to detect duplication also for Exchanges currently in progress.
On completion Camel will remove the message id from the repository if the Exchange failed, otherwise it stays there.

Options

The Idempotent Consumer has the following options:

Option Default Description
eager true Camel 2.0: Eager controls whether Camel adds the message to the repository before or after the exchange has been processed. If enabled before then Camel will be able to detect duplicate messages even when messages are currently in progress. By disabling Camel will only detect duplicates when a message has successfully been processed.
messageIdRepositoryRef null A reference to a IdempotentRepository to lookup in the registry. This option is mandatory when using XML DSL.

Using the Fluent Builders

The following example will use the header myMessageId to filter out duplicates

RouteBuilder builder = new











RouteBuilder() {





public




void configure() {





errorHandler(deadLetterChannel("mock:error"




));











from("seda:a"




) .idempotentConsumer(header("myMessageId"




),





MemoryIdempotentRepository.memoryIdempotentRepository(200))





.to("seda:b"




);





}





};





The above example will use an in-memory based MessageIdRepository which can easily run out of memory and doesn't work in a clustered environment. So you might prefer to use the JPA based implementation which uses a database to store the message IDs which have been processed

from("direct:start"











).idempotentConsumer(











header("messageId"










),











jpaMessageIdRepository(lookup(JpaTemplate.class), PROCESSOR_NAME)











).to("mock:result"










);





In the above example we are using the header messageId to filter out duplicates and using the collection myProcessorName to indicate the Message ID Repository to use. This name is important as you could process the same message by many different processors; so each may require its own logical Message ID Repository.

For further examples of this pattern in use you could look at the junit test case

Spring XML example

The following example will use the header myMessageId to filter out duplicates

<camelContext xmlns="http://camel.apache.org/schema/spring"





>











<route>










<from uri="direct:start"




/>











<idempotentConsumer messageIdRepositoryRef="myRepo"




>











<!-- use the messageId header as key for identifying duplicate messages -->















<header>










messageId</header>










<!-- if not a duplicate send it to this mock endpoint -->















<to uri="mock:result"




/>











</idempotentConsumer>










</route>










</camelContext>




Using This Pattern

If you would like to use this EIP Pattern then please read the Getting Started , you may also find the Architecture useful particularly the description of Endpoint and URIs . Then you could try out some of the Examples first before trying this pattern out.

 

-------------------------------------------------------------------------------------------------------------------------------------------

 

-------------------------------------------------------------------------------------------------------------------------------------------

 

-------------------------------------------------------------------------------------------------------------------------------------------

 

二、

Contents of /camel/trunk/camel-core/src/test/java/org/apache/camel/processor/IdempotentConsumerTest.java

Parent Directory Parent Directory | Revision Log Revision Log


Revision 927722 - ( show annotations ) ( download )
Fri Mar 26 08:03:00 2010 UTC (10 months ago) by davsclaus
File size: 6938 byte(s)
CAMEL-2576: Renamed redeliverDelay to redeliveryDelay.
1/**
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17package org.apache.camel.processor;
18
19import org.apache.camel.ContextTestSupport;
20import org.apache.camel.Endpoint;
21import org.apache.camel.Exchange;
22import org.apache.camel.Message;
23import org.apache.camel.Processor;
24import org.apache.camel.builder.RouteBuilder;
25import org.apache.camel.component.mock.MockEndpoint;
26import org.apache.camel.processor.idempotent.MemoryIdempotentRepository;
27
28/**
29 * @version $Revision$
30 */
31public class IdempotentConsumerTest extends ContextTestSupport {
32 protected Endpoint startEndpoint;
33 protected MockEndpoint resultEndpoint;
34
35 @Override
36 public boolean isUseRouteBuilder() {
37 return false;
38 }
39
40 public void testDuplicateMessagesAreFilteredOut() throws Exception {
41 context.addRoutes(new RouteBuilder() {
42 @Override
43 public void configure() throws Exception {
44 from("direct:start").idempotentConsumer(
45 header("messageId"), MemoryIdempotentRepository.memoryIdempotentRepository(200)
46 ).to("mock:result");
47 }
48 });
49 context.start();
50
51 resultEndpoint.expectedBodiesReceived("one", "two", "three");
52
53 sendMessage("1", "one");
54 sendMessage("2", "two");
55 sendMessage("1", "one");
56 sendMessage("2", "two");
57 sendMessage("1", "one");
58 sendMessage("3", "three");
59
60 assertMockEndpointsSatisfied();
61 }
62
63 public void testFailedExchangesNotAddedDeadLetterChannel() throws Exception {
64 context.addRoutes(new RouteBuilder() {
65 @Override
66 public void configure() throws Exception {
67 errorHandler(deadLetterChannel("mock:error").maximumRedeliveries(2).redeliveryDelay(0).logStackTrace(false));
68
69 from("direct:start").idempotentConsumer(
70 header("messageId"), MemoryIdempotentRepository.memoryIdempotentRepository(200)
71 ).process(new Processor() {
72 public void process(Exchange exchange) throws Exception {
73 String id = exchange.getIn().getHeader("messageId", String.class);
74 if (id.equals("2")) {
75 throw new IllegalArgumentException("Damm I cannot handle id 2");
76 }
77 }
78 }).to("mock:result");
79 }
80 });
81 context.start();
82
83 // we send in 2 messages with id 2 that fails
84 getMockEndpoint("mock:error").expectedMessageCount(2);
85 resultEndpoint.expectedBodiesReceived("one", "three");
86
87 sendMessage("1", "one");
88 sendMessage("2", "two");
89 sendMessage("1", "one");
90 sendMessage("2", "two");
91 sendMessage("1", "one");
92 sendMessage("3", "three");
93
94 assertMockEndpointsSatisfied();
95 }
96
97 public void testFailedExchangesNotAddedDeadLetterChannelNotHandled() throws Exception {
98 context.addRoutes(new RouteBuilder() {
99 @Override
100 public void configure() throws Exception {
101 errorHandler(deadLetterChannel("mock:error").handled(false).maximumRedeliveries(2).redeliveryDelay(0).logStackTrace(false));
102
103 from("direct:start").idempotentConsumer(
104 header("messageId"), MemoryIdempotentRepository.memoryIdempotentRepository(200)
105 ).process(new Processor() {
106 public void process(Exchange exchange) throws Exception {
107 String id = exchange.getIn().getHeader("messageId", String.class);
108 if (id.equals("2")) {
109 throw new IllegalArgumentException("Damm I cannot handle id 2");
110 }
111 }
112 }).to("mock:result");
113 }
114 });
115 context.start();
116
117 // we send in 2 messages with id 2 that fails
118 getMockEndpoint("mock:error").expectedMessageCount(2);
119 resultEndpoint.expectedBodiesReceived("one", "three");
120
121 sendMessage("1", "one");
122 sendMessage("2", "two");
123 sendMessage("1", "one");
124 sendMessage("2", "two");
125 sendMessage("1", "one");
126 sendMessage("3", "three");
127
128 assertMockEndpointsSatisfied();
129 }
130
131 public void testFailedExchangesNotAdded() throws Exception {
132 context.addRoutes(new RouteBuilder() {
133 @Override
134 public void configure() throws Exception {
135 // use default error handler
136 errorHandler(defaultErrorHandler());
137
138 from("direct:start").idempotentConsumer(
139 header("messageId"), MemoryIdempotentRepository.memoryIdempotentRepository(200)
140 ).process(new Processor() {
141 public void process(Exchange exchange) throws Exception {
142 String id = exchange.getIn().getHeader("messageId", String.class);
143 if (id.equals("2")) {
144 throw new IllegalArgumentException("Damm I cannot handle id 2");
145 }
146 }
147 }).to("mock:result");
148 }
149 });
150 context.start();
151
152 resultEndpoint.expectedBodiesReceived("one", "three");
153
154 sendMessage("1", "one");
155 sendMessage("2", "two");
156 sendMessage("1", "one");
157 sendMessage("2", "two");
158 sendMessage("1", "one");
159 sendMessage("3", "three");
160
161 assertMockEndpointsSatisfied();
162 }
163
164 protected void sendMessage(final Object messageId, final Object body) {
165 template.send(startEndpoint, new Processor() {
166 public void process(Exchange exchange) {
167 // now lets fire in a message
168 Message in = exchange.getIn();
169 in.setBody(body);
170 in.setHeader("messageId", messageId);
171 }
172 });
173 }
174
175 @Override
176 protected void setUp() throws Exception {
177 super.setUp();
178
179 startEndpoint = resolveMandatoryEndpoint("direct:start");
180 resultEndpoint = getMockEndpoint("mock:result");
181 }
182
183}

二、

org.apache.camel.processor.idempotent
Class IdempotentConsumer

java.lang.Object





extended by

org.apache.camel.impl.ServiceSupport




extended by

org.apache.camel.processor.idempotent.IdempotentConsumer




All Implemented Interfaces:
Navigate < Processor >, Processor , Service

public class IdempotentConsumer




extends ServiceSupport



implements Processor

, Navigate

< Processor

>

An implementation of the Idempotent Consumer pattern.

 

 

Version:
$Revision: 835732 $

Constructor Summary
IdempotentConsumer (Expression  messageIdExpression, IdempotentRepository <String > idempotentRepository, boolean eager, Processor  processor)
           

 

Method Summary
protected  void doStart ()
           
protected  void doStop ()
           
 IdempotentRepository <String > getIdempotentRepository ()
           
 Expression getMessageIdExpression ()
           
 Processor getProcessor ()
           
 boolean hasNext ()
          Are there more outputs?
 List <Processor > next ()
          Next group of outputs
protected  void onDuplicateMessage (Exchange  exchange, String  messageId)
          A strategy method to allow derived classes to overload the behaviour of processing a duplicate message
 void process (Exchange  exchange)
          Processes the message exchange
 String toString ()
           

 

Methods inherited from class org.apache.camel.impl.ServiceSupport
addChildService , getStatus , getVersion , isRunAllowed , isStarted , isStarting , isStopped , isStopping , removeChildService , start , stop

 

Methods inherited from class java.lang.Object
clone , equals , finalize , getClass , hashCode , notify , notifyAll , wait , wait , wait
 

Constructor Detail

IdempotentConsumer

public IdempotentConsumer




(Expression

 messageIdExpression,


IdempotentRepository

<String

> idempotentRepository,


boolean eager,


Processor

 processor)

Method Detail

toString

public String


toString



()
Overrides:
toString in class Object

process

public void process




(Exchange

 exchange)


throws Exception

Description copied from interface: Processor
Processes the message exchange

 

Specified by:
process in interface Processor
Parameters:
exchange - the message exchange
Throws:
Exception - if an internal processing error has occurred.

next

public List


<Processor

> next



()
Description copied from interface: Navigate
Next group of outputs

 

Specified by:
next in interface Navigate <Processor >
Returns:
next group or null if no more outputs

hasNext

public boolean hasNext




()
Description copied from interface: Navigate
Are there more outputs?

 

Specified by:
hasNext in interface Navigate <Processor >
Returns:
true if more outputs

getMessageIdExpression

public Expression


getMessageIdExpression



()

getIdempotentRepository

public IdempotentRepository


<String

> getIdempotentRepository



()

getProcessor

public Processor


getProcessor



()

doStart

protected void doStart




()


throws Exception

Specified by:
doStart in class ServiceSupport
Throws:
Exception

doStop

protected void doStop




()


throws Exception

Specified by:
doStop in class ServiceSupport
Throws:
Exception

onDuplicateMessage

protected void onDuplicateMessage




(Exchange

 exchange,


String

 messageId)
A strategy method to allow derived classes to overload the behaviour of processing a duplicate message

 

Parameters:
exchange - the exchange
messageId - the message ID of this exchange

-------------------------------------------------------------------------------------------------------------------------------------------

 

-------------------------------------------------------------------------------------------------------------------------------------------

 

-------------------------------------------------------------------------------------------------------------------------------------------

 

 

三、rg.apache.camel.spi

Interface IdempotentRepository<E>

All Known Implementing Classes:
FileIdempotentRepository , MemoryIdempotentRepository

public interface IdempotentRepository<E>










Access to a repository of Message IDs to implement the Idempotent Consumer pattern.

The add and contains methods is operating according to the Set contract.

 

 

Version:
$Revision: 782534 $

Method Summary
 boolean add (E  key)
          Adds the key to the repository.
 boolean confirm (E  key)
          Confirms the key, after the exchange has been processed sucesfully.
 boolean contains (E  key)
          Returns true if this repository contains the specified element.
 boolean remove (E  key)
          Removes the key from the repository.
 

Method Detail

add

boolean add










(E




 key)
Adds the key to the repository.

 

Parameters:
key - the key of the message for duplicate test
Returns:
true if this repository did not already contain the specified element

contains

boolean contains










(E




 key)
Returns true if this repository contains the specified element.

 

Parameters:
key - the key of the message
Returns:
true if this repository contains the specified element

remove

boolean remove










(E




 key)
Removes the key from the repository.

Is usually invoked if the exchange failed.

 

Parameters:
key - the key of the message for duplicate test
Returns:
true if the key was removed

 

confirm

boolean confirm


(E
key) Confirms the key, after the exchange has been processed sucesfully.

Parameters:
key
- the key of the message for duplicate test

Returns:
true
if the key was confirmed



------------------------------------------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------------------------------------------

------------------------------------------------------------------------------------------------------------------------------------------------------

四、Fluent Builders

Camel provides fluent builders for creating routing and mediation rules using a type-safe IDE friendly way which provides smart completion and is refactoring safe.




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
@Idempotent是一个自定义注解,用于标记需要进行幂等性检查的接口。在需要进行幂等性检查的接口上添加@Idempotent注解,然后定义一个拦截器来拦截这个注解。在拦截器中,可以使用Redis等缓存工具来实现幂等性检查。如果检查到重复请求,则直接返回结果,否则执行接口逻辑。 以下是一个使用@Idempotent注解实现幂等性检查的示例代码: ```java // 定义注解 @Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) public @interface Idempotent { String value() default ""; } // 定义拦截器 @Component public class IdempotentInterceptor implements HandlerInterceptor { @Autowired private RedisTemplate<String, Object> redisTemplate; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { Method method = ((HandlerMethod) handler).getMethod(); Idempotent idempotent = method.getAnnotation(Idempotent.class); if (idempotent != null) { String key = request.getRequestURI() + ":" + request.getMethod() + ":" + request.getParameter("token"); Boolean success = redisTemplate.opsForValue().setIfAbsent(key, 1, 60, TimeUnit.SECONDS); if (!success) { response.setContentType("application/json;charset=UTF-8"); response.getWriter().write("{\"code\":400,\"msg\":\"请勿重复请求\"}"); return false; } } return true; } } // 在需要进行幂等性检查的接口上添加@Idempotent注解 @RestController public class TestController { @Autowired private RedisTemplate<String, Object> redisTemplate; @PostMapping("/test") @Idempotent public String test(@RequestBody Map<String, Object> params) { // your code here return "success"; } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值