关于 SAP CloudFoundry 应用的 Resilience
Introduce Resilience to your Application
Step 1: Resilience
考慮以下情況:您正在開發(fā)一個云應(yīng)用程序來為您的客戶提供服務(wù)。為了讓您的客戶滿意,您當(dāng)然對實(shí)現(xiàn)應(yīng)用程序的最高可用性感興趣。
然而,可能跨越多個服務(wù)的云應(yīng)用程序本質(zhì)上是復(fù)雜的。因此,可以安全地假設(shè)某事某處會在某個時間點(diǎn)失敗。例如,對數(shù)據(jù)庫的調(diào)用可能會失敗并導(dǎo)致應(yīng)用程序的一部分失敗。如果應(yīng)用程序的其他部分依賴于失敗的部分,這些部分也會失敗。因此,單個故障可能會級聯(lián)整個系統(tǒng)并破壞它。這對于多租戶應(yīng)用程序尤其重要,其中應(yīng)用程序的單個實(shí)例為多個客戶提供服務(wù)。典型的 S/4HANA 多租戶應(yīng)用程序涉及許多下游服務(wù),例如本地 S/4HANA ERP 系統(tǒng)。
讓我們看一個具體的例子:假設(shè)您的云應(yīng)用程序依賴于 30 個系統(tǒng),每個系統(tǒng)的“完美”可用性為 99.99%。這意味著每個服務(wù)每月有 4.32 分鐘不可用(43200 分鐘 * (1 – 0.9999) = 4.32 分鐘)。
現(xiàn)在假設(shè)故障是級聯(lián)的,因此一項服務(wù)不可用意味著整個應(yīng)用程序變得不可用。鑒于上面使用的等式,現(xiàn)在的情況是這樣的:
43200 分鐘 * (1 – 0.9999^30) = 43200 分鐘 * (1 – 0.997) = 129.6 分鐘
因此,您的多租戶應(yīng)用程序每個月對于每個客戶來說都無法使用超過兩個小時!
為了避免這種情況,我們需要為應(yīng)用程序配備處理故障的能力。如果應(yīng)用程序可以處理故障,則稱為彈性。因此,彈性是我們實(shí)現(xiàn)可用性的手段。
Step 2: Resilience4j
SAP Cloud SDK 現(xiàn)在基于 Resilience4j 庫構(gòu)建,以便為您的云應(yīng)用程序提供彈性。在之前的 2.x 版本中,我們使用了 Hystrix 庫,它現(xiàn)在已經(jīng)處于維護(hù)模式有一段時間了。
Resilience4j 帶有許多模塊來保護(hù)您的應(yīng)用程序免受故障。最重要的是超時、隔板和斷路器。
超時:Resilience4j 允許為每個遠(yuǎn)程服務(wù)設(shè)置自定義超時持續(xù)時間。如果遠(yuǎn)程服務(wù)的響應(yīng)時間超過指定的超時時間,則認(rèn)為遠(yuǎn)程服務(wù)調(diào)用失敗。該值應(yīng)根據(jù)遠(yuǎn)程服務(wù)的平均響應(yīng)時間進(jìn)行調(diào)整。
隔板:這些允許限制對遠(yuǎn)程服務(wù)的并發(fā)請求數(shù)。如果并發(fā)傳入請求的數(shù)量超過配置的閾值,則稱該隔板已飽和。在這種情況下,在現(xiàn)有請求完成之前,將自動停止進(jìn)一步的請求。
斷路器:Resilience4j 使用斷路器模式來確定遠(yuǎn)程服務(wù)當(dāng)前是否可用。斷路器默認(rèn)關(guān)閉。如果遠(yuǎn)程服務(wù)調(diào)用失敗太多次,Resilience4j 將打開/跳閘斷路器。這意味著應(yīng)該對同一遠(yuǎn)程服務(wù)進(jìn)行的任何進(jìn)一步調(diào)用都將自動停止。 Resilience4j 將定期檢查服務(wù)是否再次可用,并相應(yīng)地再次關(guān)閉打開的斷路器。有關(guān)斷路器模式的更多信息,請查看 Martin Fowler 的這篇文章。
此外,SAP Cloud SDK 使您能夠提供回退功能。因此,如果調(diào)用失敗,例如因為隔板飽和或斷路器斷開/跳閘,SDK 將檢查是否實(shí)現(xiàn)了回退并自動調(diào)用它。因此,即使服務(wù)不可用,您仍然可以提供一些有用的結(jié)果,例如通過提供緩存數(shù)據(jù)。
如果您想更深入地了解內(nèi)部工作原理,請查看 Resilience4j 用戶指南。
Step 3: Make your OData call resilient
既然我們已經(jīng)介紹了為什么彈性很重要,現(xiàn)在是時候?qū)⑺肽膽?yīng)用程序了。 在上一個教程中,您創(chuàng)建了一個簡單的 servlet,它使用 SDK 的虛擬數(shù)據(jù)模型 (VDM) 和其他有用的抽象來從 ERP 系統(tǒng)中檢索業(yè)務(wù)合作伙伴。 為了使此 VDM 調(diào)用具有彈性,您必須使用 SAP Cloud SDK 提供的 ResilienceDecorator 類包裝代碼。
同時,您還將 VDM 調(diào)用本身分離到另一個類中,以便在以后的教程中具有更好的可讀性和更容易的維護(hù)。 請注意,從 SAP Cloud SDK 版本 3 開始,不再需要單獨(dú)的類來實(shí)現(xiàn)彈性。 您還可以直接向現(xiàn)有的 BusinessPartnerServlet 類添加彈性。
所以首先創(chuàng)建以下類:
./application/src/main/java/com/sap/cloud/sdk/tutorial/GetBusinessPartnersCommand.java
package com.sap.cloud.sdk.tutorial;import org.slf4j.Logger; import org.slf4j.LoggerFactory;import java.time.Duration; import java.util.Collections; import java.util.List;import com.sap.cloud.sdk.cloudplatform.resilience.ResilienceConfiguration; import com.sap.cloud.sdk.cloudplatform.resilience.ResilienceDecorator; import com.sap.cloud.sdk.cloudplatform.resilience.ResilienceIsolationMode; import com.sap.cloud.sdk.cloudplatform.resilience.ResilienceRuntimeException; import com.sap.cloud.sdk.datamodel.odata.client.exception.ODataException; import com.sap.cloud.sdk.datamodel.odata.helper.Order;import com.sap.cloud.sdk.s4hana.connectivity.ErpHttpDestination; import com.sap.cloud.sdk.s4hana.datamodel.odata.namespaces.businesspartner.AddressEmailAddress; import com.sap.cloud.sdk.s4hana.datamodel.odata.namespaces.businesspartner.BusinessPartner; import com.sap.cloud.sdk.s4hana.datamodel.odata.namespaces.businesspartner.BusinessPartnerAddress; import com.sap.cloud.sdk.s4hana.datamodel.odata.services.BusinessPartnerService; import com.sap.cloud.sdk.s4hana.datamodel.odata.services.DefaultBusinessPartnerService;public class GetBusinessPartnersCommand {private static final Logger logger = LoggerFactory.getLogger(GetBusinessPartnersCommand.class);// TODO: uncomment the lines below and insert your API key, if you are using the sandbox service// private static final String APIKEY_HEADER = "apikey";// private static final String SANDBOX_APIKEY = "";private static final String CATEGORY_PERSON = "1";private final ErpHttpDestination destination;private final BusinessPartnerService businessPartnerService;private final ResilienceConfiguration myResilienceConfig;public GetBusinessPartnersCommand(ErpHttpDestination destination) {this(destination, new DefaultBusinessPartnerService());}public GetBusinessPartnersCommand(ErpHttpDestination destination, BusinessPartnerService service) {this.destination = destination;businessPartnerService = service;myResilienceConfig = ResilienceConfiguration.of(BusinessPartnerService.class).isolationMode(ResilienceIsolationMode.TENANT_AND_USER_OPTIONAL).timeLimiterConfiguration(ResilienceConfiguration.TimeLimiterConfiguration.of().timeoutDuration(Duration.ofMillis(10000))).bulkheadConfiguration(ResilienceConfiguration.BulkheadConfiguration.of().maxConcurrentCalls(20));}public List<BusinessPartner> execute() {return ResilienceDecorator.executeSupplier(this::run, myResilienceConfig, e -> {logger.warn("Fallback called because of exception.", e);return Collections.emptyList();});}private List<BusinessPartner> run() {try {return businessPartnerService.getAllBusinessPartner().select(BusinessPartner.BUSINESS_PARTNER,BusinessPartner.LAST_NAME,BusinessPartner.FIRST_NAME,BusinessPartner.IS_MALE,BusinessPartner.IS_FEMALE,BusinessPartner.CREATION_DATE,BusinessPartner.TO_BUSINESS_PARTNER_ADDRESS.select(BusinessPartnerAddress.CITY_NAME,BusinessPartnerAddress.COUNTRY,BusinessPartnerAddress.TO_EMAIL_ADDRESS.select(AddressEmailAddress.EMAIL_ADDRESS))).filter(BusinessPartner.BUSINESS_PARTNER_CATEGORY.eq(CATEGORY_PERSON)).orderBy(BusinessPartner.LAST_NAME, Order.ASC).top(200)// TODO: uncomment the line below, if you are using the sandbox service// .withHeader(APIKEY_HEADER, SANDBOX_APIKEY).executeRequest(destination);} catch (ODataException e) {throw new ResilienceRuntimeException(e);}} }要使用 ResilienceDecorator,您至少需要做兩件事:
您希望以彈性方式執(zhí)行的代碼。它可以是 Supplier、Callable、Supplier、方法引用或簡單的 lambda 函數(shù)。您可能已經(jīng)注意到,該示例僅采用基于 VDM 的代碼調(diào)用上一教程中的 OData 服務(wù),并將其放入單獨(dú)的 run() 方法中。 ResilienceDecorator 提供的方法可以簡單地包裝所提供的函數(shù)并返回一個新函數(shù)(decorateSupplier、decorateCallable 等),以及也立即開始執(zhí)行該函數(shù)的方法(executeSupplier、executeCallable 等)。這里 executeSupplier 與基于 VDM 的代碼的方法引用一起使用。
帶有標(biāo)識符參數(shù)集的 ResilienceConfiguration 實(shí)例。這里的示例使用類引用,但也可以使用字符串標(biāo)識符。除了強(qiáng)制標(biāo)識符參數(shù)外,SAP Cloud SDK 還帶有默認(rèn)的彈性配置,因此您無需自行執(zhí)行任何其他配置。在大多數(shù)情況下,默認(rèn)配置就足夠了。但是,如果您需要更改彈性配置,您可以在 SAP Cloud SDK Javadoc 中找到有關(guān)此主題的更多信息
這是自定義彈性配置的示例。這里隔離模式設(shè)置為可選租戶+用戶,隔板最大并發(fā)調(diào)用數(shù)為20,執(zhí)行超時為10000毫秒。
myResilienceConfig = ResilienceConfiguration.of(BusinessPartnerService.class).isolationMode(ResilienceIsolationMode.TENANT_AND_USER_OPTIONAL).timeLimiterConfiguration(ResilienceConfiguration.TimeLimiterConfiguration.of().timeoutDuration(Duration.ofMillis(10000))).bulkheadConfiguration(ResilienceConfiguration.BulkheadConfiguration.of().maxConcurrentCalls(20));此外,ResilienceDecorator 的decorate… 和execute… 方法支持可選的第三個參數(shù)作為回退函數(shù),以防遠(yuǎn)程服務(wù)調(diào)用失敗。 在這種情況下,使用返回空列表的 lambda 函數(shù)。 您還可以提供靜態(tài)數(shù)據(jù)或檢查對此調(diào)用的響應(yīng)是否已被緩存。 最佳實(shí)踐是至少記錄提供的 Throwable。
更新您的彈性配置以匹配上述配置。 現(xiàn)在您有了一個工作命令,您需要調(diào)整您的 BusinessPartnerServlet 以使用新創(chuàng)建的命令:
./application/src/main/java/com/sap/cloud/sdk/tutorial/BusinessPartnerServlet.java
package com.sap.cloud.sdk.tutorial;import com.google.gson.Gson; import org.slf4j.Logger; import org.slf4j.LoggerFactory;import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.List;import com.sap.cloud.sdk.cloudplatform.connectivity.DestinationAccessor;import com.sap.cloud.sdk.s4hana.connectivity.ErpHttpDestination; import com.sap.cloud.sdk.s4hana.connectivity.DefaultErpHttpDestination; import com.sap.cloud.sdk.s4hana.datamodel.odata.namespaces.businesspartner.BusinessPartner;@WebServlet("/businesspartners") public class BusinessPartnerServlet extends HttpServlet {private static final long serialVersionUID = 1L;private static final Logger logger = LoggerFactory.getLogger(BusinessPartnerServlet.class);private static final String DESTINATION_NAME = "MyErpSystem";@Overrideprotected void doGet(final HttpServletRequest request, final HttpServletResponse response)throws ServletException, IOException {try {final ErpHttpDestination destination = DestinationAccessor.getDestination(DESTINATION_NAME).asHttp().decorate(DefaultErpHttpDestination::new);final List<BusinessPartner> businessPartners =new GetBusinessPartnersCommand(destination).execute();response.setContentType("application/json");response.getWriter().write(new Gson().toJson(businessPartners));} catch (final Exception e) {logger.error(e.getMessage(), e);response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);response.getWriter().write(e.getMessage());e.printStackTrace(response.getWriter());}} }總結(jié)
以上是生活随笔為你收集整理的关于 SAP CloudFoundry 应用的 Resilience的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 美联储加息意味着什么?
- 下一篇: 模拟城市完美布局平面图_css四大布局(