org.jgroups.blocks.ConnectionTableNIO Maven / Gradle / Ivy
Go to download
This artifact provides a single jar that contains all classes required to use remote Jakarta Enterprise Beans and Jakarta Messaging, including
all dependencies. It is intended for use by those not using maven, maven users should just import the Jakarta Enterprise Beans and
Jakarta Messaging BOM's instead (shaded JAR's cause lots of problems with maven, as it is very easy to inadvertently end up
with different versions on classes on the class path).
package org.jgroups.blocks;
import org.jgroups.logging.Log;
import org.jgroups.Address;
import org.jgroups.Global;
import org.jgroups.stack.IpAddress;
import org.jgroups.util.ShutdownRejectedExecutionHandler;
import java.io.IOException;
import java.net.*;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.nio.channels.spi.SelectorProvider;
import java.util.*;
import java.util.concurrent.*;
/**
* Manages incoming and outgoing TCP connections. For each outgoing message to destination P, if there
* is not yet a connection for P, one will be created. Subsequent outgoing messages will use this
* connection. For incoming messages, one server socket is created at startup. For each new incoming
* client connecting, a new thread from a thread pool is allocated and listens for incoming messages
* until the socket is closed by the peer.
Sockets/threads with no activity will be killed
* after some time.
*
* Incoming messages from any of the sockets can be received by setting the message listener.
*
* @author Bela Ban, Scott Marlow, Alex Fu
*/
public class ConnectionTableNIO extends BasicConnectionTable implements Runnable {
private ServerSocketChannel m_serverSocketChannel;
private Selector m_acceptSelector;
private WriteHandler[] m_writeHandlers;
private int m_nextWriteHandler = 0;
private final Object m_lockNextWriteHandler = new Object();
private ReadHandler[] m_readHandlers;
private int m_nextReadHandler = 0;
private final Object m_lockNextReadHandler = new Object();
// thread pool for processing read requests
private Executor m_requestProcessors;
private volatile boolean serverStopping=false;
private final List m_backGroundThreads = new LinkedList<>(); // Collection of all created threads
private int m_reader_threads = 3;
private int m_writer_threads = 3;
private int m_processor_threads = 5; // PooledExecutor.createThreads()
private int m_processor_minThreads = 5; // PooledExecutor.setMinimumPoolSize()
private int m_processor_maxThreads = 5; // PooledExecutor.setMaxThreads()
private int m_processor_queueSize=100; // Number of queued requests that can be pending waiting
// for a background thread to run the request.
private long m_processor_keepAliveTime = Long.MAX_VALUE; // PooledExecutor.setKeepAliveTime( milliseconds);
// negative value used to mean to wait forever, instead set to Long.MAX_VALUE to wait forever
/**
* @param srv_port
* @throws Exception
*/
public ConnectionTableNIO(int srv_port) throws Exception {
this.srv_port=srv_port;
start();
}
/**
* @param srv_port
* @param reaper_interval
* @param conn_expire_time
* @throws Exception
*/
public ConnectionTableNIO(int srv_port, long reaper_interval,
long conn_expire_time) throws Exception {
this.srv_port=srv_port;
this.reaper_interval=reaper_interval;
this.conn_expire_time=conn_expire_time;
start();
}
/**
* @param r
* @param bind_addr
* @param external_addr
* @param srv_port
* @param max_port
* @throws Exception
*/
public ConnectionTableNIO(Receiver r, InetAddress bind_addr, InetAddress external_addr, int external_port, int srv_port, int max_port)
throws Exception
{
setReceiver(r);
this.external_addr=external_addr;
this.external_port=external_port;
this.bind_addr=bind_addr;
this.srv_port=srv_port;
this.max_port=max_port;
use_reaper=true;
start();
}
public ConnectionTableNIO(Receiver r, InetAddress bind_addr, InetAddress external_addr, int external_port,
int srv_port, int max_port, boolean doStart)
throws Exception
{
setReceiver(r);
this.external_addr=external_addr;
this.external_port=external_port;
this.bind_addr=bind_addr;
this.srv_port=srv_port;
this.max_port=max_port;
use_reaper=true;
if(doStart)
start();
}
/**
* @param r
* @param bind_addr
* @param external_addr
* @param srv_port
* @param max_port
* @param reaper_interval
* @param conn_expire_time
* @throws Exception
*/
public ConnectionTableNIO(Receiver r, InetAddress bind_addr, InetAddress external_addr, int external_port,
int srv_port, int max_port,
long reaper_interval, long conn_expire_time
) throws Exception
{
setReceiver(r);
this.bind_addr=bind_addr;
this.external_addr=external_addr;
this.external_port=external_port;
this.srv_port=srv_port;
this.max_port=max_port;
this.reaper_interval=reaper_interval;
this.conn_expire_time=conn_expire_time;
use_reaper=true;
start();
}
public ConnectionTableNIO(Receiver r, InetAddress bind_addr, InetAddress external_addr, int external_port,
int srv_port, int max_port,
long reaper_interval, long conn_expire_time, boolean doStart
) throws Exception
{
setReceiver(r);
this.bind_addr=bind_addr;
this.external_addr=external_addr;
this.external_port=external_port;
this.srv_port=srv_port;
this.max_port=max_port;
this.reaper_interval=reaper_interval;
this.conn_expire_time=conn_expire_time;
use_reaper=true;
if(doStart)
start();
}
public int getReaderThreads() { return m_reader_threads; }
public void setReaderThreads(int m_reader_threads) {
this.m_reader_threads=m_reader_threads;
}
public int getWriterThreads() { return m_writer_threads; }
public void setWriterThreads(int m_writer_threads) {
this.m_writer_threads=m_writer_threads;
}
public int getProcessorThreads() { return m_processor_threads; }
public void setProcessorThreads(int m_processor_threads) {
this.m_processor_threads=m_processor_threads;
}
public int getProcessorMinThreads() { return m_processor_minThreads;}
public void setProcessorMinThreads(int m_processor_minThreads) {
this.m_processor_minThreads=m_processor_minThreads;
}
public int getProcessorMaxThreads() { return m_processor_maxThreads;}
public void setProcessorMaxThreads(int m_processor_maxThreads) {
this.m_processor_maxThreads=m_processor_maxThreads;
}
public int getProcessorQueueSize() { return m_processor_queueSize; }
public void setProcessorQueueSize(int m_processor_queueSize) {
this.m_processor_queueSize=m_processor_queueSize;
}
public long getProcessorKeepAliveTime() { return m_processor_keepAliveTime; }
public void setProcessorKeepAliveTime(long m_processor_keepAliveTime) {
this.m_processor_keepAliveTime=m_processor_keepAliveTime;
}
/**
* Try to obtain correct Connection (or create one if not yet existent)
*/
BasicConnectionTable.Connection getConnection(Address dest) throws Exception
{
Connection conn;
SocketChannel sock_ch;
synchronized (conns)
{
conn = (Connection) conns.get(dest);
if (conn == null)
{
InetSocketAddress destAddress = new InetSocketAddress(((IpAddress) dest).getIpAddress(),
((IpAddress) dest).getPort());
sock_ch = SocketChannel.open(destAddress);
sock_ch.socket().setTcpNoDelay(tcp_nodelay);
conn = new Connection(sock_ch, dest);
conn.sendLocalAddress(local_addr);
// This outbound connection is ready
sock_ch.configureBlocking(false);
try
{
if (log.isTraceEnabled())
log.trace("About to change new connection send buff size from " + sock_ch.socket().getSendBufferSize() + " bytes");
sock_ch.socket().setSendBufferSize(send_buf_size);
if (log.isTraceEnabled())
log.trace("Changed new connection send buff size to " + sock_ch.socket().getSendBufferSize() + " bytes");
}
catch (IllegalArgumentException ex)
{
if (log.isErrorEnabled()) log.error("exception setting send buffer size to " +
send_buf_size + " bytes: " + ex);
}
try
{
if (log.isTraceEnabled())
log.trace("About to change new connection receive buff size from " + sock_ch.socket().getReceiveBufferSize() + " bytes");
sock_ch.socket().setReceiveBufferSize(recv_buf_size);
if (log.isTraceEnabled())
log.trace("Changed new connection receive buff size to " + sock_ch.socket().getReceiveBufferSize() + " bytes");
}
catch (IllegalArgumentException ex)
{
if (log.isErrorEnabled()) log.error("exception setting receive buffer size to " +
send_buf_size + " bytes: " + ex);
}
int idx;
synchronized (m_lockNextWriteHandler)
{
idx = m_nextWriteHandler = (m_nextWriteHandler + 1) % m_writeHandlers.length;
}
conn.setupWriteHandler(m_writeHandlers[idx]);
// Put the new connection to the queue
try
{
synchronized (m_lockNextReadHandler)
{
idx = m_nextReadHandler = (m_nextReadHandler + 1) % m_readHandlers.length;
}
m_readHandlers[idx].add(conn);
} catch (InterruptedException e)
{
if (log.isWarnEnabled())
log.warn("Thread (" +Thread.currentThread().getName() + ") was interrupted, closing connection", e);
// What can we do? Remove it from table then.
conn.destroy();
throw e;
}
// Add connection to table
addConnection(dest, conn);
notifyConnectionOpened(dest);
if (log.isTraceEnabled()) log.trace("created socket to " + dest);
}
return conn;
}
}
public final void start() throws Exception {
super.start();
init();
srv_sock=createServerSocket(srv_port, max_port);
if (external_addr!=null) {
local_addr=new IpAddress(external_addr, external_port == 0? srv_sock.getLocalPort() : external_port);
}
else if (bind_addr != null)
local_addr=new IpAddress(bind_addr, srv_sock.getLocalPort());
else
local_addr=new IpAddress(srv_sock.getLocalPort());
if(log.isDebugEnabled()) log.debug("server socket created on " + local_addr);
// Roland Kurmann 4/7/2003, put in thread_group -- removed bela Nov 2012
acceptor=getThreadFactory().newThread(this, "ConnectionTable.AcceptorThread");
acceptor.setDaemon(true);
acceptor.start();
m_backGroundThreads.add(acceptor);
// start the connection reaper - will periodically remove unused connections
if(use_reaper && reaper == null) {
reaper=new Reaper();
reaper.start();
}
}
protected void init()
throws Exception
{
// use directExector if max thread pool size is less than or equal to zero.
if(getProcessorMaxThreads() <= 0) {
m_requestProcessors = new Executor() {
public void execute(Runnable command) {
command.run();
}
};
}
else
{
// Create worker thread pool for processing incoming buffers
ThreadPoolExecutor requestProcessors = new ThreadPoolExecutor(getProcessorMinThreads(), getProcessorMaxThreads(),
getProcessorKeepAliveTime(), TimeUnit.MILLISECONDS,
new LinkedBlockingQueue(getProcessorQueueSize()));
requestProcessors.setThreadFactory(new ThreadFactory() {
public Thread newThread(Runnable runnable) {
Thread new_thread=new Thread(runnable, "ConnectionTableNIO.Thread");
new_thread.setDaemon(true);
m_backGroundThreads.add(new_thread);
return new_thread;
}
});
requestProcessors.setRejectedExecutionHandler(new ShutdownRejectedExecutionHandler(requestProcessors.getRejectedExecutionHandler()));
m_requestProcessors = requestProcessors;
}
m_writeHandlers = WriteHandler.create(getThreadFactory(),getWriterThreads(), m_backGroundThreads, log);
m_readHandlers = ReadHandler.create(getThreadFactory(),getReaderThreads(), this, m_backGroundThreads, log);
}
/**
* Closes all open sockets, the server socket and all threads waiting for incoming messages
*/
public void stop()
{
super.stop();
serverStopping = true;
if(reaper != null)
reaper.stop();
// Stop the main selector
if(m_acceptSelector != null)
m_acceptSelector.wakeup();
// Stop selector threads
if(m_readHandlers != null)
{
for (int i = 0; i < m_readHandlers.length; i++)
{
try
{
m_readHandlers[i].add(new Shutdown());
} catch (InterruptedException e)
{
log.error("Thread ("+Thread.currentThread().getName() +") was interrupted, failed to shutdown selector", e);
}
}
}
if(m_writeHandlers != null)
{
for (int i = 0; i < m_writeHandlers.length; i++)
{
try
{
m_writeHandlers[i].queue.put(new Shutdown());
m_writeHandlers[i].selector.wakeup();
} catch (InterruptedException e)
{
log.error("Thread ("+Thread.currentThread().getName() +") was interrupted, failed to shutdown selector", e);
}
}
}
// Stop the callback thread pool
if(m_requestProcessors instanceof ThreadPoolExecutor)
((ThreadPoolExecutor)m_requestProcessors).shutdownNow();
if(m_requestProcessors instanceof ThreadPoolExecutor){
try{
((ThreadPoolExecutor) m_requestProcessors).awaitTermination(Global.THREADPOOL_SHUTDOWN_WAIT_TIME,
TimeUnit.MILLISECONDS);
}catch(InterruptedException e){
}
}
// then close the connections
synchronized(conns) {
Iterator it=conns.values().iterator();
while(it.hasNext()) {
Connection conn=(Connection)it.next();
conn.destroy();
}
conns.clear();
}
while(!m_backGroundThreads.isEmpty()) {
Thread t =m_backGroundThreads.remove(0);
try {
t.join();
} catch(InterruptedException e) {
log.error("Thread ("+Thread.currentThread().getName() +") was interrupted while waiting on thread " + t.getName() + " to finish.");
}
}
m_backGroundThreads.clear();
}
/**
* Acceptor thread. Continuously accept new connections and assign readhandler/writehandler
* to them.
*/
public void run() {
Connection conn;
while(m_serverSocketChannel.isOpen() && !serverStopping) {
int num;
try {
num=m_acceptSelector.select();
}
catch(IOException e) {
if(log.isWarnEnabled())
log.warn("Select operation on listening socket failed", e);
continue; // Give up this time
}
if(num > 0) {
Set readyKeys=m_acceptSelector.selectedKeys();
for(Iterator i=readyKeys.iterator(); i.hasNext();) {
SelectionKey key=i.next();
i.remove();
// We only deal with new incoming connections
ServerSocketChannel readyChannel=(ServerSocketChannel)key.channel();
SocketChannel client_sock_ch;
try {
client_sock_ch=readyChannel.accept();
}
catch(IOException e) {
if(log.isWarnEnabled())
log.warn("Attempt to accept new connection from listening socket failed", e);
// Give up this connection
continue;
}
if(log.isTraceEnabled())
log.trace("accepted connection, client_sock=" + client_sock_ch.socket());
try {
client_sock_ch.socket().setSendBufferSize(send_buf_size);
}
catch(IllegalArgumentException ex) {
if(log.isErrorEnabled()) log.error("exception setting send buffer size to " + send_buf_size + " bytes: ", ex);
}
catch(SocketException e) {
if(log.isErrorEnabled()) log.error("exception setting send buffer size to " + send_buf_size + " bytes: ", e);
}
try {
client_sock_ch.socket().setReceiveBufferSize(recv_buf_size);
}
catch(IllegalArgumentException ex) {
if(log.isErrorEnabled()) log.error("exception setting receive buffer size to " + send_buf_size + " bytes: ", ex);
}
catch(SocketException e) {
if(log.isErrorEnabled()) log.error("exception setting receive buffer size to " + recv_buf_size + " bytes: ", e);
}
conn=new Connection(client_sock_ch, null);
try {
Address peer_addr=conn.readPeerAddress(client_sock_ch.socket());
conn.peer_addr=peer_addr;
synchronized(conns) {
Connection tmp=(Connection)conns.get(peer_addr);
if(tmp != null) {
if(peer_addr.compareTo(local_addr) > 0) {
if(log.isTraceEnabled())
log.trace("peer's address (" + peer_addr + ") is greater than our local address (" +
local_addr + "), replacing our existing connection");
// peer's address is greater, add peer's connection to ConnectionTable, destroy existing connection
addConnection(peer_addr, conn);
tmp.destroy();
notifyConnectionOpened(peer_addr);
}
else {
if(log.isTraceEnabled())
log.trace("peer's address (" + peer_addr + ") is smaller than our local address (" +
local_addr + "), rejecting peer connection request");
conn.destroy();
continue;
}
}
else {
addConnection(peer_addr, conn);
}
}
notifyConnectionOpened(peer_addr);
client_sock_ch.configureBlocking(false);
}
catch(IOException e) {
if(log.isWarnEnabled())
log.warn("Attempt to configure non-blocking mode failed", e);
conn.destroy();
continue;
}
catch(Exception e) {
if(log.isWarnEnabled())
log.warn("Attempt to handshake with other peer failed", e);
conn.destroy();
continue;
}
int idx;
synchronized(m_lockNextWriteHandler) {
idx=m_nextWriteHandler=(m_nextWriteHandler + 1) % m_writeHandlers.length;
}
conn.setupWriteHandler(m_writeHandlers[idx]);
try {
synchronized(m_lockNextReadHandler) {
idx=m_nextReadHandler=(m_nextReadHandler + 1) % m_readHandlers.length;
}
m_readHandlers[idx].add(conn);
}
catch(InterruptedException e) {
if(log.isWarnEnabled())
log.warn("Attempt to configure read handler for accepted connection failed", e);
// close connection
conn.destroy();
}
} // end of iteration
} // end of selected key > 0
} // end of thread
if(m_serverSocketChannel.isOpen()) {
try {
m_serverSocketChannel.close();
}
catch(Exception e) {
log.error("exception closing server listening socket", e);
}
}
if(log.isTraceEnabled())
log.trace("acceptor thread terminated");
}
/**
* Finds first available port starting at start_port and returns server socket. Sets srv_port
*/
protected ServerSocket createServerSocket(int start_port, int end_port) throws Exception
{
this.m_acceptSelector = Selector.open();
m_serverSocketChannel = ServerSocketChannel.open();
m_serverSocketChannel.configureBlocking(false);
while (true)
{
try
{
SocketAddress sockAddr;
if (bind_addr == null)
{
sockAddr=new InetSocketAddress(start_port);
m_serverSocketChannel.socket().bind(sockAddr);
}
else
{
sockAddr=new InetSocketAddress(bind_addr, start_port);
m_serverSocketChannel.socket().bind(sockAddr, backlog);
}
}
catch (BindException bind_ex)
{
if (start_port == end_port)
throw (BindException) ((new BindException("No available port to bind to (start_port=" + start_port + ")")).initCause(bind_ex));
start_port++;
continue;
}
catch (SocketException bind_ex)
{
if (start_port == end_port)
throw (BindException) ((new BindException("No available port to bind to (start_port=" + start_port + ")")).initCause(bind_ex));
start_port++;
continue;
}
catch (IOException io_ex)
{
log.error("Attempt to bind serversocket failed, port="+start_port+", bind addr=" + bind_addr ,io_ex);
throw io_ex;
}
srv_port = start_port;
break;
}
m_serverSocketChannel.register(this.m_acceptSelector, SelectionKey.OP_ACCEPT);
return m_serverSocketChannel.socket();
}
protected void runRequest(Address addr, ByteBuffer buf) throws InterruptedException {
m_requestProcessors.execute(new ExecuteTask(addr, buf));
}
// Represents shutdown
private static class Shutdown {
}
// ReadHandler has selector to deal with read, it runs in seperated thread
private static class ReadHandler implements Runnable {
private final Selector selector= initHandler();
private final LinkedBlockingQueue
© 2015 - 2025 Weber Informatics LLC | Privacy Policy