點擊關注公眾號,實用技術文章及時了解
開始文檔目的原來想通過整合Sentinel,對spring cloud gateway請求進行流控;在Sentinel界面中修改和增加流控規則,同步到nacos。
百度有很多文章,但是實踐下來沒有一個能夠實現我想要的結果,於是決定在前人的基礎上研究,終於初步達成了目的。由於本人水平有限,有些概念沒有深入了解,請見諒!
版本信息nameversiondescspring boot2.7.3spring cloud2021.0.3io.springfox3.0.0knife4j3.0.2com.alibaba.cloud2021.1nacos2.1.0cluster modesentinel1.8.4mysql5.7.22整合nacos 集群模式配置(一台機器)
### nacos1### Default web server port:server.port=8840### Connect URL of DB:db.url.0=jdbc:mysql://127.0.0.1:3306/nacos_config?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTCdb.user.0=XXXXdb.password.0=XXXXXX同樣,將nacos2的server.port改為8850;nacos3的server.port改為8860
nacos1、nacos2、nacos3三個實例的cluster.conf修改#2022-09-19T08:54:21.232172.20.11.5:8840172.20.11.5:8850172.20.11.5:8860注意:集群模式下一定要配置為真實的ip加端口,不能用127.0.0.1或者localhost代替;後面為spring cloud gateway配置nacos時候,一定要寫真實的ip,否則會報以下的錯誤:
com.alibaba.nacos.api.exception.NacosException:failedtoreqAPI:/nacos/v1/ns/instanceafterallservers([http://127.0.0.1:8848])由於機器資源限制,實際我只啟動了nacos1一個實例。
spring cloud gateway網關搭建server:port:9000spring:application:name:gateway-servermain:allow-bean-definition-overriding:trueconfig:activate:on-profile:${SPRING_PROFILES_ACTIVE:dev}cloud:nacos:config:server-addr:${NACOS_SERVER_ADDR:172.20.11.5}:8840#配置中心真實ipfile-extension:yamlnamespace:middle-${SPRING_PROFILES_ACTIVE:dev}group:${SPRING_PROFILES_ACTIVE:dev}refresh-enabled:truediscovery:server-addr:${NACOS_SERVER_ADDR:172.20.11.5}:8840#服務註冊和發現中心,和配置中心使用同一個變量#集群模式:localhost:8848,localhost:8849,localhost:8850namespace:middle-${SPRING_PROFILES_ACTIVE:dev}group:${SPRING_PROFILES_ACTIVE:dev}gateway:globalcors:cors-configurations:#允許跨域請求'[**]':allowedOrigins:'*'allowedMethods:'*'discovery:locator:enabled:truesentinel:#sentinel整合transport:dashboard:${SENTINEL_DASHBOARD_ADDR:172.20.11.5}:8800#sentinel控制台訪問路徑filter:enabled:false#心跳啟動datasource:ds:nacos:#整合nacos,把流控規則保存到nacos,sentinel控制台啟動時候就能夠讀取這個配置server-addr:${NACOS_SERVER_ADDR:172.20.11.5}:8840dataId:${spring.application.name}-sentinel-flow#為什麼會這樣設置,後面會講到namespace:middle-${SPRING_PROFILES_ACTIVE:dev}groupId:${SPRING_PROFILES_ACTIVE:dev}rule-type:gw_flow#流控配置eager:truemanagement:endpoints:web:exposure:include:'*'#shutdown,health,info,loggers,gateway,sentinelendpoint:shutdown:enabled:truehealth:#適用於k8sLivenessProbe(健康檢查)和ReadinessProbe(是否已經準備好接受請求)show-details:always#不顯示詳細信息上述配置要注意幾點:
在sentinel下面配置datasource為nacos時候,就是把sentinel下流控配置存儲到nacos, 這裡採用服務名加-sentinel-flow的後綴,對應於後面sentinel-dashboard源碼的修改在網關啟動時候,加入參數-Dcsp.sentinel.app.type=1,告訴sentinel這是一個網關類型,sentinel-bashboard控制台如下顯示就對了:
兩個微服務開發,分別為demo和demo1, 這裡就不用多講了,見源碼。
sentinel-dashboard改造主要就是新建、修改和刪除流控規則時候,調用nacos提供的接口,將相關流控信息推送給nacos, 保存到mysql.我參考了下面的這個文章,在他提供的源碼上面進行了修改,實現了我的目的,非常感謝!
https://blog.csdn.net/q669239799/article/details/125777745
參考上面的文章,下載文章中提供的sentinel-bashboard源碼。在application.properties中修改端口和增加nacos配置server.port=8800nacos.address=172.20.11.5:8840nacos.namespace=middle-devnacos.group=dev通過debug發現,最後其實是調用GatewayFlowRuleController類的addFlowRule、updateFlowRule和deleteFlowRule對sentinel內存中的流控規則進行新增、修改和刪除的。所以在對內存中的流控規則操作後,再調用nacos的接口,更改nacos裡面的流控規則,達到保存到nacos的目的。這裡以修改流控規則為例子:修改NacosConfig.java, 增加對於GatewayFlowRuleEntity的轉換Beanpackagecom.alibaba.csp.sentinel.dashboard.config.nacos;importcom.alibaba.csp.sentinel.dashboard.datasource.entity.gateway.GatewayFlowRuleEntity;importcom.alibaba.csp.sentinel.dashboard.datasource.entity.rule.DegradeRuleEntity;importcom.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity;importcom.alibaba.csp.sentinel.datasource.Converter;importcom.alibaba.fastjson.JSON;importcom.alibaba.nacos.api.PropertyKeyConst;importcom.alibaba.nacos.api.config.ConfigFactory;importcom.alibaba.nacos.api.config.ConfigService;importorg.springframework.beans.factory.annotation.Value;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;importjava.util.List;importjava.util.Properties;/***@authorEricZhao*@since1.4.0*/@ConfigurationpublicclassNacosConfig{@Value("${nacos.address}")privateStringaddr;//Nacos地址,對應於上面的設置@Value("${nacos.namespace}")privateStringnamespace;//Nacos命名空間,對應於上面的設置@BeanpublicConverter<List<DegradeRuleEntity>,String>degradeRuleEntityEncoder(){returnJSON::toJSONString;}@BeanpublicConverter<String,List<DegradeRuleEntity>>degradeRuleEntityDecoder(){returns->JSON.parseArray(s,DegradeRuleEntity.class);}//添加對於GatewayFlowRuleEntity的轉換@BeanpublicConverter<List<GatewayFlowRuleEntity>,String>gatewayFlowRuleEntityEncoder(){returnJSON::toJSONString;}@BeanpublicConverter<String,List<GatewayFlowRuleEntity>>gatewayFlowRuleEntityDecoder(){returns->JSON.parseArray(s,GatewayFlowRuleEntity.class);}//end@BeanpublicConverter<List<FlowRuleEntity>,String>flowRuleEntityEncoder(){returnJSON::toJSONString;}@BeanpublicConverter<String,List<FlowRuleEntity>>flowRuleEntityDecoder(){returns->JSON.parseArray(s,FlowRuleEntity.class);}@BeanpublicConfigServicenacosConfigService()throwsException{Propertiesproperties=newProperties();//nacos集群地址properties.put(PropertyKeyConst.SERVER_ADDR,addr);//namespace為空即為publicproperties.put(PropertyKeyConst.NAMESPACE,namespace);returnConfigFactory.createConfigService(properties);}}新增GatewayFlowRuleNacosPublisher.javapackagecom.alibaba.csp.sentinel.dashboard.config.flow;importcom.alibaba.csp.sentinel.dashboard.config.nacos.NacosConfigUtil;importcom.alibaba.csp.sentinel.dashboard.datasource.entity.gateway.GatewayFlowRuleEntity;importcom.alibaba.csp.sentinel.dashboard.rule.DynamicRulePublisher;importcom.alibaba.csp.sentinel.datasource.Converter;importcom.alibaba.csp.sentinel.util.AssertUtil;importcom.alibaba.nacos.api.config.ConfigService;importcom.alibaba.nacos.api.config.ConfigType;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.beans.factory.annotation.Value;importorg.springframework.stereotype.Component;importjava.util.List;//注意,不一樣的名稱@Component("gatewayFlowRuleNacosPublisher")publicclassGatewayFlowRuleNacosPublisherimplementsDynamicRulePublisher<List<GatewayFlowRuleEntity>>{@AutowiredprivateConfigServiceconfigService;@Value("${nacos.group}")//對應於application.properties裡面的配置privateStringgroup;//使用到上面定義的Converter@AutowiredprivateConverter<List<GatewayFlowRuleEntity>,String>converter;@Overridepublicvoidpublish(Stringapp,List<GatewayFlowRuleEntity>rules)throwsException{AssertUtil.notEmpty(app,"appnamecannotbeempty");if(rules==null){return;}//通過app服務名+後綴:還記得我們前面說的,後綴是:-sentinel-flow, 這樣就能找到nacos裡面的配置信息//如果沒有設置group,缺省就是DEFAULT_GROUP//這兒,實際上是調用NacosConfigService的功能對nacos進行操作configService.publishConfig(app+NacosConfigUtil.FLOW_DATA_ID_POSTFIX,group==null?NacosConfigUtil.GROUP_ID:group,converter.convert(rules),ConfigType.JSON.getType());}}為GatewayFlowRuleController添加注入剛才創建的類的實例@Autowired@Qualifier("gatewayFlowRuleNacosPublisher")privateDynamicRulePublisher<List<GatewayFlowRuleEntity>>rulePublisher;為GatewayFlowRuleController添加函數privatevoidpublishRules(StringappName){List<GatewayFlowRuleEntity>rules=repository.findAllByApp(appName);try{rulePublisher.publish(appName,rules);}catch(Exceptione){logger.warn("publicgatewayflowrulestonacosfail");}}在GatewayFlowRuleController的updateFlowRule函數最後,添加publishRules調用//原有代碼,發送流控規則更新客戶端,比如這裡是我的網關服務if(!publishRules(app,entity.getIp(),entity.getPort())){logger.warn("publishgatewayflowrulesfailafterupdate");}else{//客戶端成功,更新保存到nacos持久化publishRules(entity.getApp());}到這兒修改流控規則持久化到nacos就完了,新增和刪除按照上面的思路改一下就行。對sentinel-bashboard進行打包,然後運行進行測試。事情還沒有完...實際在使用的時候,在sentinel更改流控規則進行更新時候,我的網關服務會出現NullPointerException的錯誤,導致sentinel-bashboard更新流控不成功,通過網關程序日誌發現,問題出在阿里提供的GatewayRuleManager這個類的applyToConvertedParamMap函數裡面:(GatewayRuleManager在阿里提供的sentinel適配於gateway的包中:sentinel-api-gateway-adapter-common-1.8.0.jar)
ParameterMetricStorage.getParamMetricForResource(resource).clearForRule(rule);問題出在這一行上面,ParameterMetricStorage.getParamMetricForResource(resource)獲取為null, 所以報NullPointerExceptin; 大致看了一下,是獲取網關相關指標的函數,這一塊也沒有精力研究,直接改為:
ParameterMetricparameterMetric=ParameterMetricStorage.getParamMetricForResource(resource);if(parameterMetric!=null){parameterMetric.clearForRule(rule);}這兒沒有完全深入研究這樣寫的影響,反正從代碼上看是沒有錯的,以後有時間再深入評估。到此為止,代碼已經能夠順利運行了。
最後...實際上,我的操作順序是這樣的
1、在nacos寫一個流控的json格式配置,主要他的data-id為:gateway-server-sentinel-flow(網關服務名+後綴)
[{"resourceMode":0,"resource":"demo1","grade":1,"count":3,"intervalSec":1,"controlBehavior":0,"burst":0},{"resourceMode":0,"resource":"demo","grade":1,"count":2,"intervalSec":1,"controlBehavior":0,"burst":0}]2、啟動sentinel-bashboard(自己打的jar)
3、啟動網關程序,可以在sentinel-bashboard控制台上面能夠看見流控規則:注意,我塗掉的黑色部分一開始是沒有的。

所以無論我怎麼設置,從網關調用後台服務從來不會阻塞;後來才發現是resource不對,應該在原來的resource前面加上ReactiveCompositeDiscoveryClient_:
[{"resourceMode":0,"resource":"ReactiveCompositeDiscoveryClient_demo1","grade":1,"count":3,"intervalSec":1,"controlBehavior":0,"burst":0},{"resourceMode":0,"resource":"ReactiveCompositeDiscoveryClient_demo","grade":1,"count":2,"intervalSec":1,"controlBehavior":0,"burst":0}]這樣,網關調用時候就能準確的進行流控了。
註:我利用http://localhost:9000/actuator/gateway/routes查看網關的路由信息,得到:

這個帶前綴的route_id實際就是在sentinelroute控制台看見的請求鏈路上的route_id(見下圖), 所以上面的資源應該加上ReactiveCompositeDiscoveryClient_前綴。再查,這個前綴實際為DiscoveryLocatorProperties裡面的routeIdPrefix屬性,缺省為ReactiveCompositeDiscoveryClient_, 在網關服務的配置文件裡面可以配置(最後一行):
spring:cloud:gateway:globalcors:cors-configurations:#允許跨域請求'[**]':allowedOrigins:'*'allowedMethods:'*'discovery:locator:enabled:trueroute-id-prefix:sentinel#前綴4、實際上,應該先從網關調用,在sentinel控制面板的請求鏈路上面顯示:

應該從這兒添加流控規則最好,然後同步到網關服務和持久化到nacos,這才是正確的步驟。
推薦
Java面試題寶典
技術內卷群,一起來學習!!

PS:因為公眾號平台更改了推送規則,如果不想錯過內容,記得讀完點一下「在看」,加個「星標」,這樣每次新文章推送才會第一時間出現在你的訂閱列表里。點「在看」支持我們吧!
