Pemandangan
Pengoperasian penerbitan layanan microser biasanya untuk mengetik paket kode baru, membunuhnya menjatuhkan aplikasi yang sedang berjalan, mengganti paket baru, dan memulainya.
Eureka digunakan sebagai Pusat Registri di Spring Cloud, yang memungkinkan keterlambatan dalam data daftar layanan, yaitu, bahkan jika aplikasi tidak lagi dalam daftar layanan, klien masih akan meminta alamat ini untuk jangka waktu tertentu. Maka permintaan akan muncul, mengakibatkan kegagalan.
Kami akan mengoptimalkan waktu penyegaran daftar layanan untuk meningkatkan ketepatan waktu dari informasi daftar layanan. Tapi apa pun yang terjadi, tidak mungkin untuk menghindari periode waktu ketika data tidak konsisten.
Jadi salah satu cara yang kita pikirkan adalah mencoba lagi mekanisme. Ketika mesin A dimulai kembali, B di kluster yang sama dapat menyediakan layanan secara normal. Jika ada mekanisme coba lagi, Anda dapat mencoba kembali B dalam skenario di atas tanpa mempengaruhi respons yang benar.
beroperasi
Operasi berikut diperlukan:
Pita: ReadTimeout: 10000 ConnectTimeout: 10000 MaxAutoretries: 0 MaxAutoretriesNextServer: 1 OktoretryTetryOlall Operasi: Salah
Memperkenalkan Paket Retret Pegas
<dependency> <GroupId> org.springframework.retry </groupid> <ArTifactId> Spring-retry </artifactid> </dependency>
Mengambil Zuul sebagai contoh, Anda juga perlu mengkonfigurasi dan mengaktifkan coba lagi:
zuul.retryable = true
Telah mengalami masalah
Namun, semuanya tidak begitu mulus. Mekanisme coba lagi tes mulai berlaku, tetapi tidak meminta mesin sehat lain seperti yang saya bayangkan. Jadi saya terpaksa pergi ke kode sumber terbuka dan akhirnya menemukan bahwa itu adalah bug kode sumber, tetapi telah diperbaiki dan versinya ditingkatkan.
Analisis Kode
Versi yang digunakan adalah
Spring-cloud-netflix-core: 1.3.6.release
Spring-retry: 1.2.1.release
Versi Ketergantungan Spring Cloud:
<DependencyManagement> <dependencies> <dependency> <GroupId> org.springframework.cloud </groupid> <ArTifactId> Dependensi awan-musim semi </artifactid> <version> $ {spring-cloud.version} </version> <peed> POM </type> <cuppor> <cuppore> </scope/scope/scopees> </version> </tipe </type> </type> </tipe> </scope> </tipe </tipe </type> </tipe> </scope>Karena RETRY diaktifkan, metode RetryableribbonLoadAnceHttpClient.Execute dieksekusi saat meminta aplikasi:
Public RibbonAPacheHTTPResponse Execute (Final RibbonApacheHtTpRequest Request, final iclientConfig configOverride) melempar Exception {final requestConfig.builder builder = requestConfig.custom (); IclientConfig config = configOverride! = Null? configOverride: this.config; builder.setConnecttimeout (config.get (commonclientConfigKey.connecttimeout, this.connecttimeout)); builder.setsocketTimeout (config.get (commonClientConfigKey.readtimeout, this.readtimeout)); builder.setredirectsenabled (config.get (CommonClientConfigKey.FollowRedirects, this.followredirects)); final requestConfig requestConfig = builder.build (); final loadbalayretrypolicy retrypolicy = loadBalAndetryPolicyFactory.create (this.getClientName (), this); RetryCallback RetryCallback = RetryCallback baru () {@Override Public RibbonApacheHTTPResponse Dowithretry (RetryContext Context) Melempar Exception {// On Retries Policy akan memilih server dan mengaturnya dalam konteks // Ekstrak server dan perbarui permintaan yang dibuat RibbonapacheHttttttttttttttttttttteTest if (context dari loadBalAdeTRetRyContext) {serviceInstance service = ((loadBalAdingRetRyContext) konteks) .getServiceInstance (); if (service! = null) {// merekonstruksi permintaan URI menggunakan host dan port yang ditetapkan dalam konteks ulang newRequest = newRequest.withnewuri (URI new (service.geturi (). getscheme (), newrequest.geturi (). getUserinfo (), service.gethost (), get () (), getUserInfo (), service.gethost (),). newRequest.geturi (). getQuery (), newRequest.geturi (). getFragment ())); }} newRequest = getSeCureRequest (request, configOverride); Httpurirequest httpurirequest = newRequest.torequest (requestConfig); httpresponse final httpresponse = retryableribbonloadAnkAncingHttpclient.this.delegate.execute (httpurirequest); if (retrypolicy.retryableStatusCode (httpresponse.getstatusLine (). getStatusCode ())) {if (closeAdleHttpresponse.class.isInstance (httpresponse)) {(closeAdleHtPrespresponse) httponse)) {((closeAdleHtPresponsponse) httponse ((httponsponse))))))) {(((httponsponse)) } lempar retryableStatusCodeException baru (retryableribbonloadAnkAnceDhttpclient.tipis.clientname, httpresponse.getstatusLine (). getStatusCode ()); } return baru RibbonApacheHttPresponse (httpresponse, httpurirequest.geturi ()); }}; kembalikan ini. }Kami menemukan bahwa kami pertama kali retrycallback, dan kemudian menjalankan ini.
Kami dengan jelas melihat bahwa kode retrycallback.dowithretry adalah kode yang sebenarnya, yang berarti bahwa metode ini.
Dilindungi <T, e memperluas Throwable> t doExecute (retrycallback <t, e> retrycallback, recoverycallback <T> RecoveryCallback, retrystate state) melempar E, lountedRyException {retrypolicy retrypolicy = this.retrypolicy; Backoffpolicy backOffpolicy = this.backoffpolicy; // Biarkan kebijakan coba lagi untuk menginisialisasi dirinya sendiri ... retrycontext konteks = terbuka (retrypolicy, state); if (this.logger.istraceEnabled ()) {this.logger.trace ("RetryContext diambil:" + konteks); } // Pastikan konteksnya tersedia secara global untuk klien yang membutuhkan // itu ... retrysynchronizationManager.register (konteks); Lowangan LastException = NULL; boolean kelelahan = false; Coba {// Beri klien kesempatan untuk meningkatkan konteks ... boolean running = doopeninterceptors (retrycallback, konteks); if (! running) {lempar New TerminatedRyException ("Coba lagi diakhiri secara tidak normal oleh Interceptor sebelum upaya pertama"); } // Dapatkan atau mulai konteks backoff ... backoffcontext backoffcontext = null; Object Resource = Context.GetAttribute ("BackOffContext"); if (sumber daya instance dari backoffcontext) {backoffcontext = (backOffcontext) sumber daya; } if (backOffcontext == null) {backoffcontext = backoffpolicy.start (konteks); if (backoffcontext! = null) {context.setAttribute ("backoffcontext", backOffcontext); }} / * * Kami mengizinkan seluruh loop untuk dilewati jika kebijakan atau konteks sudah * melarang percobaan pertama. Ini digunakan dalam kasus Retry Eksternal untuk memungkinkan pemulihan * Handleretry -Exhip tanpa pemrosesan callback (yang * akan melempar pengecualian). */ while (canretry (retrypolicy, context) &&! context.isexuetedOnly ()) {coba {if (this.logger.isdebugeNabled ()) {this.logger.debug ("Retry: count =" + context.getRetryCount ()); } // Setel ulang pengecualian terakhir, jadi jika kita berhasil // Pencegat Tutup tidak akan berpikir kita gagal ... LastException = NULL; return retrycallback.dowithretry (konteks); } catch (Throwable e) {LastException = E; coba {registerthrowable (retrypolicy, state, context, e); } Catch (Exception Ex) {Throw New TerminatedRyException ("tidak dapat mendaftar Throwable", ex); } akhirnya {doonerrorinterceptors (retrycallback, konteks, e); } if (canretry (retrypolicy, context) &&! context.isexuetedOnly ()) {coba {backoffpolicy.backoff (backoffcontext); } catch (backoffinterruptedException ex) {lastException = e; // Back Off dicegah oleh utas lain - Gagal coba lagi jika (this.logger.isdebugeNabled ()) {this.logger .debug ("Abort coba lagi karena terputus: count =" + context.getRetryCount ()); } lempar ex; }} if (this.logger.isdebugeNabled ()) {this.logger.debug ("Memeriksa rethrow: count =" + context.getRetryCount ()); } if (harus rethrow (retrypolicy, context, state)) {if (this.logger.isdebugeNabled ()) {this.logger.debug ("rethrow dalam coba lagi kebijakan: count =" + context.getRetryCount ()); } lempar retrytemplate. <e> wrapifnecary (e); }} / * * Upaya yang stateful yang dapat mencoba lagi dapat mensbsokkan kembali pengecualian sebelum sekarang, * tetapi jika kita mendapatkan sejauh ini dalam pemueran stateful ada alasan untuk itu, * seperti pemutus sirkuit atau pengklasifikasi rollback. */ if (state! = null && context.hasattribute (global_state)) {break; }} if (state == null && this.logger.isdebugeNabled ()) {this.logger.debug ("Coba lagi upaya terakhir yang gagal: count =" + context.getRetRryCount ()); } kelelahan = true; return handleretry extronted (pemulihan callback, konteks, status); } catch (throwable e) {throw retrytemplate. <e> wrapifnecessary (e); } akhirnya {tutup (retrypolicy, konteks, status, lastException == null || kelelahan); DocloseInterceptors (RetryCallback, Context, LastException); RetrysynchronizationManager.clear (); }}Menerapkan mekanisme coba lagi dalam beberapa saat loop. Ketika pengecualian terjadi ketika retrycallback.dowithretry (konteks) dieksekusi, itu akan menangkap pengecualian. Kemudian gunakan retrypolicy untuk menentukan apakah akan mencoba lagi. Memberi perhatian khusus untuk mendaftar (retrypolicy, state, context, e); metode. Tidak hanya menentukan apakah akan mencoba lagi, tetapi dalam kasus coba lagi, mesin baru akan dipilih dan dimasukkan ke dalam konteks, dan kemudian akan dibawa ketika retrycallback.dowithretry (konteks) dieksekusi, sehingga pengubah akan dicoba lagi.
Tapi mengapa konfigurasi saya tidak mengubah telepon? Kode debugging menemukan bahwa registerthrowable (retrypolicy, state, context, e); Mesin yang dipilih baik -baik saja, itu adalah mesin baru dan sehat, tetapi ketika menjalankan kode RetryCallback.dowithretry (konteks), itu masih diminta.
Jadi mari kita lihat lebih dekat Kode RetryCallback.Dowithretry (konteks):
Kami menemukan baris kode ini:
newRequest = getSeCureRequest (permintaan, configoverride); RibbonAPacheHttPRequest GetSecureRequest (RibbonAPacheHttpRequest Request, iclientConfig configoverride) {if (isSecure (configOverride)) {final uri Secureuri = Uricomponentsbuilder.fromuri (request.geturi ()) .scheme ("bttps"). return revand.withnewuri (Secureuri); } permintaan pengembalian; }NewRequest telah dibangun menggunakan konteks dalam contoh sebelumnya. Permintaan adalah data yang diminta terakhir kali. Selama Anda menjalankan kode ini, Anda akan menemukan bahwa newRequest akan selalu ditimpa oleh permintaan tersebut. Ketika kami melihat ini, kami menemukan bahwa itu adalah bug kode sumber.
Alamat masalah: https://github.com/spring-cloud/spring-cloud-netflix/issues/2667
Meringkaskan
Ini adalah proses pemeriksaan yang sangat biasa. Selama proses ini, ketika saya menemukan bahwa konfigurasi tidak memenuhi harapan saya, saya pertama -tama memeriksa arti konfigurasi dan mencobanya berkali -kali tanpa hasil. Jadi setelah men -debug breakpoint, saya menemukan bahwa breakpoint itu tidak normal. Karena adegan itu membutuhkan satu mesin untuk menjadi sehat dan satu mesin untuk offline, saya mensimulasikannya ratusan kali sebelum akhirnya saya menemukan baris kode ini. Bahkan proyek open source adalah proyek yang sangat baik, dan pasti akan memiliki bug, bukan takhayul atau buta. Di sisi lain, kemampuan membaca kode sumber juga merupakan kemampuan penting untuk menyelesaikan masalah. Misalnya, saya mencari pintu masuk kode sumber, dan butuh banyak waktu untuk menemukan kode.
Di atas adalah semua konten artikel ini. Saya berharap ini akan membantu untuk pembelajaran semua orang dan saya harap semua orang akan lebih mendukung wulin.com.