
com.facebook.swift.service.ThriftServerConfig Maven / Gradle / Ivy
Show all versions of swift-service Show documentation
/*
* Copyright (C) 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package com.facebook.swift.service;
import com.google.common.base.Optional;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import io.airlift.configuration.Config;
import io.airlift.units.DataSize;
import io.airlift.units.Duration;
import io.airlift.units.MaxDataSize;
import io.airlift.units.MinDataSize;
import java.util.Map;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import static io.airlift.units.DataSize.Unit.MEGABYTE;
public class ThriftServerConfig
{
private static final int DEFAULT_BOSS_THREAD_COUNT = 1;
private static final int DEFAULT_IO_WORKER_THREAD_COUNT = 2 * Runtime.getRuntime().availableProcessors();
private static final int DEFAULT_WORKER_THREAD_COUNT = 200;
private static final int DEFAULT_PER_CONNECTION_QUEUED_RESPONSE_LIMIT = 16;
private String bindAddress = "localhost";
private int port;
private int acceptBacklog = 1024;
private int connectionLimit;
private int maxQueuedResponsesPerConnection = DEFAULT_PER_CONNECTION_QUEUED_RESPONSE_LIMIT;
private int acceptorThreadCount = DEFAULT_BOSS_THREAD_COUNT;
private int ioThreadCount = DEFAULT_IO_WORKER_THREAD_COUNT;
private int trafficClass = 0;
private Duration idleConnectionTimeout = Duration.valueOf("60s");
private Duration taskExpirationTimeout = Duration.valueOf("5s");
private Duration queueTimeout = null;
private Optional workerThreads = Optional.absent();
private Optional maxQueuedRequests = Optional.absent();
private Optional workerExecutor = Optional.absent();
private Optional workerExecutorKey = Optional.absent();
private String transportName = "framed";
private String protocolName = "binary";
/**
* The default maximum allowable size for a single incoming thrift request or outgoing thrift
* response. A server can configure the actual maximum to be much higher (up to 0x3FFFFFFF or
* almost 1 GB). The default max could also be safely bumped up, but 64MB is chosen simply
* because it seems reasonable that if you are sending requests or responses larger than
* that, it should be a conscious decision (something you must manually configure).
*/
private DataSize maxFrameSize = new DataSize(64, MEGABYTE);
public String getBindAddress()
{
return bindAddress;
}
@Config("thrift.bind-address")
public ThriftServerConfig setBindAddress(String bindAddress)
{
this.bindAddress = bindAddress;
return this;
}
@Min(0)
@Max(65535)
public int getPort()
{
return port;
}
@Config("thrift.port")
public ThriftServerConfig setPort(int port)
{
this.port = port;
return this;
}
@MinDataSize("0B")
// 0x3FFFFFFF bytes
@MaxDataSize("1073741823B")
public DataSize getMaxFrameSize()
{
return maxFrameSize;
}
/**
* Sets a maximum frame size
* @param maxFrameSize
* @return
*/
@Config("thrift.max-frame-size")
public ThriftServerConfig setMaxFrameSize(DataSize maxFrameSize)
{
checkArgument(maxFrameSize.toBytes() <= 0x3FFFFFFF);
this.maxFrameSize = maxFrameSize;
return this;
}
/**
* Sets the number of pending connections that the {@link java.net.ServerSocket} will
* queue up before the server process can actually accept them. If your server may take a lot
* of connections in a very short interval, you'll want to set this higher to avoid rejecting
* some of the connections. Setting this to 0 will apply an implementation-specific default.
*
* The default value is 1024.
*
* Actual behavior of the socket backlog is dependent on OS and JDK implementation, and it may
* even be ignored on some systems. See JDK docs
* here
* for details.
*
* @param acceptBacklog
* @return
*/
@Config("thrift.accept-backlog")
public ThriftServerConfig setAcceptBacklog(int acceptBacklog)
{
this.acceptBacklog = acceptBacklog;
return this;
}
@Min(0)
public int getAcceptBacklog()
{
return acceptBacklog;
}
public int getAcceptorThreadCount()
{
return acceptorThreadCount;
}
@Config("thrift.acceptor-threads.count")
public ThriftServerConfig setAcceptorThreadCount(int acceptorThreadCount)
{
this.acceptorThreadCount = acceptorThreadCount;
return this;
}
public int getIoThreadCount()
{
return ioThreadCount;
}
@Config("thrift.io-threads.count")
public ThriftServerConfig setIoThreadCount(int ioThreadCount)
{
this.ioThreadCount = ioThreadCount;
return this;
}
public int getTrafficClass()
{
return trafficClass;
}
@Config("thrift.traffic-class")
public ThriftServerConfig setTrafficClass(int trafficClass)
{
this.trafficClass = trafficClass;
return this;
}
public Duration getIdleConnectionTimeout()
{
return this.idleConnectionTimeout;
}
/**
* Sets a timeout period between receiving requests from a client connection. If the timeout
* is exceeded (no complete requests have arrived from the client within the timeout), the
* server will disconnect the idle client.
*
* The default is 60s.
*
* @param idleConnectionTimeout The timeout
* @return This {@link ThriftServerConfig} instance
*/
@Config("thrift.idle-connection-timeout")
public ThriftServerConfig setIdleConnectionTimeout(Duration idleConnectionTimeout)
{
this.idleConnectionTimeout = idleConnectionTimeout;
return this;
}
public Duration getQueueTimeout()
{
return queueTimeout;
}
/**
* Sets a timeout period between receiving a request and the pulling the request off the queue.
* If the timeout expires before the request reaches the front of the queue and begins
* processing, the server will discard the request instead of processing it.
*
* @param queueTimeout The timeout
* @return This {@link ThriftServerConfig} instance
*/
@Config("thrift.queue-timeout")
public ThriftServerConfig setQueueTimeout(Duration queueTimeout)
{
this.queueTimeout = queueTimeout;
return this;
}
public Duration getTaskExpirationTimeout()
{
return taskExpirationTimeout;
}
/**
* Sets a timeout period between receiving a request and the completion of that request. If
* the timeout expires before the request reaches the front of the queue and begins processing,
* the server will discard the request instead of processing it. If the timeout expires after
* the request has started processing, the server will send an error immediately, and discard
* the result of request handling.
*
* @param taskExpirationTimeout The timeout
* @return This {@link ThriftServerConfig} instance
*/
@Config("thrift.task-expiration-timeout")
public ThriftServerConfig setTaskExpirationTimeout(Duration taskExpirationTimeout)
{
this.taskExpirationTimeout = taskExpirationTimeout;
return this;
}
@Min(0)
public int getConnectionLimit()
{
return this.connectionLimit;
}
/**
* Sets an upper bound on the number of concurrent connections the server will accept.
*
* The default is not to limit the number of connections.
*
* @param connectionLimit The maximum number of concurrent connections
* @return This {@link ThriftServerConfig} instance
*/
@Config("thrift.connection-limit")
public ThriftServerConfig setConnectionLimit(int connectionLimit)
{
this.connectionLimit = connectionLimit;
return this;
}
@Min(1)
public int getWorkerThreads()
{
return workerThreads.or(DEFAULT_WORKER_THREAD_COUNT);
}
/**
* Sets the number of worker threads that will be created for processing thrift requests after
* they have arrived. Any value passed here will be ignored if
* {@link ThriftServerConfig#setWorkerExecutor(java.util.concurrent.ExecutorService)} is called.
*
* The default value is 200.
*
* @param workerThreads Number of worker threads to use
* @return This {@link ThriftServerConfig} instance
*/
@Config("thrift.threads.max")
public ThriftServerConfig setWorkerThreads(int workerThreads)
{
this.workerThreads = Optional.of(workerThreads);
return this;
}
public String getWorkerExecutorKey()
{
return workerExecutorKey.orNull();
}
/**
* Sets the key for locating an {@link java.util.concurrent.ExecutorService} from the
* mapped executors installed by Guice modules.
*
* If you are not configuring your application using Guice, it will probably be simpler to just
* call
* {@link com.facebook.swift.service.ThriftServerConfig#setWorkerExecutor(java.util.concurrent.ExecutorService)}
* instead.
*
* Use of this method on a {@link com.facebook.swift.service.ThriftServerConfig} instance is
* incompatible with use of {@link com.facebook.swift.service.ThriftServerConfig#setWorkerExecutor(java.util.concurrent.ExecutorService)}
* or {@link com.facebook.swift.service.ThriftServerConfig#setWorkerThreads(int)}
*/
@Config("thrift.worker-executor-key")
public ThriftServerConfig setWorkerExecutorKey(String workerExecutorKey)
{
this.workerExecutorKey = Optional.fromNullable(workerExecutorKey);
return this;
}
public Integer getMaxQueuedRequests()
{
return maxQueuedRequests.orNull();
}
/**
* Sets the maximum number of received requests that will wait in the queue to be executed.
*
* After this many requests are waiting, the worker queue will start rejecting requests, which
* will cause the server to fail those requests.
*/
@Config("thrift.max-queued-requests")
public ThriftServerConfig setMaxQueuedRequests(Integer maxQueuedRequests)
{
this.maxQueuedRequests = Optional.fromNullable(maxQueuedRequests);
return this;
}
public int getMaxQueuedResponsesPerConnection()
{
return maxQueuedResponsesPerConnection;
}
/**
* Sets the maximum number of responses that may accumulate per connection before the connection
* starts blocking reads (to avoid building up limitless queued responses).
*
* This limit applies whenever either the client doesn't support receiving out-of-order
* responses.
*/
@Config("thrift.max-queued-responses-per-connection")
public ThriftServerConfig setMaxQueuedResponsesPerConnection(int maxQueuedResponsesPerConnection)
{
this.maxQueuedResponsesPerConnection = maxQueuedResponsesPerConnection;
return this;
}
/**
* Builds the {@link java.util.concurrent.ExecutorService} used for running Thrift server methods.
*
* The details of the {@link java.util.concurrent.ExecutorService} that gets built can be tweaked
* by calling any of the following (though only one of these should actually be called):
*
*
* - {@link com.facebook.swift.service.ThriftServerConfig#setWorkerThreads}
* - {@link com.facebook.swift.service.ThriftServerConfig#setWorkerExecutor}
* - {@link com.facebook.swift.service.ThriftServerConfig#setWorkerExecutorKey}
*
*
* The default behavior if none of the above were called is to synthesize a fixed-size
* {@link java.util.concurrent.ThreadPoolExecutor} using
* {@link com.facebook.swift.service.ThriftServerConfig#DEFAULT_WORKER_THREAD_COUNT}
* threads.
*/
public ExecutorService getOrBuildWorkerExecutor(Map boundWorkerExecutors)
{
if (workerExecutorKey.isPresent()) {
checkState(!workerExecutor.isPresent(),
"Worker executor key should not be set along with a specific worker executor instance");
checkState(!workerThreads.isPresent(),
"Worker executor key should not be set along with a number of worker threads");
checkState(!maxQueuedRequests.isPresent(),
"When using a custom executor, handling maximum queued requests must be done manually");
String key = workerExecutorKey.get();
checkArgument(boundWorkerExecutors.containsKey(key),
"No ExecutorService was bound to key '" + key + "'");
ExecutorService executor = boundWorkerExecutors.get(key);
checkNotNull(executor, "WorkerExecutorKey maps to null");
return executor;
}
else if (workerExecutor.isPresent()) {
checkState(!workerThreads.isPresent(),
"Worker executor should not be set along with number of worker threads");
checkState(!maxQueuedRequests.isPresent(),
"When using a custom executor, handling maximum queued requests must be done manually");
return workerExecutor.get();
}
else {
return makeDefaultWorkerExecutor();
}
}
/**
* Sets the executor that will be used to process thrift requests after they arrive. Setting
* this will override any call to
* {@link com.facebook.swift.service.ThriftServerConfig#setWorkerThreads(int)}.
*
* Use of this method on a {@link com.facebook.swift.service.ThriftServerConfig} instance is
* incompatible with use of
* {@link com.facebook.swift.service.ThriftServerConfig#setWorkerExecutorKey(String)} or
* {@link com.facebook.swift.service.ThriftServerConfig#setWorkerThreads(int)}
*
* @param workerExecutor The worker executor
* @return This {@link ThriftServerConfig} instance
*/
public ThriftServerConfig setWorkerExecutor(ExecutorService workerExecutor)
{
this.workerExecutor = Optional.of(workerExecutor);
return this;
}
private ExecutorService makeDefaultWorkerExecutor()
{
BlockingQueue queue;
if (maxQueuedRequests.isPresent()) {
// Create a limited-capacity executor that will throw RejectedExecutionException when full.
// NiftyDispatcher will handle RejectedExecutionException by sending a TApplicationException.
queue = new LinkedBlockingQueue<>(maxQueuedRequests.get());
}
else {
queue = new LinkedBlockingQueue<>();
}
return new ThreadPoolExecutor(getWorkerThreads(),
getWorkerThreads(),
0L,
TimeUnit.MILLISECONDS,
queue,
new ThreadFactoryBuilder().setNameFormat("thrift-worker-%s").build(),
new ThreadPoolExecutor.AbortPolicy());
}
/**
* Sets the name of the transport (frame codec) that this server will handle. The available
* options by default are 'unframed', 'buffered', and 'framed'. Additional modules may install
* other options. Server startup will fail if you specify an unavailable transport here.
*
* @param transportName The name of the transport
* @return This {@link ThriftServerConfig} instance
*/
@Config("thrift.transport")
public ThriftServerConfig setTransportName(String transportName)
{
this.transportName = transportName;
return this;
}
@NotNull
public String getTransportName()
{
return transportName;
}
/**
* Sets the name of the protocol that this server will speak. The available options by default
* are 'binary' and 'compact'. Additional modules may install other options. Server startup will
* fail if you specify an unavailable protocol here.
*
* @param protocolName The name of the protocol
* @return This {@link ThriftServerConfig} instance
*/
@Config("thrift.protocol")
public ThriftServerConfig setProtocolName(String protocolName)
{
this.protocolName = protocolName;
return this;
}
@NotNull
public String getProtocolName()
{
return protocolName;
}
}