mkopylec / charon-spring-boot-starter

Reverse proxy implementation in form of a Spring Boot starter.
Apache License 2.0
247 stars 55 forks source link

about performance ? #28

Closed gordanyang closed 5 years ago

gordanyang commented 8 years ago

about performance

charon config

charon:
    filter-order: 100000 
    timeout:
        connect: 5000
        read: 5000
    retrying:
        max-attempts: 3 
    metrics:
        enabled: false 
        names-prefix: charon
        logging-reporter:
            enabled: false 
            reporting-interval-in-seconds: 60 
    tracing:
        enabled: false 
    mappings-update:
        enabled: false 
    mappings:
        -
            name: echo
            path: /api/echo/
            destinations: http://localhost:9092/api/echo/
            asynchronous: true 

echo server

@RestController
@RequestMapping("/api/echo") 
public class EchoController {

    @RequestMapping(value = "/hello", method = RequestMethod.GET)
    public String echo() {      
        return null;
    }

}

use ab test

ab -v 2 -n 5000 -c 20 http://localhost:8080/api/echo/hello

some error in console

17:06:50.759 ERROR com.github.mkopylec.charon.core.retry.LoggingListener - All 1 attempts to forward HTTP request using 'echo' mapping has failed
[WARNING]
org.springframework.web.client.ResourceAccessException: I/O error on GET request for "http://localhost:9092/api/echo/hello": connection timed out: localhost/127.0.0.1:9092; nested exception is io.netty.channel.ConnectTimeoutException: connection timed out: localhost/127.0.0.1:9092
        at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:607)
        at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:572)
        at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:534)
        at com.github.mkopylec.charon.core.http.RequestForwarder.sendRequest(RequestForwarder.java:99)
        at com.github.mkopylec.charon.core.http.RequestForwarder.forwardHttpRequest(RequestForwarder.java:67)
        at com.github.mkopylec.charon.core.http.ReverseProxyFilter.lambda$null$3(ReverseProxyFilter.java:96)
        at org.springframework.retry.support.RetryTemplate.doExecute(RetryTemplate.java:263)
        at org.springframework.retry.support.RetryTemplate.execute(RetryTemplate.java:154)
        at com.github.mkopylec.charon.core.http.ReverseProxyFilter.lambda$doFilterInternal$4(ReverseProxyFilter.java:95)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
        at java.lang.Thread.run(Thread.java:745)
Caused by: io.netty.channel.ConnectTimeoutException: connection timed out: localhost/127.0.0.1:9092
        at io.netty.channel.nio.AbstractNioChannel$AbstractNioUnsafe$1.run(AbstractNioChannel.java:220)
        at io.netty.util.concurrent.PromiseTask$RunnableAdapter.call(PromiseTask.java:38)
        at io.netty.util.concurrent.ScheduledFutureTask.run(ScheduledFutureTask.java:120)
        at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:358)
        at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:374)
        at io.netty.util.concurrent.SingleThreadEventExecutor$2.run(SingleThreadEventExecutor.java:112)
        at io.netty.util.concurrent.DefaultThreadFactory$DefaultRunnableDecorator.run(DefaultThreadFactory.java:137)
        ... 1 more
17:06:55.872 ERROR com.github.mkopylec.charon.core.retry.LoggingListener - All 1 attempts to forward HTTP request using 'echo' mapping has failed

when try to test 9092 , No error appears in the console.

ab -v 2 -n 5000 -c 20 http://localhost:9092/api/echo/hello
mkopylec commented 8 years ago

I am almost sure the problem occurs because the mapping is in async mode and the code that is creating a TaskExecutor is, see CharonConfiguration:


    @Bean
    @ConditionalOnMissingBean
    public TaskExecutor charonTaskExecutor() {
        if (isAsynchronousMappingPresent()) {
            return new ThreadPoolTaskExecutor();
        }
        return null;
    }

The default ThreadPoolTaskExecutor has no limits on thread pool. Maybe the TaskExecutor configuration should be exposed as yml properties. For a quick fix you could just override the TaskExecutor bean creating a custom bean and defining the thread pool limits.

Could you please run your test with asynchronous: false?

Also note that the destination should be only http://localhost:9092 without path and for saving the path in forward request you should set the strip-path property to false

mkopylec commented 8 years ago

BTW. The current configuration properties list is a little bit different now: https://github.com/mkopylec/charon-spring-boot-starter#configuration-properties-list

gordanyang commented 8 years ago

benchmark test:

charon:
    filter-order: 100000 
    timeout:
        connect: 5000
        read: 5000
    retrying:
        max-attempts: 3 
    metrics:
        enabled: false 
        names-prefix: charon
        logging-reporter:
            enabled: false 
            reporting-interval-in-seconds: 60 
    tracing:
        enabled: false 
    mappings-update:
        enabled: false 
    mappings:
        -
            name: echo
            path: /api/echo/
            destinations: http://localhost:9092/api/echo/
            asynchronous: true
            strip-path: false 
benchmark test Requests per second Time per request
ab -n 5000 -c 20 http://localhost:8080/api/echo/hello 1100.27/sec(mean) 18.177 ms(mean)
ab -n 5000 -c 20 http://localhost:9092/api/echo/hello 2116.81/sec(mean) 9.448 ms(mean)

charon:
    filter-order: 100000 
    timeout:
        connect: 5000
        read: 5000
    retrying:
        max-attempts: 3 
    metrics:
        enabled: false 
        names-prefix: charon
        logging-reporter:
            enabled: false 
            reporting-interval-in-seconds: 60 
    tracing:
        enabled: false 
    mappings-update:
        enabled: false 
    mappings:
        -
            name: echo
            path: /api/echo/
            destinations: http://localhost:9092/api/echo/
            asynchronous: false
            strip-path: false
benchmark test Requests per second Time per request
ab -n 5000 -c 20 http://localhost:8080/api/echo/hello 597.63/sec(mean) 33.466 ms(mean)
gordanyang commented 8 years ago

when i change CharonConfiguration ,there appears other error in console.

    @Bean
    @ConditionalOnMissingBean
    public TaskExecutor charonTaskExecutor() {
        if (isAsynchronousMappingPresent()) {
            ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
            executor.setCorePoolSize(2000);
            executor.setMaxPoolSize(2000);
            executor.setQueueCapacity(4000);
            return executor;
        }
        return null;
    }
09:52:36.306 ERROR o.a.catalina.core.ContainerBase.[Tomcat].[localhost].[/].[dispatcherServlet] - Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception
org.springframework.web.client.ResourceAccessException: I/O error on GET request for "http://localhost:9092/api/echo/hello": connection timed out: localhost/127.0.0.1:9092; nested exception is io.netty.channel.ConnectTimeoutException: connection timed out: localhost/127.0.0.1:9092
        at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:633)
        at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:595)
        at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:557)
        at com.github.mkopylec.charon.core.http.RequestForwarder.sendRequest(RequestForwarder.java:101)
        at com.github.mkopylec.charon.core.http.RequestForwarder.forwardHttpRequest(RequestForwarder.java:67)
        at com.github.mkopylec.charon.core.http.ReverseProxyFilter.lambda$forwardToDestination$9(ReverseProxyFilter.java:117)
        at org.springframework.retry.support.RetryTemplate.doExecute(RetryTemplate.java:276)
        at org.springframework.retry.support.RetryTemplate.execute(RetryTemplate.java:157)
        at com.github.mkopylec.charon.core.http.ReverseProxyFilter.forwardToDestination(ReverseProxyFilter.java:117)
        at com.github.mkopylec.charon.core.http.ReverseProxyFilter.doFilterInternal(ReverseProxyFilter.java:94)
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:317)
        at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:127)
        at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:91)
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
        at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:115)
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
        at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:111)
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
        at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:169)
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
        at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:200)
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
        at com.gordan.puma.edge.adapters.StatelessAuthenticationFilter.doFilter(StatelessAuthenticationFilter.java:70)
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
        at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:200)
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
        at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:66)
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
        at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:214)
        at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:177)
        at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:346)
        at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:262)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
        at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99)
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
        at org.springframework.web.filter.HttpPutFormContentFilter.doFilterInternal(HttpPutFormContentFilter.java:87)
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
        at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:77)
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
        at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:197)
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
        at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:198)
        at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:108)
        at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:522)
        at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140)
        at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79)
        at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87)
        at org.apache.catalina.valves.RemoteIpValve.invoke(RemoteIpValve.java:677)
        at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:349)
        at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:1110)
        at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
        at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:785)
        at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1425)
        at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
        at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
        at java.lang.Thread.run(Thread.java:745)
Caused by: io.netty.channel.ConnectTimeoutException: connection timed out: localhost/127.0.0.1:9092
        at io.netty.channel.nio.AbstractNioChannel$AbstractNioUnsafe$1.run(AbstractNioChannel.java:220)
        at io.netty.util.concurrent.PromiseTask$RunnableAdapter.call(PromiseTask.java:38)
        at io.netty.util.concurrent.ScheduledFutureTask.run(ScheduledFutureTask.java:120)
        at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:358)
        at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:374)
        at io.netty.util.concurrent.SingleThreadEventExecutor$2.run(SingleThreadEventExecutor.java:112)
        at io.netty.util.concurrent.DefaultThreadFactory$DefaultRunnableDecorator.run(DefaultThreadFactory.java:137)
        ... 1 common frames omitted
mkopylec commented 8 years ago

So the problem occurs only in async mode? In your code change @ConditionalOnMissingBean to @Primary and that should fix the problem. Also your pool size is way to big. Consider the code below:

    @Bean
    @Primary
    public TaskExecutor charonTaskExecutor() {
        if (isAsynchronousMappingPresent()) {
            ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
            executor.setCorePoolSize(10);
            executor.setMaxPoolSize(20);
            executor.setQueueCapacity(100);
            return executor;
        }
        return null;
    }
mkopylec commented 8 years ago

I've created https://github.com/mkopylec/charon-spring-boot-starter/issues/29

mkopylec commented 8 years ago

Can you please try to run your test with 1.10.0 version?

mkopylec commented 7 years ago

Do you still have a problem?