SpringCloud Alibaba Nacos詳解


1. 現有項目注冊中心替換alibaba組件Nacos

QQ圖片20201109164856

1.springcloud 、springboot、springcloud alibaba版本對應

spring cloud alibaba文檔:https://github.com/alibaba/spring-cloud-alibaba/wiki

版本對應:https://github.com/alibaba/spring-cloud-alibaba/wiki/版本說明

注意:

  • 0.9版本之后,畢業版本的groudId 為 com.alibaba.com
  • 0.9版本之前,化器版本groudId 為org.springframework.cloud(不推薦使用)

image-20201029100642198

image-20201029100835653

image-20201029101405686

2. 依賴修改

目前mango系統各組件版本

  • springboot版本為1.5
  • springcloud alibaba使用畢業版版本1.5.0RELEASE ,不要使用孵化器版本
  • nacos service 阿里文檔中推薦1.1.1版本,實際上使用最新的穩定版本也可以使用,測試驗證使用1.2版本可以正常使用
<!--pom文件依賴新增一下內容,刪除其他注冊中心的相關的依賴(例如:Eureka) 和其他配置中心的依賴(例如:Springcloud config)-->
<dependencyManagement>
    <!--只是對版本進行管理,不會實際引入jar -->
    <dependencies>
        <dependency>
       <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>Edgware.SR6</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-alibaba-dependencies</artifactId>
            <version>1.5.1.RELEASE</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

<dependencies>
  <!--mango系統替換后需要指定springCloud-common包版本號-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-commons</artifactId>
        <version>1.3.5.RELEASE</version>
    </dependency>
    <!--因為nacos config是通過web獲取配置,需要導入springboot-web依賴-->
     <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!--nacos 服務發現-->
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        <exclusions>
            <exclusion>
                <artifactId>spring-cloud-commons</artifactId>
                <groupId>org.springframework.cloud</groupId>
            </exclusion>
            <exclusion>
                <artifactId>fastjson</artifactId>
                <groupId>com.alibaba</groupId>
            </exclusion>
        </exclusions>
    </dependency>
       <!--nacos 配置管理-->
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
        <exclusions>
            <exclusion>
                <artifactId>spring-cloud-commons</artifactId>
                <groupId>org.springframework.cloud</groupId>
            </exclusion>
        </exclusions>
    </dependency>
      <!--sentinel依賴,注意網關根據不同的實現方式對應着不同的sentinel依賴-->
    <dependency>
          <groupId>com.alibaba.csp</groupId>
          <artifactId>spring-boot-starter-ahas-sentinel-client</artifactId>
          <version>1.7.2</version>
	</dependency>
    
    <dependency>      
          <groupId>org.springframework.cloud</groupId>       
          <artifactId>spring‐cloud‐starter‐openfeign</artifactId>   
    </dependency>
</dependencies>

網關不同實現方式sentinle對應的依賴:

<!--zuul 1.x網關接入, Mango即是zuul 1.X-->
<dependency>
     <groupId>com.alibaba.csp</groupId>
     <artifactId>spring-cloud-zuul-starter-ahas-sentinel</artifactId>
     <version>1.1.8</version>
</dependency>
<!--springcloudGateway網關接入-->
<dependency>
      <groupId>com.alibaba.csp</groupId>
      <artifactId>spring-cloud-gateway-starter-ahas-sentinel</artifactId>
      <version>1.1.8</version>
</dependency>

3. 配置文件修改

其他詳細的配置可參考nacos-配置管理中的介紹

#bootstrap
spring:
    application:
        name: gateway
    cloud:
        sentinel:
            transport:
            	#sentinel 控制面板
                dashboard: 127.0.0.1:8083
        nacos:
            config:
            	# nacos service地址
                server-addr: 127.0.0.1:8848
                namespace: public 
                group: DEFAULT_GROUP
                data-id: gateway.yml
                file-extension: yaml
                refresh:
                    enables: true # 默認為fasle ,設為true,開啟動態刷新
#application
server:
	port: 56020 #啟動端口 命令行注入  
spring:   
	application:     
		name: gateway
     cloud:     
     	nacos:      
        	discovery:       
        		# nacos service地址
            	server‐addr: 127.0.0.1:8848

4. 代碼修改

  1. 啟動類新增服務發現注解 @EnableDiscoveryClient @EnableFeignClients
  2. 生產者聲明@FeignClient(name = "uaa") ,mango項目中,生產者遠程代理在common中,注意修改common的依賴

2. nacos介紹

Nacos是阿里的一個開源產品,它是針對微服務架構中的服務發現、配置管理、服務治理的綜合型解決方案。
nacos文檔: https://nacos.io/en-us/docs/what-is-nacos.html

2.1 nacos-配置管理

nacos-config文檔:https://github.com/alibaba/spring-cloud-alibaba/wiki/Nacos-config

2.1.1 配置中心的使用場景

微服務架構,系統拆分為一個個服務節點,配置文件也被分割,分散中就包含了很多相同的配置,造成冗余。

配置中心就是將配置從應用里剝離出來, 進行統一管理。

配置中心的服務流程

1. 用戶在配置中心更新配置信息。
2. 服務A和服務B及時得到配置更新通知,從配置中心獲取配置

2.1.2 常見的配置中心

nacos 讀寫性能最高,且和springcloud condig相比,nacos帶圖形化界面配置。

img

2.1.3 nacos 快速部署

  1. 下載地址:https://github.com/alibaba/nacos/releases,下載源碼自己編譯/下載編譯好的安裝包

  2. 安裝包啟動方式:bin目錄下執行 sh startup.sh -m standalone

    standalone 代表單機模式運行,非集群模式

    sh startup ‐m cluster 標識集群啟動

  3. 啟動成功, http://127.0.0.1:8848/nacos可打開nacos控制台,默認賬號密碼都為 nacos

  4. OPEN API 配置管理測試 :https://nacos.io/en-us/docs/open-api.html

    1. 發布配置: curl -X POST "http://127.0.0.1:8848/nacos/v1/cs/configs dataId=nacos.cfg.dataId&group=test&content=HelloWorld"
    2. 獲取配置: curl -X GET "http://127.0.0.1:8848/nacos/v1/cs/configs?dataId=nacos.cfg.dataId&group=test"

2.1.4 nacos 外部mysql數據庫支持

單機模式nacos使用的是嵌入式數據庫實現數據儲存,可以配置mysql作為外部存儲(目前只支持使用mysql)。

  1. 新建數據庫nacos_config

  2. 執行nacos下conf/nacos-mysql.sql 文件

  3. 修改nacos下conf/application.properties,增加支持mysql數據源配置,增加MySQL數據源賬號密碼

  4. 增加后記得重啟一下nacos

    spring.datasource.platform=mysql  
    db.num=1
    db.url.0=jdbc:mysql://localhost:3306/nacos_config?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true 
    db.user=
    db.password=
    

2.1.5 nacos 配置管理應用

對於nacos配置管理,通過namespace、group、Data ID 可以定位到一個配置集(也可以理解為一個配置文件)。

  • namespace:設計是 nacos 基於此做多環境以及多租戶(多個用戶共同使用nacos)數據(配置和服務)隔離的。
    一個租戶情況下:有多套不同環境,可以使用同一套nacos集群,通過建多個不同的nacespace來做配置/服務的隔離
    多個租戶情況下:不同租戶分配不同的賬號密碼,創建自己的namespace,不同租戶下namespace互不可見(nacos還沒有實現多租戶功能)

nacos 服務隔離,應該是同一namespace下才可以調用其他服務,注冊中心雖然是一個,但是注冊表不同

服務獲取配置集需要配置的內容:

  1. nacos server 地址 ,必須配置
  2. namespace:可以不指定,默認是public,若指定填寫的是namespace的id而不是名稱,id在nacos管理頁面查看
  3. group:可以不指定,默認是DEFAUT_GROUP
  4. dataId:必須指定

nacos配置管理提供了配置clone,歷史版本、回滾、訂閱者查詢等核心管理能力,通過圖形化界面很好操作。

監聽查詢
Nacos提供配置訂閱者即監聽者查詢能力,同時提供客戶端當前配置的MD5校驗值,以便幫助用戶更好的檢查配 置變更是否推送到 Client 端。


2.1.6. nacos登錄管理:

除了使用默認的nacos賬號密碼,可以通過直接在數據庫中增加賬號密碼,密碼為BCrypt加密方法
也可以在nacos service中管理登錄功能,

spring.security.enabled=false management.security=false security.basic.enabled=false nacos.security.ignore.urls=/** 

2.1.7 nacos集成分布式系統

通過 Spring cloud原生方式快捷的獲取配置內容:

1. 在bootstrap.yml基礎配置

Spring:	
	cloud:
        nacos:
            config:
            	enabled: true # 可不指定,默認true,false時關閉了nacos-config配置
                server-addr: 127.0.0.1:8848 #必須指定
                namespace: c67e4a97‐a698‐4d6d‐9bb1‐cfac5f5b51c4 # 可不指定,默認為public
                ext-configs[0]: #配置單個配置集的時候,可省略
                  	group: DEFAULT_GROUP  # 可不指定,默認為DEFAULT_GROUP
                 	file-extension: yaml  # 默認是properties
                	data-id: service.yaml # 可不指定,默認是${spring.application.name}和文件拓展名組合
                						# 指定data-id時,需要帶上文件拓展名,yaml和yml都可以,和file-extension兩者不干擾,也可以配置多個data-id,優先級根據ex-config[n]中n的數字大小決定,數字越大,優先級越高
                	refresh:
                    	enables: true # 默認為false,設為fasle 關閉動態刷新
                 ext‐config[1]:       
                  # Data Id 不在默認的組,不支持動態刷新 
                 	data‐id: ext‐config‐common02.properties  # 可以配置公用的配置               
                 	group: GLOBALE_GROUP                      

在加載配置文件時,不僅會加載${spring.application.name}.${file-extension:properties}為前綴的基礎配置

還會加載${spring.application}0 - ${profile}.${file-extension:properties}的基礎信息

${spring.profiles.active} 當通過配置文件來指定時必須放在 bootstrap.properties 文件中。但是實際項目中,時通過啟動參數-Dspring.profiles.active=<profile>來切換不同的配置

通過修改namespace、group、dataId可以獲取指定的配置集,還可以修改profile.active來指定配置集

2. 自定義共享DataId配置

spring:   
 	cloud:   
  		nacos:   
     		config:      
     			# 配置分享dataId必須帶上文件后綴名,refreable-dataids也是
     			# shared-dataids 中group為默認的DEFAULT_GROUP ,若所配的data-id不在默認組中獲取不到配置集
     			# 支持多個DataId配置,逗號隔開,優先級按照配置出現的先后順序,即后面的優先級要高於前面。
    			shared‐dataids: ext‐config‐common01.properties,ext‐config‐common02.properties 
    		 		# 指定動態刷新的 配置集
    		 		refreshable‐dataids: ext‐config‐common01.properties 

3. 配置的優先級

Spring Cloud Alibaba Nacos Config 目前提供了三種配置能力從 Nacos 拉取相關的配置。

  • A: 通過 spring.cloud.nacos.config.shared-configs[n].data-id 支持多個共享 Data Id 的配置
  • B: 通過 spring.cloud.nacos.config.extension-configs[n].data-id 的方式支持多個擴展 Data Id 的配置
  • C: 通過內部相關規則(應用名、應用名+ Profile )自動生成相關的 Data Id 配置

當三種方式共同使用時,他們的一個優先級關系是:A < B < C

4. 實時獲取最新配置

// 注入配置文件上下文
@Autowired 
private ConfigurableApplicationContext applicationContext;  

@GetMapping(value = "/configs") 
public String getConfigs(){   
    // 實時獲取配置
	return applicationContext.getEnvironment().getProperty("common.name"); 
}

2.1.8 nacos集群部署

  1. 安裝三個以上nacos

  2. 在所有nacos conf目錄下將cluster.conf.example文件改為cluster.conf,將每行配置成 ip:port。(請配置3個或3個以上節點)

    # ip:port  
    127.0.0.1:8848 
    127.0.0.1:8849 
    127.0.0.1:8850 
    
    
  3. 單機的話還需修改conf下application.properties中server.port ,防止端口沖突,如果服務器有多個ip也要指定具體的ip地址,如:nacos.inetutils.ip-address=127.0.0.1

  4. 客戶端配置,spring.cloud.nacos.config.server-addr: 127.0.0.1:8848,127.0.0.1:8849,127.0.0.1:8850

    關閉其中的注冊中心,其他注冊注冊中心會被成功選舉。

2.2服務發現

2.2.1 常見服務發現中心:

從長遠來看Nacos在以后的版本會 支持SpringCLoud+Kubernetes的組合,填補 2 者的鴻溝,在兩套體系下可以采用同一套服務發現和配置管理的解 決方案,這將大大的簡化使用和維護的成本。另外,Nacos 計划實現 Service Mesh,也是未來微服務發展的趨 勢。

image-20201030093733986

2.2.2 代碼修改

增加依賴,增加服務發現注解, 如1-4,省略

2.2.3 服務管理

  1. 可以通過nacos service 可視化界面對服務進行管理, 可以看到已經注冊的服務,且可以進行服務上線下線管理,通過配置實例的權重,修改實例接收的流量,權重為0時,不接受流量,和下線效果一樣
  2. 也可以在服務詳情頁面,進行元數據的修改,key-value的結構

2.2.4 nacos disvocery 配置項信息詳情

配置項 Key 默認值 說明
服務端地址 spring.cloud.nacos.discovery.server-addr Nacos Server 啟動監聽的ip地址和端口
服務名 spring.cloud.nacos.discovery.service ${spring.application.name} 給當前的服務命名
服務分組 spring.cloud.nacos.discovery.group DEFAULT_GROUP 設置服務所處的分組
權重 spring.cloud.nacos.discovery.weight 1 取值范圍 1 到 100,數值越大,權重越大
網卡名 spring.cloud.nacos.discovery.network-interface 當IP未配置時,注冊的IP為此網卡所對應的IP地址,如果此項也未配置,則默認取第一塊網卡的地址
注冊的IP地址 spring.cloud.nacos.discovery.ip 優先級最高
注冊的端口 spring.cloud.nacos.discovery.port -1 默認情況下不用配置,會自動探測
命名空間 spring.cloud.nacos.discovery.namespace 常用場景之一是不同環境的注冊的區分隔離,例如開發測試環境和生產環境的資源(如配置、服務)隔離等。
AccessKey spring.cloud.nacos.discovery.access-key 當要上阿里雲時,阿里雲上面的一個雲賬號名
SecretKey spring.cloud.nacos.discovery.secret-key 當要上阿里雲時,阿里雲上面的一個雲賬號密碼
Metadata spring.cloud.nacos.discovery.metadata 使用Map格式配置,用戶可以根據自己的需要自定義一些和服務相關的元數據信息
日志文件名 spring.cloud.nacos.discovery.log-name
集群 spring.cloud.nacos.discovery.cluster-name DEFAULT 配置成Nacos集群名稱
接入點 spring.cloud.nacos.discovery.enpoint UTF-8 地域的某個服務的入口域名,通過此域名可以動態地拿到服務端地址
是否集成Ribbon ribbon.nacos.enabled true 一般都設置成true即可
是否開啟Nacos Watch spring.cloud.nacos.discovery.watch.enabled true 可以設置成false來關閉 watch

3. sentinel

image-20201116143639964

流控制組件,包括流控制,並發限制,電路中斷和自適應系統保護,以確保微服務的可靠性。

sentinel包括兩個部分:

  1. 核心庫:不依賴任何框架
  2. 控制台:springboot應用,可直接運行,負責管理推送規則、監控、集群限流分配,機器發現等.

sentinel功能:

  1. 流控
  2. 斷路和並發
    1. 最大並發限制
      過限制並發線程的數量(即信號隔離)來減少不穩定資源的影響,而不是使用線程池。(hystrix使用線程池來實現隔離,可做對比//TODO
      當資源的響應時間變長時,線程將開始被占用。當線程數累積到一定數量時,新的傳入請求將被拒絕。反之亦然,當資源恢復並變得穩定時,占用的線程也將被釋放,新請求將被接受。
    2. 斷路
      根據不穩定資源的響應時間降級不穩定資源保證可靠性。當資源的響應時間太大時,將在指定的時間窗口中拒絕所有對該資源的訪問。

3.1 定義資源

能通過sentinel Api定義的diamond,就是資源

可以是:服務,方法,某一段代碼

3.1.1 Api方式定義資源

文檔:https://github.com/alibaba/Sentinel/wiki/如何使用

// 舉例
@GetMapping(value="/echo")
public String echo(){
	// 定義資源
	try(Entry entry = Sphu.entry("echo")){
	 //定義被保護的邏輯
	 	return "i am from port"+port;
	}catch(BlockException e){
		retrun "當前訪問被控制了";
	}
}

3.1.2 使用注解定義資源

sentinle提供了@SentinelResource注解的方式定義資源,更推薦使用。

主要屬性 作用
value 資源名稱,必需項(不能為空)
blockHandler 在原方法被限流/降級/系統保護的時候調用,可選項必須與原方法必須處於同一個類中、訪問類型必須為public、返回類型需要與原方法相匹配、參數類型需要和原方法相匹配。若希望使用其他類的函數,則可以指定 blockHandlerClass 為對應的類的 Class 對象,注意對應的函數必需為 static 函數,否則無法解析。
  1. 定義配置類

    @Configuration
    public class SentinelAspectConfiguration {
    
        @Bean
        public SentinelResourceAspect sentinelResourceAspect() {
            return new SentinelResourceAspect();
        }
    }
    
  2. 使用注解定義資源

    @GetMapping(value = "/echo")
    @SentinelResource(value = "echo",blockHandler = "echoBlockHandler")
    public String echo() {
    
        //調用規則
        this.initFlowRules();
        //定義被保護的邏輯
        return "i am from port " + port;
    }
    // 1. 必須處於同一個類,2. 訪問類型必須為public,3. 返回值類型必須與原方法一致,4. 參數與類型必須與原方法相同,BlockException是特例
    public String echoBlockHandler(BlockException e){
        //處理被控制的邏輯
        return "當前訪問被控制了";
    }
    
    private void initFlowRules() {
        List<FlowRule> rules = new ArrayList<>();
        FlowRule rule = new FlowRule();
        //關聯域名
        rule.setResource("echo");
        //設定限流閾值類型
        rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
        //設置限流閾值
        rule.setCount(2);
        //設置流控針對的調用來源,default為不區分來源
        rule.setLimitApp("default");
        rules.add(rule);
        FlowRuleManager.loadRules(rules);
    }
    

3.2 定義規則

規則就是保護服務穩定的准則,可以是流控規則、服務熔斷降級規則、系統保護規則、來源訪問控制規則、熱點參數規則

sentinle的所有規則都在內存中動態查詢修改,規則可以實時進行調整。

定義規則有兩種途徑:1.sentinel Api 2. sentinel控制台

3.2.1 Api規則定義實現

![1584606721445](file://E:/Sentinel%E4%BD%BF%E7%94%A8%E5%AE%9E%E8%B7%B5/%E8%AE%B2%E4%B9%89/assets/1584606721445.png?lastModify=1604835718)

// 舉例
private void initFolwRules(){
	// flowRule 流控規則,可以定義多個流控規則
	List<FlowRule> rules = new ArrayList<>();
	FlowRule rule = new FlowRule();
	//關聯域名、資源名稱
	rule.setResource("echo");
	//設定限流閥值類型
  rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
    //設置限流閾值
    rule.setCount(2);
    //設置流控針對的調用來源,default為不區分來源
    rule.setLimitApp("default");
    rules.add(rule);
   
    FlowRuleManager.loadRules(rules);
}

3.2.2 資源和規則結合

        return "當前訪問被控制了";
@GetMapping(value = "/echo")
public String echo() {

    //調用規則
    this.initFlowRules();

    //定義資源
    try (Entry entry = SphU.entry("echo")){
        //定義被保護的邏輯
        return "i am from port " + port;
    } catch (BlockException e) {
        //處理被控制的邏輯
        return "當前訪問被控制了";
    }
}

private void initFlowRules() {
    List<FlowRule> rules = new ArrayList<>();
    FlowRule rule = new FlowRule();
    //關聯域名
    rule.setResource("echo");
    //設定限流閾值類型
    rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
    //設置限流閾值
    rule.setCount(2);
    //設置流控針對的調用來源,default為不區分來源
    rule.setLimitApp("default");
    rules.add(rule);
    FlowRuleManager.loadRules(rules);
}

3.3 sentinle控制台使用

訪問:https://github.com/alibaba/Sentinel/releases。進行對應版本下載,可下載jar包,也可下載exe文件

3.3.1 客戶端接入控制台

客戶端添加控制台配置后重啟服務,若未開啟自動心跳, 則服務資源后調用后,才能在控制台看到具體服務。sentinel是延遲加載的。

project:
	name: 服務名
// application.yml中添加配置
sentinel:
	dashboard:
		server: sentinel控制台地址
		

3.4 sentinel各規則詳解

3.4.1流控- QPS(每秒查詢率)流量控制

sentinel實現流控的原理是監控應用流量的 QPS並發線程數等指標,當達到指定的閾值時對流量進行控制,以避免被瞬時的流量高峰沖垮,從而保障應用的高可用性。

3.4.1.1 不針對來源進行流控

針對來源選項值當為default,代表不針對來源。即對所有微服務都生效。

3.4.1.2 針對特定來源流控

對於一個系統而言,不同的請求來源,很有可能會設置不同的規則。

例如對於商品搜索結果頁面,針對PC端指定一個規則,針對小程序端指定一個規則。

  1. 添加依賴

  2. 聲明配置bean

    @Component
    public class MyRequestOriginParser implements RequestOriginParser {
        @Override
        public String parseOrigin(HttpServletRequest httpServletRequest) {
            // 從請求路徑或者請求頭中獲取請求來源信息
            // 此處以請求頭為例
            String origin = httpServletRequest.getParameter("origin");
            if (StringUtils.isBlank(origin)) {
                throw new IllegalArgumentException("origin must be exist");
            }
            return origin;
        }
    }
    
  3. 添加配置類

    @Configuration
    @ConditionalOnWebApplication(
        type = ConditionalOnWebApplication.Type.SERVLET
    )
    @ConditionalOnClass({CommonFilter.class})
    @ConditionalOnProperty(
        name = {"spring.cloud.sentinel.enabled"},
        matchIfMissing = true
    )
    //@EnableConfigurationProperties({SentinelProperties.class})
    // 從sentinel源碼中copy出來
    public class SentinelWebAutoConfiguration {
        //private static final Logger log = LoggerFactory.getLogger(com.alibaba.cloud.sentinel.SentinelWebAutoConfiguration.class);
        //    @Autowired
        //    private SentinelProperties properties;
        @Autowired
        private Optional<UrlCleaner> urlCleanerOptional;
        @Autowired
        private Optional<UrlBlockHandler> urlBlockHandlerOptional;
        @Autowired
        private Optional<RequestOriginParser> requestOriginParserOptional;
    
        public SentinelWebAutoConfiguration() {
        }
    
        @PostConstruct
        public void init() {
            this.urlBlockHandlerOptional.ifPresent(WebCallbackManager::setUrlBlockHandler);
            this.urlCleanerOptional.ifPresent(WebCallbackManager::setUrlCleaner);
            this.requestOriginParserOptional.ifPresent(WebCallbackManager::setRequestOriginParser);
        }
    
        @Bean
        @ConditionalOnProperty(
            name = {"spring.cloud.sentinel.filter.enabled"},
            matchIfMissing = true
        )
        public FilterRegistrationBean sentinelFilter() {
            FilterRegistrationBean<Filter> registration = new FilterRegistrationBean();
            //        com.alibaba.cloud.sentinel.SentinelProperties.Filter filterConfig = this.properties.getFilter();
            //        if (filterConfig.getUrlPatterns() == null || filterConfig.getUrlPatterns().isEmpty()) {
            //            List<String> defaultPatterns = new ArrayList();
            //            defaultPatterns.add("/*");
            //            filterConfig.setUrlPatterns(defaultPatterns);
            //        }
    
            //        registration.addUrlPatterns((String[])filterConfig.getUrlPatterns().toArray(new String[0]));
            Filter filter = new CommonFilter();
            registration.setFilter(filter);
            registration.setOrder(-2147483648);
            //registration.setOrder(filterConfig.getOrder());
            registration.addInitParameter("HTTP_METHOD_SPECIFY", String.valueOf(false));
            //log.info("[Sentinel Starter] register Sentinel CommonFilter with urlPatterns: {}.", filterConfig.getUrlPatterns());
            return registration;
        }
    }
    
    
    1. 在控制台增加流控規則,指定來源

      ![1585061229263](file://E:/Sentinel%E4%BD%BF%E7%94%A8%E5%AE%9E%E8%B7%B5/%E8%AE%B2%E4%B9%89/assets/1585061229263.png?lastModify=1604838986)

    2. 訪問資源增加origin=pc,才會被限流,不是pc時可以任意訪問。例如:http://localhost:8083/info?origin=pc

3.4.2 流控-並發線程控制

並發線程數限流用於保護業務線程數不被耗盡。

例如,當下游服務出現響應時間延長,則會導致上游服務吞吐量下降和更多的線程數占用,極端情況下甚至導致線程池耗盡。

為了避免這種情況,業內比較常見的解決方案是線程隔離(hystrix)。給不同的業務邏輯分配不同的線程池。但是這種方案會造成大量的線程上下文切換,嚴重影響效率。

sentinle實現原理:統計請求上下文線程數、超出閾值,新的請求被拒絕,效果當前與信號隔離。

// 測試,在控制台增加並發線程控制規則,可以發現有一部分請求會被隔離掉,這是因為超過了線程的閾值。
public class MyThread implements Runnable {
    @Override
    public void run() {
        RestTemplate restTemplate = new RestTemplate();
        String forObject = restTemplate.getForObject("http://localhost:8083/info", String.class);
        System.out.println(forObject);
    }
}

public class ThreadTest {
    public static void main(String[] args) throws InterruptedException {
        //線程池
        ExecutorService pool = Executors.newCachedThreadPool();
        for(int i=0;i<100;i++){
            MyThread myThread = new MyThread();
            pool.execute(myThread);
        }
    }
}

3.4.3 流控-流控模式、流控效果

  1. QPS關聯模式-快速失敗:場景為兩個方法會產生性能上相互影響,如讀方法和寫方法。若寫方法優先級高,在定義讀方法流控規則時,將關聯資源定為寫方法,當寫方法操作超過閾值,會限制讀方法。
  2. QPS鏈路模式快速失敗:鏈路即從指定入口訪問的資源達到閾值則限流

流控效果:

  1. 快速失敗:達到限流規則,不允許進行訪問

  2. warmUp:冷啟動/預熱,對流量的增加是逐步增加的,適用於處理突發性流量。threshold (閾值)/coldFactor(冷加載因子,默認為3)=最初的閾值,接着最初的閾值*預熱時長來最終實現限流。

  3. 排隊等待/勻速器:讓請求勻速通過,適用處理間隔性突發的流量。

    原理:如果當前請求距離上個通過的請求通過的時間間隔不小於預設值,則讓當前請求通過;

    否則,計算當前請求的預期通過時間,如果該請求的預期通過時間小於規則預設的 timeout 時間,則該請求會等待直到預設時間到來通過(排隊等待處理);

    若預期的通過時間超出最大排隊時長,則直接拒接這個請求。

    ![1585067276895](file://E:/Sentinel%E4%BD%BF%E7%94%A8%E5%AE%9E%E8%B7%B5/%E8%AE%B2%E4%B9%89/assets/1585067276895.png?lastModify=1604840075)

3.4.4 熔斷降級

避免某個接口異常,造成請求堆積,導致系統雪崩。

1. 熔斷降級策略

3.4.4.1 熔斷策略-平均響應時間:rt

1s 內持續進入 5 個請求,對應時刻的平均響應時間超過閾值count,以 ms 為單位),那么會在下一個時間窗口DegradeRule 中的 timeWindow,以 s 為單位)之內,對這個方法的調用都會自動地熔斷

3.4.4.2 熔斷策略-異常比例

當資源的每秒請求量 >= 5,並且每秒異常總數通過量的比值超過閾值每秒異常總數/通過量 > 閾值)之后,資源進入降級狀態,即在下一個時間窗口,對這個方法的調用自動熔斷。異常比率的閾值范圍是 [0.0, 1.0],代表 0% - 100%

3.4.4.3 熔斷策略-異常數

當資源近 1 分鍾的異常數目超過閾值之后會進行熔斷。注意由於統計是分鍾級別的,若 timeWindow 小於 60s,則結束熔斷狀態后仍可能再進入熔斷狀態。

3.4.5 熱點參數限流

熱點就是經常被訪問的數據,也叫熱數據。

熱點參數限流會統計訪問時所帶的參數,根據定義的規則進行參數級別的限流。也是一種流量控制。

=sentinel熱點參數限流實現原理:Sentinel 利用 LRU 策略統計最近最常訪問的熱點參數,結合令牌桶算法來進行參數級別的流控。

舉例:對id為1的參數進行限流,那么在訪問時,生效的級別只會在id為1時,當id為其他值時不會進行限流控制。

![1584713089706](file://E:/Sentinel%E4%BD%BF%E7%94%A8%E5%AE%9E%E8%B7%B5/%E8%AE%B2%E4%B9%89/assets/1584713089706.png?lastModify=1604846149)

3.4.5.1 熱點參數位限流

![1584714823721](file://E:/Sentinel%E4%BD%BF%E7%94%A8%E5%AE%9E%E8%B7%B5/%E8%AE%B2%E4%B9%89/assets/1584714823721.png?lastModify=1604846213)

上述配置聲明:對於hot資源通過QPS限流模式對訪問路徑上第一個參數,每秒只能訪問一次。

3.4.5.1 熱點參數值限流

![1584715354860](file://E:/Sentinel%E4%BD%BF%E7%94%A8%E5%AE%9E%E8%B7%B5/%E8%AE%B2%E4%B9%89/assets/1584715354860.png?lastModify=1604846338)

上述內容聲明:對於hot資源通過QPS限流模式對訪問路徑上第一個參數,每秒只能訪問一次。但 當參數值2,每秒能訪問兩次。當參數值3,每秒能訪問10000次。

3.4.6 系統自適應限流

文檔:https://github.com/alibaba/Sentinel/wiki/系統自適應限流

3.4.6.1 系統保護簡介

sentinel從 應用的load、cpu利用率、總體平均rt、入口QPS和並發線程幾個維度的監控指標,通過自適應的流控策略,讓系統的入口流量和系統的負載達到一個平衡。

系統保護目的:1. 保證系統不會拖垮 2.系統穩定的前提下,保證系統的吞吐量

以往的系統保護思路:根據硬指標,即load來做系統過載的保護,當系統負載高於閾值,就會禁止或減少流量的進入,load好轉即恢復流量的進入。

以往設計思路的弊端

  1. load是一個結果,根據load 的情況來調節流量的通過率會有延遲性
  2. 恢復慢:當下游應用異常導致RT過高,從而load達到了一個很高的點,當下游應用恢復,這是load應然很高,通過率依然還在受限制

sentinel系統保護的思路

  1. 根據系統能處理的請求允許進來的請求做平衡,而不是通過一個間接的指標(load)做限流。
  2. 在sentinel系統保護的做法中,load是作用啟動自適應保護因子,而允許通過的流量由處理請求的能力,即請求的響應時間以及當前系統正在處理的請求速率來決定。

3.4.6.2 系統保護-規則

系統保護規則是從應用級別的入口流量做控制的,並且僅對入口流量生效(進入應用的流量EntryType.IN,如web服務或Dubbo服務端接收的請求)。

從單台機器的load、CPU利用率、平均RT、入口QPS、並發線程數等幾個維度監控應用指標。

系統規則支持以下的模式:

  • Load 自適應(僅對 Linux/Unix-like 機器生效):系統的 load1 作為啟發指標,進行自適應系統保護。當系統 load1 超過設定的啟發值,且系統當前的並發線程數超過估算的系統容量時才會觸發系統保護(BBR 階段)。系統容量由系統的 maxQps * minRt 估算得出。設定參考值一般是 CPU cores * 2.5
  • CPU usage(1.5.0+ 版本):當系統 CPU 使用率超過閾值即觸發系統保護(取值范圍 0.0-1.0),比較靈敏。
  • 平均 RT:當單台機器上所有入口流量的平均 RT 達到閾值即觸發系統保護,單位是毫秒。
  • 並發線程數:當單台機器上所有入口流量的並發線程數達到閾值即觸發系統保護。
  • 入口 QPS:當單台機器上所有入口流量的 QPS 達到閾值即觸發系統保護

3.4.6.3 系統保護原理

3.5 動態規則、規則持久化

​ Sentinel默認會把規則信息保存到內存中,當服務重啟之后,規則就會丟失。可以將規存儲在文件、數據庫或者配置中心當中。

3.5.1 規則推送模式

  • pull(拉)模式:客戶端定期輪詢拉取規則,規則持久化,但實時性不能保證,拉取過於頻繁還有性能問題。
  • push(推)模式:規則中心統計推送,客戶端通過注冊監聽器的方式監聽變化,可以使用Nacos、zookeeper等配置中心。這種方式有更好的實時性和以執行的保證。推薦使用

4. 負載

文檔:https://juejin.im/post/6844903608371118094

在衡量服務器的性能時,經常會涉及到幾個指標,load(機器負載)、cpu、mem、qps、rt等。每個指標都有其獨特的意義,很多時候在線上出現問題時,往往會伴隨着某些指標的異常。

負載(load)是linux機器的一個重要指標,直觀了反應了機器當前的狀態。

在UNIX系統中,系統負載是對當前CPU工作量的度量,被定義為特定時間間隔內運行隊列中的平均線程數。load average 表示機器一段時間內的平均load。這個值越低越好。負載過高會導致機器無法處理其他請求及操作,甚至導致死機。Linux的負載高,主要是由於CPU使用、內存使用、IO消耗三部分構成。任意一項使用過多,都將導致服務器負載的急劇攀升。

load值代表的是對應時間內的jobs的平均數量,比如load1就表示過去1分鍾內的jobs數量的平均值。

4.1 查看機器負載

在Linux機器上,有多個命令都可以查看機器的負載信息。其中包括uptimetopw等。

  • uptime:能夠打印系統總共運行了多長時間和系統的平均負載。
➜  ~ uptime
13:29  up 23:41, 3 users, load averages: 1.74 1.87 1.97
現在時間、系統已經運行了多長時間、目前有多少登陸用戶、系統在過去的1分鍾、5分鍾和15分鍾內的平均負載。
  • w:能夠打印當前時間,系統啟動到現在的時間,登錄用戶的數目,系統在最近1分鍾、5分鍾和15分鍾的平均負載。然后是每個用戶的各項數據,項目顯示順序如下:登錄帳號、終端名稱、遠 程主機名、登錄時間、空閑時間、JCPU、PCPU、當前正在運行進程的命令行]。

  • top:是Linux下常用的性能分析工具,能夠實時顯示系統中各個進程的資源占用狀況,類似於Windows的任務管理器。

4.2 機器正常負載范圍

沒有一個明確的指標。

可以根據自己機器的實際情況,建立一個指標的基線(如近一個月的平均值),只要日常的load在基線上下范圍內不太大都可以接收,如果差距太多可能就要人為介入檢查了。

阮一峰對此的建議為:
當系統負荷持續大於0.7,你必須開始調查了,問題出在哪里,防止情況惡化。
當系統負荷持續大於1.0,你必須動手尋找解決辦法,把這個值降下來。
當系統負荷達到5.0,就表明你的系統有很嚴重的問題,長時間沒有響應,或者接近死機了。你不應該讓系統達到這個值。

注:這里的指標是但cpu的,如果是多喝系統,需乘上cpu的數量

4.3 如何降低負荷

CPU使用、內存使用、IO消耗都可能導致負載高。如果是軟件問題,有可能由於Java中的某些線程被長時間占用、大量內存持續占用等導致。建議從以下幾個方面排查代碼問題:

  1. 是否有內存泄露導致頻繁GC
  2. 是否有死鎖發生
  3. 是否有大字段的讀寫
  4. 會不會是數據庫操作導致的,排查SQL語句問題
  5. 死循環

這里還有個建議,如果發現線上機器Load飆高,可以考慮先把堆棧內存dump下來后,進行重啟,暫時解決問題,然后再考慮回滾和排查問題。

4.4 排查思路

1、使用uptime查看當前load,發現load飆高。

➜  ~ uptime
13:29  up 23:41, 3 users, load averages: 10 10 10
復制代碼

2、使用top命令,查看占用CPU較高的進程ID。

➜  ~ top

PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
1893 admin     20   0 7127m 2.6g  38m S 181.7 32.6  10:20.26 java
復制代碼

發現PID為1893的進程占用CPU 181%。而且是一個Java進程,基本斷定是軟件問題。

3、使用 top命令,查看具體是哪個線程占用率較高

➜  ~ top -Hp 1893
PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
4519 admin     20   0 7127m 2.6g  38m R 18.6 32.6   0:40.11 java
復制代碼

4、使用printf命令查看這個線程的16進制

➜  ~ printf %x 4519
11a7
復制代碼

5、使用jstack命令查看當前線程正在執行的方法。(Java命令學習系列(二)——Jstack)

➜  ~ jstack 1893 |grep -A 200 11a7
"thread-5" #500 daemon prio=10 os_prio=0 tid=0x00007f632314a800 nid=0x11a2 runnable [0x000000005442a000]
java.lang.Thread.State: RUNNABLE
at sun.misc.URLClassPath$Loader.findResource(URLClassPath.java:684)
at sun.misc.URLClassPath.findResource(URLClassPath.java:188)
at java.net.URLClassLoader$2.run(URLClassLoader.java:569)
at java.net.URLClassLoader$2.run(URLClassLoader.java:567)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findResource(URLClassLoader.java:566)
at org.hibernate.validator.internal.xml.ValidationXmlParser.getInputStreamForPath(ValidationXmlParser.java:248)
at com.hollis.test.util.BeanValidator.validate(BeanValidator.java:30)
復制代碼

從上面的線程的棧日志中,可以發現,當前占用CPU較高的線程正在執行我代碼的com.hollis.test.util.BeanValidator.validate(BeanValidator.java:30)類。那么就可以去排查這個類是否用法有問題了。

6、還可以使用jstat(Java命令學習系列(四)——jstat)來查看GC情況,看看是否有頻繁FGC,然后再使用jmap(Java命令學習系列(三)——Jmap)來dump內存,查看是否存在內存泄露。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM