Mac OS X 下安装使用 Docker (新)

两年前的一篇 Mac OS X 下安装使用 Docker 安装时还是用的 boot2docker, 如今进化到了在 Mac OS X 下用 Docker Toolbox, 而且命令也由 boot2docker 换成了 docker-machine. 当然由于是非 Linux 系统, 所以 Mac OS X 仍然需要借助于 VirtualBox 中的 Linux 虚拟机作为桥梁, Docker Toolbox 创建的虚拟机名是 default (boot2docker 创建的虚拟机名是 boot2docker-vm) 就是这一桥梁, 我们称之为 DOCKER_HOST. 文中的 default 虚拟机指的就是这个 DOCKER_HOST.

现在来看下安装步骤及体验, 因为我系统中已安装好了 VirtualBox, 所以这一步骤等会就省略了.

一. 安装 docker 和  docker-machine

官方的指南是通过下载  DockerToolbox 来安装 docker, docker-machine 和其他辅助工具. 但如果你偏于极客, 并不习惯于图形界面来安装的话, 那么安装 docker 和 docker-machine 就只要下面两个指令

如果你已选择了使用上面的两条命令来安装 docker, docker-machine 的话, 那么请跳到下一步.

如果你热爱图形界面来安装应用的话, 可以到页面 https://www.docker.com/products/docker-toolbox 中下载 Mac 的版本的 DockerToolbox-1.10.3.pkg, 当前版本是 1.10.3

Docker Toolbox Installer   Docker Toolbox includings 阅读全文 >>

拆分 Playframework 2 的 routes 为多个文件

我们用 Playframework 2 时,当 routes 中太多的路由配置时,我们可能会考虑把它们归类分布到多个文件中去。比如按 API 或用途分,有些是 RESTful API,有些是 Web 页面的,对于这种情景,我们可以由以下几个文件来组织:

1. routes 文件,这个仍然是充当入口

这里穿插着来解释下,-> 是固定写法,表示要去别外寻找了,紧接着的 /, /api, 和 /web 是分类路由的上下文了,例如,访问 api.routes 中定义的 /customers 的完整 API 路径就是 /test/customers。最后一部分是全类名,并非指别的路由文件的名称,像文件 general.routes 编译后会生成 general 包下生成  Routes.scala 文件,即类为 general.Routes. 阅读全文 >>

如何轻松理解排序函数

这是我自最早接触 Javascript 的排序之日起一直萦绕在脑海中的问题。比如一个简单的 Javascript 数组排序

[3, 2, 4, 6, 5].sort(function(e1, e2) {
    return e1 - e2;
});

当然我那时候还不会用 JSON 和无名函数来写上面的代码。 反正经常是搞不清楚想要升序或降序时,是应该 return e1 - e2 还是 return e2 - e1 ?,没把握就试一下,总是二选一。

现在许多语言对集合排序都是使用排序函数的方式,总不想每次都琢磨不定,每次都去试。需要 TDD 时测试用例必须覆盖排序用例,但还是希望以后书写排序函数时心里有个底。当然,比如在 Java 中对集合进行排序我们可以深入到源代码来理解排序函数是如何影响元素顺序的。

简单通俗点,是否有一种浅显的方式来理解排序函数呢?待我把答案慢慢叙来,就我个人理解就两个原则: 阅读全文 >>

Java 的 fork-join 框架实例备忘

Java 7 首次引入了 fork/join 框架,但一直未曾直接尝试. 而且基本上也很少在实际项目中直接写 fork-join 的代码,在我们使用第三方组件时倒是间接会接触到 fork/join 框架。譬如 Akka 的 fork-join-executor, sbt 执行测试用例时也是默认 fork/join 并发执行。fork-join 可以帮助我们把计算任务粒度细化,并更有效的利用多 CPU 内核。

fork-join 与 map-reduce 有些相妨,在 Java 7 时代我其实是忽视了它的存在。目今正在了解 Java 8 的  parallelStream 时,因为它的底层实现也是 fork/join, 所以有兴致去稍加体验一下。fork/join 的算法简单来讲就是递归对半去细化计算任务,及到不能细化时由多内核(线程)去计算被拆分的任务,最后反方向把结果汇总。

下面是从 《Java 8 IN ACTION》中截的一个说明 fork/join 的处理过程 阅读全文 >>

Java 8 的 groupingBy 产生空的 Map 分组

前面一篇 Java 8 的 groupingBy 能否产生空的 Map 分组 是提出来的思考,本篇就是上一篇的答案。

由于在 Java 8 中用 Collectors.groupingBy 对 List 进行分组时每个组里都必须存在元素,也就是

Stream<Person> stream = Stream.of(new Person("Tom", "male"), new Person("Jerry", "male"));
System.out.println(stream.collect(Collectors.groupingBy(person -> person.gender)));

只能得到结果

{male=[Tom, Jerry]}

而无法表示存在其他 gender 的可能性,并且 female=[] 的情况,即想要结果

{male=[Tom, Jerry], female=[]}

如果想得到以上的结果该当如何呢? stream.collect() 接受一个 Collector, Collectors 中只是定义了许多常用的 Collector 实现,如果不够用的话我们可以实现自己的 Collector. 下面就来定义一个 GroupingWithKeys, 它需要实现 java.util.stream.Collector 接口,有五个接口方法. 事成之后我们写 阅读全文 >>

Java 8 的 groupingBy 能否产生空的 Map 分组

我们在 Java 8 之前用 for-loop 对 List 进行分组的时候,可能会要求产生空的分组。例如对 List<Person> 按性能进行分组,即使给定的 List<Person> 中全是 male, 我们也想得到 Map 包含两个 Key

{male=[person1, person2], female=[]}

而不只是

{male=[person1, person2]}

这两种表示略有区别,第一种方式暗示着有另一种可能性。看看 for-loop 如何对 List<Person> 进行分组。

类 Person 代码 阅读全文 >>

Java 8 返回集合中第一个匹配的元素

在 Java 8 之前如果我们要找到集合中第一个匹配元素,要使用外部循环,如下面方法 findFirstMatch() 如果找到一个大于 3 的数字立即返回它,否则返回 null

  public Integer findFirstMatch() {
    List<Integer> integers = Arrays.asList(1, 4, 2, 5, 6, 3);
    for(int i: integers) {
      if(i > 3) return i;
    }
    return null;
  }

因为在 for 循环中找到第一个大于 3 的数字是 4, 并且立即返回,所以不管集合 integers 再大,也不会遍历整个集合。

注:不要纠结于上面示例方法的实际用途,实际上集体和匹配条件都该通过参数传入方法的,这里只作演示循环。

那么我们来到 Java 8 之后用 Stream API 该如何实现,翻遍了 Stream API, 能过滤元素的操作也就是 filter 方法,于是尝试这样的写法 阅读全文 >>

Java 8 Lambda 捕获外部变量

可能会把捕获外部变量的 Lambda 表达式称为闭包,那么 Java 8 的 Lambda 可以捕获什么变量呢?

  1. 捕获实例或静态变量是没有限制的(可认为是通过 final 类型的局部变量 this 来引用前两者)
  2. 捕获的局部变量必须显式的声明为 final 或实际效果的的 final 类型

回顾一下我们在 Java 8 之前,匿名类中如果要访问局部变量的话,那个局部变量必须显式的声明为  final,例如下面的代码放在 Java 7 中是编译不过的

java7-annymous-capture-local-variable

Java 7 要求 version 这个局部变量必须是 final  类型的,否则在匿名类中不可引用。 阅读全文 >>

Java 8 Stream 如何还原为集合

由于 Java 对集合的函数式操作并非原生态的,必须对得到的 stream() 进行过滤等操作,之后还是一个 stream(),一般我们最后返回给调用者需还原为相应的集合。这无法与 Scala 的 for ... yield 操作相比。例如下面在使用 Stream API 过滤获得所有大于 3 的数字之后,方法的返回值还应该还原为 List<Integer>, 这个需求非常自然

我们这儿的问题就是如何把上面的 streamOfInteger 转换为 List<Integer>, 有以下几种办法 阅读全文 >>

Java 8 Lambda 写法与简化

Java 8 的 Lambda 表达式的实现方式还是基于已有的字节码指令,由 Lambda 表达式的方法签名结合上下文并经由 SAM 推断出正确的类型来。Java 8 的 Lambda 完整书写格式是

(type parameter1 [type parameter2, ...type parametern]) -> { statements here }

这种标准格式所表示的就是方法签名。

虽不及其他语言的 Lambda 表达式,像 Swift, Scala 可省略参数部分,可用默认参数名 $0, $1, 或 _, 但 Java 8 的 Lambda 还是可以进行酌情简化

  1. 参数类型总是可省略   --     (x, y) -> { x + y; }
  2. 参数为一个时,参数括号可省略  --    x -> { System.out.println(x); }
  3. 语句为一条时,可省略大括号, 并且语句后不要分号 --  x -> System.out.println(x)
  4. 上面更进一步,如果是单条 return 语句,必须把 return 关键字去掉  --  x -> "Hello " + x
  5. 就差一点,参数部分总是不能省,无参必须写成 () -> System.out.println("hi")
  6. Java 8 中若要近似的实现无参数部分写法,那就是方法引用了 -- System.out::println

阅读全文 >>