I want to configure Thread pool size for my application which is using spring boot embedded jetty server. Below is the code snippet i am using.
I want to know what is the maximum thread pool size I can set for the embedded jetty server and is it the correct way of configuring it?
#Bean
public EmbeddedServletContainerFactory jettyConfigBean() {
JettyEmbeddedServletContainerFactory jef = new JettyEmbeddedServletContainerFactory();
jef.addServerCustomizers(new JettyServerCustomizer() {
public void customize(org.eclipse.jetty.server.Server server) {
final QueuedThreadPool threadPool = server.getBean(QueuedThreadPool.class);
server.setHandler(handlers);
}
});
return jef;
}
Speaking for spring-boot 2.0.0-SNAPSHOT:
This is for jetty and tomcat and undertow configurable via
server.jetty.acceptors
server.tomcat.max-connections
server.undertow.io-threads
See https://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/#common-application-properties for a complete reference.
The maximum number of threads you can set is dependent on your machine, not so much on the hosting software. If you have a monster machine you can probably have hundreds of threads, whereas a regular laptop would probably only be able to handle tens of threads. You need to tune your configuration by testing. A good way is to setup a load test, for example see if 50 threads with 50 req/sec will crash your service, then check if increasing/decreasing the number of threads will help. You can find the limits of your application and machine with such a technique.
Regarding the correctness of the configuration, you can read this blog post which explains it very well: http://jdpgrailsdev.github.io/blog/2014/10/07/spring_boot_jetty_thread_pool.html
This is what you can do:
While working on newer versions, you need to create a server factory bean in a configuration file. I tested this on Spring Boot version 2.1.6 while the document I referred to is for version 2.3.3
#Bean
public ConfigurableServletWebServerFactory webServerFactory() {
JettyServletWebServerFactory factory = new JettyServletWebServerFactory();
factory.setPort(8080);
factory.setContextPath("/my-app");
QueuedThreadPool threadPool = new QueuedThreadPool();
threadPool.setMinThreads(10);
threadPool.setMaxThreads(100);
threadPool.setIdleTimeout(60000);
factory.setThreadPool(threadPool);
return factory;
}
Following is the link to Spring Docs: customizing-embedded-containers
Related
This is regarding CDI spec of quarkus. Would want to understand is there a configuration bean for quarkus? How does one do any sort of configuration in quarkus?
If I get it right the original question is about #Configuration classes that can contain #Bean definitions. If so then CDI producer methods and fields annotated with #javax.enterprise.inject.Produces are the corresponding alternative.
Application configuration is a completely different question though and Jay is right that the Quarkus configuration reference is the ultimate source of information ;-).
First of all reading how cdi spec of quarkus differs from spring is important.
Please refer this guide:
https://quarkus.io/guides/cdi-reference
The learnings from this guide is there is #Produces which is an alternative to #Configuration bean in Quarkus.
Let us take an example for libs that might require a configuration through code. Example: Microsoft Azure IOT Service Client.
public class IotHubConfiguration {
#ConfigProperty(name="iothub.device.connection.string")
String connectionString;
private static final Logger LOG = Logger.getLogger(IotHubConfiguration.class);
#Produces
public ServiceClient getIot() throws URISyntaxException, IOException {
LOG.info("Inside Service Client bean");
if(connectionString==null) {
LOG.info("Connection String is null");
throw new RuntimeException("IOT CONNECTION STRING IS NULL");
}
ServiceClient serviceClient = new ServiceClient(connectionString, IotHubServiceClientProtocol.AMQPS);
serviceClient.open();
LOG.info("opened Service Client Successfully");
return serviceClient;
}
For all libs vertically intergrated with quarkus application.properties can be used and then you will get a driver obj for that broker/dbs available directly through #Inject in your #applicationScoped/#Singleton bean So, Why is that?
To Simplify and Unify Configuration
To Make Sure no code is required for configuring anything i.e. database config, broker config , quarkus config etc.
This drastically reduces the amount of code written for configuring and also Junits needed to cover that code.
Let us take an example where kafka producer configuration needs to be added: in application.properties
kafka.bootstrap.servers=${KAFKA_BROKER_URL:localhost:9092}
mp.messaging.outgoing.incoming_kafka_topic_test.topic=${KAFKA_INPUT_TOPIC_FOR_IOT_HUB:input_topic1}
mp.messaging.outgoing.incoming_kafka_topic_test.connector=smallrye-kafka
mp.messaging.outgoing.incoming_kafka_topic_test.value.deserializer=org.apache.kafka.common.serialization.StringDeserializer
mp.messaging.outgoing.incoming_kafka_topic_test.key.deserializer=org.apache.kafka.common.serialization.StringDeserializer
mp.messaging.outgoing.incoming_kafka_topic_test.health-readiness-enabled=true
For full blown project reference: https://github.com/JayGhiya/QuarkusExperiments/tree/initial_version_v1/KafkaProducerQuarkus
Quarkus References for Config:
https://quarkus.io/guides/config-reference
Example for reactive sql config: https://quarkus.io/guides/reactive-sql-clients
Now let us talk about a bonus feature that quarkus provides which improves developer experience by atleast an order of magnitude that is profile driven development and testing.
Quarkus provides three profiles:
dev - Activated when in development mode (i.e. quarkus:dev)
test - Activated when running tests
prod - The default profile when not running in development or test
mode
Let us just say that in the given example you wanted to have different topics for development and different topics for production. Let us achieve that!
%dev.mp.messaging.outgoing.incoming_kafka_topic_test.topic=${KAFKA_INPUT_TOPIC_FOR_IOT_HUB:input_topic1}
%prod.mp.messaging.outgoing.incoming_kafka_topic_test.topic=${KAFKA_INPUT_TOPIC_FOR_IOT_HUB:prod_topic}
This is how simple it is. This is extremely useful in cases where your deployments run with ssl enabled brokers/dbs etc and for dev purposes you have unsecure local brokers/dbs. This is a game changer.
I'm using Spring Boot 2.0.3 release. I want to increase the maximum pool size of HikariCP which is 10 by default.
I tried changing it in applicaiton.properties with
spring.datasource.hikari.maximum-pool-size=200
but it is not working because in the logs it still show that max pool size is 10.
The reason I want to change is because I am somehow reaching that limit in staging and I have no idea what's causing it.
I faced similar issue recently (Spring Boot v2.1.3). Posting it here in case people bump into the same scenario.
Long story short: if you're initializing DataSource using #ConfigurationProperties, those properties don't seem to require hikari prefix for maximum-pool-size, unless I'm missing something. So spring.datasource.maximum-pool-size should work if you use #ConfigurationProperties(prefix = "spring.datasource").
Details: In my case I'm initializing DataSource myself using org.springframework.boot.jdbc.DataSourceBuilder, so that I could later use it in org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean:
#Bean
#Primary
#ConfigurationProperties(prefix = "spring.datasource")
public DataSource primaryDataSource()
{
return DataSourceBuilder.create().build();
}
In this case property spring.datasource.hikari.maximum-pool-size taken from Common App Properties section in Spring Boot doc did not help. Neither did suggested above maximumPoolSize.
So I went and debugged Spring Boot code which lead me to org.springframework.boot.context.properties.bind.JavaBeanBinder and it's method bind. Value for property name for HikariDataSource setter setMaximumPoolSize was "maximum-pool-size", so just for sake of testing I renamed my property to be spring.datasource.maximum-pool-size and it finally worked.
Hope it helps. Please let me know in the comments if I missed something or took wrong path. Thanks!
As per HikariCP Github README it's maximumPoolSize so try using:
spring.datasource.hikari.maximumPoolSize = 200
But this will work only if you allow Spring Boot to create the DataSource. If you create the DataSource yourself Spring Boot properties have no effect.
Do note that 200 is a very high value that may negatively impact your database as each physical connection requires server resources. In most cases a lower value will yield better performance, see HikariCP wiki: About Pool Sizing.
Recently we ported our application from the web application running in tomcat to spring boot application with embedded tomcat.
After running the app for several days, memory and cpu usage have reached 100%.
In heap dump analysis it comes out that there was a bunch of http session objects which where not destroyed.
I can see in the debug that sessions created with configured timeout value, lets say, 5 minutes. But after this time the invalidation is not triggered. It invoked only if I do request again after the timeout period.
I have compared this behavior with app running in tomcat and I can see that session invalidation is triggered by ContainerBackgroungProcessor thread [StandardManager(ManagerBase).processExpires()]
I do not see this background thread in spring boot application.
What was done following some suggestions found:
session timeout set in application.properties:
server.session.timout=300
or in EmbeddedServletContainerCustomizer #Bean:
factory.setSessionTimout(5, TimeUnit.MINUTES)
Added HttpSessionEventPublisher and SessionRegistry beans
Nothing helps, sessions just not invalidated at the expiration time.
Some clue about this?
After some more debugging and documentation reading this is the reason and solution:
In tomcat, there is a thread spawned on behalf of the root container which scans periodically container and its child containers session pools and invalidates them. Each container/child container may be configured to have its own background processor to do the job or to rely on its host's background processor.
This controlled by context.backgroundProcessorDelay
Apache Tomcat 8 Configuration Reference
backgroundProcessorDelay -
This value represents the delay in seconds between the invocation of the backgroundProcess method on this engine and its child containers, including all hosts and contexts. Child containers will not be invoked if their delay value is not negative (which would mean they are using their own processing thread). Setting this to a positive value will cause a thread to be spawn. After waiting the specified amount of time, the thread will invoke the backgroundProcess method on this engine and all its child containers. If not specified, the default value for this attribute is 10, which represent a 10 seconds delay.
In spring boot application with embedded tomcat
there is TomcatEmbeddedServletContainerFactory.configureEngine() which sets this property -1 for the StandardEngine[Tomcat], which is the root container in tomcat hierarchy, as I understand.
All the child containers including web app also have this parameter set to -1.
And this means they all rely on someone else to do the job.
Spring do not do it, no-one do it.
The solution for me was to set this parameter for the app context:
#Bean
public EmbeddedServletContainerCustomizer servletContainerCustomizer() {
return new EmbeddedServletContainerCustomizer() {
#Override
public void customize(ConfigurableEmbeddedServletContainer container) {
if (container instanceof TomcatEmbeddedServletContainerFactory) {
TomcatEmbeddedServletContainerFactory factory = (TomcatEmbeddedServletContainerFactory) container;
TomcatContextCustomizer contextCustomizer = new TomcatContextCustomizer() {
#Override
public void customize(Context context) {
context.setBackgroundProcessorDelay(10);
}
};
List<TomcatContextCustomizer> contextCustomizers = new ArrayList<TomcatContextCustomizer>();
contextCustomizers.add(contextCustomizer);
factory.setTomcatContextCustomizers(contextCustomizers);
customizeTomcat(factory);
}
}
Morning all,
I've been struggling lately with the spring-boot-artemis-starter.
My understanding of its spring-boot support was the following:
set spring.artemis.mode=embedded and, like tomcat, spring-boot will instanciate a broker reachable through tcp (server mode). The following command should be successful: nc -zv localhost 61616
set spring.artmis.mode=native and spring-boot will only configure the jms template according to the spring.artemis.* properties (client mode).
The client mode works just fine with a standalone artemis server on my machine.
Unfortunatelly, I could never manage to reach the tcp port in server mode.
I would be grateful if somebody confirms my understanding of the embedded mode.
Thank you for tour help
After some digging I noted that the implementation provided out of the box by the spring-boot-starter-artemis uses org.apache.activemq.artemis.core.remoting.impl.invm.InVMAcceptorFactory acceptor. I'm wondering if that's not the root cause (again I'm by no means an expert).
But it appears that there is a way to customize artemis configuration.
Therefore I tried the following configuration without any luck:
#SpringBootApplication
public class MyBroker {
public static void main(String[] args) throws Exception {
SpringApplication.run(MyBroker.class, args);
}
#Autowired
private ArtemisProperties artemisProperties;
#Bean
public ArtemisConfigurationCustomizer artemisConfigurationCustomizer() {
return configuration -> {
try {
configuration.addAcceptorConfiguration("netty", "tcp://localhost:" + artemisProperties.getPort());
} catch (Exception e) {
throw new RuntimeException("Failed to add netty transport acceptor to artemis instance");
}
};
}
}
You just have to add a Connector and an Acceptor to your Artemis Configuration. With Spring Boot Artemis starter Spring creates a Configuration bean which will be used for EmbeddedJMS configuration. You can see this in ArtemisEmbeddedConfigurationFactory class where an InVMAcceptorFactory will be set for the configuration. You can edit this bean and change Artemis behaviour through custom ArtemisConfigurationCustomizer bean which will be sucked up by Spring autoconfig and be applied to the Configuration.
An example config class for your Spring Boot application:
import org.apache.activemq.artemis.api.core.TransportConfiguration;
import org.apache.activemq.artemis.core.remoting.impl.netty.NettyAcceptorFactory;
import org.apache.activemq.artemis.core.remoting.impl.netty.NettyConnectorFactory;
import org.springframework.boot.autoconfigure.jms.artemis.ArtemisConfigurationCustomizer;
import org.springframework.context.annotation.Configuration;
#Configuration
public class ArtemisConfig implements ArtemisConfigurationCustomizer {
#Override
public void customize(org.apache.activemq.artemis.core.config.Configuration configuration) {
configuration.addConnectorConfiguration("nettyConnector", new TransportConfiguration(NettyConnectorFactory.class.getName()));
configuration.addAcceptorConfiguration(new TransportConfiguration(NettyAcceptorFactory.class.getName()));
}
}
My coworker and I had the exact same problem as the documentation on this link (chapter Artemis Support) says nothing about adding an individual ArtemisConfigurationCustomizer - Which is sad because we realized that without this Customizer our Spring Boot App would start and act as if everything was okay but actually it wouldn't do anything.
We also realized that without the Customizer the application.properties file is not beeing loaded so no matter what host or port you mentioned there it would not count.
After adding the Customizer as stated by the two examples it worked without a problem.
Here some results that we figured out:
It only loaded the application.properties after configuring an ArtemisConfigurationCustomizer
You don't need the broker.xml anymore with an embedded spring boot artemis client
Many examples showing the use of Artemis use a "in-vm" protocol while we just wanted to use the netty tcp protocol so we needed to add it into the configuration
For me the most important parameter was pub-sub-domain as I was using topics and not queues. If you are using topics this parameter needs to be set to true or the JMSListener won't read the messages.
See this page: stackoverflow jmslistener-usage-for-publish-subscribe-topic
When using a #JmsListener it uses a DefaultMessageListenerContainer
which extends JmsDestinationAccessor which by default has the
pubSubDomain set to false. When this property is false it is
operating on a queue. If you want to use topics you have to set this
properties value to true.
In Application.properties:
spring.jms.pub-sub-domain=true
If anyone is interested in the full example I have uploaded it to my github:
https://github.com/CorDharel/SpringBootArtemisServerExample
The embedded mode starts the broker as part of your application. There is no network protocol available with such setup, only InVM calls are allowed. The auto-configuration exposes the necessary pieces you can tune though I am not sure you can actually have a TCP/IP channel with the embedded mode.
I am using Spring in my project and instantiating dataSource as below.
#Bean(destroyMethod="close")
public DataSource restDataSource() {
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName(env.getProperty("hibernate.connection.driver_class"));
dataSource.setUrl(env.getProperty("hibernate.connection.url"));
dataSource.setUsername(env.getProperty("hibernate.connection.username"));
dataSource.setPassword(env.getProperty("hibernate.connection.password"));
dataSource.setInitialSize(env.getRequiredProperty("hibernate.dbcp.initialSize", Integer.class));
dataSource.setMaxActive(env.getRequiredProperty("hibernate.dbcp.maxActive", Integer.class));
dataSource.setMaxIdle(env.getRequiredProperty("hibernate.dbcp.maxIdle", Integer.class));
dataSource.setMinIdle(env.getRequiredProperty("hibernate.dbcp.minIdle", Integer.class));
return dataSource;
}
Below is my properties file.
hibernate.dialect=org.hibernate.dialect.Oracle10gDialect
hibernate.connection.driver_class=oracle.jdbc.driver.OracleDriver
hibernate.connection.username=<>
hibernate.connection.password=<>
hibernate.connection.url=jdbc:oracle:thin:#<Host>:1521:<SID>
hibernate.show_sql=true
hibernate.cache.use_query_cache=true
cache.provider_class=org.hibernate.cache.EhCacheProvider
hibernate.cache.use_second_level_cache=true
hibernate.cache.region.factory_class=org.hibernate.cache.ehcache.EhCacheRegionFactory
net.sf.ehcache.configurationResourceName=ehcache.xml
**hibernate.dbcp.initialSize=10
hibernate.dbcp.maxActive=100
hibernate.dbcp.maxIdle=30
hibernate.dbcp.minIdle=10**
Please suggest :-
Any changes in the properties marked in Bold(initialSize,maxActive,maxidle,minIdle). My application will be concurrently used by around 100 users and total users are around 3000.
I am using Tomcat Server to deploy my application.Should I be using JNDI for connections instead of directly specify connection properties? Is above way of using connections good for a production System?
Instead of Commons DBCP I would suggest using HikariCP (I'm having very good experiences with that lately or if you are already on tomcat use Tomcat JDBC instead.
There is a lot written on poolsizing (see here for a nice explanation and here for a short video from Oracle). In short large poolsizes don't work and probably will make performance worse.
A rule of thumb/formula (also in the article mentioned) is to use
connections = ((core_count * 2) + effective_spindle_count)
Where core_count is the number of (actual) cores in your server and effective_spindle_count the number of disks you have. If you have server with a large disk and 4 cores it would lead to a connection pool of size 9. This should be able to handle what you need, adding more will only add overhead of monitoring, thread switching etc.