remove LocalTermServer

This commit is contained in:
gongdewei
2020-09-11 11:56:50 +08:00
parent aa7eb9b45b
commit b9ce500a97
9 changed files with 142 additions and 388 deletions
@@ -12,13 +12,18 @@ import com.alibaba.arthas.channel.proto.RegisterResult;
import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
import io.grpc.stub.StreamObserver;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.util.concurrent.DefaultThreadFactory;
import io.netty.util.concurrent.GlobalEventExecutor;
import io.netty.util.concurrent.Promise;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
/**
@@ -40,15 +45,30 @@ public class ChannelClient {
private ScheduledFuture<?> reconnectFuture;
private String channelServerAddress;
private int reconnectDelay = 5000;
private EventLoopGroup group;
public ChannelClient(String host, int port) {
this.host = host;
this.port = port;
this.channelServerAddress = host + ":" + port;
init();
}
public ChannelClient(String channelServer) {
setServerAddress(channelServer);
init();
}
private void init() {
group = new NioEventLoopGroup(1, new DefaultThreadFactory("arthas-ChannelWebsocketClient", true));
executorService = Executors.newScheduledThreadPool(1, new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
final Thread t = new Thread(r, "arthas-channel-client");
t.setDaemon(true);
return t;
}
});
}
private void setServerAddress(String channelServer) {
@@ -71,6 +91,7 @@ public class ChannelClient {
}
public void start() {
isError = true;
try {
connect();
@@ -98,6 +119,12 @@ public class ChannelClient {
//ignore ex
}
}
if (executorService != null) {
executorService.shutdown();
}
if (group != null) {
group.shutdownGracefully();
}
}
private void connect() throws Exception {
@@ -324,10 +351,6 @@ public class ChannelClient {
this.requestListener = requestListener;
}
public void setExecutorService(ScheduledExecutorService executorService) {
this.executorService = executorService;
}
public String getChannelServerAddress() {
return channelServerAddress;
}
@@ -344,6 +367,14 @@ public class ChannelClient {
this.reconnectDelay = reconnectDelay;
}
public ScheduledExecutorService getExecutorService() {
return executorService;
}
public EventLoopGroup getGroup() {
return group;
}
public interface RequestListener {
void onRequest(ActionRequest request);
}
@@ -20,7 +20,6 @@ public class ChannelClientTest {
final ChannelClient channelClient = new ChannelClient("localhost:7700");
channelClient.setAgentInfoService(new TestAgentInfoServiceImpl());
ScheduledExecutorService executorService = getExecutorService();
channelClient.setExecutorService(executorService);
channelClient.setRequestListener(new RequestHandler(channelClient, executorService));
channelClient.start();
@@ -28,7 +28,6 @@ import com.taobao.arthas.core.command.model.WelcomeModel;
import com.taobao.arthas.core.distribution.PackingResultDistributor;
import com.taobao.arthas.core.distribution.ResultDistributor;
import com.taobao.arthas.core.distribution.impl.PackingResultDistributorImpl;
import com.taobao.arthas.core.server.ArthasBootstrap;
import com.taobao.arthas.core.shell.cli.CliToken;
import com.taobao.arthas.core.shell.cli.CliTokens;
import com.taobao.arthas.core.shell.cli.Completion;
@@ -42,13 +41,13 @@ import com.taobao.arthas.core.shell.system.JobListener;
import com.taobao.arthas.core.shell.system.impl.InternalCommandManager;
import com.taobao.arthas.core.shell.term.SignalHandler;
import com.taobao.arthas.core.shell.term.Term;
import com.taobao.arthas.core.shell.term.impl.LocalTermServer;
import com.taobao.arthas.core.shell.term.impl.http.api.ApiState;
import com.taobao.arthas.core.util.ArthasBanner;
import com.taobao.arthas.core.util.DateUtils;
import com.taobao.arthas.core.util.StringUtils;
import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;
import io.netty.channel.EventLoopGroup;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;
@@ -76,6 +75,7 @@ public class ChannelRequestHandler implements ChannelClient.RequestListener {
public static final int DEFAULT_EXEC_TIMEOUT = 30000;
private final ScheduledExecutorService executorService;
private EventLoopGroup group;
private ChannelClient channelClient;
private final SessionManager sessionManager;
private final HistoryManager historyManager;
@@ -92,14 +92,8 @@ public class ChannelRequestHandler implements ChannelClient.RequestListener {
commandManager = this.sessionManager.getCommandManager();
jobController = this.sessionManager.getJobController();
executorService = Executors.newScheduledThreadPool(1, new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
final Thread t = new Thread(r, "arthas-command-execute");
t.setDaemon(true);
return t;
}
});
group = channelClient.getGroup();
executorService = channelClient.getExecutorService();
}
@Override
@@ -189,8 +183,8 @@ public class ChannelRequestHandler implements ChannelClient.RequestListener {
final BlockingQueue<ConsoleData> queue = new LinkedBlockingQueue<ConsoleData>(1000);
LocalTermServer localTermServer = ArthasBootstrap.getInstance().getLocalTermServer();
Channel clientChannel = localTermServer.connect(new Consumer<TextWebSocketFrame>() {
LocalWebsocketClient websocketClient = new LocalWebsocketClient();
Channel clientChannel = websocketClient.connectLocalServer(group, new Consumer<TextWebSocketFrame>() {
@Override
public void accept(TextWebSocketFrame frame) {
try {
@@ -0,0 +1,100 @@
package com.taobao.arthas.core.channel;
import com.alibaba.arthas.deps.org.slf4j.Logger;
import com.alibaba.arthas.deps.org.slf4j.LoggerFactory;
import com.alibaba.arthas.tunnel.client.LocalFrameHandler;
import com.taobao.arthas.common.ArthasConstants;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.ChannelPromise;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.local.LocalAddress;
import io.netty.channel.local.LocalChannel;
import io.netty.handler.codec.http.DefaultHttpHeaders;
import io.netty.handler.codec.http.HttpClientCodec;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.handler.codec.http.websocketx.WebSocketClientHandshaker;
import io.netty.handler.codec.http.websocketx.WebSocketClientHandshakerFactory;
import io.netty.handler.codec.http.websocketx.WebSocketClientProtocolHandler;
import io.netty.handler.codec.http.websocketx.WebSocketVersion;
import io.netty.util.concurrent.GenericFutureListener;
import io.termd.core.function.Consumer;
import java.net.URI;
public class LocalWebsocketClient {
private static final Logger logger = LoggerFactory.getLogger(LocalWebsocketClient.class);
public Channel connectLocalServer(EventLoopGroup group, final Consumer<TextWebSocketFrame> clientHandler) throws Exception {
try {
logger.info("connecting to local arthas server ..");
// 入参URI实际无意义,只为了程序不出错
WebSocketClientHandshaker newHandshaker = WebSocketClientHandshakerFactory.newHandshaker(new URI("ws://127.0.0.1:8563/ws"),
WebSocketVersion.V13, null, true, new DefaultHttpHeaders());
final WebSocketClientProtocolHandler websocketClientHandler = new WebSocketClientProtocolHandler(
newHandshaker);
final LocalFrameHandler localFrameHandler = new LocalFrameHandler();
Bootstrap b = new Bootstrap();
b.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000);
b.group(group).channel(LocalChannel.class)
.handler(new ChannelInitializer<LocalChannel>() {
@Override
protected void initChannel(LocalChannel ch) {
ChannelPipeline p = ch.pipeline();
p.addLast(new HttpClientCodec(), new HttpObjectAggregator(8192), websocketClientHandler,
localFrameHandler);
}
});
LocalAddress localAddress = new LocalAddress(ArthasConstants.NETTY_LOCAL_ADDRESS);
Channel localChannel = b.connect(localAddress).sync().channel();
// Channel localChannel = b.connect(localServerURI.getHost(), localServerURI.getPort()).sync().channel();
ChannelPromise handshakeFuture = localFrameHandler.handshakeFuture();
handshakeFuture.addListener(new GenericFutureListener<ChannelFuture>() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
ChannelPipeline pipeline = future.channel().pipeline();
pipeline.remove(localFrameHandler);
pipeline.addLast(new LocalClientHandler(clientHandler));
}
});
handshakeFuture.sync();
logger.info("connect to local arthas server success");
return localChannel;
} catch (Exception e){
logger.error("connect to local arthas server error", e);
throw e;
}
}
private static class LocalClientHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {
Consumer<TextWebSocketFrame> clientHandler;
public LocalClientHandler(Consumer<TextWebSocketFrame> clientHandler) {
this.clientHandler = clientHandler;
}
@Override
public void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception {
clientHandler.accept(msg);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
logger.error("Local websocket client handle error", cause);
ctx.close();
}
}
}
@@ -5,7 +5,6 @@ import java.io.File;
import java.io.IOException;
import java.lang.instrument.Instrumentation;
import java.lang.reflect.Method;
import java.net.URI;
import java.security.CodeSource;
import java.util.ArrayList;
import java.util.HashMap;
@@ -52,7 +51,6 @@ import com.taobao.arthas.core.shell.impl.ShellServerImpl;
import com.taobao.arthas.core.shell.session.SessionManager;
import com.taobao.arthas.core.shell.session.impl.SessionManagerImpl;
import com.taobao.arthas.core.shell.term.impl.HttpTermServer;
import com.taobao.arthas.core.shell.term.impl.LocalTermServer;
import com.taobao.arthas.core.shell.term.impl.http.api.HttpApiHandler;
import com.taobao.arthas.core.shell.term.impl.httptelnet.HttpTelnetTermServer;
import com.taobao.arthas.core.util.ArthasBanner;
@@ -354,7 +352,7 @@ public class ArthasBootstrap {
options.getConnectionTimeout(), workerGroup));
} else {
// listen local address in VM communication
if (configure.getTunnelServer() != null) {
if (configure.getTunnelServer() != null || configure.getChannelServer() != null) {
shellServer.registerTermServer(new HttpTermServer(configure.getIp(), configure.getHttpPort(),
options.getConnectionTimeout(), workerGroup));
}
@@ -376,7 +374,6 @@ public class ArthasBootstrap {
try {
channelClient = new ChannelClient(configure.getChannelServer());
channelClient.setAgentInfoService(new AgentInfoServiceImpl(configure));
channelClient.setExecutorService(executorService);
channelClient.setRequestListener(new ChannelRequestHandler(channelClient, sessionManager, historyManager));
channelClient.start();
} catch (Throwable e) {
@@ -1,90 +0,0 @@
package com.taobao.arthas.core.shell.term.impl;
import com.alibaba.arthas.deps.org.slf4j.Logger;
import com.alibaba.arthas.deps.org.slf4j.LoggerFactory;
import com.taobao.arthas.core.shell.future.Future;
import com.taobao.arthas.core.shell.handlers.Handler;
import com.taobao.arthas.core.shell.term.Term;
import com.taobao.arthas.core.shell.term.TermServer;
import com.taobao.arthas.core.shell.term.impl.local.LocalTtyServerBootstrap;
import io.netty.channel.Channel;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.util.concurrent.EventExecutorGroup;
import io.termd.core.function.Consumer;
import io.termd.core.tty.TtyConnection;
import java.util.concurrent.TimeUnit;
/**
* @author gongdewei 2020/9/1
*/
public class LocalTermServer extends TermServer {
private static final Logger logger = LoggerFactory.getLogger(LocalTermServer.class);
private Handler<Term> termHandler;
private LocalTtyServerBootstrap bootstrap;
private String localAddr;
private long connectionTimeout;
private EventExecutorGroup workerGroup;
public LocalTermServer(String localAddr, long connectionTimeout, EventExecutorGroup workerGroup) {
this.localAddr = localAddr;
this.connectionTimeout = connectionTimeout;
this.workerGroup = workerGroup;
}
@Override
public TermServer termHandler(Handler<Term> handler) {
this.termHandler = handler;
return this;
}
@Override
public TermServer listen(Handler<Future<TermServer>> listenHandler) {
// TODO: charset and inputrc from options
bootstrap = new LocalTtyServerBootstrap(workerGroup).setAddr(localAddr);
try {
bootstrap.start(new Consumer<TtyConnection>() {
@Override
public void accept(final TtyConnection conn) {
termHandler.handle(new TermImpl(Helper.loadKeymap(), conn));
}
}).get(connectionTimeout, TimeUnit.MILLISECONDS);
listenHandler.handle(Future.<TermServer>succeededFuture());
logger.info("local term server is started.");
} catch (Throwable t) {
logger.error("start local term server error" , t);
listenHandler.handle(Future.<TermServer>failedFuture(t));
}
return this;
}
@Override
public int actualPort() {
return -1;
}
@Override
public void close() {
close(null);
}
@Override
public void close(Handler<Future<Void>> completionHandler) {
if (bootstrap != null) {
bootstrap.stop();
if (completionHandler != null) {
completionHandler.handle(Future.<Void>succeededFuture());
}
} else {
if (completionHandler != null) {
completionHandler.handle(Future.<Void>failedFuture("telnet term server not started"));
}
}
}
public Channel connect(Consumer<TextWebSocketFrame> clientHandler) throws InterruptedException {
return bootstrap.connect(clientHandler);
}
}
@@ -1,86 +0,0 @@
package com.taobao.arthas.core.shell.term.impl.local;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.group.ChannelGroup;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.termd.core.function.Consumer;
import io.termd.core.http.HttpTtyConnection;
import io.termd.core.tty.TtyConnection;
import java.util.concurrent.TimeUnit;
/**
* @author gongdewei 2020/9/1
*/
public class LocalTtyChannelHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {
private final ChannelGroup group;
private final Consumer<TtyConnection> handler;
private HttpTtyConnection conn;
private ChannelHandlerContext context;
public LocalTtyChannelHandler(ChannelGroup group, Consumer<TtyConnection> handler) {
this.group = group;
this.handler = handler;
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
super.channelActive(ctx);
context = ctx;
conn = new HttpTtyConnection() {
@Override
protected void write(byte[] buffer) {
ByteBuf byteBuf = Unpooled.buffer();
byteBuf.writeBytes(buffer);
if (context != null) {
context.writeAndFlush(new TextWebSocketFrame(byteBuf));
}
}
@Override
public void schedule(Runnable task, long delay, TimeUnit unit) {
if (context != null) {
context.executor().schedule(task, delay, unit);
}
}
@Override
public void execute(Runnable task) {
if (context != null) {
context.executor().execute(task);
}
}
@Override
public void close() {
if (context != null) {
context.close();
}
}
};
handler.accept(conn);
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
HttpTtyConnection tmp = conn;
context = null;
conn = null;
if (tmp != null) {
Consumer<Void> closeHandler = tmp.getCloseHandler();
if (closeHandler != null) {
closeHandler.accept(null);
}
}
}
@Override
protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception {
conn.writeToDecoder(msg.text());
}
}
@@ -1,153 +0,0 @@
package com.taobao.arthas.core.shell.term.impl.local;
import com.alibaba.arthas.deps.org.slf4j.Logger;
import com.alibaba.arthas.deps.org.slf4j.LoggerFactory;
import io.netty.bootstrap.Bootstrap;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.channel.local.LocalAddress;
import io.netty.channel.local.LocalChannel;
import io.netty.channel.local.LocalServerChannel;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.util.concurrent.DefaultThreadFactory;
import io.netty.util.concurrent.EventExecutorGroup;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;
import io.netty.util.concurrent.ImmediateEventExecutor;
import io.termd.core.function.Consumer;
import io.termd.core.tty.TtyConnection;
import io.termd.core.util.CompletableFuture;
import io.termd.core.util.Helper;
/**
* Convenience class for quickly starting a Netty Tty server.
*
*/
public class LocalTtyServerBootstrap {
private static final Logger logger = LoggerFactory.getLogger(LocalTtyServerBootstrap.class);
private final ChannelGroup channelGroup = new DefaultChannelGroup(ImmediateEventExecutor.INSTANCE);
private String addr;
private EventLoopGroup serverGroup;
private Channel serverChannel;
private EventExecutorGroup workerGroup;
public LocalTtyServerBootstrap(EventExecutorGroup workerGroup) {
this.workerGroup = workerGroup;
this.addr = "local-tty-server";
}
public String getAddr() {
return addr;
}
public LocalTtyServerBootstrap setAddr(String addr) {
this.addr = addr;
return this;
}
public void start(Consumer<TtyConnection> handler, final Consumer<Throwable> doneHandler) {
serverGroup = new NioEventLoopGroup(new DefaultThreadFactory("arthas-LocalWebsocketTtyBootstrap", true));
ServerBootstrap b = new ServerBootstrap();
b.group(serverGroup).channel(LocalServerChannel.class).handler(new LoggingHandler(LogLevel.TRACE))
.childHandler(new LocalTtyServerInitializer(channelGroup, handler, workerGroup));
// Address to bind on / connect to.
final LocalAddress localAddr = new LocalAddress(addr);
final ChannelFuture f = b.bind(localAddr);
f.addListener(new GenericFutureListener<Future<? super Void>>() {
@Override
public void operationComplete(Future<? super Void> future) throws Exception {
if (future.isSuccess()) {
serverChannel = f.channel();
doneHandler.accept(null);
} else {
doneHandler.accept(future.cause());
}
}
});
}
public Channel connect(final Consumer<TextWebSocketFrame> clientHandler) throws InterruptedException {
final LocalAddress localAddr = new LocalAddress(addr);
//start client
Bootstrap cb = new Bootstrap();
cb.group(serverGroup)
.channel(LocalChannel.class)
.handler(new ChannelInitializer<LocalChannel>() {
@Override
public void initChannel(LocalChannel ch) throws Exception {
ch.pipeline().addLast(
new LoggingHandler(LogLevel.TRACE),
new LocalClientHandler(clientHandler));
}
});
// Start the client.
return cb.connect(localAddr).sync().channel();
}
public CompletableFuture<Void> start(Consumer<TtyConnection> handler) {
CompletableFuture<Void> fut = new CompletableFuture<Void>();
start(handler, Helper.startedHandler(fut));
return fut;
}
public void stop(final Consumer<Throwable> doneHandler) {
if (serverChannel != null) {
serverChannel.close();
}
channelGroup.close().addListener(new GenericFutureListener<Future<? super Void>>() {
@Override
public void operationComplete(Future<? super Void> future) throws Exception {
try {
doneHandler.accept(future.cause());
} finally {
serverGroup.shutdownGracefully();
}
}
});
}
public CompletableFuture<Void> stop() {
CompletableFuture<Void> fut = new CompletableFuture<Void>();
stop(Helper.stoppedHandler(fut));
return fut;
}
private static class LocalClientHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {
Consumer<TextWebSocketFrame> clientHandler;
public LocalClientHandler(Consumer<TextWebSocketFrame> clientHandler) {
this.clientHandler = clientHandler;
}
@Override
public void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception {
clientHandler.accept(msg);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
logger.error("Local tty client handle error", cause);
ctx.close();
}
}
}
@@ -1,38 +0,0 @@
package com.taobao.arthas.core.shell.term.impl.local;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.local.LocalChannel;
import io.netty.util.concurrent.EventExecutorGroup;
import io.termd.core.function.Consumer;
import io.termd.core.tty.TtyConnection;
public class LocalTtyServerInitializer extends ChannelInitializer<LocalChannel> {
private final ChannelGroup group;
private final Consumer<TtyConnection> handler;
private EventExecutorGroup workerGroup;
public LocalTtyServerInitializer(ChannelGroup group, Consumer<TtyConnection> handler, EventExecutorGroup workerGroup) {
this.group = group;
this.handler = handler;
this.workerGroup = workerGroup;
}
@Override
protected void initChannel(LocalChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
// pipeline.addLast(new HttpServerCodec());
// pipeline.addLast(new ChunkedWriteHandler());
// pipeline.addLast(new HttpObjectAggregator(64 * 1024));
// pipeline.addLast(workerGroup, "HttpRequestHandler", new HttpRequestHandler("/ws", new File("arthas-output")));
// pipeline.addLast(new WebSocketServerProtocolHandler("/ws"));
// pipeline.addLast(new TtyWebSocketFrameHandler(group, handler));
pipeline.addLast(new LocalTtyChannelHandler(group, handler));
}
}