Browse Source

使用 tomcat-embed-core 创建一个独立的 Tomcat Server, 用于 oss-store 节点间的内部通信

reghao 1 year ago
parent
commit
8b25e6cbe6

+ 10 - 1
oss-store/src/main/java/cn/reghao/oss/store/config/SpringLifecycle.java

@@ -1,10 +1,12 @@
 package cn.reghao.oss.store.config;
 
+import cn.reghao.jutil.jdk.thread.ThreadPoolWrapper;
 import cn.reghao.oss.api.dto.StoreNodeDto;
 import cn.reghao.oss.api.dto.disk.DiskVolume;
 import cn.reghao.oss.api.iface.StoreService;
 import cn.reghao.oss.store.config.props.SpringProperties;
 import cn.reghao.oss.store.service.ConsoleCache;
+import cn.reghao.oss.store.tomcat.TomcatStarter;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.DisposableBean;
 import org.springframework.boot.ApplicationArguments;
@@ -12,6 +14,7 @@ import org.springframework.boot.ApplicationRunner;
 import org.springframework.stereotype.Component;
 
 import java.util.List;
+import java.util.concurrent.ExecutorService;
 
 /**
  * @author reghao
@@ -23,15 +26,21 @@ public class SpringLifecycle implements ApplicationRunner, DisposableBean {
     private final SpringProperties springProperties;
     private final StoreService storeService;
     private final ConsoleCache consoleCache;
+    private final ExecutorService threadPool;
+    private final TomcatStarter tomcatStarter;
 
-    public SpringLifecycle(SpringProperties springProperties, StoreService storeService, ConsoleCache consoleCache) {
+    public SpringLifecycle(SpringProperties springProperties, StoreService storeService,
+                           ConsoleCache consoleCache, TomcatStarter tomcatStarter) {
         this.springProperties = springProperties;
         this.storeService = storeService;
         this.consoleCache = consoleCache;
+        this.threadPool = ThreadPoolWrapper.threadPool("tomcat");
+        this.tomcatStarter = tomcatStarter;
     }
 
     @Override
     public void run(ApplicationArguments args) {
+        threadPool.submit(tomcatStarter);
         registerStoreNode();
     }
 

+ 34 - 0
oss-store/src/main/java/cn/reghao/oss/store/tomcat/GlobalServlet.java

@@ -0,0 +1,34 @@
+package cn.reghao.oss.store.tomcat;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.io.Writer;
+
+/**
+ * @author reghao
+ * @date 2024-12-19 11:39:47
+ */
+@Slf4j
+@Component
+public class GlobalServlet extends HttpServlet {
+    @Override
+    protected void service(HttpServletRequest request, HttpServletResponse response)
+            throws ServletException, IOException {
+        String uri = request.getRequestURI();
+        log.info("request uri -> {}", uri);
+
+        response.setCharacterEncoding("UTF-8");
+        response.setContentType("text/plain");
+        response.setHeader("Server", "Embedded Tomcat");
+        try (Writer writer = response.getWriter()) {
+            writer.write("Hello, Embedded Tomcat!");
+            writer.flush();
+        }
+    }
+}

+ 83 - 0
oss-store/src/main/java/cn/reghao/oss/store/tomcat/TomcatStarter.java

@@ -0,0 +1,83 @@
+package cn.reghao.oss.store.tomcat;
+
+import org.apache.catalina.Context;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.connector.Connector;
+import org.apache.catalina.startup.Tomcat;
+import org.springframework.stereotype.Component;
+
+import java.io.File;
+
+/**
+ * @author reghao
+ * @date 2024-12-19 11:28:16
+ */
+@Component
+public class TomcatStarter implements Runnable {
+    private final int port;
+    private final String baseDir;
+    private final Tomcat tomcat;
+    private final GlobalServlet globalServlet;
+
+    public TomcatStarter(GlobalServlet globalServlet) {
+        this.port = 8080;
+        this.baseDir = String.format("/opt/tmp/tomcat%s", port);
+        this.tomcat = new Tomcat();
+        this.globalServlet = globalServlet;
+    }
+
+    public void run() {
+        Runtime.getRuntime().addShutdownHook(new Thread(() -> {
+                    try {
+                        tomcat.destroy();
+                    } catch (LifecycleException e) {
+                        e.printStackTrace();
+                    }
+                })
+        );
+        try {
+            init();
+        } catch (LifecycleException e) {
+            e.printStackTrace();
+        }
+    }
+
+    private void configDir(String baseDir) {
+        File root = new File(baseDir);
+        if (!root.isDirectory() && !root.mkdirs()) {
+            throw new RuntimeException("请提供Tomcat工作目录");
+        }
+        String path4root = root.getAbsolutePath();
+        tomcat.setBaseDir(path4root);
+        File webapps = new File(path4root + "/webapps");
+        if (!webapps.isDirectory() && !webapps.mkdirs()) {
+            throw new RuntimeException("无法创建webapps目录");
+        }
+    }
+
+    private void init() throws LifecycleException {
+        // 设置工作目录
+        tomcat.setBaseDir(baseDir);
+        configDir(baseDir);
+
+        // 主机名, 将生成目录: {工作目录}/work/Tomcat/{主机名}/ROOT
+        tomcat.setHostname("localhost");
+        System.out.println("工作目录: " + tomcat.getServer().getCatalinaBase().getAbsolutePath());
+
+        tomcat.setPort(port);
+        Connector conn = tomcat.getConnector(); // Tomcat 9.0 必须调用 Tomcat#getConnector() 方法之后才会监听端口
+        System.out.println("连接器设置完成: " + conn);
+
+        // contextPath要使用的上下文映射,""表示根上下文
+        // docBase上下文的基础目录,用于静态文件。相对于服务器主目录必须存在 ({主目录}/webapps/{docBase})
+        String docBase = "";
+        Context ctx = tomcat.addContext("", /*{webapps}/~*/ null);
+
+        Tomcat.addServlet(ctx, "globalServlet", globalServlet);
+        ctx.addServletMappingDecoded("/", "globalServlet");
+
+        tomcat.start();
+        System.out.println("tomcat 已启动");
+        tomcat.getServer().await();
+    }
+}