大使模式(Ambassador)

jdon發表於2019-06-14

目的
在客戶端上提供助手服務例項,並從共享資源中解除安裝常用功能。

說明
遠端服務有許多客戶端訪問它所提供的功能。該服務是遺留應用程式,無法更新。來自使用者的大量請求導致連線問題。應實施新的請求頻率規則以及延遲檢查和客戶端日誌記錄。

簡而言之
使用大使模式,我們可以實現從客戶端進行頻率較低的輪詢以及延遲檢查和日誌記錄。

Microsoft文件說明
大使服務可以被視為與客戶協同工作的程式外代理。此模式可用於以語言無關的方式解除安裝常見的客戶端連線任務,如監視,日誌記錄,路由,安全性(如TLS)和彈性模式。它通常與遺留應用程式或其他難以修改的應用程式一起使用,以擴充套件其網路功能。

程式示例
考慮到上面的例子,我們將以簡單的方式模仿功能。
我們有一個由遠端服務和大使服務實現的介面:

interface RemoteServiceInterface {

    long doRemoteFunction(int value) throws Exception;
}


表示為單例的遠端服務。

public class RemoteService implements RemoteServiceInterface {

        private static final Logger LOGGER = LoggerFactory.getLogger(RemoteService.class);
    private static RemoteService service = null;2

    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;
    }
}


服務大使新增其他功能,如日誌記錄,延遲檢查

public class ServiceAmbassador implements RemoteServiceInterface {

    private static final Logger LOGGER = LoggerFactory.getLogger(ServiceAmbassador.class);
    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) {
        RemoteService service = RemoteService.getRemoteService();
        long startTime = System.currentTimeMillis();
        long result = service.doRemoteFunction(value);
        long timeTaken = System.currentTimeMillis() - startTime;

        LOGGER.info("Time taken (ms): " + timeTaken);
        return result;
    }

    private long safeCall(int value) {

        int retries = 0;
        long result = -1;

        for (int i = 0; i < RETRIES; i++) {

            if (retries >= RETRIES) {
                return -1;
            }

            if ((result = checkLatency(value)) == -1) {
                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;
    }
}


客戶端有一個本地服務大使,用於與遠端服務進行互動:

public class Client {

    private ServiceAmbassador serviceAmbassador;

    Client() {
        serviceAmbassador = new ServiceAmbassador();
    }

    long useService(int value) {
        long result = serviceAmbassador.doRemoteFunction(value);
        LOGGER.info("Service result: " + result)
        return result;
    }
}


這裡有兩個使用該服務的客戶端。

Client host1 = new Client();
Client host2 = new Client();
host1.useService(12);
host2.useService(73);



適用場景
大使模式適用於無法修改或極難修改的遺留遠端服務。可以在客戶端上實現連線功能,從而無需更改遠端服務。

  • 大使為遠端服務提供本地介面。
  • 大使在客戶端提供日誌記錄、斷路、重試和安全保護。


典型用例

  • 控制對其他物件的訪問
  • 實現日誌記錄
  • 實現斷路
  • 解除安裝遠端服務任務
  • 促進網路連線

相關文章