Eureka adalah aplikasi tata kelola layanan terdesentralisasi, dan fitur khasnya adalah bahwa ia dapat didaftarkan dengan alamat yang Anda konfigurasi sebagai server dan layanan. Kemudian dalam artikel ini, mari kita bahas proses pendaftaran Eureka.
1. Server Eureka
Kelas inti sisi server Eureka adalah EurekabootStrap, yang mengimplementasikan pendengar ServletContextListener. Oleh karena itu, kita dapat menyimpulkan bahwa Eureka diimplementasikan berdasarkan wadah servlet. Kode kuncinya adalah sebagai berikut:
Kelas Publik EurekabootStrap mengimplementasikan servletContextListener {//...omit kode terkait/*** menginisialisasi eureka, termasuk menyinkronkan dengan peers Eureka lainnya dan menerbitkan registri. * * @see * javax.servlet.servletcontextListener#contextInitialized (javax.servlet.servletcontextEvent) */ @Override void public contextInitialized (event ServletContextEvent) {coba {initeureAnvironment (); initeureKaserverContext (); ServletContext sc = event.getSerVletContext (); sc.setAttribute (eurekaservercontext.class.getName (), serverContext); } catch (throwable e) {logger.error ("tidak dapat bootstrap server eureka:", e); Lempar RuntimeException baru ("Can't Bootstrap Eureka Server:", e); }} // hilangkan kode terkait ......}Kita dapat melihat bahwa ketika inisialisasi ServletContext selesai, lingkungan Eureka akan diinisialisasi, dan kemudian eureKaserverContext akan diinisialisasi. Kemudian kita melihat metode initeureKaserverContext:
/*** init hook untuk konteks server. Mengesampingkan logika khusus. */ Protected void initeureKaserverContext () melempar Exception {// ...... ApplicationInfomanager ApplicationInfomanager = null; if (eureKaClient == null) {eurekainstanceConfig instanceConfig = isCloud (configurationManager.getDeploymentContext ())? cloudInstanceconfig baru (): mydatacenterinstanceconfig baru baru; ApplicationInfomanager = ApplicationInfomanager baru (InstanceConfig, EureKaconfigbasedInstanceInfoProvider baru (InstanceConfig) .get ()); EureKaclientConfig eureKaClientConfig = baru defaulteureKaClientConfig (); eureKaclient = DiscoveryClient baru (ApplicationInfomanager, eureKaclientConfig); } else {applicationInfomanager = eureKaClient.getApplicationInfomanager (); } Registry PeerawareInstanceregistry; if (isaws (applicationInfomanager.getInfo ())) {registry = Awsinstanceregistry baru (eurekaserverconfig, eureKaclient.geteureKaclientConfig (), serverCodecs, eureKaclient); awsbinder = AwsbinderDelegate baru (eurekaserverconfig, eureKaclient.geteureKaclientConfig (), registri, applicationInfomanager); awsbinder.start (); } else {registry = peerawareINStanceregistryImpl baru (eurekaserverconfig, eureKaclient.geteureKaclientConfig (), servercodecs, eureKaclient); } //...Omit bagian kode}Dalam metode ini, banyak objek yang terkait dengan layanan Eureka akan dibuat. Di sini saya mencantumkan dua objek inti, yaitu eureKaclient dan peerawareinstanceregistry. Kami akan berbicara tentang bagian klien nanti. Mari kita lihat apa yang digunakan PeerawareINstanceregistry. Di sini saya menulis diagram kelas tentang kelas ini:
Menurut diagram kelas, kita dapat dengan jelas menemukan bahwa antarmuka tingkat atas dari PeerawareINstanceregistry adalah LeaseManager dan LookupService, di mana LookupService mendefinisikan perilaku contoh penemuan paling mendasar, sementara Leasemanager mendefinisikan pemrosesan operasi pendaftaran klien, ginjal, dan operasi pembatalan. Jadi dalam artikel ini, mari kita fokus pada implementasi antarmuka terkait Leasemanager. Melihat ke belakang, kami melihat peerawarinstanceregistry. Bahkan, kelas ini digunakan untuk menyalin informasi yang relevan di bawah beberapa node. Misalnya, jika sebuah simpul mendaftar untuk pembaruan dan offline, maka salinan yang relevan (pemberitahuan) akan disalin ke setiap node melalui kelas ini. Mari kita lihat bagaimana menangani pendaftaran klien:
/** * Mendaftarkan informasi tentang {@link instanceInfo} dan replika * Informasi ini untuk semua node eureka rekan. Jika ini adalah peristiwa replikasi * dari node replika lain maka tidak direplikasi. * * @param info * {@link instanceInfo} untuk didaftarkan dan direplikasi. * @param isreplication * Benar jika ini adalah peristiwa replikasi dari node replika lainnya, * false sebaliknya. */ @Override public void Register (Info Instance Final, Info Boolean Final) {int leasing = lease.default_duration_in_secs; if (info.getleaseInfo ()! = null && info.getleaseInfo (). getDurationInsecs ()> 0) {breasing = info.getleaseInfo (). getDurationInsecs (); } super.register (info, leasedurasi, isreplikasi); replicateTopeers (action.register, info.getappname (), info.getId (), info, null, isReplication); }Kita dapat melihat bahwa itu memanggil metode register dari kelas induk dan kemudian mereplikasi perilaku yang sesuai dengan node lain melalui replikatia. Replikasi spesifik tidak akan dibahas di sini. Mari kita fokus pada metode pendaftaran. Kami menemukan metode register () di kelas induk:
/*** Mendaftarkan contoh baru dengan durasi yang diberikan. * * @see com.netflix.eureka.lease.leaseManager#register (java.lang.object, int, boolean) */ Public void register (Registrasi InstanceInfo, Int Leaseduration, Boolean Isreplication) {try {read.lock (); Peta <string, sewa <sstanceInfo>> gmap = registry.get (registrant.getappname ()); Register.INCREMENT (ISReplikasi); if (gmap == null) {final concurrenthashmap <string, sewa <instanceInfo>> gnewMap = concurrenthashMap baru <string, sewa <sstanceInfo>> (); gmap = registry.putifabsent (pendaftar.getappname (), gnewmap); if (gmap == null) {gmap = gnewmap; }} Sewa <ScertingInfo> exageLease = gmap.get (Registrant.getId ()); // Pertahankan timestamp kotor terakhir tanpa menimpanya, jika sudah ada sewa jika (yang ada! = Null && (yang ada. Pendaftaran panjangLastDirtyTimestamp = terdaftar.getLastDirtImestamp (); Logger.debug ("Sewa yang ada ditemukan (ada = {}, disediakan = {}", yang ada diLastDirTimestamp, RegistrationLastDirtyTimestamp); // Ini adalah> bukannya> karena copeT -cambl. RegistrationLastDirtyTimestamp) {Logger.warn ("Ada sewa yang ada dan timestamp kotor sewa yang ada {} lebih besar" + "daripada yang terdaftar {}", Logger.warn yang ada, "Logger." pendaftaran "); pendaftaran = eksistlease.getHolder ();}} else {// sewa tidak ada dan karenanya itu adalah pendaftaran baru yang disinkronkan (kunci) {if (this.ExpectedNumberofRenewSpermin> 0) {// Sejak klien ingin membatalkannya, mengurangi threshrenewold // (1 // 0) {// karena klien ingin membatalkannya, mengurangi threshold // (1 // 0) {// karena klien ingin membatalkannya, mengurangi threshold // (1 // 0) {// karena klien ingin membatalkannya, mengurangi threshold // (1 // 0) {// karena klien ingin membatalkannya, mengurangi threshold // (1 // this.expectedNumberOfRenewSpermin = this.expectedNumberofrenewspermin + 2; Sewa <instance> sewa = sewa baru <instance> (pendaftar, sewa);Melalui kode sumber, mari kita selesaikan prosesnya secara singkat:
1) Pertama, dapatkan beberapa kolom objek instance layanan berdasarkan AppName. Jika nol, buat peta baru dan tambahkan informasi aplikasi terdaftar saat ini ke peta ini. Ada objek sewa di sini. Kelas ini menjelaskan atribut waktu T generik, seperti waktu pendaftaran, waktu startup layanan, waktu pembaruan akhir, dll. Anda dapat memperhatikan implementasinya:
/ * * Hak Cipta 2012 Netflix, Inc. * * 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 berdasarkan "sebagaimana adanya", * tanpa jaminan atau ketentuan apa pun, baik yang diungkapkan atau disiratkan. * Lihat lisensi untuk izin tata kelola bahasa tertentu dan * batasan di bawah lisensi. */paket com.netflix.eureka.lease; import com.netflix.eureka.registry.AbstractInstanceregistry;/*** menggambarkan ketersediaan berbasis waktu {@link t}. Tujuannya adalah untuk menghindari * akumulasi instance di {@link abstractInstanceregistry} sebagai hasil dari shutdown * tidak berturut -turut yang tidak biasa di lingkungan AWS. * * Jika sewa berlalu tanpa pembaruan, pada akhirnya akan berakhir terus menerus * menandai {@link t} yang terkait untuk penggusuran segera - ini mirip dengan * pembatalan eksplisit kecuali bahwa tidak ada komunikasi antara * {@link t} dan {@link LeaseManager}. * * @Author Karthik Ranganathan, Greg Kim */Sewa kelas publik <T> {enum action {register, batal, renew}; final public static int default_duration_in_secs = 90; pemegang T pribadi; Private Long EvictionTimestamp; pendaftaran panjang swasta; Layanan Panjang Pribadi; // Jadikan volatile sehingga tugas kedaluwarsa akan melihat private volatile lastpdatetimestamp yang lebih cepat volatile ini; durasi panjang pribadi; sewa publik (t r, durasi int) {holder = r; RegistrationTimestamp = System.CurrentTimeMillis (); lastupdatetimestamp = pendaftaranTimestamp; Durasi = (Durasi Durasi * 1000); } /** * Perbarui sewa, gunakan durasi pembaruan jika ditentukan oleh * terkait {@link t} selama pendaftaran, jika tidak durasi default adalah * {@link #default_duration_in_secs}. */ public void renew () {lastupDateTimestamp = system.currentTimeMillis () + durasi; } /*** membatalkan sewa dengan memperbarui waktu penggusuran. */ public void cancel () {if (evictionTimestamp <= 0) {eVictionTimestamp = system.currentTimeMillis (); }} /*** Tandai layanan sebagai UP. Ini hanya akan memengaruhi pertama kali dipanggil, * Panggilan selanjutnya akan diabaikan. ? }} /*** Atur Layanan Daun Up Timestamp. ? } /*** Memeriksa apakah sewa {@link com.netflix.appinfo.instanceInfo} yang diberikan telah kedaluwarsa atau tidak. */ public boolean isExired () {return isExired (0l); } /*** Memeriksa apakah sewa {@link com.netflix.appinfo.instanceInfo} yang diberikan telah kedaluwarsa atau tidak. * * Perhatikan bahwa karena perpanjangan () melakukan hal 'salah "dan mengatur lastupDatetimestamp ke +durasi lebih dari * apa yang seharusnya, kedaluwarsa sebenarnya akan menjadi 2 * durasi. Ini adalah bug kecil dan hanya mempengaruhi * instance yang tidak ada di depan. Boolean ISEXPIRED (Long SuteAseAsems) {return (eVictionTimestamp> 0 || system.currentTimeMillis ()> (lastupdatetimestamp + durasi + tambahan);} / ** * mendapatkan jutaan orang sejak Epoch ketika lease terdaftar. * * @Return the milionseconds sejak Epoch ketika sewa terdaftar. GetRistrationTimestamp () {ReturneTTimestamp; Lastupdatetimestamp; Sejak Epoch ketika layanan untuk sewa ditandai sebagai Up.2) Menurut ID yang terdaftar saat ini, jika Anda bisa mendapatkannya di peta, lakukan hal berikut:
2.1) Menurut waktu sentuh dari simpul yang ada saat ini dan waktu sentuh dari simpul yang terdaftar, jika waktu sebelumnya lebih lambat dari waktu yang terakhir, instance yang saat ini terdaftar harus tunduk pada instance yang ada.
2.2) Jika tidak, perbarui nomor pembaruan yang diharapkan per menit dan ambang batasnya
3) Simpan simpul pendaftaran saat ini ke dalam peta, dan proses pendaftaran kami pada dasarnya berakhir
2. Klien Eureka
Ketika server servletContext diinisialisasi, penemuan akan dibuat. Teman -teman yang akrab dengan Eureka harus terbiasa dengan dua atribut ini: Fetchregistry dan Registerwitheureka. Saat berjalan dalam mode independen Eureka terintegrasi di SpringCloud, jika kedua nilai ini tidak salah, maka startup akan melaporkan kesalahan. Mengapa melaporkan kesalahan? Faktanya, jawabannya terletak pada konstruktor Discoveryclient:
@Inject DiscoveryClient (ApplicationInfomanager ApplicationInfomanager, eureKaClientConfig, abstractDiscoveryclientOptionalArgs args, penyedia <Backupregistry> backupregistryprovider) {// ...omit bagian dari kode jika (! Config. Haruskah registerwitheeure () & {seharusnya! mendaftar atau permintaan untuk data. "); penjadwal = null; HeartbeatExecutor = null; Cacherefreshexecutor = null; eurekatransport = null; instanceReGionChecker = new InstanceReGionChecker (New PropertenBasezToregionMapper (config), clientConfig.getregion ()); // Ini adalah sedikit peretasan untuk memungkinkan kode yang ada menggunakan DiscoveryManager.getInstance () // untuk bekerja dengan DiscoveryClient DiscoveryManager.getInstance (). SetDiscoveryclient (ini); DiscoveryManager.getInstance (). SeteureKaclientConfig (config); inittimestampms = system.currentTimemillis (); Logger.info ("Discovery Client Diinisialisasi di Timestamp {} dengan Instance Instances Count: {}", initTimestampms, this.getApplications (). size ()); kembali; // Tidak perlu mengatur tugas jaringan dan kami selesai} coba {// ukuran default masing -masing 2 - 1 untuk detak jantung dan jadwal cacheerfresh = executors.newscheduledThreadpool (2, threadfactorybuilder () .setnamEformat ("DiscoveryClient-%D"). MetDaoning () .setNaMeFormat ("DiscoveryClient-%D") .setDaoning HeartbeatExecutor = new ThreadPoolExecutor (1, ClientConfig.GetHeartBeatExutorthReadPoolSize (), 0, Timeunit.seconds, Synchronousqueue Baru <Runnable> (), new threadfactoryBuilder () .setnameformat ("DiscoveryClient-heart-heart-heartoror (%de-leat-heartoor (" Discoveryclient-heartoor ("DiscoveryClient-heartoor (" Discoveryclient-heartor ("DiscoveryClient-heartoor (" Discoveryclient-heartor ("DiscoveryClient (" DiscoveryClient ("DiscoveryClient (" DiscoveryClient ("DiscoveryClient (" // Gunakan Handoff Direct Cacherefreshexecutor = New ThreadPoolExecutor (1, ClientConfig.GetCacherEFreshexecutorthReadPoolSize (), 0, TimeUnit.Seconds, Synchronousqueue baru (runnable> (), prinsip-cacheFactoryBuilder () .setnameFormat ("runnable> (), prinsip-cacheRECHEXORICTORYBUILDER () .SetnameFormat (" runnable> (), oFacFacToryBuilder (). Metnameformat ("runnable>. .membangun() ); // Gunakan handoff langsung eurekatransport = eurekatransport baru (); JadwalServerEndPointTask (eurekatransport, args); // ...omit beberapa kode initscheduledTasks (); // ....}Berdasarkan kode sumber, kita dapat menggambar kesimpulan berikut:
1) Jika keduanya harus registerwitheureka dan harus membuat registrasi salah, maka kembali secara langsung.
2) Buat kolam benang yang mengirimkan detak jantung dan menyegarkan cache
3) Inisialisasi tugas yang dibuat waktunya
Lalu mari kita lihat kode berikut dalam metode initscheduledTasks ():
// Heartbeat Timer Scheduler.schedule (New TimeDSuperVisortask ("Heartbeat", Penjadwal, HeartbeatExecutor, Pembaruan InvalIninsecs, TimeUnit.seconds, Expbackoffbound, New HeartBeatThread ()), pembaruan InInlervalinsecs, time.Berikut adalah utas yang memicu eksekusi waktu, dalam hitungan detik, dan mengeksekusi detak jantung pengiriman sesuai dengan nilai pembaruan intervalinsec. Benang detak jantung dieksekusi sebagai berikut:
/*** Tugas detak jantung yang memperbarui sewa dalam interval yang diberikan. */ Private Class HeartBeatThread mengimplementasikan runnable {public void run () {if (renew ()) {lastSuccessFulHeartBeatTimestamp = system.currentTimeMillis (); }}}Kita dapat melihat bahwa metode RUN sangat mudah untuk menjalankan metode Perpanjangan, dan jika waktunya berhasil direkam. Metode Perpanjangan:
/ ** * Perbarui dengan Layanan Eureka dengan melakukan panggilan istirahat yang sesuai */ Boolean Renew () {eurekahttpresponse <ScerteInfo> httpresponse; coba {httpresponse = eurekatransport.registrationclient.sendheartbeat (instanceInfo.getappname (), instanceInfo.getId (), instanceInfo, null); logger.debug ("{} - status detak jantung: {}", awalan + appPathIdentifier, httpresponse.getstatusCode ()); if (httpresponse.getStatusCode () == 404) {reregister_counter.increment (); logger.info ("{} - Mendaftar ulang Apps/{}", awalan + appPathIdentifier, instanceInfo.getAppname ()); long timestamp = instanceInfo.setisdirtywithtime (); Boolean Success = register (); if (sukses) {instanceInfo.unsetisdirty (timestamp); } mengembalikan kesuksesan; } return httpresponse.getstatusCode () == 200; } catch (Throwable e) {logger.error ("{} - tidak dapat mengirim detak jantung!", Awalan + AppPathIdentifier, e); mengembalikan false; }}Jika detak jantung dikirim ke sini, jika pengembaliannya adalah 404, operasi pendaftaran akan dilakukan. Perhatikan bahwa berdasarkan nilai pengembalian httpresponse, kami dapat menyimpulkan bahwa semua operasi ini didasarkan pada permintaan HTTP. Apakah itu benar? Mari kita terus lihat metode register:
/*** Daftarkan dengan layanan Eureka dengan melakukan panggilan istirahat yang sesuai. */ Boolean Register () melempar Throwable {Logger.info (awalan + AppPathIdentifier + ": Layanan Pendaftaran ..."); Eurekahttpresponse <void> httpresponse; coba {httpresponse = eurekatransport.registrationclient.register (instanceInfo); } catch (exception e) {logger.warn ("{} - registrasi gagal {}", awalan + appPathIdentifier, e.getMessage (), e); lempar e; } if (logger.isInfoEnabled ()) {logger.info ("{} - status pendaftaran: {}", awalan + appPathIdentifier, httpresponse.getstatusCode ()); } return httpresponse.getStatusCode () == 204; }Di sini, metode pendaftaran di eurekatransport disebut:
private static final class eurekatransport {private closableresolver bootstrapresolver; TransportClientFactory Private TransportClientFactory; Private EurekahttpClient RegistrationClient; Private EurekahttpClientFactory RegistrationClientFactory; Private EurekahttpClient QueryClient; private eurekahttpClientFactory queryclientfactory; void shutdown () {if (RegistrationClientFactory! = null) {RegistrationClientFactory.shutdown (); } if (queryclientfactory! = null) {queryclientfactory.shutdown (); } if (RegistrationClient! = null) {RegistrationClient.Shutdown (); } if (queryclient! = null) {queryclient.shutdown (); } if (TransportClientFactory! = NULL) {TransportClientFactory.Shutdown (); }}}Di sini kita dapat melihat bahwa klien Eureka menggunakan permintaan HTTP untuk mendaftarkan Layanan, yang berarti bahwa ketika kami membuat Discoveryclient, kami akan mendaftarkan instance dengan server.
3. Layanan Istirahat Disediakan oleh server
Kami telah melihat kode yang disediakan oleh server untuk menangani permintaan pendaftaran klien. Karena klien mendaftar melalui protokol HTTP, server harus memiliki alamat untuk menangani permintaan HTTP ini. Bahkan, server Eureka menggunakan standar JAX-RS untuk menyediakan metode REST untuk mengekspos layanan. Kita dapat melihat metode addInstance dari ApplicationResource ini:
/** * Mendaftarkan informasi tentang contoh tertentu untuk * {@link com.netflix.discovery.shared.application}. * * @param info * {@link instanceInfo} informasi dari instance. * @param isreplication * Parameter header yang berisi informasi apakah ini * direplikasi dari node lain. */@Post @consumes ({"application/json", "application/xml"}) respons publik addInstance (instanceInfo info, @headerparam (peereurekanode.header_replication) string iSreplication) {logger.debug ("mendaftarkan contoh {{{{{{{{{{{{{{{{{{{{{{{). // Validasi bahwa instanceInfo berisi semua bidang yang diperlukan jika (isBlank (info.getId ())) {return response.status (400) .Entity ("Missing InstanceId"). Build (); } lain if (isBlank (info.getHostName ())) {return response.status (400) .Entity ("Hostname yang hilang"). Build (); } else if (isBlank (info.getipaddr ())) {return response.status (400) .Entity ("Alamat IP Hilang"). Build (); } else if (isBlank (info.getappname ()))) {return response.status (400) .Entity ("AppName yang hilang"). Build (); } else if (! appname.equals (info.getappname ()))) {return response.status (400) .Entity ("AppName yang tidak cocok, mengharapkan" + appname + "tetapi" + info.getappname ()). Build (); } else if (info.getDataCenterInfo () == null) {return response.status (400) .Entity ("Missing DataCenterInfo"). Build (); } else if (info.getDataCenterInfo (). getName () == null) {return response.status (400) .Entity ("Missing DataCenterInfo"). Build (); } else if (info.getDataCenterInfo (). getName () == null) {return response.status (400) .Entity ("NOMES DATACENTERINFO NAMA"). Build (); } // menangani kasus di mana klien dapat mendaftar dengan bad DataCenterInfo dengan data yang hilang dataCenterInfo datacenterInfo = info.getDataCenterInfo (); if (DataCenterInfo instance dari unikimenifier) {string DataCenterInfoid = ((UniqueIdentifier) DataCenterInfo) .getId (); if (isBlank (datacenterInfoid)) {boolean eksperimental = "true" .equalsignorecase (serverconfig.getExperimental ("registrasi.validation.datacenterInfoid")); if (eksperimental) {string entitas = "DataCenterInfo dari tipe" + DataCenterInfo.getClass () + "harus berisi ID yang valid"; return response.status (400) .Enentity (entitas) .build (); } else if (DataCenterInfo instance dari amazonInfo) {amazoninfo amazoninfo = (amazoninfo) datacenterinfo; String efektif = amazonInfo.get (amazoninfo.metadatakey.instanceid); if (efektif == null) {amazoninfo.getMetadata (). put (amazoninfo.metadatakey.instanceid.getname (), info.getid ()); }} else {logger.warn ("Mendaftarkan DataCenterInfo Type {} tanpa ID yang sesuai", DataCenterInfo.getClass ()); }}}} registry.register (info, "true" .Equals (isReplication)); return response.status (204) .build (); // 204 untuk mundur kompatibel}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.