异步编程_性能优化

前置知识

异步编程_基础

异步编程性能调优思路

垃圾回收优化

# 异步编程会产生大量短生命周期对象,如CompletableFuture、Lambda表达式捕获的变量等。
# 这些对象主要在年轻代分配,需要调优年轻代大小和GC参数。使用G1GC或ZGC可以减少GC停顿时间。

# JVM参数优化示例
-Xms4g -Xmx4g
-XX:+UseG1GC
-XX:MaxGCPauseMillis=200
-XX:G1HeapRegionSize=16m
-XX:+G1UseAdaptiveIHOP
-XX:G1MixedGCCountTarget=8

内存池化技术

// 频繁创建CompletableFuture对象会增加GC压力。可以使用对象池或者预分配技术来减少对象创建。
// 使用对象池减少CompletableFuture创建开销
@Component
public class CompletableFuturePool {
    private final Queue<CompletableFuture<Object>> pool = new ConcurrentLinkedQueue<>();
    
    @SuppressWarnings("unchecked")
    public <T> CompletableFuture<T> acquire() {
        CompletableFuture<T> future = (CompletableFuture<T>) pool.poll();
        if (future == null) {
            future = new CompletableFuture<>();
        }
        return future;
    }
    
    public void release(CompletableFuture<?> future) {
        if (future.isDone()) {
            // 重置状态后放回池中
            pool.offer(resetFuture(future));
        }
    }
}

线程上下文切换优化

// 过多的线程会导致上下文切换开销。可以使用线程本地变量、无锁数据结构等技术减少线程间竞争。
// 使用ThreadLocal减少线程间数据传递开销
public class AsyncContext {
    private static final ThreadLocal<Map<String, Object>> context = 
        ThreadLocal.withInitial(HashMap::new);
    
    public static void setUserId(Long userId) {
        context.get().put("userId", userId);
    }
    
    public static Long getUserId() {
        return (Long) context.get().get("userId");
    }
    
    public static void clear() {
        context.remove();
    }
}

合理配置线程池

// 线程数过多会导致上下文切换开销,线程数过少会导致任务堆积。需要根据系统负载进行动态调整。

// 分层异步架构:将异步操作按照延迟和重要性分层,使用不同的线程池和调度策略。
@Configuration
public class LayeredAsyncConfig {
    
    @Bean("fastExecutor")
    public Executor fastExecutor() {
        // 低延迟任务:缓存查询、简单计算
        return new ThreadPoolExecutor(
            Runtime.getRuntime().availableProcessors(),
            Runtime.getRuntime().availableProcessors() * 2,
            0L, TimeUnit.MILLISECONDS,
            new LinkedBlockingQueue<>(50),
            new ThreadFactoryBuilder().setNameFormat("fast-%d").build()
        );
    }
    
    @Bean("ioExecutor")
    public Executor ioExecutor() {
        // I/O密集型任务:数据库查询、网络请求
        return new ThreadPoolExecutor(
            50, 200,
            60L, TimeUnit.SECONDS,
            new LinkedBlockingQueue<>(500),
            new ThreadFactoryBuilder().setNameFormat("io-%d").build()
        );
    }
    
    @Bean("bulkExecutor")
    public Executor bulkExecutor() {
        // 批量处理任务:报表生成、数据导入
        return new ThreadPoolExecutor(
            2, 5,
            300L, TimeUnit.SECONDS,
            new LinkedBlockingQueue<>(100),
            new ThreadFactoryBuilder().setNameFormat("bulk-%d").build()
        );
    }
}

// 自适应线程池:根据系统负载动态调整线程池大小,避免资源浪费和任务积压。
public class AdaptiveThreadPoolExecutor extends ThreadPoolExecutor {
    private final AtomicInteger taskCount = new AtomicInteger(0);
    private final AtomicLong totalExecutionTime = new AtomicLong(0);
    private volatile long lastAdjustmentTime = System.currentTimeMillis();
    
    @Override
    protected void beforeExecute(Thread t, Runnable r) {
        super.beforeExecute(t, r);
        taskCount.incrementAndGet();
    }
    
    @Override
    protected void afterExecute(Runnable r, Throwable t) {
        super.afterExecute(r, t);
        long executionTime = System.currentTimeMillis() - lastAdjustmentTime;
        totalExecutionTime.addAndGet(executionTime);
        
        // 每100个任务调整一次线程池大小
        if (taskCount.get() % 100 == 0) {
            adjustPoolSize();
        }
    }
    
    private void adjustPoolSize() {
        long avgExecutionTime = totalExecutionTime.get() / taskCount.get();
        int queueSize = getQueue().size();
        
        if (queueSize > getCorePoolSize() * 2 && avgExecutionTime > 1000) {
            // 任务积压且执行时间长,增加线程
            int newSize = Math.min(getMaximumPoolSize(), getCorePoolSize() + 2);
            setCorePoolSize(newSize);
        } else if (queueSize < getCorePoolSize() / 2 && avgExecutionTime < 500) {
            // 任务较少且执行快,减少线程
            int newSize = Math.max(1, getCorePoolSize() - 1);
            setCorePoolSize(newSize);
        }
    }
}

 Spring Boot异步注解(@Async)优化

// Spring的@Async注解底层使用SimpleAsyncTaskExecutor,每个任务创建新线程,性能较差。需要自定义线程池配置。
@Configuration
@EnableAsync
public class AsyncConfig implements AsyncConfigurer {
    
    @Override
    public Executor getAsyncExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(10);
        executor.setMaxPoolSize(50);
        executor.setQueueCapacity(100);
        executor.setThreadNamePrefix("async-");
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        executor.initialize();
        return executor;
    }
    
    @Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        return new CustomAsyncExceptionHandler();
    }
}

WebFlux响应式编程

// 对于高并发场景,WebFlux提供了更好的性能特性。基于Reactor库的响应式编程模型可以用更少的线程处理更多请求。
@RestController
public class ReactiveController {
    
    @GetMapping("/users/{id}")
    public Mono<UserProfile> getUserProfile(@PathVariable Long id) {
        return userService.getBasicInfo(id)
            .zipWith(orderService.getRecentOrders(id))
            .zipWith(preferenceService.getUserPreferences(id))
            .map(tuple -> UserProfile.builder()
                .basicInfo(tuple.getT1().getT1())
                .recentOrders(tuple.getT1().getT2())
                .preferences(tuple.getT2())
                .build())
            .timeout(Duration.ofSeconds(5))
            .onErrorResume(throwable -> Mono.just(UserProfile.empty()));
    }
}

 数据库异步优化

// 连接池动态调优: 根据数据库性能指标动态调整连接池参数。
@Component
public class DynamicDataSourceConfig {
    
    private final HikariDataSource dataSource;
    private final MeterRegistry meterRegistry;
    
    @Scheduled(fixedDelay = 30000) // 每30秒检查一次
    public void adjustConnectionPool() {
        HikariPoolMXBean poolMXBean = dataSource.getHikariPoolMXBean();
        
        int activeConnections = poolMXBean.getActiveConnections();
        int idleConnections = poolMXBean.getIdleConnections();
        int totalConnections = poolMXBean.getTotalConnections();
        
        // 获取数据库响应时间
        Timer.Sample sample = Timer.start(meterRegistry);
        double avgResponseTime = sample.stop("db.query.duration").totalTime(TimeUnit.MILLISECONDS);
        
        if (activeConnections > totalConnections * 0.8 && avgResponseTime > 500) {
            // 连接使用率高且响应慢,增加连接数
            int newMaxSize = Math.min(50, dataSource.getMaximumPoolSize() + 5);
            dataSource.setMaximumPoolSize(newMaxSize);
        } else if (activeConnections < totalConnections * 0.3 && avgResponseTime < 100) {
            // 连接使用率低且响应快,减少连接数
            int newMaxSize = Math.max(10, dataSource.getMaximumPoolSize() - 2);
            dataSource.setMaximumPoolSize(newMaxSize);
        }
    }
}
// 异步批处理优化:将多个小的数据库操作合并为批处理,减少网络往返。
@Service
public class AsyncBatchProcessor {
    
    private final BatchingQueue<UserUpdate> userUpdateQueue;
    
    @PostConstruct
    public void initBatchProcessor() {
        userUpdateQueue = new BatchingQueue<>(
            100,  // 批大小
            Duration.ofSeconds(5),  // 最大等待时间
            this::processBatch
        );
    }
    
    public CompletableFuture<Void> updateUserAsync(UserUpdate update) {
        return userUpdateQueue.add(update);
    }
    
    private void processBatch(List<UserUpdate> batch) {
        // 批量更新数据库
        String sql = "UPDATE users SET name = ?, email = ? WHERE id = ?";
        jdbcTemplate.batchUpdate(sql, batch, (ps, update) -> {
            ps.setString(1, update.getName());
            ps.setString(2, update.getEmail());
            ps.setLong(3, update.getId());
        });
    }
}

// 批处理队列实现
public class BatchingQueue<T> {
    private final int batchSize;
    private final Duration maxWaitTime;
    private final Consumer<List<T>> batchProcessor;
    private final Queue<BatchItem<T>> queue = new ConcurrentLinkedQueue<>();
    
    public CompletableFuture<Void> add(T item) {
        CompletableFuture<Void> future = new CompletableFuture<>();
        queue.offer(new BatchItem<>(item, future));
        
        if (queue.size() >= batchSize) {
            processBatch();
        }
        
        return future;
    }
    
    private void processBatch() {
        List<BatchItem<T>> batch = new ArrayList<>();
        BatchItem<T> item;
        while ((item = queue.poll()) != null && batch.size() < batchSize) {
            batch.add(item);
        }
        
        if (!batch.isEmpty()) {
            try {
                batchProcessor.accept(batch.stream().map(BatchItem::getItem).collect(Collectors.toList()));
                batch.forEach(batchItem -> batchItem.getFuture().complete(null));
            } catch (Exception e) {
                batch.forEach(batchItem -> batchItem.getFuture().completeExceptionally(e));
            }
        }
    }
}

缓存与异步结合优化

// 多级缓存异步更新:实现L1(本地)、L2(Redis)、L3(数据库)多级缓存的异步更新策略。
@Service
public class MultiLevelCacheService {
    
    private final Cache<String, Object> l1Cache = Caffeine.newBuilder()
        .maximumSize(1000)
        .expireAfterWrite(5, TimeUnit.MINUTES)
        .build();
    
    private final RedisTemplate<String, Object> l2Cache;
    private final Executor cacheExecutor;
    
    public CompletableFuture<Object> getAsync(String key) {
        // L1缓存命中
        Object value = l1Cache.getIfPresent(key);
        if (value != null) {
            return CompletableFuture.completedFuture(value);
        }
        
        // L2缓存查询
        return CompletableFuture.supplyAsync(() -> {
            Object l2Value = l2Cache.opsForValue().get(key);
            if (l2Value != null) {
                // 异步更新L1缓存
                l1Cache.put(key, l2Value);
                return l2Value;
            }
            
            // 查询数据库
            Object dbValue = queryFromDatabase(key);
            if (dbValue != null) {
                // 异步更新L1和L2缓存
                CompletableFuture.runAsync(() -> {
                    l1Cache.put(key, dbValue);
                    l2Cache.opsForValue().set(key, dbValue, Duration.ofMinutes(30));
                }, cacheExecutor);
            }
            
            return dbValue;
        }, cacheExecutor);
    }
    
    public void invalidateAsync(String key) {
        CompletableFuture.runAsync(() -> {
            l1Cache.invalidate(key);
            l2Cache.delete(key);
        }, cacheExecutor);
    }
}
// 多级缓存异步更新:实现L1(本地)、L2(Redis)、L3(数据库)多级缓存的异步更新策略。
@Service
public class MultiLevelCacheService {
    
    private final Cache<String, Object> l1Cache = Caffeine.newBuilder()
        .maximumSize(1000)
        .expireAfterWrite(5, TimeUnit.MINUTES)
        .build();
    
    private final RedisTemplate<String, Object> l2Cache;
    private final Executor cacheExecutor;
    
    public CompletableFuture<Object> getAsync(String key) {
        // L1缓存命中
        Object value = l1Cache.getIfPresent(key);
        if (value != null) {
            return CompletableFuture.completedFuture(value);
        }
        
        // L2缓存查询
        return CompletableFuture.supplyAsync(() -> {
            Object l2Value = l2Cache.opsForValue().get(key);
            if (l2Value != null) {
                // 异步更新L1缓存
                l1Cache.put(key, l2Value);
                return l2Value;
            }
            
            // 查询数据库
            Object dbValue = queryFromDatabase(key);
            if (dbValue != null) {
                // 异步更新L1和L2缓存
                CompletableFuture.runAsync(() -> {
                    l1Cache.put(key, dbValue);
                    l2Cache.opsForValue().set(key, dbValue, Duration.ofMinutes(30));
                }, cacheExecutor);
            }
            
            return dbValue;
        }, cacheExecutor);
    }
    
    public void invalidateAsync(String key) {
        CompletableFuture.runAsync(() -> {
            l1Cache.invalidate(key);
            l2Cache.delete(key);
        }, cacheExecutor);
    }
}

 

// 预热缓存异步策略:在系统启动或低峰期预热缓存,提高响应速度。
@Component
public class CacheWarmupService {
    
    @EventListener(ApplicationReadyEvent.class)
    public void warmupCache() {
        CompletableFuture.runAsync(() -> {
            // 预热热点数据
            List<String> hotKeys = getHotKeys();
            hotKeys.parallelStream()
                .forEach(key -> {
                    try {
                        cacheService.getAsync(key).get(1, TimeUnit.SECONDS);
                    } catch (Exception e) {
                        log.warn("预热缓存失败: {}", key, e);
                    }
                });
        });
    }
    
    @Scheduled(cron = "0 2 * * * ?") // 每天凌晨2点
    public void scheduledWarmup() {
        warmupCache();
    }
}

分布式异步优化

// 分布式任务调度:使用消息队列实现分布式异步任务处理。
@Component
public class DistributedTaskProcessor {
    
    private final RabbitTemplate rabbitTemplate;
    private final RedisTemplate<String, Object> redisTemplate;
    
    public CompletableFuture<String> submitDistributedTask(Task task) {
        String taskId = UUID.randomUUID().toString();
        CompletableFuture<String> future = new CompletableFuture<>();
        
        // 保存任务状态
        redisTemplate.opsForValue().set("task:" + taskId, "PENDING", Duration.ofHours(24));
        
        // 发送任务到消息队列
        rabbitTemplate.convertAndSend("task.queue", task, message -> {
            message.getMessageProperties().setHeader("taskId", taskId);
            return message;
        });
        
        // 异步轮询任务状态
        CompletableFuture.runAsync(() -> {
            try {
                String result = pollTaskResult(taskId);
                future.complete(result);
            } catch (Exception e) {
                future.completeExceptionally(e);
            }
        });
        
        return future;
    }
    
    private String pollTaskResult(String taskId) throws InterruptedException {
        int attempts = 0;
        while (attempts < 120) { // 最多等待10分钟
            String status = (String) redisTemplate.opsForValue().get("task:" + taskId);
            if ("COMPLETED".equals(status)) {
                return (String) redisTemplate.opsForValue().get("task:result:" + taskId);
            } else if ("FAILED".equals(status)) {
                throw new RuntimeException("任务执行失败");
            }
            Thread.sleep(5000);
            attempts++;
        }
        throw new TimeoutException("任务执行超时");
    }
}

 云原生环境优化

// Kubernetes环境下的资源限制:合理配置Pod资源限制,避免异步任务影响其他服务。
# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: async-service
spec:
  template:
    spec:
      containers:
      - name: async-service
        resources:
          requests:
            memory: "512M"
            cpu: "500m"
          limits:
            memory: "1Gi"
            cpu: "1000m"
        env:
        - name: JAVA_OPTS
          value: "-Xms512m -Xmx768m -XX:+UseG1GC -XX:MaxGCPauseMillis=200"
        - name: ASYNC_THREAD_POOL_SIZE
          value: "20"
# 服务网格集成:在Istio等服务网格中优化异步服务的流量管理。

# virtualservice.yaml
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: async-service
spec:
  http:
  - match:
    - uri:
        prefix: "/async"
    route:
    - destination:
        host: async-service
    timeout: 30s
    retries:
      attempts: 3
      perTryTimeout: 10s

监控与诊断深度技术

/**
    JVM监控指标
    1. 线程池状态:活跃线程数、队列长度、任务执行时间分布
    2. GC指标:年轻代/老年代使用率、GC频率和停顿时间
    3. CompletableFuture状态:未完成的Future数量、异常率
    性能剖析工具:
    1. JProfiler:可以详细分析异步任务的执行轨迹
    2. Async Profiler:低开销的采样分析器,适合生产环境
    3. VisualVM:免费的性能分析工具
    线程堆栈分析:使用jstack或VisualVM分析线程状态,识别阻塞点和资源竞争。
**/

// 细粒度性能指标:监控异步操作的各个维度。
@Component
public class AsyncPerformanceMonitor {
    
    private final MeterRegistry meterRegistry;
    private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
    
    @PostConstruct
    public void startMonitoring() {
        // 监控线程池状态
        scheduler.scheduleAtFixedRate(() -> {
            ThreadPoolExecutor executor = (ThreadPoolExecutor) applicationContext.getBean("asyncExecutor");
            
            Gauge.builder("threadpool.active.threads")
                .register(meterRegistry, executor, ThreadPoolExecutor::getActiveCount);
            
            Gauge.builder("threadpool.queue.size")
                .register(meterRegistry, executor, e -> e.getQueue().size());
            
            Gauge.builder("threadpool.completed.tasks")
                .register(meterRegistry, executor, ThreadPoolExecutor::getCompletedTaskCount);
        }, 0, 10, TimeUnit.SECONDS);
        
        // 监控CompletableFuture状态
        monitorCompletableFutures();
    }
    
    private void monitorCompletableFutures() {
        AtomicInteger pendingFutures = new AtomicInteger(0);
        AtomicInteger completedFutures = new AtomicInteger(0);
        
        // 使用AOP或手动埋点统计CompletableFuture数量
        Gauge.builder("completablefuture.pending")
            .register(meterRegistry, pendingFutures, AtomicInteger::get);
        
        Gauge.builder("completablefuture.completed")
            .register(meterRegistry, completedFutures, AtomicInteger::get);
    }
}
// 智能告警系统:基于机器学习的异常检测。
@Component
public class AsyncAnomalyDetector {
    
    private final CircularBuffer<Double> responseTimeBuffer = new CircularBuffer<>(100);
    private final CircularBuffer<Integer> errorCountBuffer = new CircularBuffer<>(100);
    
    @EventListener
    public void onAsyncTaskCompleted(AsyncTaskCompletedEvent event) {
        responseTimeBuffer.add(event.getResponseTime());
        
        if (isAnomaly(event.getResponseTime())) {
            alertService.sendAlert("异步任务响应时间异常", event);
        }
    }
    
    private boolean isAnomaly(double responseTime) {
        if (responseTimeBuffer.size() < 30) {
            return false; // 数据不足,不进行检测
        }
        
        double mean = responseTimeBuffer.stream().mapToDouble(Double::doubleValue).average().orElse(0.0);
        double stdDev = calculateStandardDeviation(responseTimeBuffer, mean);
        
        // 3σ原则检测异常
        return Math.abs(responseTime - mean) > 3 * stdDev;
    }
}

常见性能陷阱及解决方案

CompletableFuture链式调用性能陷阱

// 过长的异步链会创建大量中间对象,影响性能。可以通过合并操作、减少中间步骤来优化。
// 性能陷阱:过多的中间步骤
CompletableFuture<String> result = CompletableFuture
    .supplyAsync(() -> step1())
    .thenApplyAsync(this::step2)
    .thenApplyAsync(this::step3)
    .thenApplyAsync(this::step4)
    .thenApplyAsync(this::step5);

// 优化:合并多个步骤
CompletableFuture<String> optimized = CompletableFuture
    .supplyAsync(() -> {
        String s1 = step1();
        String s2 = step2(s1);
        String s3 = step3(s2);
        return step4(step5(s3));
    });

异步任务内存泄漏

// 未正确处理的CompletableFuture会导致内存泄漏。需要确保所有异步任务都有超时机制和异常处理

// 防止任务泄漏的模式
public class AsyncTaskManager {
    private final Set<CompletableFuture<?>> runningTasks = ConcurrentHashMap.newKeySet();
    
    public <T> CompletableFuture<T> submitTask(Supplier<T> task) {
        CompletableFuture<T> future = CompletableFuture
            .supplyAsync(task)
            .orTimeout(30, TimeUnit.SECONDS)
            .whenComplete((result, throwable) -> {
                runningTasks.remove(future);
                if (throwable != null) {
                    log.error("异步任务执行失败", throwable);
                }
            });
        
        runningTasks.add(future);
        return future;
    }
    
    @PreDestroy
    public void shutdown() {
        runningTasks.forEach(future -> future.cancel(true));
    }
}

实际业务场景优化 

电商订单处理系统

// 一个订单创建涉及库存检查、价格计算、优惠券验证、积分计算等多个步骤。传统同步方式总耗时为各步骤时间之和,异步优化可以并行执行独立步骤。
@Service
public class OrderService {
    
    private final Executor ioExecutor = Executors.newFixedThreadPool(20);
    private final Executor cpuExecutor = Executors.newFixedThreadPool(8);
    
    public CompletableFuture<Order> createOrderAsync(CreateOrderRequest request) {
        // 并行执行多个验证步骤
        CompletableFuture<Boolean> inventoryCheck = CompletableFuture
            .supplyAsync(() -> inventoryService.checkStock(request.getProductId(), request.getQuantity()), ioExecutor);
            
        CompletableFuture<BigDecimal> priceCalculation = CompletableFuture
            .supplyAsync(() -> priceService.calculatePrice(request.getProductId(), request.getQuantity()), ioExecutor);
            
        CompletableFuture<CouponValidation> couponValidation = CompletableFuture
            .supplyAsync(() -> couponService.validateCoupon(request.getCouponId(), request.getUserId()), ioExecutor);
            
        CompletableFuture<Integer> pointsCalculation = CompletableFuture
            .supplyAsync(() -> pointsService.calculatePoints(request.getUserId(), request.getProductId()), ioExecutor);
        
        // 组合所有异步结果
        return CompletableFuture.allOf(inventoryCheck, priceCalculation, couponValidation, pointsCalculation)
            .thenApplyAsync(ignored -> {
                // 在CPU密集型执行器中处理业务逻辑
                return buildOrder(
                    request,
                    inventoryCheck.join(),
                    priceCalculation.join(),
                    couponValidation.join(),
                    pointsCalculation.join()
                );
            }, cpuExecutor)
            .thenComposeAsync(order -> persistOrder(order), ioExecutor);
    }
}

 数据聚合服务优化

// 在微服务架构中,聚合多个服务的数据是常见场景。需要考虑超时控制、部分失败处理、熔断机制等。
@Service
public class UserProfileAggregator {
    
    private final RestTemplate restTemplate;
    private final CircuitBreaker circuitBreaker;
    
    public CompletableFuture<UserProfile> aggregateUserProfile(Long userId) {
        // 设置超时时间,避免长时间阻塞
        CompletableFuture<UserBasicInfo> basicInfo = CompletableFuture
            .supplyAsync(() -> userService.getBasicInfo(userId))
            .orTimeout(2, TimeUnit.SECONDS);
            
        CompletableFuture<List<Order>> recentOrders = CompletableFuture
            .supplyAsync(() -> orderService.getRecentOrders(userId))
            .orTimeout(3, TimeUnit.SECONDS)
            .exceptionally(throwable -> {
                log.warn("获取用户订单失败,使用默认值", throwable);
                return Collections.emptyList();
            });
            
        CompletableFuture<UserPreferences> preferences = CompletableFuture
            .supplyAsync(() -> preferenceService.getUserPreferences(userId))
            .orTimeout(1, TimeUnit.SECONDS)
            .exceptionally(throwable -> new UserPreferences());
        
        // 使用allOf等待所有任务完成,但允许部分失败
        return CompletableFuture.allOf(basicInfo, recentOrders, preferences)
            .thenApply(ignored -> UserProfile.builder()
                .basicInfo(basicInfo.join())
                .recentOrders(recentOrders.join())
                .preferences(preferences.join())
                .build());
    }
}

 

posted @ 2018-08-09 11:39  lvlin241  阅读(125)  评论(0)    收藏  举报