-
[Apache Camel] Netty4 maximumPoolSize 설정개발/Apache Camel 2020. 10. 2. 13:45
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으로 내가 직접 생성해서 해야한다는 것이다.
'개발 > Apache Camel' 카테고리의 다른 글
[Apache Camel] Netty4 Consumer 파헤치기 (0) 2020.09.22 [Apache Camel] Netty4 요청마다 RequestTimeout 변경하기 (0) 2020.09.16