目的
在客户端上提供助手服务实例,并从共享资源中卸载常用功能。
说明
远程服务有许多客户端访问它所提供的功能。该服务是遗留应用程序,无法更新。来自用户的大量请求导致连接问题。应实施新的请求频率规则以及延迟检查和客户端日志记录。
简而言之
使用大使模式,我们可以实现从客户端进行频率较低的轮询以及延迟检查和日志记录。
Microsoft文档说明
大使服务可以被视为与客户协同工作的进程外代理。此模式可用于以语言无关的方式卸载常见的客户端连接任务,如监视,日志记录,路由,安全性(如TLS)和弹性模式。它通常与遗留应用程序或其他难以修改的应用程序一起使用,以扩展其网络功能。
程序示例
考虑到上面的例子,我们将以简单的方式模仿功能。
我们有一个由远程服务和大使服务实现的接口:
<b>interface</b> RemoteServiceInterface {
<b>long</b> doRemoteFunction(<b>int</b> value) throws Exception;
}
表示为单例的远程服务。
<b>public</b> <b>class</b> RemoteService implements RemoteServiceInterface {
<b>private</b> <b>static</b> <b>final</b> Logger LOGGER = LoggerFactory.getLogger(RemoteService.<b>class</b>);
<b>private</b> <b>static</b> RemoteService service = <b>null</b>;2
<b>static</b> <b>synchronized</b> RemoteService getRemoteService() {
<b>if</b> (service == <b>null</b>) {
service = <b>new</b> RemoteService();
}
<b>return</b> service;
}
<b>private</b> RemoteService() {}
@Override
<b>public</b> <b>long</b> doRemoteFunction(<b>int</b> value) {
<b>long</b> waitTime = (<b>long</b>) Math.floor(Math.random() * 1000);
<b>try</b> {
sleep(waitTime);
} <b>catch</b> (InterruptedException e) {
LOGGER.error(<font>"Thread sleep interrupted"</font><font>, e)
}
<b>return</b> waitTime >= 200 ? value * 10 : -1;
}
}
</font>
服务大使添加其他功能,如日志记录,延迟检查
<b>public</b> <b>class</b> ServiceAmbassador implements RemoteServiceInterface {
<b>private</b> <b>static</b> <b>final</b> Logger LOGGER = LoggerFactory.getLogger(ServiceAmbassador.<b>class</b>);
<b>private</b> <b>static</b> <b>final</b> <b>int</b> RETRIES = 3;
<b>private</b> <b>static</b> <b>final</b> <b>int</b> DELAY_MS = 3000;
ServiceAmbassador() {}
@Override
<b>public</b> <b>long</b> doRemoteFunction(<b>int</b> value) {
<b>return</b> safeCall(value);
}
<b>private</b> <b>long</b> checkLatency(<b>int</b> value) {
RemoteService service = RemoteService.getRemoteService();
<b>long</b> startTime = System.currentTimeMillis();
<b>long</b> result = service.doRemoteFunction(value);
<b>long</b> timeTaken = System.currentTimeMillis() - startTime;
LOGGER.info(<font>"Time taken (ms): "</font><font> + timeTaken);
<b>return</b> result;
}
<b>private</b> <b>long</b> safeCall(<b>int</b> value) {
<b>int</b> retries = 0;
<b>long</b> result = -1;
<b>for</b> (<b>int</b> i = 0; i < RETRIES; i++) {
<b>if</b> (retries >= RETRIES) {
<b>return</b> -1;
}
<b>if</b> ((result = checkLatency(value)) == -1) {
LOGGER.info(</font><font>"Failed to reach remote: ("</font><font> + (i + 1) + </font><font>")"</font><font>);
retries++;
<b>try</b> {
sleep(DELAY_MS);
} <b>catch</b> (InterruptedException e) {
LOGGER.error(</font><font>"Thread sleep state interrupted"</font><font>, e);
}
} <b>else</b> {
<b>break</b>;
}
}
<b>return</b> result;
}
}
</font>
客户端有一个本地服务大使,用于与远程服务进行交互:
<b>public</b> <b>class</b> Client {
<b>private</b> ServiceAmbassador serviceAmbassador;
Client() {
serviceAmbassador = <b>new</b> ServiceAmbassador();
}
<b>long</b> useService(<b>int</b> value) {
<b>long</b> result = serviceAmbassador.doRemoteFunction(value);
LOGGER.info(<font>"Service result: "</font><font> + result)
<b>return</b> result;
}
}
</font>
这里有两个使用该服务的客户端。
Client host1 = <b>new</b> Client(); Client host2 = <b>new</b> Client(); host1.useService(12); host2.useService(73);
适用场景
大使模式适用于无法修改或极难修改的遗留远程服务。可以在客户端上实现连接功能,从而无需更改远程服务。
典型用例