JUnit 5 使用 Mockito 2 进行测试

JUnit 5 刚出来那时,也就是第一个版本 5.0.0 时,还不能很好的支持 Mockito 的测试,因为 Mockito 没能跟得那么紧密。那时候 JUnit 5 只能试验性的提供了一个极不正式的 com.example.mockito.MockitoExtension, 看那包名就知道不是来真的,所以决定再等。JUnit 5 不再原生支持 JUnit 4 的 Rule,一切都将是 Extension,那也是要求 Mockito 能够与之俱进。现在等来了,JUnit 5 进化到了 5.2.0, Mockito 也早已有了一个单独的模块 mockito-junit-jupiter 来迎接它。

在 Mockito 2.1.0 的 What's new in Mockito 2 中记述了 JUnit 5 为 Mockito 2 开发了一个 MockitoExtension。追溯到 Mockito 2 的 Release Notes, 我们发现 Mockito 2 官方最早引入 MockitoExtension 的版本是 2.16.3(2018-03023)。我对 Mockito 对 JUnit 5 支持的最新更新是从这个 Pull Request MockitoExtension for JUnit5 得知的。

一句话讲就是现在的 Mockito 2 有原生态的 MockitoExtension 来支援 JUnit 5, 可以非常放心可靠的让 JUnit 5 和 Mockito 2 一起稳定工作。因此前面那个包名带 example 字样的 MockitoExtension 链接也就无效了。 阅读全文 >>

AWS S3 应用 KMS Key 进行服务端数据加密

当我们把数据搬上云端,为了保护敏感数据一定要对数据存储进行加密。而对于 S3 上的数据加密最简单莫过于启用服务端数据加密,可以是 AES-256 或 AWS-KMS。进行了服务端加密码的文件有什么不同呢?首先从 AWS S3 文件列表视图中看到的文件大小与原始文件大小是一样的,其实数据在 AWS 机房是加密存储了的,比如说直接在 AWS 服务器上拷出那些 S3 文件是不能理解的。

本文涉及的内容包括:

  1. 服务器端加密后的 S3 文件有何不同
  2. 不启用 Bucket 的默认加密如何进行服务端数据加密(含 AWS CLI 和  Java 代码操作)
  3. 启用及应用 Bucket 的默认加密
  4. 上传与下载文件的 IAM role 需要的权限

服务器端加密后的 S3 文件有何不同

首先看现象,后面才能确认我们的操作是否对数据真的进行了服务端加密。查看使用 AWS-KMS key 加密后的 S3 文件的 Overview, 看到下面两个属性

Server-side encryption
AWS-KMS
KMS key ID
arn:aws:kms:us-east-1:123456789012:key/e4ce859b-786b-4b8e-8b73-4968adb6e4ed

没加密的话, Server-side encryption 是 None, 并且没有 KMS key ID 属性。 阅读全文 >>

Springfox 解决在单一资源操作多个方法实现时生成 Swagger 文档的问题

在命名本文的标题都敲打了几分钟时间,问题很简单,然而用简短的一个标题完全描述出来却有点费事。在 Spring MVC 项目结合 Springfox 来生成 Swagger API 文档时,如果一个资源操作因为请求参数的不同而映射到多个 controller 方法,那么 Swagger 可能只能生成某一个 API 条目,其余都被忽略。至于为什么说是 "可能", 可能正好未遵循命名规范而躲过了这一劫。由此引出

我们的问题

我们这里用了资源操作一词,它包含了两部分信息: 资源与操作,比如 /users/{userId} 是资源,而发生在其上的 HTTP 各种方法,如 POST, GET, PUT, DELETE 等就是操作。而 Spring MVC 中允许我们针对不同的查询参数把相同的资源操作映射到不同的 controller 方法上,也是为了保持逻辑上更为清晰。

比如下面的例子路由配置的例子

GET /users/{userId}     UserController.getUserInfo                                    //默认
GET /users/{userId}     UserController.getUserInfo                                   //当有 ?source=file 时
GET /users/{userId}     CloudUserController.getUserInfoFromCloud       //当有 ?source=cloud 时

看到上面资源与操作完全相同,仅仅因为 source 查询参数的不同而映射到三个 controller 方法。用代码体现如下图 阅读全文 >>

Spring MVC 项目中用 SpringFox 生成 Swagger2 API 文档

前面写过一篇如何建立 一个最基础的 Spring 4 MVC Maven 项目,现在是要在此基础上使用 SpringFox 来生成 Swagger 文档以及 SwaggerUI。Swagger 2 是基于 OpenAPI3.0 的实现,SpringFox 是用于在 Spring 项目中生成 Swagger 文档及 UI, 主要包括两个组件 springfox-swagger2 和  springfox-swagger-ui

本文是基于普通的 Spring MVC 项目的实例,而非针对 SpringBoot 项目,实际运用应该大同小异,无外乎 SpringBoot 中有许多自动配置项。并且本文也不涉及到 Swagger 众多注解的具体用法,具体请查看源代码或相关文档。

开始本文的示例,首先需要搭建起 Spring MVC 项目,具体做法请参考  一个最基础的 Spring 4 MVC Maven 项目

对应于两个组件 springfox-swagger2 和 springfox-swaager-ui 的应用,从大尺度上讲有两方面的内容,分别是

一:生成 Swagger 2 的 JSON API 文档

使用第一个组件 springfox-swagger2 能够让项目运行期自动生成 Swagger 2 的 JSON API 文档,分两步走

添加 springfox-swagger2 依赖

在项目的 pom.xml 文件中加入依赖配置

截止目前,springfox 官方已释放出 springfox-swagger2:2.9.0, 但是在 Maven 的中央仓库中只有 2.8.0 版,所就采用 2.8.0 版本 阅读全文 >>

一个最基础的 Spring 4 MVC Maven 项目

这是一个最基本的 Spring 4 MVC 的 Maven 项目,非 SpringBoot 的,SpringBoot 由于有许多自动化配置特性,会更简单些。本例全部用 Java 代码作为配置,免除了创建 web.xml 和如 dispatcher-servlet.xml 这样的文件。本人更倾向于 Java 配置,它的优势在于能进行编译期检查,逻辑性也强,配置文件只是改动无需重新编译,都是要重启服务的; 关于使用 XML 配置文件的方式可参考文后的链接。

本文侧重于 Spring MVC 项目提供 RESTful JSON API, 因而静态 Web 内容提及较少。创建一个 Maven 项目的方式,可以直接创建一个 pom.xml 文件,然后编辑它的内容,使用 IntelliJ IDEA 的话只需要选择导入为一个 Maven 项目就成,Eclipse 的话可能还要事先运行 mvn eclipse:eclipse 初始化一下。

项目结构布局

就是一个普通的 Maven 项目,稍稍不同的是 src/main 目录中除了 java 和 resources 之外,还有 webapp 目录,用于存放 web 静态文件或模板文件的。 阅读全文 >>

AWS Batch 是如何向 Docker 容器传递参数

AWS Batch 提供了简单有效的方式来运行 Docker 镜像,在单纯执行一个计算任务时比 ECS 使用起来要方便许多。AWS Batch 在提交任务时可以执行 command, environment 和 parameters, 那么它将如何传递那些参数给 Docker 容器呢?

首先看一下提交任务的脚本

注:aws cli 命令参数中有 - 时需要用  --parameters='--name=Spring' 等号的格式

我们将用上面的脚本提交一个任务,然后用 docker inspect <container-id> 观察 Docker 容器收到了什么参数

也没什么意外的,inspect 容器后我们看到 阅读全文 >>

如何向 Docker 容器传递参数

我们在运行 docker 镜像时希望能用下面的命令向容器传递命令行参数

docker run <image-name> <command> arg1 arg2
docker run <image-name> arg1 arg2

其实只有第一种形式,紧随镜像名后那个总是一个命令,其后才是参数。如果要向 docker 容器传递参数时,Dockerfile 该如何写,这就有必要稍稍了解一下 Dockerfile 中  CMD 和 ENTRYPOINT 这两个指令,并且它们有 exec 和 shell 两种格式的写法。详情请见上篇 Dockerfile 中命令的两种书写方式的区别

对于一个 docker 镜像,我们可以这么来理解  ENTRYPOINT 与 CMD 的关系

  1. 如果没有定义 ENTRYPOINT, CMD 将作为它的 ENTRYPOINT
  2. 定义了 ENTRYPOINT 的话,CMD 只为 ENTRYPOINT 提供参数
  3. CMD 可由 docker run <image> 后的命令覆盖,同时覆盖参数

对于 #1 和  #2 更精致的理解是容器运行的最终入口由 ENTRYPOINT 和实际的 CMD 拼接而成。ENTRYPOINT 和 CMD 需转换为实际镜像中的 exec 格式来拼接,合并后的第一个元素是命令,其余是它的参数。 阅读全文 >>

Dockerfile 中命令的两种书写方式的区别

最早的初衷是要研究一下运行 Docker 容器时如何向其传递参数,却冷不防掉入了另一个深渊,不得不关心起 Dockerfile 中命令(包括 RUN, CMD 和 ENTRYPOINT) 的两种不同写法上的区别。

所以呢,先要稍稍了解一下 Dockerfile 中 RUN, CMD, ENTRYPOINT 这三个指令

  1. RUN 执行命令并创建新的镜像层,常用于安装软件包。可以多个,为避免创建过多的镜像层,我们尽量把命令合在一起,用分号或 &&。它与容器运行期无关。
  2. CMD 设置容器启动后默认执行的命令及其参数,但 CMD 能够在启动容器时被覆盖。多个 CMD 只有最后一个是有效的
  3. ENTRYPOINT 配置容器启动时运行的命令。多个  ENTRYPOINT 也是只有最后一个有效

关于以上三个命令的区别,这儿有篇文章讲得很清楚 RUN vs CMD vs ENTRYPOINT - 每天5分钟玩转 Docker 容器技术(17),此处也照搬了些文字。

RUN, CMD 和  ENTRYPOINT 都支持两种写法,即 exec 和 shell 格式,见 Dockerfile reference #ENTRYPOINT 对这两种方式的解释。RUN 只影响如何构建镜像,所以镜像中不保留 RUN 命令。CMD 和 ENTRYPOINT 都可以在运行容器时执行命令,这里不讲述它们间的区别,而要说的是它们所支持的 exec 和  shell 两种格式的写法。此篇以 ENTRYPOINT 为例说明两种格式的区别,CMD 类似。 阅读全文 >>

AWS SQS 队列的 DLQ 设置与行为

和 Lambda 类似,AWS 的  SQS 队列也提供了 DLQ(Dead Letter Queue) 来支持重试功能,可以设定某一个消息在接收多次重新变得可见后进入到另一个 SQS 队列中。比如说队列 user-id-queue,设定了它的 Redrive Policy 为三次接收后转入到另一个 SQS 队列 user-id-dlq, 就会显示为

Maximum Receives 3
Dead Letter Queue arn:aws:sqs:us-east-1:<account_id>:user-id-dlq

在运用 SQS 的 DLQ 之前首先要理解 SQS 中消息的几个概念
  1. Message Available:  SQS 客户端可以获取到的消息, 即 Visible Messages
  2. Messages in Flight: 消息被 SQS 收取了之后,由 Available 转为 In Flight, 该状态的消息不能被客户端接收到
  3. Visibility Timeout:  消息停留在 In Flight 状态的时间, 如果在 Timeout 之前未删除这个消息,该消息重新变为 Available 状态

我们可以设置 SQS 队列的默认 Visibility Timeout 大小,也可以在代码中收取消息时指定这个值。

所以我们能够在集群环境中应用 SQS 的这个特性让多个节点同时监听单个 SQS 队列,基本上保证每个节点处理各自不同的消息。有一种例外就是:假设我们设置了 Visibility Timeout 是 30 秒,客户端 1 获取到消息后,消息变为 In Flight 状态,但 30 秒后仍然在处理过程中,此时消息回到Available 状态,客户端 2 也能获取到该消息,这也会造成单条消息的重复处理。解决的办法之一是适当延长 Visibility Timeout 的时间,给予第一个客户端更充分的处理时间。 阅读全文 >>

unmi.cc 博客大事记

写下此篇流水纯粹是为了重拾那些零星的记忆,只能建立起一个模糊的脉络,找不回具体的时间点了,现在也是试着能否从某些日志里寻回些线索来。本篇日志最早创建于 2014 年 2 月份,中间偶有修改,却一直在草稿箱里静静的躺着。终于又时隔多年首次放出一篇非技术文出来。

1. 开始的开始,2001 年工作起,进入一个几乎完全陌生的程序世界。为了奋力从大海洋中汲取营养,大跃进的方式学习,并坚持在笔记本上面现在看来像是抄写一样的记笔记,不觉几年功夫,工工整整,满满的记载了四五本笔记本。还有看到那些好的东西也爱打印下来,其实打印出来的东西与它在网络上是一回事,没完整看的还是没看完,写下来了可不一样。这样记笔记的习惯其实也是工作之后的事情,上学期间最爱在课本上记东西。

2. 到后来是网络的盛行,也是信息量的爆炸的时代,再系统再全的书籍也无法与 Google 相比了。所以大概是 2005 年想到了在 QQ 空间里记了三五篇,一块娱乐之地上记录这些东西格调十分不搭。

3. 找到了第一个真正意义上的 BSP,那就是 blogcn.com,它现在完全变样了。QQ 上的几篇只需要复制到编辑框保存就行,那应该是  2006 年的事情。blogcn 是个综合性的 BSP,不好玩,代码高亮用工具保存成的带样式的 HTML 代码,好像也只在这里折腾了一年多时光,到  2007 吧,继续寻租。 阅读全文 >>