머리말
Zuul은 Netflix가 제공하는 오픈 소스 구성 요소로, 클라우드 플랫폼에서 동적 라우팅, 모니터링, 탄력성, 보안 및 기타 에지 서비스를 제공하기 위해 노력합니다. 게이트웨이의 중요한 부분으로 사용하는 회사도 많이 있습니다. 올해 회사의 아키텍처 그룹은 게이트웨이 제품을 개발하여 동적 라우팅, 동적 권한, 현재 제한 할당량 및 기타 기능을 통합하여 다른 부서의 프로젝트에 통합 외부 네트워크 통화 관리를 제공하고 마침내 제품을 구성하기로 결정했습니다 (ALI는 실제로 이와 관련하여 성숙한 게이트웨이 제품을 가지고 있지만 개인화 된 구성에는 적합하지 않으며 현재의 제한과 현재의 다운로드를 가지고 있지 않습니다).
이 기사는 주로 Spring Cloud Zuul Unified Exception 처리 및 폴백에 대한 관련 컨텐츠를 소개합니다. 참조와 학습을 위해 공유됩니다. 아래에서 많이 말하지 않겠습니다. 자세한 소개를 함께 살펴 보겠습니다.
1. 필터에서의 통합 예외 처리
실제로 SpringCloud의 Edgware SR2 버전에는 Zuulfilter의 오류를 통합적으로 처리 할 수 있지만 실제 개발에는 각 팀이 오류의 응답 방법에 대한 고유 한 처리 사양이 있다고 생각합니다. 그렇다면 사용자 정의 예외 처리 방법은 무엇입니까?
먼저 SpringCloud에서 제공 한 SenderRorFilter를 참조 할 수 있습니다.
/ * * 저작권 2013-2015 원래 저자 또는 저자. * * Apache 라이센스에 따라 라이센스가 부여 된 버전 2.0 ( "라이센스"); * 라이센스를 준수하는 것 외에는이 파일을 사용할 수 없습니다. * 귀하는 * * http://www.apache.org/license/license/license-2.0 *에서 라이센스 사본을 얻을 수 있습니다. 적용 가능한 법률에 의해 요구되거나 서면으로 동의하지 않는 한, 라이센스에 따라 배포 된 소프트웨어 *는 "기준"에 배포됩니다. * 라이센스에 따른 특정 언어 통치 권한 및 * 제한 사항에 대한 라이센스를 참조하십시오. */패키지 org.springframework.cloud.netflix.zuul.filters.post; import javax.servlet.requestdispatcher; import javax.servlet.http.htttp.httpervletrequest; import javax.servlet.http.htttp.htttp.httervletresponse; org.comms org.apache.commons.logging.logfactory; import org.springframework.beans.beans.annotation.value; import org.spramframework.cloud.netflix.zuul.util.zuulruntimeexception; import org.springframework.util.reflectionutils; import org.springframework.util.stringutils; import com.netflix.zuul.zuulfilter; import com.netflix.zuul.context.requestcontext; import com.netflix.zuul.exception.zuulexception; import static org.springframework.cloud.netflix.zuul.filters.support.filterconstants.error_type; import static org.spramframework.cloud.netflix.zuul.filters.support.filterconstants.send_error_filter_order; /** {@link zuulfilter to gorfilter} (기본적으로) {@link requestContext#getThrowable ()}이 null이 아닌 경우. * * @Author Spencer Gibb * /// todo : edgwarepublic 클래스에서 오류 패키지로 이동 Zuulfilter {private static final log = logfactory.getLog (sendErrorFilter.class); 보호 된 정적 최종 문자열 send_error_filter_ran = "senderRorfilter.ran"; @Value ( "$ {error.path :/error}") 개인 문자열 ERRERPATH; @override public String filtertype () {return error_type; } @override public int filterorder () {return send_error_filter_order; } @override public boolean rittfilter () {requestContext ctx = requestContext.GetCurrentContext (); // 이미 ctx.getThrowable ()! = null &&! ctx.getBoolean (send_error_filter_ran, false); } @override public 객체 run () {try {requestContext ctx = requestContext.GetCurrentContext (); ZuulException Exception = FindZuulException (ctx.getThrowable ()); httpservletrequest 요청 = ctx.getRequest (); request.setattribute ( "javax.servlet.error.status_code", exception.nstatuscode); log.warn ( "필터링 중 오류", 예외); request.setattribute ( "javax.servlet.error.exception", 예외); if (stringUtils.hastext (exception.errorcause)) {request.setAttribute ( "javax.servlet.error.message", except.errorcause); } requestDispatcher dispatcher = request.getRequestDispatcher (this.errorPath); if (dispatcher! = null) {ctx.set (send_error_filter_ran, true); if (! ctx.getResponse (). iscommitted ()) {ctx.setResponsestStatScode (exception.nstatuscode); dispatcher.forward (요청, ctx.getResponse ()); }}} catch (Exception Ex) {reflectionUtils.RethrowRuntImeexception (ex); } return null; } ZuulException findZuulException (Throwable Throwable) {if (strashable.getCause () ZuulruntImeexception의 인스턴스) {// 이것은 로컬 필터 중 하나에 의해 시작된 실패 (zuulexception) trashable.getCause (). getCause (); } if (strashable.getCause () instanceof ZuulException) {// wrapped Zuul Exception Return (zuulexception) strashable.getCause (); } if (ZuulException의 Throwable 인스턴스) {// Zuul Lifecycle Return (Zuulexception)에 의해 던져진 예외; } // 폴백, 절대 여기에 도착해서는 안됩니다. 새로운 zuulexception (Throwable, httpservletresponse.sc_internal_server_error, null); } public void seterRorPath (String ErrorPath) {this.errorPath = ERRORPATH; }}여기서는 몇 가지 핵심 사항을 찾을 수 있습니다.
1) 위 코드에서 필터가 관련 오류 정보를 요청에 넣었음을 알 수 있습니다.
request.setattribute ( "javax.servlet.error.status_code", exception.nstatuscode);
request.setattribute ( "javax.servlet.error.exception", 예외);
request.setattribute ( "javax.servlet.error.message", Exception.errorcause);
2) 오류가 처리되면 처리를 위해 XXX/오류 주소로 전달됩니다.
그런 다음 실험을 할 수 있습니다. 게이트웨이 서비스 프로젝트 모듈에서 예외를 던지는 필터를 만듭니다.
package com.hzgj.lyrk.springcloud.gateway.server.filter; import com.netflix.zuul.zuulfilter; import lombok.extern.slf4j.slf4j; import org.spramepramework.stereotyp.component;@component@slf4jpublic classmyzuulfilets zuulfilets zuulfilert zuulfileth. @override public String filtertype () {return "post"; } @override public int filterorder () {return 9; } @override public boolean routfilter () {return true; } @override public 객체 run () {log.info ( "실행 오류 테스트 ..."); 새로운 runtimeexception ()을 던지십시오. // null을 반환합니다. }}그런 다음 오류를 처리 할 컨트롤러를 정의합니다.
package com.hzgj.lyrk.springcloud.gateway.server.filter; import org.springframework.http.httpstatus; import org.springframework.http.responsenity; import org.springframework.web.bind.annotation.ggetmapping; import; org.springframework.web.bind.annotation.restcontroller; import javax.servlet.http.httpservletrequest; @RestControllerPublic Class ErrorHandler {@getMapping (value = "/error") 공개 응답 <ErrorBean> error (httpservletrequest 요청) request.getAttribute ( "javax.servlet.error.message"). toString (); ErrorBean ErrorBean = New ErrorBean (); ErrorBean.SetMessage (메시지); ErrorBean.SetReason ( "프로그램 오류"); 새로운 응답 entity <> (ErrorBean, httpstatus.bad_gateway); } private static class errorbean {개인 문자열 메시지; 개인 문자열 이유; public String getMessage () {return message; } public void setMessage (문자열 메시지) {this.message = 메시지; } public string getReason () {return resaul; } public void setReason (문자열 이유) {this.Reason = 이유; }}}프로젝트를 시작한 후 게이트웨이를 통해 액세스 해 보겠습니다.
2. Zuul 폴백에 대한 질문
1. Zuul의 시간 초과 문제에 관한 :
온라인 으로이 문제에 대한 많은 해결책이 있지만 소스 코드도 게시하고 싶습니다. 이 클래스에서 hystrix와 리본을 통합하는이 클래스 AbstractribbonCommand에주의를 기울이십시오.
/ * * 저작권 2013-2016 원래 저자 또는 저자. * * Apache 라이센스에 따라 라이센스가 부여 된 버전 2.0 ( "라이센스"); * 라이센스를 준수하는 것 외에는이 파일을 사용할 수 없습니다. * 귀하는 * * http://www.apache.org/license/license/license-2.0 *에서 라이센스 사본을 얻을 수 있습니다. 적용 가능한 법률에 의해 요구되거나 서면으로 동의하지 않는 한, 라이센스에 따라 배포 된 소프트웨어 *는 "기준"에 배포됩니다. * 라이센스에 따른 특정 언어 통치 권한 및 * 제한 사항에 대한 라이센스를 참조하십시오. * */패키지 org.springframework.cloud.netflix.zuul.filters.route.support; import org.apache.commons.logging.log; import org.apache.commons.logging.logfactory; import org.springfram.netflix.ribbon.ribbonclient client client client client client client client client client client client org.springframework.cloud.netflix.ribbon.ribbonhttpresponse; import org.springframework.cloud.netflix.ribbon.support.abstractloadbalancingclient; import org.sprameframework.cloud.netflix.ribbon.scorpport.conxtawerequest; import; org.springframework.cloud.netflix.zuul.filters.zuulproperties; import org.springframework.cloud.netflix.zuul.filters.route.ribboncommand; import org.springframework.cloud.netflix.zuul.route.ribboncommand compontconconconconconconconconconconconconconconconconconconconconconconconconconconconconcontexte org.springframework.cloud.netflix.zuul.filters.route.zuulfallbackprovider; import org.springframework.cloud.netflix.zuul.filters.route.fallbackprovider; import org.springframework.http.clienthtpressance; 가져 오기; com.netflix.client.abstractloadbalancerawareclient; import com.netflix.client.clientRequest; import com.netflix.client.config.defaultclientConfigimpl; import com.netflix.client.config.iclientConfig; import com.netflix.client.config.iclient Configky; com.netflix.client.http.httpresponse; import com.netflix.config.dynamamicintproperty; import com.netflix.config.dynamicpropertyfactory; import com.netflix.hystrix.hystrixcommand; import com.netflix.hystrixcommandgroupky; com.netflix.hystrix.hystrixcommandkey; import com.netflix.hystrix.hystrixcommandproperties; import com.netflix.hystrix.hystrixcommandproperties.executionsolationStrateTrategy; import com.netflix.hystrix.hystrixThreadPoolkey; 가져 오기 com.netflix.zuul.constants.zuulconstants; import com.netflix.zuul.context.requestcontext;/** * @author spencer gibb */public acbact class abstractribboncommand <lbc extends arbosploadbalancerawareclient <rs>, rq extends clientrequest, rs extends, rs extends. hystrixcommand <clienthttpresponse> ribboncommand {private static final logger = logfactory.getLog (AbstractribbonCommand.class); 보호 된 최종 LBC 클라이언트; 보호 된 ribboncommandContext 컨텍스트; 보호 된 ZuulfallbackProvider ZuulfallbackProvider; 보호 된 ICLIENTCONFIG 구성; Public AbstractribbonCommand (LBC 클라이언트, RibbonCommandContext Context, ZuulProperties ZuulProperties) {this ( "기본값", 클라이언트, 컨텍스트, Zuulproperties); } public AbstractribbonCommand (String CommandKey, LBC 클라이언트, RibbonCommandContext Context, ZuulProperties ZuulProperties) {this (CommandKey, 클라이언트, 컨텍스트, ZuulProperties, null); } public AbstractribbonCommand (String CommandKey, LBC 클라이언트, RibbonCommandContext 컨텍스트, ZuulProperties ZuulProperties, ZuulfallbackProvider FallbackProvider) {this (Commandkey, Client, Context, Zuulproperties, FallbackProvider, NULL); } public abstractribboncommand (String Commandkey, LBC 클라이언트, RibbonCommandContext Context, ZuulProperties ZuulProperties, ZuulfallbackProvider FallbackProvider, IclientConfig Config) {this (getSetter (Commandkey, ZuulProperties, belfig), Context, Context, Context, Contex, Conteblover); } 보호 된 ABSTRACTRIBBONCOMMAND (Setter Setter, LBC 클라이언트, RibbonCommandContext Context, ZuulfallBackProvider FallbackProvider, iclientConfig Config) {Super (setter); this.client = 클라이언트; this.context = 컨텍스트; this.zuulfallbackprovider = FallbackProvider; this.config = config; } 보호 된 정적 hystrixcommandProperties.Setter createSetter (iclientConfig Config, String CommandKey, ZuulProperties ZuulProperties) {int hystrixtimeout = gethystrixtimeout (config, commandkey); hystrixcommandProperties.setter (). withExecutionIsolationStrategy (zuulproperties.getRibbonIsolationStrategy ()). } 보호 된 정적 int gethystrixtimeout (iclientconfig config, string commandKey) {int ribbontimeout = getRibbontimeout (config, commandKey); DynamicPropertyFactory DynamicPropertyFactory = DynamicPropertyFactory.getInstance (); int defaultytrixtimeout = DynamicPropertyFactory.getIntProperty ( "hystrix.command.default.execution.isolation.thread.timeoutInmilliseconds", 0) .get (); int CommandHyStrixTimeout = DynamicPropertyFactory.getIntProperty ( "hystrix.command." + CommandKey + ".Execution.isolation.thread.TimeOutInMilliseConds", 0); get (); int hystrixtimeout; if (commandHyStrixTimeout> 0) {hyStrixTimeout = CommandHyStrixTimeout; } else if (defaulThyStrixtimeout> 0) {hystrixtimeout = defaultyThtrixtimeout; } else {hystrixtimeout = ribbontimeout; } if (hystrixtimeout <ribbontimeout) {logger.warn ( " + hystrixtimeout +"명령 " + commandkey +"의 MS의 "hystrix 타임 아웃" + ribbontimeout + "ms."); } return hystrixtimeout; } 보호 된 정적 int getRibBontimeout (iclientConfig Config, String CommandKey) {int ribbontimeout; if (config == null) {ribbontimeout = ribbonclientConfiguration.default_read_timeout + ribbonclientConfiguration.default_connect_timeout; } else {int ribbonreadtimeout = gettimeout (config, commandkey, "readtimeout", iclientconfigkey.keys.readtimeout, ribbonclientConfiguration.default_read_timeout); int ribbonconnecttimeout = gettimeout (config, commandkey, "connecttimeout", iclientconfigkey.keys.connecttimeout, ribbonclientConfiguration.default_connect_timeout); int maxAutoreTries = getTimeout (config, commandKey, "maxAutoretries", iclientConfigkey.keys.MaxAutoreTries, defaultClientConfigimpl.default_max_auto_retries); int maxAutorEtriesNexTserver = getTimeout (config, commandKey, "maxAutoretRiesNexTserver", iclientConfigkey.keys.MaxAutorEtRiesNexTserver, defaultClientConfigimpl.default_max_auto_retries_next_server); ribbontimeout = (ribbonreadtimeout + ribbonconnecttimeout) * (maxautoretries + 1) * (maxautoretriesnextserver + 1); } return ribbontimeout; } private static int gettimeout (iclientconfig config, string commandkey, 문자열 속성, iClientConfigkey <integer> configkey, int defaultValue) {dynamicPropertyFactory DynamicPropertyFactory = DynamicProperTyFactory.getInstance (); return dynamicPropertyfactory.getIntProperty (CommandKey + "." + config.getNamespace () + "." + 속성, config.get (configkey, defaultValue)). get (); } @deprecated // todo는 2.0.x 보호 정적 세트 getSetter (Final String CommandKey, ZuulProperties ZuulProperties) {return getSetter (CommandKey, ZuulProperties, NULL); } 보호 된 정적 setter getSetter (Final String CommandKey, ZuulProperties ZuulProperties, iclientConfig Config) {// @formatter : Off Setter CommandSetter = setter.withGroupKey (HyStrixCommandGroupKey.Askey ( "RibbonCommand")). 최종 hystrixcommandProperties.setter setter = createSetter (config, commandkey, zuulproperties); if (zuulproperties.getRibbonisolationStrategy () == executionIsolationStrategy.semaphore) {최종 문자열 name = zuulconstants.zuul_eureka + commandKey + ".semaphore.maxSemaphores"; // 우리는이 랩이기 때문에 세마포어-이 분리로 기본값을 원합니다. // 이미 스레드 인 2 개의 다른 명령은 최종 DynamicIntProperty value = kidamicPropertyfactory.getInstance () .getIntProperty (이름, zuulproperties.getSemaphore (). getMaxSemaphores ()); setter.withExecutionSolationSemaphorEmaxConcurrentRequests (value.get ()); } else if (zuulproperties.getThreadPool (). isuseseParateTheRdeadPools ()) {Final String ThreadPoolKey = ZuulProperTies.getThreadPool (). getThreadPoolKeyPrix () + COCFOREKEY; CommandSetter.andthreadpoolkey (hystrixThreadPoolKey.Fooly.askey (ThreadPoolKey)); } return CommandSetter.andCommandProperTiesDefaults (setter); // @formatter : on} @override protected clienthttpresponse run () rows exception {final requestContext context = requestContext.getCurrentContext (); RQ 요청 = Createrequest (); RS 응답; 부울 retryableclient = this.client actractloadbalancingclient의 인스턴스 && ((AbstractLoadbalancingClient) this.client) .isclientRetryable ((ContextAwareRequest) 요청); if (retryableClient) {response = this.client.execute (요청, config); } else {response = this.client.executeWithloadBalancer (요청, config); } context.set ( "ribbonResponse", 응답); // Hystrix 명령이 // 응답에 의해 보관 된 기본 HTTP 연결을 릴리스하기 위해 시간을 초과하면 HTTPRESPONSE를 명시 적으로 닫습니다. // if (this.isResponsetImedout ()) {if (response! = null) {response.close (); }} 새로운 ribbonhttpresponse (응답)를 반환합니다. } @override Protected ClientHtttPresponse getfallback () {if (zuulfallbackProvider! = null) {return getFallBackResponse (); } return super.getfallback (); } protected clientHtttTtTpresponse getFallBackResponse () {if (allbackProvider의 zuulfallbackProvider 인스턴스) {Throwable 원인 = getFailedExecutionException (); 원인 = 원인 == null? getExecutionException () : 원인; if (원인 == null) {zuulfallbackprovider.fallbackresponse (); } else {return ((fallbackProvider) ZuulfallbackProvider) .FallBackResponse (원인); }} return ZuulfallBackProvider.FallBackResponse (); } public lbc getClient () {return client; } public ribboncommandContext getContext () {return 컨텍스트; } 보호 된 추상 rq createrequest ()는 예외를 던집니다;}참고 : getRibbontimeout 방법 및 gethystrixtimeout 메서드. 여기서이 두 메소드의 값은 경로의 이름입니다. 예를 들어, 우리가 방문하는 경우 : http : // localhost : 8088/order-server/xxx를 주문-서버 서비스에 액세스하면 CommandKey는 Order-Server입니다.
소스 코드에 따르면 먼저 게이트웨이 서버 타임 아웃 매개 변수를 설정합니다.
#Global Ribbon 설정 리본 : ConnectTimeout : 3000 Readtimeout : 3000 히트릭스 : 명령 : 기본값 : 실행 : 격리 : TimeOutinMilliseconds : 3000ZUUL : HOST : ConnectTimeOutMillis : 10000
물론 Order-Server.ribbon.xxxx = xxx를 개별적으로 주문-서버에 대한 리본의 타임 아웃 매개 변수를 설정할 수도 있습니다. Zuul의 폴백 효과를 보여주기 위해 Hystrix 타임 아웃을 여기에서 조금 더 짧게 설정했습니다. 물론 Hystrix의 기본 타임 아웃을 리본의 시간 초과보다 짧게 설정하지 않는 것이 가장 좋습니다. 이 상황은 소스 코드에서 우리에게 경고를 받았습니다.
그런 다음 Order-Server에서 다음 방법을 추가합니다.
@getMapping ( "/sleep/{sleeptime}") public String sleep (@PathVariable Long Sleeptime)는 InterruptedException {TimeUnit.seconds.sleep (sleeptime); "성공"을 반환합니다. }2. Zuul의 폴백 방법
Zuulfallbackprovider 인터페이스를 구현하고 코드를 구현할 수 있습니다.
package com.hzgj.lyrk.springcloud.gateway.server.filter; import com.google.common.collect.immutablemap; import com.google.gson.gsonbuilder; import org.springframework.cloud.netflix.zuul.filters.route.zuulbackprovider; org.springframework.http.httppheaders; import org.springframework.http.httpstatus; import org.springframework.http.mediatepe; import org.sprameframework.http.client.client.sprestons.spram.spram.spram.sprameponte.sprester.compontrest java.io.bytearrayinputStream; import java.io.ioexception; import java.io.inputStream; import java.time.localdateTime; import java.time.localTime; @ComponentPublic 클래스 폴백 핸드러는 Zuulfall BackProvider {@OverRide Public Public getroute () {// problate getroute를 구현합니다. 반품 "*"; } @override public clienthttttttpresponse fallbackResponse () {return new ClientHttPresponse () {@Override public httpstatus getStatuscode () IoException {return httpstatus.ok; } @override public int getRawstatuscode ()는 ioexception {return 200; } @override public string getStatUstext ()는 IoException {return "OK"; } @Override public void close () {} @override public inputStream getBody ()는 ioException {string result = new gsonBuilder (). create (). tojson ( "enbutablemap.of ("컨텐츠 ","요청 ","시간 ", localDateTime.Now ()); 새로운 BytearRayinputStream을 반환합니다 (result.getBytes ()); } @override public httpheaders getheaders () {httpheaders headers = new httpheaders (); headers.setContentType (mediaType.Application_json); 리턴 헤더; }}; }}현재 http : // localhost : 8088/order-server/sleep/6을 방문하고 다음과 같은 결과를 얻습니다.
방문 할 때 : http : // localhost : 8088/Order-Server/Sleep/1, 다음과 같은 결과를 얻을 수 있습니다.
요약
위는이 기사의 전체 내용입니다. 이 기사의 내용에 모든 사람의 연구 나 작업에 대한 특정 참조 가치가 있기를 바랍니다. 궁금한 점이 있으면 의사 소통을 위해 메시지를 남길 수 있습니다. Wulin.com을 지원 해주셔서 감사합니다.