kafka告警簡單方案

一、前言

  為什么要設計kafka告警方案?現成的監控項目百度一下一大堆,KafkaOffsetMonitor、KafkaManager、 Burrow等,具體參考:kafka的消息擠壓監控。由于本小組的項目使用的kafka集群并沒有被公司的kafka-manager管理,所以只能自己簡單做一個告警。

二、告警方案

  

  首先需要兩個定時任務,之間的通信依靠延遲隊列。

  左邊的定時任務按周期掃面配置Topic-Consumer列表,通過kafka api獲取消費詳情并判斷消息積壓量是否已經大于閾值,如果閾值校驗失敗則放入延遲隊里。

  右邊的定時任務按照周期從延遲隊列對應的真實隊列中取出一個Topic-Consumer關系,再次進行一下閾值的校驗,如果檢驗失敗才發送告警短信。

三、準備工作

  1、依賴配置中心

  配置中心是實現告警方案的一個關鍵點,通過配置中心可以動態獲取告警相關的屬性配置,并刷新對應的 java bean。如下是告警對應的配置bean。

@ConfigCenterBean
@ConfigurationProperties(prefix = "wmhcontrol.alarm")
@Component
public class AlarmConstants extends BaseConfigCenterBean {private static Logger LOGGER = LoggerFactory.getLogger(AlarmConstants.class);//告警電話號碼
    @ConfigFieldprivate String[] phones;//短信模板ID
    @ConfigFieldprivate String templateId;//延遲時間
    @ConfigFieldprivate Integer delay = 600;//輪訓時間
    @ConfigFieldprivate Integer period = 60;//獲取topic-consumer消費詳情地址
    @ConfigFieldprivate String tcsr;//查看topic-consumer消費詳情地址
    @ConfigFieldprivate String tclr;//全局統一閾值
    @ConfigFieldprivate Integer threshold = 1000;//topic和consumer關系列表
    @ConfigFieldprivate List<TCR> tcrs;@ToInitialprivate void refreshProperties() {try {super.doBind();LOGGER.info(String.format("%s 刷新成功..., 當前配置=%s...", this.getModuleName(), this));} catch (Exception e) {LOGGER.error("AlarmConstants 對象屬性綁定失敗...", e);}}private void toRefresh() {try {if (isCfgCenterEffect()) {ZookeeperPropertySource propertySource = ConfigHelper.getZookeeperPropertySource();if (ConfigCenterUtils.propertySourceShouldRefresh(this.getModuleName(), propertySource)) {this.refreshProperties();}}} catch (Exception e) {LOGGER.error("AlarmConstants 對象屬性刷新失敗", e);}}@ToRefreshpublic Integer getThreshold() {return threshold;}public void setThreshold(Integer threshold) {this.threshold = threshold;}@ToRefreshpublic List<TCR> getTcrs() {return tcrs;}public void setTcrs(List<TCR> tcrs) {this.tcrs = tcrs;}@ToRefreshpublic String getTcsr() {return tcsr;}public void setTcsr(String tcsr) {this.tcsr = tcsr;}@ToRefreshpublic Integer getPeriod() {return period;}public void setPeriod(Integer period) {this.period = period;}@ToRefreshpublic Integer getDelay() {return delay;}public void setDelay(Integer delay) {this.delay = delay;}@ToRefreshpublic String[] getPhones() {return phones;}public void setPhones(String[] phones) {this.phones = phones;}@ToRefreshpublic String getTemplateId() {return templateId;}public void setTemplateId(String templateId) {this.templateId = templateId;}@ToRefreshpublic String getTclr() {return tclr;}public void setTclr(String tclr) {this.tclr = tclr;}@Overridepublic String toString() {return ReflectionToStringBuilder.toString(this, ToStringStyle.JSON_STYLE, false, false, AlarmConstants.class);}@Overridepublic String getDefaultResourcePath() {return "config/alarm.properties";}@Overridepublic String getConfigPrefix() {return "wmhcontrol.alarm";}@Overridepublic String getModuleName() {return "告警配置";}@Overridepublic void refreshForEvent() {this.refreshProperties();}/*** topic 和 consumer之間的關系實體*/public static class TCR {private String topic;private String consumer;private String channel;private Integer threshold;public String getTopic() {return topic;}public void setTopic(String topic) {this.topic = topic;}public String getConsumer() {return consumer;}public void setConsumer(String consumer) {this.consumer = consumer;}public String getChannel() {return channel;}public void setChannel(String channel) {this.channel = channel;}public Integer getThreshold() {return threshold;}public void setThreshold(Integer threshold) {this.threshold = threshold;}@Overridepublic String toString() {return "TCR{" +"topic='" + topic + '\'' +", consumer='" + consumer + '\'' +", channel='" + channel + '\'' +", threshold=" + threshold +'}';}}public static class TopicConsumerDetail {private String group;private String topic;private Integer pid;private Long offset;private Long logsize;@Overridepublic String toString() {return "TopicConsumerDetail{" +"group='" + group + '\'' +", topic='" + topic + '\'' +", pid=" + pid +", offset=" + offset +", logsize=" + logsize +", lag=" + lag +", owner='" + owner + '\'' +'}';}private Long lag;private String owner;public String getGroup() {return group;}public void setGroup(String group) {this.group = group;}public String getTopic() {return topic;}public void setTopic(String topic) {this.topic = topic;}public Integer getPid() {return pid;}public void setPid(Integer pid) {this.pid = pid;}public Long getOffset() {return offset;}public void setOffset(Long offset) {this.offset = offset;}public Long getLogsize() {return logsize;}public void setLogsize(Long logsize) {this.logsize = logsize;}public Long getLag() {return lag;}public void setLag(Long lag) {this.lag = lag;}public String getOwner() {return owner;}public void setOwner(String owner) {this.owner = owner;}}
}

  告警有個全局統一的閾值,每一個topic可以指定不同的閾值。

  配置中心 和 java bean建立關聯請參考:依賴配置中心實現注有@ConfigurationProperties的bean相關屬性刷新。

  2、定時任務的周期性可動態配置

  借助?org.springframework.scheduling.annotation.SchedulingConfigurer。

  由@EnableScheduling注釋的@Configuration類實現的可選接口。通常用于設置在執行計劃任務時使用的特定TaskScheduler bean,或者用于以編程方式注冊計劃任務,而不是使用@Scheduled注釋的聲明性方法。例如,在實現基于觸發器的任務時可能需要這樣做,而@Scheduled注釋不支持這些任務。

  基本的代碼輪廓如下。

@Configuration
public class MessageCenterAlarmTask implements SchedulingConfigurer {@Overridepublic void configureTasks(ScheduledTaskRegistrar taskRegistrar) {try {//每5s檢測一下隊列taskRegistrar.addFixedRateTask(() -> {}, 5 * 1000L);//動態修改定時任務周期taskRegistrar.addTriggerTask(() -> {}, triggerContext -> new PeriodicTrigger(alarmConstants.getPeriod(), TimeUnit.SECONDS).nextExecutionTime(triggerContext));} catch (Exception e) {LOGGER.error("消息中心topic-consumer定時任務初始化失敗...", e);}}
}

  上面的代碼中的定時任務分別表示了告警方案中右邊和左邊的定時任務。

  3、延遲隊列的實現

  借助redisson分布式延遲隊列 或者 java delayqueue + redistemplate 實現分布式延遲隊列。

  參考:Redisson分布式延遲隊列官方文檔

  參考:Redisson DelayedQueue實現原理

  Redisson的集群模式配置如下。

public class RedissonBuilder {private static Logger LOGGER = LoggerFactory.getLogger(RedissonBuilder.class);public static RedissonClient getRedisson(String cluster) {String[] nodes = cluster.split(",");for (int i = 0; i < nodes.length; i++) {nodes[i] = "redis://" + nodes[i];}Config config = new Config();config.useClusterServers() //這是用的集群server.setScanInterval(2000) //設置集群狀態掃描時間.setConnectTimeout(2000).addNodeAddress(nodes);try {RedissonClient rc = Redisson.create(config);return rc;} catch (Exception e) {LOGGER.error("RedissonClient getRedisson errors...", e);return null;}}
}@Configuration
public class RedissonConfig {private static Logger LOGGER = LoggerFactory.getLogger(RedissonConfig.class);@Beanpublic RedissonClient redissonClient(@Value("${redisAddress}") String cacheAddress) {RedissonClient rc = RedissonBuilder.getRedisson(cacheAddress);try {if (!Objects.isNull(rc)) {LOGGER.info(rc.getConfig().toJSON());}} catch (IOException e) {LOGGER.error("RedissonConfig redissonClient errors... ", e);}return rc;}}

  Redisson創建延遲隊列方式

RQueue<AlarmConstants.TCR> topicConsumerQueue = redissonClient.getQueue(commonRedisKey + "message_center_tcrs");
RDelayedQueue<AlarmConstants.TCR> topicConsumerDelayedQueue = redissonClient.getDelayedQueue(topicConsumerQueue);

  首先創建目標隊列,然后通過目標隊列拿到延遲隊列。

  4、kafka api返回數據處理

  參考:簡單封裝kafka相關的api

  更具topic和consumer可以拿到如下數據。其中Lag對應的這一列表示未消費的消息數量。

  

  需要做的是把如上數據映射到?AlarmConstants.TopicConsumerDetail 這個java bean中,借助Spring BeanWrapperImpl,如下。

private static List<AlarmConstants.TopicConsumerDetail> retrieveDetail(String detailResponse) {List<AlarmConstants.TopicConsumerDetail> result = new ArrayList<>();try {Scanner scanner = new Scanner(detailResponse.replace("<pre>", StringUtils.EMPTY).replace("</pre>", StringUtils.EMPTY));String[] propertyNames = null;
//第一行對應java bean的field name
if (scanner.hasNextLine()) {String nameLine = scanner.nextLine();if (StringUtils.isBlank(nameLine)) {return result;}propertyNames = Arrays.stream(nameLine.split("\\s+")).map(propertyName -> propertyName.toLowerCase()).toArray(String[]::new);}
//剩余行對應java bean的field value
while (scanner.hasNextLine()) {AlarmConstants.TopicConsumerDetail tcd = new AlarmConstants.TopicConsumerDetail();BeanWrapper br = new BeanWrapperImpl(tcd);String valueLine = scanner.nextLine();if (StringUtils.isBlank(valueLine)) {continue;}String[] propertyValues = valueLine.split("\\s+");for (int i = 0; i < propertyValues.length; i++) { br.setPropertyValue(propertyNames[i], propertyValues[i]);}result.add(tcd);}LOGGER.info("消息中心提取topic-consumer詳情信息:" + result);} catch (Exception e) {LOGGER.error("消息中心提取topic-consumer信息異常..." + detailResponse, e);}return result; }

  處理之后的效果如下。

[TopicConsumerDetail{group='group-message-center-consumer-TELEPHONE_HOTLINE', topic='message-center-telephone-hotline-topic', pid=0, offset=10956087, logsize=10956091, lag=4, owner='group-message-center-consumer-TELEPHONE_HOTLINE_N03-NFJD-BB-SV0495-DMZ-A620R-1543818094331-835bc5f7-0'}, TopicConsumerDetail{group='group-message-center-consumer-TELEPHONE_HOTLINE', topic='message-center-telephone-hotline-topic', pid=1, offset=10950487, logsize=10950502, lag=15, owner='group-message-center-consumer-TELEPHONE_HOTLINE_N03-NFJD-BB-SV0495-DMZ-A620R-1543818094331-835bc5f7-0'}, TopicConsumerDetail{group='group-message-center-consumer-TELEPHONE_HOTLINE', topic='message-center-telephone-hotline-topic', pid=2, offset=10958523, logsize=10958529, lag=6, owner='group-message-center-consumer-TELEPHONE_HOTLINE_N03-NFJD-BB-SV0495-DMZ-A620R-1543818094331-835bc5f7-0'}, TopicConsumerDetail{group='group-message-center-consumer-TELEPHONE_HOTLINE', topic='message-center-telephone-hotline-topic', pid=3, offset=10955709, logsize=10955717, lag=8, owner='group-message-center-consumer-TELEPHONE_HOTLINE_N03-NFJD-BB-SV0495-DMZ-A620R-1543818094331-835bc5f7-0'}, TopicConsumerDetail{group='group-message-center-consumer-TELEPHONE_HOTLINE', topic='message-center-telephone-hotline-topic', pid=4, offset=10956550, logsize=10956563, lag=13, owner='group-message-center-consumer-TELEPHONE_HOTLINE_N03-NFJD-BB-SV0495-DMZ-A620R-1543818094331-835bc5f7-0'}, TopicConsumerDetail{group='group-message-center-consumer-TELEPHONE_HOTLINE', topic='message-center-telephone-hotline-topic', pid=5, offset=10956343, logsize=10956347, lag=4, owner='group-message-center-consumer-TELEPHONE_HOTLINE_N03-NFJD-BB-SV0495-DMZ-A620R-1543818202772-32905832-0'}, TopicConsumerDetail{group='group-message-center-consumer-TELEPHONE_HOTLINE', topic='message-center-telephone-hotline-topic', pid=6, offset=10954124, logsize=10954128, lag=4, owner='group-message-center-consumer-TELEPHONE_HOTLINE_N03-NFJD-BB-SV0495-DMZ-A620R-1543818202772-32905832-0'}, TopicConsumerDetail{group='group-message-center-consumer-TELEPHONE_HOTLINE', topic='message-center-telephone-hotline-topic', pid=7, offset=10949075, logsize=10949082, lag=7, owner='group-message-center-consumer-TELEPHONE_HOTLINE_N03-NFJD-BB-SV0495-DMZ-A620R-1543818202772-32905832-0'}, TopicConsumerDetail{group='group-message-center-consumer-TELEPHONE_HOTLINE', topic='message-center-telephone-hotline-topic', pid=8, offset=10963839, logsize=10963843, lag=4, owner='group-message-center-consumer-TELEPHONE_HOTLINE_N03-NFJD-BB-SV0495-DMZ-A620R-1543818202772-32905832-0'}, TopicConsumerDetail{group='group-message-center-consumer-TELEPHONE_HOTLINE', topic='message-center-telephone-hotline-topic', pid=9, offset=10958536, logsize=10958540, lag=4, owner='group-message-center-consumer-TELEPHONE_HOTLINE_N03-NFJD-BB-SV0495-DMZ-A620R-1543818202772-32905832-0'}, TopicConsumerDetail{group='group-message-center-consumer-TELEPHONE_HOTLINE', topic='message-center-telephone-hotline-topic', pid=10, offset=10955316, logsize=10955327, lag=11, owner='group-message-center-consumer-TELEPHONE_HOTLINE_N03-NFJD-BB-SV0496-DMZ-A620R-1543818798625-b7a38283-0'}, TopicConsumerDetail{group='group-message-center-consumer-TELEPHONE_HOTLINE', topic='message-center-telephone-hotline-topic', pid=11, offset=10957850, logsize=10957856, lag=6, owner='group-message-center-consumer-TELEPHONE_HOTLINE_N03-NFJD-BB-SV0496-DMZ-A620R-1543818798625-b7a38283-0'}, TopicConsumerDetail{group='group-message-center-consumer-TELEPHONE_HOTLINE', topic='message-center-telephone-hotline-topic', pid=12, offset=10954508, logsize=10954515, lag=7, owner='group-message-center-consumer-TELEPHONE_HOTLINE_N03-NFJD-BB-SV0496-DMZ-A620R-1543818798625-b7a38283-0'}, TopicConsumerDetail{group='group-message-center-consumer-TELEPHONE_HOTLINE', topic='message-center-telephone-hotline-topic', pid=13, offset=10960468, logsize=10960477, lag=9, owner='group-message-center-consumer-TELEPHONE_HOTLINE_N03-NFJD-BB-SV0496-DMZ-A620R-1543818798625-b7a38283-0'}, TopicConsumerDetail{group='group-message-center-consumer-TELEPHONE_HOTLINE', topic='message-center-telephone-hotline-topic', pid=14, offset=10955540, logsize=10955544, lag=4, owner='group-message-center-consumer-TELEPHONE_HOTLINE_N03-NFJD-BB-SV0496-DMZ-A620R-1543818798625-b7a38283-0'}]

四、告警邏輯

  1、短息發送

private String toShortMessage(AlarmConstants.TCR tcr, Long lag) {JSONObject info = new JSONObject();StringBuilder text = new StringBuilder();String messageDate = LocalDateTime.now().format(DateTimeFormatter.ISO_DATE_TIME);text.append("【Topic-Consumer閾值告警 " + messageDate + "】\r\n");text.append("\t渠道: " + tcr.getChannel() + "\r\n");text.append("\t主題: " + tcr.getTopic() + "\r\n");text.append("\t消費: " + tcr.getConsumer() + "\r\n");text.append("\t閾值: " + (Objects.isNull(tcr.getThreshold()) ? alarmConstants.getThreshold() : tcr.getThreshold()) + "\r\n");text.append("\t堆積: " + lag + "\r\n");try {String refUrl = alarmConstants.getTclr() + "?topic=" + tcr.getTopic() + "&group=" + tcr.getConsumer();JSONObject params = new JSONObject();params.put("url", refUrl);String shortUrlResponse = HttpClient.post("https://dwz.cn/admin/create", params.toJSONString(), "application/json");LOGGER.info("獲取短鏈接返回內容:" + shortUrlResponse);if (StringUtils.isNotBlank(shortUrlResponse)) {JSONObject shortUrlJson = JSON.parseObject(shortUrlResponse);Integer code = (Integer) FastJsonUtils.search(shortUrlJson, "Code");if (Integer.valueOf(0).equals(code)) {String shortUrl = (String) FastJsonUtils.search(shortUrlJson, "ShortUrl");if (StringUtils.isNotBlank(shortUrl)) {text.append("\t查看: " + shortUrl + "\r\n");}}}} catch (Exception e) {LOGGER.error("短鏈接生成異常...", e);}info.put("txt_name", "消息中心");info.put("txt_result", text.toString());return info.toJSONString();
}

  2、閾值校驗

private Long thresholdCheck(AlarmConstants.TCR tcr) {String detailUrl = alarmConstants.getTcsr() + "?topic=" + tcr.getTopic() + "&group=" + tcr.getConsumer();String detailResponseStr = HttpClient.get(detailUrl);LOGGER.info(detailUrl + " " + detailResponseStr);List<AlarmConstants.TopicConsumerDetail> detailResponse = retrieveDetail(detailResponseStr);if (CollectionUtils.isEmpty(detailResponse)) {return -1L;}Long lag = detailResponse.stream().mapToLong(AlarmConstants.TopicConsumerDetail::getLag).sum();Long threshold = Long.valueOf(Objects.isNull(tcr.getThreshold()) ? alarmConstants.getThreshold() : tcr.getThreshold());if (lag.compareTo(threshold) > 0) {return lag;}return -1L;
}

  3、兩個定時任務邏輯補充

@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {try {RQueue<AlarmConstants.TCR> topicConsumerQueue = redissonClient.getQueue(commonRedisKey + "message_center_tcrs");RDelayedQueue<AlarmConstants.TCR> topicConsumerDelayedQueue = redissonClient.getDelayedQueue(topicConsumerQueue);//每5s檢測一下隊列taskRegistrar.addFixedRateTask(() -> {AlarmConstants.TCR tcr = topicConsumerQueue.poll();if (!Objects.isNull(tcr)) {//發送告警信息Long lag = thresholdCheck(tcr);if (lag > 0) {if (ArrayUtils.isNotEmpty(alarmConstants.getPhones())) {String message = toShortMessage(tcr, lag);String tmplateId = alarmConstants.getTemplateId();LOGGER.info("消息中心告警短信內容:" + message);for (String phone : alarmConstants.getPhones()) {try {MessageUtils.sendMessage(phone, messageUrl, message, tmplateId);} catch (Exception e) {LOGGER.error(String.format("消息中心告警短信發送異常...%s %s %s", phone, messageUrl, message), e);}}}}}}, 5 * 1000L);taskRegistrar.addTriggerTask(() -> {RLock lock = null;try {lock = redissonClient.getLock(commonRedisKey + "TopicConsumerForEach");// 嘗試加鎖,最多等待5秒,上鎖以后5秒自動解鎖if (!lock.tryLock(5, 5, TimeUnit.SECONDS)) {return;}if (!CollectionUtils.isEmpty(alarmConstants.getTcrs())) {alarmConstants.getTcrs().stream().filter(tcr -> !topicConsumerDelayedQueue.contains(tcr) && (thresholdCheck(tcr) > 0)).forEach(tcr -> topicConsumerDelayedQueue.offer(tcr, alarmConstants.getDelay(), TimeUnit.SECONDS));}} catch (Exception e) {LOGGER.error("消息中心topic-consumer定時任務執行失敗...", e);} finally {if (!Objects.isNull(lock)) {lock.unlock();}}
     //動態周期性檢測Topic-Consumer閾值}, triggerContext -> new PeriodicTrigger(alarmConstants.getPeriod(), TimeUnit.SECONDS).nextExecutionTime(triggerContext));} catch (Exception e) {LOGGER.error("消息中心topic-consumer定時任務初始化失敗...", e);}
}

五、告警定時任務源碼

  請關注訂閱號(算法和技術SHARING),回復:kafka告警, 便可查看。

轉載于:https://www.cnblogs.com/hujunzheng/p/10063449.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/531210.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/531210.shtml
英文地址,請注明出處:http://en.pswp.cn/news/531210.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

RedisCacheManager設置Value序列化器技巧

CacheManager基本配置 請參考博文&#xff1a;springboot2.0 redis EnableCaching的配置和使用 RedisCacheManager構造函數 /*** Construct a {link RedisCacheManager}.* * param redisOperations*/ SuppressWarnings("rawtypes") public RedisCacheManager(RedisOp…

Nginx配置以及域名轉發

工程中的nginx配置 #user nobody; worker_processes 24; error_log /home/xxx/opt/nginx/logs/error.log; pid /home/xxx/opt/nginx/run/nginx.pid;events {use epoll;worker_connections 102400; }http {include /home/xxx/opt/nginx/conf.d/mime.types;default_…

java接口簽名(Signature)實現方案續

一、前言 由于之前寫過的一片文章 &#xff08;java接口簽名(Signature)實現方案 &#xff09;收獲了很多好評&#xff0c;此次來說一下另一種簡單粗暴的簽名方案。相對于之前的簽名方案&#xff0c;對body、paramenter、path variable的獲取都做了簡化的處理。也就是說這種方式…

支付寶敏感信息解密

支付寶官方解密文檔&#xff1a;https://docs.alipay.com/mini/introduce/aes String response "小程序前端提交的";//1. 獲取驗簽和解密所需要的參數 Map<String, String> openapiResult JSON.parseObject(response,new TypeReference<Map<String, St…

HashMap 源碼閱讀

前言 之前讀過一些類的源碼&#xff0c;近來發現都忘了&#xff0c;再讀一遍整理記錄一下。這次讀的是 JDK 11 的代碼&#xff0c;貼上來的源碼會去掉大部分的注釋, 也會加上一些自己的理解。 Map 接口 這里提一下 Map 接口與1.8相比 Map接口又新增了幾個方法&#xff1a;   …

SpringMvc接口中轉設計(策略+模板方法)

一、前言 最近帶著兩個兄弟做支付寶小程序后端相關的開發&#xff0c;小程序首頁涉及到很多查詢的服務。小程序后端服務在我司屬于互聯網域&#xff0c;相關的查詢服務已經在核心域存在了&#xff0c;查詢這塊所要做的工作就是做接口中轉。參考了微信小程序的代碼&#xff0c;發…

SpringSecurity整合JWT

一、前言 最近負責支付寶小程序后端項目設計&#xff0c;這里主要分享一下用戶會話、接口鑒權的設計。參考過微信小程序后端的設計&#xff0c;會話需要依靠redis。相關的開發人員和我說依靠Redis并不是很靠譜&#xff0c;redis在業務高峰期不穩定&#xff0c;容易出現問題&…

Springboot定時任務原理及如何動態創建定時任務

一、前言 上周工作遇到了一個需求&#xff0c;同步多個省份銷號數據&#xff0c;解綁微信粉絲。分省定時將銷號數據放到SFTP服務器上&#xff0c;我需要開發定時任務去解析文件。因為是多省份&#xff0c;服務器、文件名規則、數據規則都不一定&#xff0c;所以要做成可配置是有…

轉載:ThreadPoolExecutor 源碼閱讀

前言 之前研究了一下如何使用ScheduledThreadPoolExecutor動態創建定時任務(Springboot定時任務原理及如何動態創建定時任務)&#xff0c;簡單了解了ScheduledThreadPoolExecutor相關源碼。今天看了同學寫的ThreadPoolExecutor 的源碼解讀&#xff0c;甚是NB&#xff0c;必須轉…

Spring BPP中優雅的創建動態代理Bean

一、前言 本文章所講并沒有基于Aspectj&#xff0c;而是直接通過Cglib以及ProxyFactoryBean去創建代理Bean。通過下面的例子&#xff0c;可以看出Cglib方式創建的代理Bean和ProxyFactoryBean創建的代理Bean的區別。 二、基本測試代碼 測試實體類&#xff0c;在BPP中創建BppTest…

使用pdfBox實現pdf轉圖片,解決中文方塊亂碼等問題

一、引入依賴 <dependency><groupId>org.apache.pdfbox</groupId><artifactId>fontbox</artifactId><version>2.0.13</version> </dependency> <dependency><groupId>org.apache.pdfbox</groupId><artif…

Spring異步調用原理及SpringAop攔截器鏈原理

一、Spring異步調用底層原理 開啟異步調用只需一個注解EnableAsync Target(ElementType.TYPE) Retention(RetentionPolicy.RUNTIME) Documented Import(AsyncConfigurationSelector.class) public interface EnableAsync {/*** Indicate the async annotation type to be detec…

線程池優化之充分利用線程池資源

一、前言 最近做了電子發票的需求&#xff0c;分省開票接口和發票下載接口都有一定的延遲。為了完成開票后自動將發票插入用戶微信卡包&#xff0c;目前的解決方案是利用線程池&#xff0c;將開票后插入卡包的任務&#xff08;輪詢分省發票接口&#xff0c;直到獲取到發票相關信…

Spring MVC源碼——Root WebApplicationContext

Spring MVC源碼——Root WebApplicationContext 打算開始讀一些框架的源碼,先拿 Spring MVC 練練手,歡迎點擊這里訪問我的源碼注釋, SpringMVC官方文檔一開始就給出了這樣的兩段示例: WebApplicationInitializer示例: public class MyWebApplicationInitializer implements Web…

Spring MVC源碼——Servlet WebApplicationContext

上一篇筆記(Spring MVC源碼——Root WebApplicationContext)中記錄了下 Root WebApplicationContext 的初始化代碼.這一篇來看 Servlet WebApplicationContext 的初始化代碼 DispatcherServlet 是另一個需要在 web.xml 中配置的類, Servlet WebApplicationContext 就由它來創建…

Springboot源碼——應用程序上下文分析

前兩篇(Spring MVC源碼——Root WebApplicationContext 和 Spring MVC源碼——Servlet WebApplicationContext)講述了springmvc項目創建上下文的過程&#xff0c;這一篇帶大家了解一下springboot項目創建上下文的過程。 SpringApplication引導類 SpringApplication類用于啟動或…

基于zookeeper實現分布式配置中心(一)

最近在學習zookeeper&#xff0c;發現zk真的是一個優秀的中間件。在分布式環境下&#xff0c;可以高效解決數據管理問題。在學習的過程中&#xff0c;要深入zk的工作原理&#xff0c;并根據其特性做一些簡單的分布式環境下數據管理工具。本文首先對zk的工作原理和相關概念做一下…

基于zookeeper實現分布式配置中心(二)

上一篇&#xff08;基于zookeeper實現分布式配置中心&#xff08;一&#xff09;&#xff09;講述了zookeeper相關概念和工作原理。接下來根據zookeeper的特性&#xff0c;簡單實現一個分布式配置中心。 配置中心的優勢 1、各環境配置集中管理。 2、配置更改&#xff0c;實時推…

Redis分布式鎖實戰

背景 目前開發過程中&#xff0c;按照公司規范&#xff0c;需要依賴框架中的緩存組件。不得不說&#xff0c;做組件的大牛對CRUD操作的封裝&#xff0c;連接池、緩存路由、緩存安全性的管控都處理的無可挑剔。但是有一個小問題&#xff0c;該組件沒有對分布式鎖做實現&#xff…

基于RobotFramework實現自動化測試

Java robotframework seleniumlibrary 使用Robot Framework Maven Plugin&#xff08;http://robotframework.org/MavenPlugin/&#xff09;執行自動化測試chromedriver下載&#xff1a; http://chromedriver.storage.googleapis.com/index.htmlchromedriver和chrome版本對應…