Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

开源之夏-SOFADashboard 整体功能细分 #59

Open
glmapper opened this issue Jul 6, 2021 · 5 comments
Open

开源之夏-SOFADashboard 整体功能细分 #59

glmapper opened this issue Jul 6, 2021 · 5 comments

Comments

@glmapper
Copy link
Collaborator

glmapper commented Jul 6, 2021

https://summer.iscas.ac.cn/#/org/prodetail/210170198

@ashlee618
Copy link

计划

  • 实时性:使用定时任务、从注册中心获取服务列表和消费者生产者列表。
  • 可视化:提高用户对服务可视化的体验,提高用户获取服务的信息量。
  • 兼容性:用户可选择zk或sofa registry 作为注册中心,甚至兼容其他比如Eureka、consul等
  • 高性能:可使用缓存数据库如redis提高性能。
  • 易管理:增删改查 停用 复用,增加服务权限管理。

实时性

现在的版本是检测节点变化,然后读取数据。但是消费者消费了之后是不能同步到dashboard上面,要重新部署dashboard才能获取,我们可以设置一个定时任务,进行同步数据。

  • 可以选择timer
  • 定时任务线程池
  • quartz quartz集群

可视化

可以增加一个服务状态(正常使用、已停止、已过期等)、服务调用次数用来进行负载调优等。服务新增时间(gmt_create)、服务修改时间(gmt_modified),给服务备别名或前端新增备注行。

  • 服务状态

  • 服务插入、修改时间

  • 服务别名(备注)

  • 提供服务的作者

  • 给服务分组(类似收藏夹的功能)

  • 可以用ECharts完成图表显示模块

兼容性:

我们可以制作一个下拉框来让用户选择使用哪个注册中心,增加兼容性。

高性能:

使用Nosql数据库进行缓存,比如redis。

易管理

对服务的增删改查。

增:现在都是用sofa-rpc进行服务的发布,以后是否可以考虑把这一步骤,也合并到sofaDashboard上?不过这样要解决一个问题,就是对java代码的检查,相当于要写一个类似头歌(educoder)那样的编译器。

删:移除服务或停用服务?

改:修改服务信息(备注、分组、权限、状态)。

查:通过多个维度(比如应用名、服务名、作者、分组名...)

权限设置:

可以对服务进行权限限制,比如A这个服务提供给甲客人和乙客人,对于甲可以调用A中的get方法和set方法,但是对于乙客人只能调用get方法。

细节问题:

需要修复的问题:

1、如果先点上面的应用维度进行搜索,然后再切回服务维度,会出现额外多处几行空白的。
image-20210718171655610

2、当生产者停止提供某一服务时,应当给予用户明显的提醒,告知该服务已停用。

image-20210718171730026

架构图:

功能模块

@glmapper
Copy link
Collaborator Author

1、可以暂时不考虑对于 ark 部分的支持,如果在实际的开发过程中,有影响,可以将 ark 部分能力关闭掉(功能要支持插件化)
2、权限部分不需要具体实现,可以基于 Oauth2、spring security 搭一个框架出来,后续托管社区来实现对接具体平台
3、注册中心部分是重点需要关注的,由于 zk 和 sofaregistry 在模型上是不同的,所以在定义模型时,可以适当考虑冗余,由一个模型来呈现
4、重点是对 sofaregistry 的集成,现在的注册信息是通过 rest api 获取的,当数据量变大时,同步获取会有一定延迟,异步去拉则存在一定的时效性问题;这块需要和 sofaregistry 同学一块看下,基于事件方式,由服务端直接推过来是不是更合理。

@ashlee618
Copy link

对于缓存的探讨

目前的服务信息数据是从SOFARegistry中通过rest api获取数据,然后存入缓存中。

当运行时和检测到数据变化时,都会再通过rest api再获取一次然后存入缓存中。

这个缓存的大概实现是用3个ConcurrentHashMap实现

    private Map<String, RpcService>        serviceMap  = new ConcurrentHashMap<>();
    private Map<String, List<RpcConsumer>> consumerMap = new ConcurrentHashMap<>();
    private Map<String, List<RpcProvider>> providerMap = new ConcurrentHashMap<>();

也就是说当前的实现方法是直接把 数据存储到项目内存里面。

好处是:存取速度快 吞吐量大

坏处是:

  • 当数据量大的时候 会占用很多的系统资源 让系统变得很 “重” 影响其他服务
  • 不利于持久化存储

解决办法

原先的数据流向示意图:
image

现在的数据流向示意图:
image

保留通过 rest api 从SOFARegistry获取数据,在此基础上,抛弃了之前存入ConcurrentHashMap的方法。我们可以直接把数据封装后存入redis里面。

image

接口压力测试

采用jemeter 对/api/service/all-service?query接口进行测试,该接口是获取所有的服务列表。

测试分别采用了1个服务1个生产者1个消费者和1个服务1000个生产者1000个消费者的模式。

image

image-20210724103849350

image-20210724103903528

1个服务1个生产者1个消费者

未使用redis:

Label # 样本 平均值 中位数 90% 百分位 95% 百分位 99% 百分位 最小值 最大值 异常 % 吞吐量 接收 KB/sec 发送 KB/sec
HTTP请求 10000 521 426 1476 1521 1602 1 1847 0.00% 1968.5039370078741 667.0613927165355 547.7135365403543
HTTP请求 50000 541 463 1186 1367 1721 1 1935 0.0008% 2256.215874734895 764.9731651324398 627.8688313647849
HTTP请求 100000 504 365 1115 1347 1725 1 2062 1.40% 1858.6669640533808 689.7317435341623 510.0682573661295
HTTP请求 100000 508 383 1181 1372 1604 1 2118 0.32% 1780.6901955197834 616.7849666343175 493.98359733920364
HTTP请求 100000 566 331 1350 1572 2147 1 2918 0.382% 1487.8074182077871 517.2565319395057 412.59512087505396

使用了redis:

Label # 样本 平均值 中位数 90% 百分位 95% 百分位 99% 百分位 最小值 最大值 异常 % 吞吐量 接收 KB/sec 发送 KB/sec
HTTP请求 10000 529 365 1283 1570 2024 1 2425 0.00% 1967.7292404565133 666.7988734750098 547.5394929407713
HTTP请求 50000 667 537 1480 1731 2061 2 2474 0.49% 2034.4224274728404 712.5415944022866 563.3703910795663
HTTP请求 100000 551 337 1268 1704 2354 1 2881 0.39% 1669.1425614661748 580.6460898770677 462.7642167913655
HTTP请求 100000 676 581 1481 1679 2094 2 2901 1.521 1399.5605379910708 523.2841770496565 383.6158122130901
HTTP请求 100000 660 466 1482 1802 2143 2 2622 0.08% 1555.185766939861 529.9021824602261 432.5503327368548

1个服务1000个消费者1000个提供者

未使用redis:

Label # 样本 平均值 中位数 90% 百分位 95% 百分位 99% 百分位 最小值 最大值 异常 % 吞吐量 接收 KB/sec 发送 KB/sec
HTTP请求 10000 512 515 1029 1163 1199 1 1434 1.37% 1974.7235387045814 742.8786532385466 542.1462699323657
HTTP请求 50000 484 352 1203 1456 1792 1 2449 1.89% 2131.0147892426376 826.7616916128585 581.8186895990495
HTTP请求 100000 490 349 1197 1367 1665 1 2268 0.80% 2164.7364433380235 786.4666765883753 597.6206290927048
HTTP请求 100000 416 254 956 1188 1639 1 2524 0.15% 1924.001924001924 670.2368927368927 534.6023854617605
HTTP请求 100000 11 2 7 24 513 1 2461 0.00% 1898.97455374098 654.6269701860996 528.5554218690658

使用了redis:

Label # 样本 平均值 中位数 90% 百分位 95% 百分位 99% 百分位 最小值 最大值 异常 % 吞吐量 接收 KB/sec 发送 KB/sec
HTTP请求 10000 11110 11409 17174 18399 19594 37 20467 0.00% 315.52709 108.77 87.8
HTTP请求 50000 20354 19321 32624 35311 38081 20 46476 0.30% 271.22322 95.37 75.26
HTTP请求 100000 12064 10912 23051 31654 37782 20 46235 0.78% 257.6755092 93.46176579 71.15649046
HTTP请求 100000 22046 22044 35130 38246 41616 20 44853 1.22% 255.7132738 95.34609034 70.29910252
HTTP请求 100000 19110 18760 31679 36184 45397 20 49494 3.86% 267.8143404 116.0746346 71.66292504

分析并总结

在第一个1个服务1个消费者1个生产者模型中,可以看到两者的异常率和吞吐量是差不多的,原先内存缓存级别的会比用了redis的速度稍微更快一点

在第二个模型中,两者的差别明显变大了。在吞吐量中,原先的缓存比采用redis的高了10倍左右。我的测试环境用的是redis单机测试的,如果采用redis集群可能会更好,而且redis跟项目是一起运行的,电脑带不动2个。但我们也非常明显看得出原先的缓存方法确实在速度上是远超redis。

总结:
本次提出的将数据迁移到redis上,想法的初衷是想着当数据量越来越大的时候,使用ConcurrentHashMap存储数据,无疑是在项目里面装了个小型数据库一样,迁移出来能给系统做到一个瘦身的效果,但这同时也意味着失去了原先的高吞吐量,高响应的性能,但是能得到减轻系统、对于数据易管理、易分析等好处。

@glmapper
Copy link
Collaborator Author

@ashlee618 我的意见是不绑定到具体的实现上去,可以在接口层面提供好扩展,redis 作为存储的一种实现,基本 map 的 mem 存储也是一种实现,当然也可以扩展其他存储

@ashlee618
Copy link

ashlee618 commented Aug 1, 2021

基于Websocket+Netty+Mq实现消息实时推送

业务需求

实现高并发消息实时性地从SofaDashboard Client中把应用和实例消息推送到SoDashboard,进行展示。

1627796706

需克服难点

  • 大数据量的高并发
  • 消息推送的实时性
  • 解决短轮询的大量无效消息
  • 解决长轮询的资源消耗
  • 消息的不丢失性

技术选型

这套技术体系参考与京东的京东到家中打印小票流程的实现,和抖音上用户关注的作者视频消息推送的实现。

1、采用WebSocket长连接的特点,实现消息实时性推送。
2、采用Netty作为WebSocket的容器,比较主流的有netty、tomcat、socketIO 三个框架。
1627796956(1)
3、 采用RabbitMq作为消息队列,也可以用其他优秀的MQ框架比如Kafuka、RocketMq等。

消息交互架构图

image

消息丢失

image

生产者、消费者、消息队列这三个部分任何一个部分出问题都会导致消息丢失。

1、消息队列发生故障

在RabbitMq中,消息默认是以内存的方式存储,当Mq出现宕机时,消息就会全部丢失,这是我们不想看到的。以RabbitMq为例子,具体操作:对交换机(Exchange)、队列(Queue)、消息(Message)进行可持久化存储。

2、消费者发生故障

当消费者消费逻辑复杂时间太长的消息、超时、消费者被停机或网络问题,会导致消息无法传递到消费者手中。

具体操作:设置手动Ack模式。

3、生产者发生故障

生产者会遇到一个问题,就是不知道消息是否到达Mq中。

具体操作:设置Confirm机制和Return机制。如果没有到达可以做重试和异常处理。

消息补偿机制

当生产者正在把消息发送给Mq途中宕机,或者是Mq正在把消息进行可持久化存储时宕机,都会导致消息”胎死腹中“。

因此我们可以设置一个消息补偿机制,对这些异常的消息进行重新发送或者判定为放弃该消息的发送。

image

具体操作:需要把消息不断地都存储在db中,然后设置一个定时任务一直去读取db里面的消息,判断是否需要进行补偿重发,如果补偿次数过多,就把该消息判定为异常消息放弃发送。

总结

这套技术体系不仅可以应用在这里,也可以为后面SofaRegistry和SofaDashboard服务信息的传递。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants