Eureka는 분산 된 서비스 거버넌스 응용 프로그램이며, 그 특징은 서버와 서비스로 구성된 주소로 등록 할 수 있다는 것입니다. 그런 다음이 기사에서는 Eureka의 등록 과정에 대해 논의 해 봅시다.
1. 유레카의 서버
Eureka의 서버 측의 핵심 클래스는 Eurekabootstrap이며 ServletContextListener의 리스너를 구현합니다. 따라서 Eureka는 서블릿 컨테이너를 기반으로 구현되었다고 결론을 내릴 수 있습니다. 키 코드는 다음과 같습니다.
공개 클래스 EureKabootStrap은 ServletContextListener {//.. * * * @see * javax.servlet.servletcontextListener#contextInitialized (javax.servlet.servletcontextevent) */ @override public void contextinitialized (servletcontextevent event) {initeurekaenvironment (); initeurekaservercontext (); ServletContext sc = event.getServletContext (); sc.setattribute (eurekaserverContext.class.getName (), serverContext); } catch (Throwable e) {logger.error ( "Eureka Server를 부트 스트랩 할 수 없습니다 :", e); 새로운 runtimeexception을 던지십시오 ( "Eureka Server를 부트 스트랩 할 수 없습니다 :", e); }} // 관련 코드를 생략 ......}ServletContext 초기화가 완료되면 Eureka 환경이 초기화되고 EureKaserverContext가 초기화됩니다. 그런 다음 initeurekaservercontext 메소드를 살펴보고 있습니다.
/*** 서버 컨텍스트를위한 init hook. 사용자 정의 로직을 재정의합니다. */ 보호 된 void initeureKaserverContext ()는 예외를 던져 {// ...... ApplicationInfomanager applicationInfomanager = null; if (eureKaclient == null) {eureKainStanceConfig instanceConfig = iscloud (configurationManager.getDeploymentContext ())? 새로운 CloudInstanceConfig () : 새로운 MyDatacenterInstanceConfig (); ApplicationInfomanager = New ApplicationInfomanager (InstanceConfig, New EureKaconFigBasedInstanceInfoprovider (InstanceConfig) .get ()); eureKaclientConfig eureKaclientConfig = 새로운 defaulteureKaclientConfig (); eureKaclient = 새로운 DiscoveryClient (ApplicationInfomanager, eureKaclientConfig); } else {ApplicationInfomanager = eureKaclient.getApplicationInfomanager (); } peerawareinstanceregestry Registry; if (ISAWS (ApplicationInfomanager.getInfo ())) {레지스트리 = New AwsinstanceRegistry (EureKaserverConfig, eureKaServerConfig, eureKaclient.geteureKaclientConfig (), ServerCodecs, EureKaclient); awsbinder = 새로운 awsbinderdelegate (eurekaserverconfig, eurekaclient.geteurekaclientconfig (), 레지스트리, ApplicationInfomanager); awsbinder.start (); } else {registry = new PeeraWareInstancereGistryimpl (eureKaserverConfig, eureKaclient.geteureKaclientConfig (), ServerCodecs, eureKaclient); } //...OMIT 코드의 일부}이 방법에서는 유레카 서비스와 관련된 많은 객체가 만들어집니다. 여기에 나는 두 가지 핵심 객체, 즉 eurekaclient와 peerawareinstanceregistry를 나열합니다. 우리는 나중에 클라이언트 부분에 대해 이야기 할 것입니다. PeerawareInstanceregistry가 무엇을 사용하는지 살펴 보겠습니다. 여기에 나는이 수업에 대한 수업 다이어그램을 씁니다.
클래스 다이어그램에 따르면, 우리는 PeerawareInstanceregistry의 최상위 인터페이스가 LeAsemanager 및 LookupService라는 것을 분명히 알 수 있습니다. 여기서 LookupService는 가장 기본적인 발견 예제의 동작을 정의하는 반면 Leasemanager는 클라이언트 등록, 갱신 및 취소 작업의 처리를 정의합니다. 이 기사에서는 Leasemanager의 관련 인터페이스 구현에 중점을 두겠습니다. 되돌아 보면, 우리는 Peerawareinstanceregistry를보고 있습니다. 실제로이 클래스는 여러 노드에서 관련 정보를 복사하는 데 사용됩니다. 예를 들어, 노드가 갱신 및 오프라인으로 등록되는 경우 관련 사본 (알림) 이이 클래스를 통해 각 노드에 복사됩니다. 클라이언트 등록을 어떻게 처리하는지 봅시다.
/** * {@link instanceinfo} 및 replicas *에 대한 정보를 모든 피어 유레카 노드에 등록합니다. 이것이 다른 복제 노드의 복제 이벤트 *라면 복제되지 않습니다. * * @param info * 등록 및 복제 할 {@link instanceinfo}. * @param isreplication * true 다른 복제 노드의 복제 이벤트 인 경우 * false는 그렇지 않습니다. */ @override public void register (최종 인스턴스 인내 정보, 최종 부울 isreplication) {int leaseduration = lease.default_duration_in_secs; if (info.getLeaseInfo ()! = null && info.getLeaseInfo (). getDurationInsecs ()> 0) {LEASEDURATION = info.getLeaseInfo (). getDurationInsecs (); } super.register (정보, 임대, isreplication); ReplicateTopeers (action.register, info.getAppName (), info.getId (), info, null, isreplication); }우리는 그것이 상위 클래스의 레지스터 메소드를 호출 한 다음 복제물을 통해 다른 노드에 해당 동작을 복제한다는 것을 알 수 있습니다. 특정 복제는 여기에서 논의되지 않습니다. 등록 방법에 집중합시다. 상위 클래스에서 register () 메소드를 찾습니다.
/*** 주어진 지속 시간에 새 인스턴스를 등록합니다. * * * @see com.netflix.eureka.lease.Leasemanager#register (java.lang.object, int, boolean) */ public void register (instanceinfo 등록, int 임대, 부울 isreplication) {try {read.lock (); map <string, 임대 <instanceInfo >> gmap = registry.get (registrant.getAppName ()); register.increment (isreplication); if (gmap == null) {Final ConcurrenThashMap <string, 임대 <instanceInfo >> gnewMap = new ConcurrEntashMap <string, 임대 <instanceInfo >> (); gmap = registry.putifabsent (registrant.getAppName (), gnewMap); if (gmap == null) {gmap = gnewmap; }} 임대료 <IngistInfo> excientingLease = gmap.get (registrant.getId ()); // 이미 임대가있는 경우 (기존 LEASS! = NULL && (기름진 LEASS.GETHOLDER ()! = NULL)) {LONG ENDICALLASTDIRTYTIMESTAMP = ENDERNINGLEASE.GETHOLDER (). GETLASTDIRTYTIMESTAMP (); Long RegistrationLastDirtyTimestamp = registered.getLastDirtyTimestamp (); logger.debug ( "기존의 임대가 발견 된 = {}, 러드 = {}", 기존 lastDirtyTimestamp, registrationLastDirtyTimeStamp); // 이것은> = 대신 a> = 대신에 원격 전송 된 // instanceinfo를 사용하기 때문에 (기존) 로컬 카피를 사용합니다. registrationLastDirtyTimestamp) {logger.warn ( "기존의 임대가 있고 기존 임대의 더러운 타임 스탬프 {}는 등록되는 것보다" + "가 더 큽니다. registration = EventialLease.getholder ()} else {// 임대가 존재하지 않으므로 새로운 등록 동기화 (잠금) {if (this.expectednumberofrenewspermin> 0) {// 클라이언트가 취소하기를 원하기 때문에 threshold // (30 초, 2). 이 instance <comentinfo> (기존).소스 코드를 통해 프로세스를 간단히 정리해 봅시다.
1) 먼저, appName을 기반으로 서비스 인스턴스 개체의 일부 열을 가져옵니다. NULL 인 경우 새 맵을 작성하고 현재 등록 된 응용 프로그램 정보를이 맵에 추가하십시오. 여기에는 임대 대상이 있습니다. 이 클래스는 등록 시간, 서비스 시작 시간, 최종 업데이트 시간 등과 같은 일반 T의 시간 속성을 설명합니다. 구현에주의를 기울일 수 있습니다.
/ * * Copyright 2012 Netflix, Inc. * * Apache 라이센스에 따라 라이센스가 부여 된 버전 2.0 ( "라이센스"); * 라이센스를 준수하는 것 외에는이 파일을 사용할 수 없습니다. * 귀하는 * * http://www.apache.org/license/license/license-2.0 *에서 라이센스 사본을 얻을 수 있습니다. 적용 가능한 법률에 의해 요구되거나 서면으로 동의하지 않는 한, 라이센스에 따라 배포 된 소프트웨어 *는 "기준"에 배포됩니다. * 특정 언어 거버넌스 권한 및 라이센스에 따른 제한에 대한 라이센스를 참조하십시오. */package com.netflix.eureka.lease; import com.netflix.eureka.registry.abtractinstanceregistry;/***는 {@link t}의 시간 기반 가용성을 설명합니다. 목적은 AWS 환경에서는 드문 일이 아닌 ungraceful * 종료의 결과로 {@link actractinstanceregistry}의 인스턴스를 피하는 것입니다. * * 갱신없이 임대가 경과하면 결국 연속적으로 만료 * 즉각적인 퇴거에 대한 {@link t}를 표시합니다. * {@link t}와 {@link leasemanager} 사이의 통신이 없다는 점을 제외하고는 명시 적 취소와 유사합니다. * * @Author Karthik Ranganathan, Greg Kim */Public Class Lease <t> {enum action {register, cancel, Renew}; 공개 정적 최종 int default_duration_in_secs = 90; 개인 T 보유자; 비공개 오랫동안 퇴치 timeStamp; 개인 장기 등록 timeStamp; Private Long ServiceUptimestamp; // 만료 과제 가이 빠른 개인 휘발성 긴 LONG ULSUPDATETIMESTAMP를 볼 수 있도록 휘발성으로 만드십시오. 개인 장기 기간; 공공 임대 (t r, int durationInsecs) {holder = r; registrationtimestamp = System.CurrentTimeMillis (); lastupdateTimestamp = registrationtimestamp; 시간 (durationInsecs * 1000); } /** *리스 갱신, 등록시 * 관련 {@link t}에 의해 지정된 경우 갱신 기간을 사용하십시오. 그렇지 않으면 기본 기본 시간은 * {@link #default_duration_in_secs}입니다. */ public void Renew () {lastupdateTimestamp = System.CurrentTimeMillis () + 지속 시간; } /*** 퇴거 시간을 업데이트하여 임대를 취소합니다. */ public void cancel () {if (evictiontimeStamp <= 0) {evictiontimeStamp = system.currentTimeMillis (); }} /*** 서비스를 UP으로 표시합니다. 이것은 처음으로 불려지는 경우에만 영향을 미칩니다. * 후속 통화는 무시됩니다. */ 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}의 임대가 만료되었는지 여부를 확인합니다. * * '잘못된 "일을하고 마지막으로 마지막으로 +시간을 설정하는 것보다 +시간 이상을 설정하기 때문에 * 만료는 실제로 2 * 기간이 될 것입니다. 이것은 사소한 버그 일 것입니다. 이것은 미미한 셧다운에 영향을 미치는 경우에만 영향을 미쳐야합니다. Boolean isexpired (Long AdditionAleAsems) {return (evictiontimestamp> 0 || system.currentTimeMillis ()> (lastupdateTimestamp + duration + addubaleasems);} / ** * 임대가 등록 된 이후 수백만 개를 얻습니다. {returntimestamp는 갱신 된 이후에 수백만 분의 갱신 시간이 아니라 갱신 된 이후로 수백만 초에 주목합니다 임대가 퇴거 된 이후 수백만 초 getServiceuptimestamp () {return serviceuptimestamp} * 리턴 {return getholder () {return holder}.2) 현재 등록 된 ID에 따르면지도에서 얻을 수있는 경우 다음을 수행하십시오.
2.1) 현재 기존 노드의 터치 시간과 등록 된 노드의 터치 시간에 따라, 이전 시간이 후반보다 늦은 경우, 현재 등록 된 인스턴스는 기존 인스턴스에 따라야합니다.
2.2) 그렇지 않으면 분당 예상 갱신 수와 임계 값을 업데이트하십시오.
3) 현재 등록 노드를지도에 저장하면 등록 절차가 기본적으로 끝났습니다.
2. 유레카 클라이언트
Server ServletContext가 초기화되면 DiscoveryClient가 생성됩니다. Eureka에 익숙한 친구는이 두 가지 속성, 즉 Fetchregistry와 Registerwitheureka에 익숙해야합니다. SpringCloud에서 통합 Eureka Independent Mode에서 실행할 때이 두 값이 False가 아닌 경우 시작은 오류를보고합니다. 오류를보고하는 이유는 무엇입니까? 실제로 답은 DiscoveryClient의 생성자에 있습니다.
@inject DiscoveryClient (ApplicationInfomanager applicationInfomanager, eureKaclientConfig Config, AbstractDiscoveryClientOptionalArgs args, 제공자 <backupregistry> backupregistryProvider) {// ... 데이터에 대한 쿼리 나 쿼리가 아닙니다. "); 스케줄러 = null; HeartBeatExecutor = null; Cacherefreshexecutor = null; eurekatransport = null; InstancerEgionChecker = New InstancerEgionChecker (New PropertyBasedAztoregionMapper (config), clientConfig.getRegion ()); // 이것은 DiscoveryManager.getInstance ()를 사용하여 기존 코드를 허용하기위한 약간의 해킹입니다. di'd DiscoveryClient DiscoveryManager.getInstance (). setDiscoveryClient (this)와 함께 작동합니다. DiscoveryManager.getInstance (). seteureKaclientConfig (config); inittimeStampms = System.CurrentTimeMillis (); logger.info ( "초기 인스턴스 카운트가있는 timestamp {}에서 초기화 된 Discovery Client : {}", inittimestampms, this.getApplications (). size ()); 반품; // 네트워크 작업을 설정할 필요가없고 완료됩니다} 시도 {// HeartBeat 및 CachereFresh Scheduler = Executor.NewScheduledThreadPool (2, New ThreadFactoryBuilder () .setNameformat ( "DiscoverYClient-%D"). HeartBeatExecutor = New ThreadPoolexecutor (1, ClientConfig.getheartBeatexecutorthreadPoolsize (), 0, TimeUnit.seconds, new Synchronousqueue <Runnable> (), New ThreadTictoryBuilder () .setNameformat ( "DiscoveryClient-HeartBeateCutor-%d"). // 직접 핸드 오프 사용 CachereFreshExecutor = new ThreadPoolexecutor (1, ClientConfig.getCachereFreesHexecutorthreadPoolsize (), 0, TimeUnit.seconds, new Synchronousqueue <Runnable> (), New Thread FactoryBuilder () .setNameformat ( "DiscoveryClient CachereFreeCeEceEceEceEcutoR-%CutOpOr-%. .SetDaemon (true) .build ()); // 직접 핸드 오프 EureKatransport 사용 = New EureKatransport (); ScheduleserVerendpointTask (Eurekatransport, Args); // .... 일부 코드 initscheduledTasks (); // ....}소스 코드를 기반으로 다음과 같은 결론을 도출 할 수 있습니다.
1) 둘 다 둘 다 witheureka와해야 할 일을 거짓으로 해야하는 경우 직접 돌아갑니다.
2) 심장 박동을 보내고 캐시를 새로 고침하는 스레드 풀 만들기
3) 생성 된 시간이 정한 작업을 초기화하십시오
그런 다음 initscheduledTasks () 메소드의 다음 코드를 살펴 보겠습니다.
// 하트 비트 타이머 스케줄러.schedule (새로운 timedsupervisortask ( "하트 비트", 스케줄러, 하트 비트 외환기, RenewalinterValinsecs, TimeUnit.seconds, ExpbackoffBound, New HeartBeatthread (), RenewalinterValinsecs, timeUnit.seconds);
다음은 시간이 몇 초 만에 시간이 지남에 따라 실행되는 실을 트리거하고 Renewalintervalinsecs 값에 따라 보내는 하트 비트를 실행하는 스레드입니다. 하트 비트 스레드 스레드는 다음과 같이 실행됩니다.
/*** 주어진 간격으로 임대를 갱신하는 심장 박동 작업. */ private class heartbeatthread는 runnable {public void run () {if (renew ()) {lastSuccessfulheartBeatTimestamp = system.currentTimeMillis (); }}}실행 방법이 갱신 메소드를 실행하는 것이 매우 간단하고 시간이 성공적으로 기록 된 경우를 알 수 있습니다. 갱신 방법 :
/ ** * 적절한 휴식을 취함으로써 유레카 서비스로 갱신 */ boolean renew () {eurekahttpresponse <IngistInfo> httprosponse; {httpresponse = eurekatransport.registrationclient.sendheartBeat (instanceInfo.getAppName (), instanceInfo.getId (), instanceInfo, null); logger.debug ( "{} - 하트 비트 상태 : {}", prefix + apppathidentifier, httpresponse.getStatuscode ()); if (httpresponse.getStatuscode () == 404) {reeregister_counter.increment (); logger.info ( "{} - apps/{}", prefix + apppathidentifier, instanceInfo.getAppName ()); long timestamp = instanceInfo.setIsDirtyWithTime (); 부울 성공 = 레지스터 (); if (success) {instanceInfo.unsetisDirty (timestamp); } 반환 성공; } return httpresponse.getStatusCode () == 200; } catch (Throwable e) {logger.error ( "{} - 심장 박동을 보낼 수 없었습니다!", Prefix + AppPathidentifier, e); 거짓을 반환합니다. }}심장 박동이 여기에 전송되면 반품이 404 인 경우 등록 작업이 수행됩니다. 반환 값 httpresponse를 기반으로 이러한 모든 작업은 HTTP 요청을 기반으로한다는 결론을 내릴 수 있습니다. 그게 사실인가요? 레지스터 방법을 계속 살펴 보겠습니다.
/*** 적절한 휴식을 취하여 유레카 서비스에 등록하십시오. */ boolean register ()는 던질 수있는 {logger.info (prefix + apppathidentifier + ": 등록 서비스 ..."); eurekahttpresponse <void> httpresponse; {httpresponse = eurekatransport.registrationclient.register (instanceInfo); } catch (예외 e) {logger.warn ( "{} - 등록 실패 {}", prefix + apppathidentifier, e.getMessage (), e); e 던지기; } if (logger.isinfoenabled ()) {logger.info ( "{} - 등록 상태 : {}", prefix + apppathidentifier, httpresponse.getStatuscode ()); } return httpresponse.getStatusCode () == 204; }여기서는 eurekatransport의 등록 클리어 메소드를 다음과 같습니다.
개인 정적 최종 클래스 Eurekatransport {Private CloseAbleresolver Bootstrapresolver; 개인 TransportClientFactory TransportClientFactory; Private eurekahttpclient 등록 클라이언트; Private eurekahttpclientFactory 등록 ClientFactory; Private EureKahttpclient QueryClient; Private eurekahttpclientFactory QueryClientFactory; void shutdown () {if (registrationClientFactory! = null) {rgistrationClientFactory.shutdown (); } if (QueryClientFactory! = null) {QueryClientFactory.shutdown (); } if (registrationClient! = null) {registrationClient.shutdown (); } if (QueryClient! = null) {QueryClient.shutdown (); } if (TransportClientFactory! = NULL) {TransportClientFactory.Shutdown (); }}}여기서 Eureka의 클라이언트가 HTTP 요청을 사용하여 서비스 등록을 사용하는 것을 알 수 있습니다. 즉, DiscoveryClient를 만들 때 인스턴스를 서버에 등록합니다.
3. 서버가 제공하는 정지 서비스
우리는 이미 고객 등록 요청을 처리하기 위해 서버가 제공 한 코드를 보았습니다. 클라이언트는 HTTP 프로토콜을 통해 등록하므로 서버는이 HTTP 요청을 처리 할 주소가 있어야합니다. 실제로 Eureka Server는 JAX-RS 표준을 사용하여 서비스를 노출시키는 REST 방법을 제공합니다. 이 ApplicationResource의 추가 방법을 살펴볼 수 있습니다.
/** * * {@link com.netflix.discovery.shared.application}의 특정 인스턴스에 대한 정보를 등록합니다. * * @param info * {@link instanceInfo} 인스턴스의 정보. * @param isreplication *이 정보가 포함 된 헤더 매개 변수는 다른 노드에서 복제되어 있는지 여부입니다. */@post @consumes ({ "application/json", "application/xml"}) 공개 응답 addinstance (instanceinfo info, @headerparam (peereurekanode.header_replication) string isreplication) {logger.debug ( "registering instance = {}); // instanceInfo에 필요한 모든 필수 필드가 포함되어 있는지 확인하십시오. if (isblank (info.getId ()) {return response.status (400) .entity ( "missing instanceId"). build (); } else if (isblank (info.gethostName ()) {return response.status (400) .entity ( "누락 된 호스트 이름"). build (); } else if (isblank (info.getipaddr ()) {return response.status (400) .entity ( "IP 주소 누락"). build (); } else if (isblank (info.getAppName ())) {return response.status (400) .entity ( "누락 된 AppName"). build (); } else if (! appName.equals (info.getAppName ())) {return response.status (400) .entity ( "불일치 한 appName," + appName + "를 기대하지만" + info.getAppName ()). build (); } else if (info.getDatacenterInfo () == null) {return response.status (400) .entity ( "누락 된 DatacenterInfo"). build (); } else if (info.getDatacenterInfo (). getName () == null) {return response.status (400) .entity ( "DataCenterInfo"). build (); } else if (info.getDatacenterInfo (). getName () == null) {return response.status (400) .entity ( "DataCenterInfo 이름이 없음"). build (); } // 클라이언트가 누락 된 데이터가있는 잘못된 DatacenterInfo에 등록 할 수있는 경우 DatacenterInfo DatacenterInfo = info.getDatacenterInfo (); if (datacenterinfo inciationof incientIondifier) {String DatacenterInfoid = ((고유 한 핵심기) DatacenterInfo) .getId (); if (isblank (datacenterinfoid)) {boolean Experimental = "true".EqualSignoreCase (ServerConfig.getexPerimental ( "registration.validation.datacenterinfoid")); if (실험) {String entity = "DatacenterInfo of Type" + DatacenterInfo.getClass () + "유효한 ID를 포함해야합니다"; return response.status (400) .entity (entity) .build (); } else if (AmazonInfo의 DatacenterInfo 인스턴스) {AmazonInfo AmazonInfo = (AmazonInfo) DatacenterInfo; String EffectiCID = AmazonInfo.get (AmazonInfo.metadatakey.instanceId); if (expectionId == null) {AmazonInfo.getMetadata (). put (amazoninfo.metadatakey.instanceid.getName (), info.getId ()); }} else {logger.warn ( "적절한 ID없이 {}의 DatacEnterInfo 등록", datacenterinfo.getClass ()); }}}} registry.register (info, "true".equals (isreplication)); return response.status (204) .build (); // 204 거꾸로 호환}위는이 기사의 모든 내용입니다. 모든 사람의 학습에 도움이되기를 바랍니다. 모든 사람이 wulin.com을 더 지원하기를 바랍니다.