在Spring Cloud中是使用Eureka來實現服務的注冊與發現的
請勿使用eureka2.x 用於生產 2.x已經停止開發了 使用1.x 最新版是1.9 我這里demo是使用1.9 詳情:https://github.com/Netflix/eureka/wiki
項目骨架搭建
創建父工程
1.創建一個父工程 實現版本的統一管理 以及子項目的管理
然后下一步下一步
創建子工程
在對應的tab 勾選web和EurekaServer 創建項目后會自動生成pom依賴 注意右上角cloud版本
將eureka子工程的paren 節點改為指向父工程 這的各個節點需要改為 你的對應父工程的節點描述
<parent> <groupId>com.liqiang</groupId> <artifactId>spring-cloud-parent</artifactId> <version>0.0.1-SNAPSHOT</version> <relativePath/> <!-- lookup parent from repository --> </parent>
將工程的在父工程創建節點 並刪除子工程的 沒有的話就不管
<!--版本號的統一管理。 子類項目沒有配置版本號的時候 會從子往上找 找到 dependencyManagement 找到對應的版本--> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>
父工程model配置上eureka子工程
<modules> <module>spring-cloud-eureka</module> </modules>
最后將父工程的Src目錄刪除 這樣我們項目的基礎骨架就搭建好了 可以進行eureka的相關配置了
個人習慣 喜歡用yml配置 所以講resource下面的application.properties 后綴改為yml
最后將parent pom文件的<packaging>jar</packaging> 改為<packaging>pom</packaging>
然后執行parent安裝
配置Eureka服務端
1.增加pom依賴並在Application類添加@EnableEurekaServer注解 開啟服務注冊功能
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency>
@SpringBootApplication @EnableEurekaServer //開啟服務注冊的功能 public class SpringCloudEurekaApplication { public static void main(String[] args) { SpringApplication.run(SpringCloudEurekaApplication.class, args); } }
2.配置application.yml
server:
port: 1111
eureka:
instance:
hostname: localhost #主機名
client:
registerWithEureka: false #不注冊自己 只維護服務 #集群情況下 是需要改成true 的因為 集群是注冊中心 相互注冊自己 同步服務
fetchRegistry: false #是否開啟從 Eureka-Server 獲取注冊信息。默認值:true 集群情況下需要為true的 因為 會在別的注冊中心 獲取服務
serviceUrl:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/ #集群情況配置多注冊中心,號隔開 單機情況下 不配置也沒關系
3.訪問http://127.0.0.1:1111
表示注冊中心搭建成功。。registerWithEureka 我們設置了false 表示不注冊 自己 我們可以嘗試改成true 則可以看到 服務里面會出現服務
配置服務提供者
1.跟創建注冊中心一樣 創建一個名為provider的項目 pom配置文件 跟provider一致
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency>
2.配置 application.yml配置文件
spring:
application:
name: provider #服務名字
server:
port: 8081
eureka:
instance:
hostname: localhost #當前實例的主機名字
client:
serviceUrl:
defaultZone: http://127.0.0.1:1111/eureka/ #注冊中心地址
3. @EnableDiscoveryClient 開啟服務注冊到注冊中心的功能
@SpringBootApplication @EnableDiscoveryClient //開啟將服務注冊到注冊中心 public class SpringCloudProviderApplication { public static void main(String[] args) { SpringApplication.run(SpringCloudProviderApplication.class, args); } }
4.創建對一個對外暴露的服務(注意 必須在application啟動類所在目錄或者下級目錄)
@RestController public class HelloServerContorrler { @RequestMapping(value = "/hello") public String hello(){ return "hello"; } }
5.啟動eureka 再啟動provider 可以發現服務已經注冊到注冊中心
服務消費者配置
跟上面一樣創建一個consumer
pom配置
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <!--從注冊中心獲取服務的依賴--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!--rabbon客戶端的負載均衡--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-ribbon</artifactId> </dependency> </dependencies>
啟動類配置
@SpringBootApplication @EnableDiscoveryClient //開啟服務發現 public class ConsumerApplication { public static void main(String[] args) { SpringApplication.run(ConsumerApplication.class, args); } //LoadBalanced 通過代理RestTemplate 實現客戶端負載均衡的能力 @LoadBalanced @Bean RestTemplate restTemplate(){ return new RestTemplate(); } }
yml配置
spring:
application:
name: consumer
server:
port: 9001
eureka:
client:
serviceUrl:
defaultZone: http://127.0.0.1:1111/eureka #注冊中心地址
調用服務
@Controller public class HelloContorller { //applicaton啟動類配置的@LoadBalanced 的代理對象 @Autowired RestTemplate restTemplate; @RequestMapping(value = "/hello") @ResponseBody public String hell(){ //PROVIDER需要大寫 這里沒有配置物理地址 而是服務名 是去服務集群 獲得集群服務地址 來實現負載均衡 return restTemplate.getForEntity("http://PROVIDER/hello",String.class).getBody(); } }
訪問http://127.0.0.1:9001/hello
注冊中心集群
1.配置host文件 模擬域名映射ip 比如我們通過http://peer1:8081/user 訪問 實際上是訪問peer1對應的ip 如http://127.0.0.1:8081/user
host文件在哪兒自行百度
127.0.0.1 peer1
127.0.0.1 peer2
2.copy2個配置文件 端口改為不一致
配置文件1
server: port: 1111 eureka: instance: hostname: localhost #主機名 client: registerWithEureka: true #將自己注冊到 集群中的其他注冊中心 fetchRegistry: true #從注冊中心同步服務。 serviceUrl: defaultZone: http://peer1:1112/eureka/ #集群匯總的其他注冊中心地址 多個,號隔開 spring: application: name: eureka
配置文件2
server: port: 1112 eureka: instance: hostname: localhost #主機名 client: registerWithEureka: true #將自己注冊到 集群中的其他注冊中心 fetchRegistry: true #從注冊中心同步服務。 serviceUrl: defaultZone: http://peer1:1111/eureka/ #集群匯總的其他注冊中心地址 多個,號隔開 spring: application: name: eureka
注冊中心集群 是根據注冊中心之間相互注冊和相互訂閱同步服務實現的 可以通過上面配置看出來
4.provider和cusumer的注冊中心地址指向集群的注冊中心所有
spring:
application:
name: consumer
server:
port: 9001
eureka:
client:
serviceUrl:
defaultZone: http://peer1:1111/eureka,http://peer2:1112/eureka
spring:
application:
name: provider #服務名字
server:
port: 8081
eureka:
instance:
hostname: localhost #當前實例的主機名字
client:
serviceUrl:
defaultZone: http://peer1:1111/eureka,http://peer2:1112/eureka #注冊中心地址
3.打包安裝 手動執行jar包 並分別指定配置文件路徑替換成本地打包的路徑
java -jar /Users/liqiang/Desktop/java開發環境/javadom/spring-cloud-parent/spring-cloud-eureka/target/spring-cloud-eureka-0.0.1-SNAPSHOT.jar --spring.profiles.active=peer1
java -jar /Users/liqiang/Desktop/java開發環境/javadom/spring-cloud-parent/spring-cloud-eureka/target/spring-cloud-eureka-0.0.1-SNAPSHOT.jar --spring.profiles.active=peer2
5.啟動provider
6.啟動cusumer
高可用集群的注冊中心就搭建好了
eureka邏輯圖
默認服務提供者啟動會注冊服務到注冊中心的雙層map 第一層key為服務名 第二層為key為實例信息 同時維持一個心跳方式注冊中心認為自己掛了將自己從服務列表剔除
不知道怎么測試 正常關閉的話會通過REST通知到服務注冊中心 注冊中心將服務狀態改為down 再通知下去
客戶端配置:
# 多久未收到心跳,剔除instance 要比心跳時間大
eureka.instance.lease-expiration-duration-in-seconds:30
# 心跳時間間隔
eureka.instance.lease-renewal-interval-in-seconds: 5
服務端配置:
eureka.instance.eviction-interval-timer-in-ms: 5000 多久去剔除一次指定時間沒有續約的服務
消費者啟動會拉取注冊中心服務。為了性能 eurekaServer會維護30秒的服務清單 該清單默認30秒更新一次 可以通過以下配置
eureka.client.registry-fetch-interval-seconds=30 確保開啟 eureka.client.fetchRegistry: false #是否開啟從 Eureka-Server 獲取注冊信息。默認值:true
服務保護機制
前面說了 注冊中心是通過 服務提供者維護的心跳來控制服務是否有效 當15分鍾內 心跳失敗的比例在85%會開啟保護機制 這個時候服務就不會因為續約過期而下線(個人覺得是為了防止 注冊中心 網絡出現問題 導致服務提供者通過心跳請求不能成功續約) 保護機制會導致客戶端拿到 無效的服務 所以需要客戶端啟用啟用重試和斷路器機制
可以通過eureka.instance.enable-self-preservation=false 關閉保護機制(不建議開啟 本地開發可以開啟)
Eureka初始化過程
1.配置信息啟動都會封裝到EurekaClientConfig
2.com.netflix.discovery.DiscoveryClient 根據EurekaClientConfig會初始化3個定時任務 分別服務注冊 服務獲取 服務續約(心跳) 三個定時任務都是根據rest接口而來的
通過源碼
com.netflix.discovery.DiscoveryClient的構造函數可以發現初始化定時任務的方法
private void initScheduledTasks() { //是否從注冊中心訂閱服務對應eureka.client.registerWithEureka配置 if (clientConfig.shouldFetchRegistry()) { //多久去注冊中心拉取一次服務對應eureka.client.registry-fetch-interval-seconds int registryFetchIntervalSeconds = clientConfig.getRegistryFetchIntervalSeconds(); //服務拉取失敗的重試時間系數 int expBackOffBound = clientConfig.getCacheRefreshExecutorExponentialBackOffBound(); scheduler.schedule( new TimedSupervisorTask( "cacheRefresh", scheduler, cacheRefreshExecutor, registryFetchIntervalSeconds, TimeUnit.SECONDS, expBackOffBound, new DiscoveryClient.CacheRefreshThread() ), registryFetchIntervalSeconds, TimeUnit.SECONDS); } //是否對注冊中心注冊服務 對應eureka.client.fetchRegistry if (clientConfig.shouldRegisterWithEureka()) { //心跳間隔時間 對應eureka.instance.lease-renewal-interval-in-seconds int renewalIntervalInSecs = instanceInfo.getLeaseInfo().getRenewalIntervalInSecs(); //心跳超時重試的系數對應 int expBackOffBound = clientConfig.getHeartbeatExecutorExponentialBackOffBound(); logger.info("Starting heartbeat executor: " + "renew interval is: {}", renewalIntervalInSecs); // Heartbeat timer scheduler.schedule( new TimedSupervisorTask( "heartbeat", scheduler, heartbeatExecutor, renewalIntervalInSecs, TimeUnit.SECONDS, expBackOffBound, new DiscoveryClient.HeartbeatThread() ), renewalIntervalInSecs, TimeUnit.SECONDS); // 服務注冊定時任務 instanceInfoReplicator = new InstanceInfoReplicator( this, instanceInfo, clientConfig.getInstanceInfoReplicationIntervalSeconds(), 2); // burstSize statusChangeListener = new ApplicationInfoManager.StatusChangeListener() { @Override public String getId() { return "statusChangeListener"; } @Override public void notify(StatusChangeEvent statusChangeEvent) { if (InstanceInfo.InstanceStatus.DOWN == statusChangeEvent.getStatus() || InstanceInfo.InstanceStatus.DOWN == statusChangeEvent.getPreviousStatus()) { // log at warn level if DOWN was involved logger.warn("Saw local status change event {}", statusChangeEvent); } else { logger.info("Saw local status change event {}", statusChangeEvent); } instanceInfoReplicator.onDemandUpdate(); } }; if (clientConfig.shouldOnDemandUpdateStatusChange()) { applicationInfoManager.registerStatusChangeListener(statusChangeListener); } instanceInfoReplicator.start(clientConfig.getInitialInstanceInfoReplicationIntervalSeconds()); } else { logger.info("Not registering with Eureka server per configuration"); } }
配置參考
https://blog.csdn.net/cvntopuyef/article/details/78477724
Instance配置
服務注冊會向注冊中心發送一些用於描述自己的的元數據信息比如 服務名稱、 實例名稱、 實例IP、 實例端口等
在使用 Spring CloudEureka的時候, 所有的配置信息都通過org.springframework.cloud.netflix.eureka.EurekalnstianceConfigBean進行加載
但在真正進行服 務注冊的時候, 還是會包裝成com.netflix.appinfo.Instiancelnfo
可以通過eureka.instance.metadataMap.<key>=<value> 來進行自定發送數據
eureka.instance.instanceid 指定服務實例id 默認主機名+端口
指定服務info和heath端點路徑 heath用於服務更新服務狀態 down up info用於注冊中心點擊服務訪問信息 保證這2類不要404 默認一般不用修改 當上下文修改 可以通過以下配置指定需要引入
spring-boot-starter-actuator模塊
management.context-path=/hello
eureka.instance.statusPageUrlPath=${management.context-path}/info
eureka.instance.healthCheckUrlPath=${management.context-path}/health
instance其他配置