A fourth and final Connector/J extension point I covered in my JavaOne and Silicon Valley Code Camp presentations is load-balancing strategies. This exists in order to allow you to define behavior for balancing load across multiple back-end MySQL server instances. MySQL Connector/J’s load-balancing implementation is a simple internal connection pool. What appears to your application as a single Connection object can actually have multiple physical connections to MySQL servers underneath (one per configured host/port pair). At specific points, Connector/J will re-balance and choose another host to interface with. This extension point allows you to define how Connector/J determines which host it should pick next.
Unlike the previous extension points, my demo code does not contain examples of this. In this case, though, there are some standard implementations provided with Connector/J that we can look at, instead. The two implementations shipped with Connector/J today implement a “best response time” strategy and a “random” strategy. The default behavior when using load-balanced deployments is “random”, and the Connector/J configuration properties documentation describes their use cases. Below are the actual classes which define the behavior:
These classes – and any user-implemented load-balancing strategy – implements the com.mysql.jdbc.BalanceStrategy interface. As the purpose of this extension point is to define how load is balanced across MySQL instances, there’s really only one key method you need to focus on: pickConnection(). The purpose of this method is to return a Connection (more specifically, a com.mysql.jdbc.ConnectionImpl object). Looking at the RandomLoadBalacneStrategy code, you will see the logic that’s involved.
The first parameter to pickConnection() is a LoadBalancingConnectionProxy object. This is the object that does much of the load-balancing work. It also contains a few callback methods you will want to consider:
- getGlobalBlacklist() – this method returns a Map<String, Long> of hosts that have been identified as unavailable. The String key is the host/port, while the Long is the time that the blacklist entry should expire. Inside the proxy, this global blacklist is defined as a static Map, meaning that Host X will be found in the blacklist by one Connection if another Connection object put it there after experiencing problems. Access to the static variable is synchronized, and the Map returned from this method is a local copy.
- shouldExceptionTriggerFailover() – this method takes a SQLException and determines whether such an Exception should trigger a failover. This, too, is user-configurable, although the defaults are usually sufficient for most deployments. A previous post contains detailed information on how to customize this behavior.
- addToGlobalBlacklist() – this is the method you want to call if you want to add a host to the global blacklist.
- createConnectionForHost() – this is a utility method that handles creation of a new ConnectionImpl object based on the host/port String, so that you don’t have to wire up ConnectionImpl objects directly. If you look at the method implementation, you will see the work that goes into setting up a properly-configured ConnectionImpl.
The remaining parameters are, in order:
- List<String> – a list of configured hosts involved in load-balancing
- Map<String, ConnectionImpl> – a Map of “live” connections already established, accessed through the host/port key. Thinking of this as a connection pool, these are the cached connections which can be reused if the host/port pair is chosen, instead of doing the additional work of setting up a new physical connection.
- long – an array of response times in the same order as the List<String> of configured hosts. This is used in BestResponseTimeBalanceStrategy.
- int – number of retries that should be attempted before giving up on finding a new connection.
So, what can you do with this? People frequently ask for a true round-robin load-balancer. Our experience has been that RandomBalanceStrategy is far better, but if you really need a true round-robin load-balancing algorithm, you could implement it here.