مقدمة
Zuul هو مكون مفتوح المصدر توفره Netflix ، ملتزم بتوفير التوجيه الديناميكي والمراقبة والمرونة والأمان وخدمات الحافة الأخرى على منصة السحابة. هناك أيضًا العديد من الشركات التي تستخدمها كجزء مهم من البوابة. هذا العام ، قررت مجموعة الهندسة المعمارية للشركة تطوير منتج بوابة ، ودمج التوجيه الديناميكي ، والأذونات الديناميكية ، وحصة الحد الحالي ، والوظائف الأخرى ، وتوفير إدارة مكالمات الشبكة الخارجية الموحدة للمشاريع في الإدارات الأخرى ، وأخيراً تشكيل منتجات (Ali لديها بالفعل منتجات بوابة ناضجة في هذا الصدد ، ولكنها ليست مناسبة للتكوينات الشخصية ، ولا تحتوي على تمثيلات متكاملة ومتسابقين في الوقت الحالي.
تقدم هذه المقالة بشكل أساسي المحتوى ذي الصلة حول معالجة الاستثناءات الموحدة لـ Spring Cloud Zuul الموحدة. يتم مشاركته للرجوع إليه وتعلمك. لن أقول الكثير أدناه ، دعنا نلقي نظرة على المقدمة التفصيلية معًا.
1. استثناء موحد معالجة في المرشح
في الواقع ، في إصدار EDGware SR2 من SpringCloud ، هناك معالجة موحدة للأخطاء في Zuulfilter ، ولكن في التطوير الفعلي ، أعتقد أن كل فريق لديه مواصفات معالجة خاصة به لأساليب الاستجابة للأخطاء. فكيف تتم معالجة استثناء مخصصة؟
يمكننا أولاً الرجوع إلى senderrorfilter المقدمة من SPRINGCLOUD:
/ * * حقوق الطبع والنشر 2013-2015 المؤلف أو المؤلفين الأصليين. * * مرخصة بموجب ترخيص Apache ، الإصدار 2.0 ("الترخيص") ؛ * لا يجوز لك استخدام هذا الملف إلا في الامتثال للترخيص. * يمكنك الحصول على نسخة من الترخيص على * * http://www.apache.org/licenses/license-2.0 * * ما لم يكن مطلوبًا بموجب القانون المعمول به أو يتم الاتفاق عليه في الكتابة ، يتم توزيع البرمجيات * الموزعة بموجب الترخيص على أساس "كما هو" ، * دون ضمانات أو شروط من أي نوع ، إما صريحة أو ضمنية. * راجع ترخيص الأذونات اللغوية المحددة والقيود * بموجب الترخيص. */package org.springframework.cloud.netflix.zuul.filters.post ؛ import javax.servlet.requestDispatcher ؛ import javax.servlet.http.httpleglet org.apache.commons.logging.logfactory ؛ استيراد org.springframework.beans.factory.annotation.value ؛ استيراد org.springframework.cloud.netflix.zuul.util.zuulruntimeexception ؛ استيراد org.springframework.netilsefils ؛ org.springframework.util.stringUtils ؛ import com.netflix.zuul.zuulfilter ؛ import com.netflix.zuul.context.requestContext ؛ import com.netflix.zuul.exception.zuulexception ؛ import static static org.springframework.cloud.netflix.zuul.filters.support.filterConstants.error_type ؛ استيراد org.springframework.cloud يتم إعادة التوجيه إلى /error (افتراضيًا) إذا كان {link requestContext#getThrowable ()} ليس فارغًا. * * Author Spencer Gibb * /// ToDo: انتقل إلى حزمة الأخطاء في فئة Edgwarepublic SenderRorFilter يمتد zuulfilter {private static final log = logfactory.getlog (senderrorfilter.class) ؛ Static Final String Send_error_filter_ran = "senderrorfilter.ran" ؛ value ("$ {error.path:/error}") private string errorpath ؛ Override public string filterType () {return error_type ؛ } Override public filterorder () {return send_error_filter_order ؛ } Override public boolean shouldfilter () {requestContext ctx = requestContext.getCurrentContext () ؛ // فقط إلى الأمام إلى Errorpath إذا لم يتم إعادة توجيهه لإرجاع ctx.getthrowable ()! = null &&! ctx.getBoolean (send_error_filter_ran ، false) ؛ } Override Public Object Run () {try {requestContext ctx = requestContext.getCurrentContext () ؛ Zuulexception استثناء = findzuulexception (ctx.getThrowable ()) ؛ httpservletrequest request = ctx.getRequest () ؛ request.setattribute ("javax.servlet.error.status_code" ، استثناء. nstatuscode) ؛ log.warn ("خطأ أثناء التصفية" ، استثناء) ؛ request.setattribute ("javax.servlet.error.exception" ، استثناء) ؛ if (stringUtils.hastext (استثناء } requestDispatcher dispatcher = request.getRequestDispatcher (this.errorpath) ؛ if (dispatcher! = null) {ctx.set (send_error_filter_ran ، true) ؛ if (! ctx.getResponse (). isCommited ()) {ctx.setResPonsStatuScode (استثناء. nstatuscode) ؛ dispatcher.forward (request ، ctx.getResponse ()) ؛ }}} catch (استثناء ex) {reflectionUtils.RethRowRuntimEexception (ex) ؛ } إرجاع فارغ ؛ } zuulexception findzuulexception (قابلة للتسمية قابلة للتسمية) {if (throwable.getCause () مثيل zuulruntimeexception) {// كان هذا فشلًا بدأه أحد المرشحات المحلية (zuulexception) throw.getCause (). getCause () ؛ } if (throwable.getCause () مثيل zuulexception) {// ملفوف Zuul استثناء إرجاع (zuulexception) throwable.getCause () ؛ } if (مثيل قابل للتطبيق من Zuulexception) {// استثناء تم إلقاؤه بواسطة Zuul Lifecycle Return (zuulexception) رمي ؛ } // farrack ، يجب ألا تصل إلى هنا أبدًا Zuulexception New (يمكن رميها ، httpservletresponse.sc_internal_server_error ، null) ؛ } public void seterrorpath (string errorpath) {this.errorpath = errorpath ؛ }}هنا يمكننا العثور على عدة نقاط رئيسية:
1) في الكود أعلاه ، يمكننا أن نجد أن المرشح وضع معلومات الخطأ ذات الصلة في الطلب:
request.setattribute ("javax.servlet.error.status_code" ، استثناء. nstatuscode) ؛
request.setattribute ("javax.servlet.error.exception" ، استثناء) ؛
request.setattribute ("javax.servlet.error.message" ، استثناء. errorcause) ؛
2) بعد معالجة الخطأ ، سيتم توجيهه إلى عنوان XXX/ERROR للمعالجة
ثم يمكننا القيام بتجربة. نقوم بإنشاء مرشح يلقي استثناءات في وحدة مشروع خدمة البوابة:
package com.hzgj.lyrk.springcloud.gateway.server.filter ؛ استيراد com.netflix.zuul.zuulfilter ؛ استيراد lombok.extern.slf4j.slf4j Override public string filterType () {return "post" ؛ } Override public int filterorder () {return 9 ؛ } Override public boolean shouldfilter () {return true ؛ } Override Public Object Run () {log.info ("Run Error Test ...") ؛ رمي new RunTimeException () ؛ // إرجاع فارغ ؛ }}ثم نحدد وحدة تحكم للتعامل مع الأخطاء:
package com.hzgj.lyrk.springcloud.gateway.server.filter ؛ استيراد org.springframework.http.httpstatus org.springframework.web.bind.annotation.restController ؛ import javax.servlet.http.httpservletrequest ؛ @RestControllerP class errorder {{string = value = "/error") استجابة عامة <errorbean> error request.getAttribute ("javax.servlet.error.message"). toString () ؛ errorbean errorbean = new errorbean () ؛ errorbean.setMessage (message) ؛ errorbean.setReason ("خطأ في البرنامج") ؛ إرجاع استجابة جديدة <> (errorbean ، httpstatus.bad_gateway) ؛ } private static class errorbean {private string message ؛ سبب سلسلة خاصة السلسلة العامة getMessage () {return message ؛ } public void setMessage (رسالة سلسلة) {this.message = message ؛ } السلسلة العامة getReason () {return aspress ؛ } public void setReason (string reason) {this.reason = easure ؛ }}}بعد بدء المشروع ، دعنا نحاول الوصول إليه من خلال البوابة:
2. أسئلة حول عودة زول
1. فيما يتعلق بمشكلة مهلة زول:
هناك العديد من الحلول لهذه المشكلة عبر الإنترنت ، لكنني أريد أيضًا نشر الكود المصدري. يرجى الانتباه إلى هذه الفئة AbstractribbonCommand ، التي تدمج Hystrix و Ribbon في هذه الفئة.
/ * * حقوق الطبع والنشر 2013-2016 المؤلف أو المؤلفين الأصليين. * * مرخصة بموجب ترخيص Apache ، الإصدار 2.0 ("الترخيص") ؛ * لا يجوز لك استخدام هذا الملف إلا في الامتثال للترخيص. * يمكنك الحصول على نسخة من الترخيص على * * http://www.apache.org/licenses/license-2.0 * * ما لم يكن مطلوبًا بموجب القانون المعمول به أو يتم الاتفاق عليه في الكتابة ، يتم توزيع البرمجيات * الموزعة بموجب الترخيص على أساس "كما هو" ، * دون ضمانات أو شروط من أي نوع ، إما صريحة أو ضمنية. * راجع ترخيص الأذونات اللغوية المحددة والقيود * بموجب الترخيص. * */package org.springframework.cloud.netflix.zuul.filters.Route.Support ؛ import org.apache.commons.logging.log ؛ import org.apache.commons.logging.logfactory org.springframework.cloud.netflix.ribbon.ribbonhttpresponse ؛ استيراد org.springframework.cloud.netflix.ribbon.support.abstractloadbalancing org.springframework.cloud.netflix.zuul.filters.zuulproperties ؛ استيراد org.springframework.cloud.netflix.zuul.filters.Route.RibbonCommand ؛ org.springframework.cloud.netflix.zuul.filters.route.zuulfallbackprovider ؛ استيراد org.springframework.cloud.netflix.zuul.filters.route.fallbackprovider com.netflix.client.abstractloadbalancerawareclient ؛ import com.netflix.client.clientrequest ؛ import com.netflix.client.config.defaultClientConfigimpl ؛ import com.netflix.client.client.config.iclientconfig ؛ com.netflix.client.http.httpresponse ؛ استيراد com.netflix.config.dynamicintProperty ؛ استيراد com.netflix.config.dynamicpropertyfactory ؛ import com.netflix.hystrix.hystrixCommand ؛ import com.netflix.hystrix.hystrix.hystrix.hystrix.hystrixCommand ؛ importflix.hystrix.hystrix.hystrix.hystrix.hystrix. com.netflix.hystrix.hystrixCommandKey ؛ استيراد com.netflix.hystrix.hystrixCommandProperties ؛ استيراد com.netflix.hystrix.hystrixCommandProperties.executionIsolationStrategy ؛ com.netflix.zuul.constants.zuulconstants ؛ استيراد com.netflix.zuul.context.requestContext ؛/** يمتد HystrixCommand <ClientHttpResponse> ينفذ RibbonCommand {private static final logger = logfactory.getLog (AbstractribbonCommand.Class) ؛ عميل LBC النهائي المحمي ؛ سياق RibbonCommandContext المحمي ؛ محمي ZuulfallbackProvider ZuulfallbackProvider ؛ محمي ICLIENTCONFIG التكوين. Public AbstractribbonCommand (عميل LBC ، سياق RibbonCommandContext ، Zuulproperties zuulproperties) {هذا ("الافتراضي" ، العميل ، السياق ، zuulproperties) ؛ } ABSTRACTRACTRIBBONCOMMAND (String CommandKey ، عميل LBC ، سياق RibbonCommandContext ، Zuulproperties Zuulproperties) {this (CommandKey ، Client ، Context ، Zuulproperties ، Null) ؛ } ABSTRACTRACTRIBBONCOMMAND (String CommandKey ، عميل LBC ، سياق RibbonCommandContext ، Zuulproperties Zuulproperties ، ZuulfallbackProvider FrackingProvider) {this (CommandKey ، Client ، Context ، Zuulproperties ، Rackbackprovider ، null) ؛ } ABSTRACTRACTRIBBONCOMMAND (String CommandKey ، عميل LBC ، سياق RIBBONCOMMANDCONTEXT ، Zuulproperties Zuulproperties ، ZuulfallbackProvider ForckProvider ، IclientConfig config) {this (getSetter (commandkey ، zuulproperties ، config) ، context ، context) } محمي ArtCractRibbonCommand (Setter Setter ، عميل LBC ، سياق RibbonCommandContext ، ZuulfallbackProvider ForckProvider ، IclientConfig config) {Super (setter) ؛ this.client = العميل ؛ this.context = السياق ؛ this.zuulfallbackProvider = forpbackProvider ؛ this.config = config ؛ } static hystrixCommandProperties.Setter CreateTer (ICLientConfig config ، string commandkey ، zuulproperties zuulproperties) {int hystrixtimeout = gethystrixTimeOut (config ، commandkey) ؛ إرجاع HystrixCommandProperties.setter (). } static static int gethystrixTimeOut (iclientConfig config ، string commandKey) {int ribbontimeout = getRibbontimeout (config ، commandKey) ؛ DynamicPropertyFactory DynamicPropertyFactory = DynamicPropertyFactory.getInstance () ؛ int defaulThyStrixTimeOut = DynamicPropertyFactory.getIntProperty ("hystrix.command.default.execution.isolation.Thread.TimeoutinMillisEconds" ، 0) .get () ؛ int commandHyStrixTimeOut = DynamicPropertyFactory.getIntProperty ("hystrix.command. int hystrixtimeout ؛ if (commandhystrixTimeOut> 0) {hystrixTimeOut = commandHyStrixTimeOut ؛ } if if (defaulthystrixTimeOut> 0) {hystrixTimeout = defaulThyStrixTimeOut ؛ } آخر {hystrixTimeout = ribbontimeout ؛ } if (hystrixTimeout <ribbontimeout) {logger.warn ("تم تعيين مهلة Hystrix لـ" + hystrixTimeOut + "MS for the command" + commandkey + "أقل من مزيج من القراءة والتوصيل المهلة ،" + ribbontimeout + "ms.") ؛ } إرجاع HystrixTimeout ؛ } static static int getRibbontimeout (iclientConfig config ، string commandKey) {int ribbontimeout ؛ if (config == null) {ribbontimeout = ribbonClientConfiguration.default_read_timeout + ribbonclientConfiguration.default_connect_timeout ؛ } آخر {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 maxautoristries = getTimeout (config ، commandkey ، "maxautorestries" ، iclientconfigkey.keys.maxautoriretries ، defaultClientConfigimpl.default_max_auto_retries) ؛ int maxautoristriesnextserver = getTimeout (config ، commandkey ، "maxautoristriesNextserver" ، iclientConfigKey.keys.maxautoriretriesNextserver ، defaultClientConfigimpl.default_max_auto_next_next_server) ؛ ribbontimeout = (RibbonReadTimeout + RibbonConnectTimeOut) * (maxautoristries + 1) * (maxautorreetriesNextserver + 1) ؛ } إرجاع ribbontimeout ؛ } static private int getTimeOut (iclientConfig config ، string commandkey ، string property ، iclientConfigkey <integer> configkey ، int defaultValue) {dynamicPropertyFactory DynamicPropertyFactory = dynamicPropertyFactory.getInstance () ؛ إرجاع DynamicPropertyFactory.getIntProperty (CommandKey + "." + config.getNamesPace () + ". } deprecated // todo تم إزالتها في 2.0.x setter static setter (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 (HystrixCommandGroupeke.factory.askey ("ribbonCommand"))) .andcommandkey (HystrixCommandKey.Factory.askey (commandKey)) ؛ HystrixCommandProperties.setter setter = createSetter (config ، commandKey ، Zuulproperties) ؛ if (zuulproperties.getRibbonIsOlationStrategy () == ExecutionIsOlationStrategy.Semaphore) {Final String Name = zuulconstants.zuul_eureka + commandKey + ".Semaphore.MaxSemaphores" ؛ // نريد أن نكون افتراضيًا إلى Imphore-Isolation نظرًا لأن هذه الأوامر التي تلف/ 2 أخرى هي بالفعل مؤشر ترابط معزولة عن قيمة DynamicIntProperty = DynamicPropertyFactory.getInstance () .getIntProperty (الاسم ، zuulproperties.getSemaphore (). getMaxsSemores ()) ؛ setter.withexecutionisOlationsEmaphorEmaxConcurrentRequests (value.get ()) ؛ } آخر إذا (zuulproperties.getThreadPool (). isuseseparatethreadpools ()) {Final String threadPoolKey = zuulproperties.getThreadPool (). CommandSetTer.AndThReadPoolkey (HystrixThreadPoolKey.factory.askey (ThrospoolKey)) ؛ } return commandSetTer.andCommandPropertiesDefaults (setter) ؛ //formatter: On} Override ClientHttpResponse Run () يلقي استثناء {Final requestContext Context = requestContext.getCurrentContext () ؛ RQ request = createrequest () ؛ استجابة RS Boolean RetryAbleClient = this.client extryof ustructivallancingclient && ((AbstractLoadBalancingClient) this.client) .ISclientRetRyable ((contextawarequest) request) ؛ if (receableClient) {reponse = this.client.execute (request ، config) ؛ } else {response = this.client.executewithloadBalancer (request ، config) ؛ } context.set ("RibbonResponse" ، response) ؛ . // if (this.isresponsetimedout ()) {if (response! = null) {desponse.close () ؛ }} إرجاع New RibbonHttpResponse (استجابة) ؛ } override clienthttpresponse getFallBack () {if (zuulfallbackProvider! = null) {return getFallbackResponse () ؛ } return super.getFallback () ؛ } clientHttpresponse getFallbackResponse () {if (ZuulfallbackProvider مثيل FackBackProvider) {throwable cause = getFailedExecutionException () ؛ السبب = السبب == فارغ؟ getExecutionException (): السبب ؛ if (cause == null) {zuulfallbackprovider.fallbackResponse () ؛ } آخر {return ((forpbackProvider) ZuulfallbackProvider). FallbackResponse (cause) ؛ }} return zuulfallbackprovider.fallbackResponse () ؛ } public lbc getClient () {return client ؛ } public RibbonCommandContext getContext () {return Context ؛ } المحمي التجريدي RQ createrequest () رمي الاستثناء ؛}يرجى ملاحظة: طريقة getRibbontimeout وطريقة GethystrixTimeout ، حيث تكون قيمة هاتين الطريقتين commandkey هي اسم المسار. على سبيل المثال ، إذا زارنا: http: // localhost: 8088/order-server/xxx للوصول إلى خدمة خادم الطلب ، فإن CommandKey هو خادم الطلب
وفقًا للرمز المصدر ، قمنا أولاً بتعيين معلمات مهلة Gateway-Server:
#GLOBAL RIBBON SETTESS RIBBON: ConnectTimeout: 3000 readtimeout: 3000Hystrix: الأمر: الافتراضي: التنفيذ: العزلة: الموضوع: TimeOtinMillisEconds: 3000Zuul: المضيف: ConnectTimeOutMillis: 10000
بالطبع ، يمكنك أيضًا تعيين معلمات المهلة للشريط لخادم الطلب بشكل منفصل: order-server.ribbon.xxxx = xxx. من أجل إظهار تأثير الاحتياط في Zuul ، قمت بتعيين مهلة Hystrix أقصر قليلاً هنا. بالطبع ، من الأفضل عدم تعيين المهلة الافتراضية لـ Hystrix لتكون أقصر من مهلة الشريط. تم تحذير هذا الموقف منا في رمز المصدر.
ثم نضيف الطريقة التالية ضمن خادم الطلب:
getMapping ("/sleep/{sleeptime}") Sleep Sleep Sleep (pathvariable long time) إرجاع "النجاح" ؛ }2. طريقة العودة Zuul
يمكننا تنفيذ واجهة ZuulfallbackProvider وتنفيذ الكود:
package com.hzgj.lyrk.springcloud.gateway.server.filter ؛ import com.google.common.collect.immutablemap ؛ import com.google.gson.gsonbuilder org.springframework.http.httpheaders ؛ استيراد org.springframework.http.httpstatus ؛ استيراد org.springframework.http.mediatepe ؛ استيراد org.springframework.http.client.clienthttpresion ؛ org.springframework.stereotype.component ؛ استيراد java.io.bytearrayinputStream ؛ استيراد java.ioexception ؛ استيراد java.io.inputStream ؛ استيراد java.time.localdateTime ؛ استيراد java.localtime GetRoute () {// يمثل أن جميع الطرق يتم تكييفها مع هذا الإعداد عودة "*" ؛ } Override Public ClientHttpResponse FrackingResponse () {return New ClientHttpResponse () {Override public httpstatus getStatuscode () remoksception {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 (). إرجاع 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.