1. 引言
在云应用开发中,我们经常需要编写代码与外部系统进行网络通信。这时就可以利用 Ambassador 模式创建一个统一组件,封装所有网络相关逻辑,显著提升代码复用性。
本文将深入探讨 Ambassador 模式,分析其适用场景,并通过 Java 实现展示具体用法。
2. 什么是 Ambassador 模式?
Ambassador 模式是一种结构型设计模式,充当客户端与服务端之间的网络代理。可以将其理解为封装了所有网络通信细节的库。
它的核心功能包括:
- ✅ 网络路由抽象
- ✅ 可观测性(日志/监控)
- ✅ 重试和熔断机制
- ✅ 缓存和安全流程
当部署在独立容器时,它还能提供语言无关的通信能力,因为所有交互都通过网络接口完成。我们可以把网络客户端逻辑封装在称为 Ambassador 的独立库或容器中,作为依赖项集成到代码中,或通过 API 暴露给外部调用。
3. Java 实现 Ambassador 模式
本节我们将实现一个作为 HTTP 调用代理的 Ambassador,集成重试、超时和日志输出功能:
注意 Ambassador 代码与客户端代码在同一容器中运行。客户端只需简单调用 Ambassador 方法即可从 names-api 获取结果。
3.1. 基础配置
首先添加必要依赖,使用 spring-web 进行 HTTP 调用:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>6.2.7</version>
</dependency>
再添加 spring-retry 实现重试逻辑:
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
<version>2.0.12</version>
</dependency>
在 application.properties 中配置超时参数和 API 地址:
http.client.connect-timeout-seconds=2000
http.client.read-timeout-seconds=3000
names-api-url=https://api.example.com/names/v1
创建使用预定义超时配置的 RestTemplate Bean:
@Configuration
public class RestTemplateConfig {
private final int connectTimeoutSeconds;
private final int readTimeoutSeconds;
private final RestTemplateBuilder restTemplateBuilder;
// 全参构造器
@Bean
public RestTemplate restTemplate() {
return restTemplateBuilder.setConnectTimeout(Duration.ofMillis(connectTimeoutSeconds))
.setReadTimeout(Duration.ofMillis(readTimeoutSeconds))
.build();
}
}
最后在 Spring Boot 主类添加注解:
@EnableRetry
@EnableCaching
@SpringBootApplication
public class AmbassadorApplication {
public static void main(String[] args) {
SpringApplication.run(AmbassadorApplication.class, args);
}
}
3.2. 实现 Ambassador HTTP 客户端
现在创建作为 Ambassador 的 HTTP 客户端:
@Component
public class HttpAmbassadorNamesClient {
private final RestTemplate restTemplate;
private final Logger logger = LoggerFactory.getLogger(HttpAmbassadorNamesClient.class);
public final String apiUrl;
public HttpAmbassadorNamesClient(RestTemplate restTemplate, @Value("${names-api-url}") String apiUrl) {
this.restTemplate = restTemplate;
this.apiUrl = apiUrl;
}
@Cacheable(value = "httpResponses", key = "#root.target.apiUrl", unless = "#result == null")
@Retryable(value = { HttpServerErrorException.class }, maxAttempts = 5, backoff = @Backoff(delay = 1000))
public String getResponse() {
try {
String result = restTemplate.getForObject(apiUrl, String.class);
logger.info("HTTP 调用成功完成 url={}", apiUrl);
return result;
} catch (HttpClientErrorException e) {
logger.error("HTTP 客户端错误 错误码={} 信息={}", e.getStatusCode(), e.getMessage());
throw e;
}
}
@Recover
public String recover(Exception e) {
final String defaultResponse = "default";
logger.error("重试次数耗尽,回退到默认值. 错误={} 默认值={}", e.getMessage(), defaultResponse);
return defaultResponse;
}
}
HttpAmbassadorNamesClient 类的核心职责是提供完整配置的客户端代码来获取外部名称数据。我们注入了预配置的 RestTemplate 和 API 地址。
getResponse() 方法的关键设计点:
- 使用
@Cacheable
注解实现内存缓存(为简化演示未设置 TTL,实际生产环境需注意) - 通过
@Retryable
配置重试策略:对 5xx 错误最多重试 5 次,间隔 1 秒 - 使用 getForObject() 执行 REST 调用,成功时记录日志并返回结果
- 捕获 HttpClientErrorException 并记录错误日志后重新抛出
现在只需在客户端代码中注入 HttpAmbassadorNamesClient,调用 getResponse() 即可完成 REST 调用。
4. Ambassador 作为 Sidecar 容器
我们还可以将 Ambassador 部署在独立容器中,通过暴露的 REST API 提供服务。这样就能实现语言无关的 API 调用,统一管理重试、缓存等逻辑:
由于客户端和 Ambassador 在不同容器,需要通过网络通信获取 names-api 数据。
典型场景:当两个不同语言(如 Python 和 Go)的客户端服务都需要调用同一个 API 时,传统方案需要重复实现两套网络客户端逻辑。而 Ambassador sidecar 方案只需让两个客户端调用 Ambassador 接口即可。
4.1. 暴露 Ambassador API
创建 Ambassador 的 REST 接口:
@RestController
@RequestMapping("/v1/ambassador/names")
public class HttpAmbassadorController {
private final HttpAmbassadorNamesClient httpAmbassadorNamesClient;
public HttpAmbassadorController(HttpAmbassadorNamesClient httpAmbassadorNamesClient) {
this.httpAmbassadorNamesClient = httpAmbassadorNamesClient;
}
@GetMapping
public String get() {
return httpAmbassadorNamesClient.getResponse();
}
}
这里暴露了 /v1/ambassador/names 接口,所有客户端都可以通过这个 Ambassador 访问外部名称 API。
核心价值:将超时、重试、缓存等复杂逻辑封装在单一位置,避免在不同应用中重复实现。例如 Python 和 Go 应用只需调用 Ambassador 接口,即可获得完整的网络通信能力。
5. 优缺点分析
优势
- 代码复用性:无论是作为依赖库还是独立 API,所有网络逻辑只需编写一次即可被多个客户端使用
- 维护性提升:所有网络相关修改(如更换 API 地址、调整响应字段)只需在 Ambassador 中单点维护
- 解耦能力:客户端无需关心网络通信细节,只需调用 Ambassador 提供的简单接口
劣势
- 性能损耗:Sidecar 方案增加额外网络层,必然引入延迟 ⚠️ 不适合:对延迟极其敏感的系统(如实时交易)
- 可用性风险:增加 Ambassador 容器作为潜在故障点 ❌ 可能影响:系统整体可用性,需做好容器健康检查
6. 总结
本文展示了如何实现配置完善的 Ambassador 应用,作为不同客户端的网络代理。我们通过单一位置集成了重试、缓存、超时等机制,显著提升了系统可维护性。
同时我们也分析了其局限性——在延迟敏感系统或高可用要求场景中需谨慎使用。简单粗暴地说:Ambassador 模式是中大型系统的网络通信利器,但在简单项目或极端性能场景中可能过度设计。
源码示例:GitHub 仓库