第八章:从原型到生产系统

11 阅读10分钟

第八章:从原型到生产系统

本章字数:约16000字 阅读时间:约55分钟 难度等级:★★★★☆

声明:本文中的公司名称、包名、API地址、密钥等均已脱敏处理。文中的"梦想世界"、"dreamworld"等均为虚构名称,与任何真实公司无关。


引言

在前面的章节中,我们成功实现了完整的安全调用链。但这只是一个"能跑"的原型,距离生产环境还有很长的路要走。

本章将探讨如何将原型系统转变为可靠的生产系统,包括:

  1. 架构优化:从单机到分布式
  2. 性能优化:提升吞吐量和响应速度
  3. 可靠性设计:错误处理、重试、熔断
  4. 运维支持:监控、日志、告警

8.1 生产环境的挑战

8.1.1 原型与生产的差距

┌─────────────────────────────────────────────────────────────────┐
│                    原型 vs 生产                                  │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│  维度          原型                    生产                      │
│  ─────────────────────────────────────────────────────────────  │
│  并发量        单线程                  高并发                    │
│  可用性        能跑就行                99.9%+                    │
│  性能          不关注                  毫秒级响应                │
│  错误处理      打印日志                自动恢复                  │
│  监控          无                      全链路监控                │
│  部署          手动                    自动化                    │
│  扩展性        单机                    水平扩展                  │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘

8.1.2 主要挑战

1. Unidbg性能瓶颈

Unidbg是CPU密集型操作,每次调用都需要模拟ARM指令执行:

单次调用耗时分析:
├── setEnvironment(): ~50ms
├── getPriId(): ~100ms
├── rsaSign(): ~200ms
├── formatDK(): ~150ms
└── hmacSign(): ~100ms

总计: ~600ms(首次调用)

2. 内存占用

每个Unidbg实例占用约200-500MB内存:

内存占用分析:
├── Emulator实例: ~100MB
├── VM实例: ~50MB
├── 加载的SO库: ~50MB
└── 运行时数据: ~100MB

单实例总计: ~300MB

3. 密钥管理

  • 密钥有效期约120天
  • 需要定期刷新
  • 多实例间需要共享

8.2 架构优化

8.2.1 服务化架构

将安全调用链封装为独立服务:

┌─────────────────────────────────────────────────────────────────┐
│                    服务化架构                                    │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │                    业务服务层                            │    │
│  │  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐      │    │
│  │  │  数据采集   │  │  数据分析   │  │  数据展示   │      │    │
│  │  │   服务      │  │   服务      │  │   服务      │      │    │
│  │  └──────┬──────┘  └──────┬──────┘  └──────┬──────┘      │    │
│  └─────────┼────────────────┼────────────────┼─────────────┘    │
│            │                │                │                   │
│            └────────────────┼────────────────┘                   │
│                             │                                    │
│                             ▼                                    │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │                    安全服务层                            │    │
│  │  ┌─────────────────────────────────────────────────┐    │    │
│  │  │              Security Service                    │    │    │
│  │  │  ┌─────────┐  ┌─────────┐  ┌─────────┐          │    │    │
│  │  │  │ 激活API │  │ 签名API │  │ 密钥API │          │    │    │
│  │  │  └─────────┘  └─────────┘  └─────────┘          │    │    │
│  │  └─────────────────────────────────────────────────┘    │    │
│  └─────────────────────────────────────────────────────────┘    │
│                             │                                    │
│                             ▼                                    │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │                    基础设施层                            │    │
│  │  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐      │    │
│  │  │   Redis     │  │  Unidbg    │  │   监控      │      │    │
│  │  │  (密钥缓存) │  │   Pool     │  │   系统      │      │    │
│  │  └─────────────┘  └─────────────┘  └─────────────┘      │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘

8.2.2 Unidbg实例池

为了提高并发能力,我们实现Unidbg实例池:

package com.dreamworld.pool;

import com.dreamworld.unidbg.UnidbgManager;
import com.dreamworld.utils.LogUtils;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;

/**
 * Unidbg实例池
 */
public class UnidbgPool {
    
    private static final String TAG = "UnidbgPool";
    
    private final BlockingQueue<UnidbgManager> pool;
    private final int poolSize;
    private volatile boolean shutdown = false;
    
    public UnidbgPool(int poolSize) {
        this.poolSize = poolSize;
        this.pool = new ArrayBlockingQueue<>(poolSize);
        
        initPool();
    }
    
    /**
     * 初始化实例池
     */
    private void initPool() {
        LogUtils.i(TAG, "初始化Unidbg实例池,大小: " + poolSize);
        
        for (int i = 0; i < poolSize; i++) {
            try {
                UnidbgManager manager = createInstance();
                pool.offer(manager);
                LogUtils.d(TAG, "创建实例 " + (i + 1) + "/" + poolSize);
            } catch (Exception e) {
                LogUtils.e(TAG, "创建实例失败", e);
            }
        }
        
        LogUtils.success("实例池初始化完成,可用实例: " + pool.size());
    }
    
    /**
     * 创建新实例
     */
    private UnidbgManager createInstance() {
        UnidbgManager manager = new UnidbgManager();
        manager.initialize();
        manager.setEnvironment(1);  // 设置生产环境
        return manager;
    }
    
    /**
     * 获取实例(阻塞)
     */
    public UnidbgManager acquire() throws InterruptedException {
        return acquire(30, TimeUnit.SECONDS);
    }
    
    /**
     * 获取实例(带超时)
     */
    public UnidbgManager acquire(long timeout, TimeUnit unit) throws InterruptedException {
        if (shutdown) {
            throw new IllegalStateException("实例池已关闭");
        }
        
        UnidbgManager manager = pool.poll(timeout, unit);
        if (manager == null) {
            throw new RuntimeException("获取实例超时");
        }
        
        return manager;
    }
    
    /**
     * 归还实例
     */
    public void release(UnidbgManager manager) {
        if (manager == null) {
            return;
        }
        
        if (shutdown) {
            manager.destroy();
            return;
        }
        
        // 检查实例是否健康
        if (isHealthy(manager)) {
            pool.offer(manager);
        } else {
            LogUtils.w(TAG, "实例不健康,销毁并创建新实例");
            manager.destroy();
            
            try {
                UnidbgManager newManager = createInstance();
                pool.offer(newManager);
            } catch (Exception e) {
                LogUtils.e(TAG, "创建新实例失败", e);
            }
        }
    }
    
    /**
     * 检查实例是否健康
     */
    private boolean isHealthy(UnidbgManager manager) {
        try {
            // 简单的健康检查:调用getPriId
            String priId = manager.getPriId();
            return priId != null && priId.length() == 32;
        } catch (Exception e) {
            return false;
        }
    }
    
    /**
     * 获取可用实例数
     */
    public int getAvailableCount() {
        return pool.size();
    }
    
    /**
     * 关闭实例池
     */
    public void shutdown() {
        LogUtils.i(TAG, "关闭实例池...");
        shutdown = true;
        
        UnidbgManager manager;
        while ((manager = pool.poll()) != null) {
            manager.destroy();
        }
        
        LogUtils.success("实例池已关闭");
    }
}

8.2.3 使用实例池

/**
 * 使用实例池执行操作
 */
public String signWithPool(String data) {
    UnidbgManager manager = null;
    try {
        // 从池中获取实例
        manager = unidbgPool.acquire();
        
        // 执行签名
        return manager.hmacSign(hacKey, data);
        
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
        throw new RuntimeException("获取实例被中断", e);
    } finally {
        // 归还实例
        if (manager != null) {
            unidbgPool.release(manager);
        }
    }
}

8.3 密钥管理

8.3.1 密钥缓存策略

package com.dreamworld.cache;

import com.dreamworld.storage.KeyStorage;
import com.dreamworld.utils.LogUtils;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

/**
 * Redis密钥缓存
 */
public class RedisKeyCache implements KeyStorage {
    
    private static final String TAG = "RedisKeyCache";
    
    private static final String KEY_PREFIX = "dreamworld:key:";
    private static final String KEY_ID = KEY_PREFIX + "keyId";
    private static final String KEY_AES = KEY_PREFIX + "aesKey";
    private static final String KEY_HAC = KEY_PREFIX + "hacKey";
    private static final String KEY_VALID_FROM = KEY_PREFIX + "validFrom";
    private static final String KEY_VALID_TO = KEY_PREFIX + "validTo";
    private static final String KEY_DEVICE_ID = KEY_PREFIX + "deviceId";
    
    private final JedisPool jedisPool;
    
    public RedisKeyCache(JedisPool jedisPool) {
        this.jedisPool = jedisPool;
    }
    
    @Override
    public void setKeyId(String keyId) {
        try (Jedis jedis = jedisPool.getResource()) {
            jedis.set(KEY_ID, keyId);
        }
    }
    
    @Override
    public String getKeyId() {
        try (Jedis jedis = jedisPool.getResource()) {
            return jedis.get(KEY_ID);
        }
    }
    
    @Override
    public void setAesKey(String aesKey) {
        try (Jedis jedis = jedisPool.getResource()) {
            jedis.set(KEY_AES, aesKey);
        }
    }
    
    @Override
    public String getAesKey() {
        try (Jedis jedis = jedisPool.getResource()) {
            return jedis.get(KEY_AES);
        }
    }
    
    @Override
    public void setHacKey(String hacKey) {
        try (Jedis jedis = jedisPool.getResource()) {
            jedis.set(KEY_HAC, hacKey);
        }
    }
    
    @Override
    public String getHacKey() {
        try (Jedis jedis = jedisPool.getResource()) {
            return jedis.get(KEY_HAC);
        }
    }
    
    @Override
    public void setValidFrom(long validFrom) {
        try (Jedis jedis = jedisPool.getResource()) {
            jedis.set(KEY_VALID_FROM, String.valueOf(validFrom));
        }
    }
    
    @Override
    public long getValidFrom() {
        try (Jedis jedis = jedisPool.getResource()) {
            String value = jedis.get(KEY_VALID_FROM);
            return value != null ? Long.parseLong(value) : 0;
        }
    }
    
    @Override
    public void setValidTo(long validTo) {
        try (Jedis jedis = jedisPool.getResource()) {
            jedis.set(KEY_VALID_TO, String.valueOf(validTo));
        }
    }
    
    @Override
    public long getValidTo() {
        try (Jedis jedis = jedisPool.getResource()) {
            String value = jedis.get(KEY_VALID_TO);
            return value != null ? Long.parseLong(value) : 0;
        }
    }
    
    @Override
    public void setDeviceId(String deviceId) {
        try (Jedis jedis = jedisPool.getResource()) {
            jedis.set(KEY_DEVICE_ID, deviceId);
        }
    }
    
    @Override
    public String getDeviceId() {
        try (Jedis jedis = jedisPool.getResource()) {
            return jedis.get(KEY_DEVICE_ID);
        }
    }
    
    @Override
    public boolean isKeyValid() {
        String keyId = getKeyId();
        String hacKey = getHacKey();
        
        if (keyId == null || hacKey == null) {
            return false;
        }
        
        long now = System.currentTimeMillis();
        long validFrom = getValidFrom();
        long validTo = getValidTo();
        
        // 提前7天刷新
        long refreshThreshold = 7 * 24 * 60 * 60 * 1000L;
        
        return now >= validFrom && now < (validTo - refreshThreshold);
    }
    
    @Override
    public void clear() {
        try (Jedis jedis = jedisPool.getResource()) {
            jedis.del(KEY_ID, KEY_AES, KEY_HAC, KEY_VALID_FROM, KEY_VALID_TO);
        }
    }
    
    /**
     * 获取密钥剩余有效天数
     */
    public long getRemainingDays() {
        long validTo = getValidTo();
        long now = System.currentTimeMillis();
        return (validTo - now) / (24 * 60 * 60 * 1000L);
    }
}

8.3.2 密钥自动刷新

package com.dreamworld.scheduler;

import com.dreamworld.cache.RedisKeyCache;
import com.dreamworld.security.SecurityOperation;
import com.dreamworld.utils.LogUtils;

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * 密钥刷新调度器
 */
public class KeyRefreshScheduler {
    
    private static final String TAG = "KeyRefreshScheduler";
    
    private final RedisKeyCache keyCache;
    private final SecurityOperation securityOp;
    private final ScheduledExecutorService scheduler;
    
    // 刷新阈值:剩余7天时刷新
    private static final long REFRESH_THRESHOLD_DAYS = 7;
    
    public KeyRefreshScheduler(RedisKeyCache keyCache, SecurityOperation securityOp) {
        this.keyCache = keyCache;
        this.securityOp = securityOp;
        this.scheduler = Executors.newSingleThreadScheduledExecutor();
    }
    
    /**
     * 启动调度器
     */
    public void start() {
        LogUtils.i(TAG, "启动密钥刷新调度器");
        
        // 每小时检查一次
        scheduler.scheduleAtFixedRate(
            this::checkAndRefresh,
            0,
            1,
            TimeUnit.HOURS
        );
    }
    
    /**
     * 检查并刷新密钥
     */
    private void checkAndRefresh() {
        try {
            LogUtils.d(TAG, "检查密钥状态...");
            
            long remainingDays = keyCache.getRemainingDays();
            LogUtils.d(TAG, "密钥剩余有效期: " + remainingDays + "天");
            
            if (remainingDays <= REFRESH_THRESHOLD_DAYS) {
                LogUtils.i(TAG, "密钥即将过期,开始刷新...");
                refreshKey();
            }
            
        } catch (Exception e) {
            LogUtils.e(TAG, "检查密钥状态失败", e);
        }
    }
    
    /**
     * 刷新密钥
     */
    private synchronized void refreshKey() {
        try {
            // 重新激活获取新密钥
            // ... 调用激活流程 ...
            
            LogUtils.success("密钥刷新成功");
            
        } catch (Exception e) {
            LogUtils.e(TAG, "密钥刷新失败", e);
            // 发送告警
            sendAlert("密钥刷新失败: " + e.getMessage());
        }
    }
    
    /**
     * 发送告警
     */
    private void sendAlert(String message) {
        // 实现告警逻辑:邮件、短信、钉钉等
        LogUtils.e(TAG, "【告警】" + message);
    }
    
    /**
     * 停止调度器
     */
    public void stop() {
        LogUtils.i(TAG, "停止密钥刷新调度器");
        scheduler.shutdown();
    }
}

8.4 性能优化

8.4.1 性能瓶颈分析

┌─────────────────────────────────────────────────────────────────┐
│                    性能瓶颈分析                                  │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│  操作                耗时        瓶颈原因                        │
│  ─────────────────────────────────────────────────────────────  │
│  Unidbg初始化       2-5秒       加载SO库、创建VM                 │
│  setEnvironment     50ms        JNI调用开销                      │
│  getPriId           100ms       密钥生成计算                     │
│  rsaSign            200ms       RSA加密计算                      │
│  formatDK           150ms       AES解密计算                      │
│  hmacSign           100ms       HMAC计算                         │
│  HTTP请求           100-500ms   网络延迟                         │
│                                                                  │
│  优化策略:                                                       │
│  1. 实例池复用 - 避免重复初始化                                  │
│  2. 密钥缓存 - 避免重复激活                                      │
│  3. 异步处理 - 提高并发能力                                      │
│  4. 批量操作 - 减少调用次数                                      │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘

8.4.2 签名缓存

对于相同的签名请求,可以缓存结果:

package com.dreamworld.cache;

import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;

import java.util.concurrent.TimeUnit;

/**
 * 签名缓存
 */
public class SignatureCache {
    
    private final Cache<String, String> cache;
    
    public SignatureCache() {
        this.cache = Caffeine.newBuilder()
            .maximumSize(10000)
            .expireAfterWrite(5, TimeUnit.MINUTES)  // 5分钟过期
            .build();
    }
    
    /**
     * 生成缓存Key
     */
    public String generateKey(String method, String path, String timestamp) {
        return method + ":" + path + ":" + timestamp;
    }
    
    /**
     * 获取缓存的签名
     */
    public String get(String key) {
        return cache.getIfPresent(key);
    }
    
    /**
     * 缓存签名
     */
    public void put(String key, String signature) {
        cache.put(key, signature);
    }
    
    /**
     * 获取或计算签名
     */
    public String getOrCompute(String key, java.util.function.Supplier<String> supplier) {
        return cache.get(key, k -> supplier.get());
    }
}

8.4.3 异步签名服务

package com.dreamworld.service;

import com.dreamworld.pool.UnidbgPool;
import com.dreamworld.storage.KeyStorage;
import com.dreamworld.unidbg.UnidbgManager;
import com.dreamworld.utils.LogUtils;

import java.util.concurrent.*;

/**
 * 异步签名服务
 */
public class AsyncSignatureService {
    
    private static final String TAG = "AsyncSignatureService";
    
    private final UnidbgPool unidbgPool;
    private final KeyStorage keyStorage;
    private final ExecutorService executor;
    
    public AsyncSignatureService(UnidbgPool unidbgPool, KeyStorage keyStorage, int threads) {
        this.unidbgPool = unidbgPool;
        this.keyStorage = keyStorage;
        this.executor = Executors.newFixedThreadPool(threads);
    }
    
    /**
     * 异步签名
     */
    public CompletableFuture<String> signAsync(String stringToSign) {
        return CompletableFuture.supplyAsync(() -> {
            UnidbgManager manager = null;
            try {
                manager = unidbgPool.acquire();
                String hacKey = keyStorage.getHacKey();
                return manager.hmacSign(hacKey, stringToSign);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                throw new CompletionException(e);
            } finally {
                if (manager != null) {
                    unidbgPool.release(manager);
                }
            }
        }, executor);
    }
    
    /**
     * 批量异步签名
     */
    public CompletableFuture<List<String>> signBatchAsync(List<String> stringsToSign) {
        List<CompletableFuture<String>> futures = stringsToSign.stream()
            .map(this::signAsync)
            .collect(Collectors.toList());
        
        return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]))
            .thenApply(v -> futures.stream()
                .map(CompletableFuture::join)
                .collect(Collectors.toList()));
    }
    
    /**
     * 关闭服务
     */
    public void shutdown() {
        executor.shutdown();
        try {
            if (!executor.awaitTermination(30, TimeUnit.SECONDS)) {
                executor.shutdownNow();
            }
        } catch (InterruptedException e) {
            executor.shutdownNow();
        }
    }
}

8.4.4 性能测试

package com.dreamworld.benchmark;

import com.dreamworld.pool.UnidbgPool;
import com.dreamworld.service.AsyncSignatureService;
import com.dreamworld.utils.LogUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;

/**
 * 性能测试
 */
public class PerformanceBenchmark {
    
    private static final String TAG = "Benchmark";
    
    /**
     * 并发测试
     */
    public static void runConcurrencyTest(UnidbgPool pool, int concurrency, int requests) {
        LogUtils.separator("并发性能测试");
        LogUtils.i(TAG, "并发数: " + concurrency);
        LogUtils.i(TAG, "请求数: " + requests);
        
        AtomicInteger successCount = new AtomicInteger(0);
        AtomicInteger failCount = new AtomicInteger(0);
        AtomicLong totalTime = new AtomicLong(0);
        
        CountDownLatch latch = new CountDownLatch(requests);
        long startTime = System.currentTimeMillis();
        
        for (int i = 0; i < requests; i++) {
            final int index = i;
            new Thread(() -> {
                long reqStart = System.currentTimeMillis();
                try {
                    var manager = pool.acquire();
                    try {
                        String result = manager.getPriId();
                        if (result != null) {
                            successCount.incrementAndGet();
                        } else {
                            failCount.incrementAndGet();
                        }
                    } finally {
                        pool.release(manager);
                    }
                    totalTime.addAndGet(System.currentTimeMillis() - reqStart);
                } catch (Exception e) {
                    failCount.incrementAndGet();
                } finally {
                    latch.countDown();
                }
            }).start();
        }
        
        try {
            latch.await();
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        
        long endTime = System.currentTimeMillis();
        long duration = endTime - startTime;
        
        LogUtils.separator("测试结果");
        LogUtils.i(TAG, "总耗时: " + duration + "ms");
        LogUtils.i(TAG, "成功: " + successCount.get());
        LogUtils.i(TAG, "失败: " + failCount.get());
        LogUtils.i(TAG, "QPS: " + (requests * 1000.0 / duration));
        LogUtils.i(TAG, "平均响应时间: " + (totalTime.get() / requests) + "ms");
    }
}

8.5 监控与告警

8.5.1 监控指标

package com.dreamworld.metrics;

import io.micrometer.core.instrument.*;

/**
 * 监控指标收集
 */
public class SecurityMetrics {
    
    private final MeterRegistry registry;
    
    // 计数器
    private final Counter activationSuccessCounter;
    private final Counter activationFailCounter;
    private final Counter signatureCounter;
    
    // 计时器
    private final Timer activationTimer;
    private final Timer signatureTimer;
    
    // 仪表
    private final AtomicInteger poolAvailable;
    private final AtomicLong keyRemainingDays;
    
    public SecurityMetrics(MeterRegistry registry) {
        this.registry = registry;
        
        // 激活计数
        this.activationSuccessCounter = Counter.builder("security.activation.success")
            .description("成功激活次数")
            .register(registry);
        
        this.activationFailCounter = Counter.builder("security.activation.fail")
            .description("失败激活次数")
            .register(registry);
        
        // 签名计数
        this.signatureCounter = Counter.builder("security.signature.count")
            .description("签名次数")
            .register(registry);
        
        // 激活耗时
        this.activationTimer = Timer.builder("security.activation.time")
            .description("激活耗时")
            .register(registry);
        
        // 签名耗时
        this.signatureTimer = Timer.builder("security.signature.time")
            .description("签名耗时")
            .register(registry);
        
        // 实例池可用数
        this.poolAvailable = new AtomicInteger(0);
        Gauge.builder("security.pool.available", poolAvailable, AtomicInteger::get)
            .description("实例池可用数")
            .register(registry);
        
        // 密钥剩余天数
        this.keyRemainingDays = new AtomicLong(0);
        Gauge.builder("security.key.remaining_days", keyRemainingDays, AtomicLong::get)
            .description("密钥剩余有效天数")
            .register(registry);
    }
    
    public void recordActivationSuccess() {
        activationSuccessCounter.increment();
    }
    
    public void recordActivationFail() {
        activationFailCounter.increment();
    }
    
    public void recordSignature() {
        signatureCounter.increment();
    }
    
    public Timer.Sample startActivationTimer() {
        return Timer.start(registry);
    }
    
    public void stopActivationTimer(Timer.Sample sample) {
        sample.stop(activationTimer);
    }
    
    public Timer.Sample startSignatureTimer() {
        return Timer.start(registry);
    }
    
    public void stopSignatureTimer(Timer.Sample sample) {
        sample.stop(signatureTimer);
    }
    
    public void updatePoolAvailable(int count) {
        poolAvailable.set(count);
    }
    
    public void updateKeyRemainingDays(long days) {
        keyRemainingDays.set(days);
    }
}

8.5.2 健康检查

package com.dreamworld.health;

import com.dreamworld.pool.UnidbgPool;
import com.dreamworld.storage.KeyStorage;

/**
 * 健康检查
 */
public class HealthChecker {
    
    private final UnidbgPool unidbgPool;
    private final KeyStorage keyStorage;
    
    public HealthChecker(UnidbgPool unidbgPool, KeyStorage keyStorage) {
        this.unidbgPool = unidbgPool;
        this.keyStorage = keyStorage;
    }
    
    /**
     * 检查整体健康状态
     */
    public HealthStatus check() {
        HealthStatus status = new HealthStatus();
        
        // 检查实例池
        status.setPoolHealthy(checkPool());
        
        // 检查密钥
        status.setKeyHealthy(checkKey());
        
        // 综合状态
        status.setHealthy(status.isPoolHealthy() && status.isKeyHealthy());
        
        return status;
    }
    
    private boolean checkPool() {
        return unidbgPool.getAvailableCount() > 0;
    }
    
    private boolean checkKey() {
        return keyStorage.isKeyValid();
    }
    
    public static class HealthStatus {
        private boolean healthy;
        private boolean poolHealthy;
        private boolean keyHealthy;
        
        // Getters and Setters
        public boolean isHealthy() { return healthy; }
        public void setHealthy(boolean healthy) { this.healthy = healthy; }
        public boolean isPoolHealthy() { return poolHealthy; }
        public void setPoolHealthy(boolean poolHealthy) { this.poolHealthy = poolHealthy; }
        public boolean isKeyHealthy() { return keyHealthy; }
        public void setKeyHealthy(boolean keyHealthy) { this.keyHealthy = keyHealthy; }
    }
}

8.5.3 告警配置

# alertmanager.yml
groups:
  - name: security-alerts
    rules:
      # 密钥即将过期告警
      - alert: KeyExpiringSoon
        expr: security_key_remaining_days < 7
        for: 1h
        labels:
          severity: warning
        annotations:
          summary: "密钥即将过期"
          description: "密钥剩余有效期不足7天,请及时刷新"
      
      # 密钥已过期告警
      - alert: KeyExpired
        expr: security_key_remaining_days <= 0
        for: 5m
        labels:
          severity: critical
        annotations:
          summary: "密钥已过期"
          description: "密钥已过期,服务不可用"
      
      # 实例池耗尽告警
      - alert: PoolExhausted
        expr: security_pool_available == 0
        for: 5m
        labels:
          severity: critical
        annotations:
          summary: "实例池耗尽"
          description: "Unidbg实例池已耗尽,无法处理新请求"
      
      # 激活失败率过高告警
      - alert: HighActivationFailRate
        expr: rate(security_activation_fail[5m]) / rate(security_activation_success[5m]) > 0.1
        for: 10m
        labels:
          severity: warning
        annotations:
          summary: "激活失败率过高"
          description: "激活失败率超过10%,请检查服务状态"

8.6 部署方案

8.6.1 Docker部署

# Dockerfile
FROM openjdk:11-jdk-slim

WORKDIR /app

# 复制依赖
COPY target/lib /app/lib
COPY target/security-chain-1.0.0.jar /app/app.jar

# 复制资源文件
COPY resources /app/resources

# 设置环境变量
ENV JAVA_OPTS="-Xms512m -Xmx2g -XX:+UseG1GC"
ENV APP_OPTS=""

# 暴露端口
EXPOSE 8080

# 启动命令
ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar /app/app.jar $APP_OPTS"]
# docker-compose.yml
version: '3.8'

services:
  security-service:
    build: .
    ports:
      - "8080:8080"
    environment:
      - REDIS_HOST=redis
      - REDIS_PORT=6379
    depends_on:
      - redis
    deploy:
      replicas: 2
      resources:
        limits:
          memory: 2G
        reservations:
          memory: 1G
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
      interval: 30s
      timeout: 10s
      retries: 3
  
  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"
    volumes:
      - redis-data:/data

volumes:
  redis-data:

8.6.2 Kubernetes部署

# k8s-deployment.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: security-service
  labels:
    app: security-service
spec:
  replicas: 3
  selector:
    matchLabels:
      app: security-service
  template:
    metadata:
      labels:
        app: security-service
    spec:
      containers:
      - name: security-service
        image: dreamworld/security-service:1.0.0
        ports:
        - containerPort: 8080
        resources:
          requests:
            memory: "1Gi"
            cpu: "500m"
          limits:
            memory: "2Gi"
            cpu: "1000m"
        livenessProbe:
          httpGet:
            path: /health
            port: 8080
          initialDelaySeconds: 60
          periodSeconds: 30
        readinessProbe:
          httpGet:
            path: /ready
            port: 8080
          initialDelaySeconds: 30
          periodSeconds: 10
        env:
        - name: REDIS_HOST
          valueFrom:
            configMapKeyRef:
              name: security-config
              key: redis.host
---
apiVersion: v1
kind: Service
metadata:
  name: security-service
spec:
  selector:
    app: security-service
  ports:
  - port: 80
    targetPort: 8080
  type: ClusterIP

8.7 本章小结

8.7.1 生产化要点

┌─────────────────────────────────────────────────────────────────┐
│                    生产化要点总结                                │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│  1. 架构优化                                                     │
│     ├── 服务化:将安全调用链封装为独立服务                       │
│     ├── 实例池:复用Unidbg实例,提高并发能力                     │
│     └── 缓存:Redis缓存密钥,减少重复激活                        │
│                                                                  │
│  2. 性能优化                                                     │
│     ├── 异步处理:提高吞吐量                                     │
│     ├── 签名缓存:减少重复计算                                   │
│     └── 批量操作:减少调用次数                                   │
│                                                                  │
│  3. 可靠性设计                                                   │
│     ├── 密钥自动刷新:避免过期                                   │
│     ├── 健康检查:及时发现问题                                   │
│     └── 错误处理:优雅降级                                       │
│                                                                  │
│  4. 运维支持                                                     │
│     ├── 监控指标:全面的指标收集                                 │
│     ├── 告警配置:及时发现异常                                   │
│     └── 容器化部署:便于扩展和管理                               │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘

8.7.2 下一章预告

在下一章中,我们将进行商品数据抓取实战

  • 完整的数据抓取流程
  • 数据解析和存储
  • 增量更新策略
  • 数据质量保证

本章完