項(xiàng)目版本
Spring Boot版本:2.2.9.RELEASE
Spring Cloud版本:Hoxton.SR7
Spring Cloud Alibaba版本:2.2.1.RELEASE
官方doc
介紹
本文分為以下幾部分:
- 普通服務(wù)集成
- 控制臺(tái)整合Nacos 自己Github重新封裝地址
- 網(wǎng)關(guān)集成(spring cloud gateway)
- 控制臺(tái)啟動(dòng)庵寞、參數(shù)和指定Nacos地址+namespace等
Nacos持久化最終效果圖如下:
普通服務(wù)
注:Sentinel兼容Hystrix的FallbackFactory
從 Hystrix 遷移到 Sentinel方案(官方)
Sentinel 與 Hystrix 的對(duì)比(官方)
import feign.hystrix.FallbackFactory;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
@Slf4j
@Component
public class NacosProviderClientFallback implements FallbackFactory<NacosProviderClient> {
@Override
public NacosProviderClient create(Throwable throwable) {
return new NacosProviderClient() {
@Override
public String abc(String name, int age) {
log.error("用 nacos-provider abc 方法失敗, 異常信息={}", throwable.getMessage());
//throw new RuntimeException("abc:" + throwable.getMessage());
//這里直接返回BusinessException(),因?yàn)? throw new RuntimeException("aaaaaaadssssssssssss");
// return throwable.getClass().getName() + " : " + throwable.getMessage();
}
@Override
public String getEcho(String string) {
log.error("用 nacos-provider getEcho 方法失敗", throwable);
throw new RuntimeException("getEcho:" + throwable.getMessage());
}
};
}
}
基礎(chǔ)包
基礎(chǔ)包大概截圖:
引入以下兩個(gè)包,一個(gè)是sentinel核心組件籽御,一個(gè)是整合nacos實(shí)現(xiàn)持久化功能組件(無(wú)關(guān)順序)
<!-- alibaba Sentinel組件 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<!-- alibaba Sentinel整合nacos組件 -->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
添加自定義異常處理(不加會(huì)返回默認(rèn)的錯(cuò)誤信息Blocked by Sentinel: XXXX)
注: 網(wǎng)上方案實(shí)現(xiàn) UrlBlockHandler 接口的版本已經(jīng)比較老了攻人,新版本已經(jīng)沒(méi)有UrlBlockHandler 接口
/**
* 沒(méi)有配資源贾惦,默認(rèn)Block異常處理(只能是Block異常)
*/
@Slf4j
@Component
public class CustomBlockExceptionHandler implements BlockExceptionHandler {
@Autowired
private ObjectMapper objectMapper;
@Override
public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, BlockException e) throws Exception {
httpServletResponse.setStatus(HttpStatus.SERVICE_UNAVAILABLE.value());
if (e instanceof FlowException) {
log.error("FlowException 普通服務(wù)限流启搂,資源信息:" + JSON.toJSONString(e.getRule()));
httpServletResponse.setStatus(org.springframework.http.HttpStatus.TOO_MANY_REQUESTS.value());
ResponseUtil.responseFailed(objectMapper, httpServletResponse, 1000, "API interface limited flow.");
} else if (e instanceof DegradeException) {
log.error("DegradeException 普通服務(wù)降級(jí)窖式,資源信息:" + JSON.toJSONString(e.getRule()));
ResponseUtil.responseFailed(objectMapper, httpServletResponse, 1001, "API interface has been degraded.");
} else if (e instanceof ParamFlowException) {
ParamFlowException ex = (ParamFlowException) e;
log.error("ParamFlowException 參數(shù)熱點(diǎn)限流潮售,資源名={}痊项,參數(shù)={},資源信息={}", ex.getResourceName(), ex.getLimitParam(), JSON.toJSONString(ex.getRule()));
ResponseUtil.responseFailed(objectMapper, httpServletResponse, 1002, "API interface limited flow by params.");
} else if (e instanceof AuthorityException) {
log.error("AuthorityException 授權(quán)規(guī)則酥诽,資源信息:" + JSON.toJSONString(e.getRule()));
ResponseUtil.responseFailed(objectMapper, httpServletResponse, 1003, "API interface limited by authority.");
} else if (e instanceof SystemBlockException) {
SystemBlockException systemBlockException = (SystemBlockException) e;
log.error("SystemBlockException鞍泉,資源名:{},資源類型:{}", systemBlockException.getResourceName(), systemBlockException.getRuleLimitApp());
ResponseUtil.responseFailed(objectMapper, httpServletResponse, 1004, "API interface limited by system.");
}
}
}
非必須肮帐。配置默認(rèn)加注解 @SentinelResource(value = "abcdd",fallbackClass = DefaultBlockFallbackHandler.class, defaultFallback = "defaultFallback") 返回的異常咖驮,可以做默認(rèn)異常配置。如果默認(rèn)不符合異常需求训枢,可以在各接口自己定制自己的異常托修,可以是同類,也可以是不同類(不同類必須指定fallbackClass屬性恒界,參照上面注解例子)
以下走的默認(rèn)異常 或 不同類異常睦刃,不在同一個(gè)類中(默認(rèn)異常加fallbackClass )
@GetMapping(value = "/woqu")
@SentinelResource(value = "abcdd",
fallbackClass = DefaultBlockFallbackHandler.class, defaultFallback = "defaultFallback")
public String woqu(String name){
if(name.equals("abc")){
throw new IllegalArgumentException("adffdgdfg");
}
if(name.equals("bbb")){
try {
Thread.sleep(900);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return "aaaaaaasdjkl" + name;
}
以下走的自定義異常(可以用同一個(gè)類中的,也可以用不同類中的十酣,不同類加fallbackClass 屬性)
@SentinelResource(value = "chello", blockHandler = "exceptionHandler2", defaultFallback = "fallback2")
@GetMapping(value = "/hello")
public String abcd(@RequestParam(value = "name") String name, int age){
log.info("name={}",name);
log.info("age={}",age);
String content = nacosProviderClient.abc(name, age);
return "我是feign: " + content;
}
public String exceptionHandler2(String name, int age, BlockException ex) {
// Do some log here.
ex.printStackTrace();
return "Oops, error occurred at "+ name + ","+ age;
}
public String fallback2(Throwable e){
log.info("進(jìn)入sentinelResource注解測(cè)試,進(jìn)入fallback2涩拙,參數(shù)b={}", e.getMessage());
return "defaultFallback";
}
最后CustomRequestOriginParser這個(gè)只要是給授權(quán)黑名單或ip(AuthorityRule)生效的,可以各微服務(wù)自定義耸采,這里放到基礎(chǔ)包只是演示
簡(jiǎn)單實(shí)現(xiàn)黑名單功能:
@Component
public class CustomRequestOriginParser implements RequestOriginParser {
@Override
public String parseOrigin(HttpServletRequest request) {
// <X> 從 Header 中兴泥,獲得請(qǐng)求來(lái)源
String origin = request.getHeader("s-user");
// <Y> 如果為空,給一個(gè)默認(rèn)的
if (StringUtils.isEmpty(origin)) {
origin = "default";
}
return origin;
}
}
簡(jiǎn)單實(shí)現(xiàn)ip限流功能:
@Component
public class CustomRequestOriginParser2 implements RequestOriginParser {
@Override
public String parseOrigin(HttpServletRequest request) {
String ip = null;
try {
ip = Inet4Address.getLocalHost().getHostAddress();
} catch (UnknownHostException e) {
e.printStackTrace();
}
String u = request.getRemoteUser();
return ip;
}
}
微服務(wù)處理
上面基礎(chǔ)包搭建完畢虾宇,各微服務(wù)只需引入上述基礎(chǔ)包:
<!-- sentinel自定義封裝組件 -->
<dependency>
<groupId>com.myyshop.framework</groupId>
<artifactId>sentinel-spring-boot-starter</artifactId>
<version>1.0.0-RELEASE</version>
</dependency>
整合Nacos持久化需要引入基礎(chǔ)包(上面有)搓彻。配置則引入如下配置:
注意:下面配置的信息,只有server-addr中的地址修改成自己nacos-config的地址即可,其余信息都不需要?jiǎng)有癖幔绕涫莋roupId竭沫、dataId的格式(應(yīng)用名-flow-rules、應(yīng)用名-degrade-rules骑篙、應(yīng)用名-system-rules蜕提、應(yīng)用名-authority-rules、應(yīng)用名-param-flow-rules)靶端、rule-type不能修改谎势,這個(gè)是與sentinel控制臺(tái)源碼中推送數(shù)據(jù)到nacos約定好的。
spring:
cloud:
sentinel:
transport:
dashboard: 192.168.128.36:8081 #主要改這里杨名,其他基本不用動(dòng)
datasource:
# 名稱隨意脏榆,流控?cái)?shù)據(jù)規(guī)則配置
flow:
nacos:
server-addr: ${spring.cloud.nacos.config.server-addr}
dataId: ${spring.application.name}-flow-rules
groupId: SENTINEL_GROUP
# 規(guī)則類型,取值見:
# org.springframework.cloud.alibaba.sentinel.datasource.RuleType
rule-type: flow
namespace: ${myyshop.sentinel.nacos.namespace}
# 名稱隨意台谍,流控?cái)?shù)據(jù)規(guī)則配置
degrade:
nacos:
server-addr: ${spring.cloud.nacos.config.server-addr}
dataId: ${spring.application.name}-degrade-rules
groupId: SENTINEL_GROUP
rule-type: degrade
namespace: ${myyshop.sentinel.nacos.namespace}
# 名稱隨意须喂,流控?cái)?shù)據(jù)規(guī)則配置
system:
nacos:
server-addr: ${spring.cloud.nacos.config.server-addr}
dataId: ${spring.application.name}-system-rules
groupId: SENTINEL_GROUP
rule-type: system
namespace: ${myyshop.sentinel.nacos.namespace}
# 名稱隨意,流控?cái)?shù)據(jù)規(guī)則配置
authority:
nacos:
server-addr: ${spring.cloud.nacos.config.server-addr}
dataId: ${spring.application.name}-authority-rules
groupId: SENTINEL_GROUP
rule-type: authority
namespace: ${myyshop.sentinel.nacos.namespace}
# 名稱隨意趁蕊,流控?cái)?shù)據(jù)規(guī)則配置
param:
nacos:
server-addr: ${spring.cloud.nacos.config.server-addr}
dataId: ${spring.application.name}-param-flow-rules
groupId: SENTINEL_GROUP
rule-type: param-flow
namespace: ${myyshop.sentinel.nacos.namespace}
eager: true #控制臺(tái)熱加載坞生,false為懶加載(第一次調(diào)用接口才會(huì)加載)
Sentinel控制臺(tái)整合Nacos實(shí)現(xiàn)推拉
使用Sentinel Dashboard動(dòng)態(tài)推、拉數(shù)據(jù)同步到Nacos掷伙。
個(gè)人改Alibaba重新封裝控制臺(tái)源碼:(分支 lbj_release-1.7)
具體實(shí)現(xiàn)可以參考我整理的如下大佬們的文檔:
- https://www.liangzl.com/get-article-detail-139092.html
- https://blog.csdn.net/dsh153/article/details/105767733 (跟下面那個(gè)一樣)
- https://blog.csdn.net/weixin_41213402/article/details/105510373?utm_medium=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.add_param_isCf&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.add_param_isCf
- http://blog.didispace.com/spring-cloud-alibaba-sentinel-2-4/
- https://blog.csdn.net/LSY_CSDN_/article/details/105114573?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-3.add_param_isCf&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-3.add_param_isCf (有源碼1.6.2版本的)
- https://blog.csdn.net/lilizhou2008/article/details/97075236?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.add_param_isCf&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.add_param_isCf (主要參照這個(gè)的)
- https://blog.csdn.net/zhulin2012/article/details/100987420?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2.add_param_isCf&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2.add_param_isCf
網(wǎng)關(guān)限流(Spring Cloud Gateway)
網(wǎng)關(guān)限流文檔(官方)
Sentinel提供針對(duì)Spring Cloud Gateway的參數(shù)開關(guān)是己,該開關(guān)是針對(duì)JVM -D(java -jar -Dproject.name=***)的開關(guān),如果要開啟Sentinel控制臺(tái)對(duì)網(wǎng)關(guān)特定頁(yè)面的開關(guān)任柜,則需要配置如下(最主要是-Dcsp.sentinel.app.type=1):
-Dcsp.sentinel.dashboard.server=192.168.128.36:8081 -Dcsp.sentinel.app.type=1
如果application.yml中有如下配置卒废,則不需要加入(
-Dcsp.sentinel.dashboard.server=192.168.128.36:8081)
spring:
cloud:
sentinel:
transport:
dashboard: 192.168.128.36:8081
添加上述jar包啟動(dòng)參數(shù)之后,需要加入如下pom包依賴(引包順序要嚴(yán)格按照下面這樣宙地,測(cè)試換位置不生效摔认,不知道為什么)
<!--sentinel gateway依賴包-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
引入上述pom包依賴后,還需加入如下配置(主要是Nacos持久化配置):
spring:
cloud:
sentinel:
transport:
dashboard: 192.168.128.36:8081 #主要改這里宅粥,其他基本不用動(dòng)
datasource:
gw-flow:
nacos:
server-addr: ${spring.cloud.nacos.config.server-addr}
dataId: ${spring.application.name}-gw-flow-rules
groupId: SENTINEL_GROUP
rule-type: gw-flow
namespace: ${myyshop.sentinel.nacos.namespace}
gw-api-group:
nacos:
server-addr: ${spring.cloud.nacos.config.server-addr}
dataId: ${spring.application.name}-gw-api-rules
groupId: SENTINEL_GROUP
rule-type: gw-api-group
namespace: ${myyshop.sentinel.nacos.namespace}
degrade:
nacos:
server-addr: ${spring.cloud.nacos.config.server-addr}
dataId: ${spring.application.name}-degrade-rules
groupId: SENTINEL_GROUP
rule-type: degrade
namespace: ${myyshop.sentinel.nacos.namespace}
eager: true #控制臺(tái)熱加載参袱,false為懶加載(第一次調(diào)用接口才會(huì)加載)
網(wǎng)關(guān)需要加入配置(主要是異常相關(guān)格式化,還有代碼限流粹胯、降級(jí)等)
@Configuration
public class SentinelGatewayConfig {
private final List<ViewResolver> viewResolvers;
private final ServerCodecConfigurer serverCodecConfigurer;
public SentinelGatewayConfig(ObjectProvider<List<ViewResolver>> viewResolversProvider,
ServerCodecConfigurer serverCodecConfigurer) {
this.viewResolvers = viewResolversProvider.getIfAvailable(Collections::emptyList);
this.serverCodecConfigurer = serverCodecConfigurer;
}
// @Bean
// @Order(Ordered.HIGHEST_PRECEDENCE)
// public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler() {
// // Register the block exception handler for Spring Cloud Gateway.
// return new SentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer);
// }
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler() {
return new JsonSentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer);
}
// @Bean
// @Order(-1)
// public GlobalFilter sentinelGatewayFilter() {
// return new SentinelGatewayFilter();
// }
/*****************************************************************************/
//以下是添加 API 分組和route 維度
// @PostConstruct
// public void doInit() {
// initCustomizedApis();
// initGatewayRules();
// }
private void initCustomizedApis() {
Set<ApiDefinition> definitions = new HashSet<>();
ApiDefinition api1 = new ApiDefinition("some_customized_api")
.setPredicateItems(new HashSet<ApiPredicateItem>() {{
add(new ApiPathPredicateItem().setPattern("/Hansen666666"));
// add(new ApiPathPredicateItem().setPattern("/gprovider/**")
// .setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_PREFIX));
}});
definitions.add(api1);
GatewayApiDefinitionManager.loadApiDefinitions(definitions);
}
/**
* 配置限流規(guī)則
*/
private void initGatewayRules() {
Set<GatewayFlowRule> rules = new HashSet<>();
rules.add(new GatewayFlowRule("some_customized_api")
.setResourceMode(SentinelGatewayConstants.RESOURCE_MODE_CUSTOM_API_NAME)
.setCount(2)
.setIntervalSec(1)
);
// rules.add(new GatewayFlowRule("csdn")
// .setCount(1)
// .setIntervalSec(1)
// );
//
// rules.add(new GatewayFlowRule("gateway-provider")
// .setCount(3) // 限流閾值
// .setIntervalSec(1) // 統(tǒng)計(jì)時(shí)間窗口蓖柔,單位是秒,默認(rèn)是 1 秒
// );
GatewayRuleManager.loadRules(rules);
}
}
添加自定義異常處理(不加會(huì)返回默認(rèn)的錯(cuò)誤信息Blocked by Sentinel: XXXX)风纠,下面為模仿源碼寫的自定義異常處理况鸣。
1.繼承SentinelGatewayBlockExceptionHandler類(主要為了重寫handle方法)
2.參照DefaultBlockRequestHandler類源碼handleRequest方法和acceptsHtml方法(這個(gè)是SpringBoot默認(rèn)返回的404異常頁(yè)面,也就是說(shuō)在網(wǎng)頁(yè)上會(huì)有404異常竹观,在Postman會(huì)顯示Json異常)
public class JsonSentinelGatewayBlockExceptionHandler extends SentinelGatewayBlockExceptionHandler {
public JsonSentinelGatewayBlockExceptionHandler(List<ViewResolver> viewResolvers, ServerCodecConfigurer serverCodecConfigurer) {
super(viewResolvers, serverCodecConfigurer);
}
@Override
public Mono<Void> handle(ServerWebExchange exchange, Throwable ex) {
if (exchange.getResponse().isCommitted()) {
return Mono.error(ex);
} else {
return !BlockException.isBlockException(ex) ? Mono.error(ex) : this.handleBlockedRequest(exchange, ex).flatMap((response) -> {
if(ex instanceof ParamFlowException){
return WebfluxResponseUtil.responseFailed(exchange, 10000, HttpStatus.TOO_MANY_REQUESTS.value(),
"API interface limited flow by gateway.");
}
if(ex instanceof DegradeException){
return WebfluxResponseUtil.responseFailed(exchange, 10001, HttpStatus.SERVICE_UNAVAILABLE.value(),
"API interface degraded by gateway.");
}
return WebfluxResponseUtil.responseFailed(exchange, 10000, HttpStatus.TOO_MANY_REQUESTS.value(),
"API interface limited flow by gateway.");
});
}
}
private Mono<ServerResponse> handleBlockedRequest(ServerWebExchange exchange, Throwable throwable) {
return GatewayCallbackManager.getBlockHandler().handleRequest(exchange, throwable);
}
}
Sentinel控制臺(tái)
控制臺(tái)文檔(官方)
https://github.com/alibaba/Sentinel/wiki/%E6%8E%A7%E5%88%B6%E5%8F%B0
目前集成控制臺(tái)有兩種方式:
- 官方j(luò)ar下載地址镐捧,下載最新版本
- 官方源碼編譯獲取jar
- 自己改官方源碼增加Nacos持久化
各自問(wèn)題參照官方FAQ
https://github.com/alibaba/Sentinel/wiki/FAQ
Sentinel的JVM -D啟動(dòng)參數(shù)配置項(xiàng)潜索,包含log文件指定路徑(官方)
https://github.com/alibaba/Sentinel/wiki/%E5%90%AF%E5%8A%A8%E9%85%8D%E7%BD%AE%E9%A1%B9
控制臺(tái)VM啟動(dòng)參數(shù)
下載jar或重新編譯源碼獲取jar后,然后添加啟動(dòng)參數(shù)啟動(dòng)控制臺(tái)懂酱,idea VM options啟動(dòng)參數(shù)如下:
-Dserver.port=8081
-Dcsp.sentinel.api.port=8723
-Dcsp.sentinel.dashboard.server=localhost:8081
-Dproject.name=sentinel-lbj
-Dserver.servlet.session.timeout=864000
-Dnacos.serverAddr=localhost:8849
-Dnacos.namespace=d4178075-92ee-429a-baad-6cd01d59b9b8
上面配置解釋:
- -Dserver.port 為控制臺(tái)啟動(dòng)端口號(hào)(不配置默認(rèn)8080)
- -Dcsp.sentinel.api.port Sentinel 客戶端監(jiān)控微服務(wù) API 的端口竹习,默認(rèn)8719
- -Dcsp.sentinel.dashboard.server 默認(rèn)控制臺(tái)提供的監(jiān)控(就是sentinel-dashboard的地址),此配置必須配置列牺,如不配置整陌,則沒(méi)有sentinel-dashboard默認(rèn)控制臺(tái)
- -Dproject.name 默認(rèn)控制臺(tái)名稱,默認(rèn)sentinel-dashboard
- -Dserver.servlet.session.timeout 控制臺(tái)登錄過(guò)期時(shí)間瞎领,可以指定分鐘泌辫,默認(rèn)30分鐘,如 7200 表示 7200 秒九默,60m 表示 60 分鐘
- -Dnacos.serverAddr 官方?jīng)]有此配置震放,自己加的自定義配置,指定Nacos配置中心地址
- -Dnacos.namespace 官方?jīng)]有此配置驼修,自己加的自定義配置殿遂,指定Nacos持久化命名空間,例如本項(xiàng)目命名空間是sentinel(d4178075-92ee-429a-baad-6cd01d59b9b8)
linux 或 windows 下啟動(dòng)
下載官方j(luò)ar或拿到源碼編譯后得到j(luò)ar乙各,直接執(zhí)行下面代碼:
nohup
java -Dserver.port=8081
-Dcsp.sentinel.dashboard.server=localhost:8081
-Dnacos.namespace=d4178075-92ee-429a-baad-6cd01d59b9b8
-Dnacos.serverAddr=localhost:8849
-Dsentinel.dashboard.auth.username=myyshop
-Dsentinel.dashboard.auth.password=myyshop
-jar
sentinel-dashboard.jar &
上述是Linux后臺(tái)運(yùn)行方式墨礁,加入 nohup **** & 就會(huì)后臺(tái)運(yùn)行,否則關(guān)閉服務(wù)就斷了觅丰。Windows類似饵溅,不需要加 nohup **** &
Sentinel控制臺(tái)部分源碼(sentinel-dashboard)
以Nacos限流配置為例,F(xiàn)lowRuleNacosPublisher 發(fā)布配置到Nacos妇萄,發(fā)布的配置是JSON格式,但是JSON沒(méi)有格式化咬荷,不美觀冠句,以下推送到Nacos實(shí)現(xiàn)美化JSON功能,這樣容易改Nacos配置幸乒。
@Component("flowRuleNacosPublisher")
public class FlowRuleNacosPublisher implements DynamicRulePublisher<List<FlowRuleEntity>> {
@Autowired
private ConfigService configService;
@Autowired
private Converter<List<FlowRuleEntity>, String> converter;
@Override
public void publish(String app, List<FlowRuleEntity> rules) throws Exception {
AssertUtil.notEmpty(app, "app name cannot be empty");
if (rules == null) {
return;
}
//lbj增加格式化懦底、美化json內(nèi)容 - start
String content = converter.convert(rules);
JSONArray jsonArray = JSONArray.parseArray(content);
String prettyContent = JSON.toJSONString(jsonArray, SerializerFeature.PrettyFormat, SerializerFeature.WriteMapNullValue,
SerializerFeature.WriteDateUseDateFormat);
//lbj增加格式化、美化json內(nèi)容 - end
configService.publishConfig(app + NacosConfigUtil.FLOW_DATA_ID_POSTFIX,
NacosConfigUtil.GROUP_ID, prettyContent);
}
}
自己加入Nacos的 -Dnacos.serverAddr 和 -Dnacos.namespace 相關(guān)參數(shù)罕扎,這樣可以直接在JVM -D啟動(dòng)參數(shù)動(dòng)態(tài)加聚唐,而不用改源碼,再編譯腔召,這樣很麻煩:
@Bean
public ConfigService nacosConfigService() throws Exception {
String namespace = System.getProperty("nacos.namespace");
String serverAddr = Optional.ofNullable(System.getProperty("nacos.serverAddr")).orElse("localhost:8848");
Properties properties = new Properties();
properties.put(PropertyKeyConst.SERVER_ADDR, serverAddr);
if(StringUtil.isNotBlank(namespace)){
properties.setProperty(PropertyKeyConst.NAMESPACE, namespace);
}
return ConfigFactory.createConfigService(properties);
}
上述配置在 NacosConfig 類中掏呼,通過(guò)System.getProperty獲取啟動(dòng)參數(shù)盅安,
獲取 namespace 和 serverAddr。
踩坑記錄
如果你的微服務(wù)部署在docker中,并且沒(méi)有指定host模式(--net=host),默認(rèn)docker會(huì)走bridge模式己儒,那這樣docker鏡像內(nèi)部會(huì)分配自定義虛擬ip,這樣跟主機(jī)互ping沒(méi)有問(wèn)題,但在一個(gè)局域網(wǎng)其他網(wǎng)段ip要想ping通docker內(nèi)部虛擬ip就不行了豫领。
而現(xiàn)在國(guó)內(nèi)大部分公司都是使用docker去部署服務(wù),Sentinel默認(rèn)拿到ip是docker內(nèi)部的虛擬ip舔琅,如果Sentinel部署在另外一臺(tái)機(jī)器上(生產(chǎn)都要分開部署)等恐,則拿不到當(dāng)前服務(wù)的簇點(diǎn)鏈路,因?yàn)镾entinel訪問(wèn)不到當(dāng)前服務(wù)的端口數(shù)據(jù)(假如當(dāng)前服務(wù)ip+端口=172.17.0.3:8719)备蚓。
注:172.17.0.3是docker內(nèi)部虛擬ip鼠锈,192.168.128.20是主機(jī)ip,8719是Sentinel客戶端監(jiān)控微服務(wù) API 的端口星著,默認(rèn)8719
host模式:使用 --net=host 指定购笆。
none模式:使用 --net=none 指定。
bridge模式:使用 --net=bridge 指定虚循,默認(rèn)設(shè)置同欠。
container模式:使用 --net=container:NAME_or_ID 指定。
因此有兩種方案可以解決此情況:
- 將docker默認(rèn)模式改成host模式(--net=host)
- 將Sentinel要監(jiān)控的API端口(默認(rèn)8719)從docker中綁定到宿主機(jī)上横缔,可以使用 -p 參數(shù)顯式將一個(gè)或者一組端口從容器里綁定到宿主機(jī)上(ip:hostPort:containerPort)
第一套方案不用說(shuō)了铺遂,如果選定第二套方案,需要每個(gè)微服務(wù)都配置一個(gè)端口暴露給Sentinel茎刚,如果10個(gè)微服務(wù)襟锐,就暴露10個(gè),不能重復(fù)膛锭,需要如下配置(這里以u(píng)aa為例):
spring:
cloud:
sentinel:
transport:
client-ip: 192.168.128.20 #如果是docker容器粮坞,需要指定宿主機(jī)ip,這樣sentinel就不會(huì)拉docker內(nèi)虛擬ip
port: 8777 #指定Sentinel客戶端監(jiān)控微服務(wù)API的端口初狰,
上述ip+端口配置好后(以u(píng)aa為例)莫杈,通過(guò)docker run啟動(dòng)uaa服務(wù)(這里uaa暴露給sentinel的端口是8777,其他服務(wù)端口盡量不能重復(fù))奢入,配置如下:
docker run -p 7001:7001 -p 8777:8777 --name ${app_name} \
--link registry2:registry2 \
-v /usr/local/server-log/uaa-server:/logs \
-d ${app_name}:latest
注:8777是Sentinel客戶端監(jiān)控 uaa的端口筝闹,不指定默認(rèn)8719