คำนำ
Zuul เป็นส่วนประกอบโอเพ่นซอร์สที่ได้รับจาก Netflix มุ่งมั่นที่จะให้การกำหนดเส้นทางแบบไดนามิกการตรวจสอบความยืดหยุ่นความปลอดภัยและบริการขอบอื่น ๆ บนแพลตฟอร์มคลาวด์ นอกจากนี้ยังมีหลาย บริษัท ที่ใช้เป็นส่วนสำคัญของเกตเวย์ ในปีนี้กลุ่มสถาปัตยกรรมของ บริษัท ตัดสินใจพัฒนาผลิตภัณฑ์เกตเวย์บูรณาการการกำหนดเส้นทางแบบไดนามิกการอนุญาตแบบไดนามิกโควต้าขีด จำกัด ปัจจุบันและฟังก์ชั่นอื่น ๆ ให้การจัดการการเรียกเครือข่ายภายนอกแบบครบวงจรสำหรับโครงการในแผนกอื่น ๆ
บทความนี้ส่วนใหญ่แนะนำเนื้อหาที่เกี่ยวข้องเกี่ยวกับ Spring Cloud Zuul Unified Exception Exception และทางเลือก มันถูกแบ่งปันสำหรับการอ้างอิงและการเรียนรู้ของคุณ ฉันจะไม่พูดด้านล่างมากนักลองมาดูการแนะนำรายละเอียดด้วยกัน
1. การจัดการข้อยกเว้นแบบครบวงจรในตัวกรอง
ในความเป็นจริงใน SpringCloud เวอร์ชัน Edgware SR2 มีการจัดการข้อผิดพลาดแบบครบวงจรใน Zuulfilter แต่ในการพัฒนาจริงฉันคิดว่าแต่ละทีมมีข้อกำหนดการประมวลผลของตัวเองสำหรับวิธีการตอบสนองของข้อผิดพลาด แล้ววิธีจัดการข้อยกเว้นแบบกำหนดเองได้อย่างไร?
ก่อนอื่นเราสามารถอ้างถึง SenderRorFilter ที่จัดทำโดย SpringCloud:
/ * * ลิขสิทธิ์ 2013-2015 ผู้แต่งหรือผู้แต่งดั้งเดิม * * ได้รับอนุญาตภายใต้ใบอนุญาต Apache เวอร์ชัน 2.0 ("ใบอนุญาต"); * คุณไม่สามารถใช้ไฟล์นี้ยกเว้นตามใบอนุญาต * คุณอาจได้รับสำเนาใบอนุญาตที่ * * http://www.apache.org/licenses/license-2.0 * * เว้นแต่ว่ากฎหมายที่บังคับใช้หรือตกลงที่จะเป็นลายลักษณ์อักษรซอฟต์แวร์ * แจกจ่ายภายใต้ใบอนุญาตจะถูกแจกจ่ายใน "ตาม" โดยไม่มีการรับประกันหรือเงื่อนไขใด ๆ * ดูใบอนุญาตสำหรับภาษาเฉพาะที่ควบคุมการอนุญาตและ * ข้อ จำกัด ภายใต้ใบอนุญาต */แพ็คเกจ org.springframework.cloud.netflix.zuul.filters.post; นำเข้า Javax.servlet.requestDispatcher; นำเข้า Javax.servlet.http.httpservletRequest org.apache.commons.logging.logfactory; นำเข้า org.springframework.beans.factory.annotation.value; นำเข้า org.springframework.cloud.netflix.zuul.util.zuulruntimeException; org.springframework.util.stringutils; นำเข้า com.netflix.zuul.zuulfilter; นำเข้า com.netflix.zuul.context.requestcontext; นำเข้า com.netflix.zuul.exception.zuulexception; org.springframework.cloud.netflix.zuul.filters.support.filterconstants.error_type; นำเข้า org.springframework.cloud.netflix.zuul.filters.support.filterconstants ที่ส่งต่อไปยัง /ข้อผิดพลาด (โดยค่าเริ่มต้น) ถ้า {@link requestcontext#getThrowable ()} ไม่เป็นโมฆะ * * @author Spencer Gibb * /// TODO: ย้ายไปยังแพ็คเกจข้อผิดพลาดในคลาส Edgwarepublic SenderRorFilter ขยาย Zuulfilter {บันทึกสุดท้ายคงที่ส่วนตัว = logfactory.getLog (SenderRoFilter.Class); สตริงสุดท้ายคงที่ได้รับการป้องกัน send_error_filter_ran = "senderrorfilter.ran"; @Value ("$ {Error.Path:/ข้อผิดพลาด}") String ErrorsPath ส่วนตัว; @Override Public String FilterType () {return error_type; } @Override public int filterOrder () {return send_error_filter_order; } @Override บูลีนสาธารณะควร filter () {requestcontext ctx = requestcontext.getCurrentContext (); // ส่งต่อไปยัง ErrorsPath เท่านั้นหากยังไม่ได้รับการส่งต่อเพื่อส่งคืน ctx.getThrowable ()! = null &&! ctx.getBoolean (send_error_filter_ran, false); } @Override วัตถุสาธารณะรัน () {ลอง {requestContext ctx = requestcontext.getCurrentContext (); zuulexception exception = findzuulexception (ctx.getthrowable ()); httpservletRequest Request = 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", Exception.errorcause); } requestDispatcher dispatcher = request.getRequestDispatcher (this.errorpath); if (dispatcher! = null) {ctx.set (send_error_filter_ran, true); if (! ctx.getResponse (). isCommitted ()) {ctx.setResponSestatusCode (Exception.nstatusCode); dispatcher.forward (คำขอ, ctx.getResponse ()); }}} catch (Exception Ex) {reflectionUtils.RethRowRuntimeException (Ex); } return null; } zuulexception findzuulexception (throwable throwable) {ถ้า (throwable.getcause () อินสแตนซ์ของ ZuulruntimeException) {// นี่เป็นความล้มเหลวที่เริ่มต้นโดยหนึ่งในตัวกรองท้องถิ่นกลับ (zuulexception) throw.getCause () } if (throwable.getCause () อินสแตนซ์ของ zuulexception) {// zuul exception return (zuulexception) throwable.getCause (); } if (อินสแตนซ์ที่โยนได้ของ zuulexception) {// ข้อยกเว้นที่ถูกโยนโดย Zuul Lifecycle Return (Zuulexception) Trowable; } // ทางเลือกไม่ควรกลับมาที่นี่ใหม่ zuulexception (throwable, httpservletResponse.sc_internal_server_error, null); } โมฆะสาธารณะ setERRORRORPATH (String errorPath) {this.errorPath = ERRORSPATH; -ที่นี่เราสามารถค้นหาประเด็นสำคัญหลายประการ:
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/ข้อผิดพลาดสำหรับการประมวลผล
จากนั้นเราสามารถทำการทดลองได้ เราสร้างตัวกรองที่โยนข้อยกเว้นในโมดูลโครงการ Gateway-Service:
แพ็คเกจ com.hzgj.lyrk.springcloud.gateway.server.filter; นำเข้า com.netflix.zuul.zuulfilter; นำเข้า lombok.extern.slf4j.slf4j; นำเข้า org.springframework.stereotype.component; {@Override Public String FilterType () {return "post"; } @Override public int filterOrder () {return 9; } @Override บูลีนสาธารณะควร filter () {return true; } @Override วัตถุสาธารณะรัน () {log.info ("เรียกใช้การทดสอบข้อผิดพลาด ... "); โยน runtimeException ใหม่ (); // return null; -จากนั้นเรากำหนดคอนโทรลเลอร์เพื่อจัดการกับข้อผิดพลาด:
แพ็คเกจ com.hzgj.lyrk.springcloud.gateway.server.filter; นำเข้า org.springframework.http.httpstatus นำเข้า org.springframework.http.responseentity; org.springframework.web.bind.annotation.restcontroller; นำเข้า Javax.servlet.http.httpservletRequest; @RestControllerPublic Class ErrorSherTler {@getMapping (value = "/ข้อผิดพลาด") request.getAttribute ("javax.servlet.error.message"). toString (); ErrorBean ErrorBean = new ErrorBean (); ErrorBean.setMessage (ข้อความ); ErrorBean.setReason ("ข้อผิดพลาดของโปรแกรม"); ส่งคืน ResponseEntity ใหม่ <> (ErrorBean, httpstatus.bad_gateway); } Class Errorbean ส่วนตัวแบบคงที่ {ข้อความสตริงส่วนตัว; เหตุผลสตริงส่วนตัว สตริงสาธารณะ getMessage () {ส่งคืนข้อความ; } โมฆะสาธารณะ setMessage (ข้อความสตริง) {this.message = ข้อความ; } Public String getReason () {return Reason; } โมฆะสาธารณะ setReason (เหตุผลสตริง) {this.reason = เหตุผล; -หลังจากเริ่มโครงการลองเข้าถึงผ่านเกตเวย์:
2. คำถามเกี่ยวกับ Zuul ทางเลือก
1. เกี่ยวกับปัญหาการหมดเวลาของ Zuul:
มีวิธีแก้ปัญหามากมายสำหรับปัญหานี้ทางออนไลน์ แต่ฉันต้องการโพสต์ซอร์สโค้ดด้วย โปรดให้ความสนใจกับ AbstractribbonCommand คลาสนี้ซึ่งรวม Hystrix และ Ribbon ในชั้นเรียนนี้
/ * * ลิขสิทธิ์ 2013-2016 ผู้แต่งหรือผู้แต่งดั้งเดิม * * ได้รับอนุญาตภายใต้ใบอนุญาต Apache เวอร์ชัน 2.0 ("ใบอนุญาต"); * คุณไม่สามารถใช้ไฟล์นี้ยกเว้นตามใบอนุญาต * คุณอาจได้รับสำเนาใบอนุญาตที่ * * http://www.apache.org/licenses/license-2.0 * * เว้นแต่ว่ากฎหมายที่บังคับใช้หรือตกลงที่จะเป็นลายลักษณ์อักษรซอฟต์แวร์ * แจกจ่ายภายใต้ใบอนุญาตจะถูกแจกจ่ายใน "ตาม" โดยไม่มีการรับประกันหรือเงื่อนไขใด ๆ * ดูใบอนุญาตสำหรับภาษาเฉพาะที่ควบคุมการอนุญาตและ * ข้อ จำกัด ภายใต้ใบอนุญาต * */แพ็คเกจ org.springframework.cloud.netflix.zuul.filters.route.support; นำเข้า org.apache.commons.logging.log; นำเข้า org.apache.Commons.logging.logfactory; org.springframework.cloud.netflix.ribbon.ribbonhttpresponse; นำเข้า org.springframework.cloud.netflix.ribbon.support.abstractloadbalancing client; นำเข้า 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; import org.springframework com.netflix.client.abstractloadbalancerawareClient; นำเข้า com.netflix.client.clientrequest; นำเข้า com.netflix.client.config.defaultClientconfigimpl; นำเข้า com.netflix.config.iclientconfigientconlientconlientcontconlientcontconlientconlientconlient com.netflix.client.http.httpresponse; นำเข้า com.netflix.config.dynamicintproperty; นำเข้า com.netflix.config.dynamicpropertyfactory; นำเข้า com.netflix.hystrix.hystrixCommand; com.netflix.hystrix.hystrixCommandkey; นำเข้า com.netflix.hystrix.hystrixCommandProperties; นำเข้า com.netflix.hystrix.hystrixCommandProperties.executionisolationStrategy; com.netflix.zuul.constants.zuulconstants; นำเข้า com.netflix.zuul.context.requestcontext;/** * @author Spencer Gibb */Public Abstractribboncommand <lbc ขยายบทคัดย่อ ขยาย HystrixCommand <clienthttpresponse> ใช้ RibbonCommand {logger บันทึกสุดท้ายคงที่ส่วนตัว = logfactory.getLog (AbstractribbonCommand.class); ได้รับการปกป้องไคลเอนต์ LBC สุดท้าย; บริบท RibbonCommandContext ที่ได้รับการป้องกัน ได้รับการคุ้มครอง Zuulfallbackprovider Zuulfallbackprovider; การกำหนดค่า iClientConfig ที่ได้รับการป้องกัน Public AbstractribbonCommand (LBC ไคลเอ็นต์, บริบท RibbonCommandContext, Zuulproperties Zuulproperties) {สิ่งนี้ ("เริ่มต้น", ลูกค้า, บริบท, Zuulproperties); } Public AbstractribbonCommand (String CommandKey, LBC ไคลเอนต์, บริบท RibbonCommandContext, Zuulproperties Zuulproperties) {สิ่งนี้ (commandkey, ลูกค้า, บริบท, Zuulproperties, null); } Public AbstractribbonCommand (String CommandKey, LBC ไคลเอ็นต์, บริบท RibbonCommandContext, Zuulproperties Zuulproperties, Zuulfallbackprovider FallbackProvider) {สิ่งนี้ (commandkey ลูกค้าบริบท } Public AbstractribbonCommand (String CommandKey, LBC ไคลเอนต์, บริบท RibbonCommandContext, Zuulproperties Zuulproperties, Zuulfallbackprovider FallbackProvider, IclientConfig config) {สิ่งนี้ } AbstractribbonCommand ที่ได้รับการป้องกัน (Setter Setter, LBC ไคลเอ็นต์, บริบท RibbonCommandContext, ZuulfallbackProvider FallbackProvider, IclientConfig config) {super (setter); this.client = ไคลเอนต์; this.context = บริบท; this.zuulfallbackprovider = fallbackprovider; this.config = config; } ป้องกัน hystrixCommandProperties.setter createSetter (iClientConfig config, สตริง commandkey, zuulproperties zuulproperties) {int hystrixtimeout = gethyStrixtimeout (config, commandkey); ส่งคืน HystrixCommandProperties.setter (). withExecutionisolationStrategy (Zuulproperties.getribbonisolationStrategy ()). withExecutimeOutInmilliseconds (Hystrixtimeout); } ป้องกัน int gethystrixtimeout (iclientConfig config, String CommandKey) {int ribbontimeout = getribbontimeout (config, commandKey); DynamicPropertyFactory DynamicPropertyFactory = DynamicPropertyFactory.getInstance (); int defaulthyStrixtimeout = DynamicPropertyFintory.getIntProperty ("Hystrix.Command.default.execution.isolation.thread.timeoutinmilliseconds", 0) .get (); int commandhyStrixtimeout = DynamicPropertyFintory.getIntProperty ("Hystrix.Command." + CommandKey + ".Execution.isolation.thread.timeoutinmilliseconds", 0) .get (); int hystrixtimeout; if (CommandHyStrixtimeout> 0) {Hystrixtimeout = CommandHyStrixtimeout; } อื่นถ้า (defaulthyStrixtimeout> 0) {HyStrixtimeout = defaulthyStrixtimeOut; } else {Hystrixtimeout = Ribbontimeout; } if (Hystrixtimeout <Ribbontimeout) {logger.warn ("การหมดเวลา Hystrix ของ" + Hystrixtimeout + "MS สำหรับคำสั่ง" + CommandKey + "ถูกตั้งค่าต่ำกว่าการรวมกันของการอ่านริบบิ้นและเชื่อมต่อหมดเวลา" + Ribbontimeout + "MS.") } ส่งคืน Hystrixtimeout; } ได้รับการป้องกัน int 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, สตริง commandkey, คุณสมบัติสตริง, iclientConfigkey <teger> configkey, int defaultValue) {DynamicPropertyFactory DynamicPropertyFortory = DynamicProperTyFactory.GetInStance (); Return DynamicProperTyFactory.getIntProperty (CommandKey + "." + config.getNamespace () + "." + คุณสมบัติ, config.get (configkey, defaultValue)). get (); } @deprecated // todo ลบออกใน 2.0.x setter คงที่ป้องกัน getSetter (สตริงสุดท้าย commandkey, zuulproperties zuulproperties) {return getsetter (commandkey, zuulproperties, null); } การป้องกันการตั้งค่าคงที่ getSetter (สตริงสุดท้าย commandkey, zuulproperties zuulproperties, iClientConfig config) {// @Formatter: ปิดคอมมิวชั่น SetTer = setter.WithGroupKey HystrixCommandProperties.Setter setter = createsetter (config, commandkey, zuulproperties); if (zuulproperties.getribbonisolationsTrategy () == ExecutionisolationStrategy.semaphore) {ชื่อสตริงสุดท้าย = zuulconstants.zuul_eureka + Commandkey + ".semaphore.maxsemaphores"; // เราต้องการเริ่มต้นเป็น semaphore-isolation เนื่องจากคำสั่ง wraps // 2 คำสั่งอื่น ๆ ที่มีเธรดการแยก dynamicintProperty สุดท้ายแล้ว = DynamicPropertyFactory.getInstance () .getIntProperty (ชื่อ, ZuulProperties.getSemaphore () setter.ithexecutionisolationsemaphoremaxconcurrentrequests (value.get ()); } อื่นถ้า (zuulproperties.getThreadpool (). isusesparatethreadpools ()) {สตริงสุดท้าย threadpoolkey = zuulproperties.getThreadPool (). getThreadPoolKeyPrefix () + CommandKey; Commandsetter.andthreadpoolkey (Hystrixthreadpoolkey.factory.askey (ThreadPoolkey)); } return commandsetter.andcommandpropertiesdefaults (setter); // @Formatter: on} @Override protected clienthttpresponse run () โยนข้อยกเว้น {final requestcontext context = requestcontext.getCurrentContext (); คำขอ RQ = createRequest (); การตอบสนองของ RS; บูลีน retryableClient = this.client อินสแตนซ์ของ AbstractloadBalancingClient && ((AbstractloadBalancingClient) this.client). if (retryableClient) {response = this.client.execute (คำขอ, config); } else {response = this.client.executewithloadbalancer (คำขอ, config); } context.set ("ribbonresponse", การตอบสนอง); // ปิด HTTPRESSPONSE อย่างชัดเจนหากคำสั่ง Hystrix หมดเวลาไปที่ // ปล่อยการเชื่อมต่อ HTTP พื้นฐานที่จัดขึ้นโดยการตอบกลับ // ถ้า (this.isresponsetimedout ()) {ถ้า (ตอบกลับ! = null) {response.close (); }} ส่งคืน Ribbonhttpresponse ใหม่ (การตอบสนอง); } @Override protected clienthttpresponse getFallback () {ถ้า (zuulfallbackprovider! = null) {return getFallBackResponse (); } return super.getFallback (); } clienthttpresponse getFallbackResponse () {ถ้า (zuulfallbackprovider อินสแตนซ์ของ fallbackprovider) {สาเหตุที่น่าสนใจ = getFaileDexecutionException (); สาเหตุ = สาเหตุ == null? getExecutionException (): สาเหตุ; if (สาเหตุ == null) {zuulfallbackprovider.fallbackResponse (); } else {return ((fallbackprovider) ZuulfallbackProvider) .FallbackResponse (สาเหตุ); }} ส่งคืน ZuulfallBackProvider.FallBackResponse (); } สาธารณะ lbc getClient () {return client; } RibbonCommandContext GetContext () {กลับบริบท; } การป้องกันบทคัดย่อ RQ CreateRequest () โยนข้อยกเว้น;}โปรดทราบ: วิธี getribbontimeout และวิธี gethystrixtimeout ซึ่งค่าของสองวิธีนี้ commandkey เป็นชื่อของเส้นทาง ตัวอย่างเช่นถ้าเราเยี่ยมชม: http: // localhost: 8088/order-server/xxx เพื่อเข้าถึงบริการคำสั่งซื้อเซิร์ฟเวอร์จากนั้น commandkey คือคำสั่งซื้อเซิร์ฟเวอร์
ตามซอร์สโค้ดก่อนอื่นเราตั้งค่าพารามิเตอร์การหมดเวลาเกตเวย์เซิร์ฟเวอร์:
#Global Ribbon Settings Ribbon: ConnectTimeout: 3000 ReadTimeOut: 3000Hystrix: คำสั่ง: ค่าเริ่มต้น: การดำเนินการ: การแยก: เธรด: TimeOutinMilliseconds: 3000ZUUL: โฮสต์: ConnectTimeOutMillis: 10000
แน่นอนคุณสามารถตั้งค่าพารามิเตอร์การหมดเวลาของริบบิ้นสำหรับการสั่งซื้อเซิร์ฟเวอร์แยกกัน: order-server.ribbon.xxxx = xxx เพื่อแสดงให้เห็นถึงเอฟเฟกต์ทางเลือกใน Zuul ฉันตั้งค่าการหมดเวลา Hystrix สั้นลงเล็กน้อยที่นี่ แน่นอนว่าเป็นการดีที่สุดที่จะไม่ตั้งค่าการหมดเวลาเริ่มต้นของ Hystrix ให้สั้นกว่าการหมดเวลาของริบบิ้น สถานการณ์นี้ได้รับการเตือนจากเราในซอร์สโค้ด
จากนั้นเราเพิ่มวิธีการต่อไปนี้ภายใต้คำสั่งซื้อเซิร์ฟเวอร์:
@getMapping ("/sleep/{sleeptime}") Public String Sleep (@PathVariable Long Sleeptime) พ่น InterruptedException {timeUnit.seconds.sleep (เวลานอน); กลับ "ความสำเร็จ"; -2. วิธีทางเลือกของ Zuul
เราสามารถใช้อินเทอร์เฟซ ZuulfallbackProvider และใช้รหัส:
แพ็คเกจ com.hzgj.lyrk.springcloud.gateway.server.filter; นำเข้า com.google.common.collect.immutableMap; นำเข้า com.google.gson.gsonbuilder; นำเข้า org.springframework.cloud.netflix.zuul. org.springframework.http.httpheaders; นำเข้า org.springframework.http.httpstatus นำเข้า org.springframework.http.mediatype; นำเข้า org.springframework org.springframework.stereotype.Component; นำเข้า java.io.ByTearrayInputStream นำเข้า java.io.ioException; นำเข้า java.io.InputStream; นำเข้า Java.time.localdatetime; นำเข้า Java.time.localtime; getRoute () {// แสดงให้เห็นว่าเส้นทางทั้งหมดได้รับการปรับให้เข้ากับการตั้งค่านี้กลับมา "*"; } @Override public clienthttpresponse fallbackResponse () {ส่งคืนไคลเอนต์ใหม่ httpresponse () {@Override สาธารณะ httpstatus getStatusCode () โยน ioexception {return httpstatus.ok; } @Override สาธารณะ int getRawStatusCode () พ่น IOException {return 200; } @Override สตริงสาธารณะ getStatustext () พ่น IOException {return "ตกลง"; } @Override โมฆะสาธารณะปิด () {} @Override public public inputStream getBody () พ่น IOException {string result = new gsonBuilder (). สร้าง (). tojson (immutableMap.of ("ErrorCode", 500, "เนื้อหา", " ส่งคืน ByteArrayInputStream ใหม่ (result.getBytes ()); } @Override public httpheaders getheaders () {httpheaders ส่วนหัว = httpheaders ใหม่ (); Headers.setContentType (MediaType.Application_JSON); ส่วนหัวกลับ; - -ในเวลานี้เราไปที่: http: // localhost: 8088/order-server/sleep/6 และรับผลลัพธ์ต่อไปนี้:
เมื่อเราเยี่ยมชม: http: // localhost: 8088/order-server/sleep/1 เราได้รับผลต่อไปนี้:
สรุป
ข้างต้นเป็นเนื้อหาทั้งหมดของบทความนี้ ฉันหวังว่าเนื้อหาของบทความนี้จะมีค่าอ้างอิงบางอย่างสำหรับการศึกษาหรือที่ทำงานของทุกคน หากคุณมีคำถามใด ๆ คุณสามารถฝากข้อความไว้เพื่อสื่อสาร ขอบคุณสำหรับการสนับสนุน Wulin.com