JMS
Camel 1.0부터 지원
producer와 consumer 모두 지원
이 컴포넌트를 사용하면 JMS 큐나 토픽으로 메시지를 보내거나(또는 소비하거나) 할 수 있어요. 전송에는 Spring의 JmsTemplate, 소비에는 MessageListenerContainer를 쓰는 등 Spring의 JMS 지원을 통한 선언적 트랜잭션을 활용합니다.
출처: 문서
본문
Maven 사용자라면 이 컴포넌트를 쓰려고 pom.xml에 다음 의존성을 추가해야 해요:
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-jms</artifactId>
<version>x.x.x</version>
<!-- use the same version as your Camel core version -->
</dependency>
팁 ActiveMQ 사용하기
Apache ActiveMQ를 쓴다면 ActiveMQ에 맞게 최적화된 ActiveMQ 컴포넌트를 쓰는 게 좋아요. 이 페이지의 모든 옵션과 샘플은 ActiveMQ 컴포넌트에서도 그대로 유효해요.
참고 트랜잭션과 캐싱
JMS와 함께 트랜잭션을 쓴다면 성능에 영향을 줄 수 있으니 아래의 Transactions and Cache Levels 섹션을 보세요.
참고 JMS 위의 요청/응답
요청/응답 관련 중요 내용은 이 페이지 아래쪽의 Request-reply over JMS 섹션을 꼭 읽어 주세요. Camel은 성능과 클러스터 환경을 위해 설정할 수 있는 옵션을 여러 개 제공하거든요.
URI 형식 (URI Format)
jms:[queue:|topic:]destinationName[?options]
destinationName은 JMS 큐 또는 토픽 이름이에요. 기본적으로 destinationName은 큐 이름으로 해석됩니다. 예를 들어 FOO.BAR 큐에 연결하려면 이렇게 쓰세요:
jms:FOO.BAR
원하면 선택적인 queue: 접두어를 붙일 수도 있어요:
jms:queue:FOO.BAR
토픽에 연결하려면 topic: 접두어를 반드시 붙여야 해요. 예를 들어
Stocks.Prices 토픽에 연결하려면 이렇게 쓰세요:
jms:topic:Stocks.Prices
쿼리 옵션은 다음 형식으로 URI 뒤에 붙여요. ?option=value&option=value&…
노트 (Notes)
ActiveMQ 사용하기 (Using ActiveMQ)
JMS 컴포넌트는 메시지를 보낼 때 Spring의 JmsTemplate을 재사용해요. 이건 non-J2EE 컨테이너에서 쓰기엔 이상적이지 않아서, 성능 문제를 피하려면 보통 JMS 프로바이더 쪽에 어느 정도 캐싱이 필요합니다.
Apache ActiveMQ를 메시지 브로커로 쓸 생각이라면, 다음 중 하나를 하는 걸 권장해요:
-
ActiveMQ에 맞게 이미 최적화돼 있는 ActiveMQ 컴포넌트를 쓰세요.
-
ActiveMQ의
PoolingConnectionFactory를 쓰세요.
트랜잭션과 캐시 레벨 (Transactions and Cache Levels)
메시지를 소비하면서 트랜잭션(transacted=true)을 쓴다면, 캐시 레벨의 기본 설정이 성능에 영향을 줄 수 있어요.
XA 트랜잭션을 쓴다면 캐싱을 할 수 없어요. 캐싱하면 XA 트랜잭션이 제대로 동작하지 않을 수 있습니다.
XA를 쓰지 않는다면 캐싱을 고려하세요. 성능이 빨라지거든요. 예를 들어 cacheLevelName=CACHE_CONSUMER처럼 설정할 수 있어요.
cacheLevelName의 기본 설정은 CACHE_AUTO예요. 이 기본값은 모드를 자동으로 감지해서 캐시 레벨을 이렇게 정합니다:
-
transacted=false면CACHE_CONSUMER -
transacted=true면CACHE_NONE
그러니 기본 설정은 보수적이라고 할 수 있어요. non-XA 트랜잭션을 쓴다면 cacheLevelName=CACHE_CONSUMER를 쓰는 걸 고려해 보세요.
Durable 구독 (Durable Subscriptions)
JMS 2.0에서의 Durable 구독 (Durable Subscriptions with JMS 2.0)
durable 토픽 구독을 쓰고 싶다면 durableSubscriptionName을 지정해야 해요.
JMS 1.1에서의 Durable 구독 (Durable Subscriptions with JMS 1.1)
durable 토픽 구독을 쓰고 싶다면 clientId와 durableSubscriptionName 둘 다 지정해야 해요. clientId 값은 유일해야 하고, 전체 네트워크에서 하나의 JMS 연결 인스턴스만 쓸 수 있어요.
참고 Apache ActiveMQ Classic이나 Apache ActiveMQ Artemis를 쓴다면 Virtual Topic이라는 기능을 쓰는 편이 좋을 수 있어요. 이걸 쓰면 유일한
clientId가 필요 없어질 수 있어요.이 기능을 활용하는 방법의 자세한 내용은 Artemis 또는 ActiveMQ Classic의 전용 문서를 참고할 수 있어요.
ActiveMQ Classic의 durable 메시징에 대한 자세한 내용은 여기에서 찾을 수 있어요.
메시지 헤더 매핑 (Message Header Mapping)
메시지 헤더를 쓸 때 JMS 스펙은 헤더 이름이 유효한 Java 식별자여야 한다고 정하고 있어요. 그러니 헤더 이름을 유효한 Java 식별자로 지어 주세요. 이렇게 하면 헤더를 JMS 셀렉터 안에서도 쓸 수 있다는 장점이 있어요(JMS 셀렉터의 SQL92 문법이 헤더에 Java 식별자 문법을 요구하거든요).
Camel이 헤더 이름을 받아들이는 현재 헤더 이름 전략은 이렇습니다:
-
점은
_DOT_로 치환되고, Camel이 메시지를 소비할 때 되돌려져요. -
하이픈은
_HYPHEN_으로 치환되고, Camel이 메시지를 소비할 때 되돌려져요.
Camel에는 HeaderFilterStrategy 구현이 두 개 들어 있어요:
-
org.apache.camel.component.jms.ClassicJmsHeaderFilterStrategy- Camel 4.8까지 쓰이던 클래식 전략이에요. -
org.apache.camel.component.jms.JmsHeaderFilterStrategy- Camel 4.9부터의 새 기본 전략이에요.
ClassicJmsHeaderFilterStrategy
헤더 이름을 매핑하는 클래식 전략은 Camel 4.8 이하에서 쓰였어요.
이 전략은 CamelFileName, CamelBeanMethodName 같은 Camel 내부 헤더도 포함해요. 덕분에 JMS 너머로 다른 Camel 인스턴스에 Camel 메시지를 보내면서 이 정보를 보존할 수 있죠. 하지만 그만큼 JMS 메시지에 Camel… 키를 가진 프로퍼티가 실린다는 뜻이기도 해요. 이건 항상 바람직하지는 않아서, Camel 4.9부터 기본값을 바꿨습니다.
참고 Camel 4.8 이하에서 모든
Camel…헤더를 제거하려면 커스텀HeaderFilterStrategy를 설정하면 돼요.
JmsHeaderFilterStrategy
Camel 4.9부터의 새 기본 전략은 다른 컴포넌트와 비슷하게 동작해요. Camel… 헤더는 제거되고, 최종 사용자가 명시적으로 지정한 헤더만 허용됩니다.
경고 Spring JMS로의 매핑
이 프로퍼티들 상당수는 Camel이 메시지를 주고받을 때 쓰는 Spring JMS의 프로퍼티에 대응해요. 자세한 내용은 관련 Spring 문서를 참고하세요.
옵션 설정 방법 (Configuring Options)
Camel 컴포넌트는 두 단계로 나눠 설정합니다:
-
컴포넌트(component) 레벨
-
엔드포인트(endpoint) 레벨
컴포넌트 옵션 설정 (Configuring Component Options)
컴포넌트 레벨에서는 엔드포인트들이 이어서 물려받는 일반적이고 공유되는 설정을 해요. 가장 상위의 설정 레벨이죠.
예를 들어 컴포넌트에는 보안 설정, 인증용 자격 증명, 네트워크 연결 URL 같은 것들이 있을 수 있어요.
컴포넌트에 따라 옵션이 몇 개뿐인 것도 있고 아주 많은 것도 있어요. 컴포넌트는 보통 널리 쓰이는 기본값이 미리 설정되어 있어서, 몇 가지 옵션만 설정하거나 아예 하나도 건드리지 않아도 되는 경우가 많아요.
컴포넌트는 다음 방법으로 설정할 수 있어요:
-
the Component DSL.
-
설정 파일(
application.properties,*.yaml파일 등). -
Java 코드에서 직접.
엔드포인트 옵션 설정 (Configuring Endpoint Options)
엔드포인트는 옵션이 많아서 설정에 시간을 더 쓰게 될 거예요. 이 옵션들이 엔드포인트가 원하는 일을 하도록 커스터마이즈해 줘요. 옵션은 엔드포인트가 consumer(from)로 쓰이는지, producer(to)로 쓰이는지, 둘 다인지에 따라 분류되기도 해요.
엔드포인트 설정은 대부분 엔드포인트 URI에 _path_와 query 파라미터로 바로 하는 경우가 많아요. Java에서 엔드포인트와 데이터 포맷을 _타입 세이프_하게 설정하려면 Endpoint DSL과 DataFormat DSL을 쓸 수도 있어요.
옵션을 설정할 때 좋은 습관은 Property Placeholders를 사용하는 거예요.
프로퍼티 플레이스홀더는 이런 장점이 있어요:
-
하드코딩된 URL, 포트 번호, 민감한 정보 같은 설정을 쓰지 않도록 도와줘요.
-
설정을 코드 밖으로 분리할 수 있어요.
-
코드가 더 유연해지고 재사용하기 좋아져요.
다음 두 섹션에서 모든 옵션을 나열해요. 먼저 컴포넌트, 그다음 엔드포인트 순서예요.
컴포넌트 옵션 (Component Options)
JMS 컴포넌트가 지원하는 옵션은 아래와 같아요.
| Name | Description | Default | Type |
|---|---|---|---|
| clientId (common) | 사용할 JMS 클라이언트 ID를 지정해요. 지정한다면 유일해야 하고 하나의 JMS 연결 인스턴스만 쓸 수 있어요. 보통 JMS 1.1의 durable 토픽 구독에서만 필요합니다. | String | |
| connectionFactory (common) | 사용할 커넥션 팩토리예요. 커넥션 팩토리는 컴포넌트나 엔드포인트 둘 중 하나에 반드시 설정해야 합니다. | ConnectionFactory | |
| disableReplyTo (common) | 메시지의 JMSReplyTo 헤더를 Camel이 무시할지 지정해요. true면 Camel은 JMSReplyTo 헤더가 가리키는 목적지로 응답을 보내지 않아요. 라우트에서 소비하면서 응답 메시지는 코드의 다른 컴포넌트가 처리하길 원할 때, 또는 서로 다른 브로커 사이에서 Camel을 프록시처럼 쓰면서 메시지를 한 시스템에서 다른 시스템으로 라우팅할 때 유용합니다. | false | boolean |
| durableSubscriptionName (common) | durable 토픽 구독을 지정할 때 쓰는 durable 구독자 이름이에요. clientId 옵션도 함께 설정해야 합니다. | String | |
| jmsMessageType (common) | |||
| Camel에서 브로커로 JMS 메시지를 보낼 때(요청/응답에서도) 특정 jakarta.jms.Message 구현을 강제로 쓰게 해요. Camel이 메시지를 받을 때는 보낸 클라이언트가 정한 타입에 고정되므로 이 옵션이 적용되지 않아요. 기본적으로 Camel은 메시지 본문 타입에서 JMS 메시지 타입을 정하는데, 이 옵션으로 직접 지정할 수 있죠. 가능한 값: Bytes, Map, Object, Stream, Text. |
Enum values:
-
Bytes
-
Map
-
Object
-
Stream
-
Text
| | JmsMessageType | | replyTo (common) | 명시적인 ReplyTo 목적지를 지정해요(consumer에서 들어오는 Message.getJMSReplyTo() 값을 덮어씀). | | String | | testConnectionOnStartup (common) | 시작할 때 연결을 테스트할지 지정해요. 이렇게 하면 Camel 시작 시 모든 JMS consumer가 브로커와 유효한 연결을 갖는 게 보장돼요. 연결을 얻지 못하면 시작 중 예외를 던져서, 실패한 연결로 Camel이 시작되는 걸 막아줍니다. JMS producer도 함께 테스트해요. | false | boolean | | acknowledgementModeName (consumer) |
JMS 승인(acknowledgement) 이름이에요. SESSION_TRANSACTED, CLIENT_ACKNOWLEDGE, AUTO_ACKNOWLEDGE, DUPS_OK_ACKNOWLEDGE 중 하나입니다.
Enum values:
-
SESSION_TRANSACTED
-
CLIENT_ACKNOWLEDGE
-
AUTO_ACKNOWLEDGE
-
DUPS_OK_ACKNOWLEDGE
| AUTO_ACKNOWLEDGE | String | | artemisConsumerPriority (consumer) | 컨슈머 우선순위를 쓰면 활성 상태인 우선순위 높은 컨슈머가 메시지를 받도록 보장할 수 있어요. 보통 큐에 연결된 활성 컨슈머들은 라운드 로빈 방식으로 메시지를 받습니다. 컨슈머 우선순위를 쓰면 같은 높은 우선순위의 활성 컨슈머가 여러 개일 때 라운드 로빈으로 전달되고, 높은 우선순위 컨슈머가 메시지를 받을 크레딧이 없거나 메시지 수락을 거절한 경우(예: 컨슈머의 셀렉터 조건에 맞지 않을 때)에만 낮은 우선순위 컨슈머에게 갑니다. | | int | | asyncConsumer (consumer) | JmsConsumer가 Exchange를 비동기로 처리할지 여부예요. 켜면 이전 메시지를 비동기로 처리하는 동안(비동기 라우팅 엔진이) JmsConsumer가 큐에서 다음 메시지를 미리 가져올 수 있어서, 메시지가 100% 엄격한 순서로 처리되지 않을 수 있어요. 끄면(기본) 이전 Exchange 처리가 끝나야 다음 메시지를 가져옵니다. transacted를 켰다면 트랜잭션은 동기로 실행해야 해서 asyncConsumer=true여도 비동기로 돌지 않아요. | false | boolean | | autoStartup (consumer) | consumer 컨테이너를 자동으로 시작할지 지정해요. | true | boolean | | cacheLevel (consumer) | 내부 JMS 리소스의 캐시 레벨을 ID로 지정해요. 자세한 건 cacheLevelName 옵션을 보세요. | | int | | cacheLevelName (consumer) |
내부 JMS 리소스의 캐시 레벨을 이름으로 지정해요. 기본 설정은 CACHE_AUTO예요. 자세한 내용은 Spring 문서와 트랜잭션 캐시 레벨(Transactions Cache Levels) 문서를 참고하세요. 가능한 값: CACHE_AUTO, CACHE_CONNECTION, CACHE_CONSUMER, CACHE_NONE, CACHE_SESSION.
Enum values:
-
CACHE_AUTO
-
CACHE_CONNECTION
-
CACHE_CONSUMER
-
CACHE_NONE
-
CACHE_SESSION
| CACHE_AUTO | String | | concurrentConsumers (consumer) | JMS에서 소비할 때의 기본 동시 consumer 수를 지정해요(JMS 위의 요청/응답 제외). 스레드를 동적으로 늘렸다 줄이는 건 maxMessagesPerTask 옵션으로 조절해요. JMS 위에서 요청/응답을 할 때는 replyToConcurrentConsumers 옵션으로 응답 메시지 리스너의 동시 consumer 수를 조절합니다. | 1 | int | | maxConcurrentConsumers (consumer) | JMS에서 소비할 때의 최대 동시 consumer 수를 지정해요(JMS 위의 요청/응답 제외). 스레드를 동적으로 늘렸다 줄이는 건 maxMessagesPerTask 옵션으로 조절해요. JMS 위에서 요청/응답을 할 때는 replyToMaxConcurrentConsumers 옵션으로 응답 메시지 리스너의 동시 consumer 수를 조절합니다. | | int | | replyToDeliveryPersistent (consumer) | 응답에 기본으로 영속(persistent) 전달을 쓸지 지정해요. | true | boolean | | selector (consumer) | 사용할 JMS 셀렉터를 지정해요. | | String | | subscriptionDurable (consumer) | 구독을 durable로 만들지 설정해요. 쓸 durable 구독 이름은 subscriptionName 프로퍼티로 지정할 수 있어요. 기본값은 false고, durable 구독을 등록하려면 true로 하되 보통 subscriptionName 값을 함께 지정하는 게 좋습니다(메시지 리스너 클래스 이름으로 충분하다면 없어도 돼요). 토픽(pub-sub 도메인)을 들을 때만 의미가 있어서 이 설정은 pubSubDomain 플래그도 함께 바꿔요. | false | boolean | | subscriptionName (consumer) | 만들 구독의 이름을 지정해요. 공유 또는 durable 구독을 쓰는 토픽(pub-sub 도메인)에 적용돼요. 구독 이름은 이 클라이언트의 JMS 클라이언트 ID 안에서 유일해야 하고, 기본값은 지정한 메시지 리스너의 클래스 이름입니다. 참고: 구독별로 동시 consumer는 1개만 허용되는데(JMS 2.0 공유 구독 제외), 이건 이 메시지 리스너 컨테이너의 기본값이에요. | | String | | subscriptionShared (consumer) | 구독을 공유(shared)로 만들지 설정해요. 쓸 공유 구독 이름은 subscriptionName 프로퍼티로 지정할 수 있어요. 기본값은 false고, 공유 구독을 등록하려면 true로 하되 보통 subscriptionName 값을 함께 지정하는 게 좋습니다(메시지 리스너 클래스 이름으로 충분하다면 없어도 돼요). 공유 구독은 durable일 수도 있어서 subscriptionDurable과 함께 쓰는 경우가 많아요. 토픽(pub-sub 도메인)을 들을 때만 의미가 있어서 이 설정은 pubSubDomain 플래그도 함께 바꿉니다. JMS 2.0 호환 브로커가 필요해요. | false | boolean | | acceptMessagesWhileStopping (consumer (advanced)) | consumer가 멈추는 동안에도 메시지를 받아들일지 지정해요. 런타임에 JMS 라우트를 시작하고 멈추는데 큐에 아직 메시지가 쌓여 있다면 이 옵션을 켜는 걸 고려해 보세요. false인 상태에서 JMS 라우트를 멈추면 메시지가 거부될 수 있고, 브로커는 재전송을 시도하다가 또 거부되고, 결국 브로커의 데드 레터 큐로 옮겨질 수도 있어요. 이걸 피하려면 이 옵션을 켜는 게 좋아요. | false | boolean | | allowReplyManagerQuickStop (consumer (advanced)) | 요청/응답 메시징의 reply manager가 쓰는 DefaultMessageListenerContainer가, JmsConfiguration#isAcceptMessagesWhileStopping이 켜져 있고 org.apache.camel.CamelContext가 멈추는 중일 때 runningAllowed 플래그로 퀵 스톱을 허용할지 여부예요. 이 퀵 스톱 기능은 일반 JMS consumer에는 기본으로 켜져 있고, reply manager에서 쓰려면 이 플래그를 켜야 합니다. | false | boolean | | consumerType (consumer (advanced)) |
쓸 consumer 타입이에요. 이 타입이 어떤 Spring JMS 리스너를 쓸지 결정해요. Default는 org.springframework.jms.listener.DefaultMessageListenerContainer를, Simple은 org.springframework.jms.listener.SimpleMessageListenerContainer를 씁니다. Custom을 지정하면 messageListenerContainerFactory 옵션으로 정의한 MessageListenerContainerFactory가 사용할 org.springframework.jms.listener.AbstractMessageListenerContainer를 결정해요. 값: Simple, Default, Custom.
Enum values:
-
Simple
-
Default
-
Custom
| Default | ConsumerType | | defaultTaskExecutorType (consumer (advanced)) |
DefaultMessageListenerContainer에서 쓸 기본 TaskExecutor 타입을 지정해요. consumer 엔드포인트와 producer 엔드포인트의 ReplyTo consumer 둘 다에 적용됩니다. SimpleAsync는 Spring의 SimpleAsyncTaskExecutor를, ThreadPool은 최적 값의 Spring ThreadPoolTaskExecutor(캐시드 스레드 풀 비슷)를 써요. 설정하지 않으면 이전 동작이 기본인데, consumer 엔드포인트는 캐시드 스레드 풀, 응답 consumer는 SimpleAsync를 씁니다. 동시 consumer 수가 늘었다 줄었다 하는 탄력적 구성에서 스레드 낭비를 줄이려면 ThreadPool을 권장해요. 값: ThreadPool, SimpleAsync.
Enum values:
-
ThreadPool
-
SimpleAsync
| | DefaultTaskExecutorType | | eagerLoadingOfProperties (consumer (advanced)) | 메시지가 로드되자마자 JMS 프로퍼티와 페이로드를 미리 로드해요. JMS 프로퍼티가 필요 없을 때는 비효율적이지만, 때로는 JMS 프로바이더나 JMS 프로퍼티 사용의 문제를 일찍 잡아낼 수 있습니다. eagerPoisonBody 옵션도 참고하세요. | false | boolean | | eagerPoisonBody (consumer (advanced)) | eagerLoadingOfProperties가 켜져 있는데 JMS 메시지 페이로드(JMS 본문이나 프로퍼티)가 poison(읽거나 매핑할 수 없음)이면, 메시지를 처리할 수 있도록 이 텍스트를 본문으로 설정해요(poison 원인은 이미 Exchange의 예외로 저장돼 있습니다). eagerPoisonBody=false로 설정하면 끌 수 있어요. eagerLoadingOfProperties 옵션도 참고하세요. | Poison JMS message due to ${exception.message} | String | | exposeListenerSession (consumer (advanced)) | 메시지를 소비할 때 리스너 세션을 노출할지 지정해요. | true | boolean | | replyToConsumerType (consumer (advanced)) |
응답 consumer(요청/응답 시)의 consumer 타입이에요. 이 타입이 어떤 Spring JMS 리스너를 쓸지 결정해요. Default는 org.springframework.jms.listener.DefaultMessageListenerContainer를, Simple은 org.springframework.jms.listener.SimpleMessageListenerContainer를 씁니다. Custom을 지정하면 messageListenerContainerFactory 옵션으로 정의한 MessageListenerContainerFactory가 사용할 org.springframework.jms.listener.AbstractMessageListenerContainer를 결정해요. 값: Simple, Default, Custom.
Enum values:
-
Simple
-
Default
-
Custom
| Default | ConsumerType | | replyToSameDestinationAllowed (consumer (advanced)) | JMS consumer가 자신이 소비하는 것과 같은 목적지로 응답 메시지를 보내도록 허용할지 여부예요. 이렇게 하면 같은 메시지를 소비하고 다시 보내는 무한 루프를 막을 수 있습니다. | false | boolean | | taskExecutor (consumer (advanced)) | 메시지 소비에 쓸 커스텀 task executor를 지정할 수 있어요. | | TaskExecutor | | deliveryDelay (producer) | JMS 전송 호출에 쓸 지연(delivery delay)을 설정해요. 이 옵션은 JMS 2.0 호환 브로커가 필요합니다. | -1 | long | | deliveryMode (producer) |
사용할 전달 모드를 지정해요. 가능한 값은 jakarta.jms.DeliveryMode에 정의된 값들이에요. NON_PERSISTENT = 1, PERSISTENT = 2.
Enum values:
-
1
-
2
| | Integer | | deliveryPersistent (producer) | 기본으로 영속(persistent) 전달을 쓸지 지정해요. | true | boolean | | explicitQosEnabled (producer) | 메시지를 보낼 때 deliveryMode, priority, timeToLive 같은 QoS 설정을 쓸지 지정해요. 이 옵션은 Spring JmsTemplate을 기반으로 하고, deliveryMode·priority·timeToLive 옵션은 현재 엔드포인트에 적용됩니다. 반면 preserveMessageQos 옵션은 메시지 단위로 동작하면서 QoS 프로퍼티를 Camel In 메시지 헤더에서만 읽어요. | false | Boolean | | formatDateHeadersToIso8601 (producer) | JMS 날짜 프로퍼티를 ISO 8601 표준 형식으로 표기할지 설정해요. | false | boolean | | lazyStartProducer (producer) | producer를 lazy(첫 메시지 시점)로 시작할지 여부예요. lazy 시작을 쓰면 producer가 시작 중 실패해서 라우트 시작이 실패하는 상황에서도 CamelContext와 라우트가 시작될 수 있고, 시작 실패 처리를 Camel의 라우팅 에러 핸들러로 넘길 수 있어요. 다만 첫 메시지 처리 시점에 producer를 만들고 시작하는 데 시간이 좀 걸려서 전체 처리 시간이 길어질 수 있는 점은 유의하세요. | false | boolean | | preserveMessageQos (producer) | 메시지에 지정된 QoS 설정으로 메시지를 보내고 싶다면 true로 하세요(JMS 엔드포인트의 QoS 설정 대신). 고려되는 세 헤더는 JMSPriority, JMSDeliveryMode, JMSExpiration이고, 전부 또는 일부만 줄 수 있어요. 주지 않으면 Camel은 엔드포인트 값을 대신 씁니다. 즉 이 옵션을 쓰면 헤더가 엔드포인트 값을 이기는 거예요. 반면 explicitQosEnabled 옵션은 엔드포인트에 설정된 값만 쓰고 메시지 헤더 값은 쓰지 않습니다. | false | boolean | | priority (producer) |
1보다 큰 값은 전송 시 메시지 우선순위를 지정해요(1이 가장 낮고 9가 가장 높음). 이 옵션이 효과를 내려면 explicitQosEnabled 옵션도 켜야 합니다. 값: 1, 2, 3, 4, 5, 6, 7, 8, 9.
Enum values:
-
1
-
2
-
3
-
4
-
5
-
6
-
7
-
8
-
9
| 4 | int | | replyToConcurrentConsumers (producer) | JMS 위에서 요청/응답을 할 때의 기본 동시 consumer 수를 지정해요. 스레드를 동적으로 늘렸다 줄이는 건 maxMessagesPerTask 옵션으로 조절합니다. | 1 | int | | replyToMaxConcurrentConsumers (producer) | JMS 위에서 요청/응답을 할 때의 최대 동시 consumer 수를 지정해요. 스레드를 동적으로 늘렸다 줄이는 건 maxMessagesPerTask 옵션으로 조절합니다. | | int | | replyToOnTimeoutMaxConcurrentConsumers (producer) | JMS 위에서 요청/응답을 할 때 타임아웃이 발생해도 라우팅을 계속하는 데 쓸 최대 동시 consumer 수를 지정해요. | 1 | int | | replyToOverride (producer) | JMS 메시지에 명시적인 ReplyTo 목적지를 넣어서 replyTo 설정을 덮어써요. 메시지를 원격 큐로 포워딩하면서 응답은 ReplyTo 목적지에서 받고 싶을 때 유용합니다. | | String | | replyToType (producer) |
JMS 위에서 요청/응답을 할 때 replyTo 큐에 어떤 전략을 쓸지 명시적으로 지정해요. 기본적으로 Camel은 임시 큐를 쓰고, replyTo가 설정돼 있으면 기본으로 Shared를 씁니다. 이 옵션으로 공유 큐 대신 전용(exclusive) 큐를 쓸 수 있어요. 자세한 건 Camel JMS 문서를 보되, 특히 클러스터 환경에서의 영향에 대한 설명과, Shared 응답 큐가 Temporary·Exclusive보다 성능이 낮다는 점을 참고하세요. 값: Temporary, Shared, Exclusive.
Enum values:
-
Temporary
-
Shared
-
Exclusive
| | ReplyToType | | requestTimeout (producer) | InOut 교환 패턴에서 응답을 기다리는 타임아웃(밀리초)이에요. 기본값은 20초입니다. CamelJmsRequestTimeout 헤더로 엔드포인트에 설정된 타임아웃 값을 메시지별로 따로 덮어쓸 수 있어요. requestTimeoutCheckerInterval 옵션도 참고하세요. | 20000 | long | | timeToLive (producer) | 메시지를 보낼 때 메시지의 time-to-live(밀리초)를 지정해요. | -1 | long | | allowAdditionalHeaders (producer (advanced)) | JMS 스펙상 유효하지 않은 값을 가질 수 있는 추가 헤더를 허용하는 옵션이에요. 예를 들어 WMQ 같은 일부 메시징 시스템은 JMS_IBM_MQMD_ 접두사 헤더에 바이트 배열 등 잘못된 타입의 값을 넣곤 해요. 헤더 이름을 쉼표로 여러 개 지정할 수 있고, 접미사로 와일드카드 매칭도 됩니다. | | String | | allowNullBody (producer (advanced)) | 본문 없는 메시지 전송을 허용할지 여부예요. 이 옵션이 false인데 메시지 본문이 null이면 JMSException이 던져집니다. | true | boolean | | alwaysCopyMessage (producer (advanced)) | true면 Camel이 producer로 전송할 때 항상 JMS 메시지 복사본을 만들어요. replyToDestinationSelectorName이 설정된 경우 같은 상황에서 복사가 필요합니다(참고로 replyToDestinationSelectorName이 설정되면 Camel은 alwaysCopyMessage를 true로 바꿔요). | false | boolean | | correlationProperty (producer (advanced)) | InOut 교환 패턴에서 메시지 상관관계를 맺을 때 JMSCorrelationID JMS 프로퍼티 대신 이 JMS 프로퍼티를 써요. 설정하면 메시지가 오직 이 프로퍼티 값으로만 상관관계가 맺어지고, JMSCorrelationID 프로퍼티는 무시되며 Camel이 설정하지도 않습니다. | | String | | disableTimeToLive (producer (advanced)) | time to live를 강제로 끄는 옵션이에요. 예를 들어 JMS 위에서 요청/응답을 하면 Camel은 기본적으로 requestTimeout 값을 보내는 메시지의 time to live로 씁니다. 문제는 보내는 시스템과 받는 시스템의 시계가 맞아야 하는데, 이게 항상 쉽지 않아요. 그럴 때 disableTimeToLive=true로 보내는 메시지에 time to live를 설정하지 않으면, 받는 시스템에서 메시지가 만료되지 않아요. 자세한 내용은 아래 'About time to live' 섹션을 보세요. | false | boolean | | forceSendOriginalMessage (producer (advanced)) | mapJmsMessage=false를 쓸 때 라우트 도중 헤더를 건드리면(get 또는 set) Camel은 새 JMS 메시지를 만들어 새 JMS 목적지로 보내요. 이 옵션을 true로 하면 Camel이 받은 원본 JMS 메시지를 보내도록 강제합니다. | false | boolean | | includeSentJMSMessageID (producer (advanced)) | InOnly(fire and forget)로 JMS 목적지에 보낼 때만 적용돼요. 켜면 메시지를 JMS 목적지로 보낼 때 JMS 클라이언트가 실제로 쓴 JMSMessageID로 Camel Exchange를 풍부하게 해줍니다. | false | boolean | | replyCorrelationProperty (producer (advanced)) | InOut 교환 패턴에서 응답 메시지 상관관계를 맺을 때 JMSCorrelationID JMS 프로퍼티 대신 이 JMS 프로퍼티를 써요. 'correlationProperty'와의 차이는, 'correlationProperty'는 상관관계 id 값을 담는 요청 프로퍼티를 지정할 뿐 응답 셀렉터(JMSCorrelationID=)에는 영향을 주지 않지만, 'replyCorrelationProperty'는 상관관계 id 값을 담는 응답 프로퍼티를 지정하며 응답 셀렉터(=)에 영향을 준다는 점이에요. | | String | | replyToCacheLevelName (producer (advanced)) |
JMS 위에서 요청/응답을 할 때 응답 consumer의 캐시 레벨을 이름으로 지정해요. 이 옵션은 고정 응답 큐(임시 큐 아님)를 쓸 때만 적용됩니다. Camel은 기본으로 exclusive 큐 또는 replyToSelectorName을 쓰는 shared 큐에는 CACHE_CONSUMER를, replyToSelectorName 없는 shared 큐에는 CACHE_SESSION을 써요. IBM WebSphere 같은 일부 JMS 브로커는 동작하려면 replyToCacheLevelName=CACHE_NONE을 설정해야 할 수 있습니다. 참고: 임시 큐를 쓸 때는 CACHE_NONE이 허용되지 않으니 CACHE_CONSUMER나 CACHE_SESSION 같은 더 높은 값을 써야 해요. 가능한 값: CACHE_AUTO, CACHE_CONNECTION, CACHE_CONSUMER, CACHE_NONE, CACHE_SESSION.
Enum values:
-
CACHE_AUTO
-
CACHE_CONNECTION
-
CACHE_CONSUMER
-
CACHE_NONE
-
CACHE_SESSION
| | String | | replyToDestinationSelectorName (producer (advanced)) | 고정 이름으로 JMS 셀렉터를 설정해요. 공유 큐를 쓸 때(임시 응답 큐를 안 쓸 때) 다른 사람의 응답 중 내 응답만 걸러 보려면 이걸 쓰면 됩니다. | | String | | streamMessageTypeEnabled (producer (advanced)) | StreamMessage 타입을 켤지 설정해요. 파일, InputStream 같은 스트리밍 페이로드는 BytesMessage 또는 StreamMessage로 보내는데, 이 옵션이 어느 쪽을 쓸지 정합니다. 기본인 BytesMessage는 페이로드 전체를 메모리로 읽어야 해요. 이 옵션을 켜면 페이로드를 청크 단위로 메모리에 읽고 각 청크를 더 이상 데이터가 없을 때까지 StreamMessage에 씁니다. | false | boolean | | allowAutoWiredConnectionFactory (advanced) | 커넥션 팩토리를 설정하지 않았을 때 레지스트리에서 ConnectionFactory를 자동으로 찾을지 여부예요. ConnectionFactory 인스턴스가 하나만 발견되면 그걸 씁니다. 기본으로 켜져 있어요. | true | boolean | | allowAutoWiredDestinationResolver (advanced) | destination resolver를 설정하지 않았을 때 레지스트리에서 DestinationResolver를 자동으로 찾을지 여부예요. DestinationResolver 인스턴스가 하나만 발견되면 그걸 씁니다. 기본으로 켜져 있어요. | true | boolean | | allowSerializedHeaders (advanced) | 직렬화된 헤더를 포함할지 제어해요. transferExchange가 true일 때만 적용되고, 객체가 직렬화 가능해야 합니다. Camel은 직렬화할 수 없는 객체는 빼고 WARN 레벨로 기록해요. | false | boolean | | artemisStreamingEnabled (advanced) | Apache Artemis 스트리밍 모드에 맞춰 최적화할지 여부예요. Artemis를 JMS StreamMessage 타입과 함께 쓸 때 메모리 오버헤드를 줄일 수 있습니다. Apache Artemis를 쓸 때만 켜야 해요. | false | boolean | | asyncStartListener (advanced) | 라우트 시작 시 JmsConsumer 메시지 리스너를 비동기로 시작할지 여부예요. 예를 들어 JmsConsumer가 원격 JMS 브로커에 연결하지 못하면 재시도나 페일오버하느라 블록될 수 있고, 그러면 Camel도 라우트 시작하느라 블록됩니다. 이 옵션을 true로 하면 JmsConsumer가 전용 스레드로 비동기 모드에서 브로커에 연결하는 동안 라우트는 시작될 수 있어요. 이 옵션을 쓸 때는 연결에 실패하면 WARN 레벨로 예외가 기록되고 consumer는 메시지를 받지 못한다는 점을 유의하세요. 이후 라우트를 재시작해서 재시도할 수 있습니다. | false | boolean | | asyncStopListener (advanced) | 라우트를 멈출 때 JmsConsumer 메시지 리스너를 비동기로 멈출지 여부예요. | false | boolean | | autowiredEnabled (advanced) | 오토와이어링 사용 여부예요. 레지스트리에서 일치하는 타입의 인스턴스가 하나만 있는지 찾아서 컴포넌트에 자동으로 설정하는(옵션이 autowired로 표시돼 있어야 함) 자동 오토와이어링에 쓰입니다. JDBC 데이터 소스, JMS 커넥션 팩토리, AWS 클라이언트 등을 자동 설정할 때 유용해요. | true | boolean | | browseLimit (advanced) | 브라우징용으로 메모리에 유지할 최대 메시지 수예요. 무제한은 0. | 100 | int | | configuration (advanced) | 공유 JMS 설정을 쓰려면 이걸 사용해요. | | JmsConfiguration | | destinationResolver (advanced) | org.springframework.jms.support.destination.DestinationResolver를 교체 가능하게 제공해요. 자신만의 리졸버를 쓸 수 있습니다(예: JNDI 레지스트리에서 실제 목적지를 조회). | | DestinationResolver | | errorHandler (advanced) | Message 처리 중 잡히지 않은 예외가 던져졌을 때 호출할 org.springframework.util.ErrorHandler를 지정해요. errorHandler를 설정하지 않으면 기본으로 WARN 레벨로 기록됩니다. errorHandlerLoggingLevel과 errorHandlerLogStackTrace 옵션으로 로그 레벨과 스택 트레이스 기록 여부를 설정할 수 있어서, 커스텀 errorHandler를 직접 만드는 것보다 훨씬 설정이 쉬워요. | | ErrorHandler | | exceptionListener (advanced) | 하부 JMS 예외 통지를 받을 JMS Exception Listener를 지정해요. | | ExceptionListener | | idleConsumerLimit (advanced) | 특정 시점에 유휴(idle) 상태로 있을 수 있는 consumer 수의 상한을 지정해요. | 1 | int | | idleReceivesPerTaskLimit (advanced) | 지정한 횟수의 유휴 수신(idle receive)에 도달하면 consumer를 유휴로 표시해요. 유휴 수신은 설정된 setReceiveTimeout이 지난 후 receiver가 null 메시지를 반환한 순간부터 세요. 이 값으로 유휴 태스크 수가 setIdleTaskExecutionLimit를 넘는지 확인해서 태스크를 재배치할지 판단하고, 그렇지 않으면 리소스를 아낄 수 있습니다. 이 설정은 setMaxMessagesPerTask와 달리, 이 한계에 도달하면 받은 메시지가 null이든 아니든 태스크를 반납하고 재배치해요. 태스크마다 충분히 큰 배치를 원하지만 처리할 메시지가 없어지는 즉시 빨리 반납되길 원한다면 이 설정 하나만으로는 유연하지 않을 수 있어요. setIdleTaskExecutionLimit와의 차이는, 이 설정은 유휴로 표시되는 반복이 몇 번이면 태스크를 반납할지를 결정한다는 점입니다. 예를 들어 setMaxMessagesPerTask='500', #setIdleReceivesPerTaskLimit='60', setReceiveTimeout='1000', setIdleTaskExecutionLimit='1'로 하면, 태스크마다 500개 메시지를 처리하다가 이후 60번의 유휴 수신이 이어지면 태스크가 유휴로 표시되고 반납돼요. 이는 마지막 메시지 처리 후 새 메시지가 안 오면 60초 후에 태스크가 반납된다는 뜻이기도 합니다. | | int | | idleTaskExecutionLimit (advanced) | 수신 태스크의 유휴 실행 횟수 상한을 지정해요. 실행 중 아무 메시지도 받지 못한 상태가 이 한계에 도달하면 태스크를 종료하고 수신을 다른 실행 중인 태스크에 맡깁니다(동적 스케줄링의 경우; maxConcurrentConsumers 설정 참고). Spring에서 제공하는 추가 문서도 있어요. | 1 | int | | includeAllJMSXProperties (advanced) | JMS에서 Camel Message로 매핑할 때 JMSX 접두사 프로퍼티를 모두 포함할지 여부예요. true로 하면 JMSXAppID, JMSXUserID 같은 프로퍼티가 포함됩니다. 참고: 커스텀 headerFilterStrategy를 쓰면 이 옵션은 적용되지 않아요. | false | boolean | | includeCorrelationIDAsBytes (advanced) | JMS consumer가 JMSCorrelationIDAsBytes를 Camel Message의 헤더로 포함할지 여부예요. | true | boolean | | jmsKeyFormatStrategy (advanced) |
JMS 스펙에 맞도록 JMS 키를 인코딩·디코딩하는 교체 가능한 전략이에요. Camel은 default와 passthrough 두 구현을 기본 제공합니다. default 전략은 점과 하이픈(.과 -)을 안전하게 변환하고, passthrough 전략은 키를 그대로 둬요. JMS 헤더 키에 잘못된 문자가 있어도 신경 쓰지 않는 브로커에 쓸 수 있습니다. org.apache.camel.component.jms.JmsKeyFormatStrategy를 직접 구현해서 # 표기법으로 참조할 수도 있어요.
Enum values:
-
default
-
passthrough
| | JmsKeyFormatStrategy | | mapJmsMessage (advanced) | 받은 JMS 메시지를 알맞은 페이로드 타입으로 자동 매핑할지 지정해요. 예: jakarta.jms.TextMessage를 String으로. | true | boolean | | maxMessagesPerTask (advanced) | 태스크당 메시지 수예요. -1은 무제한입니다. 동시 consumer에 범위(min max)를 쓰면 이 옵션을 예: 100 같은 값으로 설정해서, 할 일이 줄었을 때 consumer가 얼마나 빨리 줄어들지 조절할 수 있어요. | -1 | int | | messageConverter (advanced) | 커스텀 Spring org.springframework.jms.support.converter.MessageConverter를 써서 jakarta.jms.Message와의 매핑을 직접 제어할 수 있어요. | | MessageConverter | | messageCreatedStrategy (advanced) | Camel이 JMS 메시지를 보낼 때 jakarta.jms.Message 객체를 새로 만들 때마다 호출되는 MessageCreatedStrategy를 지정해요. | | MessageCreatedStrategy | | messageIdEnabled (advanced) | 전송 시 메시지 ID를 붙일지 지정해요. 이건 JMS 브로커에 주는 힌트일 뿐입니다. 프로바이더가 힌트를 받아들이면 메시지 ID는 null로 설정되어야 하고, 힌트를 무시하면 보통의 유일한 값으로 설정되어야 해요. | true | boolean | | messageListenerContainerFactory (advanced) | 어떤 org.springframework.jms.listener.AbstractMessageListenerContainer로 메시지를 소비할지 결정하는 MessageListenerContainerFactory의 레지스트리 ID예요. 이걸 설정하면 consumerType이 자동으로 Custom으로 바뀝니다. | | MessageListenerContainerFactory | | messageTimestampEnabled (advanced) | 전송 시 타임스탬프를 기본으로 켤지 지정해요. 이건 JMS 브로커에 주는 힌트일 뿐입니다. 프로바이더가 힌트를 받아들이면 타임스탬프는 0으로 설정되어야 하고, 힌트를 무시하면 보통의 값으로 설정되어야 해요. | true | boolean | | objectMessageEnabled (advanced) | JMS ObjectMessage 전송·수신을 켤지 여부예요. Java 객체 직렬화는 알려진 보안 취약점 원천이라 기본은 꺼져 있습니다. 메시지 출처를 신뢰하고 JMS로 Java 직렬화 객체를 주고받아야 할 때만 켜세요. 꺼져 있으면 Camel은 JMS ObjectMessage 인스턴스를 만들거나 읽는 걸 거부해요. transferExchange, transferException처럼 내부적으로 ObjectMessage에 의존하는 옵션은 이 옵션이 켜져 있어야 동작합니다. | false | boolean | | pubSubNoLocal (advanced) | 자기 연결이 발행한 메시지의 전달을 막을지 지정해요. | false | boolean | | queueBrowseStrategy (advanced) | 큐를 브라우징할 때 커스텀 QueueBrowseStrategy를 쓰려면 이걸 지정해요. | | QueueBrowseStrategy | | receiveTimeout (advanced) | 메시지 수신 타임아웃(밀리초)이에요. | 1000 | long | | recoveryInterval (advanced) | 복구 시도 사이의 간격, 즉 연결을 새로 고치는 간격(밀리초)이에요. 기본값은 5000ms, 즉 5초입니다. | 5000 | long | | requestTimeoutCheckerInterval (advanced) | JMS 위에서 요청/응답을 할 때 Camel이 타임아웃된 Exchange를 얼마나 자주 확인할지 설정해요. 기본은 초당 한 번이고, 타임아웃에 더 빨리 반응해야 하면 이 간격을 줄여서 더 자주 확인하면 돼요. 타임아웃 값은 requestTimeout 옵션으로 정해집니다. | 1000 | long | | serviceLocationEnabled (advanced) | 시작 시 JMS 브로커의 네트워크 주소 위치를 탐지할지 여부예요. 이 정보는 ConnectionFactory에 대한 리플렉션으로 모으며 벤더마다 다릅니다. 이 옵션으로 끌 수 있어요. | true | boolean | | synchronous (advanced) | 동기 처리를 엄격하게 쓸지 설정해요. | false | boolean | | temporaryQueueResolver (advanced) | 임시 큐 생성에 자신만의 리졸버를 쓸 수 있게 해주는 교체 가능한 TemporaryQueueResolver예요(일부 메시징 시스템은 임시 큐 생성에 특별한 요구사항이 있어요). | | TemporaryQueueResolver | | transferException (advanced) | 켜고 Request Reply(InOut) 메시징을 쓰는 중 consumer 쪽에서 Exchange가 실패하면, 발생한 예외가 jakarta.jms.ObjectMessage로 응답에 실려 돌아와요. 클라이언트가 Camel이면 돌아온 예외를 다시 던집니다. 이렇게 하면 Camel JMS를 라우팅의 브리지로 쓸 수 있어요(예: 영속 큐로 견고한 라우팅). transferExchange도 켜져 있으면 이 옵션이 우선합니다. 잡은 예외는 직렬화 가능해야 하고, consumer 쪽 원본 예외는 producer로 돌아올 때 org.apache.camel.RuntimeCamelException 같은 바깥 예외로 감싸일 수 있어요. 데이터가 Java 객체 직렬화를 쓰고 받는 쪽이 클래스 수준에서 역직렬화할 수 있어야 해서 producer와 consumer가 강하게 결합되므로, 신중하게 쓰세요! | false | boolean | | transferExchange (advanced) | 본문과 헤더만이 아니라 Exchange 전체를 와이어로 전송할 수 있어요. 전송되는 필드는: In body, Out body, Fault body, In headers, Out headers, Fault headers, exchange properties, exchange exception입니다. 객체가 직렬화 가능해야 하고, Camel은 직렬화할 수 없는 객체는 빼고 WARN 레벨로 기록해요. 이 옵션은 producer와 consumer 양쪽에서 켜야 합니다. 그래야 Camel이 페이로드가 일반 페이로드가 아니라 Exchange임을 알 수 있어요. 데이터가 Java 객체 직렬화를 쓰고 받는 쪽이 클래스 수준에서 역직렬화할 수 있어야 해서 producer와 consumer가 호환 Camel 버전을 써야 하므로, 신중하게 쓰세요! | false | boolean | | useMessageIDAsCorrelationID (advanced) | InOut 메시지에서 JMSMessageID를 항상 JMSCorrelationID로 쓸지 지정해요. | false | boolean | | waitForProvisionCorrelationToBeUpdatedCounter (advanced) | JMS 위에서 요청/응답을 하고 useMessageIDAsCorrelationID 옵션이 켜져 있을 때, 임시(provisional) 상관관계 id가 실제 상관관계 id로 갱신되기를 기다리는 횟수예요. | 50 | int | | waitForProvisionCorrelationToBeUpdatedThreadSleepingTime (advanced) | 임시 상관관계 id가 갱신되기를 기다리는 동안 매번 잠들 간격(밀리초)이에요. | 100 | long | | waitForTemporaryReplyToToBeUpdatedCounter (advanced) | JMS 위에서 요청/응답을 할 때 임시 replyTo 큐가 만들어져 준비되기를 기다리는 횟수예요. | 200 | int | | waitForTemporaryReplyToToBeUpdatedThreadSleepingTime (advanced) | 임시 replyTo 큐가 준비되기를 기다리는 동안 매번 잠들 간격(밀리초)이에요. | 100 | long | | headerFilterStrategy (filter) | Camel 메시지와 주고받는 헤더를 필터링할 커스텀 org.apache.camel.spi.HeaderFilterStrategy를 쓰려면 이걸 지정해요. | | HeaderFilterStrategy | | errorHandlerLoggingLevel (logging) |
잡히지 않은 예외를 기록하는 기본 errorHandler의 로그 레벨을 설정할 수 있어요. 값: TRACE, DEBUG, INFO, WARN, ERROR, OFF.
Enum values:
-
TRACE
-
DEBUG
-
INFO
-
WARN
-
ERROR
-
OFF
| WARN | LoggingLevel | | errorHandlerLogStackTrace (logging) | 기본 errorHandler가 스택 트레이스를 기록할지 제어할 수 있어요. | true | boolean | | deserializationFilter (security) | jakarta.jms.ObjectMessage.getObject()가 반환하는 본문 클래스에 심층 방어(defense-in-depth) 검사로 적용할 ObjectInputFilter 패턴(jdk.serialFilter 문법)을 설정해요. 이 패턴은 JMS 프로바이더가 페이로드를 역직렬화한 뒤에 평가되므로, 이 옵션만으로는 프로바이더의 ObjectInputStream 안에서 일어나는 가젯 체인 실행을 막을 수 없어요. 그런 공격을 막으려면 JMS 프로바이더 자체의 역직렬화 필터와(또는) JVM 전역 -Djdk.serialFilter도 함께 설정하세요. 이 옵션을 설정하지 않고 JVM 전역 필터도 없으면, java.net.은 거부하고 java., javax., org.apache.camel.은 허용하는 보수적인 기본 필터가 적용됩니다. | | String | | password (security) | ConnectionFactory에 쓸 비밀번호예요. username/password를 ConnectionFactory에 직접 설정할 수도 있습니다. | | String | | username (security) | ConnectionFactory에 쓸 사용자 이름이에요. username/password를 ConnectionFactory에 직접 설정할 수도 있습니다. | | String | | transacted (transaction) | 트랜잭션 모드를 쓸지 지정해요. | false | boolean | | transactedInOut (transaction) | InOut 동작(요청/응답)이 기본으로 트랜잭션 모드를 쓸지 지정해요. 이 플래그를 true로 하면 Spring JmsTemplate의 sessionTransacted가 true로 설정되고, InOut 동작에 쓰는 JmsTemplate의 acknowledgeMode도 transacted가 됩니다. Spring JMS 참고: JTA 트랜잭션 안에서는 createQueue, createTopic 메서드에 전달되는 파라미터가 반영되지 않아요. Java EE 트랜잭션 컨텍스트에 따라 컨테이너가 이 값들을 스스로 정합니다. 비슷하게 로컬 관리 트랜잭션 안에서도 이 파라미터는 반영되지 않는데, 이 경우 Spring JMS는 기존 JMS 세션으로 동작하기 때문이에요. 이 플래그를 true로 하면 관리 트랜잭션 밖에서는 짧은 로컬 JMS 트랜잭션을, 관리 트랜잭션(XA 트랜잭션 제외)이 있으면 동기화된 로컬 JMS 트랜잭션을 씁니다. 이러면 JMS 트랜잭션이 메인 트랜잭션(네이티브 JDBC 트랜잭션일 수 있음)과 함께 관리되고, JMS 트랜잭션은 메인 트랜잭션 직후에 커밋되는 효과가 있어요. | false | boolean | | lazyCreateTransactionManager (transaction (advanced)) | true면 transacted=true인데 주입된 transactionManager가 없을 때 Camel이 JmsTransactionManager를 만들어요. | true | boolean | | transactionManager (transaction (advanced)) | 사용할 Spring 트랜잭션 매니저예요. | | PlatformTransactionManager | | transactionName (transaction (advanced)) | 사용할 트랜잭션 이름이에요. | | String | | transactionTimeout (transaction (advanced)) | 트랜잭션 모드를 쓸 때의 트랜잭션 타임아웃 값(초)이에요. | -1 | int |
엔드포인트 옵션 (Endpoint Options)
JMS 엔드포인트는 다음 URI 문법으로 설정해요:
jms:destinationType:destinationName
다음 _path_와 query 파라미터를 씁니다:
경로 파라미터 (Path Parameters)
| Name | Description | Default | Type |
|---|---|---|---|
| destinationType (common) | |||
| 사용할 목적지 종류예요. 값: queue, topic, temp-queue, temp-topic. |
Enum values:
-
queue
-
topic
-
temp-queue
-
temp-topic
| queue | String | | destinationName (common) | 필수 목적지로 쓸 큐 또는 토픽 이름이에요. | | String |
쿼리 파라미터 (Query Parameters)
| Name | Description | Default | Type |
|---|---|---|---|
| clientId (common) | 사용할 JMS 클라이언트 ID를 지정해요. 지정한다면 유일해야 하고 하나의 JMS 연결 인스턴스만 쓸 수 있어요. 보통 JMS 1.1의 durable 토픽 구독에서만 필요합니다. | String | |
| connectionFactory (common) | 사용할 커넥션 팩토리예요. 커넥션 팩토리는 컴포넌트나 엔드포인트 둘 중 하나에 반드시 설정해야 합니다. | ConnectionFactory | |
| disableReplyTo (common) | 메시지의 JMSReplyTo 헤더를 Camel이 무시할지 지정해요. true면 Camel은 JMSReplyTo 헤더가 가리키는 목적지로 응답을 보내지 않아요. 라우트에서 소비하면서 응답 메시지는 코드의 다른 컴포넌트가 처리하길 원할 때, 또는 서로 다른 브로커 사이에서 Camel을 프록시처럼 쓰면서 메시지를 한 시스템에서 다른 시스템으로 라우팅할 때 유용합니다. | false | boolean |
| durableSubscriptionName (common) | durable 토픽 구독을 지정할 때 쓰는 durable 구독자 이름이에요. clientId 옵션도 함께 설정해야 합니다. | String | |
| jmsMessageType (common) | |||
| Camel에서 브로커로 JMS 메시지를 보낼 때(요청/응답에서도) 특정 jakarta.jms.Message 구현을 강제로 쓰게 해요. Camel이 메시지를 받을 때는 보낸 클라이언트가 정한 타입에 고정되므로 이 옵션이 적용되지 않아요. 기본적으로 Camel은 메시지 본문 타입에서 JMS 메시지 타입을 정하는데, 이 옵션으로 직접 지정할 수 있죠. 가능한 값: Bytes, Map, Object, Stream, Text. |
Enum values:
-
Bytes
-
Map
-
Object
-
Stream
-
Text
| | JmsMessageType | | replyTo (common) | 명시적인 ReplyTo 목적지를 지정해요(consumer에서 들어오는 Message.getJMSReplyTo() 값을 덮어씀). | | String | | testConnectionOnStartup (common) | 시작할 때 연결을 테스트할지 지정해요. 이렇게 하면 Camel 시작 시 모든 JMS consumer가 브로커와 유효한 연결을 갖는 게 보장돼요. 연결을 얻지 못하면 시작 중 예외를 던져서, 실패한 연결로 Camel이 시작되는 걸 막아줍니다. JMS producer도 함께 테스트해요. | false | boolean | | acknowledgementModeName (consumer) |
JMS 승인(acknowledgement) 이름이에요. SESSION_TRANSACTED, CLIENT_ACKNOWLEDGE, AUTO_ACKNOWLEDGE, DUPS_OK_ACKNOWLEDGE 중 하나입니다.
Enum values:
-
SESSION_TRANSACTED
-
CLIENT_ACKNOWLEDGE
-
AUTO_ACKNOWLEDGE
-
DUPS_OK_ACKNOWLEDGE
| AUTO_ACKNOWLEDGE | String | | artemisConsumerPriority (consumer) | 컨슈머 우선순위를 쓰면 활성 상태인 우선순위 높은 컨슈머가 메시지를 받도록 보장할 수 있어요. 보통 큐에 연결된 활성 컨슈머들은 라운드 로빈 방식으로 메시지를 받습니다. 컨슈머 우선순위를 쓰면 같은 높은 우선순위의 활성 컨슈머가 여러 개일 때 라운드 로빈으로 전달되고, 높은 우선순위 컨슈머가 메시지를 받을 크레딧이 없거나 메시지 수락을 거절한 경우(예: 컨슈머의 셀렉터 조건에 맞지 않을 때)에만 낮은 우선순위 컨슈머에게 갑니다. | | int | | asyncConsumer (consumer) | JmsConsumer가 Exchange를 비동기로 처리할지 여부예요. 켜면 이전 메시지를 비동기로 처리하는 동안(비동기 라우팅 엔진이) JmsConsumer가 큐에서 다음 메시지를 미리 가져올 수 있어서, 메시지가 100% 엄격한 순서로 처리되지 않을 수 있어요. 끄면(기본) 이전 Exchange 처리가 끝나야 다음 메시지를 가져옵니다. transacted를 켰다면 트랜잭션은 동기로 실행해야 해서 asyncConsumer=true여도 비동기로 돌지 않아요. | false | boolean | | autoStartup (consumer) | consumer 컨테이너를 자동으로 시작할지 지정해요. | true | boolean | | cacheLevel (consumer) | 내부 JMS 리소스의 캐시 레벨을 ID로 지정해요. 자세한 건 cacheLevelName 옵션을 보세요. | | int | | cacheLevelName (consumer) |
내부 JMS 리소스의 캐시 레벨을 이름으로 지정해요. 기본 설정은 CACHE_AUTO예요. 자세한 내용은 Spring 문서와 트랜잭션 캐시 레벨(Transactions Cache Levels) 문서를 참고하세요. 가능한 값: CACHE_AUTO, CACHE_CONNECTION, CACHE_CONSUMER, CACHE_NONE, CACHE_SESSION.
Enum values:
-
CACHE_AUTO
-
CACHE_CONNECTION
-
CACHE_CONSUMER
-
CACHE_NONE
-
CACHE_SESSION
| CACHE_AUTO | String | | concurrentConsumers (consumer) | JMS에서 소비할 때의 기본 동시 consumer 수를 지정해요(JMS 위의 요청/응답 제외). 스레드를 동적으로 늘렸다 줄이는 건 maxMessagesPerTask 옵션으로 조절해요. JMS 위에서 요청/응답을 할 때는 replyToConcurrentConsumers 옵션으로 응답 메시지 리스너의 동시 consumer 수를 조절합니다. | 1 | int | | maxConcurrentConsumers (consumer) | JMS에서 소비할 때의 최대 동시 consumer 수를 지정해요(JMS 위의 요청/응답 제외). 스레드를 동적으로 늘렸다 줄이는 건 maxMessagesPerTask 옵션으로 조절해요. JMS 위에서 요청/응답을 할 때는 replyToMaxConcurrentConsumers 옵션으로 응답 메시지 리스너의 동시 consumer 수를 조절합니다. | | int | | replyToDeliveryPersistent (consumer) | 응답에 기본으로 영속(persistent) 전달을 쓸지 지정해요. | true | boolean | | selector (consumer) | 사용할 JMS 셀렉터를 지정해요. | | String | | subscriptionDurable (consumer) | 구독을 durable로 만들지 설정해요. 쓸 durable 구독 이름은 subscriptionName 프로퍼티로 지정할 수 있어요. 기본값은 false고, durable 구독을 등록하려면 true로 하되 보통 subscriptionName 값을 함께 지정하는 게 좋습니다(메시지 리스너 클래스 이름으로 충분하다면 없어도 돼요). 토픽(pub-sub 도메인)을 들을 때만 의미가 있어서 이 설정은 pubSubDomain 플래그도 함께 바꿔요. | false | boolean | | subscriptionName (consumer) | 만들 구독의 이름을 지정해요. 공유 또는 durable 구독을 쓰는 토픽(pub-sub 도메인)에 적용돼요. 구독 이름은 이 클라이언트의 JMS 클라이언트 ID 안에서 유일해야 하고, 기본값은 지정한 메시지 리스너의 클래스 이름입니다. 참고: 구독별로 동시 consumer는 1개만 허용되는데(JMS 2.0 공유 구독 제외), 이건 이 메시지 리스너 컨테이너의 기본값이에요. | | String | | subscriptionShared (consumer) | 구독을 공유(shared)로 만들지 설정해요. 쓸 공유 구독 이름은 subscriptionName 프로퍼티로 지정할 수 있어요. 기본값은 false고, 공유 구독을 등록하려면 true로 하되 보통 subscriptionName 값을 함께 지정하는 게 좋습니다(메시지 리스너 클래스 이름으로 충분하다면 없어도 돼요). 공유 구독은 durable일 수도 있어서 subscriptionDurable과 함께 쓰는 경우가 많아요. 토픽(pub-sub 도메인)을 들을 때만 의미가 있어서 이 설정은 pubSubDomain 플래그도 함께 바꿉니다. JMS 2.0 호환 브로커가 필요해요. | false | boolean | | acceptMessagesWhileStopping (consumer (advanced)) | consumer가 멈추는 동안에도 메시지를 받아들일지 지정해요. 런타임에 JMS 라우트를 시작하고 멈추는데 큐에 아직 메시지가 쌓여 있다면 이 옵션을 켜는 걸 고려해 보세요. false인 상태에서 JMS 라우트를 멈추면 메시지가 거부될 수 있고, 브로커는 재전송을 시도하다가 또 거부되고, 결국 브로커의 데드 레터 큐로 옮겨질 수도 있어요. 이걸 피하려면 이 옵션을 켜는 게 좋아요. | false | boolean | | allowReplyManagerQuickStop (consumer (advanced)) | 요청/응답 메시징의 reply manager가 쓰는 DefaultMessageListenerContainer가, JmsConfiguration#isAcceptMessagesWhileStopping이 켜져 있고 org.apache.camel.CamelContext가 멈추는 중일 때 runningAllowed 플래그로 퀵 스톱을 허용할지 여부예요. 이 퀵 스톱 기능은 일반 JMS consumer에는 기본으로 켜져 있고, reply manager에서 쓰려면 이 플래그를 켜야 합니다. | false | boolean | | consumerType (consumer (advanced)) |
쓸 consumer 타입이에요. 이 타입이 어떤 Spring JMS 리스너를 쓸지 결정해요. Default는 org.springframework.jms.listener.DefaultMessageListenerContainer를, Simple은 org.springframework.jms.listener.SimpleMessageListenerContainer를 씁니다. Custom을 지정하면 messageListenerContainerFactory 옵션으로 정의한 MessageListenerContainerFactory가 사용할 org.springframework.jms.listener.AbstractMessageListenerContainer를 결정해요. 값: Simple, Default, Custom.
Enum values:
-
Simple
-
Default
-
Custom
| Default | ConsumerType | | defaultTaskExecutorType (consumer (advanced)) |
DefaultMessageListenerContainer에서 쓸 기본 TaskExecutor 타입을 지정해요. consumer 엔드포인트와 producer 엔드포인트의 ReplyTo consumer 둘 다에 적용됩니다. SimpleAsync는 Spring의 SimpleAsyncTaskExecutor를, ThreadPool은 최적 값의 Spring ThreadPoolTaskExecutor(캐시드 스레드 풀 비슷)를 써요. 설정하지 않으면 이전 동작이 기본인데, consumer 엔드포인트는 캐시드 스레드 풀, 응답 consumer는 SimpleAsync를 씁니다. 동시 consumer 수가 늘었다 줄었다 하는 탄력적 구성에서 스레드 낭비를 줄이려면 ThreadPool을 권장해요. 값: ThreadPool, SimpleAsync.
Enum values:
-
ThreadPool
-
SimpleAsync
| | DefaultTaskExecutorType | | eagerLoadingOfProperties (consumer (advanced)) | 메시지가 로드되자마자 JMS 프로퍼티와 페이로드를 미리 로드해요. JMS 프로퍼티가 필요 없을 때는 비효율적이지만, 때로는 JMS 프로바이더나 JMS 프로퍼티 사용의 문제를 일찍 잡아낼 수 있습니다. eagerPoisonBody 옵션도 참고하세요. | false | boolean | | eagerPoisonBody (consumer (advanced)) | eagerLoadingOfProperties가 켜져 있는데 JMS 메시지 페이로드(JMS 본문이나 프로퍼티)가 poison(읽거나 매핑할 수 없음)이면, 메시지를 처리할 수 있도록 이 텍스트를 본문으로 설정해요(poison 원인은 이미 Exchange의 예외로 저장돼 있습니다). eagerPoisonBody=false로 설정하면 끌 수 있어요. eagerLoadingOfProperties 옵션도 참고하세요. | Poison JMS message due to ${exception.message} | String | | exceptionHandler (consumer (advanced)) | consumer가 커스텀 ExceptionHandler를 쓰게 해요. bridgeErrorHandler 옵션이 켜져 있으면 이 옵션은 쓰이지 않습니다. 기본적으로 consumer는 예외를 직접 처리하는데, WARN이나 ERROR 레벨로 기록하고 무시해요. | | ExceptionHandler | | exchangePattern (consumer (advanced)) |
consumer가 exchange를 만들 때의 교환 패턴을 설정해요. 값: InOnly, InOut.
Enum values:
-
InOnly
-
InOut
| | ExchangePattern | | exposeListenerSession (consumer (advanced)) | 메시지를 소비할 때 리스너 세션을 노출할지 지정해요. | true | boolean | | replyToConsumerType (consumer (advanced)) |
응답 consumer(요청/응답 시)의 consumer 타입이에요. 이 타입이 어떤 Spring JMS 리스너를 쓸지 결정해요. Default는 org.springframework.jms.listener.DefaultMessageListenerContainer를, Simple은 org.springframework.jms.listener.SimpleMessageListenerContainer를 씁니다. Custom을 지정하면 messageListenerContainerFactory 옵션으로 정의한 MessageListenerContainerFactory가 사용할 org.springframework.jms.listener.AbstractMessageListenerContainer를 결정해요. 값: Simple, Default, Custom.
Enum values:
-
Simple
-
Default
-
Custom
| Default | ConsumerType | | replyToSameDestinationAllowed (consumer (advanced)) | JMS consumer가 자신이 소비하는 것과 같은 목적지로 응답 메시지를 보내도록 허용할지 여부예요. 이렇게 하면 같은 메시지를 소비하고 다시 보내는 무한 루프를 막을 수 있습니다. | false | boolean | | taskExecutor (consumer (advanced)) | 메시지 소비에 쓸 커스텀 task executor를 지정할 수 있어요. | | TaskExecutor | | deliveryDelay (producer) | JMS 전송 호출에 쓸 지연(delivery delay)을 설정해요. 이 옵션은 JMS 2.0 호환 브로커가 필요합니다. | -1 | long | | deliveryMode (producer) |
사용할 전달 모드를 지정해요. 가능한 값은 jakarta.jms.DeliveryMode에 정의된 값들이에요. NON_PERSISTENT = 1, PERSISTENT = 2.
Enum values:
-
1
-
2
| | Integer | | deliveryPersistent (producer) | 기본으로 영속(persistent) 전달을 쓸지 지정해요. | true | boolean | | explicitQosEnabled (producer) | 메시지를 보낼 때 deliveryMode, priority, timeToLive 같은 QoS 설정을 쓸지 지정해요. 이 옵션은 Spring JmsTemplate을 기반으로 하고, deliveryMode·priority·timeToLive 옵션은 현재 엔드포인트에 적용됩니다. 반면 preserveMessageQos 옵션은 메시지 단위로 동작하면서 QoS 프로퍼티를 Camel In 메시지 헤더에서만 읽어요. | false | Boolean | | formatDateHeadersToIso8601 (producer) | JMS 날짜 프로퍼티를 ISO 8601 표준 형식으로 표기할지 설정해요. | false | boolean | | preserveMessageQos (producer) | 메시지에 지정된 QoS 설정으로 메시지를 보내고 싶다면 true로 하세요(JMS 엔드포인트의 QoS 설정 대신). 고려되는 세 헤더는 JMSPriority, JMSDeliveryMode, JMSExpiration이고, 전부 또는 일부만 줄 수 있어요. 주지 않으면 Camel은 엔드포인트 값을 대신 씁니다. 즉 이 옵션을 쓰면 헤더가 엔드포인트 값을 이기는 거예요. 반면 explicitQosEnabled 옵션은 엔드포인트에 설정된 값만 쓰고 메시지 헤더 값은 쓰지 않습니다. | false | boolean | | priority (producer) |
1보다 큰 값은 전송 시 메시지 우선순위를 지정해요(1이 가장 낮고 9가 가장 높음). 이 옵션이 효과를 내려면 explicitQosEnabled 옵션도 켜야 합니다. 값: 1, 2, 3, 4, 5, 6, 7, 8, 9.
Enum values:
-
1
-
2
-
3
-
4
-
5
-
6
-
7
-
8
-
9
| 4 | int | | replyToConcurrentConsumers (producer) | JMS 위에서 요청/응답을 할 때의 기본 동시 consumer 수를 지정해요. 스레드를 동적으로 늘렸다 줄이는 건 maxMessagesPerTask 옵션으로 조절합니다. | 1 | int | | replyToMaxConcurrentConsumers (producer) | JMS 위에서 요청/응답을 할 때의 최대 동시 consumer 수를 지정해요. 스레드를 동적으로 늘렸다 줄이는 건 maxMessagesPerTask 옵션으로 조절합니다. | | int | | replyToOnTimeoutMaxConcurrentConsumers (producer) | JMS 위에서 요청/응답을 할 때 타임아웃이 발생해도 라우팅을 계속하는 데 쓸 최대 동시 consumer 수를 지정해요. | 1 | int | | replyToOverride (producer) | JMS 메시지에 명시적인 ReplyTo 목적지를 넣어서 replyTo 설정을 덮어써요. 메시지를 원격 큐로 포워딩하면서 응답은 ReplyTo 목적지에서 받고 싶을 때 유용합니다. | | String | | replyToType (producer) |
JMS 위에서 요청/응답을 할 때 replyTo 큐에 어떤 전략을 쓸지 명시적으로 지정해요. 기본적으로 Camel은 임시 큐를 쓰고, replyTo가 설정돼 있으면 기본으로 Shared를 씁니다. 이 옵션으로 공유 큐 대신 전용(exclusive) 큐를 쓸 수 있어요. 자세한 건 Camel JMS 문서를 보되, 특히 클러스터 환경에서의 영향에 대한 설명과, Shared 응답 큐가 Temporary·Exclusive보다 성능이 낮다는 점을 참고하세요. 값: Temporary, Shared, Exclusive.
Enum values:
-
Temporary
-
Shared
-
Exclusive
| | ReplyToType | | requestTimeout (producer) | InOut 교환 패턴에서 응답을 기다리는 타임아웃(밀리초)이에요. 기본값은 20초입니다. CamelJmsRequestTimeout 헤더로 엔드포인트에 설정된 타임아웃 값을 메시지별로 따로 덮어쓸 수 있어요. requestTimeoutCheckerInterval 옵션도 참고하세요. | 20000 | long | | timeToLive (producer) | 메시지를 보낼 때 메시지의 time-to-live(밀리초)를 지정해요. | -1 | long | | allowAdditionalHeaders (producer (advanced)) | JMS 스펙상 유효하지 않은 값을 가질 수 있는 추가 헤더를 허용하는 옵션이에요. 예를 들어 WMQ 같은 일부 메시징 시스템은 JMS_IBM_MQMD_ 접두사 헤더에 바이트 배열 등 잘못된 타입의 값을 넣곤 해요. 헤더 이름을 쉼표로 여러 개 지정할 수 있고, 접미사로 와일드카드 매칭도 됩니다. | | String | | allowNullBody (producer (advanced)) | 본문 없는 메시지 전송을 허용할지 여부예요. 이 옵션이 false인데 메시지 본문이 null이면 JMSException이 던져집니다. | true | boolean | | alwaysCopyMessage (producer (advanced)) | true면 Camel이 producer로 전송할 때 항상 JMS 메시지 복사본을 만들어요. replyToDestinationSelectorName이 설정된 경우 같은 상황에서 복사가 필요합니다(참고로 replyToDestinationSelectorName이 설정되면 Camel은 alwaysCopyMessage를 true로 바꿔요). | false | boolean | | correlationProperty (producer (advanced)) | InOut 교환 패턴에서 메시지 상관관계를 맺을 때 JMSCorrelationID JMS 프로퍼티 대신 이 JMS 프로퍼티를 써요. 설정하면 메시지가 오직 이 프로퍼티 값으로만 상관관계가 맺어지고, JMSCorrelationID 프로퍼티는 무시되며 Camel이 설정하지도 않습니다. | | String | | disableTimeToLive (producer (advanced)) | time to live를 강제로 끄는 옵션이에요. 예를 들어 JMS 위에서 요청/응답을 하면 Camel은 기본적으로 requestTimeout 값을 보내는 메시지의 time to live로 씁니다. 문제는 보내는 시스템과 받는 시스템의 시계가 맞아야 하는데, 이게 항상 쉽지 않아요. 그럴 때 disableTimeToLive=true로 보내는 메시지에 time to live를 설정하지 않으면, 받는 시스템에서 메시지가 만료되지 않아요. 자세한 내용은 아래 'About time to live' 섹션을 보세요. | false | boolean | | forceSendOriginalMessage (producer (advanced)) | mapJmsMessage=false를 쓸 때 라우트 도중 헤더를 건드리면(get 또는 set) Camel은 새 JMS 메시지를 만들어 새 JMS 목적지로 보내요. 이 옵션을 true로 하면 Camel이 받은 원본 JMS 메시지를 보내도록 강제합니다. | false | boolean | | includeSentJMSMessageID (producer (advanced)) | InOnly(fire and forget)로 JMS 목적지에 보낼 때만 적용돼요. 켜면 메시지를 JMS 목적지로 보낼 때 JMS 클라이언트가 실제로 쓴 JMSMessageID로 Camel Exchange를 풍부하게 해줍니다. | false | boolean | | lazyStartProducer (producer (advanced)) | producer를 lazy(첫 메시지 시점)로 시작할지 여부예요. lazy 시작을 쓰면 producer가 시작 중 실패해서 라우트 시작이 실패하는 상황에서도 CamelContext와 라우트가 시작될 수 있고, 시작 실패 처리를 Camel의 라우팅 에러 핸들러로 넘길 수 있어요. 다만 첫 메시지 처리 시점에 producer를 만들고 시작하는 데 시간이 좀 걸려서 전체 처리 시간이 길어질 수 있는 점은 유의하세요. | false | boolean | | replyCorrelationProperty (producer (advanced)) | InOut 교환 패턴에서 응답 메시지 상관관계를 맺을 때 JMSCorrelationID JMS 프로퍼티 대신 이 JMS 프로퍼티를 써요. 'correlationProperty'와의 차이는, 'correlationProperty'는 상관관계 id 값을 담는 요청 프로퍼티를 지정할 뿐 응답 셀렉터(JMSCorrelationID=)에는 영향을 주지 않지만, 'replyCorrelationProperty'는 상관관계 id 값을 담는 응답 프로퍼티를 지정하며 응답 셀렉터(=)에 영향을 준다는 점이에요. | | String | | replyToCacheLevelName (producer (advanced)) |
JMS 위에서 요청/응답을 할 때 응답 consumer의 캐시 레벨을 이름으로 지정해요. 이 옵션은 고정 응답 큐(임시 큐 아님)를 쓸 때만 적용됩니다. Camel은 기본으로 exclusive 큐 또는 replyToSelectorName을 쓰는 shared 큐에는 CACHE_CONSUMER를, replyToSelectorName 없는 shared 큐에는 CACHE_SESSION을 써요. IBM WebSphere 같은 일부 JMS 브로커는 동작하려면 replyToCacheLevelName=CACHE_NONE을 설정해야 할 수 있습니다. 참고: 임시 큐를 쓸 때는 CACHE_NONE이 허용되지 않으니 CACHE_CONSUMER나 CACHE_SESSION 같은 더 높은 값을 써야 해요. 가능한 값: CACHE_AUTO, CACHE_CONNECTION, CACHE_CONSUMER, CACHE_NONE, CACHE_SESSION.
Enum values:
-
CACHE_AUTO
-
CACHE_CONNECTION
-
CACHE_CONSUMER
-
CACHE_NONE
-
CACHE_SESSION
| | String | | replyToDestinationSelectorName (producer (advanced)) | 고정 이름으로 JMS 셀렉터를 설정해요. 공유 큐를 쓸 때(임시 응답 큐를 안 쓸 때) 다른 사람의 응답 중 내 응답만 걸러 보려면 이걸 쓰면 됩니다. | | String | | streamMessageTypeEnabled (producer (advanced)) | StreamMessage 타입을 켤지 설정해요. 파일, InputStream 같은 스트리밍 페이로드는 BytesMessage 또는 StreamMessage로 보내는데, 이 옵션이 어느 쪽을 쓸지 정합니다. 기본인 BytesMessage는 페이로드 전체를 메모리로 읽어야 해요. 이 옵션을 켜면 페이로드를 청크 단위로 메모리에 읽고 각 청크를 더 이상 데이터가 없을 때까지 StreamMessage에 씁니다. | false | boolean | | allowSerializedHeaders (advanced) | 직렬화된 헤더를 포함할지 제어해요. transferExchange가 true일 때만 적용되고, 객체가 직렬화 가능해야 합니다. Camel은 직렬화할 수 없는 객체는 빼고 WARN 레벨로 기록해요. | false | boolean | | artemisStreamingEnabled (advanced) | Apache Artemis 스트리밍 모드에 맞춰 최적화할지 여부예요. Artemis를 JMS StreamMessage 타입과 함께 쓸 때 메모리 오버헤드를 줄일 수 있습니다. Apache Artemis를 쓸 때만 켜야 해요. | false | boolean | | asyncStartListener (advanced) | 라우트 시작 시 JmsConsumer 메시지 리스너를 비동기로 시작할지 여부예요. 예를 들어 JmsConsumer가 원격 JMS 브로커에 연결하지 못하면 재시도나 페일오버하느라 블록될 수 있고, 그러면 Camel도 라우트 시작하느라 블록됩니다. 이 옵션을 true로 하면 JmsConsumer가 전용 스레드로 비동기 모드에서 브로커에 연결하는 동안 라우트는 시작될 수 있어요. 이 옵션을 쓸 때는 연결에 실패하면 WARN 레벨로 예외가 기록되고 consumer는 메시지를 받지 못한다는 점을 유의하세요. 이후 라우트를 재시작해서 재시도할 수 있습니다. | false | boolean | | asyncStopListener (advanced) | 라우트를 멈출 때 JmsConsumer 메시지 리스너를 비동기로 멈출지 여부예요. | false | boolean | | browseLimit (advanced) | 브라우징용으로 메모리에 유지할 최대 메시지 수예요. 무제한은 0. | 100 | int | | destinationResolver (advanced) | org.springframework.jms.support.destination.DestinationResolver를 교체 가능하게 제공해요. 자신만의 리졸버를 쓸 수 있습니다(예: JNDI 레지스트리에서 실제 목적지를 조회). | | DestinationResolver | | errorHandler (advanced) | Message 처리 중 잡히지 않은 예외가 던져졌을 때 호출할 org.springframework.util.ErrorHandler를 지정해요. errorHandler를 설정하지 않으면 기본으로 WARN 레벨로 기록됩니다. errorHandlerLoggingLevel과 errorHandlerLogStackTrace 옵션으로 로그 레벨과 스택 트레이스 기록 여부를 설정할 수 있어서, 커스텀 errorHandler를 직접 만드는 것보다 훨씬 설정이 쉬워요. | | ErrorHandler | | exceptionListener (advanced) | 하부 JMS 예외 통지를 받을 JMS Exception Listener를 지정해요. | | ExceptionListener | | headerFilterStrategy (advanced) | Camel 메시지와 주고받는 헤더를 필터링할 커스텀 HeaderFilterStrategy를 쓰려면 이걸 지정해요. | | HeaderFilterStrategy | | idleConsumerLimit (advanced) | 특정 시점에 유휴(idle) 상태로 있을 수 있는 consumer 수의 상한을 지정해요. | 1 | int | | idleReceivesPerTaskLimit (advanced) | 지정한 횟수의 유휴 수신(idle receive)에 도달하면 consumer를 유휴로 표시해요. 유휴 수신은 설정된 setReceiveTimeout이 지난 후 receiver가 null 메시지를 반환한 순간부터 세요. 이 값으로 유휴 태스크 수가 setIdleTaskExecutionLimit를 넘는지 확인해서 태스크를 재배치할지 판단하고, 그렇지 않으면 리소스를 아낄 수 있습니다. 이 설정은 setMaxMessagesPerTask와 달리, 이 한계에 도달하면 받은 메시지가 null이든 아니든 태스크를 반납하고 재배치해요. 태스크마다 충분히 큰 배치를 원하지만 처리할 메시지가 없어지는 즉시 빨리 반납되길 원한다면 이 설정 하나만으로는 유연하지 않을 수 있어요. setIdleTaskExecutionLimit와의 차이는, 이 설정은 유휴로 표시되는 반복이 몇 번이면 태스크를 반납할지를 결정한다는 점입니다. 예를 들어 setMaxMessagesPerTask='500', #setIdleReceivesPerTaskLimit='60', setReceiveTimeout='1000', setIdleTaskExecutionLimit='1'로 하면, 태스크마다 500개 메시지를 처리하다가 이후 60번의 유휴 수신이 이어지면 태스크가 유휴로 표시되고 반납돼요. 이는 마지막 메시지 처리 후 새 메시지가 안 오면 60초 후에 태스크가 반납된다는 뜻이기도 합니다. | | int | | idleTaskExecutionLimit (advanced) | 수신 태스크의 유휴 실행 횟수 상한을 지정해요. 실행 중 아무 메시지도 받지 못한 상태가 이 한계에 도달하면 태스크를 종료하고 수신을 다른 실행 중인 태스크에 맡깁니다(동적 스케줄링의 경우; maxConcurrentConsumers 설정 참고). Spring에서 제공하는 추가 문서도 있어요. | 1 | int | | includeAllJMSXProperties (advanced) | JMS에서 Camel Message로 매핑할 때 JMSX 접두사 프로퍼티를 모두 포함할지 여부예요. true로 하면 JMSXAppID, JMSXUserID 같은 프로퍼티가 포함됩니다. 참고: 커스텀 headerFilterStrategy를 쓰면 이 옵션은 적용되지 않아요. | false | boolean | | jmsKeyFormatStrategy (advanced) |
JMS 스펙에 맞도록 JMS 키를 인코딩·디코딩하는 교체 가능한 전략이에요. Camel은 default와 passthrough 두 구현을 기본 제공합니다. default 전략은 점과 하이픈(.과 -)을 안전하게 변환하고, passthrough 전략은 키를 그대로 둬요. JMS 헤더 키에 잘못된 문자가 있어도 신경 쓰지 않는 브로커에 쓸 수 있습니다. org.apache.camel.component.jms.JmsKeyFormatStrategy를 직접 구현해서 # 표기법으로 참조할 수도 있어요.
Enum values:
-
default
-
passthrough
| | JmsKeyFormatStrategy | | mapJmsMessage (advanced) | 받은 JMS 메시지를 알맞은 페이로드 타입으로 자동 매핑할지 지정해요. 예: jakarta.jms.TextMessage를 String으로. | true | boolean | | maxMessagesPerTask (advanced) | 태스크당 메시지 수예요. -1은 무제한입니다. 동시 consumer에 범위(min max)를 쓰면 이 옵션을 예: 100 같은 값으로 설정해서, 할 일이 줄었을 때 consumer가 얼마나 빨리 줄어들지 조절할 수 있어요. | -1 | int | | messageConverter (advanced) | 커스텀 Spring org.springframework.jms.support.converter.MessageConverter를 써서 jakarta.jms.Message와의 매핑을 직접 제어할 수 있어요. | | MessageConverter | | messageCreatedStrategy (advanced) | Camel이 JMS 메시지를 보낼 때 jakarta.jms.Message 객체를 새로 만들 때마다 호출되는 MessageCreatedStrategy를 지정해요. | | MessageCreatedStrategy | | messageIdEnabled (advanced) | 전송 시 메시지 ID를 붙일지 지정해요. 이건 JMS 브로커에 주는 힌트일 뿐입니다. 프로바이더가 힌트를 받아들이면 메시지 ID는 null로 설정되어야 하고, 힌트를 무시하면 보통의 유일한 값으로 설정되어야 해요. | true | boolean | | messageListenerContainerFactory (advanced) | 어떤 org.springframework.jms.listener.AbstractMessageListenerContainer로 메시지를 소비할지 결정하는 MessageListenerContainerFactory의 레지스트리 ID예요. 이걸 설정하면 consumerType이 자동으로 Custom으로 바뀝니다. | | MessageListenerContainerFactory | | messageTimestampEnabled (advanced) | 전송 시 타임스탬프를 기본으로 켤지 지정해요. 이건 JMS 브로커에 주는 힌트일 뿐입니다. 프로바이더가 힌트를 받아들이면 타임스탬프는 0으로 설정되어야 하고, 힌트를 무시하면 보통의 값으로 설정되어야 해요. | true | boolean | | objectMessageEnabled (advanced) | JMS ObjectMessage 전송·수신을 켤지 여부예요. Java 객체 직렬화는 알려진 보안 취약점 원천이라 기본은 꺼져 있습니다. 메시지 출처를 신뢰하고 JMS로 Java 직렬화 객체를 주고받아야 할 때만 켜세요. 꺼져 있으면 Camel은 JMS ObjectMessage 인스턴스를 만들거나 읽는 걸 거부해요. transferExchange, transferException처럼 내부적으로 ObjectMessage에 의존하는 옵션은 이 옵션이 켜져 있어야 동작합니다. | false | boolean | | pubSubNoLocal (advanced) | 자기 연결이 발행한 메시지의 전달을 막을지 지정해요. | false | boolean | | receiveTimeout (advanced) | 메시지 수신 타임아웃(밀리초)이에요. | 1000 | long | | recoveryInterval (advanced) | 복구 시도 사이의 간격, 즉 연결을 새로 고치는 간격(밀리초)이에요. 기본값은 5000ms, 즉 5초입니다. | 5000 | long | | requestTimeoutCheckerInterval (advanced) | JMS 위에서 요청/응답을 할 때 Camel이 타임아웃된 Exchange를 얼마나 자주 확인할지 설정해요. 기본은 초당 한 번이고, 타임아웃에 더 빨리 반응해야 하면 이 간격을 줄여서 더 자주 확인하면 돼요. 타임아웃 값은 requestTimeout 옵션으로 정해집니다. | 1000 | long | | synchronous (advanced) | 동기 처리를 엄격하게 쓸지 설정해요. | false | boolean | | temporaryQueueResolver (advanced) | 임시 큐 생성에 자신만의 리졸버를 쓸 수 있게 해주는 교체 가능한 TemporaryQueueResolver예요(일부 메시징 시스템은 임시 큐 생성에 특별한 요구사항이 있어요). | | TemporaryQueueResolver | | transferException (advanced) | 켜고 Request Reply(InOut) 메시징을 쓰는 중 consumer 쪽에서 Exchange가 실패하면, 발생한 예외가 jakarta.jms.ObjectMessage로 응답에 실려 돌아와요. 클라이언트가 Camel이면 돌아온 예외를 다시 던집니다. 이렇게 하면 Camel JMS를 라우팅의 브리지로 쓸 수 있어요(예: 영속 큐로 견고한 라우팅). transferExchange도 켜져 있으면 이 옵션이 우선합니다. 잡은 예외는 직렬화 가능해야 하고, consumer 쪽 원본 예외는 producer로 돌아올 때 org.apache.camel.RuntimeCamelException 같은 바깥 예외로 감싸일 수 있어요. 데이터가 Java 객체 직렬화를 쓰고 받는 쪽이 클래스 수준에서 역직렬화할 수 있어야 해서 producer와 consumer가 강하게 결합되므로, 신중하게 쓰세요! | false | boolean | | transferExchange (advanced) | 본문과 헤더만이 아니라 Exchange 전체를 와이어로 전송할 수 있어요. 전송되는 필드는: In body, Out body, Fault body, In headers, Out headers, Fault headers, exchange properties, exchange exception입니다. 객체가 직렬화 가능해야 하고, Camel은 직렬화할 수 없는 객체는 빼고 WARN 레벨로 기록해요. 이 옵션은 producer와 consumer 양쪽에서 켜야 합니다. 그래야 Camel이 페이로드가 일반 페이로드가 아니라 Exchange임을 알 수 있어요. 데이터가 Java 객체 직렬화를 쓰고 받는 쪽이 클래스 수준에서 역직렬화할 수 있어야 해서 producer와 consumer가 호환 Camel 버전을 써야 하므로, 신중하게 쓰세요! | false | boolean | | useMessageIDAsCorrelationID (advanced) | InOut 메시지에서 JMSMessageID를 항상 JMSCorrelationID로 쓸지 지정해요. | false | boolean | | waitForProvisionCorrelationToBeUpdatedCounter (advanced) | JMS 위에서 요청/응답을 하고 useMessageIDAsCorrelationID 옵션이 켜져 있을 때, 임시(provisional) 상관관계 id가 실제 상관관계 id로 갱신되기를 기다리는 횟수예요. | 50 | int | | waitForProvisionCorrelationToBeUpdatedThreadSleepingTime (advanced) | 임시 상관관계 id가 갱신되기를 기다리는 동안 매번 잠들 간격(밀리초)이에요. | 100 | long | | waitForTemporaryReplyToToBeUpdatedCounter (advanced) | JMS 위에서 요청/응답을 할 때 임시 replyTo 큐가 만들어져 준비되기를 기다리는 횟수예요. | 200 | int | | waitForTemporaryReplyToToBeUpdatedThreadSleepingTime (advanced) | 임시 replyTo 큐가 준비되기를 기다리는 동안 매번 잠들 간격(밀리초)이에요. | 100 | long | | errorHandlerLoggingLevel (logging) |
잡히지 않은 예외를 기록하는 기본 errorHandler의 로그 레벨을 설정할 수 있어요. 값: TRACE, DEBUG, INFO, WARN, ERROR, OFF.
Enum values:
-
TRACE
-
DEBUG
-
INFO
-
WARN
-
ERROR
-
OFF
| WARN | LoggingLevel | | errorHandlerLogStackTrace (logging) | 기본 errorHandler가 스택 트레이스를 기록할지 제어할 수 있어요. | true | boolean | | deserializationFilter (security) | jakarta.jms.ObjectMessage.getObject()가 반환하는 본문 클래스에 심층 방어(defense-in-depth) 검사로 적용할 ObjectInputFilter 패턴(jdk.serialFilter 문법)을 설정해요. 이 패턴은 JMS 프로바이더가 페이로드를 역직렬화한 뒤에 평가되므로, 이 옵션만으로는 프로바이더의 ObjectInputStream 안에서 일어나는 가젯 체인 실행을 막을 수 없어요. 그런 공격을 막으려면 JMS 프로바이더 자체의 역직렬화 필터와(또는) JVM 전역 -Djdk.serialFilter도 함께 설정하세요. 이 옵션을 설정하지 않고 JVM 전역 필터도 없으면, java.net.은 거부하고 java., javax., org.apache.camel.은 허용하는 보수적인 기본 필터가 적용됩니다. | | String | | password (security) | ConnectionFactory에 쓸 비밀번호예요. username/password를 ConnectionFactory에 직접 설정할 수도 있습니다. | | String | | username (security) | ConnectionFactory에 쓸 사용자 이름이에요. username/password를 ConnectionFactory에 직접 설정할 수도 있습니다. | | String | | transacted (transaction) | 트랜잭션 모드를 쓸지 지정해요. | false | boolean | | transactedInOut (transaction) | InOut 동작(요청/응답)이 기본으로 트랜잭션 모드를 쓸지 지정해요. 이 플래그를 true로 하면 Spring JmsTemplate의 sessionTransacted가 true로 설정되고, InOut 동작에 쓰는 JmsTemplate의 acknowledgeMode도 transacted가 됩니다. Spring JMS 참고: JTA 트랜잭션 안에서는 createQueue, createTopic 메서드에 전달되는 파라미터가 반영되지 않아요. Java EE 트랜잭션 컨텍스트에 따라 컨테이너가 이 값들을 스스로 정합니다. 비슷하게 로컬 관리 트랜잭션 안에서도 이 파라미터는 반영되지 않는데, 이 경우 Spring JMS는 기존 JMS 세션으로 동작하기 때문이에요. 이 플래그를 true로 하면 관리 트랜잭션 밖에서는 짧은 로컬 JMS 트랜잭션을, 관리 트랜잭션(XA 트랜잭션 제외)이 있으면 동기화된 로컬 JMS 트랜잭션을 씁니다. 이러면 JMS 트랜잭션이 메인 트랜잭션(네이티브 JDBC 트랜잭션일 수 있음)과 함께 관리되고, JMS 트랜잭션은 메인 트랜잭션 직후에 커밋되는 효과가 있어요. | false | boolean | | lazyCreateTransactionManager (transaction (advanced)) | true면 transacted=true인데 주입된 transactionManager가 없을 때 Camel이 JmsTransactionManager를 만들어요. | true | boolean | | transactionManager (transaction (advanced)) | 사용할 Spring 트랜잭션 매니저예요. | | PlatformTransactionManager | | transactionName (transaction (advanced)) | 사용할 트랜잭션 이름이에요. | | String | | transactionTimeout (transaction (advanced)) | 트랜잭션 모드를 쓸 때의 트랜잭션 타임아웃 값(초)이에요. | -1 | int |
메시지 헤더 (Message Headers)
JMS 컴포넌트가 지원하는 메시지 헤더는 아래와 같아요:
| Name | Description | Default | Type |
|---|---|---|---|
CamelJmsDestination (producer) Constant: JMS_DESTINATION |
목적지예요. | Destination | |
CamelJmsDestinationName (producer) Constant: JMS_DESTINATION_NAME |
목적지로 쓸 큐 또는 토픽의 이름이에요. | String | |
CamelJMSDestinationProduced (common) Constant: JMS_DESTINATION_NAME_PRODUCED |
메시지가 전송된 큐 또는 토픽의 이름이에요. | String | |
JMSXGroupID (common) Constant: JMS_X_GROUP_ID |
JMS 그룹 ID예요. | String | |
JMSMessageID (common) Constant: JMS_HEADER_MESSAGE_ID |
JMS 고유 메시지 ID예요. | String | |
JMSCorrelationID (common) Constant: JMS_HEADER_CORRELATION_ID |
JMS 상관관계 ID예요. | String | |
JMSCorrelationIDAsBytes (common) Constant: JMS_HEADER_CORRELATION_ID_AS_BYTES |
바이트 형태의 JMS 상관관계 ID예요. | byte[] | |
JMSDeliveryMode (common) Constant: JMS_HEADER_DELIVERY_MODE |
JMS 전달 모드예요. | int | |
JMSDestination (common) Constant: JMS_HEADER_DESTINATION |
JMS 목적지예요. | Destination | |
JMSExpiration (common) Constant: JMS_HEADER_EXPIRATION |
JMS 만료(expiration) 값이에요. | long | |
JMSPriority (common) Constant: JMS_HEADER_PRIORITY |
JMS 우선순위예요(0이 가장 낮고 9가 가장 높아요). | int | |
JMSRedelivered (common) Constant: JMS_HEADER_REDELIVERED |
JMS 메시지가 재전달됐는지 여부예요. | boolean | |
JMSTimestamp (common) Constant: JMS_HEADER_TIMESTAMP |
JMS 타임스탬프예요. | long | |
JMSReplyTo (common) Constant: JMS_HEADER_REPLY_TO |
JMS reply-to 목적지예요. | Destination | |
JMSType (common) Constant: JMS_HEADER_TYPE |
JMS 타입이에요. | String | |
JMSXUserID (common) Constant: JMS_HEADER_XUSER_ID |
XUser ID예요. | String | |
CamelJmsMessageType (common) Constant: JMS_MESSAGE_TYPE |
|||
| 메시지 타입이에요. |
Enum values:
-
Bytes
-
Map
-
Object
-
Stream
-
Text
-
Blob
| | JmsMessageType |
| CamelJmsRequestTimeout (producer) Constant: JMS_REQUEST_TIMEOUT | InOut 교환 패턴에서 응답을 기다리는 타임아웃(밀리초)이에요. | 20000 | long |
예제 (Examples)
JMS는 다른 컴포넌트의 예제에서도 많이 쓰여요. 여기서는 시작에 도움이 될 몇 가지 샘플만 소개할게요.
JMS에서 받기 (Receiving from JMS)
다음 샘플에서는 JMS 메시지를 받아서 POJO로 라우팅하는 라우트를 설정해요:
from("jms:queue:foo")
.to("bean:myBusinessLogic");
<route>
<from uri="jms:queue:foo"/>
<to uri="bean:myBusinessLogic"/>
</route>
- route:
from:
uri: jms:queue:foo
steps:
- to:
uri: bean:myBusinessLogic
어떤 EIP 패턴이든 쓸 수 있어서, 라우트를 컨텍스트 기반으로 만들 수도 있어요. 예를 들어 주문 토픽에서 큰 금액을 쓰는 고객(big spender)만 걸러내는 방법은 이렇습니다:
from("jms:topic:OrdersTopic")
.filter().method("myBean", "isGoldCustomer")
.to("jms:queue:BigSpendersQueue");
<route>
<from uri="jms:topic:OrdersTopic"/>
<filter>
<method ref="myBean" method="isGoldCustomer"/>
<to uri="jms:queue:BigSpendersQueue"/>
</filter>
</route>
- route:
from:
uri: jms:topic:OrdersTopic
steps:
- filter:
method:
ref: myBean
method: isGoldCustomer
steps:
- to:
uri: jms:queue:BigSpendersQueue
JMS로 보내기 (Sending to JMS)
아래 샘플에서는 파일 폴더를 폴링해서 파일 내용을 JMS 토픽으로 보내요. 파일 내용을 BytesMessage가 아니라 TextMessage로 담고 싶으니, 본문을 String으로 변환해야 해요:
from("file://orders")
.convertBodyTo(String.class)
.to("jms:topic:OrdersTopic");
<route>
<from uri="file://orders"/>
<convertBodyTo type="String"/>
<to uri="jms:topic:OrdersTopic"/>
</route>
- route:
from:
uri: file://orders
steps:
- convertBodyTo:
type: String
- to:
uri: jms:topic:OrdersTopic
애노테이션 사용하기 (Using Annotations)
Camel에는 애노테이션도 있어서, POJO Consuming과 POJO Producing을 쓸 수 있어요.
다른 예제들 (Other Examples)
JMS는 이 Camel 문서의 다른 컴포넌트와 EIP 패턴 예제에도 많이 등장해요. 문서를 자유롭게 둘러보세요.
Exchange를 저장하는 Dead Letter Queue로 JMS 사용하기 (Using JMS as a Dead Letter Queue storing Exchange)
보통 JMS를 전송 수단으로 쓰면 본문과 헤더만 페이로드로 전송돼요. JMS를 Dead Letter Channel과 함께 쓰면서 JMS 큐를 Dead Letter Queue로 쓴다면, 보통은 발생한 Exception이 JMS 메시지에 저장되지 않아요. 하지만 JMS 데드 레터 큐의 transferExchange 옵션을 쓰면, Exchange 전체를 org.apache.camel.support.DefaultExchangeHolder를 담은 jakarta.jms.ObjectMessage로 큐에 저장하라고 Camel에 지시할 수 있어요. 이렇게 하면 Dead Letter Queue에서 소비하면서 Exchange.EXCEPTION_CAUGHT 키의 Exchange 프로퍼티로 발생한 예외를 꺼낼 수 있습니다. 아래 데모가 이를 보여줘요:
Java 전용: errorHandler 설정은 Java DSL 전용이에요
// setup error handler to use JMS as queue and store the entire Exchange
errorHandler(deadLetterChannel("jms:queue:dead?transferExchange=true"));
그러면 JMS 큐에서 소비하면서 문제를 분석할 수 있어요:
from("jms:queue:dead").to("bean:myErrorAnalyzer");
<route>
<from uri="jms:queue:dead"/>
<to uri="bean:myErrorAnalyzer"/>
</route>
- route:
from:
uri: jms:queue:dead
steps:
- to:
uri: bean:myErrorAnalyzer
그리고 myErrorAnalyzer 빈에서는:
Java 전용: exchange 프로퍼티와 예외를 프로그래매틱하게 접근해요
String body = exchange.getIn().getBody();
Exception cause = exchange.getProperty(Exchange.EXCEPTION_CAUGHT, Exception.class);
// the cause message is
String problem = cause.getMessage();
에러만 저장하는 Dead Letter Channel로 JMS 사용하기 (Using JMS as a Dead Letter Channel storing error only)
JMS를 써서 원인 에러 메시지를 저장하거나, 직접 초기화하는 커스텀 본문을 저장할 수도 있어요. 다음 예제는 실패한 exchange를 JMS 데드 레터 큐로 옮기기 전에 Message Translator EIP로 변환을 적용해요:
Java 전용: errorHandler와 exceptionMessage()는 Java DSL 전용이에요
// we sent it to a seda dead queue first
errorHandler(deadLetterChannel("seda:dead"));
// and on the seda dead queue we can do the custom transformation before its sent to the JMS queue
from("seda:dead").transform(exceptionMessage()).to("jms:queue:dead");
여기서는 transform에 원본 원인 에러 메시지만 저장했어요. 하지만 어떤 Expression이든 쓸 수 있어요. 예를 들어 빈의 메서드를 호출하거나 커스텀 프로세서를 쓸 수 있죠.
사용법 (Usage)
JMS와 Camel 사이의 메시지 매핑 (Message Mapping between JMS and Camel)
Camel은 jakarta.jms.Message와 org.apache.camel.Message 사이에서 메시지를 자동으로 매핑해 줘요.
JMS 메시지를 보낼 때 Camel은 메시지 본문을 다음 JMS 메시지 타입으로 변환해요:
| 본문 타입 | JMS 메시지 | 설명 |
|---|---|---|
String |
jakarta.jms.TextMessage |
|
org.w3c.dom.Node |
jakarta.jms.TextMessage |
DOM이 String으로 변환돼요. |
Map |
jakarta.jms.MapMessage |
|
java.io.Serializable |
jakarta.jms.ObjectMessage |
|
byte[] |
jakarta.jms.BytesMessage |
|
java.io.File |
jakarta.jms.BytesMessage |
|
java.io.Reader |
jakarta.jms.BytesMessage |
|
java.io.InputStream |
jakarta.jms.BytesMessage |
|
org.apache.camel.WrappedFile |
jakarta.jms.BytesMessage |
|
org.apache.camel.StreamCache |
jakarta.jms.BytesMessage |
|
java.nio.ByteBuffer |
jakarta.jms.BytesMessage |
참고
streamMessageTypeEnabled옵션이 켜져 있으면java.io.File,java.io.Reader,java.io.InputStream,org.apache.camel.WrappedFile,org.apache.camel.StreamCache타입의 본문은jakarta.jms.BytesMessage대신jakarta.jms.StreamMessage로 보내져요.artemisStreamingEnabled가 적용 중이면 그때도 Camel은jakarta.jms.BytesMessage를 강제해요. ActiveMQ Artemis가 bytes 메시지를 요구하는 최적화된 스트리밍 모드를 갖고 있기 때문이에요.
JMS 메시지를 받을 때 Camel은 JMS 메시지를 다음 본문 타입으로 변환해요:
| JMS 메시지 | 본문 타입 |
|---|---|
jakarta.jms.TextMessage |
String |
jakarta.jms.BytesMessage |
byte[] |
jakarta.jms.MapMessage |
Map<String, Object> |
jakarta.jms.ObjectMessage |
Object |
jakarta.jms.StreamMessage |
java.io.InputStream |
JMS 메시지 자동 매핑 끄기 (Disabling auto-mapping of JMS messages)
위의 자동 매핑을 끄려면 mapJmsMessage 옵션을 쓰면 돼요. 꺼두면 Camel이 받은 JMS 메시지를 매핑하려 하지 않고, 그대로 페이로드로 써요. 이러면 매핑 오버헤드를 피하고 JMS 메시지를 그냥 통과시킬 수 있어요. 예컨대 classpath에 없는 클래스를 담은 jakarta.jms.ObjectMessage JMS 메시지도 라우팅할 수 있게 됩니다.
커스텀 MessageConverter 사용하기 (Using a custom MessageConverter)
messageConverter 옵션을 쓰면 Spring org.springframework.jms.support.converter.MessageConverter 클래스에서 매핑을 직접 할 수 있어요.
예를 들어 아래 라우트에서는 JMS 주문 큐로 메시지를 보낼 때 커스텀 메시지 컨버터를 써요:
from("file://inbox/order").to("jms:queue:order?messageConverter=#myMessageConverter");
<route>
<from uri="file://inbox/order"/>
<to uri="jms:queue:order?messageConverter=#myMessageConverter"/>
</route>
- route:
from:
uri: file://inbox/order
steps:
- to:
uri: jms:queue:order
parameters:
messageConverter: "#myMessageConverter"
JMS 목적지에서 소비할 때도 커스텀 메시지 컨버터를 쓸 수 있어요.
매핑 전략 선택 제어하기 (Controlling the mapping strategy selected)
엔드포인트 URL의 jmsMessageType 옵션을 쓰면 모든 메시지에 특정 메시지 타입을 강제할 수 있어요.
아래 라우트에서는 폴더에서 파일을 폴링해서 jakarta.jms.TextMessage로 보내요. JMS producer 엔드포인트가 텍스트 메시지를 쓰도록 강제했거든요:
from("file://inbox/order").to("jms:queue:order?jmsMessageType=Text");
<route>
<from uri="file://inbox/order"/>
<to uri="jms:queue:order?jmsMessageType=Text"/>
</route>
- route:
from:
uri: file://inbox/order
steps:
- to:
uri: jms:queue:order
parameters:
jmsMessageType: Text
메시지마다 CamelJmsMessageType 키의 헤더를 설정해서 메시지 타입을 지정할 수도 있어요. 예를 들면:
Java 전용: Java 코드가 필요한 JmsMessageType enum을 써요
from("file://inbox/order").setHeader("CamelJmsMessageType", JmsMessageType.Text).to("jms:queue:order");
가능한 값은 enum 클래스인 org.apache.camel.jms.JmsMessageType에 정의돼 있어요.
보낼 때의 메시지 포맷 (Message format when sending)
JMS 와이어로 보내지는 exchange는 JMS Message 스펙을 따라야 해요.
exchange.in.header의 헤더 키에는 다음 규칙이 적용돼요:
-
JMS나JMSX로 시작하는 키는 예약돼 있어요. -
exchange.in.headers키는 리터럴이어야 하고 모두 유효한 Java 식별자여야 해요(키 이름에 점을 쓰지 마세요). -
JMS 메시지를 소비할 때 Camel은 점과 하이픈을 치환하고 소비할 때 되돌려요:
.은_DOT_로 치환되고 Camel이 메시지를 소비할 때 되돌려져요.
-는_HYPHEN_으로 치환되고 Camel이 메시지를 소비할 때 되돌려져요..is replaced by_DOT_and the reverse replacement when Camel consumes the message.
-is replaced by_HYPHEN_and the reverse replacement when Camel consumes the message. -
키 포맷팅에 자신만의 커스텀 전략을 쓸 수 있게 해주는
jmsKeyFormatStrategy옵션도 참고하세요.
exchange.in.header의 헤더 값에는 다음 규칙이 적용돼요:
- 값은 primitive나 그 상대 객체(예:
Integer,Long,Character)여야 해요.String,CharSequence,Date,BigDecimal,BigInteger타입은 모두toString()표현으로 변환돼요. 다른 타입은 모두 버려져요.
Camel은 헤더 값을 버릴 때 org.apache.camel.component.jms.JmsBinding 카테고리로 DEBUG 레벨 로그를 남겨요. 예를 들면:
2008-07-09 06:43:04,046 [main ] DEBUG JmsBinding
- Ignoring non primitive header: order of class: org.apache.camel.component.jms.issues.DummyOrder with value: DummyOrder{orderId=333, itemId=4444, quantity=2}
받을 때의 메시지 포맷 (Message format when receiving)
Camel은 메시지를 받을 때 Exchange에 다음 프로퍼티를 추가해요:
| 프로퍼티 | 타입 | 설명 |
|---|---|---|
org.apache.camel.jms.replyDestination |
jakarta.jms.Destination |
응답 목적지예요. |
Camel은 JMS 메시지를 받을 때 In 메시지 헤더에 다음 JMS 프로퍼티를 추가해요:
| 헤더 | 타입 | 설명 |
|---|---|---|
JMSCorrelationID |
String |
JMS 상관관계 ID예요. |
JMSDeliveryMode |
int |
JMS 전달 모드예요. |
JMSDestination |
jakarta.jms.Destination |
JMS 목적지예요. |
JMSExpiration |
long |
JMS 만료(expiration) 값이에요. |
JMSMessageID |
String |
JMS 고유 메시지 ID예요. |
JMSPriority |
int |
JMS 우선순위예요(0이 가장 낮고 9가 가장 높아요). |
JMSRedelivered |
boolean |
JMS 메시지가 재전달됐는지 여부예요. |
JMSReplyTo |
jakarta.jms.Destination |
JMS reply-to 목적지예요. |
JMSTimestamp |
long |
JMS 타임스탬프예요. |
JMSType |
String |
JMS 타입이에요. |
JMSXGroupID |
String |
JMS 그룹 ID예요. |
위 정보는 모두 표준 JMS라서, 자세한 내용은 JMS 문서에서 확인할 수 있어요.
Camel로 메시지를 주고받을 때 JMSReplyTo에 대해 (About using Camel to send and receive messages and JMSReplyTo)
JMS 컴포넌트는 복잡해서, 어떤 경우에는 동작 방식을 세심하게 살펴야 해요. 그래서 주의 깊게 봐야 할 영역/함정을 몇 가지 짧게 정리해요.
Camel이 JMSProducer로 메시지를 보낼 때는 다음 조건들을 확인해요:
-
메시지 교환 패턴.
-
엔드포인트나 메시지 헤더에
JMSReplyTo가 설정됐는지 여부. -
JMS 엔드포인트에 다음 옵션들이 설정돼 있는지 여부:
disableReplyTo,preserveMessageQos,explicitQosEnabled.
이 모든 게 사용 사례를 지원하려면 이해하고 설정하기가 좀 복잡할 수 있어요.
JmsProducer
JmsProducer는 설정에 따라 이렇게 동작해요:
| 교환 패턴 | 다른 옵션 | 설명 |
|---|---|---|
| InOut | - | Camel이 응답을 기대하고 임시 JMSReplyTo를 설정한 뒤, 메시지를 보내고 나면 임시 큐에서 응답 메시지를 듣기 시작해요. |
| InOut | JMSReplyTo가 설정됨 |
Camel이 응답을 기대하고, 메시지를 보낸 후 지정된 JMSReplyTo 큐에서 응답 메시지를 듣기 시작해요. |
| InOnly | - | Camel이 메시지를 보내고 응답을 기대하지 않아요. |
| InOnly | JMSReplyTo가 설정됨 |
기본적으로 Camel은 JMSReplyTo 목적지를 버리고 메시지를 보내기 전에 JMSReplyTo 헤더를 지워요. 그러고 나서 메시지를 보내며 응답을 기대하지 않아요. Camel은 이를 DEBUG 레벨로 로그에 남겨요. JMSReplyTo를 유지하려면 preserveMessageQos=true를 쓰면 돼요. 어떤 경우든 JmsProducer는 응답을 기대하지 않아서 메시지를 보낸 뒤 계속 진행해요. |
JmsConsumer
JmsConsumer는 설정에 따라 이렇게 동작해요:
| 교환 패턴 | 다른 옵션 | 설명 |
|---|---|---|
| InOut | - | Camel이 JMSReplyTo 큐로 응답을 돌려보내요. |
| InOnly | - | 패턴이 _InOnly_라서 Camel이 응답을 돌려보내지 않아요. |
| - | disableReplyTo=true |
이 옵션은 응답을 억제해요. |
그러니 exchange에 설정된 메시지 교환 패턴에 주의하세요.
라우트 도중에 JMS 목적지로 메시지를 보낼 때는 쓸 교환 패턴을 지정할 수 있어요. 자세한 건 Request Reply를 보세요.
InOnly 메시지를 JMS 토픽으로 보내고 싶을 때 유용해요:
from("activemq:queue:in")
.to("bean:validateOrder")
.to(ExchangePattern.InOnly, "activemq:topic:order")
.to("bean:handleOrder");
<route>
<from uri="activemq:queue:in"/>
<to uri="bean:validateOrder"/>
<to uri="activemq:topic:order" pattern="InOnly"/>
<to uri="bean:handleOrder"/>
</route>
- route:
from:
uri: activemq:queue:in
steps:
- to:
uri: bean:validateOrder
- to:
uri: activemq:topic:order
pattern: InOnly
- to:
uri: bean:handleOrder
엔드포인트 재사용하고 런타임에 계산한 다른 목적지로 보내기 (Reuse endpoint and send to different destinations computed at runtime)
아주 많은 다른 JMS 목적지로 메시지를 보내야 한다면, JMS 엔드포인트를 재사용하면서 실제 목적지는 메시지 헤더에 지정하는 게 합리적이에요. Camel이 같은 엔드포인트를 재사용하면서 다른 목적지로 보낼 수 있거든요. 엔드포인트 생성 수를 크게 줄여서 메모리와 스레드 자원도 아껴요.
목적지는 다음 헤더로 지정할 수 있어요:
| 헤더 | 타입 | 설명 |
|---|---|---|
CamelJmsDestination |
jakarta.jms.Destination |
목적지 객체예요. |
CamelJmsDestinationName |
String |
목적지 이름이에요. |
예를 들어 다음 라우트는 런타임에 목적지를 계산해서 JMS URL에 나온 목적지를 덮어쓰는 방법을 보여줘요:
from("file://inbox")
.to("bean:computeDestination")
.to("activemq:queue:dummy");
<route>
<from uri="file://inbox"/>
<to uri="bean:computeDestination"/>
<to uri="activemq:queue:dummy"/>
</route>
- route:
from:
uri: file://inbox
steps:
- to:
uri: bean:computeDestination
- to:
uri: activemq:queue:dummy
큐 이름인 dummy는 그냥 자리표시자(placeholder)예요. JMS 엔드포인트 URL의 일부로 제공해야 하지만, 이 예제에서는 무시돼요.
computeDestination 빈에서는 CamelJmsDestinationName 헤더를 설정해서 실제 목적지를 지정해요:
Java 전용: 목적지 헤더를 프로그래매틱하게 설정하는 빈 메서드예요
public void setJmsHeader(Exchange exchange) {
String id = ....
exchange.getIn().setHeader("CamelJmsDestinationName", "order:" + id);
}
그러면 Camel이 이 헤더를 읽어서 엔드포인트에 설정된 것 대신 이걸 목적지로 써요. 즉 이 예제에서는 id 값이 2였다면 Camel이 activemq:queue:order:2로 메시지를 보내요.
CamelJmsDestination과 CamelJmsDestinationName 헤더가 둘 다 설정되면 CamelJmsDestination이 우선해요. JMS producer는 라우트에서 실수로 생기는 루프를 막으려고(메시지가 다른 JMS 엔드포인트로 포워딩되는 시나리오) 두 헤더를 exchange에서 제거하고, 만들어진 JMS 메시지로 전파하지 않는다는 점도 기억하세요.
여러 JMS 프로바이더 설정하기 (Configuring different JMS providers)
JMS 프로바이더는 Spring XML에서 이렇게 설정할 수 있어요:
JMS 컴포넌트 인스턴스는 원하는 만큼 설정할 수 있고, id 속성으로 유일한 이름을 붙이면 돼요. 앞선 예제는 activemq 컴포넌트를 설정하는 거예요. 같은 방식으로 MQSeries, TibCo, BEA, Sonic 등을 설정할 수도 있어요.
이름 붙은 JMS 컴포넌트가 생기면, URI로 그 컴포넌트 안의 엔드포인트를 참조할 수 있어요. 예를 들어 컴포넌트 이름이 activemq라면 activemq:[queue:|topic:]destinationName 형식의 URI로 목적지를 참조할 수 있죠. 이 방식은 다른 모든 JMS 프로바이더에도 똑같이 쓸 수 있어요.
이건 SpringCamelContext가 엔드포인트 URI의 스킴 이름에 해당하는 컴포넌트를 스프링 컨텍스트에서 느긋하게 가져오고(lazy fetch), 그 Component가 엔드포인트 URI를 해석하는 방식으로 동작해요.
JNDI로 ConnectionFactory 찾기 (Using JNDI to find the ConnectionFactory)
J2EE 컨테이너를 쓴다면 Spring의 보통 <bean> 메커니즘 대신 JNDI를 조회해서 JMS ConnectionFactory를 찾아야 할 수도 있어요. 이럴 때는 Spring의 factory bean이나 새 Spring XML 네임스페이스를 쓸 수 있어요. 예를 들면:
<bean id="weblogic" class="org.apache.camel.component.jms.JmsComponent">
<property name="connectionFactory" ref="myConnectionFactory"/>
</bean>
<jee:jndi-lookup id="myConnectionFactory" jndi-name="jms/connectionFactory"/>
JNDI 조회에 대한 자세한 내용은 Spring 레퍼런스 문서의 The jee schema를 보세요.
동시 소비 (Concurrent Consuming)
JMS에서 흔한 요구사항은 여러 스레드로 메시지를 동시에 소비해서 애플리케이션 반응성을 높이는 거예요. concurrentConsumers 옵션으로 JMS 엔드포인트를 서비스할 스레드 수를 지정할 수 있어요. 이렇게요:
from("jms:SomeQueue?concurrentConsumers=20")
.bean(MyClass.class);
<route>
<from uri="jms:SomeQueue?concurrentConsumers=20"/>
<to uri="bean:MyClass"/>
</route>
- route:
from:
uri: jms:SomeQueue
parameters:
concurrentConsumers: 20
steps:
- to:
uri: bean:MyClass
이 옵션은 다음 방법 중 하나로 설정할 수 있어요:
-
JmsComponent에 설정하거나, -
엔드포인트 URI에 설정하거나,
-
JmsEndpoint에서setConcurrentConsumers()를 직접 호출해요.
async consumer로 동시 소비하기 (Concurrent Consuming with async consumer)
각 동시 consumer는 현재 메시지가 완전히 처리됐을 때만 JMS 브로커에서 다음 메시지를 가져와요. asyncConsumer=true 옵션을 쓰면 이전 메시지가 비동기로 처리되는 동안(비동기 라우팅 엔진이) consumer가 JMS 큐에서 다음 메시지를 미리 가져올 수 있어요. 자세한 내용은 페이지 위쪽의 asyncConsumer 옵션 표를 보세요.
from("jms:SomeQueue?concurrentConsumers=20&asyncConsumer=true")
.bean(MyClass.class);
<route>
<from uri="jms:SomeQueue?concurrentConsumers=20&asyncConsumer=true"/>
<to uri="bean:MyClass"/>
</route>
- route:
from:
uri: jms:SomeQueue
parameters:
concurrentConsumers: 20
asyncConsumer: true
steps:
- to:
uri: bean:MyClass
JMS 위의 요청-응답 (Request-reply over JMS)
Camel은 JMS 위의 Request Reply를 지원해요. 요점은 JMS 큐로 메시지를 보낼 때 Exchange의 MEP가 InOut이어야 한다는 거예요.
Camel은 성능과 클러스터 환경에 영향을 주는 JMS 위의 요청/응답 설정 옵션을 여러 개 제공해요. 아래 표가 옵션을 요약해요.
| 옵션 | 성능 | 클러스터 | 설명 |
|---|---|---|---|
Temporary |
Fast | Yes | 임시 큐가 응답 큐로 쓰이고, Camel이 자동으로 만들어줘요. 이걸 쓰려면 replyTo 큐 이름을 지정하지 마세요. 그리고 임시 큐를 쓴다는 점을 분명히 하려고 replyToType=Temporary를 설정할 수도 있어요. |
Shared |
Slow | Yes | 공유 영속 큐가 응답 큐로 쓰여요. 큐는 미리 만들어야 하지만, Apache ActiveMQ처럼 즉석에서 만들어 주는 브로커도 있어요. 이걸 쓰려면 replyTo 큐 이름을 반드시 지정해야 해요. 그리고 공유 큐를 쓴다는 점을 분명히 하려고 replyToType=Shared를 설정할 수도 있어요. 공유 큐는 이 Camel 애플리케이션을 동시에 실행하는 여러 노드가 있는 클러스터 환경에서 쓸 수 있어요. 모두 같은 공유 응답 큐를 쓰는 거죠. 기대한 응답 메시지를 상관관계로 맺으려고 JMS Message 셀렉터를 쓰기 때문에 가능한데, 대신 성능에는 영향을 줘요. JMS Message 셀렉터는 느려서 Temporary나 Exclusive 큐만큼 빠르지 않아요. 성능을 더 끌어올리는 방법은 아래에서 계속 봐요. |
Exclusive |
Fast | No (*Yes) | 전용 영속 큐가 응답 큐로 쓰여요. 큐는 미리 만들어야 하지만, Apache ActiveMQ처럼 즉석에서 만들어 주는 브로커도 있어요. 이걸 쓰려면 replyTo 큐 이름을 반드시 지정해야 하고, replyTo 큐 이름이 설정돼 있으면 기본으로 Shared가 쓰이므로 전용 큐를 쓰라고 Camel에게 알리려면 반드시 replyToType=Exclusive를 설정해야 해요. 전용 응답 큐를 쓰면 JMS Message 셀렉터를 쓰지 않아서, 다른 애플리케이션이 이 큐를 함께 쓰면 안 돼요. 전용 큐는 이 Camel 애플리케이션을 동시에 실행하는 여러 노드가 있는 클러스터 환경에서 쓸 수 없어요. 요청 메시지를 보낸 같은 노드로 응답 큐가 돌아온다는 걸 보장할 수 없기 때문이에요. 그래서 공유 큐는 이걸 보장하려고 JMS Message 셀렉터를 쓰는 거예요. 다만 노드마다 유일한 이름으로 각 Exclusive 응답 큐를 설정하면 클러스터 환경에서 실행할 수 있어요. 그러면 응답 메시지가 응답을 기다리는 해당 노드의 큐로 돌아가거든요. |
concurrentConsumers |
Fast | Yes | 동시 메시지 리스너로 응답 메시지를 동시에 처리할 수 있어요. concurrentConsumers와 maxConcurrentConsumers 옵션으로 범위를 지정할 수 있어요. 주의: Shared 응답 큐는 동시 리스너와 잘 맞지 않을 수 있으니 이 옵션은 신중하게 쓰세요. |
maxConcurrentConsumers |
Fast | Yes | 동시 메시지 리스너로 응답 메시지를 동시에 처리할 수 있어요. concurrentConsumers와 maxConcurrentConsumers 옵션으로 범위를 지정할 수 있어요. 주의: Shared 응답 큐는 동시 리스너와 잘 맞지 않을 수 있으니 이 옵션은 신중하게 쓰세요. |
JmsProducer는 InOut을 감지하고, 쓸 응답 목적지가 담긴 JMSReplyTo 헤더를 제공해요. 기본적으로 Camel은 임시 큐를 쓰지만, 엔드포인트의 replyTo 옵션으로 고정 응답 큐를 지정할 수도 있어요(고정 응답 큐에 대한 내용은 아래에서 더 봐요).
Camel은 응답 큐에서 듣는 consumer를 자동으로 세팅해 주니까, 여러분은 아무것도 할 필요가 없어요.
이 consumer는 응답을 듣는 Spring DefaultMessageListenerContainer인데, 동시 consumer가 하나로 고정돼 있어요.
그래서 응답을 처리할 스레드가 하나뿐이라 응답이 순서대로 처리돼요. concurrentConsumers와 maxConcurrentConsumers 옵션으로 리스너가 동시 스레드를 쓰도록 설정할 수 있고, 그 덕분에 Camel에서 아래처럼 더 쉽게 설정할 수 있어요:
Java 전용: 요청-응답 패턴에 to(ExchangePattern.InOut, …)를 써요
from(xxx)
.to(ExchangePattern.InOut, "activemq:queue:foo?concurrentConsumers=5")
.to(yyy)
.to(zzz);
이 라우트에서는 Camel이 다섯 스레드짜리 스레드 풀로 응답을 비동기로 라우팅하도록 지시했어요.
JMS 위의 요청-응답과 공유 고정 응답 큐 사용 (Request-reply over JMS and using a shared fixed reply queue)
아래 예제처럼 JMS 위의 Request Reply에서 고정 응답 큐를 쓴다면 주의하세요.
Java 전용: 요청-응답 패턴에 to(ExchangePattern.InOut, …)를 써요
from(xxx)
.to(ExchangePattern.InOut, "activemq:queue:foo?replyTo=bar")
.to(yyy)
이 예제에서는 "bar"라는 이름의 고정 응답 큐를 써요. 고정 응답 큐를 쓸 때 Camel은 기본적으로 그 큐가 공유(shared)된다고 가정하고, 그래서 기대한 응답 메시지만 가져오려고(예: JMSCorrelationID 기준) JMSSelector를 써요. 전용 고정 응답 큐는 다음 섹션을 보세요. 덕분에 임시 큐만큼 빠르지는 않아요. receiveTimeout 옵션으로 Camel이 응답 메시지를 끌어오는 주기를 빠르게 할 수 있어요. 기본값은 1000밀리초라서, 아래처럼 250밀리초로 설정하면 초당 4번 끌어올 수 있어요:
Java 전용: 요청-응답 패턴에 to(ExchangePattern.InOut, …)를 써요
from(xxx)
.to(ExchangePattern.InOut, "activemq:queue:foo?replyTo=bar&receiveTimeout=250")
.to(yyy)
이러면 Camel이 메시지 브로커에 끌어오기 요청을 더 자주 보내게 돼서 네트워크 트래픽이 더 필요해요.
가능하면 임시 큐를 쓰는 게 일반적으로 권장돼요.
JMS 위의 요청-응답과 전용 고정 응답 큐 사용 (Request-reply over JMS and using an exclusive fixed reply queue)
앞선 예제에서 Camel은 "bar"라는 고정 응답 큐가 공유됐다고 가정해서, 기대한 응답 메시지만 소비하려고 JMSSelector를 썼어요. 하지만 JMS 셀렉터는 느려서 이 방식에는 단점이 있어요. 게다가 응답 큐의 consumer가 새 JMS 셀렉터 id로 갱신되는 것도 느려요. 실제로는 receiveTimeout 옵션이 타임아웃될 때만 갱신되는데, 기본값이 1초라 이론상 응답 메시지 감지에 최대 1초 정도까지 걸릴 수 있어요. 반면 고정 응답 큐가 Camel 응답 consumer에게 전용이라면 JMS 셀렉터를 쓰지 않아도 돼서 더 성능이 좋아요. 실제로 임시 큐를 쓰는 것만큼 빠르죠. ReplyToType 옵션을 Exclusive로 설정하면
응답 큐가 전용이라고 Camel에게 알릴 수 있어요. 아래 예제를 보세요:
Java 전용: 요청-응답 패턴에 to(ExchangePattern.InOut, …)를 써요
from(xxx)
.to(ExchangePattern.InOut, "activemq:queue:foo?replyTo=bar&replyToType=Exclusive")
.to(yyy)
큐는 엔드포인트마다 전용이어야 한다는 점을 유의하세요. 라우트가 두 개라면 각각 유일한 응답 큐가 필요해요. 다음 예제를 보세요:
Java 전용: 요청-응답 패턴에 to(ExchangePattern.InOut, …)를 써요
from(xxx)
.to(ExchangePattern.InOut, "activemq:queue:foo?replyTo=bar&replyToType=Exclusive")
.to(yyy)
from(aaa)
.to(ExchangePattern.InOut, "activemq:queue:order?replyTo=order.reply&replyToType=Exclusive")
.to(bbb)
클러스터 환경에서 실행할 때도 마찬가지예요. 클러스터의 각 노드는 유일한 응답 큐 이름을 써야 해요. 그러지 않으면 클러스터의 각 노드가 다른 노드의 응답으로 의도된 메시지를 집어 가져올 수 있거든요. 클러스터 환경에서는 대신 공유 응답 큐를 쓰는 게 권장돼요.
보내는 쪽과 받는 쪽의 시계 동기화 (Synchronizing clocks between senders and receivers)
시스템 사이에서 메시징을 할 때는 시스템들의 시계가 동기화돼 있는 게 바람직해요. 예를 들어 JMS 메시지를 보낼 때 메시지에 time to live 값을 설정할 수 있는데, 받는 쪽은 이 값을 보고 메시지가 이미 만료됐는지 판단해서, 소비하고 처리하는 대신 버릴 수 있죠. 하지만 이러려면 보내는 쪽과 받는 쪽 모두 시계가 동기화돼 있어야 해요.
참고 ActiveMQ를 쓴다면 timestamp plugin으로 시계를 동기화할 수 있어요.
time to live 정보 (About time to live)
먼저 위의 시계 동기화 내용을 읽어 주세요.
Camel로 JMS 위에서 요청/응답(InOut)을 하면 Camel은 보내는 쪽에서 타임아웃을 쓰는데, 기본값은 requestTimeout 옵션의 20초예요. 더 크거나 작은 값을 설정해서 조절할 수 있어요. 하지만 보내지는 JMS 메시지에는 여전히 time to live 값이 설정돼요. 그래서 시스템들 사이에 시계가 동기화돼 있어야 하죠. 그렇지 않다면 time to live 값 설정을 끄고 싶을 수 있는데, Camel 2.8부터 disableTimeToLive 옵션으로 가능해졌어요. 이 옵션을 disableTimeToLive=true로 설정하면 JMS 메시지를 보낼 때 time to live 값을 설정하지 않아요. 하지만 요청 타임아웃은 여전히 동작합니다. 그러니 예를 들어 JMS 위에서 요청/응답을 하면서 time to live를 껐다면, Camel은 여전히 20초 타임아웃(requestTimeout 옵션)을 써요. 이 옵션도 설정할 수 있고요. 이렇게 requestTimeout과 disableTimeToLive 두 옵션으로 요청/응답을 세밀하게 제어할 수 있어요.
엔드포인트에 설정된 값 대신 쓸 요청 타임아웃 값을 메시지의 헤더로 제공할 수도 있어요. 예를 들면:
from("direct:someWhere")
.to("jms:queue:foo?replyTo=bar&requestTimeout=30s")
.to("bean:processReply");
<route>
<from uri="direct:someWhere"/>
<to uri="jms:queue:foo?replyTo=bar&requestTimeout=30s"/>
<to uri="bean:processReply"/>
</route>
- route:
from:
uri: direct:someWhere
steps:
- to:
uri: jms:queue:foo
parameters:
replyTo: bar
requestTimeout: 30s
- to:
uri: bean:processReply
위 라우트에는 requestTimeout이 30초로 설정된 엔드포인트가 있어요. 그래서 Camel은 bar 큐로 응답 메시지가 돌아오기를 최대 30초까지 기다려요. 응답 메시지를 받지 못하면 org.apache.camel.ExchangeTimedOutException이 Exchange에 설정되고, Camel은 메시지 라우팅을 계속하지만 예외 때문에 실패하고, Camel의 에러 핸들러가 반응해요.
메시지별 타임아웃 값을 쓰고 싶다면 CamelJmsRequestTimeout 헤더에 long 타입의 타임아웃 값을 설정하면 돼요.
팁 헤더 이름은
CamelJmsRequestTimeout이에요.
예를 들어 아래처럼 service 빈의 "whatIsTheTimeout" 메서드를 호출하는 빈으로 메시지마다 타임아웃 값을 계산할 수 있어요:
from("direct:someWhere")
.setHeader("CamelJmsRequestTimeout", method(ServiceBean.class, "whatIsTheTimeout"))
.to("jms:queue:foo?replyTo=bar&requestTimeout=30s")
.to("bean:processReply");
<route>
<from uri="direct:someWhere"/>
<setHeader name="CamelJmsRequestTimeout">
<method ref="serviceBean" method="whatIsTheTimeout"/>
</setHeader>
<to uri="jms:queue:foo?replyTo=bar&requestTimeout=30s"/>
<to uri="bean:processReply"/>
</route>
- route:
from:
uri: direct:someWhere
steps:
- setHeader:
name: CamelJmsRequestTimeout
method:
ref: serviceBean
method: whatIsTheTimeout
- to:
uri: jms:queue:foo
parameters:
replyTo: bar
requestTimeout: 30s
- to:
uri: bean:processReply
Camel로 JMS 위에서 fire and forget(InOut)을 할 때는 Camel이 기본적으로 메시지에 time to live 값을 설정하지 않아요. timeToLive 옵션으로 값을 설정할 수 있어요. 예를 들어 5초를 뜻하게 하려면 timeToLive=5000으로 설정해요. disableTimeToLive 옵션은 InOnly 메시징에서도 time to live를 강제로 끄는 데 쓸 수 있어요. requestTimeout 옵션은 InOnly 메시징에는 쓰이지 않아요.
트랜잭션 소비 켜기 (Enabling Transacted Consumption)
흔한 요구사항은 큐에서 트랜잭션 안으로 소비한 다음 Camel 라우트로 메시지를 처리하는 거예요. 이러려면 component/endpoint에 다음 프로퍼티들을 설정하면 돼요:
-
transacted= true -
transactionManager= 트랜잭션 매니저 - 보통JmsTransactionManager
자세한 내용은 Transactional Client EIP 패턴을 보세요.
JMS 위의 트랜잭션과 [Request Reply]
JMS 위에서 Request Reply를 쓸 때는 하나의 트랜잭션을 쓸 수 없어요. JMS는 커밋이 수행될 때까지 아무 메시지도 보내지 않아서, 트랜잭션이 커밋되기 전까지는 서버 쪽이 아무것도 받지 못해요. 그래서 Request Reply를 쓰려면 요청을 보낸 후에 트랜잭션을 커밋하고, 응답을 받는 데는 별도의 트랜잭션을 써야 해요.
이 문제를 다루려고 JMS 컴포넌트는 oneway 메시징과 요청-응답 메시징에 각각 다른 프로퍼티로 트랜잭션 사용을 지정해요:
transacted 프로퍼티는 InOnly 메시지 Exchange Pattern(MEP)에 만 적용돼요.
DMLC transacted session API를 활용하려면 component/endpoint에 다음 프로퍼티들을 쓰면 돼요:
-
transacted= true -
lazyCreateTransactionManager= false
이렇게 하면 좋은 점은 TransactionManager를 설정하지 않고 로컬 트랜잭션을 쓸 때 cacheLevel 설정이 존중된다는 거예요. TransactionManager가 설정되면 DMLC 레벨에서는 캐싱이 일어나지 않아서, 풀링 커넥션 팩토리에 의존해야 해요. 이런 설정에 대한 자세한 내용은 여기와 여기를 보세요.
늦은 응답에 JMSReplyTo 사용하기 (Using JMSReplyTo for late replies)
Camel을 JMS 리스너로 쓰면 ReplyTo jakarta.jms.Destination 객체 값을 ReplyTo 키의 Exchange 프로퍼티로 설정해요. 이 Destination은 이렇게 얻을 수 있어요:
Java 전용: JMS Destination 객체와 Camel Java API를 써요
Destination replyDestination = exchange.getIn().getHeader("JMSReplyTo", Destination.class);
그리고 나중에 일반 JMS나 Camel로 응답을 보내는 데 쓰면 돼요.
Java 전용: JmsEndpoint와 ProducerTemplate Java API를 써요
// we need to pass in the JMS component, and in this sample we use ActiveMQ
JmsEndpoint endpoint = JmsEndpoint.newInstance(replyDestination, activeMQComponent);
// now we have the endpoint we can use regular Camel API to send a message to it
template.sendBody(endpoint, "Here is the late reply.");
응답을 보내는 다른 방법은, 보낼 때 같은 Exchange 프로퍼티에 replyDestination 객체를 담아 주는 거예요. 그러면 Camel이 이 프로퍼티를 집어서 실제 목적지로 써요. 단, 엔드포인트 URI에 더미 목적지가 포함돼 있어야 해요. 예를 들면:
Java 전용: ProducerTemplate과 Processor로 JMS 목적지 객체를 프로그래매틱하게 설정해요
// we pretend to send it to some non-existing dummy queue
template.send("activemq:queue:dummy", exchange -> {
// and here we override the destination with the ReplyTo destination object so the message is sent to there instead of dummy
exchange.getIn().setHeader("CamelJmsDestination", replyDestination);
exchange.getIn().setBody("Here is the late reply.");
});
요청 타임아웃 사용하기 (Using a request timeout)
아래 샘플에서는 Request Reply 스타일 메시지 Exchange(requestBody 메서드 = InOut)를 slow 큐로 보내서 Camel에서 추가로 처리하고, 돌아오는 응답을 기다려요:
InOnly 메시지를 보내면서 JMSReplyTo 헤더 유지하기 (Sending an InOnly message and keeping the JMSReplyTo header)
camel-jms로 JMS 목적지에 보낼 때 producer는 MEP로 InOnly인지 InOut인지 판단해요. 하지만 InOnly 메시지를 보내면서 JMSReplyTo 헤더를 유지하고 싶을 때가 있어요. 그러려면 Camel에게 유지하라고 지시해야 해요. 그러지 않으면 JMSReplyTo 헤더가 버려져요.
예를 들어 foo 큐로 InOnly 메시지를 보내면서 bar 큐를 가리키는 JMSReplyTo를 유지하려면 이렇게 할 수 있어요:
Java 전용: 커스텀 JMS 헤더로 보낼 때 ProducerTemplate 테스트 API를 써요
template.send("activemq:queue:foo?preserveMessageQos=true", exchange -> {
exchange.getIn().setBody("World");
exchange.getIn().setHeader("JMSReplyTo", "bar");
});
여기서는 preserveMessageQos=true를 써서 Camel이 JMSReplyTo 헤더를 유지하도록 지시했어요.
목적지에 JMS 프로바이더 옵션 설정하기 (Setting JMS provider options on the destination)
IBM WebSphere MQ 같은 일부 JMS 프로바이더는 JMS 목적지에 옵션을 설정해야 해요. 예를 들어 targetClient 옵션을 지정해야 할 수 있죠. targetClient는 WebSphere MQ 옵션이지 Camel URI 옵션이 아니라서, JMS 목적지 이름에 이렇게 설정해야 해요:
// ...
.setHeader("CamelJmsDestinationName", constant("queue:///MY_QUEUE?targetClient=1"))
.to("wmq:queue:MY_QUEUE?useMessageIDAsCorrelationID=true");
<!-- ... -->
<setHeader name="CamelJmsDestinationName">
<constant>queue:///MY_QUEUE?targetClient=1</constant>
</setHeader>
<to uri="wmq:queue:MY_QUEUE?useMessageIDAsCorrelationID=true"/>
# ...
- setHeader:
name: CamelJmsDestinationName
constant: "queue:///MY_QUEUE?targetClient=1"
- to:
uri: wmq:queue:MY_QUEUE
parameters:
useMessageIDAsCorrelationID: true
WMQ 버전 중에는 이 옵션을 목적지 이름에 받아들이지 않는 것도 있어서, 이런 예외가 뜰 수 있어요:
com.ibm.msg.client.jms.DetailedJMSException: JMSCC0005: The specified value 'MY_QUEUE?targetClient=1' is not allowed for 'XMSC_DESTINATION_NAME'
해결 방법은 커스텀 DestinationResolver를 쓰는 거예요:
Java 전용: 커스텀 `DestinationResolver`로 JMS 컴포넌트를 프로그래매틱하게 설정해요
JmsComponent wmq = new JmsComponent(connectionFactory);
wmq.setDestinationResolver((session, destinationName, pubSubDomain) -> {
MQQueueSession wmqSession = (MQQueueSession) session;
return wmqSession.createQueue("queue:///" + destinationName + "?targetClient=1");
});
더 알아보기 (Learn more)
JMS 컴포넌트는 다른 컴포넌트 예제와 EIP 패턴 문서에서도 자주 등장해요. ActiveMQ 브로커를 쓴다면 ActiveMQ 컴포넌트 문서를 보고, Artemis 브로커라면 JMS 컴포넌트를 그대로 쓰면 돼요. 요청/응답 구성이나 트랜잭션 설정은 위의 Request-reply over JMS, Transactions and Cache Levels 섹션을 먼저 보는 걸 추천해요.