Backbone.js 使用 Collection

在前面我们的 Backbone.js 用上了 Model, 但绝大数的情况下我们处理的都是一批的 Model 数据列表,所以需要有一个 Collection 来容纳 Model, 就像 Java 里最常用的 List。

声明 Collection 时需要指定他处理的 Model 类型,也就是个泛型参数,如我们这样定义 Collection:

然后就是往 Collection 中如何填充 Model 实例,有好多种,这里只演示最直接的方式。还 add, fetch, 及对 Collection 排序, 遍历等各种操作。 阅读全文 >>

SpringBoot 中自定义 Filter 支持 gzip 压缩的请求体

SpringBoot Web 项目要支持响应数据的自动压缩只需要在 application.properties 中配置 server.compression.enabled=true 即可,默认为 false. 这样对于默认 server.compression.min-response-size=2KB 达到 2KB 大小的响应,并且请求头中有 Accept-Encoding: gzipdeflate 就会压缩响应数据。

相关的配置请参考:SpringBoot Server Properties

  1. server.compression.enabled: 默认为  false
  2. server.compression.excluded-user-agents: 针对哪些 user-agents(逗号分隔) 不压缩,默认为空
  3. server.compression.mime-types: 会对哪些响应的 content-type 进行压缩,默认为 text/html, text/xml, text/plain, text/css, text/javascript, application/javascript, application/json, application/xml
  4. server.compression.min-response-size: 默认为 2KB

文本数据压缩比可达到百分之七八十,对于节约网络消耗来说是非常可关的,不过要些许 CPU 资源。说完了响应的自动压缩,如果请求数据较大也应考虑对请求进行压缩。比如客户端发送请求时带上 Content-Encoding:gzip, 并且请求内容是 gzip 压缩的。

提示:如果 SpringBoot Web 是放在 AWS API Gateway 后端,那么 AWS API Gateway 会在看到请求头 Content-Encoding 的值为 gzip, compress, deflate, 或 br, 会自动解压缩请求数据,然后转发解压缩后的数据到后端,这时候 SpringBoot Web 无需进行请求数据的解压处理。不过对响应数据的压缩是 SpringBoot Web 要做的,AWS API Gateway 并无该功能。

SpringBoot 默认不支持自动解压缩请求内容,如果手动在 Controller 方法中,可以接收字节数组,然后自行解压缩。比如定义如下的 Controller 方法 阅读全文 >>

Apache Airflow 任务中使用模板或上下文

本文大概记录一下在 Apache Airflow 的 Task 或 Operator(这两个基本是同一概念) 中如何使用 模板(Template) 和上下文(Context). Airflow 的模板引擎用的  Jinja Template, 它也被 FastAPI 和 Flask 所采纳。首先只有构造 Operator 时的参数或参数指定的文件内容中,或者调用 Operator render_template() 方法才能用模板语法,像 {{ ds }}.  Apache Airflow 有哪些模板变量可用请参考: Templates references / Variables, 本文将会打印出一个 Task 的 context 变量列出所有可用的上下文变量, 不断的深入,最后在源代码找到相关的定义。

通过使用模板或上下文,我们能能够在任务中使用到 Apache Airflow 一些内置的变量值,如 DAG 或任务当前运行时的状态等 。

当我们手动触发一个 DAG 时, 在 Configuration JSON 中输入的参数也能在 context 中找到. 所有的 Operator  继承链可追溯到 AbstractOperator -> Templater, 因此所有的 Operator(Task) 都能通过调用 Templater.render_template() 方法对模板进行渲染,该方法的原型是 阅读全文 >>

Java ArrayList 默认容量及增长策略

早先对 Java ArrayList 的扩容理解是在 new ArrayList() 时会默认建立一个内部容量为 16(这个数值还是错的,往后看) 大小的数组,然而插入数据容量不足时会扩容为原来的 1.5 倍,并用 System.arraycopy()  移动原来的数组到新的大数组中,所以为了频繁的内部扩容操作,在已知 ArrayList 将来大小的情况下,应该在创建 ArrayList 时指定大小,如 new ArrayList(1000)。那么是否指定初始容量对性能会有多大的影响仍缺乏感性的认识。

本文通过具体的测试主要掌握以下知识

  1. new ArrayList() 默认容量大小(JDK 8 以前是 10, JDK 8 及以后为 0)
  2. ArrayList 何时进行扩容,以及每次扩容多少
  3. new ArrayList() 时是否指定初始容量值的性能对比
  4. 除了 ArrayList 自动扩容外,它会不会自动缩容呢?

new ArrayList() 的默认容量多少及增容策略

就像 JDK 8 的 HashMap 引入了红黑树改善性,随着 JDK 版本的升级 ArrayList 的内部实现也在演进。回到 JDK 7, 当我们不指定容量 new ArrayList() 创建一个对象时的实现是 阅读全文 >>

Dubbo 最基础的 RPC 应用(使用 ZooKeeper)

看国内的一些项目时 Dubbo 这个词经常闪现,一直也不以为然,未作搜索,当然也不知道它是做什么用的。直到最近阅读关于大型网站架构相关的书中反复提到 Dubbo 后,觉得不能再对它视而不见。Google 了一下,它是在阿里巴巴创建贡献给了 Apache 的开源项目,在阿里巴巴的大型应用中久经考验过的。Dubbo 是什么呢?借用官方 Dubbo 介绍

Apache Dubbo 是一款 RPC 服务开发框架,用于解决微服务架构下的服务治理与通信问题,官方提供了 Java、Golang 等多语言 SDK 实现。使用 Dubbo 开发的微服务原生具备相互之间的远程地址发现与通信能力, 利用 Dubbo 提供的丰富服务治理特性,可以实现诸如服务发现、负载均衡、流量调度等服务治理诉求。Dubbo 被设计为高度可扩展,用户可以方便的实现流量拦截、选址的各种定制逻辑。

Dubbo 是国内企业贡献的,所以官方有原生的中文文档,它某些时候与 Spring Cloud 齐名,又有些像 AWS 的 ECS Service Discovery, Service Connect 加上 ELB 的功能。 阅读全文 >>

Java 使用堆外内存(off-heap memory) 作为缓存

我们通常用的 Java 缓存基本可认为是扩展了 HashMap 或 ConcurrentHashMap 的实现,它们各自实现自己的缓存策略,如时间与空间的控制,生命周期管理,是否支持分布式,溢出时能否转储到磁盘。关于 Java 本地缓存的存储分为内存与磁盘,内存多数情况下指的是堆内内存(on-heap), 而介于堆内内存与文件存储之间的就是堆外内存(off-heap)

  1. 堆内存储(on-heap): 操作最快,无需序列化,但大量数据时会影响到 GC 的效率
  2. 堆外存储(off-heap): 存储在 Java 进程内存但非 JVM 堆内(不在 -Xmx 指定的内存范围内),使用或保存时需进行序列化/反序列化过程(在堆内与堆外转换),但不受 GC 影响,有助于提它来 GC 的效率
  3. 文件存储:不仅存在序列化与反序列化过程,还带 IO 操作,所以最慢,唯一优点就是大

我们查看一下当前 Spring 支持的缓存实现, Supported Cache Providers, 列有 Generic, JCache(JSR-107), EhCache 2.x, Hazelcast, Infinispan, Couchbase, Redis, Caffeine, Simple, 这其中无一支持堆外缓存,其中的 EhCache 要付费使用 EhCache 3(Big Memory) 才能支持 off-heap。 阅读全文 >>

Rust 语言学习笔记(五)

终于来到了 Rust 的精髓所在了,那就是使之不依赖于垃圾回收又能保障内存安全且高效运行的所有权系统(Ownership System)。想要用 Rust 做一个稍显规模项目必定绕不过它,所有权系统包括所有权(Ownership), 借用(Borrowing), 生命周期(Lifetimes)。

以下概念的复述基本是从 《Rust编程: 入门, 实战与进阶》一书中而来,那里面有些内容是来自于官方的 The Rust Programming Languge - Understanding Ownership

所有权系统的基本概念

Rust 的编程语法很快就能上手,让学习 Rust 曲线陡然大增的也就是这个所有权系统。所有权检测在编译期完成,Rust 能编译出来的代码就是安全高效的。要理解 Rust 的所有权系统必须首先明白以下两组概念:

  1. 栈内存(Stack),值语义(Value Semantic),按位复制(浅复制)(Shallow Copy),复制语义(Copy Semantic)
  2. 堆内存(Heap), 引用语义(Reference Semantic), 深复制(Deep Copy),移动语义(Move Semantic), 借用(Borrowing)

和其他语言一样,大小固定的所有基本类型都可以存储在栈上,栈上存取数据总是在栈顶操作,很快,而访问堆内存需要搜索内存地址。所有权系统的主要任务是用来跟踪堆上的数据,即引用语义的数据。 阅读全文 >>

Rust 语言学习笔记(三)

引用与解除引用

觉得还是有必要继续深入学习一下 Rust 再练手,毕竟仍然看到 & 和 * 符号还有些恍惚,大概就是 C/C++ 里的取地址和取值操作吧,实际上也确实类似。只是叫法略有不同, 还有就是在 C/C++ 多用了指针的概念。

在 C/C++ 中, &:称作 Address-of Operator, 在 Rust 中称作 Reference Operator, 而 * 在 C/C++ 和 Rust 中都叫做 Dereference Operator。以前学 C/C++ 经常被一系列的 &, * 打晕了头,如今参考了它们的英文名称立刻变得清晰了起来。

就像当初看汇编各种寻址方式弄得头都大了,其实也就是依照约定。 阅读全文 >>

Rust 语言学习笔记(二)

再继续快速学习一下 Rust 的以下几个知识点,就可以开始着手做点小工具了

  1. 基本数据类型
  2. 复合数据类型
  3. 基本的流程控制

Rust 设计为有效使用内存考虑的,它提供了非常细力度的数据类型,如整数分为有无符号,宽度从 8 位到 128 位,分别表示为 i8, u8, u128 等。浮点数有 f32 和 f64,以及 bool 和 range 类型。

元组

元组和 Python 的元组用法类似,Immutable, 可混合类型

数组

数组类型中的元素类型相同,表示为  [T; n], T 为类型,n 为元素个数 阅读全文 >>

Java StackOverflowError 与递归及尾递归优化

本篇同样是阅读《100 Java Mistakes and How to Avoid Them》的一则笔记,只是火力全集中在 StackOverflowError 这个单一主题之上, 且主要与递归及尾递归优化相关。一提到 StackOverflow, 恐怕首先是想到那个代码搬运源网站 stackoverflow.com,其次才是代码执行过程中的 StackOverflowError 错误。

什么是 StackOverflow, 准确来说是指线程的栈内存不足,无法在栈中分配新的内存(或创建新的栈帧)。我们通常会把它与方法调用关联起来,因为一次方法调用会创建一个新的栈帧,分配在栈上的局部变量(包括基本类型与对象引用),和栈帧都要占用线程栈内存。而我们平时所说的方法调用栈,或出现异常时打印出的异常栈都是一个概念。

StackOverflowError 一般出现在方法调用太深(方法调方法),手动编写的方法调用一般不容易达到这个限制,所以它与递归调用关系最为密切,递归调用次数太多或甚至没有出口条件无限递归; 也可能是经过一番递归调用后,再正常调用后续方法时出现 StackOverflowError(因为前面的递归调用资源消耗的差不多)

何谓递归调用,简单的说就是方法自己调用自己,从而实现循环的效果,或使代码更精练,例如经典排序中的快速和归并排序就要用到递归。但循环与递归又有本质上的区别,循环不增加调用深度;递归却不同,递归分递进与回归两个过程,递进调用时每次调用前都需通过压栈来保留现场,逆向回归时再逐级恢复现场,保留现场的过程就要从线程栈中分配内存空间; 死循环可能不会造成程序异常,但死(无限)递归必定出现 StackOverflowError。 阅读全文 >>