Kata pengantar
Zuul adalah komponen open source yang disediakan oleh Netflix, berkomitmen untuk menyediakan routing dinamis, pemantauan, ketahanan, keamanan, dan layanan tepi lainnya di platform cloud. Ada juga banyak perusahaan yang menggunakannya sebagai bagian penting dari gateway. Tahun ini, kelompok arsitektur perusahaan memutuskan untuk mengembangkan produk gateway, mengintegrasikan perutean dinamis, izin dinamis, kuota batas saat ini dan fungsi lainnya, menyediakan manajemen panggilan jaringan eksternal yang terpadu untuk proyek -proyek di departemen lain, dan akhirnya membentuk produk (Ali sebenarnya memiliki produk gateway yang matang dalam hal ini, tetapi tidak sesuai dengan konfigurasi yang dipersonalisasi, dan tidak memiliki batasan yang diintegrasikan, tetapi ini tidak sesuai dengan konfigurasi yang dipersonalisasi, dan tidak memiliki batasan yang diintegrasikan, dan tidak ada yang diintegrasikan.
Artikel ini terutama memperkenalkan konten yang relevan tentang penanganan Spring Cloud Zuul Unified Exception dan Fallback. Ini dibagikan untuk referensi dan pembelajaran Anda. Saya tidak akan mengatakan banyak hal di bawah ini, mari kita lihat perkenalan terperinci bersama.
1. Penanganan pengecualian terpadu di filter
Faktanya, dalam versi Edgware SR2 dari Springcloud, ada penanganan kesalahan yang terpadu di Zuulfilter, tetapi dalam pengembangan aktual, saya pikir masing -masing tim memiliki spesifikasi pemrosesan sendiri untuk metode respons kesalahan. Jadi bagaimana cara melakukan penanganan pengecualian khusus?
Pertama -tama kita dapat merujuk ke senderrorfilter yang disediakan oleh SpringCloud:
/ * * Hak Cipta 2013-2015 Penulis atau penulis asli. * * Dilisensikan di bawah lisensi Apache, versi 2.0 ("lisensi"); * Anda tidak boleh menggunakan file ini kecuali sesuai dengan lisensi. * Anda dapat memperoleh salinan lisensi di * * http://www.apache.org/licenses/license-2.0 * * kecuali diharuskan oleh hukum yang berlaku atau disepakati secara tertulis, perangkat lunak * yang didistribusikan di bawah lisensi didistribusikan atas dasar "sebagaimana adanya", * tanpa jaminan atau ketentuan apa pun, baik yang diungkapkan atau disiratkan. * Lihat lisensi untuk izin yang mengatur bahasa tertentu dan * batasan di bawah lisensi. */paket org.springframework.cloud.netflix.zuul.filters.post; import javax.servlet.requestdispatcher; import javax.servlet.http.htpservletrequest; impor orvax.servlet.http.htppservleteFet; org.apache.commons.logging.logfactory; impor org.springframework.beans.factory.annotation.value; impor org.springframework.cloud.netflix.zuul.util.zuulimeeexception; impor org.springfrunutil.zuulimeeexception; impor org.springframework.util.zuulimeeexception; impor org.springframework.zuulruntimeeexception; impor org.springframework.zuulruntimeException; impor org.springframework.util.stringutils; import com.netflix.zuul.zuulfilter; import com.netflix.zuul.context.requestContext; impor statis com.netflix.zuul.exception.zuulException; impor statis statat. org.springframework.cloud.netflix.zuul.filters.support.filterconstants.error_type; impor static org.springframework.cloud.netflix.zuul.filters.support.filterconstants.send_rorflix.filter. /error (secara default) if {@link requestContext#getThrowable ()} tidak nol. * * @Author Spencer Gibb * /// TODO: Pindah ke Paket Kesalahan di kelas Edgwarepublic SendErrorFilter memperluas Zuulfilter {log private static log = logfactory.getLog (sendErrorfilter.class); string final statis yang dilindungi send_error_filter_ran = "senderrorfilter.ran"; @Value ("$ {error.path:/error}") private string errorPath; @Override public string filtertype () {return error_type; } @Override public int filterorder () {return send_error_filter_order; } @Override public boolean seharusnya filter () {requestContext ctx = requestContext.getCurrentContext (); // hanya meneruskan ke errorPath jika belum diteruskan untuk mengembalikan ctx.getThrowable ()! = null &&! ctx.getBoolean (send_error_filter_ran, false); } @Override Public Object Run () {coba {requestContext ctx = requestContext.getCurrentContext (); ZuulException Exception = findzuulException (ctx.getThrowable ()); HttpservletRequest request = ctx.getRequest (); request.setAttribute ("javax.servlet.error.status_code", exception.nstatuscode); log.warn ("kesalahan selama penyaringan", pengecualian); request.setAttribute ("javax.servlet.error.exception", pengecualian); 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 (permintaan, ctx.getResponse ()); }}} catch (exception ex) {reflectionutils.rethrowruntimeException (ex); } return null; } ZuulException findzuulException (lempar lempar) {if (throwable.getCause () instance dari ZuulrunTimeException) {// Ini adalah kegagalan yang diprakarsai oleh salah satu filter lokal pengembalian (ZuulException) Throwable.getCause (). GetCause (); } if (throwable.getCause () instanceof ZuulException) {// wrapped zuul exception return (zuulException) throwable.getCause (); } if (instance yang dapat dilempar dari ZuulException) {// Pengecualian yang dilemparkan oleh Zuul Lifecycle Return (ZuulException) Throwable; } // Fallback, tidak boleh sampai di sini mengembalikan ZuulException baru (Throwable, httpservletResponse.sc_internal_server_error, null); } public void setErrorPath (string errorPath) {this.errorpath = errorPath; }}Di sini kita dapat menemukan beberapa poin penting:
1) Dalam kode di atas, kita dapat menemukan bahwa filter telah memasukkan informasi kesalahan yang relevan ke dalam permintaan:
request.setAttribute ("javax.servlet.error.status_code", exception.nstatuscode);
request.setAttribute ("javax.servlet.error.exception", pengecualian);
request.setAttribute ("javax.servlet.error.message", exception.errorcause);
2) Setelah kesalahan diproses, itu akan diteruskan ke alamat XXX/kesalahan untuk diproses
Kemudian kita bisa melakukan percobaan. Kami membuat filter yang melempar pengecualian dalam modul proyek layanan gateway:
Paket com.hzgj.lyrk.springcloud.gateway.server.filter; import com.netflix.zuul.zuulfilter; import lombok.extern.slf4j.slf4j; impor org.springframework.stereotype.skon. @Override public string filtertype () {return "post"; } @Override public int filterorder () {return 9; } @Override public boolean seharusnya filter () {return true; } @Override Public Object Run () {log.info ("Jalankan tes kesalahan ..."); lempar runimeException baru (); // return null; }}Kemudian kami mendefinisikan pengontrol untuk menangani kesalahan:
Paket com.hzgj.lyrk.springcloud.gateway.server.filter; impor org.springframework.http.httpstatus; impor org.springframework.nttp.responseentity; impor org.springframework.wind.anannot; org.springframework.web.bind.annotation.RestController;import javax.servlet.http.HttpServletRequest;@RestControllerpublic class ErrorHandler { @GetMapping(value = "/error") public ResponseEntity<ErrorBean> error(HttpServletRequest request) { String message = request.getAttribute ("javax.servlet.error.message"). ToString (); ErrorBean errorBean = new errorBean (); errorbean.setMessage (pesan); errorbean.setreason ("kesalahan program"); Return New ResponseEntity <> (ErrorBean, httpstatus.bad_gateway); } private static class errorBean {private string pesan; alasan string pribadi; Public String getMessage () {return pesan; } public void setMessage (string message) {this.message = pesan; } public String getReason () {return rale; } public void setReason (string reason) {this.reason = alasan; }}}Setelah memulai proyek, mari kita coba mengaksesnya melalui gateway:
2. Pertanyaan tentang Zuul Fallback
1. Mengenai masalah batas waktu Zuul:
Ada banyak solusi untuk masalah ini secara online, tetapi saya juga ingin memposting kode sumber. Harap perhatikan kelas ini AbstractribbonCommand, yang mengintegrasikan hystrix dan pita di kelas ini.
/ * * Hak Cipta 2013-2016 Penulis asli atau penulis. * * Dilisensikan di bawah lisensi Apache, versi 2.0 ("lisensi"); * Anda tidak boleh menggunakan file ini kecuali sesuai dengan lisensi. * Anda dapat memperoleh salinan lisensi di * * http://www.apache.org/licenses/license-2.0 * * kecuali diharuskan oleh hukum yang berlaku atau disepakati secara tertulis, perangkat lunak * yang didistribusikan di bawah lisensi didistribusikan atas dasar "sebagaimana adanya", * tanpa jaminan atau ketentuan apa pun, baik yang diungkapkan atau disiratkan. * Lihat lisensi untuk izin yang mengatur bahasa tertentu dan * batasan di bawah lisensi. * */paket org.springframework.cloud.netflix.zuul.filters.route.support; impor org.apache.commons.logging.log; impor org.apache.commons.logging.logfactory; impor org.springframework.cloud.netflix.logfactory; org.springframework.cloud.netflix.logfactory; org.springframework.cloud.netflix.logfactory; org.springframework.cloud.netflix.ribbon.ribbonHttpresponse; impor org.springframework.cloud.netflix.ribbon.support.AbstractloadAnkClient; import org.spramework.cloud.netflix.ribbon.support.support.support.support org.springframework.cloud.netflix.zuul.filters.zuulproperties; impor org.springframework.cloud.netflix.zuul.filters.route.ribbonCommand; import org.springframework.cloud.netflix.zuulct org.springframework.cloud.netflix.zuul.filters.route.zuulfallbackprovider; impor org.springframework.cloud.netflix.zuul.filters.route.fallbackprovider; org comfflix.client.abstractloadBalanCerAslient; import com.netflix.client.clientRequest; import com.netflix.client.config.defaultclientconfigImpl; import com.netflix.client.config.iconfig.iconfig; impor com.netflix.client.config.iconfig; comfflix.client.http.httpresponse; import com.netflix.config.dynamicintproperty; import com.netflix.config.dynamicpropertyfactory; import com.netflix.hystrix.hytrixCommand; com.netflix.hystrix.hystrixCommandkey; import com.netflix.hystrix.hystrixCommandproperties; impor com.netflix.hystrix.hystrixCommandproperties.executionistrategategy; impor com.netflix.hystrix.hystrix.executionistrategategate; com.netflix.hystrix.hystrix.executionistrategate; com.netflix.zuul.constants.ZuulConstants;import com.netflix.zuul.context.RequestContext;/** * @author Spencer Gibb */public abstract class AbstractRibbonCommand<LBC extends AbstractLoadBalancerAwareClient<RQ, RS>, RQ extends ClientRequest, RS extends HttpResponse> Extends hystrixCommand <ClientHttpresponse> mengimplementasikan RibbonCommand {private static final log logger = logfactory.getLog (abstractribbonCommand.class); Klien LBC akhir yang dilindungi; Konteks RibbonCommandContext yang Dilindungi; ZuulfallbackProvider yang dilindungi ZuulfallbackProvider; konfigurasi iclientConfig yang dilindungi; PUBLIK ABSTRACTRIBBONCOMMAND (Klien LBC, Konteks RibbonCommandContext, Zuulproperties Zuulproperties) {this ("Default", Client, Context, Zuulproperties); } public abstractribbonCommand (String CommandKey, klien LBC, Konteks RibbonCommandContext, Zuulproperties Zuulproperties) {this (CommandKey, Client, Context, Zuulproperties, Null); } public abstractribbonCommand (String CommandKey, klien LBC, Konteks RibbonCommandContext, Zuulproperties Zuulproperties, ZuulfallBackProvider FallbackProvider) {this, CommandKey, Client, Zuulproperties, FallbackProvider, Null); } public AbstractRibbonCommand(String commandKey, LBC client, RibbonCommandContext context, ZuulProperties zuulProperties, ZuulFallbackProvider fallbackProvider, IClientConfig config) { this(getSetter(commandKey, zuulProperties, config), client, context, fallbackProvider, config); } Protected AbstractribbonCommand (setter setter, klien LBC, Konteks RibbonCommandContext, ZuulfallBackProvider FallbackProvider, config iclientConfig) {super (setter); this.client = klien; this.context = konteks; this.zuulfallbackProvider = fallbackProvider; this.config = config; } lindung statis hystrixCommandproperties.setter createsetter (iclientConfig config, string commandKey, zuulproperties zuulproperties) {int hystrixTimeout = getHystrixTimeout (config, commandKey); return hystrixCommandproperties.setter (). WithExecutionisolationsTrategy (zuulproperties.getribbonisolationstrategy ()). WithExecutionTimeOutInmilliseconds (hystrixTimeout); } protected static int getHystrixTimeout (iclientConfig config, string commandKey) {int ribbonTimeout = getRibBonTimeOut (config, commandKey); DynamicPropertyFactory DynamicPropertyFactory = DynamicPropertyFactory.getInstance (); int defaulthystrixTimeout = DynamicPropertyFactory.getInproperty ("hystrix.command.default.execution.isolation.thread.timeoutInmilliseconds", 0) .get (); int commandHystrixTimeout = DynamicPropertyFactory.getInproperty ("hystrix.command." + CommandKey + ".execution.isolation.thread.timeoutInmilliseconds", 0) .get (); int hystrixTimeout; if (commandHystrixTimeout> 0) {hystrixTameout = commandHystrixTtimeout; } lain jika (defaulThystrixTameout> 0) {hystrixTimeOut = defaulThystrixTameout; } else {hystrixTimeout = RibbonTimeout; } if (hystrixTimeout <ribbonTimeOut) {logger.warn ("Hystrix timeout" + hystrixTimeout + "ms untuk perintah" + commandKey + "diatur lebih rendah dari kombinasi pita baca dan hubungkan batas waktu," + RibbonTimeout + "ms."); } return hystrixTimeout; } protected static 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.maxautoretryNextServer, defaultClientConfigImpl.default_max_auto_retries_next_server); RibbonTimeout = (RibbonReadTimeOut + RibbonConnectTimeout) * (MaxAutoretries + 1) * (MaxAutoretriesNextServer + 1); } return ribbonTimeout; } private static int getTimeout (iclientConfig config, string commandKey, string properti, iclientConfigKey <Integer> configkey, int defaultValue) {DynamicPropertyFactory DynamicPropertyFactory = DynamicPropertyFactory.getInstance (); return dynamicPropertyFactory.getIntProperty (commandKey + "." + config.getnamespace () + "." + Properti, config.get (configKey, defaultValue)). get (); } @Deprecated // TODO Dihapus di 2.0.x STATIC STATIC SETTER GetSetter (Final String CommandKey, Zuulproperties Zuulproperties) {return getSetter (CommandKey, Zuulproperties, NULL); } getsetter setter statis yang dilindungi (final string commandkey, zuulproperties zuulproperties, iclientConfig config) {// @formatter: off setter commandsetter = setter.withgroupkey (hystrixCommandkey.factory.askey ("ribbonCommandKey) .andanMandkey.factory.askey (" ribbonCommandKey)) .andKEXOmmandKey.factory.askey ("" RibBonCommandKey)) .AndixCommandKey.factory.askey ("" RibBonCommandKey))) .Andixomandkey.factory.askey ("" RibBonCommandKey))) .andKey). final hystrixCommandproperties.setter setter = createSetter (config, commandKey, zuulproperties); if (zuulproperties.getRibbonisolationstrategy () == executionisolationstrategy.semaphore) {final string name = zuulconstants.zuul_eureka + commandKey + ".semaphore.maxsemaphores"; // Kami ingin default ke semaphore-isolasi karena wraps ini // 2 perintah lainnya yang sudah terisolasi nilainya DynamicIntProperty final terisolasi = DynamicPropertyFactory.getInstance () .getIntProperty (nama, zuulproperty.getSemaphore (). getMaxsemaphores ()); setter.withexecutionolationseMaphoremaxConcurrentRentRequests (value.get ()); } lain jika (zuulproperties.getThreadpool (). iSuseParatEthreadPools ()) {string final threadpoolkey = zuulproperties.getThreadpool (). getThreadPoolKeyPrefix () + commandKey; commandsetter.andthreadpoolkey (hystrixthreadpoolkey.factory.askey (threadpoolkey)); } return commandsetter.andcommandpropertiesDefaults (setter); // @Formatter: on} @Override Dilindungi ClientHttPresponse run () melempar Exception {final requestContext context = requestContext.getCurrentContext (); Rq request = createRequest (); Respons RS; boolean retryableClient = this.client instance dari AbstractLoadAnceDClient && ((AbstractLoadAndingClient) this.client) .isclientretryable ((ContextAdareRequest) permintaan); if (retryableClient) {response = this.client.execute (request, config); } else {response = this.client.executeWithLoadBalancer (request, config); } context.set ("RibbonResponse", Response); // Secara eksplisit tutup httpresponse jika perintah hystrix menghitung waktu untuk // lepaskan koneksi http yang mendasari yang dipegang oleh respons. // if (this.isResponsetimedout ()) {if (response! = null) {response.close (); }} return ribbonHttpresponse baru (respons); } @Override dilindungi clientHttpresponse getfallback () {if (zuulfallbackprovider! = Null) {return getfallbackResponse (); } return super.getfallback (); } clientHttpresponse getfallBackResponse () {if (zuulfallbackprovider instance dari fallbackProvider) {throwable cause = getFailedExecutionException (); Penyebab = Penyebab == NULL? getExecutionException (): penyebab; if (cause == null) {zuulfallbackprovider.fallbackResponse (); } else {return ((fallbackprovider) zuulfallbackprovider) .fallbackResponse (penyebab); }} return zuulfallbackprovider.fallbackResponse (); } public lbc getClient () {return client; } public RibbonCommandContext getContext () {return Context; } terlindungi abstrak rq createRequest () melempar pengecualian;}Harap dicatat: Metode GetRibBonTimeout dan Metode GetHystrixTimeout, di mana nilai kedua metode ini CommandKey adalah nama rute. Misalnya, jika kita mengunjungi: http: // localhost: 8088/order-server/xxx untuk mengakses layanan pesanan-server, maka commandKey adalah server pesanan
Menurut kode sumber, pertama-tama kami mengatur parameter batas waktu gateway-server:
#Global Ribbon Pengaturan Pita: Connecttimeout: 3000 Readtimeout: 3000Hystrix: Perintah: Default: Eksekusi: Isolasi: Thread: TimeoutInmilliseconds: 3000Zuul: Host: ConnectTimeOutMillis: 10000
Tentu saja, Anda juga dapat mengatur parameter batas waktu pita untuk server pesanan secara terpisah: order-server.ribbon.xxxx = xxx. Untuk menunjukkan efek fallback di Zuul, saya mengatur batas waktu Hystrix sedikit lebih pendek di sini. Tentu saja, yang terbaik adalah tidak mengatur batas waktu default Hystrix menjadi lebih pendek dari batas waktu pita. Situasi ini telah diperingatkan tentang kita dalam kode sumber.
Kemudian kami menambahkan metode berikut di bawah server pesanan:
@GetMapping ("/sleep/{sleeptime}") public string sleep (@pathvariable long sleeptime) melempar interruptedException {timeunit.seconds.sleep (sleeptime); mengembalikan "kesuksesan"; }2. Metode Fallback Zuul
Kami dapat mengimplementasikan antarmuka ZuulfallBackProvider dan mengimplementasikan kode:
Paket com.hzgj.lyrk.springcloud.gateway.server.filter; import com.google.common.collect.immutablemap; import com.google.gson.gsonbuilder; Impor org.spramework.cloud.netflix.zuul.filters.filters.filters.filters.filters.filters.filters.filters.filters.filters.filters.filters.filters.filters.filters.filters.filters.filters.filters.filters.filters.filters.revally org.springframework.http.httpheaders; impor org.springframework.http.httpStatus; impor org.spramework.http.mediatypon org.spramewework.http.client.client.client.clientponseTPonFerFramewework.http.client.client.clientsponson; java.io.bytearrayInputStream; import java.io.ioException; import java.io.inputStream; import java.time.localdateTime; get oVa {@component class fallbackhandler yang mengimplementasikan zuulfallFrovider { @override @complic publicer) Pengaturan kembali "*"; } @Override Public ClientHttPresponse FallbackResponse () {return new clientHttpresponse () {@Override public httpstatus getStatusCode () melempar ioException {return httpstatus.oke; } @Override int int getRawstatusCode () melempar ioException {return 200; } @Override Public String getStAteStext () melempar ioException {return "ok"; } @Override public void close () {} @Override public inputStream getBody () melempar ioException {string result = new gsonBuilder (). Create (). TOJSON (immutableMap.of ("errorCode", 500, "Content", "Request Gagal", "Time", localdateTime. return bytearrayInputStream baru (result.getbytes ()); } @Override public httpheaders getHeaders () {httpheaders headers = new httpheaders (); headers.setContentType (mediatype.application_json); header kembali; }}; }}Saat ini, kami mengunjungi: http: // localhost: 8088/order-server/sleep/6 dan mendapatkan hasil berikut:
Ketika kami mengunjungi: http: // localhost: 8088/order-server/sleep/1, kami mendapatkan hasil berikut:
Meringkaskan
Di atas adalah seluruh konten artikel ini. Saya berharap konten artikel ini memiliki nilai referensi tertentu untuk studi atau pekerjaan semua orang. Jika Anda memiliki pertanyaan, Anda dapat meninggalkan pesan untuk berkomunikasi. Terima kasih atas dukungan Anda ke wulin.com.