mirror of
https://gitee.com/BDWare/agent-backend
synced 2025-04-27 14:42:16 +00:00
379 lines
16 KiB
Java
379 lines
16 KiB
Java
package org.bdware.server;
|
||
|
||
import com.google.gson.JsonElement;
|
||
import com.google.gson.JsonObject;
|
||
import io.netty.bootstrap.ServerBootstrap;
|
||
import io.netty.buffer.PooledByteBufAllocator;
|
||
import io.netty.channel.*;
|
||
import io.netty.channel.ChannelHandler.Sharable;
|
||
import io.netty.channel.nio.NioEventLoopGroup;
|
||
import io.netty.channel.socket.SocketChannel;
|
||
import io.netty.channel.socket.nio.NioServerSocketChannel;
|
||
import io.netty.handler.codec.http.HttpObjectAggregator;
|
||
import io.netty.handler.codec.http.HttpServerCodec;
|
||
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
|
||
import io.netty.handler.ssl.OpenSsl;
|
||
import io.netty.handler.ssl.OptionalSslHandler;
|
||
import io.netty.handler.ssl.SslContext;
|
||
import io.netty.handler.ssl.SslContextBuilder;
|
||
import io.netty.handler.stream.ChunkedWriteHandler;
|
||
import io.prometheus.client.exporter.HTTPServer;
|
||
import org.apache.commons.io.FileUtils;
|
||
import org.apache.logging.log4j.Level;
|
||
import org.apache.logging.log4j.LogManager;
|
||
import org.apache.logging.log4j.Logger;
|
||
import org.apache.logging.log4j.core.config.Configurator;
|
||
import org.bdware.sc.*;
|
||
import org.bdware.sc.bean.Contract;
|
||
import org.bdware.sc.bean.ContractExecType;
|
||
import org.bdware.sc.db.CMTables;
|
||
import org.bdware.sc.db.KeyValueDBUtil;
|
||
import org.bdware.sc.db.MultiIndexTimeRocksDBUtil;
|
||
import org.bdware.sc.util.ExceptionUtil;
|
||
import org.bdware.sdk.consistency.ConsistencyPluginManager;
|
||
import org.bdware.server.action.FileActions;
|
||
import org.bdware.server.action.UserManagerAction;
|
||
import org.bdware.server.doip.ContractRepositoryMain;
|
||
import org.bdware.server.http.CMHttpHandler;
|
||
import org.bdware.server.nodecenter.client.NodeCenterClientHandler;
|
||
import org.bdware.server.tcp.TCPClientFrameHandler;
|
||
import org.bdware.server.ws.ContractManagerFrameHandler;
|
||
import org.bdware.units.NetworkManager;
|
||
|
||
import java.io.*;
|
||
import java.lang.reflect.Field;
|
||
import java.net.MalformedURLException;
|
||
import java.net.URL;
|
||
import java.net.URLClassLoader;
|
||
import java.util.Date;
|
||
import java.util.concurrent.TimeUnit;
|
||
import java.util.concurrent.atomic.AtomicInteger;
|
||
|
||
public class CMHttpServer {
|
||
private static final Logger LOGGER = LogManager.getLogger(CMHttpServer.class);
|
||
private static final String CONFIG_PATH = "cmconfig.json";
|
||
public static EventLoopGroup workerGroup = new NioEventLoopGroup();
|
||
public static MultiIndexTimeRocksDBUtil nodeLogDB =
|
||
new MultiIndexTimeRocksDBUtil(
|
||
"./ContractManagerDB", CMTables.LocalNodeLogDB.toString());
|
||
public static URLClassLoader pluginLoader;
|
||
private static SslContext sslContext = null;
|
||
final String PATH = "/SCIDE/SCExecutor";
|
||
private final int port;
|
||
|
||
private CMHttpServer(int port) {
|
||
this.port = port;
|
||
}
|
||
|
||
private static void configServer(CMDConf cmdConf) throws IOException {
|
||
if (cmdConf.disableDoRepo) {
|
||
DoConfig.callContractUsingDOI = false;
|
||
}
|
||
GlobalConf.instance.isLAN = cmdConf.isLAN;
|
||
if (!cmdConf.doipCertPath.isEmpty()) {
|
||
String[] conf = cmdConf.doipCertPath.split(":");
|
||
// DOAConf.certPath = conf[0];
|
||
// DOAConf.certPassword = conf[1];
|
||
}
|
||
|
||
if (!cmdConf.doipUserHandle.isEmpty()) {
|
||
DOAConf.repoDoid = cmdConf.doipUserHandle;
|
||
}
|
||
if (!cmdConf.doipLhsAddress.isEmpty()) {
|
||
DOAConf.lhsAddress = cmdConf.doipLhsAddress;
|
||
}
|
||
if (cmdConf.withBdledgerServer) {
|
||
ContractManager.threadPool.execute(
|
||
() -> NetworkManager.instance.initP2P(cmdConf.servicePort + 4));
|
||
}
|
||
// 可自动运行bdledger可执行文件,也可在shell脚步中运行和停止
|
||
if (!cmdConf.withBdledgerClient.isEmpty()) {
|
||
ContractManager.scheduledThreadPool.schedule(
|
||
() -> {
|
||
File ledgerClient = new File(cmdConf.withBdledgerClient);
|
||
LOGGER.debug("canRead=" + ledgerClient.canRead() +
|
||
" path=" + ledgerClient.getAbsolutePath());
|
||
try {
|
||
Runtime.getRuntime().exec(ledgerClient.getAbsolutePath());
|
||
} catch (IOException e) {
|
||
LOGGER.warn("start bdledger client failed: " + e.getMessage());
|
||
}
|
||
},
|
||
1, TimeUnit.SECONDS);
|
||
}
|
||
if (cmdConf.enableEventPersistence) {
|
||
ContractManager.eventPersistenceEnabled = true;
|
||
}
|
||
if (!cmdConf.enableSsl.isEmpty()) {
|
||
try {
|
||
String[] filePaths = cmdConf.enableSsl.split(":");
|
||
File chainedFile = new File(filePaths[0]), keyFile = new File(filePaths[1]);
|
||
if (chainedFile.exists() && keyFile.exists()) {
|
||
sslContext =
|
||
SslContextBuilder.forServer(chainedFile, keyFile)
|
||
.ciphers(
|
||
null,
|
||
(ciphers, defaultCiphers, supportedCiphers) ->
|
||
defaultCiphers.stream()
|
||
.filter(x -> null != x && !x.contains("RC4"))
|
||
.toArray(String[]::new))
|
||
.build();
|
||
LOGGER.info("openssl isAvailable:" + OpenSsl.isAvailable());
|
||
}
|
||
} catch (Exception e) {
|
||
LOGGER.warn("Enabling SSL failed: " + e.getMessage());
|
||
LOGGER.debug(ExceptionUtil.exceptionToString(e));
|
||
}
|
||
}
|
||
|
||
// cmi file
|
||
ContractClient.cmi = cmdConf.cmi;
|
||
FileWriter fw = new FileWriter("CMI");
|
||
fw.write(ContractClient.cmi);
|
||
fw.close();
|
||
LOGGER.info("write CMI=" + ContractClient.cmi + " to ./CMI");
|
||
FileActions.setTextFileSuffixes(cmdConf.textFileSuffixes);
|
||
|
||
// plugins
|
||
CMHttpHandler.wsPluginActions = parseStrAsList(cmdConf.wsPluginActions);
|
||
TCPClientFrameHandler.clientToAgentPlugins = parseStrAsList(cmdConf.clientToAgentPlugins);
|
||
NodeCenterClientHandler.clientToClusterPlugins = parseStrAsList(cmdConf.clientToClusterPlugins);
|
||
org.bdware.units.tcp.TCPClientFrameHandler.tcpPlugins = parseStrAsList(cmdConf.tcpPlugins);
|
||
if (!cmdConf.debug.isEmpty()) {
|
||
try {
|
||
String[] classes = cmdConf.debug.split(",");
|
||
for (String clz : classes) {
|
||
if ("all".equalsIgnoreCase(clz)) {
|
||
Configurator.setRootLevel(Level.DEBUG);
|
||
break;
|
||
}
|
||
Configurator.setLevel(clz, Level.DEBUG);
|
||
LOGGER.warn("set debug: " + clz);
|
||
}
|
||
} catch (Exception e) {
|
||
LOGGER.warn(e.getMessage());
|
||
}
|
||
}
|
||
|
||
if (cmdConf.startContract != null && cmdConf.startContract.size() > 0) {
|
||
ContractManager.scheduledThreadPool.schedule(
|
||
() -> {
|
||
try {
|
||
for (JsonElement je : cmdConf.startContract) {
|
||
JsonObject jo = je.getAsJsonObject();
|
||
if (!jo.has("path"))
|
||
continue;
|
||
|
||
String path = jo.get("path").getAsString();
|
||
File f = new File(path);
|
||
if (!f.getName().endsWith(".ypk") || !f.exists())
|
||
continue;
|
||
Contract c = new Contract();
|
||
c.setScript(f.getAbsolutePath());
|
||
c.setType(ContractExecType.Sole);
|
||
if (jo.has("killBeforeStart")) {
|
||
ContractManager.instance.stopContract(jo.get("killBeforeStart").getAsString());
|
||
}
|
||
if (jo.has("owner"))
|
||
c.setOwner(jo.get("owner").getAsString());
|
||
else
|
||
c.setOwner(UserManagerAction.getNodeManager());
|
||
if (jo.has("createParam"))
|
||
c.setCreateParam(jo.get("createParam"));
|
||
ContractManager.instance.startContract(c);
|
||
}
|
||
} catch (Exception e) {
|
||
e.printStackTrace();
|
||
}
|
||
},
|
||
10, TimeUnit.SECONDS);
|
||
}
|
||
if (cmdConf.datachainConf != null) {
|
||
GlobalConf.resetDataChain(cmdConf.datachainConf);
|
||
}
|
||
if (cmdConf.overwrite) {
|
||
cmdConf.write(CONFIG_PATH);
|
||
}
|
||
|
||
}
|
||
|
||
private static String[] parseStrAsList(String str) {
|
||
if (str == null) {
|
||
return new String[]{};
|
||
}
|
||
return str.split(",");
|
||
}
|
||
|
||
private static void addDirToPath(String s) {
|
||
try {
|
||
LOGGER.info("add to path: " + s);
|
||
// Field field = ClassLoader.class.getDeclaredField("sys_paths");
|
||
Field field = ClassLoader.class.getDeclaredField("usr_paths");
|
||
field.setAccessible(true);
|
||
String[] path = (String[]) field.get(null);
|
||
String[] temp = new String[path.length + 1];
|
||
System.arraycopy(path, 0, temp, 0, path.length);
|
||
temp[path.length] = s;
|
||
field.set(null, temp);
|
||
} catch (Exception e) {
|
||
LOGGER.error(e.getMessage());
|
||
LOGGER.debug(ExceptionUtil.exceptionToString(e));
|
||
}
|
||
}
|
||
|
||
public static void main(String[] args) throws IOException {
|
||
RecoverMechTimeRecorder.startCMHttpServer = System.currentTimeMillis();
|
||
File confFile = new File(CONFIG_PATH);
|
||
File confTemplate = new File(CONFIG_PATH + ".template");
|
||
if (!confTemplate.exists()) {
|
||
CMDConf conf = new CMDConf();
|
||
conf.write(confTemplate.getAbsolutePath());
|
||
}
|
||
if (!confFile.exists() && confTemplate.exists()) {
|
||
FileUtils.copyFile(confTemplate, confFile);
|
||
}
|
||
CMDConf cmdConf = CMDConf.parseFile(CONFIG_PATH);
|
||
|
||
// addDirToPath(new File("./dynamicLibrary").getAbsolutePath());
|
||
|
||
// for compatibility
|
||
if (cmdConf.cmi.isEmpty()) {
|
||
cmdConf.overwrite = true;
|
||
File cmiF = new File("./CMI");
|
||
try {
|
||
BufferedReader br = new BufferedReader(new FileReader(cmiF));
|
||
cmdConf.cmi = br.readLine().replaceAll("\\s+", "");
|
||
br.close();
|
||
LOGGER.info("read CMI from ./CMI file");
|
||
} catch (Exception ignored) {
|
||
}
|
||
}
|
||
|
||
cmdConf.parseArgs(args);
|
||
configServer(cmdConf);
|
||
|
||
// check default key pair
|
||
File keyFile = new File("manager.key");
|
||
if (keyFile.exists()) {
|
||
try {
|
||
BufferedReader br = new BufferedReader(new FileReader(keyFile));
|
||
String pubKey = br.readLine();
|
||
String nowManager =
|
||
KeyValueDBUtil.instance.getValue(
|
||
CMTables.ConfigDB.toString(), "__NodeManager__");
|
||
// manager.key is used when node manager isn' set
|
||
if (null == nowManager || nowManager.isEmpty()) {
|
||
KeyValueDBUtil.instance.setValue(
|
||
CMTables.ConfigDB.toString(), "__NodeManager__", pubKey);
|
||
KeyValueDBUtil.instance.setValue(
|
||
CMTables.NodeRole.toString(), pubKey,
|
||
"NodeManager,ContractProvider,ContractUser,ContractInstanceManager");
|
||
KeyValueDBUtil.instance.setValue(
|
||
CMTables.NodeTime.toString(),
|
||
pubKey,
|
||
Long.toString(new Date().getTime()));
|
||
LOGGER.info("set node manager from manager.key");
|
||
}
|
||
} catch (IOException ignored) {
|
||
}
|
||
}
|
||
|
||
start(cmdConf.ip, cmdConf.servicePort, cmdConf);
|
||
}
|
||
|
||
public static void start(String ip, int port, CMDConf cmdConf) throws IOException {
|
||
LOGGER.info("start server at:" + port);
|
||
GlobalConf.initMasterAddress(ip + ":" + (port + 1));
|
||
GlobalConf.initIpPort(ip + ":" + port);
|
||
LOGGER.debug("dir:" + new File("./").getAbsolutePath());
|
||
new CMHttpServer(port).start();
|
||
ContractRepositoryMain.start();
|
||
}
|
||
|
||
/**
|
||
* port: http & websocket port port+1: tcp port port+2: doip port port+3: prometheus
|
||
*/
|
||
private void start() {
|
||
ConsistencyPluginManager.setContext(new SDKContext());
|
||
// EpollEventLoopGroup
|
||
// EpollServerSocketChannel
|
||
// ContractManager.reconnectPort = (port - 18000) * 30 + 1630;
|
||
// if (ContractManager.reconnectPort < 0) ContractManager.reconnectPort = 1630;
|
||
File[] pluginJar = new File("./pluginLib/")
|
||
.listFiles(pathname -> pathname.getName().endsWith(".jar"));
|
||
URL[] urls;
|
||
if (pluginJar != null && pluginJar.length > 0) {
|
||
urls = new URL[pluginJar.length];
|
||
for (int i = 0; i < pluginJar.length; i++) {
|
||
try {
|
||
urls[i] = pluginJar[i].toURI().toURL();
|
||
LOGGER.info("add plugin:" + pluginJar[i].getName());
|
||
} catch (MalformedURLException e) {
|
||
e.printStackTrace();
|
||
}
|
||
}
|
||
} else {
|
||
urls = new URL[]{};
|
||
}
|
||
pluginLoader = new URLClassLoader(urls, CMHttpServer.class.getClassLoader());
|
||
if (port >= 18000 && port < 18100) {
|
||
ContractManager.cPort = new ContractPort(1615 + (port - 18000) * 30);
|
||
} else {
|
||
ContractManager.cPort = new ContractPort(1615);
|
||
}
|
||
final CMHttpHandler serverHandler = new CMHttpHandler();
|
||
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
|
||
NettyConnectServerHandler trafficSharp =
|
||
new NettyConnectServerHandler(new AtomicInteger(0));
|
||
try {
|
||
ServerBootstrap b1 = new ServerBootstrap();
|
||
b1.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
|
||
b1.group(bossGroup, workerGroup)
|
||
.channel(NioServerSocketChannel.class)
|
||
.localAddress(port)
|
||
.childHandler(
|
||
new ChannelInitializer<SocketChannel>() {
|
||
@Override
|
||
protected void initChannel(SocketChannel arg0) {
|
||
if (sslContext != null) {
|
||
arg0.pipeline().addLast(new OptionalSslHandler(sslContext));
|
||
}
|
||
arg0.pipeline()
|
||
.addLast(trafficSharp)
|
||
.addLast(new HttpServerCodec())
|
||
.addLast(new HttpObjectAggregator(10 * 1024 * 1024))
|
||
.addLast(
|
||
new WebSocketServerProtocolHandler(
|
||
PATH, null, true))
|
||
.addLast(new ChunkedWriteHandler())
|
||
.addLast(serverHandler)
|
||
.addLast(new ContractManagerFrameHandler());
|
||
}
|
||
});
|
||
final Channel ch = b1.bind(port).sync().channel();
|
||
LOGGER.debug("[CMHttpServer] listen master port at:" + port);
|
||
new HTTPServer(port + 3);
|
||
NetworkManager.instance.initTCP(port + 1, workerGroup);
|
||
ch.closeFuture().sync();
|
||
} catch (Exception e) {
|
||
e.printStackTrace();
|
||
System.exit(1);
|
||
} finally {
|
||
bossGroup.shutdownGracefully();
|
||
workerGroup.shutdownGracefully();
|
||
}
|
||
}
|
||
|
||
@Sharable
|
||
public static class NettyConnectServerHandler extends ChannelInboundHandlerAdapter {
|
||
public NettyConnectServerHandler(AtomicInteger connectNum) {
|
||
}
|
||
|
||
@Override
|
||
public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
|
||
super.channelRegistered(ctx);
|
||
CongestionControl.httpCControl(ctx);
|
||
}
|
||
}
|
||
}
|