Question:
Good morning, I'm doing a project and using Tomcat 8 as my server. The question is, I have a Linux server to host my application, but I would like to know if I need to do some different configuration when using Tomcat in production.
Are any modifications related to security or server usage required? My server is hosted in SP and has 16Gb of ram. Is something needed so that it can make use of the full capacity of the server? or even modify some jvm settings on the machine when we are using it for projects in production?
My project makes use of VRaptor 4 and hibernate as a priority. If anyone can provide some tips.
Thanks.
Answer:
TL;DR
There is no definitive answer, but certainly production configurations can differ between development and production, for example.
The problem is that these settings vary from system to system and many are actually not done directly in Tomcat, but in the environment, in Java or in the application itself.
I will try to describe some items that are actually basic, but that can serve as a start
Memory
Do performance and load tests by analyzing the system memory using some profiler like jvisualvm that come in the JDK.
Determine the memory used by after startup and after some time of use.
Set the JVM's initial memory parameters to something close to the average memory consumed, that way the application will start up faster because Java won't have to allocate memory multiple times.
Try simulating a large load on the system and see how it behaves. Of course, this varies by context, so try to guess how many users you need to serve at peak times.
For this, consider the heaviest routines in the system. Sometimes a system has a routine that, when executed, consumes a lot of resources. Ideally, such a routine should run in some kind of queue, otherwise two or three users can run it simultaneously and crash the system.
After analyzing the system under stress, set the maximum memory so that it is possible to meet the demand of users, but always being careful not to put too much memory and thus end up using disk paging, taking into account all other processes, such as databases. data, for example.
I always use the JVM parameter -XX:+HeapDumpOnOutOfMemoryError
. If an OOEM (Out Of Memory Error) occurs, this will generate an image of the memory and you can relatively easily analyze which routine or object is to blame or which is leaking memory.
Warming and health check
On the production server, it is always interesting to create a routine that runs after the application is started to warm up the instance and verify that it has been correctly initialized.
Warming is important to ensure that system performance will remain the same after a reboot, for example. It works like an elevator button that pushes itself three times automatically to ensure that the elevator always comes faster.
All joking aside, warm-up and verification should ensure that caches and objects used in key system routines are properly initialized with no issues.
Encoding and locale
Be sure to standardize the encoding and locale for both the system, JVM and tomcat, preferably to UTF-8
.
Tomcat's feature is to interpret requests without an encoding defined as ISO-8859-1
. But there is a URIEncoding
attribute that can be added to the connector in server.xml
where you can set your system default.
logs
Obviously, tomcat's log level should be much lower than in development.
While in dev the debug level can be the default, in production we use info or even warning . Each write to the log file is time consumed. One expert stated that he has seen logs being responsible for 50% of an application's resource consumption.
It's important to find a balance between logging and performance and, preferably, have some mechanism to dynamically switch the level of logs and be able to track some functionality when problems occur.
Rotate logs and avoid writing to very large and fragmented files.
Caching and Scheduling
In development, some simplified mechanism is usually used to simulate caching and task scheduling.
For production, if there is a need to cache data using some tool or library, such as ehcache or memcached , the respective configuration in Tomcat can be added for proper use.
The same goes for Quartz, for example. A production server must have its configuration parameterized according to the users' needs, which may be different from a development environment.
static resources
Depending on system demand, you may want to put CSS files, JavaScript, images, fonts and other static resources on a separate web server or CDN, thus freeing up Tomcat to process what matters.
Another alternative is to use a proxy for caching like Varnish Cache . After proper configuration, it will cache static resources on first access and subsequently serve them to the user without accessing Tomcat.
Also consider minifying, concatenating and compressing the files. Scripts and Styles can have unnecessary characters removed (minification), they can be concatenated (joining multiple files into one), and various other content can be compressed (gzip) for faster transmission to the browser.
Safety
Normal requests to the system, if it is secure, will hardly pose any risk to the server.
However, the Tomcat management tools that allow you to monitor and manage the server must be secure. To tell you the truth, I've never seen this done directly in Tomcat while these tools are exported to the web.
What is usually done is to use a proxy or firewall to block all external access that is not directed specifically to your application in the form of HTTP requests.
All access to management tools must be through a secure connection, for example using an SSH endpoint and/or a VPN connection.
The default settings should be secure enough for most cases, but someone might have changed something without realizing the risk, so it's good to look at all the security considerations in the documentation and see if there's a need to change anything.
Database
Regardless of whether you use Tomcat to connect to the database or do it in your own application, using a connection pool is essential for performance rather than creating and closing connections for each access to the database.
In addition, it is important to monitor the use of connections to detect leaks. It is extremely common for inattentive programmers to forget to close resources in a finally
block, for example and every now and then a connection is not returned to the pool . Soon the system starts to slow down, because with fewer connections, new requests start to wait in a queue to access the database. Soon the system will be completely locked, not because of a big processing, but because all the connections with the bank are blocked and the new requests to the server are eternally waiting or until some time out occurs.
As with memory, monitor connection usage during requests. If a request uses a lot of connections, it's time to refactor the system to make better use of them. Otherwise, set a limit according to the maximum expected user load at peak times.
Also consider duration of timeouts . We often put low values, for example, 30 seconds, because we think that no routine should take longer than that and if there are any leaks in connections, the system recovers in 30 seconds. But that routine always appears that takes relatively much longer than the others and we are forced to increase this value to, say, 5 minutes or something. There is no magic.
It is important not to forget that the performance of accessing the database is not only linked to the execution of a query, but the time taken to traverse the results ( ResultSet
) must be considered.
A colleague and I once managed to reduce the response time of a query screen from, amazingly, 30 minutes to 3. The order of magnitude more was caused by a bad configuration of how the JDBC driver collected the results from the database. He was doing it record by record. We did a tuning using various tests and came to the conclusion that transmitting records in blocks of 300 to 400 records had the optimal performance for that query. More than that, the gain was minimal and the memory consumption exacerbated.
Limits and timeouts
File uploads should be limited according to system needs. It makes no sense, for example, to allow 1 gigabyte uploads if the system only allows uploads of small avatars.
Long requests are a sign of a problem, but as I mentioned above about long database connections, some specific routines may require a longer timeout.
Some examples of how to configure this you can see here .
Others
Make sure the APR is installed and working. This is a native component library that improves the performance of tomcat.
I hope it's not using JSPs but some template engine like Velocity. But in case you are, my condolences first, and secondly, ensure that the JSPs of the WAR that is deployed in production are precompiled in class
files. This avoids wasting time compiling the JSPs during the user request. And don't forget that JSPs must be compiled using the exact version of the application server.
Always try to use HTTPS and keep a valid certificate and not just for login. All most modern systems support 100% HTTPS.
If you have a lot of concurrency, you may need to increase the number of threads in the server's request service pool. This article says you can scale up proportionally to the number of cores, but honestly I've never seen the need to put a value too high.
Considerations
I know most things haven't been aimed exactly at Tomcat, but I've done my best to align my server-specific and general Java web application knowledge.
I did this because in the vast majority of cases the burden of performance is on the application and security on the system/environment.