LauncherServer 代码记录
概述
LauncherServer是一个用来接收LauncherBackend发送spark app状态变化的服务
当LauncherServer收到状态变化的信息后,会根据信息类型调用用户通过ChildProcAppHandle类注册进来的listener
核心方法为:
newAppHandle 注册一个新的ChildProcAppHandle
acceptConnections 服务执行的核心方法,用于监听连接,处理请求
ServerConnection.handle 处理请求内容的实际对象和方法
代码记录
class LauncherServer implements Closeable {
private static final Logger LOG = Logger.getLogger(LauncherServer.class.getName());
private static final String THREAD_NAME_FMT = "LauncherServer-%d";
private static final long DEFAULT_CONNECT_TIMEOUT = 10000L;
/** For creating secrets used for communication with child processes. */
private static final SecureRandom RND = new SecureRandom();
private static volatile LauncherServer serverInstance; //使用单例设计模式,来保证服务只被启动一次
/**
* Creates a handle for an app to be launched. This method will start a server if one hasn't been
* started yet. The server is shared for multiple handles, and once all handles are disposed of,
* the server is shut down.
*/
/*
* 实现LauncherServer的单例对象,返回一个新的ChildProcAppHandle来使用户注册自己的listener
*/
static synchronized ChildProcAppHandle newAppHandle() throws IOException {
LauncherServer server = serverInstance != null ? serverInstance : new LauncherServer(); //单例在这里返回
server.ref();
serverInstance = server;
String secret = server.createSecret(); //创建客户端唯一身份识别
while (server.pending.containsKey(secret)) {
secret = server.createSecret();
}
return server.newAppHandle(secret); //返回客户端对应的唯一ChildProcAppHandle
}
//获取server的单例对象
static LauncherServer getServerInstance() {
return serverInstance;
}
private final AtomicLong refCount;
private final AtomicLong threadIds;
private final ConcurrentMap<String, ChildProcAppHandle> pending;
private final List<ServerConnection> clients;
private final ServerSocket server;
private final Thread serverThread;
private final ThreadFactory factory;
private final Timer timeoutTimer;
private volatile boolean running;
//server的唯一内部构造函数,保证了无法从外部被初始化
private LauncherServer() throws IOException {
this.refCount = new AtomicLong(0);
ServerSocket server = new ServerSocket(); //这些及以下代码建立了socket 监听服务端口,初始化内部线程执行核心代码
try {
server.setReuseAddress(true);
server.bind(new InetSocketAddress(InetAddress.getLoopbackAddress(), 0)); //在本地回环地址上监听随机端口
this.clients = new ArrayList<>();
this.threadIds = new AtomicLong();
this.factory = new NamedThreadFactory(THREAD_NAME_FMT);
this.pending = new ConcurrentHashMap<>();
this.timeoutTimer = new Timer("LauncherServer-TimeoutTimer", true);
this.server = server;
this.running = true;
this.serverThread = factory.newThread(this::acceptConnections); //初始化核心执行线程执行acceptConnections方法
serverThread.start(); //开始执行
} catch (IOException ioe) {
close();
throw ioe;
} catch (Exception e) {
close();
throw new IOException(e);
}
}
/**
* Creates a new app handle. The handle will wait for an incoming connection for a configurable
* amount of time, and if one doesn't arrive, it will transition to an error state.
*/
ChildProcAppHandle newAppHandle(String secret) {
ChildProcAppHandle handle = new ChildProcAppHandle(secret, this);
ChildProcAppHandle existing = pending.putIfAbsent(secret, handle);
CommandBuilderUtils.checkState(existing == null, "Multiple handles with the same secret.");
return handle;
}
@Override
public void close() throws IOException {
synchronized (this) {
if (running) {
running = false;
timeoutTimer.cancel();
server.close();
synchronized (clients) {
List<ServerConnection> copy = new ArrayList<>(clients);
clients.clear();
for (ServerConnection client : copy) {
client.close();
}
}
}
}
if (serverThread != null) {
try {
serverThread.join();
} catch (InterruptedException ie) {
// no-op
}
}
}
void ref() {
refCount.incrementAndGet();
}
void unref() {
synchronized(LauncherServer.class) {
if (refCount.decrementAndGet() == 0) {
try {
close();
} catch (IOException ioe) {
// no-op.
} finally {
serverInstance = null;
}
}
}
}
int getPort() {
return server.getLocalPort();
}
/**
* Removes the client handle from the pending list (in case it's still there), and unrefs
* the server.
*/
void unregister(ChildProcAppHandle handle) {
pending.remove(handle.getSecret());
unref();
}
//LauncherServer的核心逻辑代码,整个代码在while 中持续运行
//每次接收一个新请求的时候就建立一个clientConnection在新线程中来处理这个请求
private void acceptConnections() {
try {
while (running) {
final Socket client = server.accept(); //等待一个新请求
TimerTask timeout = new TimerTask() { //生成新请求对应的超时对象
@Override
public void run() {
LOG.warning("Timed out waiting for hello message from client.");
try {
client.close();
} catch (IOException ioe) {
// no-op.
}
}
};
ServerConnection clientConnection = new ServerConnection(client, timeout); //建立客户请求处理对象
Thread clientThread = factory.newThread(clientConnection); //建立请求处理对象对应的执行线程
synchronized (timeout) {
clientThread.start(); //执行
synchronized (clients) {
clients.add(clientConnection); //放到全局client列表中
}
long timeoutMs = getConnectionTimeout();
// 0 is used for testing to avoid issues with clock resolution / thread scheduling,
// and force an immediate timeout.
if (timeoutMs > 0) {
timeoutTimer.schedule(timeout, getConnectionTimeout()); //进行超时等待
} else {
timeout.run();
}
}
}
} catch (IOException ioe) {
if (running) {
LOG.log(Level.SEVERE, "Error in accept loop.", ioe);
}
}
}
private long getConnectionTimeout() {
String value = SparkLauncher.launcherConfig.get(SparkLauncher.CHILD_CONNECTION_TIMEOUT);
return (value != null) ? Long.parseLong(value) : DEFAULT_CONNECT_TIMEOUT;
}
//创建唯一凭证的方法
private String createSecret() {
byte[] secret = new byte[128];
RND.nextBytes(secret);
StringBuilder sb = new StringBuilder();
for (byte b : secret) {
int ival = b >= 0 ? b : Byte.MAX_VALUE - b;
if (ival < 0x10) {
sb.append("0");
}
sb.append(Integer.toHexString(ival));
}
return sb.toString();
}
//处理客户端请求的内部执行类
private class ServerConnection extends LauncherConnection {
private TimerTask timeout;
private ChildProcAppHandle handle;
//对应的构造函数,参数是客户端的socket和对应的超时任务
ServerConnection(Socket socket, TimerTask timeout) throws IOException {
super(socket);
this.timeout = timeout;
}
/*核心处理类,这个类被父类LauncherConnection中的run调用,在run中处理了socket的请求内容
*把请求消息当成参数传递给这个方法
*LauncherConnection继承了closeable和Runnable
*/
@Override
protected void handle(Message msg) throws IOException {
try {
if (msg instanceof Hello) { //判断消息是否是握手消息
timeout.cancel();
timeout = null;
Hello hello = (Hello) msg;
ChildProcAppHandle handle = pending.remove(hello.secret); //取出对应的ChildProcAppHandle
if (handle != null) {
handle.setConnection(this);
handle.setState(SparkAppHandle.State.CONNECTED); //调用setState触发用户注册的listener
this.handle = handle;
} else {
throw new IllegalArgumentException("Received Hello for unknown client.");
}
} else { //如果不是用户握手消息表明这是一个已存在的handle
if (handle == null) {
throw new IllegalArgumentException("Expected hello, got: " +
msg != null ? msg.getClass().getName() : null);
}
if (msg instanceof SetAppId) { //根据消息类型触发不同的用户listener
SetAppId set = (SetAppId) msg;
handle.setAppId(set.appId); //这里通过setAppId触发用户listener
} else if (msg instanceof SetState) {
handle.setState(((SetState)msg).state);
} else {
throw new IllegalArgumentException("Invalid message: " +
msg != null ? msg.getClass().getName() : null);
}
}
} catch (Exception e) {
LOG.log(Level.INFO, "Error handling message from client.", e);
if (timeout != null) {
timeout.cancel();
}
close();
} finally {
timeoutTimer.purge();
}
}
@Override
public void close() throws IOException {
synchronized (clients) {
clients.remove(this);
}
super.close();
if (handle != null) {
if (!handle.getState().isFinal()) {
LOG.log(Level.WARNING, "Lost connection to spark application.");
handle.setState(SparkAppHandle.State.LOST);
}
handle.disconnect();
}
}
}
}