日常工作中如何对接第三方系统?

前言

作为接口调用方,对接外部系统时最怕什么?

我最怕的是对方不可控------接口突然超时、响应格式变更、签名算法升级,每一个都可能引发生产事故。

今天这篇文章给大家分享一下SpringBoot项目对接第三方系统的5种方案,希望对你会有所帮助。

一、核心原则:防御性编程

作为调用方,你必须假设:

  1. 网络随时会超时(设置合理的超时时间)
  2. 接口随时会出错(完善的错误处理)
  3. 响应格式可能变(灵活的解析逻辑)

基于这些原则,下面是5个非常实用的方案:

二、方案一:同步HTTP客户端

适用场景

  • 大多数API调用场景
  • QPS < 100 的中低频调用
  • 简单的增删改查操作

核心代码:配置是关键

java 复制代码
@Configuration
public class ExternalApiConfig {
    
    @Bean("externalRestTemplate")
    public RestTemplate externalRestTemplate() {
        // 1. 连接池配置(必须!)
        PoolingHttpClientConnectionManager pool = 
            new PoolingHttpClientConnectionManager();
        pool.setMaxTotal(200);          // 总连接数
        pool.setDefaultMaxPerRoute(50); // 每个外部域名50个连接
        
        // 2. 超时配置(比内部系统要短)
        RequestConfig config = RequestConfig.custom()
            .setConnectTimeout(3000)    // 连接超时3秒
            .setSocketTimeout(8000)     // 读取超时8秒(外部通常较慢)
            .setConnectionRequestTimeout(1000) // 从池中获取连接超时
            .build();
        
        // 3. 创建HttpClient
        CloseableHttpClient client = HttpClients.custom()
            .setConnectionManager(pool)
            .setDefaultRequestConfig(config)
            .setRetryHandler(new DefaultHttpRequestRetryHandler(1, false)) // 只重试1次
            .build();
        
        return new RestTemplate(new HttpComponentsClientHttpRequestFactory(client));
    }
}

@Service
public class PaymentApiClient {
    
    @Autowired
    @Qualifier("externalRestTemplate")
    private RestTemplate restTemplate;
    
    /**
     * 调用支付接口(生产级实现)
     */
    public PaymentResult callPayment(PaymentRequest request) {
        String requestId = UUID.randomUUID().toString();
        long startTime = System.currentTimeMillis();
        
        try {
            // 1. 准备请求
            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.APPLICATION_JSON);
            headers.set("X-Request-ID", requestId);
            headers.set("X-Timestamp", String.valueOf(System.currentTimeMillis()));
            
            // 2. 生成签名(外部API通常要求)
            String sign = SignUtils.generateSignature(request, headers);
            headers.set("X-Signature", sign);
            
            HttpEntity<PaymentRequest> entity = new HttpEntity<>(request, headers);
            
            // 3. 发送请求
            ResponseEntity<String> response = restTemplate.exchange(
                "https://api.payment.com/v1/pay",
                HttpMethod.POST,
                entity,
                String.class
            );
            
            // 4. 解析响应(字符串解析更灵活)
            String responseBody = response.getBody();
            long costTime = System.currentTimeMillis() - startTime;
            
            if (response.getStatusCode() == HttpStatus.OK) {
                // 先尝试标准解析
                try {
                    PaymentResponse paymentResponse = JsonUtils.parse(responseBody, PaymentResponse.class);
                    if (paymentResponse.isSuccess()) {
                        log.info("支付成功,订单: {},耗时: {}ms", 
                                request.getOrderId(), costTime);
                        return PaymentResult.success(paymentResponse.getTradeNo());
                    } else {
                        log.warn("支付失败,订单: {},错误: {}", 
                                request.getOrderId(), paymentResponse.getErrorMsg());
                        return PaymentResult.failed(paymentResponse.getErrorMsg());
                    }
                } catch (JsonProcessingException e) {
                    // 响应格式异常,尝试兼容性解析
                    log.warn("支付响应格式异常,尝试兼容解析: {}", responseBody);
                    return handleUnusualResponse(responseBody);
                }
            } else {
                log.error("支付接口HTTP错误,状态码: {},响应: {}", 
                         response.getStatusCode(), responseBody);
                return PaymentResult.failed("支付服务异常");
            }
            
        } catch (ResourceAccessException e) {
            // 网络超时
            long costTime = System.currentTimeMillis() - startTime;
            log.error("支付接口网络超时,订单: {},耗时: {}ms", 
                     request.getOrderId(), costTime, e);
            return PaymentResult.failed("网络超时,请稍后重试");
            
        } catch (Exception e) {
            // 其他所有异常
            log.error("支付接口调用异常,订单: {},请求ID: {}", 
                     request.getOrderId(), requestId, e);
            return PaymentResult.failed("系统异常");
        }
    }
    
    /**
     * 智能重试(根据错误类型决定是否重试)
     */
    public PaymentResult callPaymentWithRetry(PaymentRequest request, int maxRetries) {
        int retryCount = 0;
        
        while (retryCount <= maxRetries) {
            PaymentResult result = callPayment(request);
            
            if (result.isSuccess()) {
                return result;
            }
            
            // 只有网络超时和5xx错误才重试
            if (shouldRetry(result.getErrorCode())) {
                retryCount++;
                if (retryCount <= maxRetries) {
                    try {
                        // 指数退避
                        long delay = (long) Math.pow(2, retryCount) * 1000;
                        Thread.sleep(delay);
                        log.info("第{}次重试支付,订单: {}", retryCount, request.getOrderId());
                    } catch (InterruptedException ie) {
                        Thread.currentThread().interrupt();
                        return PaymentResult.failed("支付被中断");
                    }
                }
            } else {
                // 业务错误不重试
                break;
            }
        }
        
        return PaymentResult.failed("支付失败,已重试" + maxRetries + "次");
    }
    
    private boolean shouldRetry(String errorCode) {
        // 网络超时、服务不可用等可以重试
        return "NETWORK_TIMEOUT".equals(errorCode) || 
               "SERVICE_UNAVAILABLE".equals(errorCode) ||
               "GATEWAY_TIMEOUT".equals(errorCode);
    }
}

为什么这么设计?

设计点 原因 最佳实践
连接池 避免TCP握手开销 每个外部域名50-100连接
超时设置 外部网络不稳定 连接3秒,读取5-10秒
请求ID 问题排查追踪 UUID + 时间戳
灵活解析 外部接口可能变更 先标准解析,失败后兼容解析
智能重试 不是所有失败都该重试 仅重试网络问题,不重试业务错误

优缺点

优点

  • 简单直接,维护成本低
  • 适合大多数场景
  • 调试方便

缺点

  • 同步阻塞,消耗线程
  • 缺乏高级容错

适用场景:低频后台任务、数据同步、简单查询

三、方案二:异步非阻塞

适用场景

  • 高并发聚合查询
  • 实时数据获取
  • IO密集型操作

核心代码:WebClient实现

java 复制代码
@Service
public class LogisticsAsyncService {
    
    private final WebClient webClient;
    
    public LogisticsAsyncService() {
        this.webClient = WebClient.builder()
            .baseUrl("https://api.logistics.com")
            .defaultHeader("Content-Type", "application/json")
            .clientConnector(new ReactorClientHttpConnector(
                HttpClient.create()
                    .responseTimeout(Duration.ofSeconds(3))
                    .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 3000)
            ))
            .filter((request, next) -> {
                // 添加监控
                long start = System.currentTimeMillis();
                return next.exchange(request)
                    .doOnSuccess(response -> {
                        long cost = System.currentTimeMillis() - start;
                        metrics.recordApiCall("logistics", cost, 
                            response.statusCode().value());
                    });
            })
            .build();
    }
    
    /**
     * 聚合查询:同时查多家快递,谁快用谁的
     */
    public Mono<LogisticsInfo> queryAllLogistics(String trackingNo) {
        // 并行发起多个查询
        Mono<LogisticsInfo> sfMono = querySF(trackingNo)
            .timeout(Duration.ofSeconds(2))
            .onErrorReturn(LogisticsInfo.empty("顺丰"));
            
        Mono<LogisticsInfo> stoMono = querySTO(trackingNo)
            .timeout(Duration.ofSeconds(2))
            .onErrorReturn(LogisticsInfo.empty("申通"));
            
        Mono<LogisticsInfo> ytMono = queryYT(trackingNo)
            .timeout(Duration.ofSeconds(2))
            .onErrorReturn(LogisticsInfo.empty("圆通"));
        
        // 聚合结果:取最快返回的有效结果
        return Flux.merge(sfMono, stoMono, ytMono)
            .filter(info -> info.isValid()) // 过滤掉空结果
            .next() // 取第一个有效结果
            .switchIfEmpty(Mono.error(new LogisticsException("所有查询都失败")));
    }
    
    private Mono<LogisticsInfo> querySF(String trackingNo) {
        return webClient.get()
            .uri("/sf/query?trackingNo={no}", trackingNo)
            .header("X-Request-ID", UUID.randomUUID().toString())
            .retrieve()
            .onStatus(HttpStatus::isError, response -> 
                response.bodyToMono(String.class)
                    .flatMap(error -> Mono.error(
                        new LogisticsException("顺丰查询失败: " + error)
                    ))
            )
            .bodyToMono(LogisticsInfo.class);
    }
    
    /**
     * 批量查询(高并发场景)
     */
    public Flux<LogisticsInfo> batchQuery(List<String> trackingNos) {
        return Flux.fromIterable(trackingNos)
            .parallel(10) // 并行度
            .runOn(Schedulers.parallel())
            .flatMap(this::queryAllLogistics)
            .sequential();
    }
}

性能对比

查询方式 3家快递耗时 线程占用 适合场景
同步串行 ~6秒 1线程 低并发
异步并行 ~2秒 少量线程 高并发

优缺点

优点

  • 高并发,低资源消耗
  • 响应时间快
  • 天然支持超时控制

缺点

  • 编程模型复杂
  • 调试困难
  • 错误处理需要技巧

适用场景:物流查询、价格比较、实时数据聚合

四、方案三:熔断降级

适用场景

  • 支付、认证等核心链路
  • 外部依赖不稳定
  • 需要保证系统可用性

核心代码:Resilience4j实现

java 复制代码
@Service
@Slf4j
public class PaymentServiceWithCircuitBreaker {
    
    // 1. 熔断器配置
    private final CircuitBreakerConfig circuitBreakerConfig = 
        CircuitBreakerConfig.custom()
            .failureRateThreshold(50) // 失败率阈值50%
            .slidingWindowType(SlidingWindowType.COUNT_BASED)
            .slidingWindowSize(20)    // 最近20次请求
            .minimumNumberOfCalls(10) // 最少10次调用才开始统计
            .waitDurationInOpenState(Duration.ofSeconds(30)) // 熔断30秒
            .permittedNumberOfCallsInHalfOpenState(5) // 半开状态允许5次调用
            .recordExceptions(IOException.class, TimeoutException.class, 
                            RestClientException.class)
            .ignoreExceptions(BusinessException.class) // 业务异常不触发熔断
            .build();
    
    private final CircuitBreaker circuitBreaker = 
        CircuitBreaker.of("external-payment", circuitBreakerConfig);
    
    // 2. 限流器(防止恢复期流量过大)
    private final RateLimiter rateLimiter = RateLimiter.of("payment-api", 
        RateLimiterConfig.custom()
            .limitForPeriod(100)      // 每秒100个请求
            .limitRefreshPeriod(Duration.ofSeconds(1))
            .timeoutDuration(Duration.ofMillis(500))
            .build()
    );
    
    @Autowired
    private RestTemplate restTemplate;
    
    /**
     * 支付接口(熔断 + 限流 + 降级)
     */
    @CircuitBreaker(name = "external-payment", fallbackMethod = "paymentFallback")
    @RateLimiter(name = "payment-api")
    @TimeLimiter(name = "payment-api", fallbackMethod = "timeoutFallback")
    public CompletableFuture<PaymentResult> payWithProtection(String orderId, BigDecimal amount) {
        return CompletableFuture.supplyAsync(() -> {
            // 调用外部支付API
            PaymentResponse response = callExternalPayment(orderId, amount);
            return processPaymentResponse(response);
        });
    }
    
    /**
     * 熔断降级方法
     */
    private PaymentResult paymentFallback(String orderId, BigDecimal amount, Exception e) {
        log.warn("支付服务熔断降级,订单: {},异常: {}", orderId, e.getClass().getSimpleName());
        
        // 1. 记录到待处理表
        pendingPaymentService.savePending(orderId, amount, "CIRCUIT_BREAKER_OPEN");
        
        // 2. 返回友好提示
        return PaymentResult.builder()
            .success(false)
            .code("SYSTEM_BUSY")
            .message("系统繁忙,请稍后查看支付结果")
            .needManualCheck(true)
            .fallback(true)
            .build();
    }
    
    /**
     * 超时降级方法
     */
    private PaymentResult timeoutFallback(String orderId, BigDecimal amount, TimeoutException e) {
        log.warn("支付服务超时降级,订单: {},超时时间: {}ms", orderId, e.getMessage());
        
        // 快速失败,避免阻塞
        return PaymentResult.builder()
            .success(false)
            .code("TIMEOUT")
            .message("支付响应超时,请稍后重试")
            .build();
    }
    
    /**
     * 熔断器状态监听
     */
    @PostConstruct
    public void initCircuitBreakerListener() {
        circuitBreaker.getEventPublisher()
            .onStateTransition(event -> {
                log.warn("支付熔断器状态变更: {} -> {}",
                        event.getStateTransition().getFromState(),
                        event.getStateTransition().getToState());
                
                // 发送告警
                if (event.getStateTransition().getToState() == CircuitBreaker.State.OPEN) {
                    alertService.sendAlert("支付服务熔断器开启", 
                        "失败率超过阈值,已开启熔断");
                }
            });
    }
    
    /**
     * 补偿任务:处理降级的支付
     */
    @Scheduled(fixedDelay = 30000) // 每30秒执行一次
    public void compensatePendingPayments() {
        List<PendingPayment> pendings = pendingPaymentService.findUnprocessed();
        
        for (PendingPayment pending : pendings) {
            try {
                // 检查熔断器状态
                if (circuitBreaker.tryAcquirePermission()) {
                    PaymentResult result = callExternalPayment(
                        pending.getOrderId(), pending.getAmount());
                    
                    if (result.isSuccess()) {
                        pendingPaymentService.markAsSuccess(pending.getId());
                    }
                }
            } catch (Exception e) {
                log.error("补偿支付失败: {}", pending.getOrderId(), e);
            }
        }
    }
}

熔断器状态机

优缺点

优点

  • 防止雪崩效应
  • 快速失败,保护系统
  • 自动恢复

缺点

  • 增加系统复杂度
  • 需要合理配置参数
  • 降级逻辑需要设计

适用场景:支付、短信、认证等核心外部依赖

五、方案四:API网关(统一出口)

适用场景

  • 对接多个外部系统
  • 需要统一认证、限流、监控
  • 团队规模较大

网关配置示例

yaml 复制代码
# application.yml
spring:
  cloud:
    gateway:
      routes:
        # 支付服务路由
        - id: external-payment
          uri: https://api.payment.com
          predicates:
            - Path=/api/external/payment/**
          filters:
            - StripPrefix=2
            - name: CircuitBreaker
              args:
                name: paymentBreaker
                fallbackUri: forward:/fallback/payment
            - name: RequestRateLimiter
              args:
                redis-rate-limiter.replenishRate: 50
                redis-rate-limiter.burstCapacity: 100
            - AddRequestHeader=X-Client-ID, ${spring.application.name}
            - AddRequestHeader=X-Request-ID, ${uuid()}
        
        # 短信服务路由
        - id: external-sms
          uri: https://api.sms.com
          predicates:
            - Path=/api/external/sms/**
          filters:
            - StripPrefix=2
            - name: Retry
              args:
                retries: 2
                statuses: 500,502,503,504

全局过滤器

java 复制代码
@Component
@Slf4j
public class ExternalApiFilter implements GlobalFilter, Ordered {
    
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        
        // 1. 请求ID(全链路追踪)
        String requestId = request.getHeaders().getFirst("X-Request-ID");
        if (requestId == null) {
            requestId = UUID.randomUUID().toString();
            request = request.mutate()
                .header("X-Request-ID", requestId)
                .build();
        }
        
        // 2. 签名验证(调用外部API可能需要)
        if (!verifyRequestSignature(request)) {
            log.warn("请求签名验证失败,请求ID: {}", requestId);
            return unauthorized(exchange, "Invalid signature");
        }
        
        // 3. 限流检查(按客户端)
        String clientId = extractClientId(request);
        if (!rateLimiter.tryAcquire(clientId)) {
            log.warn("客户端限流,clientId: {},请求ID: {}", clientId, requestId);
            return tooManyRequests(exchange);
        }
        
        // 4. 记录访问日志
        long startTime = System.currentTimeMillis();
        
        return chain.filter(exchange.mutate().request(request).build())
            .doOnSuccess(v -> {
                long costTime = System.currentTimeMillis() - startTime;
                log.info("外部API调用完成,路径: {},耗时: {}ms,请求ID: {}",
                        request.getPath(), costTime, requestId);
                
                // 记录监控指标
                metrics.recordExternalApiCall(
                    request.getURI().getHost(),
                    costTime,
                    exchange.getResponse().getStatusCode().value()
                );
            })
            .doOnError(e -> {
                long costTime = System.currentTimeMillis() - startTime;
                log.error("外部API调用失败,路径: {},耗时: {}ms,请求ID: {},异常: {}",
                         request.getPath(), costTime, requestId, e.getMessage());
            });
    }
    
    @Override
    public int getOrder() {
        return -100;
    }
}

网关架构优势

优缺点

优点

  • 统一入口,便于管理
  • 集中安全控制
  • 完整监控能力

缺点

  • 单点故障风险
  • 性能瓶颈可能
  • 配置复杂

适用场景:企业级多系统对接、需要统一管控

六、方案五:封装为SDK

适用场景

  • 复杂外部系统对接
  • 多个项目需要调用同一外部API
  • 需要封装复杂业务逻辑

SDK设计示例

java 复制代码
/**
 * 微信支付SDK - 生产级设计
 */
public class WeChatPayClient {
    
    private final String mchId;
    private final String appId;
    private final HttpClient httpClient;
    private final Signer signer;
    
    // 构建器模式
    public static class Builder {
        private String mchId;
        private String appId;
        private String apiKey;
        private PrivateKey privateKey;
        private int timeout = 5000;
        
        public Builder mchId(String mchId) {
            this.mchId = mchId;
            return this;
        }
        
        public WeChatPayClient build() {
            return new WeChatPayClient(this);
        }
    }
    
    private WeChatPayClient(Builder builder) {
        this.mchId = builder.mchId;
        this.appId = builder.appId;
        this.signer = new WeChatSigner(builder.privateKey);
        this.httpClient = new InternalHttpClient(builder);
    }
    
    /**
     * 统一下单(核心方法)
     */
    public CreateOrderResult createOrder(CreateOrderRequest request) {
        // 1. 参数校验
        Validator.validate(request);
        
        // 2. 填充公共参数
        request.setMchId(mchId);
        request.setAppId(appId);
        request.setNonceStr(generateNonce());
        request.setTimeStamp(String.valueOf(System.currentTimeMillis() / 1000));
        
        // 3. 生成签名
        String sign = signer.sign(request.toMap());
        request.setSign(sign);
        
        // 4. 发送请求(带重试和监控)
        HttpResponse<CreateOrderResponse> response = httpClient.post(
            "/v3/pay/transactions/jsapi",
            request,
            CreateOrderResponse.class
        );
        
        // 5. 验证响应签名
        signer.verifyResponse(response);
        
        // 6. 转换为业务结果
        return CreateOrderResult.from(response.getBody());
    }
    
    /**
     * 查询订单
     */
    public OrderQueryResult queryOrder(String outTradeNo) {
        return httpClient.get(
            "/v3/pay/transactions/out-trade-no/" + outTradeNo,
            OrderQueryResult.class
        );
    }
    
    /**
     * 关闭订单
     */
    public CloseOrderResult closeOrder(String outTradeNo) {
        CloseOrderRequest request = new CloseOrderRequest();
        request.setOutTradeNo(outTradeNo);
        
        return httpClient.post(
            "/v3/pay/transactions/out-trade-no/" + outTradeNo + "/close",
            request,
            CloseOrderResult.class
        );
    }
    
    /**
     * 内部HTTP客户端(封装所有细节)
     */
    private static class InternalHttpClient {
        private final RestTemplate restTemplate;
        private final ObjectMapper objectMapper;
        
        public <T> T post(String path, Object request, Class<T> responseType) {
            // 统一的POST请求处理
            String url = "https://api.mch.weixin.qq.com" + path;
            
            try {
                // 设置请求头
                HttpHeaders headers = new HttpHeaders();
                headers.setContentType(MediaType.APPLICATION_JSON);
                headers.set("Accept", "application/json");
                
                // 序列化请求体
                String requestBody = objectMapper.writeValueAsString(request);
                HttpEntity<String> entity = new HttpEntity<>(requestBody, headers);
                
                // 发送请求
                ResponseEntity<String> response = restTemplate.exchange(
                    url, HttpMethod.POST, entity, String.class);
                
                // 处理响应
                if (response.getStatusCode() == HttpStatus.OK) {
                    return objectMapper.readValue(response.getBody(), responseType);
                } else {
                    throw new ExternalApiException(
                        "微信支付API错误: " + response.getStatusCode());
                }
            } catch (Exception e) {
                throw new ExternalApiException("调用微信支付失败", e);
            }
        }
    }
}

// 使用示例
@Service
public class OrderService {
    
    private final WeChatPayClient weChatPay;
    
    public OrderService() {
        this.weChatPay = new WeChatPayClient.Builder()
            .mchId("your_mch_id")
            .appId("your_app_id")
            .build();
    }
    
    public PaymentResult createWechatOrder(Order order) {
        try {
            CreateOrderRequest request = convertToWechatRequest(order);
            CreateOrderResult result = weChatPay.createOrder(request);
            
            if (result.isSuccess()) {
                return PaymentResult.success(result.getPrepayId());
            } else {
                return PaymentResult.failed(result.getErrorMessage());
            }
        } catch (ExternalApiException e) {
            log.error("微信支付下单失败", e);
            return PaymentResult.failed("微信支付服务异常");
        }
    }
}

SDK设计原则

原则 实现方式 好处
单一职责 只处理支付相关 职责清晰
开闭原则 接口稳定,实现可扩展 易于升级
依赖倒置 依赖抽象,不依赖具体 便于测试
最少知道 封装复杂细节 使用简单

优缺点

优点

  • 使用简单,封装复杂逻辑
  • 统一错误处理
  • 便于升级和维护

缺点

  • 开发成本高
  • 需要持续维护
  • 可能有版本冲突

适用场景:复杂支付对接、多个项目复用、深度定制需求

七、如何选择?

决策矩阵

考虑因素 方案1 方案2 方案3 方案4 方案5
开发速度 ⭐⭐⭐⭐⭐ ⭐⭐⭐ ⭐⭐
并发性能 ⭐⭐ ⭐⭐⭐⭐⭐ ⭐⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐
可用性 ⭐⭐ ⭐⭐⭐ ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐⭐
维护成本 ⭐⭐⭐⭐⭐ ⭐⭐⭐ ⭐⭐ ⭐⭐
适用场景 简单查询 高并发查询 核心业务 多系统 复杂集成

快速决策流程

各场景推荐方案

  1. 初创公司快速验证:方案1(简单直接)
  2. 电商物流查询:方案2(异步聚合)
  3. 支付核心链路:方案3(熔断降级)
  4. 企业多系统对接:方案4(API网关)
  5. 复杂支付多项目用:方案5(封装SDK)

总结

必须遵守的原则

  1. 超时设置是生命线

    • 连接超时:3-5秒
    • 读取超时:5-10秒
    • 总超时:不超过15秒
  2. 重试要谨慎

    • 只重试网络问题
    • 业务错误不重试
    • 使用指数退避
  3. 监控必须到位

    • 成功率监控
    • 耗时监控
    • QPS监控
  4. 错误处理要完整

    • 区分异常类型
    • 记录足够日志
    • 提供友好提示
  5. 幂等性设计

    • 唯一请求ID
    • 防止重复提交
    • 状态检查机制

选择方案时,记住:没有最好的方案,只有最适合的方案

根据你的具体场景、团队能力和业务需求来选择。

作为调用方,最重要的是保持谦逊------外部系统不可控,你的代码必须足够健壮。

最后说一句(求关注,别白嫖我)

如果这篇文章对您有所帮助,或者有所启发的话,帮忙关注一下我的同名公众号:苏三说技术,您的支持是我坚持写作最大的动力。

求一键三连:点赞、转发、在看。

关注公众号:【苏三说技术】,在公众号中回复:进大厂,可以免费获取我最近整理的10万字的面试宝典,好多小伙伴靠这个宝典拿到了多家大厂的offer。

相关推荐
苏三说技术10 小时前
Claude Code从失控到起飞,只用了这些技巧
后端
长栎11 小时前
写 for 循环写了十年,你却从没用过迭代器模式最狠的那一面
后端
LiaCode11 小时前
Redis 在生产项目的使用
前端·后端
用户5598224812211 小时前
Docker Compose Down 导致容器数据误删——ext4 日志恢复全记录
后端
LiaCode11 小时前
一天学完 redis 的爽翻版核心知识总结
前端·后端
大刚测试开发实战11 小时前
如何内网穿透访问本地私有化部署的TestHub
前端·后端·github
xiaodaoluanzha11 小时前
迄今為止,最簡單的編程語言 Nolang
前端·后端
Csvn11 小时前
Docker 容器管理入门 — 从镜像到容器编排
后端
用户7623524259111 小时前
ShardingJDBC
后端
行者全栈架构师11 小时前
IDEA 中 Maven 项目的 15 个红色报错快速解决方法
java·后端