hc-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From alex.x.at...@jpmorgan.com
Subject Attempt to implement HA with HttpClient
Date Wed, 18 May 2005 13:07:30 GMT
Apologies in advance if this is the incorrect mailing list.

We are trying to implement High Avaliablity with HttpClient (great package 
btw), we dont have the luxury of a load balancer to do this for now, so we 
(I) thought we could do it programmatically.


The way I thought would work, is to subclass the 
SimpleHttpConnectionManager and return a connection that succeeded. Below 
is the source for this implementation.

Everything seems to work fine, the failover succeeds and the client 
connects to the failover host. However, under heavy usage, and if the 
primary host is down, the occasional error occurs, with the exception: 


Address already in use: connect
java.net.BindException: Address already in use: connect
        at java.net.PlainSocketImpl.socketConnect(Native Method)
        at java.net.PlainSocketImpl.doConnect(PlainSocketImpl.java:305)
        at 
java.net.PlainSocketImpl.connectToAddress(PlainSocketImpl.java:171)
        at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:158)
        at java.net.Socket.connect(Socket.java:452)
        at java.net.Socket.connect(Socket.java:402)
        at java.net.Socket.<init>(Socket.java:309)
        at java.net.Socket.<init>(Socket.java:184)
        at 
org.apache.commons.httpclient.protocol.DefaultProtocolSocketFactory.createSocket(DefaultProtocolSocketFactory.java:79)
        at 
org.apache.commons.httpclient.protocol.DefaultProtocolSocketFactory.createSocket(DefaultProtocolSocketFactory.java:121)
        at 
org.apache.commons.httpclient.HttpConnection.open(HttpConnection.java:704)
        at 
org.apache.commons.httpclient.HttpMethodDirector.executeWithRetry(HttpMethodDirector.java:382)
        at 
org.apache.commons.httpclient.HttpMethodDirector.executeMethod(HttpMethodDirector.java:168)
        at 
org.apache.commons.httpclient.HttpClient.executeMethod(HttpClient.java:393)
        at 
org.apache.commons.httpclient.HttpClient.executeMethod(HttpClient.java:324)
        at 
com.jpmorgan.cove.api.http.OrderValidationImpl.validate(OrderValidationImpl.java:251)
        at 
com.jpmorgan.cove.api.http.OrderValidationImpl.validateInstruments(OrderValidationImpl.java:113)
        at 
com.jpmorgan.cove.api.http.OrderValidationImpl.validateInstrument(OrderValidationImpl.java:97)
        at 
com.jpmorgan.cove.api.test.ClientAPITest.testValidateInstrumentsAsNonBatch(ClientAPITest.java:142)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at 
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
        at 
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)



This is the source, am I doing something wrong (or everything wrong??)


/**
     * This inner class attempts to implement an HA version of the 
SimpleHttpConnectionManager.
     */
    protected final class HAHttpConnectionManager extends
        SimpleHttpConnectionManager {

        // set up the primary and failover host configurations
        private URL primaryUrl;
        private URL failoverUrl;

        private HostConfiguration primary;
        private HostConfiguration failover;

        private String user;
        private String pass;

        // used to determaine which path to return based on the this flag
        private boolean primaryIsOK;

        private String path;

        /**
         * Constructor with a list of failover hosts.
         * Note: this is a list of failover hosts only, the primary is 
passed
         * to the method to get the connection.
         * 
         * @param failOverHosts
         * @return HttpConnection if available or null if not
         */
        public HAHttpConnectionManager() {

            try {
                primaryUrl = new 
URL(ConfigHelper.getInstance().getProperty(
                        PrimaryURLKey));
                failoverUrl = new 
URL(ConfigHelper.getInstance().getProperty(
                        FailoverURLKey));

                primary = new HostConfiguration();
                failover = new HostConfiguration();

                primary.setHost(primaryUrl.getHost(), 
primaryUrl.getPort(),
                        primaryUrl.getProtocol());
                failover.setHost(failoverUrl.getHost(), 
failoverUrl.getPort(),
                        failoverUrl.getProtocol());

                user = ConfigHelper.getInstance().getProperty(USERKey);
                pass = ConfigHelper.getInstance().getProperty(PASSKey);

            } catch (Exception e) {
                _logger.warn("unable to instantiate "
                        + this.getClass().getName(), e);
                throw new RuntimeException("Error creating instance of "
                        + this.getClass().getName());
            }
        }

        /**
         * 
         */
        public HttpConnection getConnection(HostConfiguration config) {
            return getConnectionWithTimeout(config, 0L);
        }

        /**
         * 
         */
        public  HttpConnection getConnectionWithTimeout(
                HostConfiguration primary, long timeout) {
 
            HttpConnection connection = null;

            // set the flag to assume primary connection is ok
            primaryIsOK = true;

            // let the sub class create a connection for us
            connection = super.getConnectionWithTimeout(primary, timeout);

            // test the connection for validity, if primary failed start 
failover
            if (!isValidConnection(connection)) {
                // primary connection was bad, try the failover 
connections
 
                primaryIsOK = false;

                _logger.warn("primary connection failed: "
                        + primary.getHostURL());

                if (null != failover) {
                    _logger.info("trying failover host "
                            + failover.getHostURL());

                    connection = super.getConnectionWithTimeout(failover, 
timeout);
 
                    // test if the connection is valid
                    if (isValidConnection(connection)) {
                        _logger.info("failover succeeded using host: "
                                + failover.getHostURL());
                    } else {
                        _logger.warn("failover also failed to connect: "
                                + failover.getHostURL());  
                    }
                }
            }
            return connection;
        }

        /**
         * 
         * @param connection
         * @return
         */
        private boolean isValidConnection(HttpConnection connection) {
            if (null == connection)
                return false;

            String uri = connection.getHost() + ":" + 
connection.getPort();
            try {

                // try to open a connection
 
                if (connection.isOpen()) {
                    _logger.debug("connection is open so is ok: " + uri);
                    return true;
                }
                _logger.debug("testing connection: " + uri);
                connection.open();
                // success, so close a connection
                connection.close();
                // return connection is ok
                _logger.debug("connection is ok.");
                return true;
            } catch (IOException io) {
                // connection open failed
                _logger.warn("HttpConnection is bad: " + uri);
                return false;
            }
        }

        /**
         * set the auth for the given connection.
         * @param config
         */
        public AuthScope getAuthScope() {

            String host = null;
            int port = 0;

            // check which host we are using to authenticate
            if (primaryIsOK) {
                host = primary.getHost();
                port = primary.getPort();
            } else {
                host = failover.getHost();
                port = failover.getPort();
            }
            // use BASIC authentication for any realm
            return new AuthScope(host, port, AuthScope.ANY_REALM,
                    AuthPolicy.BASIC);
        }

        public Credentials getCredentials() {
            return new UsernamePasswordCredentials(user, pass);
        }

        /*
         * get the path from the url to the servlet.
         */
        public String getPath() {
            return primaryIsOK == true ? primaryUrl.getPath() : 
failoverUrl
                    .getPath();
        }

        /*
         * get the primary config.
         */
        public HostConfiguration getPrimaryHostConfiguration() {
            return primary;
        }

        public HostConfiguration getFailoverHostConfiguration() {
            return failover;
        }
 
        public HostConfiguration getHostConfiguration() {
            return primaryIsOK == true ? primary : failover;
        }
    }
This communication is for informational purposes only. It is not intended
as an offer or solicitation for the purchase or sale of any financial
instrument or as an official confirmation of any transaction. All market prices,
data and other information are not warranted as to completeness or accuracy and
are subject to change without notice. Any comments or statements made herein 
do not necessarily reflect those of JPMorgan Chase & Co., its subsidiaries 
and affiliates


Mime
  • Unnamed multipart/alternative (inline, None, 0 bytes)
View raw message