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

前言

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

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

今天这篇文章给大家分享一下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。

相关推荐
凌览19 小时前
0成本、0代码、全球CDN:Vercel + Notion快速搭建个人博客
前端·后端
决战灬19 小时前
Hologres高性能写入
后端
座山雕~19 小时前
spring
java·后端·spring
悟空码字19 小时前
刚刚,TRAE宣布,全量免费开放
后端·trae·solo
东百牧码人20 小时前
C# 雪花ID实现方案
后端
乌日尼乐20 小时前
【Java】IO流完全指南
java·后端
Java水解20 小时前
Spring Boot:Java开发的神奇加速器(二)
spring boot·后端
sunbin20 小时前
Eclipse 数据空间组件-最小化运行环境MVD_of_EDC-10
后端
问道飞鱼20 小时前
【Rust编程】Cargo 工具详解:从基础到高级的完整指南
开发语言·后端·rust·cargo