[Apache Camel] Netty4 maximumPoolSize 설정
Apache Camel Netty4, Netty4-Http 에서 maximumPoolSize가 뜻하는 것은 실제 Exchange를 가지고 Processor를 진행하는 주체인 NettyEventExecutorGroup 의 갯수이다.

Netty4 Endpoint 설정이 아닌 Netty4 Component 설정은
SpringBoot의 application.properties에서 auto-configuration이 가능하다. ( 아시다 시피 starter 여야 auto-configuration이 가능하므로 메이븐으로 camel-netty4-starter 받는다 )
그러나 아래와 같이 netty4 같은경우 maximumPoolSize를 설정해도




이렇게 application.properties의 값을 가져와서 NettyComponentConfiguration 에다가 입력이 된다.
그럼 이제 NettyComponent를 생성 시 NettyComponentConfiguration을 이용해서 ComponentConfiguration을 설정하면 EventExecutorGroup의 PoolSize를 입력 시 properties에서 설정한 2가 들어가야하는데 전혀 반영이 되지 않았다.


후 ... 아무리 살펴봐도 NettyComponentConfiguration 값이 반영되는 로직을 찾을 수가 없었다.
현업에서 사용한 2.21.1 버전, 지금 이 글을 작성할때 쓴 최신버전 3.0.0-M4 다 그렇다.
하지만 웃긴것은
Netty4Component를 기반으로한 Netty4HttpComponent는 또 application.properties에 설정한 옵션이 먹는다.

디버그로 무슨 차이인지 확인을 해보았다.



결국 Bean을 생성할 때 factoryMethodName이 있는지 없는지 여부에 의해 Bean 생성이 갈리게 되었다!!!
그럼 factoryMethod가 뭔지 살펴봤더니

바로 starter에 의해 AutoConfiguration이 진행되는 클래스의 메소드 였던것이다.
그래서 바로 configureNettyHttpComponent 메소드를 살펴보았다.
@Lazy
@Bean({"netty-http-component", "netty4-http-component"})
@ConditionalOnMissingBean(NettyHttpComponent.class)
public NettyHttpComponent configureNettyHttpComponent() throws Exception {
NettyHttpComponent component = new NettyHttpComponent();
component.setCamelContext(camelContext);
Map<String, Object> parameters = new HashMap<>();
IntrospectionSupport.getProperties(configuration, parameters, null,
false);
for (Map.Entry<String, Object> entry : parameters.entrySet()) {
Object value = entry.getValue();
Class<?> paramClass = value.getClass();
if (paramClass.getName().endsWith("NestedConfiguration")) {
Class nestedClass = null;
try {
nestedClass = (Class) paramClass.getDeclaredField(
"CAMEL_NESTED_CLASS").get(null);
HashMap<String, Object> nestedParameters = new HashMap<>();
IntrospectionSupport.getProperties(value, nestedParameters,
null, false);
Object nestedProperty = nestedClass.newInstance();
CamelPropertiesHelper.setCamelProperties(camelContext,
nestedProperty, nestedParameters, false);
entry.setValue(nestedProperty);
} catch (NoSuchFieldException e) {
}
}
}
CamelPropertiesHelper.setCamelProperties(camelContext, component,
parameters, false);
if (ObjectHelper.isNotEmpty(customizers)) {
for (ComponentCustomizer<NettyHttpComponent> customizer : customizers) {
boolean useCustomizer = (customizer instanceof HasId)
? HierarchicalPropertiesEvaluator.evaluate(
applicationContext.getEnvironment(),
"camel.component.customizer",
"camel.component.netty4-http.customizer",
((HasId) customizer).getId())
: HierarchicalPropertiesEvaluator.evaluate(
applicationContext.getEnvironment(),
"camel.component.customizer",
"camel.component.netty4-http.customizer");
if (useCustomizer) {
LOGGER.debug("Configure component {}, with customizer {}",
component, customizer);
customizer.customize(component);
}
}
}
return component;
}
이렇게 NettyHttpComponent 생성 후 CamelPropetiesHelper.setCamelProperties() 메소드에 의해 application.properties에 등록되어있던 값들이 채워진 후 return 하게 되었다.
그래서 maximum-pool-size 가 적용되었던 것이다.
그러면 우리 NettyComponent는 ..?
@Lazy
@Bean({"netty-component", "netty4-component"})
@ConditionalOnMissingBean(NettyComponent.class)
public NettyComponent configureNettyComponent() throws Exception {
NettyComponent component = new NettyComponent();
component.setCamelContext(camelContext);
Map<String, Object> parameters = new HashMap<>();
IntrospectionSupport.getProperties(configuration, parameters, null,
false);
for (Map.Entry<String, Object> entry : parameters.entrySet()) {
Object value = entry.getValue();
Class<?> paramClass = value.getClass();
if (paramClass.getName().endsWith("NestedConfiguration")) {
Class nestedClass = null;
try {
nestedClass = (Class) paramClass.getDeclaredField(
"CAMEL_NESTED_CLASS").get(null);
HashMap<String, Object> nestedParameters = new HashMap<>();
IntrospectionSupport.getProperties(value, nestedParameters,
null, false);
Object nestedProperty = nestedClass.newInstance();
CamelPropertiesHelper.setCamelProperties(camelContext,
nestedProperty, nestedParameters, false);
entry.setValue(nestedProperty);
} catch (NoSuchFieldException e) {
}
}
}
CamelPropertiesHelper.setCamelProperties(camelContext, component,
parameters, false);
if (ObjectHelper.isNotEmpty(customizers)) {
for (ComponentCustomizer<NettyComponent> customizer : customizers) {
boolean useCustomizer = (customizer instanceof HasId)
? HierarchicalPropertiesEvaluator.evaluate(
applicationContext.getEnvironment(),
"camel.component.customizer",
"camel.component.netty4.customizer",
((HasId) customizer).getId())
: HierarchicalPropertiesEvaluator.evaluate(
applicationContext.getEnvironment(),
"camel.component.customizer",
"camel.component.netty4.customizer");
if (useCustomizer) {
LOGGER.debug("Configure component {}, with customizer {}",
component, customizer);
customizer.customize(component);
}
}
}
return component;
}
이렇게 똑같은 메소드를 가지고있다.

그런데 Bean생성 시 factoryBeanName, factoryMethodName이 존재하지 않는다 ... NettyComponentAutoConfiguration의 configureNettyComponent() 메소드로 생성이 되지않는다는 것
그냥 beanClass를 통해 new NettyComponent()만이 이루어 지는 것이다. 그래서 maximum-pool-size default값인 16이 계속 입력되었던것
후 .. 그럼 더 거슬러 올라가서 어디서 이러한 mbd가 생성되는지 확인을 해봐야겠다.

SpringBootCamelContext에서 Component를 초기화하는 시점에서부터 이 둘의 생성방법이 달라지게 되었다.


결론적으로 "netty4", "netty4-component" Component가 Singleton으로 Bean이 생성되어있지 않아서 발생한 문제였다.
@Lazy
@Bean({"netty-component", "netty4-component"})
@ConditionalOnMissingBean(NettyComponent.class)
public NettyComponent configureNettyComponent() throws Exception {
.
.
.
}
분명히 Bean이 달려있는데 어째서 ... 흠 ..
그럼 진짜로 Bean으로 생성이 되어있지 않은건지 테스트 해보았다.
@Configuration
public class TcpRouter extends RouteBuilder{
@Bean
public StringDecoder stringDecoder() {
return new StringDecoder();
}
@Bean
public StringEncoder stringEncoder() {
return new StringEncoder();
}
@Autowired
private NettyHttpComponentAutoConfiguration nettyHttpComponent;
@Autowired
private NettyComponentAutoConfiguration nettyComponent;
@Override
public void configure() throws Exception {
NettyHttpComponent nhc = nettyHttpComponent.configureNettyHttpComponent();
NettyComponent nc = nettyComponent.configureNettyComponent();
from("netty4:tcp://localhost:60010?decoders=stringDecoder&encoders=stringEncoder&workerCount=2")
.process(exchange->{
System.out.println("start tcp:"+exchange.getMessage().getBody(String.class));
Thread.sleep(3000);
System.out.println("end tcp:"+exchange.getMessage().getBody(String.class));
})
.end();
}
}
위와 같이 실행하니 NettyHttpComponent.configureNettyHttpComponent() 메소드는 무사히 지나갔다.
그러나
***************************
APPLICATION FAILED TO START
***************************
Description:
A component required a bean named 'netty-component' that could not be found.
The following candidates were found but could not be injected:
- Bean method 'configureNettyComponent' in 'NettyComponentAutoConfiguration' not loaded because @ConditionalOnMissingBean (types: org.apache.camel.component.netty4.NettyComponent; SearchStrategy: all) found beans of type 'org.apache.camel.component.netty4.NettyComponent' netty-http-component
Action:
Consider revisiting the entries above or defining a bean named 'netty-component' in your configuration.
netty-http-component 가 netty-component를 상속하여서 생긴 문제같은데 실제로 netty-component만 auto-configuration을 하면 에러가 발생하지 않는다.
그래서 Apache Camel 측에 메일을 보냈더니

old version이라고 3.4 이상만 지원한단다...
결국 현재 버전에서 netty-http-component 와 netty-component 를 동시에 사용할 경우 auto-configuration 은 netty-http-component만 적용되고 netty-component는 따로 custom하게 Bean으로 내가 직접 생성해서 해야한다는 것이다.