架构

基于 Docker 搭建开发环境(三):链路追踪

基于 Docker 搭建开发环境(三):链路追踪

D瓜哥
基于 Docker 搭建开发环境系列: 基于 Docker 搭建开发环境(一):数据库+监控 基于 Docker 搭建开发环境(二):EFK 日志套件 基于 Docker 搭建开发环境(三):链路追踪 在上一篇文章 基于 Docker 搭建开发环境(一):数据库+监控 和 基于 Docker 搭建开发环境(二):EFK 日志套件 两篇文章中,分别介绍了“数据库+监控”和“EFK 日志套件”。这篇文章给大家分享一下如何在本地搭建起一套简单的分布式链路追踪。 在 AI 的帮助下,如同砍瓜切菜一样,非常迅速地就完成了 基于 Docker 搭建开发环境(二):EFK 日志套件 的搭建。原以为搞这个也会分分钟的问题,结果应用的追踪数据一致无法正常发送到 Jaeger 中,各种改端口号都不行。后来,无意间看了 OpenTelemetry 的配置文档,增加了一个协议配置,全部流程竟然通了,非常神奇! 站在更高的视角去看,链路追踪其实是可观测性的一部分,包括上篇文章的日志,也是可观测性的一部分。日志、追踪、度量,三者是相辅相成的。 图 1. 可观测性 在 OpenTelemetry 出现之前,日志、追踪、度量是分离的,三者各各自为战。而 OpenTelemetry 的出现,则是试图将三者统一。目前 OpenTelemetry 是云原生架构中,最炙手可热的分布式链路追踪解决方案,它提供了一套相关标准,各个厂商可以在这套标准之上进行各种各样的组件开发,大家可以根据自己的需要,选择不同的组件,进行可插拔式的安装。 图 2. OpenTelemetry 的野心 在这篇文章中,链路追踪的解决方案选择的是 OpenTelemetry + OpenTelemetry Collector + Jaeger。 OpenTelemetry OpenTelemetry 并不需要在 Docker 中启动或者配置什么。在目前的架构中,Jaeger 是作为 OpenTelemetry 的一个实现来出现的。 OpenTelemetry 需要做的就是下载一个 Java Agent,执行 docker/config/opentelemetry/download-opentelemetry-agent.sh 脚本即可下载最新版的 Java Agent。在业务应用启动时,增加如下 JVM 参数:
基于 Docker 搭建开发环境(二):EFK 日志套件

基于 Docker 搭建开发环境(二):EFK 日志套件

D瓜哥
基于 Docker 搭建开发环境系列: 基于 Docker 搭建开发环境(一):数据库+监控 基于 Docker 搭建开发环境(二):EFK 日志套件 基于 Docker 搭建开发环境(三):链路追踪 在上一篇文章 基于 Docker 搭建开发环境(一):数据库+监控 中,介绍了一下如何使用 Docker 搭建起 MySQL + NACOS + Prometheus + Grafana 集成数据库、注册中心+配置管理、监控的开发环境。这篇文章来介绍一下如何在原来的基础上接入 Elasticsearch + Fluentd + Kibana 套件,并且将 NACOS 的日志接入到 Elasticsearch 里。 Elasticsearch 由于 Elasticsearch 8+ 的版本修改了安全策略,不允许 Kibana 使用超级管理员 elastic 连接 Elasticsearch,这里选用 7.x 版本做演示。 还有一点需要提醒,在设置 Elasticsearch 的超级管理员 elastic 的账户密码时,如果密码是全部的阿拉伯数字,那么需要用双引号或者单引号括起来。 在测试中,还遇到一个磁盘过载导致的只读问题。解决方式如下: curl -X GET "localhost:9200/_cat/allocation?v&pretty" 查看磁盘使用情况 解除只读状态 $ curl -X PUT "localhost:9200/test/_settings" -H 'Content-Type: application/json' -d' { "index.blocks.read_only_allow_delete": null } '
基于 Docker 搭建开发环境(一):数据库+监控

基于 Docker 搭建开发环境(一):数据库+监控

D瓜哥
基于 Docker 搭建开发环境系列: 基于 Docker 搭建开发环境(一):数据库+监控 基于 Docker 搭建开发环境(二):EFK 日志套件 基于 Docker 搭建开发环境(三):链路追踪 去年,很多同事要换 Mac 本,所以,写了 新 Mac 安装软件脚本,方便大家一键换机。最近想玩一下 Spring Cloud 以及相关周边的部署、监控等开源解决方案。由于组件众多及为了便于迁移和共享,计划基于 Docker 及 Docker Compose 搭建一套相关的开发环境。记录一下,方便有相同需求的朋友借鉴。 最新版的 Docker 在下载镜像时,会先访问一下 Docker 的官方站点。由于国内众所周知的网络情况,访问 Docker 官方站点总失败。所以,即使配置了国内 Docker 镜像站点也会失败。只需要将 Docker 软件回滚到 4.30.0 即可。(Mac 下验证有效,其他操作系统待进一步验证。) MySQL 开发中,最常用的应该就是数据库了。所以,先来搞 MySQL 数据库。 创建如下目录结构,并添加相关相关文件: $ tree . ├── README.adoc ├── clean.sh ├── data │ └── mysql │ └── .gitkeep ├── docker │ ├── config │ │ └── mysql │ │ └── init.sql │ ├── env │ │ └── mysql.env │ └── images │ └── mysql.dockerfile └── docker-compose.yml
深入研究 BeanFactoryPostProcessor

深入研究 BeanFactoryPostProcessor

D瓜哥
D瓜哥在 Spring 扩展点概览及实践 中概要性地介绍了一下 Spring 的核心扩展点。里面也提到了 BeanFactoryPostProcessor 和 BeanDefinitionRegistryPostProcessor,但仅仅提了一句,没有深入研究。在 Spring 扩展点实践:整合 MyBATIS 中,由于 MapperScannerConfigurer 实现了 BeanDefinitionRegistryPostProcessor 接口,也只是简单介绍了一些作用,又一次没有深入研究。 最近,在开发一个插件时,遇到了一个问题:利用 BeanFactoryPostProcessor 对一些特定 BeanDefinition 设置属性,但生成的 Bean 却没有相关的属性值。由此,对 BeanFactoryPostProcessor 做了一些研究。记录一下,以备不时之需。 Spring 启动流程简介 在 Spring 启动流程概述 中,D瓜哥对 Spring 的启动流程做了比较详细的介绍。同时画了一张启动流程图,如下: 图 1. AbstractApplicationContext.refresh — 重塑容器 从该图中可以明显看到,如果需要对 Spring 的 BeanDefinition 做些修改,那么,就需要通过实现 BeanFactoryPostProcessor 接口,来对 Spring 做些扩展。坦白讲,为了上述流程图只展示了一个非常概要性的流程。如果深入一下 invokeBeanFactoryPostProcessors 方法的细节,会发现这又是一番天地。 BeanFactoryPostProcessor 调用详解 D瓜哥把 invokeBeanFactoryPostProcessors 方法的流程图也画了出来,细节如下: 图 2. BeanDefinitionRegistryPostProcessor & BeanFactoryPostProcessor 调用过程 从这张流程图上可以看出 BeanFactoryPostProcessor 的调用过程,比在 Spring 启动流程概述 中介绍的要复杂很多: 首先,执行 BeanDefinitionRegistryPostProcessor#postProcessBeanDefinitionRegistry 方法,顺序如下: 关于 BeanDefinitionRegistryPostProcessor 的处理流程,D瓜哥在 Spring 扩展点概览及实践:BeanDefinitionRegistryPostProcessor 中有更详细的描述,不了解的朋友请参考那篇文章的介绍。
Spring 应用合并之路

Spring 应用合并之路

D瓜哥
公司最近一年在推进降本增效,在用尽各种手段之后,发现应用太多,每个应用都做跨机房容灾部署,则最少需要 4 台机器(称为容器更合适)。那么,将相近应用做一个合并,减少维护项目,提高机器利用率就是一个可选方案。 经过前后三次不同的折腾,最后探索出来一个可行方案。记录一下,分享出来,希望对有相关需求的研发童鞋有所帮助。下面按照四种可能的方案,分别做介绍。另外,为了方便做演示,专门整了两个演示项目: diguage/merge-demo-boot — 合并项目,下面简称为 boot。 diguage/merge-demo-web — 被合并项目,下面简称为 web。 Jar 包引用 这个方式,可能是给人印象最容易的方式。仔细思考一下,从维护性的角度来看,这个方式反而是最麻烦的方式,理由如下: web 项目每次更新,都需要重新打包发布新版; boot 项目也需要跟着更新发布。拉一次屎,脱两次裤子。属实麻烦。 还需要考虑 web 项目的加载问题,类似下面要描述的,是否共用容器: 共用容器 — 这是最容器想到的方式。但是这种方式,需要解决 Bean 冲突的问题。 不共用容器 — 这种方式需要处理 web 容器如何加载的问题。默认应该是无法识别。 基于这些考虑,这种方式直接被抛弃了。 仓库合并,公用一套容器 这是第一次尝试使用的方案。也是遇到问题最多的方案。 将两个仓库做合并。 将 web 仓库的地址配置到 boot 项目里: git remote add web git@github.com:diguage/merge-demo-web.git; 在 boot 项目里,切出来一个分支: git switch -c web; 将 web 分支的提交清空: git update-ref -d HEAD,然后做一次提交; 将 web 项目的代码克隆到 web 分支上: git pull --rebase --allow-unrelated-histories web master;注意,这里需要加 --allow-unrelated-histories 参数,以允许不相干的仓库进行合并。 从 boot 项目的 master 分支上,切出来一个合并分支: git switch -c merge; 将 web 项目向 boot 项目合并: git merge --allow-unrelated-histories web;注意,这里需要加 --allow-unrelated-histories 参数,以允许不相干的仓库进行合并。 处理代码冲突,完成合并即可。
Spring 对占位符的处理(一):XML 中的 Bean

Spring 对占位符的处理(一):XML 中的 Bean

D瓜哥
最近有小伙伴在开发时,遇到了一个 Spring 占位符,例如 ${token}, 在不同环境下处理不一致的问题,正好对 Spring 对占位符的处理也有一些不清楚的地方,趁此机会,把 Spring 对占位符的处理机制深入了解一下,方便后续排查问题。 经常阅读D瓜哥博客的朋友可能知道,D瓜哥在 Spring 扩展点实践:整合 Apache Dubbo(一): Spring 插件机制简介 中已经介绍了 Spring 的插件机制。在阅读以下内容之前,建议大家先去阅读一下这篇文章中“Spring 插件机制简介”章节的内容,以便于无缝衔接。 在分析的过程中发现, Spring 对占位符有两种截然不同的出来阶段:① XML 配置文件中的占位符;② Java 源代码中 @Value 注解中的占位符。由于内容较多,一篇讲解完有些过长,所以分三篇文章来分别介绍这两种处理过程。 本篇首先来介绍一下对 XML 配置文件中的占位符的处理。 示例代码 在正式开始之前,先来看一下示例代码: UserRpc.java /** * @author D瓜哥 · https://www.diguage.com * @since 2023-05-02 10:23:49 */ public class UserRpc { @Value("${user.appId}") private String appId; // 这里不使用注解,而是使用 XML 配置 // @Value("${user.token}") private String token; } token.properties user.appId=dummyAppId user.token=dummyToken spring.xml <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"> <!-- @author D瓜哥 · https://www.diguage.com --> <context:annotation-config/> <bean id="userRpc" class="com.diguage.truman.context.UserRpc"> <!-- XML 配置的占位符实例在此 --> <property name="token" value="${user.token}"/> </bean> <context:property-placeholder location="classpath:token.properties"/> </beans> <bean> 标签处理 在 Spring 启动流程概述 中,已经介绍过,Spring 的启动过程几乎都被封装在 AbstractApplicationContext#refresh 方法中。在 refresh 方法中调用了 refreshBeanFactory 方法;在 refreshBeanFactory 方法执行过程中,调用了 loadBeanDefinitions 方法。而 BeanDefinition 的加载是由 org.springframework.context.support.AbstractRefreshableApplicationContext#loadBeanDefinitions 来完成的。通过 XML 文件配置的 Bean 是由 org.springframework.context.support.AbstractXmlApplicationContext#loadBeanDefinitions(org.springframework.beans.factory.support.DefaultListableBeanFactory) (AbstractRefreshableApplicationContext 的子类)处理完成的。处理过程的时序图如下:
Avro、ProtoBuf、Thrift 的模式演进之法【翻译】

Avro、ProtoBuf、Thrift 的模式演进之法【翻译】

D瓜哥
前面系统研究了 Hessian 序列化协议。并以此为契机,顺带实例对比了 Hessian、MessagePack 和 JSON 的序列化。早在 2012 年,Martin Kleppmann 就写了一篇文章 《Schema evolution in Avro, Protocol Buffers and Thrift》,也是基于实例,对比了 Avro、ProtoBuf、Thrift 的差别。现在翻译出来,方便做系列研究。 整个“序列化系列”目录如下: Hessian 2.0 序列化协议(中文版) — Hessian 序列化协议的中文翻译版。根据后面的“协议解释与实战”系列文章,增加了协议内容错误提示。 Hessian 协议解释与实战(一):布尔、日期、浮点数与整数 — 介绍布尔型数据、日期类型、浮点类型数据和整数类型数据等四种类型的数据的处理。 Hessian 协议解释与实战(二):长整型、二进制数据与 Null — 介绍长整数类型数据、二进制数据和 null 等三种类型的数据的处理。 Hessian 协议解释与实战(三):字符串 — 专门介绍了关于字符串的处理。由于字符串需要铺垫的基础知识比较多,处理细节也有繁琐,所以单独成篇来介绍。 Hessian 源码分析(Java) — 开始第四篇分析之前,先来介绍一下 Hessian 的源码实现。方便后续展开说明。 Hessian 协议解释与实战(四):数组与集合 — 铺垫了一些关于实例对象的处理,重点介绍关于数组和集合的相关处理。 Hessian 协议解释与实战(五):对象与映射 — 重点介绍关于对象与映射的相关处理。 Hessian、Msgpack 和 JSON 实例对比 — 用实例对比 JSON、Hessian 和 MessagePack 的区别。 Avro、ProtoBuf、Thrift 的模式演进之路 — 翻译的 Martin Kleppmann 的文章,重点对比了 Avro、ProtoBuf、Thrift 的序列化处理思路。
Hessian、Msgpack 和 JSON 实例对比

Hessian、Msgpack 和 JSON 实例对比

D瓜哥
前段时间,翻译了 Hessian 2.0 的序列化协议,发布在了 Hessian 2.0 序列化协议(中文版)。但是,其中有很多言语不详之处。所以,接下来会用几篇文章来详细解释并实践一下 Hessian 序列化协议,以求做到知其然知其所以然。 目录如下: Hessian 2.0 序列化协议(中文版) — Hessian 序列化协议的中文翻译版。根据后面的“协议解释与实战”系列文章,增加了协议内容错误提示。 Hessian 协议解释与实战(一):布尔、日期、浮点数与整数 — 介绍布尔型数据、日期类型、浮点类型数据和整数类型数据等四种类型的数据的处理。 Hessian 协议解释与实战(二):长整型、二进制数据与 Null — 介绍长整数类型数据、二进制数据和 null 等三种类型的数据的处理。 Hessian 协议解释与实战(三):字符串 — 专门介绍了关于字符串的处理。由于字符串需要铺垫的基础知识比较多,处理细节也有繁琐,所以单独成篇来介绍。 Hessian 源码分析(Java) — 开始第四篇分析之前,先来介绍一下 Hessian 的源码实现。方便后续展开说明。 Hessian 协议解释与实战(四):数组与集合 — 铺垫了一些关于实例对象的处理,重点介绍关于数组和集合的相关处理。 Hessian 协议解释与实战(五):对象与映射 — 重点介绍关于对象与映射的相关处理。 Hessian、Msgpack 和 JSON 实例对比 — 用实例对比 JSON、Hessian 和 MessagePack 的区别。 Avro、ProtoBuf、Thrift 的模式演进之路 — 翻译的 Martin Kleppmann 的文章,重点对比了 Avro、ProtoBuf、Thrift 的序列化处理思路。 本文用实际来对比一下 JSON、Hessian 和 MessagePack 的区别。
Hessian 协议解释与实战(五):对象与映射

Hessian 协议解释与实战(五):对象与映射

D瓜哥
前段时间,翻译了 Hessian 2.0 的序列化协议,发布在了 Hessian 2.0 序列化协议(中文版)。但是,其中有很多言语不详之处。所以,接下来会用几篇文章来详细解释并实践一下 Hessian 序列化协议,以求做到知其然知其所以然。目录如下: Hessian 2.0 序列化协议(中文版) — Hessian 序列化协议的中文翻译版。根据后面的“协议解释与实战”系列文章,增加了协议内容错误提示。 Hessian 协议解释与实战(一):布尔、日期、浮点数与整数 — 介绍布尔型数据、日期类型、浮点类型数据和整数类型数据等四种类型的数据的处理。 Hessian 协议解释与实战(二):长整型、二进制数据与 Null — 介绍长整数类型数据、二进制数据和 null 等三种类型的数据的处理。 Hessian 协议解释与实战(三):字符串 — 专门介绍了关于字符串的处理。由于字符串需要铺垫的基础知识比较多,处理细节也有繁琐,所以单独成篇来介绍。 Hessian 源码分析(Java) — 开始第四篇分析之前,先来介绍一下 Hessian 的源码实现。方便后续展开说明。 Hessian 协议解释与实战(四):数组与集合 — 铺垫了一些关于实例对象的处理,重点介绍关于数组和集合的相关处理。 Hessian 协议解释与实战(五):对象与映射 — 重点介绍关于对象与映射的相关处理。 Hessian、Msgpack 和 JSON 实例对比 — 用实例对比 JSON、Hessian 和 MessagePack 的区别。 Avro、ProtoBuf、Thrift 的模式演进之路 — 翻译的 Martin Kleppmann 的文章,重点对比了 Avro、ProtoBuf、Thrift 的序列化处理思路。 在上一篇文章 Hessian 协议解释与实战(四):数组与集合 中研究了数组和集合的处理方式。接下来介绍对象和映射的处理。 基础工具方法 基础工具方法就不再赘述,请直接参考 Hessian 协议解释与实战(一):基础工具方法 中提到的几个方法。
Hessian 协议解释与实战(四):数组与集合

Hessian 协议解释与实战(四):数组与集合

D瓜哥
前段时间,翻译了 Hessian 2.0 的序列化协议,发布在了 Hessian 2.0 序列化协议(中文版)。但是,其中有很多言语不详之处。所以,接下来会用几篇文章来详细解释并实践一下 Hessian 序列化协议,以求做到知其然知其所以然。目录如下: Hessian 2.0 序列化协议(中文版) — Hessian 序列化协议的中文翻译版。根据后面的“协议解释与实战”系列文章,增加了协议内容错误提示。 Hessian 协议解释与实战(一):布尔、日期、浮点数与整数 — 介绍布尔型数据、日期类型、浮点类型数据和整数类型数据等四种类型的数据的处理。 Hessian 协议解释与实战(二):长整型、二进制数据与 Null — 介绍长整数类型数据、二进制数据和 null 等三种类型的数据的处理。 Hessian 协议解释与实战(三):字符串 — 专门介绍了关于字符串的处理。由于字符串需要铺垫的基础知识比较多,处理细节也有繁琐,所以单独成篇来介绍。 Hessian 源码分析(Java) — 开始第四篇分析之前,先来介绍一下 Hessian 的源码实现。方便后续展开说明。 Hessian 协议解释与实战(四):数组与集合 — 铺垫了一些关于实例对象的处理,重点介绍关于数组和集合的相关处理。 Hessian 协议解释与实战(五):对象与映射 — 重点介绍关于对象与映射的相关处理。 Hessian、Msgpack 和 JSON 实例对比 — 用实例对比 JSON、Hessian 和 MessagePack 的区别。 Avro、ProtoBuf、Thrift 的模式演进之路 — 翻译的 Martin Kleppmann 的文章,重点对比了 Avro、ProtoBuf、Thrift 的序列化处理思路。 在上一篇文章 Hessian 源码分析(Java) 对 Hessian 的 Java 实现做了一个概要的分析,对处理流程以及整体架构做了一个简单的分析。接下来,回到主题,继续来解释 Hessian 序列化协议。这篇文章,我们来重点分析一下数组与集合相关的操作。