| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134 |
- package top.lvzhiqiang.util;
- import com.xxl.job.core.context.XxlJobHelper;
- import org.jsoup.Jsoup;
- import org.springframework.stereotype.Component;
- import top.lvzhiqiang.entity.ProxyNode;
- import top.lvzhiqiang.mapper.XrayProxyMapper;
- import javax.annotation.Resource;
- import java.io.IOException;
- import java.net.InetSocketAddress;
- import java.net.Proxy;
- import java.nio.charset.StandardCharsets;
- import java.nio.file.Files;
- import java.nio.file.Paths;
- import java.util.List;
- import java.util.concurrent.TimeUnit;
- /**
- * 管理 Xray 的配置和进程
- */
- @Component
- public class XrayManager {
- private static final String XRAY_PATH = "/usr/program/xray/xray";
- private static final String CONFIG_PATH = "/usr/program/xray/config.json";
- private static Process xrayProcess;
- private static final int LOCAL_SOCKS_PORT = 10808;
- @Resource
- private XrayProxyMapper xrayProxyMapper;
- // 1. 生成新的 Xray 配置
- private String generateXrayConfig(ProxyNode node) {
- return XrayConfigGenerator.generateFullConfig(node, LOCAL_SOCKS_PORT);
- }
- // 2. 停止旧的 Xray 进程
- private void stopXray() {
- if (xrayProcess != null && xrayProcess.isAlive()) {
- XxlJobHelper.log("Stopping current Xray process...");
- xrayProcess.destroy();
- try {
- // 等待进程优雅关闭
- if (!xrayProcess.waitFor(5, TimeUnit.SECONDS)) {
- xrayProcess.destroyForcibly();
- }
- } catch (InterruptedException e) {
- Thread.currentThread().interrupt();
- xrayProcess.destroyForcibly();
- }
- }
- }
- // 3. 启动新的 Xray 进程
- private void startXray() throws IOException {
- XxlJobHelper.log("Starting new Xray process...");
- ProcessBuilder pb = new ProcessBuilder(XRAY_PATH, "run", "-c", CONFIG_PATH);
- // 将输出重定向到文件或父进程,以便调试
- pb.redirectErrorStream(true);
- xrayProcess = pb.start();
- // 确保进程在 JVM 退出时被销毁
- Runtime.getRuntime().addShutdownHook(new Thread(this::stopXray));
- // 启动后等待几秒确保端口监听
- try {
- TimeUnit.SECONDS.sleep(3);
- } catch (InterruptedException e) {
- Thread.currentThread().interrupt();
- }
- XxlJobHelper.log("New Xray process started.");
- }
- // 4. 切换节点的主逻辑
- public void switchNode() {
- boolean flag = false;
- Proxy proxy = new Proxy(Proxy.Type.SOCKS, new InetSocketAddress("127.0.0.1", LOCAL_SOCKS_PORT));
- try {
- verifySSH("https://www.google.com", Proxy.NO_PROXY);
- String javdbTitle = verifySSH("https://www.javdb.com", proxy);
- if (!javdbTitle.contains("JavDB")) {
- flag = true;
- }
- } catch (IOException e) {
- flag = true;
- }
- if (flag) {
- List<ProxyNode> nodes = xrayProxyMapper.findProxyNodeList();
- for (ProxyNode node : nodes) {
- try {
- // A. 生成新配置并写入文件
- String newConfig = generateXrayConfig(node);
- Files.write(Paths.get(CONFIG_PATH), newConfig.getBytes(StandardCharsets.UTF_8));
- // B. 停止旧进程
- stopXray();
- // C. 启动新进程
- startXray();
- // D. 验证连接 (可选但推荐)
- String javdbTitle = verifySSH("https://www.javdb.com", proxy);
- if (javdbTitle.contains("JavDB")) {
- break;
- }
- } catch (Exception e) {
- System.err.println("Node " + node.getId() + " failed: " + e.getMessage());
- // 标记节点失效 db.updateNodeStatus(node.getId(), "dead");
- // 继续循环尝试下一个节点
- }
- }
- }
- }
- // 5. 连接测试(必须用原生 Java 网络库)
- public String verifySSH(String targetUrl, Proxy proxy) throws IOException {
- // 创建 SOCKS 代理对象
- XxlJobHelper.log("Attempting to crawl via SOCKS5 proxy...");
- org.jsoup.nodes.Document doc = Jsoup.connect(targetUrl)
- .proxy(proxy)
- .timeout(10000)
- .get();
- String title = doc.title();
- XxlJobHelper.log("Crawl successful! Title: " + title);
- return title;
- }
- }
|