Eureka هو تطبيق حوكمة الخدمة اللامركزية ، وميزةها المميزة هي أنه يمكن تسجيله في العنوان الذي قمت بتكوينه كخادم وخدمة. ثم في هذه المقالة ، دعنا نناقش عملية تسجيل Eureka.
1. خادم يوريكا
الفئة الأساسية لجانب خادم Eureka هي EurekabootStrap ، والتي تنفذ مستمع ServletContextListener. لذلك ، يمكننا أن نستنتج أن Eureka يتم تنفيذها على أساس حاويات servlet. رمز المفتاح كما يلي:
تنفذ الطبقة العامة eurekabootstrap servletContextRistener {//...omit الكود المرتبط/*** تهيئة Eureka ، بما في ذلك مزامنة مع أقران Eureka الآخرين ونشر السجل. * * see * javax.servlet.servletcontextlistener#contextInitialized (javax.servlet.servletcontextevent) */ Override public void contextinitialized (servletcontextevent حدث) {try {initeurekaenvrienment () ؛ initeurekaserverContext () ؛ servletContext sc = event.getServletContext () ؛ sc.setAttribute (eurekaservercontext.class.getName () ، serverContext) ؛ } catch (throwable e) {logger.error ("لا يمكن bootstrap eureka server:" ، e) ؛ رمي new RunTimeException ("لا يمكن bootstrap eureka server:" ، e) ؛ }} // إغفال الكود ذي الصلة ......}يمكننا أن نرى أنه عند اكتمال تهيئة ServletContext ، سيتم تهيئة بيئة Eureka ، ثم سيتم تهيئة EureKaserverContext. ثم نلقي نظرة على طريقة initeurekaservercontext:
/*** خطاف init لسياق الخادم. تجاوز المنطق المخصص. */ void void initeurekaservercontext () يلقي الاستثناء {// ...... ApplicationInfomanager ApplicationInfomanager = null ؛ if (eurekaclient == null) {eurekainstanceconfig instanceconfig = isCloud (configurationManager.getDeploymentContext ())؟ New CloudInstanceConfig (): new MyDatacenterInstanceConfig () ؛ ApplicationInfomanager = جديد ApplicationInfomanager (Instanceconfig ، New EurekaconFigBasedInstanceInfoprovider (instanceconfig) .get ()) ؛ eurekaclientconfig eurekaclientConfig = جديد defaulteurekaclientConfig () ؛ eurekaclient = new DiscoveryClient (ApplicationInfomanager ، eurekaclientConfig) ؛ } آخر {applicationInfomanager = eurekaclient.getApplicationInfomanager () ؛ } سجل peerawareinstanceregistry ؛ if (isaws (applicationInfomanager.getInfo ())) {registry = new AwsInstancereGistry (eurekaserverConfig ، eurekaclient.geteurekaclientConfig () ، servercodecs ، eurekaclient) ؛ AWSBINDER = جديد AWSBINDERDELEGATE (eurekaserverConfig ، eurekaclient.geteurekaclientconfig () ، التسجيل ، applicationInfomanager) ؛ awsbinder.start () ؛ } else {registry = new PeeraWareInstanCeregistryImpl (eurekaserverConfig ، eurekaclient.geteurekaclientconfig () ، servercodecs ، eurekaclient) ؛ } //...omit جزء من الكود}في هذه الطريقة ، سيتم إنشاء العديد من الكائنات المتعلقة بخدمة Eureka. هنا أدرج كائنين أساسيين ، وهما eurekaclient و peerawareinstanceregistry. سنتحدث عن جزء العميل لاحقًا. دعنا نلقي نظرة على ما تستخدمه Peerawareinstanceregistry. هنا أكتب مخططًا دراسيًا حول هذا الفصل:
وفقًا لمخطط الفصل الدراسي ، يمكننا أن نجد بوضوح أن الواجهة العليا من peerawareinstanceregistry هي Leasemanager و LookupService ، حيث تحدد LookupService سلوك مثال الاكتشاف الأساسي ، بينما يحدد Leasemanager معالجة تسجيلات العميل ، والتجديد ، وعمليات الإلغاء. لذلك في هذه المقالة ، دعونا نركز على تنفيذ الواجهات ذات الصلة لـ Leasemanager. إذا نظرنا إلى الوراء ، فإننا ننظر إلى peerawareinstanceregistry. في الواقع ، يتم استخدام هذه الفئة لنسخ المعلومات ذات الصلة تحت عقد متعددة. على سبيل المثال ، إذا كانت عقدة تسجل للتجديد وغير متصل ، فسيتم نسخ النسخة ذات الصلة (الإخطار) إلى كل عقدة من خلال هذه الفئة. دعونا نرى كيف تتعامل مع تسجيل العميل:
/** * يسجل المعلومات حول {link evalueinfo} و replicas * هذه المعلومات لجميع عقد الأقران eureka. إذا كان هذا هو حدث النسخ المتماثل * من العقد النسخ المتماثلة الأخرى ، فلن يتم تكراره. * * @param info * {link evalueinfo} ليتم تسجيله وتكراره. * param isreplication * صحيح إذا كان هذا حدثًا نسخًا من العقد النسخ المتماثلة الأخرى ، * خطأ خلاف ذلك. */ Override Public Void Register (Final extuleinfo info ، isreplication boolean النهائي) {inSeduration = reease.default_duration_in_secs ؛ if (info.getLeaseInfo ()! = null && info.getLeaseInfo (). } super.register (info ، reaseduration ، isreplication) ؛ REPLICATEPERES (Action.register ، info.getAppName () ، info.getID () ، info ، null ، isReplication) ؛ }يمكننا أن نرى أنه يستدعي طريقة التسجيل في الفئة الأصل ثم يكرر السلوك المقابل للعقد الأخرى من خلال أجهزة النسخ المتماثل. لن تتم مناقشة النسخ المتماثل المحدد هنا. دعونا نركز على طريقة التسجيل. نجد طريقة التسجيل () في الفصل الأصل:
/*** يسجل مثيلًا جديدًا بمدة معينة. * * seee com.netflix.eureka.lease.leasemanager#register (java.lang.object ، int ، boolean) */ سجل الفراغ العام (extalInfo تسجيل ، inteduration ، iSreplication boolean) {try {read.lock () ؛ MAP <string ، reegy <aluteinfo >> gmap = registry.get (registrant.getAppName ()) ؛ register.increment (isreplication) ؛ if (gmap == null) {final concurrenthashMap <string ، reege <almateinfo >> gnewmap = new concurrenthashMap <string ، reege <extalInfo >> () ؛ gmap = registry.putifabsent (registrant.getAppName () ، gnewmap) ؛ if (gmap == null) {gmap = gnewmap ؛ }} الإيجار <eCHANTIONINFO> NERRATIONLEASE = gmap.get (registrant.getID ()) ؛ // احتفظ بآخر طابع زمني قذر دون الكتابة فوقه ، إذا كان هناك بالفعل عقد إيجار إذا (nullease! = null && (fructionLease.getholder ()! = null)) {long nurnlastdirtytimestamp = nerentlease.getholder (). getlastdirtytimestamp () ؛ long registrationLastDirtyTimestamp = register.getLastDirtyTimestamp () ؛ logger.debug ("تم العثور على عقد إيجار موجود (موجود = {{} ، متوفر = {}" ، fructionLastDirtyTimestamp ، registrationLastDirtyTimestamp) ؛ registrationlastdirtytimestamp) {logger.warn (هناك عقد إيجار موجود وجهاز عقد إيجار موجود أكبر " +" من المسجل {} " expresslease.getholder () ؛ this.exedNumberofrenewspermin + 2 ؛ الإيجار <extrealinfo> readisturation) ؛من خلال رمز المصدر ، دعونا نفرز العملية بإيجاز:
1) أولاً ، احصل على بعض أعمدة كائنات مثيل الخدمة بناءً على AppName. إذا كان NULL ، فقم بإنشاء خريطة جديدة وأضف معلومات التطبيق المسجلة الحالية إلى هذه الخريطة. هناك كائن الإيجار هنا. يصف هذه الفئة سمات الوقت لـ T ، مثل وقت التسجيل ، ووقت بدء الخدمة ، ووقت التحديث النهائي ، وما إلى ذلك ، يمكنك الانتباه إلى تنفيذها:
/ * * حقوق الطبع والنشر 2012 Netflix ، Inc. * * مرخصة بموجب ترخيص Apache ، الإصدار 2.0 ("الترخيص") ؛ * لا يجوز لك استخدام هذا الملف إلا في الامتثال للترخيص. * يمكنك الحصول على نسخة من الترخيص على * * http://www.apache.org/licenses/license-2.0 * * ما لم يكن مطلوبًا بموجب القانون المعمول به أو يتم الاتفاق عليه في الكتابة ، يتم توزيع البرمجيات * الموزعة بموجب الترخيص على أساس "كما هو" ، * دون ضمانات أو شروط من أي نوع ، إما صريحة أو ضمنية. * راجع ترخيص أذونات حوكمة اللغة المحددة والقيود * بموجب الترخيص. */package com.netflix.eureka.lease ؛ import com.netflix.eureka.registry.abstractinstanceregistry ؛/*** يصف توفر قائم على الوقت لـ {link t}. الغرض هو تجنب * تراكم الحالات في {link AbstractInstanceregistry} نتيجة لإغلاق * غير المألوف غير المألوف في بيئات AWS. * * إذا انقضت عقد الإيجار دون تجديد ، فسيتم انتهاء صلاحيته في النهاية بشكل مستمر * وضع علامة {link t} المرتبطة به - وهذا يشبه * إلغاء صريح باستثناء أنه لا يوجد اتصال بين * {link t} و {link leasemanager}. * * Author Karthik Ranganathan ، Greg Kim */Public Class Lease <T> {enum Action {Record ، Cancel ، Reneft} ؛ الثابت العام النهائي int default_duration_in_secs = 90 ؛ حامل T الخاص خاص طويل exectimestamp ؛ التسجيل الطويل الخاص ؛ خدمة خاصة طويلة serviceuptimestamp // اجعلها متقلبة بحيث ترى مهمة انتهاء الصلاحية هذه الأسرع المتقلبة الخاصة الطويلة LastupDateTimestamp ؛ مدة طويلة خاصة ؛ عقد الإيجار العام (T R ، int maturitinsecs) {Holder = r ؛ registrationTimestamp = system.currentTimeMillis () ؛ LastUpDateTimEstamp = registrationTimestamp ؛ المدة = (murteeNsecs * 1000) ؛ } /** * تجديد عقد الإيجار ، استخدم مدة التجديد إذا تم تحديده بواسطة * Associated {link t} أثناء التسجيل ، وإلا فإن المدة الافتراضية هي * {link #default_duration_in_secs}. */ public void review () {lastupDateTimEstamp = System.CurrentTimeMillis () + matter ؛ } /*** يلغي عقد الإيجار عن طريق تحديث وقت الإخلاء. */ public void cancel () {if (extictTimeStamp <= 0) {extictTimestamp = system.currentTimeMillis () ؛ }} /*** حدد الخدمة كما يصل. سيؤثر هذا فقط على التأثير على المرة الأولى التي تسمى ، * سيتم تجاهل المكالمات اللاحقة. */ public void serviceup () {if (serviceuptimestamp == 0) {serviceuptimestamp = system.currentTimeMillis () ؛ }} /*** اضبط خدمة الأوراق على الطابع الزمني. */ public void setServiceuptImestamp (long serviceuptimestamp) {this.ServiceUptImestamp = serviceuptimestamp ؛ } /*** يتحقق مما إذا كان عقد الإيجار من {link com.netflix.appinfo.instanceinfo} قد انتهى أم لا. */ public boolean isexpired () {return isExpired (0l) ؛ } /*** يتحقق مما إذا كان عقد الإيجار من {link com.netflix.appinfo.instanceinfo} قد انتهى أم لا. * * لاحظ أنه نظرًا للتجديد () القيام بالأمر الخاطئ "وتحديد LastUpDateTimestamp إلى +مدة أكثر من * ما ينبغي أن يكون ، فإن انتهاء الصلاحية سيكون فعليًا 2 * المدة. هذا خطأ بسيط ويجب أن يؤثر فقط على * الحالات التي لا يمكن إيقافها بشكل مريح. Boolean isExpired (addaleaseMs) {return (exiTimeStamp> 0 || system.currentTimeMillis ()> (lastupdatetimestamp + المدة + الإضافات addalleasems) GetRegistrationTimestamp () {Return TrishingTimestamp ؛ } /** يحصل على الملايين منذ أن تم إخلاء عقد الإيجار. تم وضع علامة على ما يصل.2) وفقًا للمعرف المسجل حاليًا ، إذا كنت تستطيع الحصول عليه في الخريطة ، قم بما يلي:
2.1) وفقًا لوقت اللمس للعقدة الموجودة حاليًا ووقت اللمس للعقدة المسجلة ، إذا كان الوقت السابق متأخراً عن الوقت الأخير ، فإن المثيل المسجل حاليًا يخضع للمثيل الحالي.
2.2) خلاف ذلك ، قم بتحديث رقم التجديد المتوقع في الدقيقة وعتبةه
3) احفظ عقدة التسجيل الحالية في الخريطة ، وقد انتهت عملية التسجيل الخاصة بنا بشكل أساسي
2. عميل يوريكا
عند تهيئة Server ServletContext ، سيتم إنشاء DiscoveryClient. يجب أن يكون الأصدقاء الذين هم على دراية بـ Eureka على دراية بهاتين الأماميين: Fetchregistry و Recordwitheureka. عند التشغيل في وضع Eureka المستقل المتكامل في SpringCloud ، إذا لم تكن هاتان القيمتان غير صحيحين ، فسيقوم بدء التشغيل بالإبلاغ عن خطأ. لماذا يبلغ عن خطأ؟ في الواقع ، تكمن الإجابة في مُنشئ DiscoveryClient:
inject DiscoveryClient (ApplicationInfomanager applicationInfomanager ، eurekaclientConfig config ، agrectedDiscoveryClientOptionAlargs args ، Provider <Spackupregistry> backupregistryprovider) {//..... لا التسجيل ولا الاستعلام للبيانات. ") ؛ جدولة = فارغة ؛ Heartbeatexecutor = فارغة ؛ cacherefreshexecutor = null ؛ eurekatransport = null ؛ instanceregionchecker = جديد instancereChecker (propertyBasedazToregionMapper (config) ، clientConfig.getRegion ()) ؛ // هذا هو جزء من الاختراق للسماح للرمز الموجود باستخدام discoverymanager.getInstance () // للعمل مع discoveryclient discoverymanager.getInstance (). setDiscoveryClient (هذا) ؛ DiscoveryManager.getInstance (). seteurekaclientConfig (config) ؛ ittimestampms = system.currentTimeMillis () ؛ logger.info ("Discovery Client تم تهيئة في Timestamp {} مع العد الحالات الأولية: {}" ، ittimestampms ، this.getapplications (). size ()) ؛ يعود؛ // لا حاجة لإعداد مهام الشبكة ، وقد انتهينا} حاول {// الحجم الافتراضي من 2 - 1 لكل من نبضات القلب و cacherefresh Scheduler = evectorors.NewScheduledThreadPool (2 ، new threadfactorybuilder () .setNameFormat ("discoveryclient- ٪ d") .setdaemon (). HeartBeatExecutor = جديد ThreadPoolexecutor (1 ، ClientConfig.geTheartBeatexecutorTorDpoolsize () ، 0 ، timeUnit.Seconds ، synchronousqueue <runnable> () new threadfactorybuilder () .setNameFormat ( ) ؛ // استخدم Handoff Direct Cacherefreshexecutor = new threadpoolexecutor (1 ، clientConfig.getCachereSexExecutorthReadPoolSize () .setdaemon (صواب) .build ()) ؛ // استخدم Handoff Direct eurekatransport = new eurekatransport () ؛ ScheduleserverEndPointTask (eurekatransport ، args) ؛ //..... بعض الكود initscheduledtasks () ؛ // ....}بناءً على رمز المصدر ، يمكننا استخلاص الاستنتاجات التالية:
1) إذا كان من الممكن أن يكون كلاهما registerwitheureka ويجب أن يكون الأمر كاذبًا ، فعليك العودة مباشرة.
2) قم بإنشاء تجمع مؤشر ترابط يرسل دقات القلب وينعش ذاكرة التخزين المؤقت
3) تهيئة المهام الموقوتة التي تم إنشاؤها
ثم دعونا نلقي نظرة على الكود التالي في طريقة initscheduledtasks ():
// Heartbeat Timer Scheduler.Schedule (TimedSuperVisortask جديد ("Heartbeat" ، جدولة ، Heartbeatexecutor ، Renewalintervalinsecs ، TimeUnit.Seconds ، Presbackoffbound ، New HeartThread ()) ، Renewalintervalinsecs ، timeunit.seconds) ؛فيما يلي موضوع يؤدي إلى تنفيذ موقوت ، في ثوانٍ ، وينفذ نبضات القلب وفقًا لقيمة RenewalIntervalinsecs. ينفذ موضوع نبضات القلب على النحو التالي:
/*** مهمة نبضات القلب التي تجدد عقد الإيجار في الفواصل الزمنية المحددة. */ Private Class HeartbeatthRead تنفذ Runnable {public void run () {if (REAND ()) {lastSuccessfulHeartBeatTimestamp = System.CurrentTimeMillis () ؛ }}}يمكننا أن نرى أن طريقة التشغيل بسيطة للغاية لتنفيذ طريقة التجديد ، وإذا تم تسجيل الوقت بنجاح. طريقة التجديد:
/ ** * تجديد مع خدمة Eureka من خلال إجراء مكالمة REST المناسبة */ Boolean Renense () {eurekahttpresponse <exturefo> httpresponse ؛ جرب {httpresponse = eurekatransport.registrationClient.SendHeartBeat (extueInfo.getAppName () ، extalInfo.getId () ، extailInfo ، null) ؛ logger.debug ("{} - حالة نبضات: {}" ، بادئة + apppathidentifier ، httpresponse.getStatusCode ()) ؛ if (httpresponse.getStatusCode () == 404) {reregister_counter.increment () ؛ logger.info ("{} - إعادة تسجيل التطبيقات/{}" ، بادئة + apppathidentifier ، eChateInfo.getAppName ()) ؛ Timestamp long = eChateInfo.setIsDirtyWithTime () ؛ نجاح منطقي = سجل () ؛ if (success) {estanceInfo.unsetisDirty (timestamp) ؛ } نجاح العودة ؛ } return httpresponse.getStatusCode () == 200 ؛ } catch (throwable e) {logger.error ("{} - لم يتمكن من إرسال نبضات القلب!" ، بادئة + apppathidentifier ، e) ؛ العودة كاذبة }}إذا تم إرسال نبضات القلب إلى هنا ، إذا كانت العائد 404 ، فسيتم تنفيذ عملية التسجيل. لاحظ أنه بناءً على قيمة الإرجاع httpresponse ، يمكننا أن نستنتج أن جميع هذه العمليات تستند إلى طلبات HTTP. هل هذا صحيح؟ دعنا نستمر في النظر إلى طريقة التسجيل:
/*** سجل مع خدمة Eureka عن طريق إجراء مكالمة REST المناسبة. */ Boolean Register () رميات {logger.info (بادئة + AppPathIndifier + ": تسجيل الخدمة ...") ؛ eurekahttpresponse <Void> httpresponse ؛ حاول {httpresponse = eurekatransport.registrationClient.register (extoryInfo) ؛ } catch (استثناء e) {logger.warn ("{} - فشل التسجيل {}" ، بادئة + apppathidentifier ، e.getMessage () ، e) ؛ رمي ه ؛ } if (logger.isinfoenabled ()) {logger.info ("{} - حالة التسجيل: {}" ، بادئة + appathidentifier ، httpresponse.getStatusCode ()) ؛ } return httpresponse.getStatusCode () == 204 ؛ }هنا ، تسمى طريقة التسجيل في eurekatransport:
فئة ثابتة خاصة eurekatransport {private clostableresolver bootstrapresolver ؛ TransportClientFactory Private TransportClientFactory ؛ eurekahttpclient private registrationClient ؛ eurekahttpclientfactory private registrationClientFactory ؛ QueryClient eurekahttpclient الخاص ؛ الخاص eurekahttpclientfactory queryclientFactory ؛ foid sthowddown () {if (registrationClientFactory! = null) {registrationClientFactory.Shutdown () ؛ } if (QueryClientFactory! = null) {QueryClientFactory.shutdown () ؛ } if (registrationClient! = null) {registrationClient.shutdown () ؛ } if (QueryClient! = null) {queryclient.shutdown () ؛ } if (TransportClientFactory! = null) {transporclientFactory.shutdown () ؛ }}}هنا يمكننا أن نرى أن عميل Eureka يستخدم طلب HTTP لتسجيل الخدمة ، مما يعني أنه عند إنشاء DiscoveryClient ، سنقوم بتسجيل المثيل بالخادم.
3. خدمة الراحة التي يقدمها الخادم
لقد رأينا بالفعل الرمز الذي قدمه الخادم للتعامل مع طلبات تسجيل العميل. نظرًا لأن العميل يسجل من خلال بروتوكول HTTP ، يجب أن يكون للخادم عنوان للتعامل مع طلب HTTP هذا. في الواقع ، يستخدم خادم Eureka معيار Jax-RS لتوفير طريقة REST لفضح الخدمة. يمكننا إلقاء نظرة على طريقة Addinstance لهذا ApplicationResource:
/** * يسجل معلومات حول مثيل معين لـ * {link com.netflix.discovery.shared.application}. * * param info * {link evalueinfo} معلومات المثيل. * param isReplication * معلمة رأس تحتوي على معلومات ما إذا كان هذا يتم نسخه من العقد الأخرى. */postconsumes ({"application/json" ، "application/xml"}) addinstance public addinstance (easuleInfo info ، @headerparam (peereurekanode.header_replication) string isReplication) {dger.deBug ("stivation {} (replication = {}) . } if if (isBlank (info.gethostname ())) {return response.status (400) .entity ("missingname hostname"). build () ؛ } if if (isBlank (info.getipaddr ())) {return response.status (400) .entity ("عنوان IP مفقود"). build () ؛ } if if (isBlank (info.getAppName ())))) } آخر if (! appname.equals (info.getAppName ())))) {return response.status (400) .entity ("appname غير متطابق ، متوقعًا" + appname + "ولكن" + info.getAppName ()). build () ؛ } if if (info.getDatacenterInfo () == null) {return response.status (400) .entity ("datacenterinfo"). build () ؛ } آخر إذا (info.getDatacenterInfo (). getName () == null) {return response.status (400) .entity ("DatacenterInfo"). build () ؛ } if if (info.getDataCenterInfo (). getName () == null) {return response.status (400) .entity ("name datacenterinfo missing"). build () ؛ }. if (datacenterInfo extryof requiredIdeNifier) {String datacenterInfoid = ((requiredIntifier) datacenterInfo) .getId () ؛ if (isBlank (datacenterInfoid)) {boolean reparical = "true" .equalsignorecase (serverConfig.getExperimental ("registration.validation.datacenterinfoid")) ؛ if (تجريبي) {string intity = "datacenterinfo من النوع" + datacenterInfo.getClass () + "يجب أن يحتوي على معرف صالح" ؛ Return Response.Status (400). } آخر إذا (datacenterinfo extryof amazoninfo) {Amazoninfo Amazoninfo = (Amazoninfo) datacenterinfo ؛ سلسلة فعالة = AmazonInfo.get (Amazoninfo.metadatakey.instanceid) ؛ if (reventiveId == null) {Amazoninfo.getMetAdata (). put (AmazonInfo.MetAdatakey.instanceId.getName () ، info.getId ()) ؛ }} else {logger.warn ("تسجيل DataCenterInfo من النوع {} بدون معرف مناسب" ، datacenterInfo.getClass ()) ؛ }}}} registry.register (info ، "true" .equals (isReplication)) ؛ Return Response.Status (204) .Build () ؛ // 204 لتكون متوافقة مع الوراء}ما سبق هو كل محتوى هذه المقالة. آمل أن يكون ذلك مفيدًا لتعلم الجميع وآمل أن يدعم الجميع wulin.com أكثر.