package com.xiaomi.infra.chronos; import java.io.IOException; import java.net.InetSocketAddress; import java.util.Properties; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.thrift.TProcessor; import org.apache.thrift.protocol.TBinaryProtocol; import org.apache.thrift.server.TServer; import org.apache.thrift.server.TThreadPoolServer; import org.apache.thrift.server.TThreadPoolServer.Args; import org.apache.thrift.transport.TServerSocket; import org.apache.thrift.transport.TTransportException; import org.apache.thrift.protocol.TBinaryProtocol.Factory; import com.xiaomi.infra.chronos.exception.FatalChronosException; import com.xiaomi.infra.chronos.exception.ChronosException; import com.xiaomi.infra.chronos.generated.ChronosService; import com.xiaomi.infra.chronos.zookeeper.FailoverServer; /** * The thrift server for clients to get the precise auto-increasing timestamp. It relies on * ZooKeeper for active/backup servers switching and the max allocated timestamp is persistent in * ZooKeeper. * * @see ChronosServerWatcher */ public class ChronosServer extends FailoverServer { private static final Log LOG = LogFactory.getLog(ChronosServer.class); public static final String CLUSTER_NAME = "clusterName"; public static final String MAX_THREAD = "maxThread"; public static final String ZK_ADVANCE_TIMESTAMP = "zkAdvanceTimestamp"; private final Properties properties; private final ChronosServerWatcher chronosServerWatcher; private ChronosImplement chronosImplement; private TServer thriftServer; /** * Construct ChronosServer with ChronosServerWatcher and properties. * * @param chronosServerWatcher the ChronosServerWatcher * @param properties the properties of ChronosServer * @throws TTransportException when error to init thrift server * @throws ChronosException when error to construct ChronosServerWatcher */ public ChronosServer(final ChronosServerWatcher chronosServerWatcher, Properties properties) throws TTransportException, ChronosException { super(chronosServerWatcher); this.chronosServerWatcher = chronosServerWatcher; this.properties = properties; LOG.info("Init thrift server in " + chronosServerWatcher.getHostPort().getHostPort()); initThriftServer(); } /** * Construct ChronosServer with ChronosServerWatcher. * * @throws TTransportException when error to init thrift server * @throws ChronosException when error to construct ChronosServerWatcher */ public ChronosServer(final ChronosServerWatcher chronosServerWatcher) throws TTransportException, ChronosException { this(chronosServerWatcher, chronosServerWatcher.getProperties()); } /** * Construct ChronosServer with properties. * * @param properties the properties of ChronosServer * @throws TTransportException when error to init thrift server * @throws ChronosException when error to construct ChronosServerWatcher * @throws IOException when whe error to construct ChronosServerWatcher */ public ChronosServer(Properties properties) throws TTransportException, ChronosException, IOException { this(new ChronosServerWatcher(properties), properties); } /** * Initialize Thrift server of ChronosServer. * * @throws TTransportException when error to initialize thrift server * @throws FatalChronosException when set a smaller timestamp in ZooKeeper * @throws ChronosException when error to set timestamp in ZooKeeper */ private void initThriftServer() throws TTransportException, FatalChronosException, ChronosException { int maxThread = Integer.parseInt(properties.getProperty(MAX_THREAD, String.valueOf(Integer.MAX_VALUE))); String serverHost = properties.getProperty(FailoverServer.SERVER_HOST); int serverPort = Integer.parseInt(properties.getProperty(FailoverServer.SERVER_PORT)); TServerSocket serverTransport = new TServerSocket(new InetSocketAddress(serverHost, serverPort)); Factory proFactory = new TBinaryProtocol.Factory(); chronosImplement = new ChronosImplement(properties, chronosServerWatcher); TProcessor processor = new ChronosService.Processor(chronosImplement); Args rpcArgs = new Args(serverTransport); rpcArgs.processor(processor); rpcArgs.protocolFactory(proFactory); rpcArgs.maxWorkerThreads(maxThread); thriftServer = new TThreadPoolServer(rpcArgs); } /** * Initialize persistent timestamp and start to serve as active master. */ @Override public void doAsActiveServer() { try { LOG.info("As active master, init timestamp from ZooKeeper"); chronosImplement.initTimestamp(); } catch (Exception e) { LOG.fatal("Exception to init timestamp from ZooKeeper, exit immediately"); System.exit(0); } chronosServerWatcher.setBeenActiveMaster(true); LOG.info("Start to accept thrift request"); startThriftServer(); } /** * Stop thrift server of ChronosServer. */ public void stopThriftServer() { if (thriftServer != null) { thriftServer.stop(); } } /** * Start thrift server of ChronosServer. */ public void startThriftServer() { if (thriftServer != null) { thriftServer.serve(); } } /** * The main entrance of ChronosServer to start the service. * * @param args will ignore all the command-line arguments */ public static void main(String[] args) { Properties properties = new Properties(); LOG.info("Load chronos.cfg configuration from class path"); try { properties.load(ChronosServer.class.getClassLoader().getResourceAsStream("chronos.cfg")); properties.setProperty(FailoverServer.BASE_ZNODE, "/chronos" + "/" + properties.getProperty(CLUSTER_NAME)); } catch (IOException e) { LOG.fatal("Error to load chronos.cfg configuration, exit immediately", e); System.exit(0); } ChronosServer chronosServer = null; LOG.info("Init chronos server and connect ZooKeeper"); try { chronosServer = new ChronosServer(properties); } catch (Exception e) { LOG.fatal("Exception to init chronos server, exit immediately", e); System.exit(0); } chronosServer.run(); } }