XrayManager.java 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. package top.lvzhiqiang.util;
  2. import com.xxl.job.core.context.XxlJobHelper;
  3. import org.jsoup.Jsoup;
  4. import org.springframework.stereotype.Component;
  5. import top.lvzhiqiang.entity.ProxyNode;
  6. import top.lvzhiqiang.mapper.XrayProxyMapper;
  7. import javax.annotation.Resource;
  8. import java.io.IOException;
  9. import java.net.InetSocketAddress;
  10. import java.net.Proxy;
  11. import java.nio.charset.StandardCharsets;
  12. import java.nio.file.Files;
  13. import java.nio.file.Paths;
  14. import java.util.List;
  15. import java.util.concurrent.TimeUnit;
  16. /**
  17. * 管理 Xray 的配置和进程
  18. */
  19. @Component
  20. public class XrayManager {
  21. private static final String XRAY_PATH = "/usr/program/xray/xray";
  22. private static final String CONFIG_PATH = "/usr/program/xray/config.json";
  23. private static Process xrayProcess;
  24. private static final int LOCAL_SOCKS_PORT = 10808;
  25. @Resource
  26. private XrayProxyMapper xrayProxyMapper;
  27. // 1. 生成新的 Xray 配置
  28. private String generateXrayConfig(ProxyNode node) {
  29. return XrayConfigGenerator.generateFullConfig(node, LOCAL_SOCKS_PORT);
  30. }
  31. // 2. 停止旧的 Xray 进程
  32. private void stopXray() {
  33. if (xrayProcess != null && xrayProcess.isAlive()) {
  34. XxlJobHelper.log("Stopping current Xray process...");
  35. xrayProcess.destroy();
  36. try {
  37. // 等待进程优雅关闭
  38. if (!xrayProcess.waitFor(5, TimeUnit.SECONDS)) {
  39. xrayProcess.destroyForcibly();
  40. }
  41. } catch (InterruptedException e) {
  42. Thread.currentThread().interrupt();
  43. xrayProcess.destroyForcibly();
  44. }
  45. }
  46. }
  47. // 3. 启动新的 Xray 进程
  48. private void startXray() throws IOException {
  49. XxlJobHelper.log("Starting new Xray process...");
  50. ProcessBuilder pb = new ProcessBuilder(XRAY_PATH, "run", "-c", CONFIG_PATH);
  51. // 将输出重定向到文件或父进程,以便调试
  52. pb.redirectErrorStream(true);
  53. xrayProcess = pb.start();
  54. // 确保进程在 JVM 退出时被销毁
  55. Runtime.getRuntime().addShutdownHook(new Thread(this::stopXray));
  56. // 启动后等待几秒确保端口监听
  57. try {
  58. TimeUnit.SECONDS.sleep(3);
  59. } catch (InterruptedException e) {
  60. Thread.currentThread().interrupt();
  61. }
  62. XxlJobHelper.log("New Xray process started.");
  63. }
  64. // 4. 切换节点的主逻辑
  65. public void switchNode() {
  66. boolean flag = false;
  67. Proxy proxy = new Proxy(Proxy.Type.SOCKS, new InetSocketAddress("127.0.0.1", LOCAL_SOCKS_PORT));
  68. try {
  69. verifySSH("https://www.google.com", Proxy.NO_PROXY);
  70. String javdbTitle = verifySSH("https://www.javdb.com", proxy);
  71. if (!javdbTitle.contains("JavDB")) {
  72. flag = true;
  73. }
  74. } catch (IOException e) {
  75. flag = true;
  76. }
  77. if (flag) {
  78. List<ProxyNode> nodes = xrayProxyMapper.findProxyNodeList();
  79. for (ProxyNode node : nodes) {
  80. try {
  81. // A. 生成新配置并写入文件
  82. String newConfig = generateXrayConfig(node);
  83. Files.write(Paths.get(CONFIG_PATH), newConfig.getBytes(StandardCharsets.UTF_8));
  84. // B. 停止旧进程
  85. stopXray();
  86. // C. 启动新进程
  87. startXray();
  88. // D. 验证连接 (可选但推荐)
  89. String javdbTitle = verifySSH("https://www.javdb.com", proxy);
  90. if (javdbTitle.contains("JavDB")) {
  91. break;
  92. }
  93. } catch (Exception e) {
  94. System.err.println("Node " + node.getId() + " failed: " + e.getMessage());
  95. // 标记节点失效 db.updateNodeStatus(node.getId(), "dead");
  96. // 继续循环尝试下一个节点
  97. }
  98. }
  99. }
  100. }
  101. // 5. 连接测试(必须用原生 Java 网络库)
  102. public String verifySSH(String targetUrl, Proxy proxy) throws IOException {
  103. // 创建 SOCKS 代理对象
  104. XxlJobHelper.log("Attempting to crawl via SOCKS5 proxy...");
  105. org.jsoup.nodes.Document doc = Jsoup.connect(targetUrl)
  106. .proxy(proxy)
  107. .timeout(10000)
  108. .get();
  109. String title = doc.title();
  110. XxlJobHelper.log("Crawl successful! Title: " + title);
  111. return title;
  112. }
  113. }