Spring Cloud
- 1· Eureka注冊中心
-
- 什麼是Eureka
- 基礎架構
- Eureka原理圖
- 編寫EurekaServer
-
- 添加eureka依賴
- 編寫啟動類
- 編寫配置yml配置檔案
- 服務注冊
-
- 服務提供端添加依賴
- 在啟動類上開啟Eureka用戶端功能
- 編寫配置
- 服務發現
-
- 在用戶端添加依賴
- 在啟動類添加開啟Eureka用戶端發現的注解
- 編寫配置
- 修改代碼,用DiscoveryClient類的方法,根據服務名稱,擷取服務執行個體
- 失效剔除和自我保護
-
- 服務下線
- 失效剔除
- 自我保護
-
- 關閉自我保護模式
- 負載均衡Ribbon
-
- 什麼是Ribbon
- 開啟負載均衡
- 修改負載均衡算法
- Hystrix
-
- 雪崩問題
- 線程隔離&服務降級
- 編寫 Hystrix服務降級
- 服務熔斷
-
- 熔斷原理
- 熔斷原理圖
- 改熔斷政策
- Feign
-
- 負載均衡
- Hystrix支援
- 請求壓縮
- 日志級别
- Spring Cloud Gateway網關
-
- Gateway加入後的架構
- spring cloud config
- spring cloud bus
Spring Cloud是Spring旗下的項目之一,它将現在非常流行的一些技術整合到一起,實作了諸如:配置管理,服務發現,智能路由,負載均衡,熔斷器,控制總線,叢集狀态等等功能。其主要涉及的元件包括:
- Eureka:注冊中心
- Zuul:服務網關(現在常用gateway)
- Ribbon:負載均衡
- Feign:服務調用
- Hystrix:熔斷器
1· Eureka注冊中心
在介紹Eureka注冊中心前,我們先建立兩個模型:服務提供者(user-service),服務消費者(consumer)。使用者操作消費者子產品來調用提供者子產品通路資料。
什麼是Eureka
Eureka是基于REST(Representational State Transfer)服務,主要以AWS雲服務為支撐,提供服務發現并實作負載均衡和故障轉移。我們稱此服務Eureka服務。Eureka提供了Java用戶端元件,Eureka Client,友善與服務端的互動。用戶端内置了基于round-robin實作的簡單負載均衡。在Netflix,為Eureka提供更為複雜的負載均衡方案進行封裝,以實作高可用,它包括基于流量、資源使用率以及請求傳回狀态的權重負載均衡。
基礎架構
Eureka架構中的三個核心角色:
- 服務注冊中心:Eureka的服務端應用,提供服務注冊和發現功能。
- 服務提供者:提供服務的應用,可以是Spring Boot應用,也可以是其它任意技術實作,隻要對外提供的是REST風格服務即可。
- 服務消費者:消費應用從注冊中心擷取服務清單,進而得知每個服務方的資訊,知道去哪裡調用服務方。
Eureka原理圖

- Eureka:就是服務注冊中心(可以是一個叢集),對外暴露自己的位址
- 提供者:啟動後向Eureka注冊自己資訊(位址,提供什麼服務)
- 消費者:向Eureka訂閱服務,Eureka會将對應服務的所有提供者位址清單發送給消費者,并且定期更新
- 心跳(續約):提供者定期通過HTTP方式向Eureka重新整理自己的狀态
編寫EurekaServer
添加eureka依賴
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
</dependencies>
編寫啟動類
//聲明目前應用時Eureka服務
@EnableEurekaServer
@SpringBootApplication
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class, args);
}
}
編寫配置yml配置檔案
server:
#如果jvm參數存在則使用參數port下面同理 ${port:10086}
port: 10086
ng:
application:
name: eureka-server
eureka:
client:
service-url:
#eureka服務的位址,如果做叢集,需要指定其他的eureka位址 ${defaultZone:http://127.0.0.1:10086/eureka}
defaultZone: http://127.0.0.1:10086/eureka
#不注冊自己
register-with-eureka: false
#不拉取服務
fetch-registry: false
服務注冊
服務提供端添加依賴
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency
在啟動類上開啟Eureka用戶端功能
通過添加 @EnableDiscoveryClient 來開啟Eureka用戶端功能
@SpringBootApplication
@MapperScan("com.lxs.user.mapper")
@EnableDiscoveryClient //開啟Eureka用戶端發現功能
public class UserApplication {
public static void main(String[] args) {
SpringApplication.run(UserApplication.class, args);
}
}
編寫配置
server:
port: ${port:9999}
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/springcloud?useSSL=false
username: root
password: 123456
application:
name: user-service
mybatis:
type-aliases-package: com.yh.user.pojo
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:10086/eureka
#http://127.0.0.1:10086/eureka,http://127.0.0.1:10087/eureka
不用指定register-with-eureka和fetch-registry,因為預設是true
服務發現
在用戶端添加依賴
<!-- Eureka用戶端 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
在啟動類添加開啟Eureka用戶端發現的注解
@SpringBootApplication
@EnableDiscoveryClient // 開啟Eureka用戶端
public class UserConsumerDemoApplication {
@Bean
public RestTemplate restTemplate() {
return new RestTemplate(new OkHTTP3ClientHTTPRequestFactory());
}
public static void main(String[] args) {
SpringApplication.run(UserConsumerDemoApplication.class, args);
}
}
編寫配置
spring:
application:
name: consumer-demo
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:10086/eureka
修改代碼,用DiscoveryClient類的方法,根據服務名稱,擷取服務執行個體
@RestController
@RequestMapping("/consumer")
public class ConsumerController {
@Autowired
private RestTemplate restTemplate;
@GetMapping("{id}")
public String queryById(@PathVariable Long id) {
String url = "http://user-service/user/" + id;
return restTemplate.getForObject(url, String.class);
}
}
失效剔除和自我保護
服務下線
當服務進行正常關閉操作時,它會觸發一個服務下線的REST請求給Eureka Server,告訴服務注冊中心:“我要下線
了”。服務中心接受到請求之後,将該服務置為下線狀态
失效剔除
有時我們的服務可能由于記憶體溢出或網絡故障等原因使得服務不能正常的工作,而服務注冊中心并未收到“服務下線”的請求。相對于服務提供者的“服務續約”操作,服務注冊中心在啟動時會建立一個定時任務,預設每隔一段時間
(預設為60秒)将目前清單中逾時(預設為90秒)沒有續約的服務剔除,這個操作被稱為失效剔除。 可以通過eureka.server.eviction-interval-timer-in-ms 參數對其進行修改,機關是毫秒。
自我保護
當關停一個服務,就會在Eureka面闆看到一條警告:
這是觸發了Eureka的自我保護機制。當一個服務未按時進行心跳續約時,Eureka會統計最近15分鐘心跳失敗的服務執行個體的比例是否超過了85%,當EurekaServer節點在短時間内丢失過多用戶端(可能發生了網絡分區故障)。在生産環境下,因為網絡延遲等原因,心跳失敗執行個體的比例很有可能超标,但是此時就把服務剔除清單并不妥當,因為服務可能沒有當機。Eureka就會把目前執行個體的注冊資訊保護起來,不予剔除。生産環境下這很有效,保證了大多
數服務依然可用。
關閉自我保護模式
eureka:
server:
enable-self-preservation: false # 關閉自我保護模式(預設為打開)
eviction-interval-timer-in-ms: 1000 # 掃描失效服務的間隔時間(預設為60*1000ms)
負載均衡Ribbon
什麼是Ribbon
根據伺服器名從eureka-server中獲得位址清單,Ribbon會根據負載均衡算法從位址清單中得到一個位址進行通路。算法分為輪詢和随機。預設為輪詢
開啟負載均衡
因為Eureka中已經內建了Ribbon,是以我們無需引入新的依賴。直接修改代碼:
在RestTemplate的配置方法上添加 @LoadBalanced 注解:
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
修改負載均衡算法
user-service:
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
Hystrix
Hystix是Netflix開源的一個延遲和容錯庫,用于隔離通路遠端服務,防止出現級聯失敗。
雪崩問題
微服務中,服務間調用關系錯綜複雜,一個請求,可能需要調用多個微服務接口才能實作,會形成非常複雜的調用鍊路。如果此時某個微服務發生異常,請求阻塞,使用者請求就不會得到響應,則tomcat的這個線程不會釋放,于是越來越多的使用者請求到來,越來越多的線程會阻塞。伺服器支援的線程和并發數有限,請求一直阻塞,會導緻伺服器資源耗盡,進而導緻所有其它服務都不可用,形成雪崩效應。
這就好比,一個汽車生産線,生産不同的汽車,需要使用不同的零件,如果某個零件因為種種原因無法使用,那麼就會造成整台車無法裝配,陷入等待零件的狀态,直到零件到位,才能繼續組裝。 此時如果有很多個車型都需要這
個零件,那麼整個工廠都将陷入等待的狀态,導緻所有生産都陷入癱瘓。一個零件的波及範圍不斷擴大。
Hystrix解決雪崩問題的手段,主要包括:
線程隔離
服務降級
線程隔離&服務降級
線程隔離:
Hystrix為每個依賴服務調用配置設定一個小的線程池,如果線程池已滿調用将被立即拒絕,預設不采用排隊,加速失敗判定時間。
使用者的請求将不再直接通路服務,而是通過線程池中的空閑線程來通路服務,如果線程池已滿,或者請求逾時,則會進行降級處理。
服務降級:
服務降級可以優先保證核心服務。使用者的請求故障時,不會被阻塞,更不會無休止的等待或者看到系統崩潰,至少可以看到一個執行結果(例如傳回友好的提示資訊)。服務降級雖然會導緻請求失敗,但是不會導緻阻塞,而且最多會影響這個依賴服務對應的線程池中的資源,對其它服務沒有響應。
觸發Hystrix服務降級的情況:
- 線程池已滿
- 請求逾時
編寫 Hystrix服務降級
- 引入依賴
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
2.開啟熔斷
在啟動類消費者 ConsumerApplication 上添加注解:@EnableCircuitBreaker
@SpringBootApplication
@EnableDiscoveryClient
@EnableCircuitBreaker
public class ConsumerApplication {
// ...
}
注意:@SpringCloudApplication=
@SpringBootApplication
@EnableDiscoveryClient
@EnableCircuitBreaker
@SpringCloudApplication
public class ConsumerApplication {
// ...
}
3.編寫降級邏輯
當目标服務的調用出現故障,使用HystrixCommand來完成一個快速失敗,并給出使用者一個友好的提示。需要提前編寫好失敗時的降級處理邏輯。
@RestController
@RequestMapping("/consumer")
@Slf4j
public class ConsumerController {
@Autowired
private RestTemplate restTemplate;
@Autowired
private DiscoveryClient discoveryClient;
@GetMapping("{id}")
@HystrixCommand(fallbackMethod = "queryByIdFallback")
public String queryById(@PathVariable Long id) {
String url = "http://user-service/user/" + id;
return restTemplate.getForObject(url, String.class);
}
public String queryByIdFallback(Long id) {
log.error("查詢使用者資訊失敗。id:{}", id);
return "對不起,網絡太擁擠了!";
}
}
注:因為熔斷的降級邏輯方法必須跟正常邏輯方法保證:相同的參數清單和傳回值聲明。
@HystrixCommand(fallbackMethod = “queryByIdFallBack”):用來聲明一個降級邏輯的方法
4.逾時設定
Hystrix的預設逾時時長為1s
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds=2000:
服務熔斷
熔斷原理
- 在服務熔斷中,使用的熔斷器,也叫斷路器,其英文單詞為:Circuit Breaker 熔斷機制與家裡使用的電路熔斷原理類似;當如果電路發生短路的時候能立刻熔斷電路,避免發生災難。在分布式系統中應用服務熔斷後;服務調用方可以自己進行判斷哪些服務反應慢或存在大量逾時,可以針對這些服務進行主動熔斷,防止整個系統被拖垮。
- Hystrix的服務熔斷機制,可以實作彈性容錯;當服務請求情況好轉之後,可以自動重連。通過斷路的方式,将後續請求直接拒絕,一段時間(預設5秒)之後允許部分請求通過,如果調用成功則回到斷路器關閉狀态,否則繼續打開,拒絕請求的服務。
熔斷原理圖
狀态機有3個狀态:
- Closed:關閉狀态(斷路器關閉),所有請求都正常通路。
- Open:打開狀态(斷路器打開),所有請求都會被降級。Hystrix會對請求情況計數,當一定時間内失敗請求百分比達到門檻值,則觸發熔斷,斷路器會完全打開。預設失敗比例的門檻值是50%,請求次數最少不低于20次。
- HalfOpen:半開狀态,不是永久的,斷路器打開後會進入休眠時間(預設是5S)。随後斷路器會自動進入半開狀态。此時會釋放部分請求通過,若這些請求都是健康的,則會關閉斷路器,否則繼續保持打開,再次進行休眠計時
改熔斷政策
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds= 5000:
circuitBreaker:
errorThresholdPercentage: 50 # 觸發熔斷錯誤比例門檻值,預設值50%
sleepWindowInMilliseconds: 10000 # 熔斷後休眠時長,預設值5秒
requestVolumeThreshold: 10 # 熔斷觸發最小請求次數,預設值是20
Feign
Feign可以把Rest的請求進行隐藏,僞裝成類似SpringMVC的Controller一樣。不用再自己拼接url,拼接參數等等操作,一切都交給Feign去做。
1 .Feign依賴
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
2.Feign的用戶端
@FeignClient("user-service")
public interface UserClient {
//http://user-service/user/123
@GetMapping("/user/{id}")
User queryById(@PathVariable("id") Long id);
}
- 首先這是一個接口,Feign會通過動态代理,幫我們生成實作類。這點跟Mybatis的mapper很像
- @FeignClient ,聲明這是一個Feign用戶端,同時通過 value 屬性指定服務名稱
- 接口中的定義方法,完全采用SpringMVC的注解,Feign會根據注解幫我們生成URL,并通路擷取結果
- @GetMapping中的/user,請不要忘記;因為Feign需要拼接可通路的位址
編寫新的控制器類 ConsumerFeignController ,使用UserClient通路:
@RestController
@RequestMapping("/cf")
public class ConsumerFeignController {
@Autowired
private UserClient userClient;
@GetMapping("/{id}")
public User queryById(@PathVariable Long id){
return userClient.queryById(id);
}
}
3.開啟Feign功能
在 ConsumerApplication 啟動類上,添加注解,開啟Feign功能
@SpringCloudApplication
@EnableFeignClients //開啟feign功能
public class ConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class, args);
}
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
負載均衡
Feign中本身已經內建了Ribbon依賴和自動配置,是以不需要額外引入依賴,也不需要再注冊 RestTemplate 對象。Fegin内置的ribbon預設設定了請求逾時時長,預設是1000ms,可以通過手動配置來修改這個逾時時長:
ribbon:
ReadTimeout: 2000 # 讀取逾時時長
ConnectTimeout: 1000 # 建立連結的逾時時長
或者為某一個具體service指定
user-service
ribbon:
ReadTimeout: 2000 # 讀取逾時時長
ConnectTimeout: 1000 # 建立連結的逾時時長
因為ribbon内部有重試機制,一旦逾時,會自動重新發起請求。可以通過配置來組織發起請求
ribbon:
ConnectTimeout: 1000 # 連接配接逾時時長
ReadTimeout: 2000 # 資料通信逾時時長
MaxAutoRetries: 0 # 目前伺服器的重試次數
MaxAutoRetriesNextServer: 0 # 重試多少次服務
OkToRetryOnAllOperations: false # 是否對所有的請求方式都重試
Hystrix支援
Feign預設有對Hystrix的內建,預設情況下是關閉的。需要通過下面的參數來開啟:
feign:
hystrix:
enabled: true # 開啟Feign的熔斷功能
首先,要定義一個類,實作剛才編寫的UserFeignClient,作為fallback的處理類
@FeignClient("user-service")
public interface UserClient {
//http://user-service/user/123
@GetMapping("/user/{id}")
User queryById(@PathVariable("id") Long id);
}
@Component
public class UserClientFallback implements UserClient {
@Override
public User queryById(Long id) {
User user = new User();
user.setId(id);
user.setName("使用者異常");
return user;
}
}
然後在UserClient中,指定剛才編寫的實作類
@FeignClient(value = "user-service", fallback = UserFeignClientFallback.class)
public interface UserFeignClient {
@GetMapping("/user/{id}")
User queryUserById(@PathVariable("id") Long id);
}
請求壓縮
Spring Cloud Feign 支援對請求和響應進行GZIP壓縮,以減少通信過程中的性能損耗。通過下面的參數即可開啟請求與響應的壓縮功能。也可以對請求的資料類型,以及觸發壓縮的大小下限進行設定:
feign:
compression:
request:
enabled: true # 開啟請求壓縮
mime-types: text/html,application/xml,application/json # 設定壓縮的資料類型
min-request-size: 2048 # 設定觸發壓縮的大小下限
response:
enabled: true # 開啟響應壓縮
日志級别
可以通過 logging.level.lxs.xx=debug 來設定日志級别。然而這個對Fegin用戶端而言不會産生效果。因為@FeignClient 注解修改的用戶端在被代理時,都會建立一個新的Fegin.Logger執行個體。我們需要額外指定這個日志的級别才可以。
1.在 consumer-demo 的配置檔案中設定com.xxx包下的日志級别都為 debug添加如下配置:
logging:
level:
com.xxx: debug
2.編寫配置類,定義日志級别
@Configuration
public class FeignConfig {
@Bean
Logger.Level feignLoggerLevel(){
//記錄所有請求和響應的明細,包括頭資訊、請求體、中繼資料
return Logger.Level.FULL;
}
}
這裡指定的Level級别是FULL,Feign支援4種級别:
- NONE:不記錄任何日志資訊,這是預設值。
- BASIC:僅記錄請求的方法,URL以及響應狀态碼和執行時間
- HEADERS:在BASIC的基礎上,額外記錄了請求和響應的頭資訊
- FULL:記錄所有請求和響應的明細,包括頭資訊、請求體、中繼資料
3.在 UserClient 接口類上的@FeignClient注解中指定配置類
@FeignClient(value = "user-service", fallback = UserClientFallback.class, configuration =
FeignConfig.class)
Spring Cloud Gateway網關
- Spring Cloud Gateway是Spring官網基于Spring 5.0、 Spring Boot 2.0、Project Reactor等技術開發的網關服務。
- Spring Cloud Gateway基于Filter鍊提供網關基本功能:安全、監控/埋點、限流等。
- Spring Cloud Gateway為微服務架構提供簡單、有效且統一的API路由管理方式。
- Spring Cloud Gateway是替代Netflix Zuul的一套解決方案。
Spring Cloud Gateway元件的核心是一系列的過濾器,通過這些過濾器可以将用戶端發送的請求轉發(路由)到對應的微服務。 Spring Cloud Gateway是加在整個微服務最前沿的防火牆和代理器,隐藏微服務結點IP端口資訊,進而加強安全保護。Spring Cloud Gateway本身也是一個微服務,需要注冊到Eureka服務注冊中心。
網關的核心功能是:過濾和路由
Gateway加入後的架構
不同于Feign,Feign是不同微服務之間的互相調用請求。Gateway不管是來自于用戶端(PC或移動端)的請求,還是服務内部調用。一切對服務的請求都可經過網關,然後再由網關來實作 鑒權、動态路由等等操作。Gateway就是我們服務的統一入口。
spring cloud config
在碼雲中建立一個倉庫,并添加yml配置檔案
回到Idea,建立一個config子產品
添加config依賴
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
</dependencies>
@SpringBootApplication
@EnableConfigServer //開啟配置服務
public class ConfigServerApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigServerApplication.class, args);
}
}
修改application.yml
server:
port: 12000
spring:
application:
name: config-server
cloud:
config:
server:
git:
#碼雲倉庫位址
uri: https://gitee.com/yin-hang1531/my-config
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:10086/eureka
成功通路出配置資訊
将服務者子產品application.yml中的配置删除,建立bootstrap.yml将添加一下配置資訊可從遠端倉庫中調用公共配置資訊(以後配置資訊交給nacos管理)
spring:
cloud:
config:
# 要與倉庫中的配置檔案的application保持一緻
name: user
# 要與倉庫中的配置檔案的profile保持一緻
profile: dev
# 要與倉庫中的配置檔案所屬的版本(分支)一樣
label: master
discovery:
# 使用配置中心
enabled: true
# 配置中心服務名
service-id: config-server
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:10086/eureka
bootstrap.yml檔案也是Spring Boot的預設配置檔案,而且其加載的時間相比于application.yml更早。
application.yml和bootstrap.yml雖然都是Spring Boot的預設配置檔案,但是定位卻不相同。bootstrap.yml
可以了解成系統級别的一些參數配置,這些參數一般是不會變動的。application.yml 可以用來定義應用級别
的參數,如果搭配 spring cloud config 使用,application.yml 裡面定義的檔案可以實作動态替換。
總結就是,bootstrap.yml檔案相當于項目啟動時的引導檔案,内容相對固定。application.yml檔案是微服務的一些正常配置參數,變化比較頻繁。
spring cloud bus
****Spring Cloud Bus是用輕量的消息代理将分布式的節點連接配接起來,可以用于廣播配置檔案的更改或者服務的監控管理。也就是消息總線可以為微服務做監控,也可以實作應用程式之間互相通信。 Spring Cloud Bus可選的消息代理有RabbitMQ和Kafka。(以後配置資訊改變自動重新整理交給nacos)
使用了Bus之後 :