Dubbo学习系列之十一(Dashboard+Nacos规则推送)

中国武术,门派林立,都是号称多少代的XXX传人,结果在面对现代武术时,经常被KO秒杀,为啥,光靠宣传和口号撑门面,终究是靠不住,必须得有真货 ,得经得住考验,所以不能只说Sentinel有多好,也得给出些证据,那么,前文实践了规则生成和使用,再来看看SentinelDashboard,体验下是否真如宣传的那么强大,并配合Nacos做规则统一配置和推送,下面我们就来操作一把,内容较多,分两部分。

工具: Idea201902/JDK11/ZK3.5.5/Gradle5.4.1/RabbitMQ3.7.13/Mysql8.0.11/Lombok0.26/Erlang21.2/postman7.5.0/Redis3.2/RocketMQ4.5.2/Sentinel1.6.3/SpringBoot2.1.6/Nacos1.1.3

难度:  新手--战士--老兵--大师

目标

  1. 使用SentinelDashboard实现规则配置
  2. 使用Nacos实现规则统一管理、持久化和推送

步骤

整体框架依旧,多模块微服务架构商城系统后台,一个共享模块,多个功能模块,暂时无前台。

Part One:

用SentinelDashboard实现规则配置

1.先看下SentinelDashboard界面:

左侧为接入的应用和机器,并将各类规则独立管理,工作区之一就是各类监控数据,实现了规则管理的可视化操作和系统监控台。

2.引入sentinel-dubbo-adapter这个依赖,可以将Dubbo 的服务接口和方法(包括调用端和服务端)自动设置成为 Sentinel 中的资源,

compile group: ‘com.alibaba.csp‘, name: ‘sentinel-dubbo-adapter‘, version: ‘1.6.3‘
 

3.进入SentinelDashboard的jar包目录,使用命令运行:

java -Dserver.port=8718 -Dcsp.sentinel.dashboard.server=localhost:8718 -Dproject.name=sentinel-dashboard -Dcsp.sentinel.api.port=8719 -jar sentinel-dashboard.jar
  • Dserver.port=8718 控制台端口,sentinel控制台是一个spring boot程序。
  • Dcsp.sentinel.dashboard.server=localhost:8718 控制台的地址,指定控制台后客户端会自动向该地址发送心跳包。
  • Dproject.name=sentinel-dashboard  指定Sentinel控制台程序的名称
  • Dcsp.sentinel.api.port=8719 (默认8719) 客户端提供给Dashboard访问或者查看Sentinel的运行访问的参数

另外:csp.sentinel.dashboard.server这个配置是用在客户端,这里Sentinel控制台也使用是用于自己监控自己程序的api,否则无法显示控制台的api情况,当然这个也可以根据情况不显示。

启动成功,注意下图中的log地址,所有规则执行过程都有日志记录:

4.启动项目,ZK-->Redis-->RabbitMQ-->RocketMQ-->Nacos-->business-->stock-->logistic-->finance,RabbitMQ我安装为window服务,设置为自动启动,当然,其他如Redis也可以照做,只要你硬件够硬。

5.URL访问:http://localhost:8718,可以看到business模块中有dubbo的@Service注解服务都自动识别为resource,这样代码中就可以直接使用了,印证前面的第2点,

同时,前文通过代码设置的流控规则也可见于此:

this.initFlowQpsRule("saveOrder");

优先级问题:

  • 如果我这里将saveOrder阀值设置为0,结果会怎样?测试-->保存订单成功,说明代码内规则优先级高于外部配置
  • 如果有多个对同一resource的相同类型的规则,经验证,结论为按最严格规则执行,下图中情形将按照阀值为0执行热点规则:

6.再对saveOrder修改为一个“热点规则”,即可实时注入到应用内存中,规则立即生效,运行postman测试,就会显示拒绝结果!

另外:当应用关闭,那么这些规则将全部清除,因心跳检测会失败,没有应用可以使用这些规则,也说明规则并未持久化

Part Two:

使用Nacos实现规则统一管理、持久化和推送

1.背景知识:sentinel规则缓存在应用机器内存中,前篇通过API硬编码规则的方式一般仅用于测试和演示,生产上一般通过动态规则源的方式来管理规则,数据源扩展常见的实现方式有:

拉模式:客户端主动向某个规则管理中心定期轮询拉取规则,这个规则中心可以是 RDBMS、文件,甚至是 VCS 等。简单,缺点是无法及时获取变更;

推模式:规则中心统一推送,客户端通过注册监听器的方式时刻监听变化,比如使用 Nacos、Zookeeper 等配置中心。有更好的实时性和一致性保证。

流程为:配置中心控制台/Sentinel 控制台 → 配置中心 → Sentinel 数据源 → Sentinel

Sentinel 目前支持以下数据源扩展:

Pull-based: 文件、Consul (since 1.7.0)

Push-based: ZooKeeper, Redis, Nacos, Apollo

2.这里实现基于Nacos的push模式的动态流控规则,以改造stock模块为例,添加依赖:

testCompile group: ‘com.alibaba.csp‘, name: ‘sentinel-datasource-nacos‘, version: ‘1.6.3‘
 //
 compile group: ‘com.alibaba.nacos‘, name: ‘nacos-api‘, version: ‘1.1.3‘
 

3.因为SentinelDashboard原始版本没有同步Nacos的逻辑,故需要修改源码做适配,下载源码项目(V1.7.0)后,pom文件中,注释掉scope行,使datasource-nacos生效:

<!-- for Nacos rule publisher sample -->
<dependency>
  <groupId>com.alibaba.csp</groupId>
  <artifactId>sentinel-datasource-nacos</artifactId>
  <!--此行注释掉<scope>test</scope>-->
</dependency>
 

4.将\Sentinel\sentinel-dashboard\src\test\java\com\alibaba\csp\sentinel\dashboard\rule\nacos这个nacos目录复制到\Sentinel\sentinel-dashboard\src\main\java\com\alibaba\csp\sentinel\dashboard\rule下,形成如下结构:

5.看下这几个类: com.alibaba.csp.sentinel.dashboard.rule.nacos.NacosConfigUtil 工具类,实际就是定义了一些常量,略,

public final class NacosConfigUtil {

    public static final String GROUP_ID = "SENTINEL_GROUP";

    public static final String FLOW_DATA_ID_POSTFIX = "-flow-rules";
    public static final String PARAM_FLOW_DATA_ID_POSTFIX = "-param-rules";
    public static final String CLUSTER_MAP_DATA_ID_POSTFIX = "-cluster-map";

    /**
     * cc for `cluster-client`
     */
    public static final String CLIENT_CONFIG_DATA_ID_POSTFIX = "-cc-config";
    /**
     * cs for `cluster-server`
     */
    public static final String SERVER_TRANSPORT_CONFIG_DATA_ID_POSTFIX = "-cs-transport-config";
    public static final String SERVER_FLOW_CONFIG_DATA_ID_POSTFIX = "-cs-flow-config";
    public static final String SERVER_NAMESPACE_SET_DATA_ID_POSTFIX = "-cs-namespace-set";

    private NacosConfigUtil() {}
}
 

com.alibaba.csp.sentinel.dashboard.rule.nacos.NacosConfig 配置类,注入String和List<FlowRuleEntity>互相转换的转换器,这里使用了JDK8语法,返回函数:

@Configuration
public class NacosConfig {
    @Bean
    public Converter<List<FlowRuleEntity>, String> flowRuleEntityEncoder() {
        return JSON::toJSONString;
    }
    @Bean
    public Converter<String, List<FlowRuleEntity>> flowRuleEntityDecoder() {
        return s -> JSON.parseArray(s, FlowRuleEntity.class);
    }
    @Bean
    public ConfigService nacosConfigService() throws Exception {
        return ConfigFactory.createConfigService("localhost");
    }
}
 

com.alibaba.csp.sentinel.dashboard.rule.nacos.FlowRuleNacosProvider 即从Nacos获得应用的流控规则:

@Component("flowRuleNacosProvider")
public class FlowRuleNacosProvider implements DynamicRuleProvider<List<FlowRuleEntity>> {

    @Autowired
    private ConfigService configService;
    @Autowired
    private Converter<String, List<FlowRuleEntity>> converter;

    @Override
    public List<FlowRuleEntity> getRules(String appName) throws Exception {
        String rules = configService.getConfig(appName + NacosConfigUtil.FLOW_DATA_ID_POSTFIX,
            NacosConfigUtil.GROUP_ID, 3000);
        if (StringUtil.isEmpty(rules)) {
            return new ArrayList<>();
        }
        return converter.convert(rules);
    }
}
 

根据应用名,将规则发布到远程配置中心:

@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;
        }
        configService.publishConfig(app + NacosConfigUtil.FLOW_DATA_ID_POSTFIX,
            NacosConfigUtil.GROUP_ID, converter.convert(rules));
    }
}
 

6.修改com.alibaba.csp.sentinel.dashboard.controller.v2.FlowControllerV2,原文:

@Autowired
@Qualifier("flowRuleDefaultProvider")
private DynamicRuleProvider<List<FlowRuleEntity>> ruleProvider;
@Autowired
@Qualifier("flowRuleDefaultPublisher")
private DynamicRulePublisher<List<FlowRuleEntity>> rulePublisher;
 

修改要注入的Bean,使用前面复制来的Provider和Publisher,修改为:

@Autowired
@Qualifier("flowRuleNacosProvider")
private DynamicRuleProvider<List<FlowRuleEntity>> ruleProvider;
@Autowired
@Qualifier("flowRuleNacosPublisher")
private DynamicRulePublisher<List<FlowRuleEntity>> rulePublisher;
 

7.修改文件: Sentinel\sentinel-dashboard\src\main\webapp\resources\app\scripts\directives\sidebar\sidebar.html 将

<!--<li ui-sref-active="active">-->
  <!--<a ui-sref="dashboard.flow({app: entry.app})">-->
    <!--<i class="glyphicon glyphicon-filter"></i>&nbsp;&nbsp;流控规则同步</a>-->
<!--</li>-->
 

修改为如下,开启SentinelDashboard左侧的菜单:

 <li ui-sref-active="active" ng-if="entry.appType==0">
     <a ui-sref="dashboard.flow({app: entry.app})">
     <i class="glyphicon glyphicon-filter"></i>&nbsp;&nbsp;流控规则同步</a>
</li>
 

8.重新打包,运行sentinel-dashboard.jar(见第3点),重新生成的jar运行效果:

9.测试:

测试1:在SentinelDashboard【流控规则同步】中新建流控规则,规则会存储到Nacos;

测试2:直接在Nacos上修改流控规则,然后刷新SentinelDashboard,显示也会被修改;

测试3:重启Sentinel控制台,并重启微服务;刷新控制台,可以发现规则依然存在。

SentinelDashboard中建立一个流控规则:

对应Nacos中的生成的内容:

至此,完成了流控规则的持久化!其他规则持久化类似,略。

10.最后是微服务中同步Nacos,需使用sentinel-datasource-nacos依赖,然后创建 NacosDataSource 并将其注册至对应的 RuleManager 上即可: 改造下logistic模块,先写一个工具类:com.biao.mall.logistic.util.SentinelRuleUtil 注意这里的变量值是从bootstrap.yml加载的,为啥?因为从application.yml中将无法取值。

public class SentinelRuleUtil {
    // nacos server ip
    private static final String remoteAddress = "${spring.cloud.sentinel.datasource.flow.nacos.server-addr}";
    // nacos group
    private static final String groupId = "${spring.cloud.sentinel.datasource.flow.nacos.groupId}";
    // nacos dataId
    private static final String dataId = "${spring.cloud.sentinel.datasource.flow.nacos.dataId}";
    // if change to true, should be config NACOS_NAMESPACE_ID
    private static boolean isDemoNamespace = false;
    // fill your namespace id,if you want to use namespace.
    // for example: 0f5c7314-4983-4022-ad5a-347de1d1057d,you can get it on nacos‘s console
    private static final String NACOS_NAMESPACE_ID = "0f5c7314-4983-4022";

    public static void loadFlowRules(){
        // public NacosDataSource(final String serverAddr, final String groupId, final String dataId,
        //                           Converter<String, T> parser)
        ReadableDataSource<String, List<FlowRule>> flowDataSource = new NacosDataSource<>(remoteAddress,groupId,dataId,
                source -> JSON.parseObject(source, new TypeReference<List<FlowRule>>(){
                }));
        FlowRuleManager.register2Property(flowDataSource.getProperty());
    }

    private static void loadMyNamespaceRules() {
        Properties properties = new Properties();
        properties.put("serverAddr",remoteAddress);
        properties.put("namespace",NACOS_NAMESPACE_ID);
        //NacosDataSource(final Properties properties, final String groupId, final String dataId,
        //                           Converter<String, T> parser)
        ReadableDataSource<String, List<FlowRule>> flowRuleDataSource = new NacosDataSource<>(properties,groupId,dataId,
                source -> JSON.parseObject(source, new TypeReference<List<FlowRule>>() {
                }));
        FlowRuleManager.register2Property(flowRuleDataSource.getProperty());
    }
 

11.再修改下com.biao.mall.logistic.controller.DubboDeliveryController 使用注解定义一个资源,然后使用上面的Util加载从Nacos获取规则,

  @SentinelResource(value = "saveOneDelivery")
    @PostMapping("/delivery/one")
    public ResEntity<String> saveOneDelivery(@RequestBody String jsonString) throws BlockException {
//        Method method = this.getClazz().getMethod("saveOneDelivery");
//        String resourceName = "saveOneDelivery";
//        try(Entry entry = SphU.entry(resourceName)){
            JSONObject jsonObject = (JSONObject) JSON.parse(jsonString);
            String orderId = jsonObject.getString("orderId");
            //保存待发一个物流单
            deliveryService.saveLogisticSheet(orderId);
            //响应封装
            ResEntity<String> resEntity = new ResEntity<>();
            resEntity.setCode(ResConstant.SUCCESS_CODE);
            resEntity.setMsg(ResConstant.SUCCESS_STRING);
            resEntity.setData("delivery received.");
            return resEntity;
        }
 

12.在com.biao.mall.logistic.controller.DubboDeliveryController中使用静态块动态加载规则。

@RestController
public class DubboDeliveryController {

    static {
        //从Nacos动态加载规则,static标签,可以让规则只加载一次
        SentinelRuleUtil.loadFlowRules();
    }
    ...
    }
 

13.测试: 先在SentinelDashboard中建一个流控规则,然后可以看到自动同步到Nacos中,访问logistic模块,SentinelDashboard中就可以看到监控数据,贴图,略。

14.代码地址:其中的day 14

https://github.com/xiexiaobiao/dubbo-project.git

后记:

1.目前SentinelDashboard可下载官方版本为1.6.3,下载地址:https://github.com/alibaba/Sentinel/releases,1.7.0需要自行编译打包生成,生成jar包可能遇到的问题,我已在前文(Dubbo学习系列之十(Sentinel之限流与降级))中的后记部分做了些说明。

2.若RocketMQ安装在Linux虚拟机上,推荐在正常运行时,保存个Snapshot,每次使用,直接恢复到快照,快捷迅速,

3.必须注意:QPS统计的是通过REST调用的请求数量;

4.SentinelDashboard运行后,需要应用先运行产生实际流量,才会开始统计规则监控数据。

推荐阅读:

原文地址:https://www.cnblogs.com/xxbiao/p/11609949.html

时间: 2024-10-13 16:32:48

Dubbo学习系列之十一(Dashboard+Nacos规则推送)的相关文章

Dubbo学习系列之十三(Mycat数据库代理)

软件界有只猫,不用我说,各位看官肯定知道是哪只,那就是大名鼎鼎的Tomcat,现在又来了一只猫,据说是位东方萌妹子,暂且认作Tom猫的表妹,本来叫OpencloudDB,后又改名为Mycat,或许Cat更亲切?那现在就来认识下这只小猫吧. 数据库的核心地位就不说了,但现在的问题是,各种RDB,各种NoSQL交织,又是分布式.多租户的场景下,心里有没有十足的把握能稳住如此局面呢.有需求,就有市场!自然,相应的技术也应运而生,Mycat作为一款DB中间件,可以作为应用和DB间的“桥梁”,让后台DB的

Dubbo学习系列之十二(Quartz任务调度)

Quartz词义为"石英"水晶,然后聪明的人类利用它发明了石英手表,因石英晶体在受到电流影响时,它会产生规律的振动,于是,这种时间上的规律,也被应用到了软件界,来命名了一款任务调度框架--Quartz.现实软件逻辑中,周期任务有着广泛的存在,如定时刷新配置信息,定期盘点库存,定时收发邮件等,至于定时任务处理,也有Spring的ScheduledThreadPool,还有基于注解@Scheduled的方式,ScheduledThreadPool主要是基于相对时间,不方便控制,而@Sche

Dubbo学习系列之十(Sentinel之限流与降级)

各位看官,先提个问题,如果让你设计一套秒杀系统,核心要点是啥???我认为有三点:缓存.限流和分离.想当年12306大面积崩溃,还有如今的微博整体宕机情况,感觉就是限流降级没做好,"用有限的资源响应过量请求"——这就是限流降级的核心.限流降级组件,当今开源界应该是Hystrix最为出名,这也得益于SpringCloud的流行,当然,挑战者总是有的,于是Sentinel横空出世,正因实际生产使用中似乎并不多见,所以才有必要拿来一用,不然就脱离了此系列文章的主旨了,就是要见些不一样的风景!

C#微信公众号开发系列教程五(接收事件推送与消息排重)

微信公众号开发系列教程一(调试环境部署) 微信公众号开发系列教程一(调试环境部署续:vs远程调试) C#微信公众号开发系列教程二(新手接入指南) C#微信公众号开发系列教程三(消息体签名及加解密) C#微信公众号开发系列教程四(接收普通消息) C#微信公众号开发系列教程五(接收事件推送与消息排重) 在上一篇的博文中讲到,微信的消息可以大体分为两种类型,一种是包括:文本,语音,图片等的普通消息,另一种就是本篇要将的事件类型.包括:关注/取消关注事件,扫描带参数二维码事件,上报地理位置事件,自定义菜

Dubbo学习系列之六(微服务架构实战)

看了最近文章的反馈,似乎波澜不惊的样子,应该是看官觉得都是小菜,那我就直上硬菜,人狠话不多,开始!准备:Idea201902/JDK11/ZK3.5.5/Gradle5.4.1/RabbitMQ3.7.13/Mysql8.0.11/Lombok0.26/Erlang21.2/postman7.5.0 难度:新手--战士--老兵--大师 目标:1,模拟商城系统,订单服务RPC调用库存服务,同时物流服务 RPC调用订单服务(订单服务双重身份) 2.订单服务通过MQ消息机制与物流服务通信 步骤: 1.

WP8.1学习系列(第二十一章)——本地应用数据

了解如何存储和检索本地应用数据存储中的设置和文件. 路线图: 本主题与其他主题有何关联?请参阅: 使用 C# 或 Visual Basic 的 Windows 运行时应用的路线图 使用 C++ 的 Windows 运行时应用的路线图 获取应用的设置和文件容器 使用 ApplicationData.LocalSettings 属性可以获取 ApplicationDataContainer 对象中的设置.使用ApplicationData.LocalFolder 属性可以获取 StorageFold

WP8.1学习系列(第十一章)——中心控件Hub开发指南

在本文中 先决条件 什么是中心控件? 添加中心控件 将分区添加到中心 添加交互式分区头用于导航 将展示磁贴添加到中心 使用窄应用中的垂直中心 借助中心使用语义式缩放视图 摘要和后续步骤 重要的 API Hub HubSection SemanticZoom 使用 Hub 控件创建一个进入应用的入口页.Hub 控件在丰富的平移视图中显示内容,这样用户一眼就能看见新鲜有趣的内容,从而吸引他们深入了解你的应用中的更多内容. 先决条件 查看并了解 Windows 导航模式. 查看并了解中心控件指南. 我

Java学习系列(二十一)Java面向对象之注解详解

转载请注明出处:http://blog.csdn.net/lhy_ycu/article/details/45295947 一.前言 Java中的注解Annotation运用到很多方面,比如之前讲的单元测试中的@Test.Spring.SpringMVC还有其他框架等等.Java本身自带的注解也有一些,比如:@Override(子类要重写/覆写父类的对应方法).@Deprecated(表示方法不建议被使用).@SuppressWarnings(抑制警告)等等.当然,我们也可以自定义一些自己需要的

OpenFire源码学习之十八:IOS离线推送

IOS离线推送 场景: 如果您有IOS端的APP,在会话聊天的时候,用户登陆了但可能会退出了界面.这时候其他终端给目标端发送消息时候,消息可以发送到IOS的推送服务器.用过QQ的都知道,你会有哦一条消息在您的主屏上展示.这个就是利用了IOS的推送服务器呢.那么openfire只需要判断用户不在线的时候将消息推送给IOS端. 苹果服务器的消息推送都需要手机的唯一标志,也就是唯一的终端设备号.那么IOS端在登陆的时候需要将该手机的设备号传递给OF服务器.这个传递很简单,您可以自定义发送IQ消息.也可