本文主要介紹 SpringCloud 的入門
一、SpringCloud 的五大元件
(需要牢牢記住他們,現在混個眼熟,下面會詳細介紹。)

自學參考文檔:
- SpringCloud 官方文檔(漢化版):https://springcloud.cc/spring-cloud-dalston.html
- SpringCloud中國社群:http://springcloud.cn/
- SpringCloud中文網:https://springcloud.cc
自學參考文章:
- 一文詳解微服務架構:https://www.cnblogs.com/skabyy/p/11396571.html
自學參考知乎各個大神們的回答:
- 微服務架構是什麼?:https://www.zhihu.com/question/65502802
自學參考視訊:
- kuangstudy:https://www.kuangstudy.com/course/play/1321005531116863490
二、什麼是微服務
Spring官網:https://spring.io/
微服務(Microservice Architecture) 是近幾年流行的一種架構思想,關于它的概念很難一言以蔽之。究竟什麼是微服務呢?我們在此引用ThoughtWorks 公司的首席科學家 Martin Fowler 于2014年提出的一段話:
原文:https://martinfowler.com/articles/microservices.html
中文:https://www.cnblogs.com/liuning8023/p/4493156.html
就目前而言,對于微服務,業界沒有一個統一的标準定義。通常而言,微服務架構是一種架構模式,或者說是一種架構風格,它提倡将單一的應用程式劃分為一組小的服務,每個服務運作在其獨特的自己的程序内,服務之間互相協調,互相配置,為使用者提供最終價值。服務之間采用輕量級的通信機制互相溝通,每個服務都圍繞着具體的業務進行建構,并且能夠被獨立的部署到生産環境中。另外,應盡量避免統一的,集中式的服務管理機制,對具體的一個服務而言,應根據業務上下文,選擇适合的語言,工具對其進行建構,可以有一個非常輕量級的集中管理來協調這些服務,可以用不同的語言來編寫服務,也可以使用不同的資料庫。 (牢牢記住上面這段概念,幾乎每句話都有微服務的關鍵點)
三、傳統開發模式 VS 微服務
- 傳統的web開發方式
一般被稱為Monolithic(單體式開發)。所有的功能打包在一個 WAR包裡,基本沒有外部依賴(除了容器),部署在一個JEE容器(Tomcat,JBoss,WebLogic)裡,包含了 DO/DAO,Service,UI等所有邏輯。
優點:
集中式管理,開發簡單,功能都在本地,沒有分布式的管理和調用。
缺點:
1.效率低,開發都在同一個項目改代碼,互相等待,沖突不斷;
2.穩定性差,一個微小的問題,可能導緻整個程式挂掉;
3.維護性難,代碼高耦合,内部關系難以摸清楚;
4.擴充性差,無法滿足高并發下的業務需求;
- 随着業務的發展,移動端興起
這一階段,架構設計存在着很多不合理的地方:
1.網站和移動端存在着很多相同業務邏輯的重複代碼;
2.資料庫被多個應用依賴,無法重構和優化;
3.資料有時候通過資料庫共享,有時候通過接口調用傳輸,接口調用關系雜亂;
4.單個應用為了給其他應用提供接口,設計得越來越複雜,包含本不屬于它得邏輯;
5.應用之間界限模糊,功能歸屬混亂,出現問題後各部門職責很難劃分,出現分歧或争端;
6.所有的應用都在一個資料庫上操作,資料庫出現性能瓶頸;
7.管理背景保障級别比較低,添加新的功能可能影響到其他應用;
8.開發、部署、維護、更新愈發困難;
- 下一階段,除去大量的備援代碼
在這一階段,服務已經被拆分開了,但是資料庫依然是共用的。會出現一些問題:
1.資料庫性能瓶頸,而且存在一定的風險;
2.資料庫表結構可能被多個服務依賴,維護困難;
- 提高系統的實時性,再次更新架構(微服務)
此時,拆分後的各個服務可以采用異構的技術。比如,資料分析服務可以使用資料倉庫作為持久化層,以便于高效地做一些統計計算;促銷服務通路比較頻繁,是以可以加入緩存機制。
微服務,它是具體解決某一個問題/提供落地對應服務的一個服務應用,狹義的看,可以看作是IDEA中的一個個微服務工程,或者Moudel。IDEA 工具裡面使用Maven開發的一個個獨立的小Moudel,它具體是使用SpringBoot開發的一個小子產品,專業的事情交給專業的子產品來做,一個子產品就做着一件事情。強調的是一個個的個體,每個個體完成一個具體的任務或者功能。
微服務的優點:
1.單一職責原則;
2.每個服務足夠内聚,足夠小,代碼容易了解;
3.開發效率高,一個服務可能就是專一的隻幹一件事;
4.微服務能夠被小團隊單獨開發,這個團隊隻需2-5個開發人員組成;
5.松耦合,無論是在開發階段或部署階段都是獨立的;
6.可以使用不同的語言開發;
7.易于和第三方內建,微服務允許容易且靈活的方式內建自動部署,通過持續內建工具,如jenkins,Hudson,bamboo;
8.每個微服務都有自己的存儲能力,可以有自己的資料庫,也可以有統一的資料庫;
微服務的缺點:
1.開發人員要處理分布式系統的複雜性;
2.多服務運維難度,随着服務的增加,運維的壓力也在增大;
3.各個服務間的通信成本問題;
4.整個應用分散成多個服務,定位故障相對困難;
5.一個服務故障可能産生雪崩效用,導緻整個系統故障;
整體解決思路如下:
四、SpringCloud入門
官網:http://projects.spring.io/spring-cloud/
SpringCloud沒有采用數字編号的方式命名版本号,而是采用了倫敦地鐵站的名稱,同時根據字母表的順序來對應版本時間順序,比如最早的Realse版本:Angel,第二個Realse版本:Brixton,然後是Camden、Dalston、Edgware,目前最新的是Hoxton SR4 CURRENT GA通用穩定版。
- 微服務技術棧有那些?
微服務技術條目 | 落地技術 |
---|---|
服務開發 | SpringBoot、Spring、SpringMVC等 |
服務配置和管理 | Netfix公司的Archaius、阿裡的Diamond等 |
服務注冊與發現 | Eureka、Consul、Zookeeper等 |
服務調用 | Rest、PRC、gRPC |
服務熔斷器 | Hystrix、Envoy等 |
負載均衡 | Ribbon、Nginx等 |
服務接口調用(用戶端調用服務的簡化工具 | Fegin等 |
消息隊列 | Kafka、RabbitMQ、ActiveMQ等 |
服務配置中心管理 | SpringCloudConfig、Chef等 |
服務路由(API網關) | Zuul等 |
服務監控 | Zabbix、Nagios、Metrics、Specatator等 |
全鍊路追蹤 | Zipkin、Brave、Dapper等 |
資料流操作開發包 | SpringCloud Stream(封裝與Redis,Rabbit,Kafka等發送接收消息) |
時間消息總站 | SpringCloud Bus |
服務部署 | Docker、OpenStack、Kubernetes等 |
- 各微服務架構對比
功能點/服務架構 | Netflix/SpringCloud | Motan | gRPC | Thrit | Dubbo/DubboX |
---|---|---|---|---|---|
功能定位 | 完整的微服務架構 | RPC架構,但整合了ZK或Consul,實作叢集環境的基本服務注冊發現 | RPC架構 | 服務架構 | |
支援Rest | 是,Ribbon支援多種可拔插的序列号選擇 | 否 | |||
支援RPC | 是(Hession2) | 是 | |||
支援多語言 | 是(Rest形式) | ||||
是(服務端zuul+用戶端Ribbon),zuul-服務,動态路由,雲端負載均衡Eureka(針對中間層伺服器) | 是(用戶端) | ||||
配置服務 | Netfix Archaius,Spring Cloud Config Server 集中配置 | 是(Zookeeper提供) | |||
服務調用鍊監控 | 是(zuul),zuul提供邊緣服務,API網關 | ||||
高可用/容錯 | 是(服務端Hystrix+用戶端Ribbon) | ||||
典型應用案例 | Netflix | Sina | |||
社群活躍程度 | 高 | 一般 | 2017年後重新開始維護,之前中斷了5年 | ||
學習難度 | 中等 | 低 | |||
文檔豐富程度 | |||||
其他 | Spring Cloud Bus為我們的應用程式帶來了更多管理端點 | 支援降級 | Netflix内部在開發內建gRPC | IDL定義 | 實踐的公司比較多 |
- Rest 搭建學習環境
1.建立父工程 springcloud (使用 pom 打包方式)
4.0.0com.zhouspringcloud1.0-SNAPSHOTspringcloud-consumer-dept-80springcloud-provider-dept-8001springcloud-apipomUTF-81.81.84.121.2.171.16.18org.springframework.cloudspring-cloud-alibaba-dependencies0.2.0.RELEASEpomimport
org.springframework.cloudspring-cloud-dependenciesGreenwich.SR1pomruntimeorg.springframework.bootspring-boot-dependencies2.1.4.RELEASEpomimportmysqlmysql-connector-java5.1.47com.alibabadruid1.2.5org.mybatis.spring.bootmybatis-spring-boot-starter1.3.2ch.qos.logbacklogback-core1.2.3junitjunit${junit.version}log4jlog4j${log4j.version}org.projectlomboklombok${lombok.version}org.springframework.bootspring-boot-maven-pluginsrc/main/java**/*.*falsesrc/main/resources**/*.*false
注意:父工程為 springcloud,其下有多個子 module
2.建立子子產品 springcloud-api,它隻負責接管 pojo
pom 依賴
springcloudcom.zhou1.0-SNAPSHOT4.0.0springcloud-apiorg.projectlomboklombok
3.在SQLyog中建立資料庫 db01
idea連接配接此資料庫并建立一些字段和資料
insert into dept (dname, db_source) VALUES ('開發部',DATABASE());
insert into dept (dname, db_source) VALUES ('設計部',DATABASE());
insert into dept (dname, db_source) VALUES ('人事部',DATABASE());
insert into dept (dname, db_source) VALUES ('營運部',DATABASE());
insert into dept (dname, db_source) VALUES ('企劃部',DATABASE());
insert into dept (dname, db_source) VALUES ('編輯部',DATABASE());
4.建立實體類
package com.zhou.springcloud.pojo;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import java.io.Serializable;
@Data
@NoArgsConstructor
@Accessors(chain = true) //鍊式程式設計,預設 boolean chain() default false;
public class Dept implements Serializable { //Dept 實體類
private Long deptno;//主鍵
private String dname;
//這個資料存在那個資料庫的字段,微服務,一個服務對應一個資料庫,同一個資訊可能存在不同的資料庫
private String db_source;
public Dept(String dname) {
this.dname = dname;
}
}
5.建立子子產品 springcloud-provider-dept-8001 (服務的提供者)
子子產品 springcloud-provider-dept-8001 的整體項目結構如下:
springcloudcom.zhou1.0-SNAPSHOT4.0.0springcloud-provider-dept-8001com.zhouspringcloud-api1.0-SNAPSHOTjunitjunitmysqlmysql-connector-javacom.alibabadruidch.qos.logbacklogback-coreorg.mybatis.spring.bootmybatis-spring-boot-starterorg.springframework.bootspring-boot-testorg.springframework.bootspring-boot-starter-weborg.springframework.bootspring-boot-starter-jettyorg.springframework.bootspring-boot-devtools
application.yaml
server:
port: 8001
#mybatis 配置
mybatis:
type-aliases-package: com.zhou.springcloud.pojo
config-location: classpath:mybatis/mybatis-config.xml
mapper-locations: classpath:mybatis/mapper/*.xml
#speing 配置
spring:
application:
name: springcloud-provider-dept
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/db01?useUnicode=true&characterEncoding=utf-8
username: root
password: root
mybatis-config.xml
DeptMapper 接口
package com.zhou.springcloud.mapper;
import com.zhou.springcloud.pojo.Dept;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;
import java.util.List;
@Mapper
@Repository
public interface DeptMapper {
boolean addDept(Dept dept);
Dept queryById(@Param("id") Long id);
ListqueryAll();
}
DeptMapper.xml
insert into dept (dname, db_source)
values (#{dname},DATABASE());select * from dept where deptno=#{id};select * from dept
DeptService 接口
package com.zhou.springcloud.service;
import com.zhou.springcloud.pojo.Dept;
import java.util.List;
public interface DeptService {
boolean addDept(Dept dept);
Dept queryById(Long id);
ListqueryAll();
}
DeptServiceImpl.java
package com.zhou.springcloud.service;
import com.zhou.springcloud.mapper.DeptMapper;
import com.zhou.springcloud.pojo.Dept;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class DeptServiceImpl implements DeptService{
@Autowired
private DeptMapper deptMapper;
@Override
public boolean addDept(Dept dept) {
return deptMapper.addDept(dept);
}
@Override
public Dept queryById(Long id) {
return deptMapper.queryById(id);
}
@Override
public ListqueryAll() {
return deptMapper.queryAll();
}
}
DeptController.java
package com.zhou.springcloud.controller;
import com.zhou.springcloud.pojo.Dept;
import com.zhou.springcloud.service.DeptService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
//提供Restful服務
@RestController
public class DeptController {
@Autowired
private DeptService deptService;
@GetMapping("/dept/add")//友善此時的測試,這裡用了Get,沒有用Post
public boolean addDept(Dept dept) {
return deptService.addDept(dept);
}
@GetMapping("/dept/get/{id}")
public Dept getDept(@PathVariable("id") Long id){
return deptService.queryById(id);
}
@GetMapping("/dept/list")
public ListqueryAll(){
return deptService.queryAll();
}
}
6.啟動類
package com.zhou.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
//啟動類
@SpringBootApplication
public class DeptProvider_8001 {
public static void main(String[] args) {
SpringApplication.run(DeptProvider_8001.class,args);
}
}
7.Run 測試
通路:http://localhost:8001/dept/list (測試成功!)
通路:http://localhost:8001/dept/get/1 (測試成功!)
通路:http://localhost:8001/dept/add?dname=地獄部 (測試成功!)
查詢資料庫
1.建立子子產品 springcloud-consumer-dept-80 (服務的消費者)
springcloudcom.zhou1.0-SNAPSHOT4.0.0springcloud-consumer-dept-80com.zhouspringcloud-api1.0-SNAPSHOTorg.springframework.bootspring-boot-starter-weborg.springframework.bootspring-boot-devtools
application.yml
server:
port: 80
ConfigBean.java
package com.zhou.springcloud.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
@Configuration //@Configuration 相當于 spring中 applicationContext.xml
public class ConfigBean {
@Bean
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
RestTemplate 部分源碼:
DeptConsumerController.java
package com.zhou.springcloud.controller;
import com.zhou.springcloud.pojo.Dept;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.client.RestTemplate;
import java.util.List;
@Controller
public class DeptConsumerController {
//了解:消費者,不應該有 service 層
//RestTemplate.... 供我們直接調用就可以了,注冊到Spring中
@Autowired
private RestTemplate restTemplate;
//http://localhost:8081/dept/add?dname=地獄部
private static final String REST_URL_PREFIX="http://localhost:8081";
@RequestMapping("/consumer/dept/add")
@ResponseBody
public boolean add(Dept dept){
return restTemplate.postForObject(REST_URL_PREFIX + "/dept/add", dept,Boolean.class);
}
@RequestMapping("/consumer/dept/get/{id}")
@ResponseBody
public Dept get(@PathVariable("id") Long id){
return restTemplate.getForObject(REST_URL_PREFIX + "/dept/get/" + id, Dept.class);
}
@RequestMapping("/consumer/dept/list")
@ResponseBody
public Listlist(){
return restTemplate.getForObject(REST_URL_PREFIX + "/dept/list", List.class);
}
}
DeptConsumer_80.java 啟動類
package com.zhou.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class DeptConsumer_80 {
public static void main(String[] args) {
SpringApplication.run(DeptConsumer_80.class,args);
}
}
2.修改:springcloud-provider-dept-8001 它的 DeptController.java類的addDept()方法
(否則,在下面測試中,使用浏覽器url傳參的方式來插入資料時,資料庫中顯示dname的值為null)
@PostMapping("dept/add")
public boolean addDept(@RequestBody Dept dept){
return deptService.addDept(dept);
}
3.啟動
首先,啟動服務的提供者 springcloud-provider-dept-8001 的啟動類 DeptProvider_8001
其次,啟動服務的消費者 springcloud-consumer-dept-80 的啟動類 DeptConsumer_80
4.Run 測試
通路:http://localhost/consumer/dept/list
通路:http://localhost/consumer/dept/get/5
通路:http://localhost/consumer/dept/add?dname=架構師3
檢視資料庫
五、Eureka 服務注冊中心
- Eureka 定義
Eureka 是Netflix的一個子子產品,也是核心子產品之一。Eureka是基于REST的服務,用于定位服務,以實作雲端中間件層服務發現和故障轉移,服務注冊與發現對于微服務來說是非常重要的,有了服務注冊與發現,隻需要使用服務的辨別符,就可以通路到服務,而不需要修改服務調用的配置檔案了,功能類似于Dubbo的注冊中心,比如Zookeeper。
在雲中,應用程式不能總是知道其他服務的确切位置。一個服務注冊中心,比如Netflix Eureka,或者一個sidecar解決方案,比如HashiCorp Consul,都會有所幫助。springcloud為流行的注冊中心提供DiscoveryClient實作,比如Eureka、Consul、Zookeeper,甚至Kubernetes的内置系統。還有一個springcloud負載均衡器可以幫助您在服務執行個體之間小心地配置設定負載。
官方介紹:https://spring.io/projects/spring-cloud-netflix
springcloudnetflix通過自動配置并綁定到Spring環境和其他Spring程式設計模型習慣用法,為Spring啟動應用程式提供Netflix作業系統內建。通過一些簡單的注釋,您可以快速啟用和配置應用程式中的常見模式,并使用經過測試的Netflix元件建構大型分布式系統。提供的模式包括服務發現(Eureka)、斷路器(Hystrix)、智能路由(Zuul)和用戶端負載平衡(Ribbon)
- Dubbo 和 SpringCloud對比
最大差別:Spring Cloud 抛棄了Dubbo的RPC通信,采用的是基于HTTP的REST方式。
二者解決的問題域不一樣:Dubbo的定位是一款RPC架構,而SpringCloud的目标是微服務架構下的一站式解決方案。
Dubbo | SpringCloud | |
---|---|---|
服務注冊中心 | Zookeeper | Spring Cloud Netfilx Eureka |
服務調用方式 | RPC | REST API |
Dubbo-monitor | Spring Boot Admin | |
斷路器 | 不完善 | Spring Cloud Netfilx Hystrix |
服務網關 | 無 | Spring Cloud Netfilx Zuul |
分布式配置 | Spring Cloud Config | |
服務跟蹤 | Spring Cloud Sleuth | |
消息總棧 | Spring Cloud Bus | |
資料流 | Spring Cloud Stream | |
批量任務 | Spring Cloud Task |
嚴格來說,這兩種方式各有優劣。雖然從一定程度來說,SpringCloud犧牲了服務調用的性能,但也避免了上面提到的原生RPC帶來的問題。而且REST相比RPC更為靈活,服務提供方和調用方的依賴隻依靠一紙契約,不存在代碼級别的強依賴,這個優點在當下強調快速演化的微服務環境下,顯得更加合适。
- Eureka基本的架構
1.Springcloud 封裝了Netflix公司開發的Eureka子產品來實作服務注冊與發現 (對比Zookeeper).
2.Eureka采用了C-S的架構設計,EurekaServer作為服務注冊功能的伺服器,他是服務注冊中心.
3.系統中的其他微服務,使用Eureka的用戶端連接配接到EurekaServer并維持心跳連接配接。(友善監控系統中各個微服務是否正常運作)
- 與Dubbo架構對比
- 建構 Eureka 代碼示例
1.建立子子產品 springcloud-eureka-7001
springcloud-eureka-7001
org.springframework.cloud
spring-cloud-starter-netflix-eureka-server
2.2.7.RELEASE
org.springframework.boot
spring-boot-devtools
server:
port: 7001
# Eureka 部署
eureka:
instance:
# Eureka服務端的執行個體名字
hostname: localhost
client:
# 表示是否向 Eureka 注冊中心注冊自己(這個子產品本事是伺服器,是以不需要)
register-with-eureka: false
# fetch-registry 如果為 false,則表示自己為注冊中心,用戶端的為 true
fetch-registry: false
# Eureka 監控頁面
service-url:
#public static final String DEFAULT_URL = "http://localhost:8761/eureka/";
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
啟動類 EurekaServer_7001.java
package com.zhou.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
/**
* @Auther: zhouzhou
* @Description: 啟動之後,通路 http://127.0.0.1:7001/
*/
@SpringBootApplication
@EnableEurekaServer //服務端的啟動類,可以接受别人注冊進來
public class EurekaServer_7001 {
public static void main(String[] args) {
SpringApplication.run(EurekaServer_7001.class,args);
}
}
Run 測試
報錯資訊:Caused by: java.lang.ClassNotFoundException: org.springframework.boot.context.properties.ConfigurationPropertiesBean
觀察報錯資訊,得出有可能是版本沖突問題,解決辦法是将 spring-cloud-starter-netflix-eureka-server 依賴的版本降為2.1.4.RELEASE
【注意】:報錯原因是版本問題,可以選擇到官網檢視版本是否一緻,比如 SPRINGCLOUD的版本,我的父依賴用的是GREENWICH.SR1
你如果用了HOXTON.SR10 甚至更新的,請自行查找對應的版本依賴。
繼續測試,Run 成功!
通路:http://localhost:7001/
- Eureka 服務注冊 資訊配置以及自我保護機制
配置 Eureka-client
1.在上面建立的子子產品 springlouc-provider-dept-8001 的 pom 中添加依賴
org.springframework.cloud
spring-cloud-starter-netflix-eureka-client
2.1.4.RELEASE
2.application.yaml 中添加設定
# Eureka配置:配置注冊中心位址
eureka:
client:
service-url:
defaultZone: http://localhost:7001/eureka/
3.啟動類中使用 @EnableEurekaClient注解
package com.zhou.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
//啟動類
@SpringBootApplication
@EnableEurekaClient
public class DeptProvider_8001 {
public static void main(String[] args) {
SpringApplication.run(DeptProvider_8001.class,args);
}
}
4.先啟動7001服務端,再啟動8001用戶端進行測試,通路監控頁:http://localhost:7001/ 産看結果如圖,成功
5.修改 Eureka 上的預設描述資訊
# Eureka配置:配置注冊中心位址
eureka:
client:
service-url:
defaultZone: http://localhost:7001/eureka/
instance:
instance-id: springcloud-provider-dept8001 # 修改eureka上的預設描述資訊
prefer-ip-address: true
檢視:
6.配置關于服務加載的監控資訊(springcloud-provider-dept-8001)
沒配置之前,通路:springcloud-provider-dept8001
跳出的頁面如下:
配置服務加載的監控資訊步驟如下:
pom.xml中添加依賴
org.springframework.boot
spring-boot-starter-actuator
application.yaml 中添加配置
# info配置
info:
# 項目名稱
app.name: zhouzhou-springcloud
# 公司名稱
company.name: blog.zhouzhou.com
7.Run 再重新整理頁面,繼續通路:springcloud-provider-dept8001,跳出頁面如下
- EureKa自我保護機制
預設情況下,如果Eureka Server在90秒内沒有接收到某個微服務執行個體的心跳,會登出該執行個體。但是在微服務架構下服務之間通常都是跨程序調用,網絡通信往往面臨很多問題,比如微服務狀态正常,網絡分區故障,導緻此執行個體被登出。
固定時間内大量執行個體被登出,可能會嚴重威脅某個微服務架構的可用性,為了解決這個問題,Eureka開發了自我保護機制。
Eureka Server在運作期間會去統計心跳失敗比例在15分鐘之内是否低于85%,如果低于85%,Eureka Server即進入自我保護機制。
Eureka Server觸發自我保護機制後,頁面會出現提示:
Eureka Server進入自我保護機制,會出現以下幾種情況:
(1)Eureka不再從注冊清單中移除因為長時間沒收到心跳而應該過期的服務;
(2)Eureka仍然能夠接受新服務的注冊和查詢,但是不會被同步到其它節點上(即保證目前節點依然可用);
(3)當網絡穩定後,目前執行個體新的注冊資訊會被同步到其它節點上;
Eureka自我保護機制是為了防止誤殺服務而提供的一種機制。當個别用戶端出現心跳失聯時,則認為是用戶端的問題,剔除用戶端;當Eureka 捕獲到大量的心跳失敗時,則認為可能是網絡問題,進入自我保護機制;當用戶端心跳恢複時,Eureka會自動退出自我保護機制。
如果在保護期内剛好這個服務提供者非正常下線了,此時服務消費者就會拿到一無效的服務執行個體,則會調用失敗。對于這個問題需要服務消費者要有一些容錯機制,比如重試,斷路器等。
- 注冊進來的微服務,擷取其中的一些資訊(團隊開發)
1.檢視 EurekaDiscoveryClient 源碼:
2.觀察 DiscoveryClient 源碼:
3.在 springcloud-provider-dept-8001 的 DeptController.java中添加 discovery() 方法
//DiscoveryClient 可以用來擷取一些配置的資訊,得到具體的微服務
@Autowired
private DiscoveryClient discoveryClient;
/**
* 擷取一些注冊進來的微服務的資訊
* @return
*/
@GetMapping("dept/discovery")
public Object discovery(){
//擷取微服務清單清單
System.out.println("getServices()=>"+discoveryClient.getServices());
System.out.println("description()=>"+discoveryClient.description());
Listinstances = discoveryClient.getInstances("SPRINGCLOUD-PROVIDER-DEPT");
for (ServiceInstance instance : instances) {
System.out.println(
instance.getHost()+"\t"+ //主機名稱
instance.getPort()+"\t"+ //端口号
instance.getUri()+"\t"+ //uri
instance.getInstanceId() //服務id
);
}
return this.discoveryClient;
}
4.上面 discoveryClient.getInstances()的參數 ---> SPRINGCLOUD-PROVIDER-DEPT
5.主啟動類中加入 @EnableDiscoveryClient 注解
package com.zhou.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
//啟動類
@SpringBootApplication
//@EnableEurekaClient 開啟Eureka用戶端注解,在服務啟動後自動向注冊中心注冊服務
@EnableEurekaClient
//@EnableDiscoveryClient 開啟服務發現用戶端的注解,可以用來擷取一些配置的資訊,得到具體的微服務
@EnableDiscoveryClient
public class DeptProvider_8001 {
public static void main(String[] args) {
SpringApplication.run(DeptProvider_8001.class,args);
}
}
6.Run 測試
先啟動 springcloud-eureka-7001 中的住啟動類 EurekaServer_7001,
再啟動 springcloud-provider-dept-8001 中的主啟動類 DeptProvider_8001,
通路:http://localhost:7001/ 一切正常
繼續通路:http://localhost:8001/dept/discovery
springcloud-provider-dept-8001 控制台輸出:
- Eureka:叢集環境配置
整體結構如下:
1.建立 子子產品springcloud-eureka-7002 和 springcloud-eureka-7003
2.添加 pom 依賴 (與springcloud-eureka-7001相同)
3.application.yml配置 (與springcloud-eureka-7001相同)
(端口号用各自的 7001、7002和7003)
4.主啟動類 (與springcloud-eureka-7001相同)
5.叢集成員互相關聯
配置一些自定義本機名字,在C:\Windows\System32\drivers\etc找到本機hosts檔案,在hosts檔案最後加上,要通路的本機名稱(預設是localhost)
127.0.0.1 eureka7001.com
127.0.0.1 eureka7002.com
127.0.0.1 eureka7003.com
【注意】:修改hosts檔案後一定要儲存,如果遇到 修改hosts檔案無權限的問題,參考下圖配置:
6.修改 各自的 application.yml 的配置
(1)設定各自的 服務端的執行個體名字(hostname)
(2)設定各自的 叢集(關聯)
springcloud-eureka-7001 的 application.yaml
server:
port: 7001
# Eureka 部署
eureka:
instance:
# Eureka服務端的執行個體名字
hostname: eureka7001.com
client:
# 表示是否向 Eureka 注冊中心注冊自己(這個子產品本事是伺服器,是以不需要)
register-with-eureka: false
# fetch-registry 如果為 false,則表示自己為注冊中心,用戶端的為 true
fetch-registry: false
# Eureka 監控頁面
service-url:
# public static final String DEFAULT_URL = "http://localhost:8761/eureka/";
# 單機:defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
# 叢集(關聯):7001關聯 7002、7003
defaultZone: http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
springcloud-eureka-7002 的 application.yaml
server:
port: 7002
eureka:
instance:
# Eureka服務端的執行個體名字
hostname: eureka7002.com
client:
register-with-eureka: false
fetch-registry: false
service-url:
# 叢集(關聯):7002關聯 7001、7003
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7003.com:7003/eureka/
springcloud-eureka-7003 的 application.yaml
server:
port: 7003
eureka:
instance:
# Eureka服務端的執行個體名字
hostname: eureka7003.com
client:
register-with-eureka: false
fetch-registry: false
service-url:
# 叢集(關聯):7003關聯 7001、7002
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/
7.通過 springcloud-provider-dept-8001 的yaml配置檔案,修改 Eureka 的配置:配置服務注冊中心位址
# Eureka配置:配置注冊中心位址
eureka:
client:
service-url:
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
instance:
instance-id: springcloud-provider-dept8001 # 修改eureka上的預設描述資訊
prefer-ip-address: true
8.模拟叢集搭建完畢。(可以把一個項目挂載到三個伺服器上了)
測試,通路:http://localhost:7001/
測試,通路:http://localhost:7002/
測試,通路:http://localhost:7003/
- Eureka與Zookeeper 的對比
1.CAP原則
- RDBMS (MySQL\Oracle\sqlServer) ===> ACID
- NoSQL (Redis\MongoDB) ===> CAP
2.ACID是什麼
- A (Atomicity) 原子性
- C (Consistency) 一緻性
- I (Isolation) 隔離性
- D (Durability) 持久性
3.CAP是什麼
- A (Availability) 可用性
- P (Partition tolerance) 分區容錯性
CAP的三進二:CA、AP、CP
4.CAP理論的核心
一個分布式系統不可能同時很好的滿足一緻性,可用性和分區容錯性這三個需求
根據CAP原理,将NoSQL資料庫分成了滿足CA原則,滿足CP原則和滿足AP原則三大類
- CA:單點叢集,滿足一緻性,可用性的系統,通常可擴充性較差
- CP:滿足一緻性,分區容錯的系統,通常性能不是特别高
- AP:滿足可用性,分區容錯的系統,通常可能對一緻性要求低一些
5.作為分布式服務注冊中心,Eureka比Zookeeper好在哪裡?
著名的CAP理論指出,一個分布式系統不可能同時滿足C (一緻性) 、A (可用性) 、P (容錯性),由于分區容錯性P再分布式系統中是必須要保證的,是以我們隻能再A和C之間進行權衡。
Zookeeper 保證的是 CP —> 滿足一緻性,分區容錯的系統,通常性能不是特别高
Eureka 保證的是 AP —> 滿足可用性,分區容錯的系統,通常可能對一緻性要求低一些
- Zookeeper保證的是CP
當向注冊中心查詢服務清單時,我們可以容忍注冊中心傳回的是幾分鐘以前的注冊資訊,但不能接收服務直接down掉不可用。也就是說,服務注冊功能對可用性的要求要高于一緻性。但zookeeper會出現這樣一種情況,當master節點因為網絡故障與其他節點失去聯系時,剩餘節點會重新進行leader選舉。問題在于,選舉leader的時間太長,30-120s,且選舉期間整個zookeeper叢集是不可用的,這就導緻在選舉期間注冊服務癱瘓。在雲部署的環境下,因為網絡問題使得zookeeper叢集失去master節點是較大機率發生的事件,雖然服務最終能夠恢複,但是,漫長的選舉時間導緻注冊長期不可用,是不可容忍的。
- Eureka保證的是AP
Eureka看明白了這一點,是以在設計時就優先保證可用性。Eureka各個節點都是平等的,幾個節點挂掉不會影響正常節點的工作,剩餘的節點依然可以提供注冊和查詢服務。而Eureka的用戶端在向某個Eureka注冊時,如果發現連接配接失敗,則會自動切換至其他節點,隻要有一台Eureka還在,就能保住注冊服務的可用性,隻不過查到的資訊可能不是最新的,除此之外,Eureka還有之中自我保護機制,如果在15分鐘内超過85%的節點都沒有正常的心跳,那麼Eureka就認為用戶端與注冊中心出現了網絡故障,此時會出現以下幾種情況:
(1)Eureka不在從注冊清單中移除因為長時間沒收到心跳而應該過期的服務
(2)Eureka仍然能夠接受新服務的注冊和查詢請求,但是不會被同步到其他節點上 (即保證目前節點依然可用)
(3)當網絡穩定時,目前執行個體新的注冊資訊會被同步到其他節點中
是以,Eureka可以很好的應對因網絡故障導緻部分節點失去聯系的情況,而不會像zookeeper那樣使整個注冊服務癱瘓。
六、Ribbon:負載均衡(基于用戶端)
Spring Cloud Ribbon 是基于Netflix Ribbon 實作的一套用戶端負載均衡的工具。
Ribbon 是 Netflix 釋出的開源項目,主要功能是提供用戶端的軟體負載均衡算法,将 Netflix 的中間層服務連接配接在一起。Ribbon 的用戶端元件提供一系列完整的配置項,如:連接配接逾時、重試等。
- LB,即負載均衡 (LoadBalancer) ,在微服務或分布式叢集中經常用的一種應用。
- 負載均衡簡單的說就是将使用者的請求平攤的配置設定到多個服務上,進而達到系統的HA (高用)。
- 常見的負載均衡軟體有 Nginx、Lvs 等等。
- Dubbo、SpringCloud 中均給我們提供了負載均衡,SpringCloud 的負載均衡算法可以自定義。
負載均衡簡單分類:
(1)集中式LB
即在服務的提供方和消費方之間使用獨立的LB設施,如Nginx(反向代理伺服器),由該設施負責把通路請求通過某種政策轉發至服務的提供方!
(2) 程序式 LB
将LB邏輯內建到消費方,消費方從服務注冊中心獲知有哪些位址可用,然後自己再從這些位址中選出一個合适的伺服器。
Ribbon 就屬于程序内LB,它隻是一個類庫,內建于消費方程序,消費方通過它來擷取到服務提供方的位址。
- 內建 Ribbon
向子子產品 springcloud-consumer-dept-80 中的 pom 檔案中添加Ribbon和Eureka依賴
org.springframework.cloudspring-cloud-starter-ribbon1.4.6.RELEASEorg.springframework.cloudspring-cloud-starter-eureka1.4.6.RELEASE
在application.yaml檔案中配置 Eureka
# Eureka 配置
eureka:
client:
register-with-eureka: false # 不向 Eureka 注冊自己
service-url: # 從三個注冊中心随機取一個去通路
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
主啟動類加上@EnableEurekaClient注解,開啟 Eureka
package com.zhou.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
//Ribbon 和 Eureka 整合以後,用戶端可以直接調用,不用關心IP位址和端口号
@SpringBootApplication
@EnableEurekaClient //開啟 Eureka 用戶端
public class DeptConsumer_80 {
public static void main(String[] args) {
SpringApplication.run(DeptConsumer_80.class,args);
}
}
自定義Spring配置類:ConfigBean.java 配置負載均衡實作RestTemplate
package com.zhou.springcloud.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
@Configuration //@Configuration 相當于 spring中 applicationContext.xml
public class ConfigBean {
//配置負載均衡實作RestTemplate
@Bean
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
修改conroller:DeptConsumerController.java
//http://localhost:8081/dept/add?dname=地獄部
//private static final String REST_URL_PREFIX="http://localhost:8081";
private static final String REST_URL_PREFIX = "http://SPRINGCLOUD-PROVIDER-DEPT";
- 使用Ribbon實作負載均衡