ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [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
    NettyComponentConfiguration.setMaximumPoolSize()

     

    이렇게 application.properties의 값을 가져와서 NettyComponentConfiguration 에다가 입력이 된다.

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

    NettyComponent.createExecutorService()

    후 ... 아무리 살펴봐도 NettyComponentConfiguration 값이 반영되는 로직을 찾을 수가 없었다.

    현업에서 사용한 2.21.1 버전, 지금 이 글을 작성할때 쓴 최신버전 3.0.0-M4 다 그렇다.

     

    하지만 웃긴것은

    Netty4Component를 기반으로한 Netty4HttpComponent는 또 application.properties에 설정한 옵션이 먹는다.

    netty4-http.maximum-pool-size 4로설정


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

    결국 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으로 내가 직접 생성해서 해야한다는 것이다.

    댓글

Designed by Tistory.