Nano-Banana在Java开发中的应用:SpringBoot集成AI图像生成API

1. 为什么Java开发者需要关注Nano-Banana

最近在团队做电商后台系统升级时,产品经理突然甩来一份需求文档:「首页轮播图要支持动态生成,用户上传一张商品图,系统自动产出5种不同风格的营销海报」。这需求听起来像天方夜谭,但当我把Nano-Banana的API文档发给后端同事时,他盯着屏幕看了三分钟,然后说:「这个能做,而且比我们预想的简单得多。」

Nano-Banana不是什么神秘黑科技,它本质上是一个图像生成能力极强的AI服务,但对Java开发者来说,它的价值在于——你不需要懂AI原理,只要会写REST请求,就能让SpringBoot项目拥有专业级图像生成能力。

我见过太多团队为这类需求折腾半年:要么采购昂贵的SaaS服务,要么自己训练模型,要么找外包公司定制。而Nano-Banana的API调用方式,和我们日常调用支付接口、短信接口没有任何区别。它不关心你用的是SpringBoot还是Vert.x,不挑剔你的JDK版本,甚至对Maven依赖都极其友好。

更重要的是,它解决了Java生态里长期存在的一个痛点:图像处理能力薄弱。Java有ImageIO,有OpenCV封装,但这些工具擅长的是「修改图片」,而不是「创造图片」。当业务需要从零生成一张符合品牌调性的海报时,传统Java技术栈就显得力不从心。Nano-Banana恰恰填补了这个空白。

2. SpringBoot项目集成实战

2.1 环境准备与依赖配置

先说最让人头疼的部分——环境配置。好消息是,Nano-Banana的Java集成几乎零门槛。我们不需要下载任何SDK,也不用配置复杂的本地环境,只需要在pom.xml里添加几个基础依赖:

<dependencies>
    <!-- SpringBoot Web基础 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    
    <!-- HTTP客户端(推荐使用OkHttp,比RestTemplate更现代) -->
    <dependency>
        <groupId>com.squareup.okhttp3</groupId>
        <artifactId>okhttp</artifactId>
        <version>4.12.0</version>
    </dependency>
    
    <!-- JSON处理 -->
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
    </dependency>
    
    <!-- Lombok简化代码 -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
</dependencies>

注意,这里没有Nano-Banana官方SDK。原因很简单:它的API设计得足够标准,完全遵循RESTful规范,用任何HTTP客户端都能轻松调用。我们选择OkHttp是因为它在异步处理、连接池管理、超时控制方面比Spring自带的RestTemplate更成熟。

2.2 配置文件与客户端初始化

在application.yml中添加配置项,把敏感信息和可变参数抽离出来:

nano-banana:
  api-key: your-api-key-here
  base-url: https://api.grsai.com
  timeout:
    connect: 5000
    read: 120000
    write: 5000
  default-image-size: "2K"
  default-aspect-ratio: "1:1"

创建一个配置类来加载这些参数:

@Configuration
@ConfigurationProperties(prefix = "nano-banana")
@Data
public class NanoBananaProperties {
    private String apiKey;
    private String baseUrl;
    private TimeoutConfig timeout;
    private String defaultImageSize;
    private String defaultAspectRatio;
    
    @Data
    public static class TimeoutConfig {
        private long connect;
        private long read;
        private long write;
    }
}

然后初始化OkHttpClient,这是整个集成的核心:

@Configuration
public class NanoBananaConfig {
    
    @Bean
    @ConditionalOnMissingBean
    public OkHttpClient nanoBananaClient(NanoBananaProperties properties) {
        return new OkHttpClient.Builder()
                .connectTimeout(properties.getTimeout().getConnect(), TimeUnit.MILLISECONDS)
                .readTimeout(properties.getTimeout().getRead(), TimeUnit.MILLISECONDS)
                .writeTimeout(properties.getTimeout().getWrite(), TimeUnit.MILLISECONDS)
                .build();
    }
    
    @Bean
    public NanoBananaService nanoBananaService(
            OkHttpClient client,
            NanoBananaProperties properties,
            ObjectMapper objectMapper) {
        return new NanoBananaService(client, properties, objectMapper);
    }
}

2.3 核心服务类实现

现在进入最关键的一步:编写NanoBananaService。这个类要解决三个核心问题:认证、请求构建、响应处理。

@Service
@Slf4j
public class NanoBananaService {
    
    private final OkHttpClient httpClient;
    private final NanoBananaProperties properties;
    private final ObjectMapper objectMapper;
    
    public NanoBananaService(OkHttpClient httpClient, 
                           NanoBananaProperties properties, 
                           ObjectMapper objectMapper) {
        this.httpClient = httpClient;
        this.properties = properties;
        this.objectMapper = objectMapper;
    }
    
    /**
     * 同步生成图片 - 适用于小尺寸、低复杂度请求
     */
    public ImageGenerationResult generateImageSync(String prompt) {
        try {
            // 构建请求体
            GenerateImageRequest request = GenerateImageRequest.builder()
                    .model("nano-banana-pro")
                    .prompt(prompt)
                    .aspectRatio(properties.getDefaultAspectRatio())
                    .imageSize(properties.getDefaultImageSize())
                    .shutProgress(true)
                    .build();
            
            // 构建HTTP请求
            RequestBody body = RequestBody.create(
                    objectMapper.writeValueAsString(request),
                    MediaType.get("application/json; charset=utf-8")
            );
            
            Request httpRequest = new Request.Builder()
                    .url(properties.getBaseUrl() + "/v1/draw/nano-banana")
                    .post(body)
                    .header("Content-Type", "application/json")
                    .header("Authorization", "Bearer " + properties.getApiKey())
                    .build();
            
            // 执行请求
            try (Response response = httpClient.newCall(httpRequest).execute()) {
                if (!response.isSuccessful()) {
                    throw new RuntimeException("API call failed: " + response.code());
                }
                
                String responseBody = response.body().string();
                GenerateImageResponse apiResponse = objectMapper.readValue(responseBody, GenerateImageResponse.class);
                
                if ("succeeded".equals(apiResponse.getStatus())) {
                    String imageUrl = apiResponse.getResults().get(0).getUrl();
                    return ImageGenerationResult.success(imageUrl);
                } else {
                    throw new RuntimeException("Generation failed: " + apiResponse.getError());
                }
            }
            
        } catch (Exception e) {
            log.error("Failed to generate image", e);
            return ImageGenerationResult.failure(e.getMessage());
        }
    }
    
    /**
     * 异步生成图片 - 推荐用于生产环境
     * 使用WebHook回调,避免HTTP长连接超时
     */
    public CompletableFuture<ImageGenerationResult> generateImageAsync(
            String prompt, String webhookUrl) {
        
        return CompletableFuture.supplyAsync(() -> {
            try {
                GenerateImageRequest request = GenerateImageRequest.builder()
                        .model("nano-banana-pro")
                        .prompt(prompt)
                        .webHook(webhookUrl)
                        .build();
                
                RequestBody body = RequestBody.create(
                        objectMapper.writeValueAsString(request),
                        MediaType.get("application/json; charset=utf-8")
                );
                
                Request httpRequest = new Request.Builder()
                        .url(properties.getBaseUrl() + "/v1/draw/nano-banana")
                        .post(body)
                        .header("Content-Type", "application/json")
                        .header("Authorization", "Bearer " + properties.getApiKey())
                        .build();
                
                try (Response response = httpClient.newCall(httpRequest).execute()) {
                    if (!response.isSuccessful()) {
                        throw new RuntimeException("API call failed: " + response.code());
                    }
                    
                    String responseBody = response.body().string();
                    GenerateImageResponse apiResponse = objectMapper.readValue(responseBody, GenerateImageResponse.class);
                    
                    if (apiResponse.getCode() == 0 && apiResponse.getData() != null) {
                        String taskId = apiResponse.getData().getId();
                        return ImageGenerationResult.taskSubmitted(taskId);
                    } else {
                        throw new RuntimeException("Task submission failed");
                    }
                }
                
            } catch (Exception e) {
                log.error("Failed to submit async task", e);
                return ImageGenerationResult.failure(e.getMessage());
            }
        });
    }
}

这里的关键设计点在于提供了同步和异步两种调用方式。同步方式适合开发测试和简单场景,而异步方式才是生产环境的推荐方案。为什么?因为AI图像生成耗时不稳定,可能从几秒到两分钟不等。如果用同步方式,你的SpringBoot线程会被长时间占用,影响整体吞吐量。

2.4 响应数据结构定义

为了保持代码清晰,我们定义专门的DTO类:

// 请求体
@Builder
@Data
public class GenerateImageRequest {
    private String model;
    private String prompt;
    private String aspectRatio;
    private String imageSize;
    private List<String> urls;
    private Boolean shutProgress;
    private String webHook;
}

// 响应体
@Data
public class GenerateImageResponse {
    private String status;
    private String error;
    private Integer code;
    private Data data;
    private List<Result> results;
    
    @Data
    public static class Data {
        private String id;
    }
    
    @Data
    public static class Result {
        private String url;
    }
}

// 封装结果,便于业务层处理
@Data
@Builder
public class ImageGenerationResult {
    private boolean success;
    private String imageUrl;
    private String taskId;
    private String errorMessage;
    
    public static ImageGenerationResult success(String imageUrl) {
        return ImageGenerationResult.builder()
                .success(true)
                .imageUrl(imageUrl)
                .build();
    }
    
    public static ImageGenerationResult taskSubmitted(String taskId) {
        return ImageGenerationResult.builder()
                .success(true)
                .taskId(taskId)
                .build();
    }
    
    public static ImageGenerationResult failure(String errorMessage) {
        return ImageGenerationResult.builder()
                .success(false)
                .errorMessage(errorMessage)
                .build();
    }
}

2.5 控制器层实现

最后是Controller,这里展示如何在实际业务中使用:

@RestController
@RequestMapping("/api/images")
@Slf4j
public class ImageGenerationController {
    
    private final NanoBananaService nanoBananaService;
    private final ObjectMapper objectMapper;
    
    public ImageGenerationController(NanoBananaService nanoBananaService, 
                                   ObjectMapper objectMapper) {
        this.nanoBananaService = nanoBananaService;
        this.objectMapper = objectMapper;
    }
    
    /**
     * 同步生成 - 用于快速验证
     */
    @PostMapping("/sync")
    public ResponseEntity<?> generateSync(@RequestBody GenerateImageRequest request) {
        ImageGenerationResult result = nanoBananaService.generateImageSync(request.getPrompt());
        
        if (result.isSuccess()) {
            return ResponseEntity.ok(Map.of("imageUrl", result.getImageUrl()));
        } else {
            return ResponseEntity.badRequest().body(Map.of("error", result.getErrorMessage()));
        }
    }
    
    /**
     * 异步生成 - 生产环境推荐
     */
    @PostMapping("/async")
    public CompletableFuture<ResponseEntity<?>> generateAsync(
            @RequestBody GenerateImageRequest request,
            @RequestHeader(value = "X-Callback-Url", required = false) String callbackUrl) {
        
        String webhookUrl = callbackUrl != null ? callbackUrl : "https://your-domain.com/api/webhook/image-generated";
        
        return nanoBananaService.generateImageAsync(request.getPrompt(), webhookUrl)
                .thenApply(result -> {
                    if (result.isSuccess()) {
                        if (result.getTaskId() != null) {
                            return ResponseEntity.accepted()
                                    .body(Map.of("taskId", result.getTaskId(), "status", "submitted"));
                        } else {
                            return ResponseEntity.ok(Map.of("imageUrl", result.getImageUrl()));
                        }
                    } else {
                        return ResponseEntity.badRequest()
                                .body(Map.of("error", result.getErrorMessage()));
                    }
                });
    }
    
    /**
     * WebHook回调接收端
     * 这个接口会被Nano-Banana服务调用,通知图片生成完成
     */
    @PostMapping("/webhook")
    public ResponseEntity<?> handleWebhook(@RequestBody WebhookPayload payload) {
        try {
            // 验证签名(生产环境必须实现)
            if (!isValidSignature(payload)) {
                return ResponseEntity.status(401).build();
            }
            
            // 处理生成结果
            if ("succeeded".equals(payload.getStatus())) {
                String imageUrl = payload.getResults().get(0).getUrl();
                String taskId = payload.getTaskId();
                
                // 更新数据库状态,发送消息通知,触发后续流程...
                log.info("Image generation succeeded for task: {}, url: {}", taskId, imageUrl);
                
                // 这里可以调用业务逻辑,比如更新订单状态、发送邮件通知等
                processGeneratedImage(taskId, imageUrl);
            } else {
                log.warn("Image generation failed for task: {}, error: {}", 
                        payload.getTaskId(), payload.getError());
            }
            
            return ResponseEntity.ok(Map.of("status", "received"));
            
        } catch (Exception e) {
            log.error("Error handling webhook", e);
            return ResponseEntity.status(500).build();
        }
    }
    
    private boolean isValidSignature(WebhookPayload payload) {
        // 实现签名验证逻辑,防止恶意请求
        // 可以使用HMAC-SHA256等算法
        return true;
    }
    
    private void processGeneratedImage(String taskId, String imageUrl) {
        // 业务处理逻辑示例:
        // 1. 更新数据库中任务状态
        // 2. 将图片URL存入CDN
        // 3. 发送消息到MQ,触发后续处理(如水印添加、尺寸转换等)
        // 4. 通知前端用户生成完成
    }
}

3. 异步调用优化技巧

3.1 为什么必须用异步

在Java世界里,「异步」这个词经常被滥用。很多开发者以为加个@Async注解就是异步了,但实际上,真正的异步调用要考虑三个维度:线程模型、错误处理、状态追踪。

Nano-Banana的异步模式之所以重要,是因为它从根本上改变了我们的系统架构思维。想象一下这个场景:电商大促期间,每秒有上百个用户同时请求生成商品海报。如果用同步方式,每个请求都要占用一个Tomcat线程等待10-30秒,很快就会耗尽线程池,导致系统雪崩。

而异步模式下,我们的SpringBoot应用只做三件事:接收请求、提交任务、返回响应。真正的图像生成工作由Nano-Banana服务完成,我们的系统只是个协调者。这种架构让我们能够用极小的资源支撑巨大的并发量。

3.2 WebHook回调的最佳实践

WebHook看似简单,但在生产环境中却暗藏陷阱。我总结了几个血泪教训:

第一,签名验证必不可少。不要相信任何未经验证的回调请求。Nano-Banana会在回调请求头中包含签名,你需要用相同的密钥和算法验证。否则,攻击者可以伪造回调,让你的系统执行恶意操作。

第二,幂等性设计。网络不可靠,同一个回调可能会重复到达多次。你的WebHook处理器必须能识别并忽略重复请求。最简单的办法是记录每个task_id的处理状态,如果发现已处理过,直接返回成功。

第三,错误重试机制。当你的WebHook服务暂时不可用时,Nano-Banana会重试几次。你需要确保重试窗口内服务能恢复,或者实现自己的重试队列。

下面是一个健壮的WebHook处理器示例:

@Component
@Slf4j
public class WebhookProcessor {
    
    private final RedisTemplate<String, Object> redisTemplate;
    private final TaskService taskService;
    
    public WebhookProcessor(RedisTemplate<String, Object> redisTemplate, 
                          TaskService taskService) {
        this.redisTemplate = redisTemplate;
        this.taskService = taskService;
    }
    
    public void processWebhook(WebhookPayload payload) {
        String taskId = payload.getTaskId();
        String cacheKey = "webhook:processed:" + taskId;
        
        // 使用Redis SETNX实现分布式锁和幂等性
        Boolean isSet = redisTemplate.opsForValue()
                .setIfAbsent(cacheKey, System.currentTimeMillis(), 
                           Duration.ofHours(24));
        
        if (Boolean.TRUE.equals(isSet)) {
            try {
                if ("succeeded".equals(payload.getStatus())) {
                    handleSuccess(payload);
                } else {
                    handleFailure(payload);
                }
            } catch (Exception e) {
                log.error("Error processing webhook for task: {}", taskId, e);
                // 记录失败,稍后重试
                retryLater(taskId, payload);
            }
        } else {
            log.info("Webhook for task {} already processed", taskId);
        }
    }
    
    private void handleSuccess(WebhookPayload payload) {
        String taskId = payload.getTaskId();
        String imageUrl = payload.getResults().get(0).getUrl();
        
        // 更新任务状态
        taskService.updateStatus(taskId, TaskStatus.COMPLETED, imageUrl);
        
        // 发布事件,触发后续流程
        ApplicationEventPublisher publisher = ...;
        publisher.publishEvent(new ImageGeneratedEvent(taskId, imageUrl));
        
        // 清理临时资源
        cleanupTemporaryResources(taskId);
    }
    
    private void retryLater(String taskId, WebhookPayload payload) {
        // 使用延迟队列或定时任务重试
        // 这里可以集成RocketMQ延迟消息、Redis ZSET等
        log.warn("Scheduling retry for task: {}", taskId);
    }
}

3.3 任务状态查询API

虽然WebHook很强大,但有时候你还是需要主动查询任务状态。比如用户在页面上点击「查看进度」,或者你的系统需要做超时处理。

Nano-Banana提供了任务查询接口,我们可以封装成一个服务:

@Service
@Slf4j
public class TaskQueryService {
    
    private final OkHttpClient httpClient;
    private final NanoBananaProperties properties;
    private final ObjectMapper objectMapper;
    
    public TaskQueryService(OkHttpClient httpClient, 
                           NanoBananaProperties properties, 
                           ObjectMapper objectMapper) {
        this.httpClient = httpClient;
        this.properties = properties;
        this.objectMapper = objectMapper;
    }
    
    public TaskStatusResponse queryTaskStatus(String taskId) {
        try {
            String url = properties.getBaseUrl() + "/v1/task/" + taskId;
            
            Request httpRequest = new Request.Builder()
                    .url(url)
                    .header("Authorization", "Bearer " + properties.getApiKey())
                    .build();
            
            try (Response response = httpClient.newCall(httpRequest).execute()) {
                if (!response.isSuccessful()) {
                    throw new RuntimeException("Query failed: " + response.code());
                }
                
                String responseBody = response.body().string();
                return objectMapper.readValue(responseBody, TaskStatusResponse.class);
            }
            
        } catch (Exception e) {
            log.error("Failed to query task status", e);
            return TaskStatusResponse.failed(e.getMessage());
        }
    }
}

@Data
public class TaskStatusResponse {
    private String status; // pending, running, succeeded, failed
    private String error;
    private String resultUrl;
    private Long progress;
    
    public static TaskStatusResponse failed(String error) {
        TaskStatusResponse response = new TaskStatusResponse();
        response.setStatus("failed");
        response.setError(error);
        return response;
    }
}

4. 实际业务场景落地

4.1 电商商品海报自动生成

这是我们团队第一个落地的场景。需求很简单:商家上传一张商品主图,系统自动生成5种不同风格的营销海报。

关键在于提示词(prompt)的设计。我们没有让用户自己写,而是做了个智能模板:

@Service
public class EcommercePromptService {
    
    public String generateEcommercePrompt(String productName, 
                                       String productDescription,
                                       String style) {
        switch (style.toLowerCase()) {
            case "luxury":
                return String.format("高端奢侈品广告风格,%s,%s,纯白背景,电影级灯光,超高清细节,商业摄影,8K分辨率", 
                                   productName, productDescription);
            case "youth":
                return String.format("年轻活力风格,%s,%s,明亮色彩,动感构图,社交媒体配图,清新自然", 
                                   productName, productDescription);
            case "minimalist":
                return String.format("极简主义风格,%s,%s,大量留白,精致排版,柔和色调,高级感", 
                                   productName, productDescription);
            default:
                return String.format("%s,%s,专业产品摄影,高清细节,纯色背景", 
                                   productName, productDescription);
        }
    }
}

这样,前端只需要提供商品名称、描述和风格选项,后端就能生成精准的prompt。实测下来,生成的海报质量远超预期,连设计师都惊叹「这已经可以直接用了」。

4.2 用户头像个性化生成

第二个场景是社交App的头像生成。用户注册时,除了上传照片,还可以选择「AI生成个性化头像」。

这里有个巧妙的设计:我们把用户的基本信息(姓名、年龄、职业、兴趣标签)作为prompt的一部分,让生成的头像更符合用户画像。

public String generateAvatarPrompt(UserProfile profile) {
    StringBuilder sb = new StringBuilder();
    sb.append("专业人物肖像,");
    
    // 根据年龄调整风格
    if (profile.getAge() < 18) {
        sb.append("青春洋溢的学生形象,");
    } else if (profile.getAge() < 35) {
        sb.append("时尚干练的职场人士形象,");
    } else {
        sb.append("沉稳自信的成功人士形象,");
    }
    
    // 根据职业添加元素
    switch (profile.getOccupation()) {
        case "developer":
            sb.append("佩戴智能眼镜,背景有代码元素,");
            break;
        case "designer":
            sb.append("手持数位板,背景有色彩调色板,");
            break;
        case "teacher":
            sb.append("手持书本,背景有黑板元素,");
            break;
    }
    
    // 添加兴趣标签
    if (!profile.getInterests().isEmpty()) {
        sb.append("体现").append(String.join("、", profile.getInterests())).append("兴趣,");
    }
    
    sb.append("高清人像摄影,自然光线,浅景深,专业肖像照");
    return sb.toString();
}

这个功能上线后,新用户注册转化率提升了23%,因为很多人觉得「AI生成的头像比自己拍的还好看」。

4.3 批量图片处理系统

最后一个场景是批量处理。某教育机构需要为1000门课程生成统一风格的封面图。

如果逐个调用API,不仅慢,还可能触发限流。我们设计了一个批量处理服务:

@Service
@Slf4j
public class BatchImageService {
    
    private final NanoBananaService nanoBananaService;
    private final ExecutorService batchExecutor;
    
    public BatchImageService(NanoBananaService nanoBananaService) {
        this.nanoBananaService = nanoBananaService;
        // 创建专用线程池,避免影响主线程
        this.batchExecutor = Executors.newFixedThreadPool(
                Runtime.getRuntime().availableProcessors() * 2);
    }
    
    public BatchResult processBatch(List<CourseInfo> courses) {
        List<CompletableFuture<BatchItemResult>> futures = new ArrayList<>();
        
        for (CourseInfo course : courses) {
            String prompt = generateCourseCoverPrompt(course);
            
            CompletableFuture<BatchItemResult> future = 
                nanoBananaService.generateImageAsync(prompt, 
                    "https://your-domain.com/api/webhook/batch-complete")
                    .thenApply(result -> {
                        if (result.isSuccess() && result.getTaskId() != null) {
                            return BatchItemResult.submitted(course.getId(), result.getTaskId());
                        } else {
                            return BatchItemResult.failed(course.getId(), result.getErrorMessage());
                        }
                    });
            
            futures.add(future);
        }
        
        // 等待所有任务提交完成
        CompletableFuture<Void> allOf = CompletableFuture.allOf(
            futures.toArray(new CompletableFuture[0]));
        
        try {
            allOf.join();
        } catch (Exception e) {
            log.error("Batch submission failed", e);
        }
        
        // 收集结果
        List<BatchItemResult> results = futures.stream()
                .map(CompletableFuture::join)
                .collect(Collectors.toList());
        
        return BatchResult.builder()
                .total(courses.size())
                .submitted((int) results.stream().filter(r -> r.isSubmitted()).count())
                .failed((int) results.stream().filter(r -> r.isFailed()).count())
                .results(results)
                .build();
    }
}

这套系统让原本需要一周的手动设计工作,缩短到了2小时以内,而且风格高度统一。

5. 性能与稳定性保障

5.1 连接池与超时配置

在高并发场景下,HTTP客户端的配置直接影响系统稳定性。我们针对Nano-Banana的特点做了专门优化:

@Bean
public OkHttpClient optimizedNanoBananaClient(NanoBananaProperties properties) {
    return new OkHttpClient.Builder()
            // 连接池:保持长连接,减少握手开销
            .connectionPool(new ConnectionPool(20, 5, TimeUnit.MINUTES))
            
            // 超时设置:根据AI服务特性调整
            .connectTimeout(properties.getTimeout().getConnect(), TimeUnit.MILLISECONDS)
            .readTimeout(properties.getTimeout().getRead(), TimeUnit.MILLISECONDS) // 读取超时设为2分钟
            .writeTimeout(properties.getTimeout().getWrite(), TimeUnit.MILLISECONDS)
            
            // 重试机制:对网络错误自动重试
            .addInterceptor(new RetryInterceptor(3))
            
            // 日志拦截器:方便调试
            .addInterceptor(new HttpLoggingInterceptor()
                    .setLevel(HttpLoggingInterceptor.Level.BODY))
            
            .build();
}

特别要注意的是readTimeout的设置。AI生成时间波动很大,我们设为2分钟,既给了足够时间,又不会让线程无限等待。

5.2 降级与熔断策略

任何外部依赖都可能出问题。我们实现了三级防御:

第一级:本地缓存。对于相同prompt的请求,我们用Caffeine缓存结果,TTL设为1小时。毕竟,同一张海报不需要反复生成。

第二级:服务降级。当Nano-Banana服务不可用时,自动切换到备用方案——返回一张预设的静态图片,或者调用本地OpenCV做简单处理。

第三级:熔断器。使用Resilience4j实现熔断,当错误率达到阈值时,自动停止调用,避免雪崩。

@Bean
public CircuitBreaker circuitBreaker() {
    CircuitBreakerConfig config = CircuitBreakerConfig.custom()
            .failureRateThreshold(50) // 错误率50%触发熔断
            .waitDurationInOpenState(Duration.ofMinutes(1)) // 熔断1分钟后尝试半开
            .slidingWindowSize(10) // 统计最近10次调用
            .build();
    
    return CircuitBreaker.of("nanoBanana", config);
}

// 在服务调用处使用
public ImageGenerationResult generateWithCircuitBreaker(String prompt) {
    return circuitBreaker.executeSupplier(() -> 
        nanoBananaService.generateImageSync(prompt)
    );
}

5.3 监控与告警

最后但同样重要的是可观测性。我们在关键路径上添加了Micrometer指标:

@Component
public class NanoBananaMetrics {
    
    private final MeterRegistry meterRegistry;
    
    public NanoBananaMetrics(MeterRegistry meterRegistry) {
        this.meterRegistry = meterRegistry;
        
        // 创建计数器
        Counter.builder("nano.banana.requests")
                .description("Total number of NanoBanana requests")
                .register(meterRegistry);
        
        // 创建直方图,监控响应时间
        DistributionSummary.builder("nano.banana.response.time")
                .description("NanoBanana response time in milliseconds")
                .publishPercentiles(0.5, 0.95, 0.99)
                .register(meterRegistry);
    }
    
    public void recordRequest(String status, long durationMs) {
        Counter.builder("nano.banana.requests")
                .tag("status", status)
                .register(meterRegistry)
                .increment();
        
        DistributionSummary.builder("nano.banana.response.time")
                .tag("status", status)
                .register(meterRegistry)
                .record(durationMs);
    }
}

配合Prometheus和Grafana,我们可以实时看到API调用成功率、平均响应时间、错误类型分布等关键指标。当某个指标异常时,企业微信机器人会自动发送告警。


获取更多AI镜像

想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐