Skip to content

Commit 5393c96

Browse files
committed
Adding README.md
1 parent bae51f5 commit 5393c96

File tree

1 file changed

+181
-0
lines changed

1 file changed

+181
-0
lines changed

ambassador/README.md

Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
---
2+
layout: pattern
3+
title: Ambassador
4+
folder: ambassador
5+
permalink: /patterns/ambassador/
6+
categories: Structural
7+
tags:
8+
- Java
9+
- Difficulty-Intermediate
10+
---
11+
12+
## Intent
13+
Provide a helper service that sends network requests on behalf of a client and offload common additional connectivity tasks.
14+
15+
## Explanation
16+
Real world example
17+
18+
> A remote service has many clients accessing a function it provides. The service is a legacy application and is impossible to update. Large numbers of requests from users are causing connectivity issues. New rules for request frequency should be implemented along with latency checks and client-side logging.
19+
20+
In plain words
21+
22+
> Using the ambassador pattern, we can implement less-frequent polling from clients along with latency checks and logging.
23+
24+
Microsoft documentation states
25+
26+
> An ambassador service can be thought of as an out-of-process proxy that is co-located with the client.
27+
This pattern can be useful for offloading common client connectivity tasks such as monitoring, logging, routing, security (such as TLS), and resiliency patterns in a language agnostic way. It is often used with legacy applications, or other applications that are difficult to modify, in order to extend their networking capabilities. It can also enable a specialized team to implement those features.
28+
29+
**Programmatic Example**
30+
31+
With the above example in mind we will imitate the functionality in a simple manner. We have an interface implemented by the remote service as well as the ambassador service:
32+
33+
```java
34+
interface RemoteServiceInterface {
35+
36+
long doRemoteFunction(int value) throws Exception;
37+
}
38+
```
39+
40+
A remote services represented as a singleton.
41+
42+
```java
43+
public class RemoteService implements RemoteServiceInterface {
44+
45+
private static RemoteService service = null;
46+
47+
static synchronized RemoteService getRemoteService() {
48+
if (service == null) {
49+
service = new RemoteService();
50+
}
51+
return service;
52+
}
53+
54+
private RemoteService() {
55+
56+
}
57+
58+
@Override
59+
public long doRemoteFunction(int value) {
60+
61+
long waitTime = (long) Math.floor(Math.random() * 1000);
62+
63+
try {
64+
sleep(waitTime);
65+
} catch (InterruptedException e) {
66+
e.printStackTrace();
67+
}
68+
return waitTime >= 200 ? value * 10 : -1;
69+
}
70+
}
71+
```
72+
73+
A service ambassador adding additional features such as logging, latency checks
74+
75+
```java
76+
public class ServiceAmbassador implements RemoteServiceInterface {
77+
78+
private static final Logger LOGGER = LoggerFactory.getLogger(ServiceAmbassador.class);
79+
private static final int RETRIES = 3;
80+
private static final int DELAY_MS = 3000;
81+
82+
ServiceAmbassador() {
83+
84+
}
85+
86+
@Override
87+
public long doRemoteFunction(int value) {
88+
89+
return safeCall(value);
90+
}
91+
92+
private long checkLatency(int value) {
93+
RemoteService service = RemoteService.getRemoteService();
94+
long startTime = System.currentTimeMillis();
95+
long result = service.doRemoteFunction(value);
96+
long timeTaken = System.currentTimeMillis() - startTime;
97+
98+
LOGGER.info("Time taken (ms): " + timeTaken);
99+
return result;
100+
}
101+
102+
private long safeCall(int value) {
103+
104+
int retries = 0;
105+
long result = -1;
106+
107+
for (int i = 0; i < RETRIES; i++) {
108+
109+
if (retries >= RETRIES) {
110+
return -1;
111+
}
112+
113+
if ((result = checkLatency(value)) == -1) {
114+
LOGGER.info("Failed to reach remote: (" + (i + 1) + ")");
115+
retries++;
116+
try {
117+
sleep(DELAY_MS);
118+
} catch (InterruptedException e) {
119+
e.printStackTrace();
120+
}
121+
} else {
122+
break;
123+
}
124+
}
125+
return result;
126+
}
127+
}
128+
```
129+
130+
A client has a local service ambassador used to interact with the remote service:
131+
132+
```java
133+
public class Client {
134+
135+
private ServiceAmbassador serviceAmbassador;
136+
137+
Client() {
138+
serviceAmbassador = new ServiceAmbassador();
139+
}
140+
141+
long useService(int value) {
142+
long result = serviceAmbassador.doRemoteFunction(value);
143+
System.out.println(result);
144+
return result;
145+
}
146+
}
147+
```
148+
149+
And here are two clients using the service.
150+
151+
```java
152+
Client host1 = new Client();
153+
Client host2 = new Client();
154+
host1.useService(12);
155+
host2.useService(73);
156+
```
157+
158+
## Applicability
159+
Ambassador is applicable when working with a legacy remote service that cannot
160+
be modified or would be extremely difficult to modify. Connectivity features can
161+
be implemented on the client avoiding the need for changes on the remote service.
162+
163+
* Ambassador provides a local interface for a remote service.
164+
* Ambassador provides logging, circuit breaking, retries and security on the client.
165+
166+
## Typical Use Case
167+
168+
* Control access to another object
169+
* Implement logging
170+
* Implement circuit breaking
171+
* Offload remote service tasks
172+
* Facilitate network connection
173+
174+
## Real world examples
175+
176+
* [Kubernetes-native API gateway for microservices](https://github.com/datawire/ambassador)
177+
178+
## Credits
179+
180+
* [Ambassador pattern](https://docs.microsoft.com/en-us/azure/architecture/patterns/ambassador)
181+
* [Designing Distributed Systems: Patterns and Paradigms for Scalable, Reliable Services](https://books.google.co.uk/books?id=6BJNDwAAQBAJ&pg=PT35&lpg=PT35&dq=ambassador+pattern+in+real+world&source=bl&ots=d2e7GhYdHi&sig=Lfl_MDnCgn6lUcjzOg4GXrN13bQ&hl=en&sa=X&ved=0ahUKEwjk9L_18rrbAhVpKcAKHX_KA7EQ6AEIWTAI#v=onepage&q=ambassador%20pattern%20in%20real%20world&f=false)

0 commit comments

Comments
 (0)