细节内容:简历写着熟悉 Dubbo,居然连 Dubbo 线程池监控都不知道?
优采云 发布时间: 2022-10-29 10:27细节内容:简历写着熟悉 Dubbo,居然连 Dubbo 线程池监控都不知道?
程序员的成长之路
互联网/程序员/技术/数据共享
专注于
阅读这篇文章大约需要 8 分钟。
来自:互联网
Dubbo 是一个优秀的微服务框架。它以其高性能、易用性、易扩展性被广泛应用于互联网、金融保险、科技公司、制造、零售物流等领域。如今,Dubbo 框架已经成为互联网开发中比较常用的技术框架。
在 Dubbo 框架中,当客户端调用服务端时,请求到达服务端后,会有一个专门的线程池来接收参数并进行处理。因此,想要实现 Dubbo 的线程池监控,首先需要了解 Dubbo 底层对于业务线程池的实现原理。
Dubbo 底部线程池视图
我这里使用的框架是 Dubbo 2.7.8 版本,它通过一个名为 ExecutorRepository 的类来管理底层的线程池,该类负责在 Dubbo 中创建和管理线程池。通过这个扩展接口,我们可以获取到 Dubbo 实际运行的业务线程池对象。
具体处理逻辑部分如下:
package org.idea.dubbo.monitor.core.collect;
import org.apache.dubbo.common.extension.ExtensionLoader;
import org.apache.dubbo.common.threadpool.manager.DefaultExecutorRepository;
import org.apache.dubbo.common.threadpool.manager.ExecutorRepository;
import java.lang.reflect.Field;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ThreadPoolExecutor;
/**
* @Author idea
* @Date created in 7:04 下午 2022/6/29
*/
public class DubboThreadPoolCollector {
/**
* 获取Dubbo的线程池
* @return
*/
public static ThreadPoolExecutor getDubboThreadPoolInfo(){
//dubbo线程池数量监控
try {
ExtensionLoader executorRepositoryExtensionLoader = ExtensionLoader.getExtensionLoader(ExecutorRepository.class);
DefaultExecutorRepository defaultExecutorRepository = (DefaultExecutorRepository) executorRepositoryExtensionLoader.getDefaultExtension();
Field dataField = defaultExecutorRepository.getClass().getDeclaredField("data");
dataField.setAccessible(true);
ConcurrentMap data = (ConcurrentMap) dataField.get(defaultExecutorRepository);
ConcurrentMap executorServiceConcurrentMap = data.get("java.util.concurrent.ExecutorService");
//获取到默认的线程池模型
ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) executorServiceConcurrentMap.get(9090);
return threadPoolExecutor;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
好了,现在我们知道了如何在代码中实时查看 Dubbo 线程池的信息,那么接下来要做的就是如何采集这些线程池的数据,上报,最后上报通过图中显示的统计数据存储的数据。
下面,我们将根据采集展示数据,上报,展示三个链接。
采集数据
在采集的数据部分,有两种方式可以到采集,如下:
使用两种不同的模式采集得到的数据可能会有一些差异。以下是两种方法的比较:
通过实际业务场景分析,其实第二种方式的性能损失很小,甚至可以忽略不计,所以用这种方式来处理采集数据比较合适。
下面我们来看看如何用这种方式实现数据采集。
首先我们需要自己定义一个过滤器过滤器:
package org.idea.dubbo.monitor.core.filter;
import org.apache.dubbo.common.constants.CommonConstants;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.rpc.*;
import org.idea.dubbo.monitor.core.DubboMonitorHandler;
import java.util.concurrent.ThreadPoolExecutor;
import static org.idea.dubbo.monitor.core.config.CommonCache.DUBBO_INFO_STORE_CENTER;
/**
* @Author idea
* @Date created in 2:33 下午 2022/7/1
*/
@Activate(group = CommonConstants.PROVIDER)
public class DubboRecordFilter implements Filter {
@Override
public Result invoke(Invoker invoker, Invocation invocation) throws RpcException {
ThreadPoolExecutor threadPoolExecutor = DubboMonitorHandler.getDubboThreadPoolInfo();
//请求的时候趣统计线程池,当请求量太小的时候,这块的数据可能不准确,但是如果请求量大的话,就接近准确了
DUBBO_INFO_STORE_CENTER.reportInfo(9090,threadPoolExecutor.getActiveCount(),threadPoolExecutor.getQueue().size());
return invoker.invoke(invocation);
}
}
DUBBO_INFO_STORE_CENTER 的代码如下所示:
并在dubbo的spi配置文件中指定:
dubboRecordFilter=org.idea.dubbo.monitor.core.filter.DubboRecordFilter
当提供者添加这个过滤器时,如果有请求到达服务器,就会通过这个过滤器触发采集操作。
package org.idea.dubbo.monitor.core.collect;
import org.idea.dubbo.monitor.core.bo.DubboInfoStoreBO;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* Dubbo数据存储中心
*
* @Author idea
* @Date created in 11:15 上午 2022/7/1
*/
public class DubboInfoStoreCenter {
private static Map dubboInfoStoreBOMap = new ConcurrentHashMap();
public void reportInfo(Integer port, Integer corePoolSize, Integer queueLength) {
synchronized (this) {
DubboInfoStoreBO dubboInfoStoreBO = dubboInfoStoreBOMap.get(port);
if (dubboInfoStoreBO != null) {
boolean hasChange = false;
int currentMaxPoolSize = dubboInfoStoreBO.getMaxCorePoolSize();
<p>
int currentMaxQueueLength = dubboInfoStoreBO.getMaxCorePoolSize();
if (corePoolSize > currentMaxPoolSize) {
dubboInfoStoreBO.setMaxCorePoolSize(corePoolSize);
hasChange = true;
}
if (queueLength > currentMaxQueueLength) {
dubboInfoStoreBO.setMaxQueueLength(queueLength);
hasChange = true;
}
if (hasChange) {
dubboInfoStoreBOMap.put(port, dubboInfoStoreBO);
}
} else {
dubboInfoStoreBO = new DubboInfoStoreBO();
dubboInfoStoreBO.setMaxQueueLength(queueLength);
dubboInfoStoreBO.setMaxCorePoolSize(corePoolSize);
dubboInfoStoreBOMap.put(port, dubboInfoStoreBO);
}
}
}
public DubboInfoStoreBO getInfo(Integer port){
return dubboInfoStoreBOMap.get(port);
}
public void cleanInfo(Integer port) {
dubboInfoStoreBOMap.remove(port);
}
}</p>
注意这个采集类只会保存一段时间的采集数据,然后周期性的重置。
这样做的原因是利用这张图统计指定时间段内的最大线程数和队列数,然后在上报存储中心时清除这些峰值数据。
关于 DubboInfoStoreCenter 对象的定义,我把它放在了一个叫 CommonCache 的类中,如下:
package org.idea.dubbo.monitor.core.config;
import org.idea.dubbo.monitor.core.store.DubboInfoStoreCenter;
/**
* @Author idea
* @Date created in 12:15 下午 2022/7/1
*/
public class CommonCache {
public static DubboInfoStoreCenter DUBBO_INFO_STORE_CENTER = new DubboInfoStoreCenter();
}
所以在上面的过滤器中,我们可以通过静态类引用直接调用它的采集接口。
好了,现在作为一个整体,我们实现了过滤器中实时的采集线程池数据,并暂时存储在一个map表中,这个map的数据主要是记录了一段时间的采集器 角色内要使用的峰值线程池。
那么接下来,我们来看看reporter模块主要是做什么的。
报告数据
在上报数据之前,最重要的是选择合适的存储组件。首先,要上报的数据量并不大。我们可以将采集的时间设置为15秒,然后设计一个任务给采集每隔15秒上报一次dubbo线程池的数据。那么,一天需要上报 5,760 份报告。如果一次上报并存储一条记录,那么一天需要存储的数据不是很多。
另外,存储的业务数据也不需要保存太久。一般存储一周就够了,所以我最终选择了Redis来做这个存储。
每次我们实际关注的主要是三个数据字段。我将它们的定义组织成以下对象:
package org.idea.dubbo.monitor.core.bo;
/**
* @Author idea
* @Date created in 7:17 下午 2022/6/29
*/
public class ThreadInfoBO {
private Integer activePoolSize;
private Integer queueLength;
private long saveTime;
public Integer getActivePoolSize() {
return activePoolSize;
}
public void setActivePoolSize(Integer activePoolSize) {
this.activePoolSize = activePoolSize;
}
public Integer getQueueLength() {
return queueLength;
}
public void setQueueLength(Integer queueLength) {
this.queueLength = queueLength;
}
public long getSaveTime() {
return saveTime;
}
public void setSaveTime(long saveTime) {
this.saveTime = saveTime;
}
@Override
public String toString() {
return "ThreadInfoBO{" +
", queueLength=" + queueLength +
", saveTime=" + saveTime +
'}';
}
}
然后会启动一个线程任务,每隔 15 秒会执行一轮上报数据:
package org.idea.dubbo.monitor.core.report;
import com.alibaba.fastjson.JSON;
import org.idea.dubbo.monitor.core.bo.DubboInfoStoreBO;
import org.idea.dubbo.monitor.core.bo.ThreadInfoBO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import static org.idea.dubbo.monitor.core.config.CommonCache.DUBBO_INFO_STORE_CENTER;
/**
* @Author idea
* @Date created in 12:13 下午 2022/7/1
<p>
*/
public class DubboInfoReportHandler implements CommandLineRunner {
@Autowired
private IReportTemplate reportTemplate;
private static final Logger LOGGER = LoggerFactory.getLogger(DubboInfoReportHandler.class);
public static ExecutorService executorService = Executors.newFixedThreadPool(1);
public static int DUBBO_PORT = 9090;
@Override
public void run(String... args) throws Exception {
executorService.submit(new Runnable() {
@Override
public void run() {
while (true) {
try {
Thread.sleep(10000);
DubboInfoStoreBO dubboInfoStoreBO = DUBBO_INFO_STORE_CENTER.getInfo(DUBBO_PORT);
ThreadInfoBO threadInfoBO = new ThreadInfoBO();
threadInfoBO.setSaveTime(System.currentTimeMillis());
if(dubboInfoStoreBO!=null){
threadInfoBO.setQueueLength(dubboInfoStoreBO.getMaxQueueLength());
threadInfoBO.setActivePoolSize(dubboInfoStoreBO.getMaxCorePoolSize());
} else {
//这种情况可能是对应的时间段内没有流量请求到provider上
threadInfoBO.setQueueLength(0);
threadInfoBO.setActivePoolSize(0);
}
//这里是上报器上报数据到redis中
reportTemplate.reportData(JSON.toJSONString(threadInfoBO));
//上报之后,这里会重置map中的数据
DUBBO_INFO_STORE_CENTER.cleanInfo(DUBBO_PORT);
LOGGER.info(" =========== Dubbo线程池数据上报 =========== ");
} catch (Exception e) {
e.printStackTrace();
}
}
}
});
}
}</p>
这种情况下需要注意的是,Dubbo应用的线程池上报任务要等整个SpringBoot应用启动成功后再触发,否则可能会出现一些数据不准确的情况。所以在定义Bean初始化线程的时候,我选择了CommandLineRunner接口。
如果你仔细看代码,你可能会看到这样一个类:
org.idea.dubbo.monitor.core.report.IReportTemplate
这个类定义了数据报告器的基本动作,下面是它的具体代码:
package org.idea.dubbo.monitor.core.report;
/**
* 上报模版
*
* @Author idea
* @Date created in 7:10 下午 2022/6/29
*/
public interface IReportTemplate {
/**
* 上报数据
*
* @return
*/
boolean reportData(String json);
}
实现类部分如下所示:
package org.idea.dubbo.monitor.core.report.impl;
import org.idea.dubbo.monitor.core.report.IReportTemplate;
import org.idea.qiyu.cache.redis.service.IRedisService;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.time.LocalDate;
import java.util.concurrent.TimeUnit;
/**
* @Author idea
* @Date created in 7:12 下午 2022/6/29
*/
@Component
public class RedisTemplateImpl implements IReportTemplate {
@Resource
private IRedisService redisService;
private static String queueKey = "dubbo:threadpool:info:";
@Override
public boolean reportData(String json) {
redisService.lpush(queueKey + LocalDate.now().toString(), json);
redisService.expire(queueKey + LocalDate.now().toString(),7, TimeUnit.DAYS);
return true;
}
}
这里我使用列表结构来存储这些数据指标,设置一个一周的过期时间,最终存储到redis之后的格式如下:
数据显示
好了,现在我们已经完成了对线程池的监控,我们只需要设计一个管理控制台,从缓存中提取上报的数据并显示页面。
实现的逻辑比较简单,只需要定义统计图表所需的数据结构,然后在控制器中返回即可,如下图所示:
显示的最终效果如下:
随着dubbo接口请求量的变化,统计图可以展示dubbo线程池的数据变化。如果想让统计图实时显示数据,只需要在js中写一个定时调用的函数即可。
这里我使用 echart 插件来渲染图表。我选择最简单的统计图表类型。也可以根据自己的具体需求在echart官网选择合适的模型进行渲染。官网地址:
推荐文章:织梦主动推送插件-解决网站没有收录,网站关键词没有排名
SEO从业者其实每天都挺忙的,不断更新网站内容,寻求更好的外链,思考如何拦截流量,如何争取更好的排名。但是,很多站长都遇到过这个问题。他们努力了几个月,名次在几十位之外徘徊,不知道接下来要做什么。当局是粉丝,旁观者是清楚的。事实上,当我们跳出工作时,我们会发现很多问题。下面博主将与大家分享解决关键词无排名的五项法则。
1、搜索引擎主动推送功能的作用是什么?
考虑以下两点:
1.可以被百度搜索引擎爬虫及时发现
如果我们在发布文章后主动将链接提交给百度搜索引擎爬虫,岂不是可以缩短百度搜索引擎爬虫发现你网站新链接的时间?这样,新发布的页面可以最快的时间百度收录。
2.还可以保护原创
世界文章是大抄袭,你讨厌那些抄袭者吗?明明是我自己写的原创文章,却被别人网站抄了,没什么。但是那些抄袭的文章,他们的排名居然比你高,你不生气吗?
3.如何快速创建原创内容
使用的织梦采集伪原创百度推送插件只需简单几步即可轻松采集内容数据。对织梦采集 执行简单设置。完成后,织梦采集会根据用户设置的关键词将内容和图片进行高精度匹配,可选择保存在本地。您也可以选择伪原创后发布,提供方便快捷的内容采集伪原创发布服务!!
和其他织梦采集这个织梦采集相比,基本没有门槛,也不需要花很多时间去学习正则表达式或者html标签,只需一分钟即可上手,只需输入关键词即可实现采集(织梦采集也具备关键词采集的功能)。一路挂断!设置任务自动执行采集伪原创发布和推送任务。
几十万个不同的cms网站可以统一管理。一个人维护数百个 网站文章 更新也不是问题。这类织梦采集发布插件工具也配备了很多SEO功能,通过采集伪原创软件发布时也可以提升很多SEO方面。
例如:网站主动推送(让搜索引擎更快发现我们的网站) 自动匹配图片(文章如果内容中没有图片,会自动配置相关图片) 设置自动下载图片和将它们保存在本地或第三方(让内容不在对方的外部链接中)。自动内链(让搜索引擎更深入地抓取你的链接)、前后插入内容或标题,以及网站内容插入或随机作者、随机阅读等,形成“高原创 ”。定期发布(定期发布文章让搜索引擎准时抓取你的网站内容)
这些SEO小功能不仅提高了网站页面原创的度数,还间接提升了网站的收录排名。通过软件工具直接监控管理文章采集发布和百度推送状态,不再需要每天登录网站后台查看。目前博主亲测软件是免费的,可以直接下载使用!
二、外链量
(1) 质量是否太低?
很多朋友每天都需要花费大量的时间在外链的建设上,不管他们的网站和论坛怎么先发链接,久而久之,你的外链其实很脆弱,很多人容易接受外部链接。链上的网站或者论坛,根本就没有被搜索引擎收录大写,甚至很多网站和论坛,不管你发多少外链,都没有更新,无人管理就可以了,这将无济于事。,所以外部链接的质量非常重要。一个好的外链不仅能给你的网站带来好的排名,还能带来流量,所以朋友们在外链的建设中要多注意质量是否足够高。, 没有价值。
(2) 数量多还是少?
一些SEO从业者在对新站点进行排名时遵循“内容为王,外链为王”,他们都在X宝或某些链接中将网站换成自己的网站。购买数十万个反向链接是没有意义的,甚至是危险的。搜索引擎看到你是一个新站点,一夜之间弹出了数百个反向链接,他们会判断你是在作弊。, 有被 K 的风险。
当然,外部链接太少,跟不上节奏,效果不佳。有的朋友的外链建设是三天钓两天晒网。没有规则,也没有圈子。如果你有五个高质量的反向链接,从长远来看,你的网站反向链接的数量和质量都会相当可观,当然排名也会上升。
3、内链合理吗?
内链是优化网站体验的好方法,目的是满足用户的二次需求,就像加班的电梯扶手旁边为什么放一堆零食,目的就是刺激消费。
内部链接的建设一定要有理有据,不能太多也不能太少。从用户体验的角度来看,关键词可以做到哪些,哪些位置最适合做内链,是不是比简单的图文更胜一筹?文字更有吸引力。所有的SEO从业者都需要从用户的角度认真地建立自己的网站内链。分析内链是否合格,可以从跳出率入手。如果你的内链跳出率很高,那么你应该考虑一下内链是否有问题。
4. 关键词是否堆叠
很多站长把网站关键词散布在网站里面,是为了让自己的网站快速排名,这对于关键词排名不足也很重要杀手,关键词的堆叠对网站内容的语法有一定的影响。其次,搜索引擎也会认为你在作弊。有时候他们不仅不会给你排名,还会用K站来警告你!
5. 内容质量
“内容为王”这句话是对的,即使你的网站SEO优化不是那么好,但如果你能写出精彩的内容,你的网站流量自然而然,排名更上一层楼必要的。所以不要让自己网站被垃圾文章和一大堆伪原创填满,自己写一些原创的内容,百度蜘蛛喜欢捡新鲜的,只要你能喂它,你的网站必须有排名和流量。
6. 网站 结构
如果以上四项都做好了,那么就需要分析一下你的网站结构是否对SEO优化不友好,比如标题、关键词、描述是否需要改。是不是网站的栏目设置不合理,是不是网站导航的排列有问题,是不是网站的代码太繁琐等等。网站内部结构问题。
网站排名由许多因素组成。我们的 SEO 从业者需要从每个细节分析他们的网站 问题。. 看完这篇文章,如果觉得不错,不妨采集一下,或者发给需要的朋友同事。关注博主,每天为你展示各种SEO经验,打通你的二线任命和主管!