Terraform 进阶 - 部署 Lambda 并创建相关资源

昨日刚刚体验了 Terraform 是一个什么鬼东西 Terraform 使用 - 从最简单例子开始,今天再进一步。将来尝试的是使用 Terraform 来部署一个 Lambda 应用,并创建相关的资源。

本例中的 Lambda 要由 Kinesis 来触发,并写数据到 S3 Bucket 中去,所以需要做的事情大致如下:

  1. 创建 IAM Role, 该 Role 要能访问 S3, Kinesis 和 CloudWatch
  2. 创建一个 Kinesis Stream (指定 Shard 数目)
  3. 创建一个 S3 Bucket
  4. 部署 Lambda (要指定能访问 S3 Bucket 的 Role, 并其他参数,如环境变量)
  5. 设置 Lambda 的 Kinesis 触发器 (指定源 Kinesis Stream 和  batchSize)

以下是 Lambda 的实现代码,从 Kinesis 读出字符串,逗号分割,第一部分作为 S3 Key, 第二部分作为文件内容写入到 S3 Bucket 中去。S3 Bucket 名称从环境变量中读取。 阅读全文 >>

Java 反射有效的修改 final 属性值

两年前写过一篇 Java 反射修改 final 属性值, 在这里重新温习一下,假设有个类

class Person {
    public final String name = "Mike";
}

这里声明 name 为非静态的属性只是为了说明反射修改 final 属性无关乎静态不静态,静态只是表现在它是一个类属性,在一个类加载器空间只会有一份拷贝,仅此而已。

创建一个通用方法进行反射修改属性值

调用 modify(...) 方法试图修改 person 的 name 属性 阅读全文 >>

Terraform 使用 - 从最简单例子开始

Terraform 是一个 IT 基础架构自动化编排工具,它的口号是 "Write, Plan, and create Infrastructure as Code", 基础架构即代码。具体的说就是可以用代码来管理维护 IT 资源,比如针对 AWS,我们可以用它创建,修改,删除 S3 Bucket, Lambda, EC2 实例,Kinesis, VPC 等各种资源。并且在真正运行之前可以看到执行计划(即干运行-dryrun)。由于状态保存到文件中,因此能够离线方式查看资源情况 -- 当然,前提是不要在 Terraform 之外对资源进行修改。

Terraform 配置的状态除了能够保存在本地文件中,也可以保存到 ConsulS3, azure, http, swift 等处。

Terraform 是一个高度可扩展的工具,通过 Provider 来支持新的基础架构,AWS 不过为目前官方内建 68 个 Providers 中的一个。其他能用 Terraform 的地方有 Alicloud(阿里云, 实名制备案才能用), Google Cloud, Heroku, Kubernetes, Microsoft Azure, MySQL, RabbitMQ, Docker 等等。愿意的话可以写自己的 Provider, 如搞个 Kafka 的话,用来管理 Topic 等的创建,维护工作。

Terraform 之前我们对 AWS 的操作用的是 awscli, 或 Serverless。awscli 什么都能做,但它是无状态的,必须明确用不同的命令来创建,修改和删除。Serverless 不是用来管理基础架构的,用它创建  Lambda 时创建资源都是很麻烦的事。AWS 提供的 CloudFormation 才是与 Terraform 较类似的工具,但是看到用法就头疼。 阅读全文 >>

Kafka 发布消息时如何选择 Partition

本文旨在了解 Kafka 发送消息到有多个 Partition 的 Topic 时如何选择 Partition。或许多数人已经知道 Kafka 默认(当 key 为 null) 时采用 Round-robin 策略,也就是雨露均沾,风水轮流转,实现类是 DefaultPartitioner。但我们实际应用中为保持相关消息按序到,就必须送到指定的 Partition,方法可以有

  1. 指定 Partition 编号
  2. 指定 Key
  3. 自定义 Partitioner - 实现 org.apache.kafka.clients.producer.Partitioner, 并通过属性注册

还应考究当指定了 Key 或 Partition 编号发送消息后,后续消息 key 为 null 会选用哪个 Partition。最后再思考一个问题,Consumer 每次  poll 时是获得的消息列表是否只包含一个 Partition 源还是可以多个 Partiton 源。

为完成本次实验,可以本地搭建一个 Kafka 环境,参考 简单搭建 Apache Kafka 分布式消息系统。待 Zookeeper 和 Kafka 正常启动后,我们用下面的命令创建一个 Partition 数量为 3 的 Topic partition-test

bin/kafka-topics.sh --create --zookeeper localhost:2181 --replication-factor 1 --partitions 3 --topic partition-test

验证一下该 Topic 的信息 阅读全文 >>

谁说 HTTP GET 就不能通过 Body 来发送数据呢?

当我们被问及 HTTP 的 GET 与 POST 两种请求方式的区别的时候,很多答案是说 GET 的数据须通过 URL 以 Query Parameter 来传送,而 POST 可以通过请求体来发送数据,所以因 URL 的受限,往往 GET 无法发送太多的字符。这个回答好比在启用了 HTTPS 时,GET 请求 URL 中的参数仍然是明文传输的一样。

GET 果真不能通过 Request Body 来传送数据吗?非也。如此想法多半是因循着网页中 form 的 method 属性只有 get 与 post 两种而来。因为把 form 的 method 设置为 post, 表单数据会放在 body 中,而 method 为 get(默认值) 时, 提交时浏览器会把表单中的字符拼接到 action 的 URL 后作为 query parameter 传送。于是乎就有了这么一种假像:HTTP GET 必须通过 URL 的查询参数来发送数据。

其实 HTTP 规范并未规定说 GET 就不能发送 body 数据,在 RFC GET 中只是说

The GET method means retrieve whatever information (in the form of an entity) is identified by the Request-URI.

只是说 GET 意味着通过 URI 来识别资源。

我也是本着传统上对 GET 与 POST 区别的误解很多年,今天突然意识到 GET 应该可以使用 body, 况且 HTTP 本身是一个纯文本的协议。没有测试就没有 100% 的发言权,所以做了如下的测试 阅读全文 >>

自定义类加载器动态加载 JDBC 驱动

我们可以用自定义的 URLClassLoader 从外部动态加载类,并使用它。但数据库驱动的管理类 DriverManager 却不比较苛刻,不承认非当前应用系统加载器加载的驱动类。见 DriverManager 的 JavaDoc 

When the method getConnection is called, the DriverManager will attempt to locate a suitable driver from amongst those loaded at initialization and those loaded explicitly using the same classloader as the current applet or application

对于有有应用自定义类加载器加载数据库驱动类的需求时,就要对原 Driver 简单包装一下。继续往后会说介绍为什么要这么做。

说明一下,DriverManager 能够根据 JDBC 连接字符串匹配到驱动类,所以一般来说都不需要显式调用 DriverManager.registerDriver() 方法。

先看 DriverManager 在应用外部驱动类时会出现什么情况 阅读全文 >>

再论机械式针对接口编程

两月前论及到 针对接口编程及敏捷编程,如今再一次老调重提。面向对象有一个很重要设计原则:

针对接口编程,而不是针对实现编程

这一原则关键在于理解什么是接口,请参照前文提到的上一篇。因为 Java 语言中有 interface 这个概念,于是 Java 的 interface 便躺枪了,好好的针对接口编程在 Java 中就机械式的变成了针对 interface 的编程。

以致于只要有实现的地方都可能变成像 UserDao.java(接口) 与 UserDaoImpl.java 成对出现,在以后的重构中基本上是为了实现而修改接口,这也进一步违背了针对接口编程的初衷,接口怎么一点也不稳固。

或许有人认为先声明一个 interface,然后我们可能会有多种实现(为将来考虑,不过多数时候只会是单一实现),这很像是 interface 存在的一个理由。可是当我们写下类似 UserDaoImpl 这个样的实现类名时直接宣告了该接口其实就只有一种实现,不然的话再加一个实现的话类名为哪般?UserDaoImpl2? 显然多种实现时 UserDaoImpl 需要重命名了。 阅读全文 >>