开发工具

新 Mac 安装软件脚本

新 Mac 安装软件脚本

D瓜哥
最近公司可以申请零净值 MacBook 笔记本,就随手申请了一个。由于有很多软件需要安装,就搜集了一下以前安装软件的命令,整理成一个安装脚本,分享出来,方便后续再次装机。 1. xcode-select 作为开发人员,这是基础工具包,必须安装: xcode-select --install 2. 安装脚本 安装脚本主要构成如下: 2.1. oh my zsh 安装脚本先安装了 oh-my-zsh。最近帮同事搞 MacBook,没有 oh-my-zsh 的加持,写命令行浑身难受。 Figure 1. oh-my-zsh 由于安装 oh my zsh 会导致脚本退出,所以,单独安装: #!/usr/bin/env bash # # Author: D瓜哥 · https://www.diguage.com # # 安装 oh-my-zsh sh -c "$(curl -fsSL https://cdn.jsdelivr.net/gh/ohmyzsh/ohmyzsh/tools/install.sh)" 2.2. Homebrew 脚本里面主要使用了 Homebrew 来安装软件。 Figure 2. Homebrew 2.3. sdkman Java JDK + Maven 等相关安装,主要使用了 Sdkman,方便多个版本相互切换。 Figure 3. sdkman 2.4. 感谢 jsDelivr 为了解决安装 oh-my-zsh 和 Homebrew 时,GitHub 访问不畅,使用 jsDelivr 将它们的安装链接进行改写,可以利用 CDN 加速,让安装过程更加顺利。
关于接口可维护性的一些建议

关于接口可维护性的一些建议

D瓜哥
在做新需求开发或者相关系统的维护更新时,尤其是涉及到不同系统的接口调用时,在可维护性方面,总感觉有很多地方差强人意。一些零星思考,抛砖引玉,希望引发更多的思考和讨论。总结了大概有如下几条建议: 在接口注释中加入接口文档链接 将调用接口处写上被调用接口文档链接 将接口源代码发布到私服仓库 对于状态值常量,优先在接口参数类或者返回值类中定义 如果使用 Map 对象作为传输载体,要提供 Key 值定义常量 针对 Map 返回值,可以考虑使用将 Map 转化成对象 尽可能简化接口依赖 只传递必要字段,尽量避免大而全的接口 将接口的参数和返回值原始数据打印到日志中 将 RPC 接口的类名及方法打印到日志中 核心思想:以人为本,就近原则,触手可及 下面,D瓜哥对每一条建议做一个详细说明。 1. 在接口注释中加入接口文档链接 在做接口开发时,无论是对自有接口的升级改造,还是针对外部接口的从头接入,都涉及到接口文档。不同之处是,前者的工作重点是书写或者更新接口文档;而后者是根据接口文档开发合适的接入代码。但是,经常遇到的一个麻烦是,找不到接口文档。在组内需要找老同事询问;如果是跨部门,还需要两层甚至三层的进行转接,非常麻烦。 D瓜哥认为,在这种情况下,为了方便大家维护,最好的办法就是将接口文档链接直接放在代码注释中,这样后续维护的人员,直接就可以点击链接直达接口文档,简单方便高效。如果是新建的接口,就可以先创建一个空文档,把链接放在注释中,后续再书写文档内容。如果是维护已有接口,可以在维护时,将缺失的链接加入到注释中,自己方便,也方便其他人进行后续的维护更新。这样,在循序渐进的过程中,逐步就可以把文档链接补充到代码中,方便维护代码,也同步更新文档。 2. 将调用接口处写上被调用接口文档链接 在调用其他系统的接口时,没有接口文档,几乎寸步难行。在第一次接入接口时,绝大多数情况下,都是参考着接口文档做接入工作。但是,目前的情况时,接入时参考文档,参考完就随手把文档给“扔了”。后续如果还需要做进一步升级维护,还需要到处找接口文档;另外,交互的系统难免有一些 Bug,在和其他系统维护人员对接处理 Bug 时,只有接口没有文档,对方可能也需要去找文档链接。无形中,很多时间都浪费在了找文档的过程中。 D瓜哥最近尝试了一个实践,就是在接口调用的地方,把接口文档链接当做注释加入到代码中。这样,无论是后续维护升级,还是沟通协调处理问题,都非常方便。别人问接口是什么,连接口+文档都可以一把复制就搞定。 经过最近一段时间的实践情况来看,这个处理非常方便,是一个非常值得推广的实践。再插一句,也可以像一条建议一样,可以在维护代码时,不断把已接入的接口文档加入到调用接口的地方,循序渐进,方便后续人维护升级。 3. 将接口源代码发布到私服仓库 接口文档链接在注释中,在构建结果中就不复存在了。所以,为了方便接口使用方可以在接口中查询到对应的接口文档,就需要把源码也发布到私服仓库中。 这里只说明一下 Java 的相关处理办法。如果使用 Maven 作为构建工具的话,默认是不会将源代码发布到私服仓库中的。关于如何将源代码发布到,在 升级 Maven 插件:将源码发布到私服仓库 中已经做过相关介绍,这里就不再赘述。 除了将源码发布到私服仓库,另外,还建议编译构建时,保持方法的原始参数命名。这个也可以通过配置 Maven 插件来完成,具体配置见: 升级 Maven 插件:字节码文件包含原始参数名称。 4. 对于状态值常量,优先在接口参数类或者返回值类中定义 在做接口开发时,很多数据都有一个状态值,比如订单状态,再比如接口状态等等。目前的一个情况时,这些状态值大部分书写在文档中,在接入接口时,需要接入方自定义这些状态值。这就有些繁琐了,而且状态定义也不明确,甚至有可能遗漏一些重要的状态值。有些懒省事,直接在代码中硬编码一个魔法值,后续维护的跟还需要根据上下文反推这个值的含义,非常不利于维护。 D瓜哥个人觉得,有两个处理办法: 如果状态值不是很多,优先在接口参数类或者返回值类中定义。 如果状态值很多,可以考虑单独抽取成一个常量类或者枚举类。 这样使用的时候,触手可及。不需要到处去找。 5. 如果使用 Map 对象作为传输载体,要提供 Key 值定义常量 有些系统可能考虑方便增加字段,选择使用 Map 作为数据载体。自己开发的时候很爽,但是给接口接入却非常不友好。接入方从 Map 中获取数据时,要么自己定义 Key 值;要么直接使用魔法值硬编码在代码中。使用前者方案,就需要在各个接入方都需要自定义一套;使用后者,初期是省事了,后来维护的人员就懵逼了。这都无形中增加了很多维护成本。
Versions Maven 插件简介

Versions Maven 插件简介

D瓜哥
在 制定组织内 Maven BOM 的一些规范 中,D瓜哥 介绍了一些组织内指定 Maven BOM 的一些规范。根据这些规范,D瓜哥 创建并维护了部门内部的 Maven BOM。今年,要求对部门内的陈旧依赖做一些升级工作。所以,在 关于升级 Spring 等依赖的一些经验 中介绍了一些升级开源依赖的经验;在上一篇文章 升级 Maven 插件 中介绍了升级 Maven 插件的一些注意事项。 D瓜哥一直坚持“机器可以干的事情,就应该交给机器干”。对于依赖管理,Maven Enforcer 插件就可以对依赖做必要的检查,所以,在 使用 Maven Enforcer 插件检查依赖 中,介绍了如何使用 Apache Maven Enforcer 来管理依赖。由于要维护部门内部的 Maven BOM,同时由于版本控的特质,所以,需要时长检查依赖升级情况。原来都是手动检查,需要一个一个去搜索各个依赖,不仅费时费力,而且还低效。最近,Maven 有一个插件可以胜任这个工作,它就是: Versions Maven Plugin。 依赖检查 Versions Maven Plugin 支持两种配置方式: 外置配置文件 maven-version-rules.xml; 内置在 POM 文件中,直接写在插件的 <configuration> 标签中。 第一种方案不方便迁移。还要额外管理一个配置文件。推荐使用第二种方式。另外,直接将这些配置放在 Maven BOM 中,使用继承的方式使用 Maven BOM,那么子项目就自动继承了这些配置。后续也只需要一个地方的配置即可。示例配置如下: <!-- @author: D瓜哥 · https://www.diguage.com --> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>versions-maven-plugin</artifactId> <version>2.15.0</version> <configuration> <ruleSet> <ignoreVersions> <!
升级 Maven 插件

升级 Maven 插件

D瓜哥
D瓜哥在 关于升级 Spring 等依赖的一些经验 中,介绍了一些升级 Spring 等依赖的一些经验。在 升级 iBATIS/MyBATIS 对处理 DuplicateKeyException 的影响 中,分析了升级 iBATIS/MyBATIS 对处理 DuplicateKeyException 异常的影响。在升级中,还遇到一些 Maven 插件相关的问题。这里也分享出来,希望对大家有所帮助。 Properties 文件编码错误 在升级过程中,遇到过 Properties 文件编码错误的问题。可以通过配置对应的编码来解决这个问题。配置如下: <!-- D瓜哥 · https://www.diguage.com --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-resources-plugin</artifactId> <version>3.3.0</version> <configuration> <propertiesEncoding>ISO-8859-1</propertiesEncoding> </configuration> </plugin> 参考资料 Apache Maven Resources Plugin – Filtering Properties Files。 使用 Maven Enforcer 插件检查依赖 私以为“机器可以干的事情,就应该交给机器干”。对于依赖管理,Maven Enforcer 插件就可以对依赖做必要的检查。所以,推荐使用 Maven Enforcer 插件来检查低版本及有安全漏洞的依赖。 详细介绍请参考: 使用 Maven Enforcer 插件检查依赖 字节码文件包含原始参数名称 一些对外发布的依赖,建议将原始参数名称编译到构建结果里。可以通过指定构建参数来完成。 <!-- D瓜哥 · https://www.diguage.com --> <plugin> <groupId>org.
升级 iBATIS/MyBATIS 对处理 DuplicateKeyException 的影响

升级 iBATIS/MyBATIS 对处理 DuplicateKeyException 的影响

D瓜哥
在 关于升级 Spring 等依赖的一些经验 中,分享了一些开源依赖的升级经验。部分小伙伴质疑升级 iBATIS/MyBATIS 会影响对 DuplicateKeyException 异常的处理。这篇文章就从源码分析/代码更新的就角度来分析一下升级相关依赖是否会对 DuplicateKeyException 异常的处理带来实质性的影响。 由于主要的技术栈涉及 MySQL 驱动、iBATIS、MyBATIS、Spring 周边等。所以,本文仅分析涉及的这些依赖。 D瓜哥使用 MySQL: Employees Sample Database 搭建了一个 Spring + MyBATIS + MySQL Connector/J 的测试环境。连续插入两条一样的数据,单步调试,在 com.mysql.jdbc.MysqlIO#sendCommand 方法中,就可以观察到如下异常: Figure 1. MySQL Error 1062 从这里可以明显看出,MySQL 驱动返回的异常中, venderCode 编码是 1062。 顺着这个线,往上走,到 org.apache.ibatis.session.defaults.DefaultSqlSession#update(java.lang.String, java.lang.Object) 方法中,可以看到, Figure 2. MyBATIS wrap Exception 在这里,会将 SQLException 包装成 PersistenceException,这也是 MyBATIS 对外暴露的统一的异常类。 继续往上走,就到了 org.mybatis.spring.SqlSessionTemplate.SqlSessionInterceptor#invoke 方法: Figure 3. MyBATIS translateException 在 SqlSessionInterceptor#invoke 方法的异常处理中,将 PersistenceException 异常通过 org.springframework.dao.support.PersistenceExceptionTranslator#translateExceptionIfPossible 方法,将异常转换成 DataAccessException 对象。 DataAccessException 类是 Spring 数据访问的异常类基类。
关于升级 Spring 等依赖的一些经验

关于升级 Spring 等依赖的一些经验

D瓜哥
到公司后,熟悉了一些项目后,发现大部分项目的依赖都比较陈旧,比如某些项目还在使用 Spring 3.x 的版本。所以,在进行需求开发时,也顺手把一些项目的依赖给升级了一下。周五,一个小伙伴问我关于升级 Spring 的经验。正好趁此机会,把一些经验总结一下。 下面的描述以 Java 8 为准,没有在其他版本 Java 上试验过。参考时,请慎重。描述的原则如下: 尽量选择还在维护中的版本,而不是已经 End of Life 的过时版。这样有问题可以及时反馈并得到修复。 Java 8 是目标版本,所以,一定要兼容 Java 8。 Spring Framework 升级 Spring Framework 从 3.2.6.RELEASE 开始提供 BOM。可以利用 BOM 简化 Spring 依赖声明: <!-- D瓜哥 · https://www.diguage.com --> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-framework-bom</artifactId> <version>5.3.25</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> 这样,就不需要重复声明 Spring 依赖的版本,直接使用即可。 Spring Framework Bom 保证了 Spring 自身依赖的版本统一。 这里,关于 Spring 的升级,还有几点需要说明: 从 Spring 3.X 升级到 Spring 4.X+ 后,原来的 MappingJacksonHttpMessageConverter 已经被删除了;直接使用 MappingJackson2HttpMessageConverter 即可。
使用 Maven Enforcer 插件检查依赖

使用 Maven Enforcer 插件检查依赖

D瓜哥
最近公司项目要对一些内部依赖做集中升级。为此,D瓜哥发布了一个 BOM(BOM stands for Bill Of Materials),用于规范项目依赖及版本。 但是升级后,效果不理想,检查发现还是有不少依赖的版本依然不符合要求。经同事提醒,可以使用 Apache Maven Enforcer 来做规范检查,测试一下效果确实不错。 将 Apache Maven Enforcer 和 Extra Enforcer Rules 的文档大致巴拉了一遍之后,根据项目的实际情况,挑选出来可用规则如下: 比较有用的几个规则 bannedDependencies – 排除不需要的依赖,引入需要的依赖。 banDuplicatePomDependencyVersions – 防止依赖重复声明。 dependencyConvergence – 确保所有依赖收敛到相同的版本。也可以考虑加入。 reactorModuleConvergence – 多模块开发时,确保父子模块的版本是一致的。 requireJavaVersion – 检查 JDK 的版本 requireMavenVersion – 检查 Maven 的版本 requireReleaseVersion – 这个可以通过激活生产环境的 profile 来启用该规则,保证发布的不是快照版。 requireUpperBoundDeps – 确保直接引用的依赖不比间接解析出来的依赖版本低。感觉这个也挺有用,但是使用方式还没搞清楚。实例有些模糊。 banDuplicateClasses – 检查重复类定义。可以避免一些特殊情况。 requirePropertyDiverges – 确保项目定义的属性与依赖中包含的属性不重复。 enforceBytecodeVersion – 确保使用的字节码版本不高于指定版本。 banCircularDependencies – 确保没有循环依赖。 requireEncoding – 指定项目字符集。 实践总结 D瓜哥把上面的规则几乎全部试用了一遍,把发现的一些需要特别注意的地方标注记录一下吧:
文档技术方案选型:AsciiDoc vs Markdown

文档技术方案选型:AsciiDoc vs Markdown

D瓜哥在前面的文章 使用 Hugo 搭建博客 中介绍了如何用 Hugo 搭建个人博客。部门准备系统地整理一下各个小组的文档。恰好 D瓜哥 对写文档非常感兴趣,正好写个材料分享一下血泪经验。 编辑进化之路 第一代:WordPress 缺点:写作和排版割裂,排版耗时且繁琐 第二代:MarkDown 缺点:方言众多,工具链不够完整。 现在已经改观很多。 第三代:AsciiDoc 轻量级标记语言的优点 思路与格式融为一体 在整理文档时,随手加入格式管理,不需要为格式分心,也无须浪费时间调整排版。 代码高亮 AsciiDoc 与 MarkDown 都支持 /** * @author D瓜哥 · https://www.diguage.com/ */ public class Main { public static void main(String[] args) { System.out.println("Hello, D瓜哥!"); } } 文本格式 文本格式,天然跨平台,支持性好,方便编辑与管理。 结合 Git,支持版本管理。 生态完善 Markdown Hugo: The world’s fastest framework for building websites — Hugo 使用 yuin/goldmark: A markdown parser written in Go. 来做转换工作。也支持 AsciiDoc,不过需要挑选比较合适的主题: Hugo Themes。
优化网站

优化网站

D瓜哥
前几年应大势所趋,使用 Let’s Encrypt 给所有网站都上了 HTTPS。因为去年年中把博客托管到 GitHub 上了,导致一起申请 HTTPS 证书的站点无法按时更新证书。所以,所有证书都过期了。前几天有朋友发消息问我,Byte Buddy 的中文文档是不是我搞的?正好借机把证书更新了一下。 此后不久,无意间查看了一下网站服务器的操作系统和 Nginx 版本,发现竟然是 Ubuntu 16.04 + Nginx 1.12。Ubuntu 16.04 都”过期“了,正好得空升级一下。 升级操作系统 以前没有升级过操作系统大版本,正好借此机会练手: # 升级操作系统版本执行,先做一下常规升级 sudo apt-get update sudo apt-get upgrade sudo reboot # 检查可以升级的版本 sudo do-release-upgrade -c # 开始升级 sudo do-release-upgrade 升级完成后,检查操作系统版本: $ cat /etc/os-release NAME="Ubuntu" VERSION="20.04.3 LTS (Focal Fossa)" ID=ubuntu ID_LIKE=debian PRETTY_NAME="Ubuntu 20.04.3 LTS" VERSION_ID="20.04" HOME_URL="https://www.ubuntu.com/" SUPPORT_URL="https://help.ubuntu.com/" BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/" PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy" VERSION_CODENAME=focal UBUNTU_CODENAME=focal 有几点需要注意: LTS 版本升级,只能一步一步升级,从 16.04 升级到 18.04,再从 18.
日志最佳实践探究

日志最佳实践探究

加入公司以来,参与了很多个项目的开发维护;也排查处理过很多线上问题;为了写 Mock 测试,也专门去日志系统上扒拉过不少日志等等。在整个过程中,对日志的认识有了不少更深刻的认识和体会。也发现不少问题。这里先从存在的问题展开论述。 日志存在的问题 从个人的眼光上来看,当前的系统存在如下问题: 必要日志没有打印出来,导致在追踪问题或测试代码时,带来不必要的麻烦。比如查看一个接口的返回值用于 Mock 测试;再比如 RPC 调用报错,返回值以及错误信息没有打印到日志中,不知道具体错误原因是什么。 日志抽取中日志路径配置错误,导致日志重复收集,带来不必要的处理和存储成本。 日志代码不规范,导致不必要的性能消耗;或者大促时,日志降级不生效。 日志框架繁多,造成造成冲突,遗漏部分日志。 日志配置不规范,不利于日志的采集和清洗。 日志和调用链路物理隔离,查看一个请求的整个调用链路上的日志非常不方便,不利于问题的快速排查和定位。 大家的系统中,存在什么样的日志问题?欢迎留言交流讨论。 针对这些问题,我觉得有些地方值得发力一下。然后,做了一些探索,总结一下,以备后续使用。 日志最佳实践探索 对于日志的使用,相信所有的开发人员都比较清楚,网上也有大量资料,相关日志框架的官方文档,也写的非常详尽,这里就不再赘述。 本文从一个角度对日志规范进行探究:在排查问题时,能否通过日志来尽快地了解系统运行状态,定位问题原因?另外,由于 Java 的日志框架特别多,有一些比较容易迷惑的问题,尝试做出一点总结。 系统运行后,不严格地说,再去观察系统运行状态,就类似于在黑夜中行走。此时,向你扔过来一块板砖🧱,那么,事后如何追责呢? 请问:你能否成功躲开这块叫做 Bug 的板砖🧱? 日志用来记录用户操作、系统运行状态等,是一个系统的重要组成部分。然而,由于日志通常不属于系统的核心功能,但是在日志对于排查问题,有无可替代的作用,理应得到所有开发人员的重视(不重视,怎么甩锅?!)! If dog is a man’s best friend, logs are software engineer’s best friend. — Geshan Manandhar Logging best practices 好的日志可以帮助系统的开发和运维人员: 了解线上系统的运行状态 快速准确定位线上问题 发现系统瓶颈 预警系统潜在风险 挖掘产品最大价值 可以将一个流程完整串起来(比如orderId) …… 不好的日志导致: 对系统的运行状态一知半解,甚至一无所知 系统出现问题无法定位,或者需要花费巨大的时间和精力 无法发现系统瓶颈,不知优化从何做起 无法基于日志对系统运行过程中的错误和潜在风险进行监控和报警 对挖掘用户行为和提升产品价值毫无帮助 …… 日志从功能来说,可分为诊断日志、统计日志、审计日志。统计日志一般由运维组负责;而审计日志,一般是需要通过代码来实现。这里重点来说说诊断日志。 诊断日志, 典型的有: 请求入口和出口 外部服务调用和返回 资源消耗操作: 如读写文件等 容错行为: 如云硬盘的副本修复操作