Skip to content
Lightweight SMTP connection pool with clustering support, wait/release mechanism, connection lifecycle management, eager/lazy loading pool with load balancing and auto-expiry policy support
Java
Branch: master
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
.circleci
src
.gitignore
LICENSE-2.0.txt
NOTICE.txt
README.md
RELEASE.txt
how to release.txt
pom.xml

README.md

APACHE v2 License Latest Release Javadocs Codacy

smtp-connection-pool

smtp-connection-pool is an ultra lightweight SMTP connection pool with clustering support, claim/wait/release mechanism, connection lifecycle management, eager/lazy loading pool with auto-expiry policy support.

This library does not take care of creating or sending emails; it just pools (hot) reusable Transport instances using Session instances provided by the user.

This SMTP connection pool is used by Simple Java Mail, which offers a complete solutions to creating, converting and sending emails.

about

This library aims to improve performance for sending emails using Java Mail (now Jakarta Mail).

It represents three improvements over usual manual Session.getTransport().connect() approach:

  1. Support Transport (open) connection reuse over multiple threads
  2. Implement an SMTP connection pool so we have multiple reusable Transport connections (including lazy / eager initialization)
  3. Take performance to the next level and support clustered SMTP servers, so you can really scale up SMPT servers if your use-case requires it.

This library builds on top of clustered-object-pool.

Note: This library doesn't configure mail Session instances itself: it only manages the connection you can make with them. This library leaves it up to the user on how the connection behaves (to which server, proxy, SSL, TLS, session / connection timeouts etc). Simple Java Mail offers a complete solution for sending emails (which uses SMTP Connection Pool).

possible approaches

There are a couple of scenario's you can solve with clustered-object-pool:

  • Have 1 cluster with 1 pool of size 1. Where you have one SMTP connection, but can share/reuse it among threads.
  • Have 1 cluster with 1 pool of size n. Where multiple resources are shared/reused among threads.
  • Have 1 cluster with n pools of size 1. If you have one cluster with rotating pools to draw a shareable/reusable SMTP connection from. Useful when you want to spread load around different servers.
  • Have 1 cluster with n pools of size n. Same as above, except with multiple SMTP connections. For example multiple connections to multiple servers.
  • Have n clusters .... Same as all the above except you have dedicated clusters for different purposes. For example a cluster for handling internal mails and a cluster for outgoing mails.

To keep API simple, this library provides both a simple Connection Pool class as well as a Clustered Connection Pool class. The only difference is in the generics for key-types they pass on to the superclass.

Essential performance boost of using a connection pool

A very common scenario is to have a single connection being reused over many email-sending threads and usually this is enough. This can be achieved by having 1 cluster with 1 pool of size 1. This already gives a real boost over not using a connection pool, since threads using the same transport but each establishing a new connection each takes half of the time of sending the email itself.

Scale up performance with multple concurrent connections

The next solution satisfies most performance needs by far: having 1 cluster with 1 pool, but multiple connections. This takes the above approach to the next level by allowing multiple concurrent connections to your mail server. If your server can handle it, you really scale up on performance. Try benchmarking your server with test emails with different pool sizes to see when performance starts to degrade.

Take on the world with a cluster of mail servers

Finally, the next solution satisfies if you really need to send a lot of emails in a reasonable time. Define a cluster of several mail servers to which you can have one or multiple concurrent connections. You rarely need this kind of performance, but sending news letters or world wide updates become can benefit greatly from this.

Setup

<dependency>
	<groupId>org.simplejavamail</groupId>
	<artifactId>smtp-connection-pool</artifactId>
	<version>1.1.0</version>
</dependency>

Usage

Creating a simple SMTP connection pool

// Simple on-demand (lazy loading) connection pool with default size of 4, 
// where the connections remain open until the pool is shut down.
SmtpConnectionPool pool = new SmtpConnectionPool(new SmtpClusterConfig());

PoolableObject<Transport> pollableTransport = pool.claimResourceFromCluster(session);
// ... send the email
pollableTransport.release(); // make available in the connection pool again

The pool looks like a cluster and you still claim connections from a cluster, but for each server (backed by a Session) a new cluster is defined under the hood so effectively nothing is clustered.

Creating a completely customized clustering SMTP connection pool

Let's see what options we have:

SmtpClusterConfig smtpClusterConfig = new SmtpClusterConfig();
smtpClusterConfig.getConfigBuilder()
        .allocatorFactory(new MyCustomTransportAllocatorFactory())
        .defaultCorePoolSize(10) // eagerly start making up to 10 SMTP connections
        .defaultMaxPoolSize(10) // maximum pool size, after which claims become blocking
        // default is never-expire, this one closes connections randomly between 5 to 10 seconds after last use
        .defaultExpirationPolicy(new SpreadedTimeoutSinceLastAllocationExpirationPolicy<Transport>(5, 10, SECONDS)) 
        .cyclingStrategy(new RandomAccessCyclingStrategy()) // default is round-robin
        .claimTimeout(new Timeout(30, SECONDS)); // wait for available connection until max 30 seconds, default is indefinitely
        
SmtpConnectionPoolClustered pool = new SmtpConnectionPoolClustered(smtpClusterConfig);

New clusters and pools are created on-demand with the global defaults, based on cluster keys (for example a UUID) and pool keys (Session instances) passed to the claim invocations. You can however...

Configure different behavior for specific clusters and pools (servers)

// continuing the above code example...
UUID keyCluster1 = UUID.randomUUID();
UUID keyCluster2 = UUID.randomUUID();

Session sessionServerA = ...;
Session sessionServerB = ...;

// define different behavior only for server A in cluster 1
pool.registerResourcePool(new ResourceClusterAndPoolKey<>(keyCluster1, sessionServerA),
    new TimeoutSinceCreationExpirationPolicy<Transport>(30, SECONDS),
    4, // core pool size of eagerly opened and available connections
    10); // max pool size
You can’t perform that action at this time.