All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.bytesgo.littleproxy.server.ServerGroup Maven / Gradle / Ivy

Go to download

LittleProxy is a high performance HTTP proxy written in Java and using the Netty networking framework.

The newest version!
package com.bytesgo.littleproxy.server;

import java.nio.channels.spi.SelectorProvider;
import java.util.ArrayList;
import java.util.EnumMap;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.bytesgo.littleproxy.config.ProxyServerConfiguration;
import com.bytesgo.littleproxy.exception.UnknownTransportProtocolException;
import com.bytesgo.littleproxy.model.enums.TransportProtocol;
import com.bytesgo.littleproxy.util.NettyUdtUtil;
import com.bytesgo.littleproxy.util.ProxyUtil;

import io.netty.channel.EventLoopGroup;

/**
 * Manages thread pools for one or more proxy server instances. When servers are
 * created, they must register with the ServerGroup using
 * {@link #registerProxyServer(HttpProxyServer)}, and when they shut down, must
 * unregister with the ServerGroup using
 * {@link #unregisterProxyServer(HttpProxyServer, boolean)}.
 */
public class ServerGroup {
	private static final Logger log = LoggerFactory.getLogger(ServerGroup.class);

	/**
	 * The default number of threads to accept incoming requests from clients.
	 * (Requests are serviced by worker threads, not acceptor threads.)
	 */
	public static final int DEFAULT_INCOMING_ACCEPTOR_THREADS = 2;

	/**
	 * The default number of threads to service incoming requests from clients.
	 */
	public static final int DEFAULT_INCOMING_WORKER_THREADS = 8;

	/**
	 * The default number of threads to service outgoing requests to servers.
	 */
	public static final int DEFAULT_OUTGOING_WORKER_THREADS = 8;

	/**
	 * Global counter for the {@link #serverGroupId}.
	 */
	private static final AtomicInteger serverGroupCount = new AtomicInteger(0);

	/**
	 * A name for this ServerGroup to use in naming threads.
	 */
	private final String name;

	/**
	 * The ID of this server group. Forms part of the name of each thread created
	 * for this server group. Useful for differentiating threads when multiple proxy
	 * instances are running.
	 */
	private final int serverGroupId;

	private final int incomingAcceptorThreadSize;
	private final int incomingWorkerThreadSize;
	private final int outgoingWorkerThreadSize;

	/**
	 * List of all servers registered to use this ServerGroup. Any access to this
	 * list should be synchronized using the {@link #SERVER_REGISTRATION_LOCK}.
	 */
	public final List httpProxyServers = new ArrayList(1);

	/**
	 * A mapping of {@link TransportProtocol}s to their initialized
	 * {@link ProxyThreadPool}. Each transport uses a different thread pool, since
	 * the initialization parameters are different.
	 */
	private final EnumMap protocolThreadPools = new EnumMap(
			TransportProtocol.class);

	/**
	 * A mapping of selector providers to transport protocols. Avoids special-casing
	 * each transport protocol during transport protocol initialization.
	 */
	private static final EnumMap TRANSPORT_PROTOCOL_SELECTOR_PROVIDERS = new EnumMap(
			TransportProtocol.class);
	static {
		TRANSPORT_PROTOCOL_SELECTOR_PROVIDERS.put(TransportProtocol.TCP, SelectorProvider.provider());

		// allow the proxy to operate without UDT support. this allows clients that do
		// not use UDT to exclude the barchart
		// dependency completely.
		if (ProxyUtil.isUdtAvailable()) {
			TRANSPORT_PROTOCOL_SELECTOR_PROVIDERS.put(TransportProtocol.UDT, NettyUdtUtil.getUdtProvider());
		} else {
			log.debug("UDT provider not found on classpath. UDT transport will not be available.");
		}
	}

	/**
	 * True when this ServerGroup is stopped.
	 */
	private final AtomicBoolean stopped = new AtomicBoolean(false);

	/**
	 * Creates a new ServerGroup instance for a proxy. Threads created for this
	 * ServerGroup will have the specified ServerGroup name in the Thread name. This
	 * constructor does not actually initialize any thread pools; instead, thread
	 * pools for specific transport protocols are lazily initialized as needed.
	 *
	 * @param name                       ServerGroup name to include in thread names
	 * @param incomingAcceptorThreadSize number of acceptor threads per protocol
	 * @param incomingWorkerThreadSize   number of client-to-proxy worker threads
	 *                                   per protocol
	 * @param outgoingWorkerThreadSize   number of proxy-to-server worker threads
	 *                                   per protocol
	 */
	public ServerGroup(String name, ProxyServerConfiguration proxyServerConfiguration) {
		this.name = name;
		if (proxyServerConfiguration == null) {
			proxyServerConfiguration = new ProxyServerConfiguration();
		}
		this.serverGroupId = serverGroupCount.getAndIncrement();
		this.incomingAcceptorThreadSize = proxyServerConfiguration.getAcceptorThreadSize();
		this.incomingWorkerThreadSize = proxyServerConfiguration.getClientToProxyWorkerThreadSize();
		this.outgoingWorkerThreadSize = proxyServerConfiguration.getProxyToServerWorkerThreadSize();
	}

	/**
	 * Lock for initializing any transport protocols.
	 */
	private final Object THREAD_POOL_INIT_LOCK = new Object();

	/**
	 * Retrieves the {@link ProxyThreadPool} for the specified transport protocol.
	 * Lazily initializes the thread pools for the transport protocol if they have
	 * not yet been initialized. If the protocol has already been initialized, this
	 * method returns immediately, without synchronization. If initialization is
	 * necessary, the initialization process creates the acceptor and worker threads
	 * necessary to service requests to/from the proxy.
	 * 

* This method is thread-safe; no external locking is necessary. * * @param protocol transport protocol to retrieve thread pools for * @return thread pools for the specified transport protocol */ private ProxyThreadPool getThreadPool(TransportProtocol protocol) { // if the thread pools have not been initialized for this protocol, initialize // them if (protocolThreadPools.get(protocol) == null) { synchronized (THREAD_POOL_INIT_LOCK) { if (protocolThreadPools.get(protocol) == null) { log.debug( "Initializing thread pools for {} with {} acceptor threads, {} incoming worker threads, and {} outgoing worker threads", protocol, incomingAcceptorThreadSize, incomingWorkerThreadSize, outgoingWorkerThreadSize); SelectorProvider selectorProvider = TRANSPORT_PROTOCOL_SELECTOR_PROVIDERS.get(protocol); if (selectorProvider == null) { throw new UnknownTransportProtocolException(protocol); } ProxyThreadPool threadPool = new ProxyThreadPool(selectorProvider, incomingAcceptorThreadSize, incomingWorkerThreadSize, outgoingWorkerThreadSize, name, serverGroupId); protocolThreadPools.put(protocol, threadPool); } } } return protocolThreadPools.get(protocol); } /** * Lock controlling access to the {@link #registerProxyServer(HttpProxyServer)} * and {@link #unregisterProxyServer(HttpProxyServer, boolean)} methods. */ private final Object SERVER_REGISTRATION_LOCK = new Object(); /** * Registers the specified proxy server as a consumer of this server group. The * server group will not be shut down until the proxy unregisters itself. * * @param proxyServer proxy server instance to register */ public void registerProxyServer(HttpProxyServer proxyServer) { synchronized (SERVER_REGISTRATION_LOCK) { httpProxyServers.add(proxyServer); } } /** * Unregisters the specified proxy server from this server group. If this was * the last registered proxy server, the server group will be shut down. * * @param proxyServer proxy server instance to unregister * @param graceful when true, the server group shutdown (if necessary) will * be graceful */ public void unregisterProxyServer(HttpProxyServer proxyServer, boolean graceful) { synchronized (SERVER_REGISTRATION_LOCK) { boolean wasRegistered = httpProxyServers.remove(proxyServer); if (!wasRegistered) { log.warn( "Attempted to unregister proxy server from ServerGroup that it was not registered with. Was the proxy unregistered twice?"); } if (httpProxyServers.isEmpty()) { log.debug("Proxy server unregistered from ServerGroup. No proxy servers remain registered, so shutting down ServerGroup."); shutdown(graceful); } else { log.debug("Proxy server unregistered from ServerGroup. Not shutting down ServerGroup ({} proxy servers remain registered).", httpProxyServers.size()); } } } /** * Shuts down all event loops owned by this server group. * * @param graceful when true, event loops will "gracefully" terminate, waiting * for submitted tasks to finish */ private void shutdown(boolean graceful) { if (!stopped.compareAndSet(false, true)) { log.info("Shutdown requested, but ServerGroup is already stopped. Doing nothing."); return; } log.info("Shutting down server group event loops " + (graceful ? "(graceful)" : "(non-graceful)")); // loop through all event loops managed by this server group. this includes // acceptor and worker event loops // for both TCP and UDP transport protocols. List allEventLoopGroups = new ArrayList(); for (ProxyThreadPool threadPools : protocolThreadPools.values()) { allEventLoopGroups.addAll(threadPools.getAllEventLoops()); } for (EventLoopGroup group : allEventLoopGroups) { if (graceful) { group.shutdownGracefully(); } else { group.shutdownGracefully(0, 0, TimeUnit.SECONDS); } } if (graceful) { for (EventLoopGroup group : allEventLoopGroups) { try { group.awaitTermination(60, TimeUnit.SECONDS); } catch (InterruptedException e) { Thread.currentThread().interrupt(); log.warn("Interrupted while shutting down event loop"); } } } log.debug("Done shutting down server group"); } /** * Retrieves the client-to-proxy acceptor thread pool for the specified * protocol. Initializes the pool if it has not yet been initialized. *

* This method is thread-safe; no external locking is necessary. * * @param protocol transport protocol to retrieve the thread pool for * @return the client-to-proxy acceptor thread pool */ public EventLoopGroup getClientToProxyAcceptor(TransportProtocol protocol) { return getThreadPool(protocol).getClientToProxyAcceptor(); } /** * Retrieves the client-to-proxy acceptor worker pool for the specified * protocol. Initializes the pool if it has not yet been initialized. *

* This method is thread-safe; no external locking is necessary. * * @param protocol transport protocol to retrieve the thread pool for * @return the client-to-proxy worker thread pool */ public EventLoopGroup getClientToProxyWorker(TransportProtocol protocol) { return getThreadPool(protocol).getClientToProxyWorker(); } /** * Retrieves the proxy-to-server worker thread pool for the specified protocol. * Initializes the pool if it has not yet been initialized. *

* This method is thread-safe; no external locking is necessary. * * @param protocol transport protocol to retrieve the thread pool for * @return the proxy-to-server worker thread pool */ public EventLoopGroup getProxyToServerWorker(TransportProtocol protocol) { return getThreadPool(protocol).getProxyToServerWorker(); } /** * @return true if this ServerGroup has already been stopped */ public boolean isStopped() { return stopped.get(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy