mirror of
https://github.com/tiennm99/java-design-patterns.git
synced 2026-05-17 12:59:54 +00:00
dc7495e8bd
* Typo corrections * bridge pattern translated to spanish * builder pattern translated to spanish * context-object pattern translated to spanish * converter pattern translated to spanish * dependency injection pattern translated to spanish * factory pattern translated to spanish * factory-kit pattern translated to spanish * factory-method pattern translated to spanish * monostate pattern translated to spanish * multiton pattern translated to spanish * object mother pattern translated to spanish * object pool pattern translated to spanish * property pattern translated to spanish * prototype pattern translated to spanish * registry pattern translated to spanish * step builder pattern translated to spanish * value object pattern translated to spanish * typo corrections and resources urls refactor * grammar corrections * Add image to each pattern --------- Co-authored-by: luismateoh <luismateohm@gmail.com> Co-authored-by: luis.hincapie <luis.hincapie@blankfactor.com>
209 lines
6.4 KiB
Markdown
209 lines
6.4 KiB
Markdown
---
|
|
title: Ambassador
|
|
category: Structural
|
|
language: es
|
|
tag:
|
|
- Decoupling
|
|
- Cloud distributed
|
|
---
|
|
|
|
## Propósito
|
|
|
|
Proporcionar una instancia de servicio auxiliar a un cliente y delegar en ella las funcionalidades comunes de un recurso compartido.
|
|
|
|
## Explicación
|
|
|
|
Ejemplo real
|
|
|
|
> Un servicio remoto tiene muchos clientes accediendo a una función que este servicio proporciona. El servicio es una aplicación heredada y
|
|
> es imposible actualizarla. Un gran número de solicitudes por parte de los usuarios están causando problemas de conectividad. Nuevas reglas
|
|
> respecto a la frecuencia de solicitudes deberían implementarse junto con comprobaciones de latencia y registros del lado del cliente.
|
|
|
|
En otras palabras
|
|
|
|
> Con el patrón Ambassador, podemos implementar una menor frecuencia en solicitudes de clientes junto con comprobaciones de latencia y
|
|
> registros.
|
|
|
|
Según la Documentación de Microsoft
|
|
|
|
> Un servicio de Ambassador puede considerarse como un proxy fuera de proceso que coexiste con el cliente.
|
|
>
|
|
> Este patrón puede ser útil para la descarga de tareas comunes de conectividad de cliente, como la supervisión, el registro, el enrutamiento,
|
|
> la seguridad (por ejemplo, TLS) y los patrones de resistencia(*) de una manera independiente del lenguaje. A menudo se utiliza con aplicaciones heredadas,
|
|
> u otras aplicaciones que son difíciles de modificar, con el fin de ampliar sus capacidades de red. También puede
|
|
> habilitar un equipo especializado para implementar esas características.
|
|
|
|
**Código de ejemplo**
|
|
|
|
Con la introducción anterior en mente vamos a imitar su funcionalidad en el siguiente ejemplo. Tenemos una interface implementada
|
|
por el servicio remoto así como el servicio ambassador:
|
|
|
|
```java
|
|
interface RemoteServiceInterface {
|
|
long doRemoteFunction(int value) throws Exception;
|
|
}
|
|
```
|
|
|
|
Un servicio remoto representado como un singleton (Instancia única).
|
|
|
|
```java
|
|
@Slf4j
|
|
public class RemoteService implements RemoteServiceInterface {
|
|
private static RemoteService service = null;
|
|
|
|
static synchronized RemoteService getRemoteService() {
|
|
if (service == null) {
|
|
service = new RemoteService();
|
|
}
|
|
return service;
|
|
}
|
|
|
|
private RemoteService() {}
|
|
|
|
@Override
|
|
public long doRemoteFunction(int value) {
|
|
long waitTime = (long) Math.floor(Math.random() * 1000);
|
|
|
|
try {
|
|
sleep(waitTime);
|
|
} catch (InterruptedException e) {
|
|
LOGGER.error("Thread sleep interrupted", e);
|
|
}
|
|
|
|
return waitTime >= 200 ? value * 10 : -1;
|
|
}
|
|
}
|
|
```
|
|
|
|
Un servicio ambassador añadiendo funcionalidades adicionales como registros, comprobación de latencia
|
|
|
|
```java
|
|
@Slf4j
|
|
public class ServiceAmbassador implements RemoteServiceInterface {
|
|
private static final int RETRIES = 3;
|
|
private static final int DELAY_MS = 3000;
|
|
|
|
ServiceAmbassador() {
|
|
}
|
|
|
|
@Override
|
|
public long doRemoteFunction(int value) {
|
|
return safeCall(value);
|
|
}
|
|
|
|
private long checkLatency(int value) {
|
|
var startTime = System.currentTimeMillis();
|
|
var result = RemoteService.getRemoteService().doRemoteFunction(value);
|
|
var timeTaken = System.currentTimeMillis() - startTime;
|
|
|
|
LOGGER.info("Time taken (ms): " + timeTaken);
|
|
return result;
|
|
}
|
|
|
|
private long safeCall(int value) {
|
|
var retries = 0;
|
|
var result = (long) FAILURE;
|
|
|
|
for (int i = 0; i < RETRIES; i++) {
|
|
if (retries >= RETRIES) {
|
|
return FAILURE;
|
|
}
|
|
|
|
if ((result = checkLatency(value)) == FAILURE) {
|
|
LOGGER.info("Failed to reach remote: (" + (i + 1) + ")");
|
|
retries++;
|
|
try {
|
|
sleep(DELAY_MS);
|
|
} catch (InterruptedException e) {
|
|
LOGGER.error("Thread sleep state interrupted", e);
|
|
}
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
}
|
|
```
|
|
|
|
Un cliente tiene un servicio ambassador local usado para interactuar con el servicio remoto:
|
|
|
|
```java
|
|
@Slf4j
|
|
public class Client {
|
|
private final ServiceAmbassador serviceAmbassador = new ServiceAmbassador();
|
|
|
|
long useService(int value) {
|
|
var result = serviceAmbassador.doRemoteFunction(value);
|
|
LOGGER.info("Service result: " + result);
|
|
return result;
|
|
}
|
|
}
|
|
```
|
|
|
|
A continuación dos clientes usando el servicio.
|
|
|
|
```java
|
|
public class App {
|
|
public static void main(String[] args) {
|
|
var host1 = new Client();
|
|
var host2 = new Client();
|
|
host1.useService(12);
|
|
host2.useService(73);
|
|
}
|
|
}
|
|
```
|
|
|
|
Esta es la salida que obtendremos tras ejecutar el ejemplo:
|
|
|
|
```java
|
|
Time taken (ms): 111
|
|
Service result: 120
|
|
Time taken (ms): 931
|
|
Failed to reach remote: (1)
|
|
Time taken (ms): 665
|
|
Failed to reach remote: (2)
|
|
Time taken (ms): 538
|
|
Failed to reach remote: (3)
|
|
Service result: -1
|
|
```
|
|
|
|
## Diagrama de clase
|
|
|
|

|
|
|
|
## Aplicaciones
|
|
|
|
Ambassador es aplicable cuando trabajamos con un servicio remoto heredado que no puede ser modificado o que sería extremamente
|
|
difícil de modificar. Las características de conectividad pueden implementarse en el cliente sin necesidad de realizar cambios en el servicio
|
|
remoto.
|
|
|
|
* Ambassador proporciona una interface local para un servicio remoto.
|
|
* Ambassador proporciona registros, interrupción de circuitos, reintentos y seguridad en el cliente.
|
|
|
|
## Casos de uso típicos
|
|
|
|
* Control de acceso a otro objeto
|
|
* Implementación de registros o logs
|
|
* Implementación de interrupciones de circuito
|
|
* Delegar tareas de servicios remotos
|
|
* Facilitar la conexión a la red
|
|
|
|
## Usos conocidos
|
|
|
|
* [Pasarela API Kubernetes-native para microservicios](https://github.com/datawire/ambassador)
|
|
|
|
## Patrones relacionados
|
|
|
|
* [Proxy](https://java-design-patterns.com/patterns/proxy/)
|
|
|
|
## Créditos
|
|
|
|
* [Ambassador Pattern (Documentación de Microsoft en inglés)](https://docs.microsoft.com/en-us/azure/architecture/patterns/ambassador)
|
|
* [Designing Distributed Systems: Patterns and Paradigms for Scalable, Reliable Services](https://www.amazon.com/s?k=designing+distributed+systems&sprefix=designing+distri%2Caps%2C156&linkCode=ll2&tag=javadesignpat-20&linkId=a12581e625462f9038557b01794e5341&language=en_US&ref_=as_li_ss_tl)
|
|
|
|
## Notas del traductor
|
|
(*) La versión original en inglés de la documentación de Microsoft hace referencia al término resiliencia y
|
|
en su traducción al español lo traduce como resistencia, aunque enlaza al apartado patrones de confiabilidad. Véase:
|
|
* [Versión de la Documentación para el Patrón Ambassador de Microsoft en español.](https://learn.microsoft.com/es-es/azure/architecture/patterns/ambassador)
|