作者归档:hongjiang

Sierra三宗罪

1) Sierra下很多原有软件无法运行

有些通过mac store下载的需要重新下载才可运行:

搜狗拼音无法使用,需要下载最新版本重新安装。

2) 有个photoanalysisd的进程在后台分析你的图片,导致cpu占用率很高,可以用下面方式停止:

 ➜  cat stop-photoanalysisd.sh
#!/bin/bash

launchctl stop com.apple.photoanalysisd
launchctl unload -w /System/Library/LaunchAgents/com.apple.photoanalysisd.plist

3) 原先VPN配置里的PPTP模式都丢失了

《Scala函数式编程》中文版勘误

感谢林晴、史珩、shahito、乐乐Joker 以及其他匿名用户的反馈,勘误表如下(另外,原书作者另写了一本伴生书,对文章的错别字以及个别代码样例错误做了纠正,伴生书pdf可从作者网站免费下载:http://blog.higher-order.com/assets/fpiscompanion.pdf 为了保持一致,伴生书中修订的内容在中文版中并没有修改。伴生书中指出的错误并不多,也不影响本书阅读,主要是为书中的练习做解释)

页数:目录
 原文:1.3 引用透明、纯粹度以及替代模型
 修改:1.3 引用透明、纯粹性以及代换模型

页数:目录 XI
 原文:4 不是用异常来处理错误
 修改:4 不使用异常来处理错误

页数:目录 XII
 原文:第二部分  功能设计和组合子库
 修改:第二部分  函数式设计和组合子库

 原文:9.1 代数设计,走起
 修改:9.1 代数设计

页数:目录 XIII
 原文:第三部分  函数设计的通用结构
 修改:第三部分  函数式设计的通用结构

页数:目录 XV
 原文:14 本地影响和可变状态
 修改:14 局部作用和可变状态

 原文:14.3 纯粹是相对于上下文的
 修改:14.3 纯粹性是相对于上下文的

页数:原推荐序
 第一段中
 原文:函数式编程作为书题出现在Scala中是个有趣的现象。
 修改:《Scala函数式编程》是个有趣的书名。

 第二段中
 原文:它同时承认非纯粹函数和纯函数
 修改:它同时允许非纯粹函数和纯函数

 第五段中
 原文:从第一个原理扩展到
 修改:从首要原则扩展到

页数:第一部分 函数式编程介绍
 第一段中
 原文:我们以一个激进的前提开始读这本书
 修改:我们以一种激进的前提开始这本书

 原文:比如读取文件或修改内存时。
 修改:比如读取文件或修改内存。

 第二段中
 原文:并给你一些有益的理念。
 修改:让你对函数式编程的好处有些概念。

页数:8
 原文:引用透明与纯粹度
 修改:引用透明与纯粹性

 原文:1.3 引用透明、纯粹度以及替代模型
 修改:1.3 引用透明、纯粹性以及代换模型

 // 统一翻译为“代换模型”
 原文:我们称之为代替模型( substitution model )。
 修改:我们称之为代换模型( substitution model )。

 原文:其中一个例子的所有表达式都是引用透明的,可用替代模型来推导,
 修改:其中一个例子的所有表达式都是引用透明的,可用代换模型来推导,

 原文:让我们尝试在Scala解析器(也称作REPL,
 修改:让我们尝试在Scala解释器(也称作REPL,

页数:10
 原文:与之相反的是,替换模型则很容易推理
 修改:与之相反的是,代换模型则很容易推理

 原文:即使没有用过“替代模型”这一名词,
 修改:即使没有用过“代换模型”这一名词,

 原文:还对引用透明和代替模型进行了讨论,
 修改:还对引用透明和代换模型进行了讨论,

页数:22
 // andThan -> andThen
 原文:同时还提供了一个 andThan 方法, f andThan g 等价于 g compose f
 修改:同时还提供了一个 andThen 方法, f andThen g 等价于 g compose f

页数:23
 原文:对于像这样小的一行层序,还不算困难
 修改:对于像这样的一行小程序,还不算困难

页数:40
 原文:不是用异常来处理错误
 修改:不使用异常来处理错误

页数:41
 原文:让替代模型的简单推导无法适用
 修改:让代换模型的简单推导无法适用

页数:54
  原文(最下面的注释): 我们现在使用Scala标注库
  修改:我们现在使用Scala标准库

页数:57
 原文:如果表达式 f(x) 对所有的evaluates to bottom的表达是x,也是evaluates to bottom,那么f是严格求值的。
 修改:如果表达式f(x)对于所有evaluates to bottom的表达式x同样是evaluates to bottom的,那么f是严格求值的。

页数:67
 原文:目的是模拟投6面色子死亡法
 修改:目的是模拟投6面体骰子

页数:73
 原文:这是向右移的方式,
 修改:这是朝着正确方向前进,

 原文:用纯函数式API实现一个更加可测的死亡色子?
 修改:用纯函数式API实现一个更加可测的投骰子?

 原文:这里是一个死亡色子 rollDie 的实现,
 修改:这里是一个投骰子 rollDie 的实现,

页数:77
 原文:第二部分 功能设计和组合子库
 修改:第二部分 函数式设计和组合子库

页数:81
 原文:两边的表达式是无法实现平行执行的。
 修改:两边的表达式是无法实现并行执行的。

 注释2
 原文:并在衍生下一个并行计算之前等待前一个并行计算完成,这样的计算比较高效同时也保证串行执行。
 修改:并在衍生下一个并行计算之前等待前一个并行计算完成,这样的计算实际是串行的。

页数:90
 原文:(法则常常是从标识的具体例子中得出来的)
 修改:(法则常常是从恒等式的具体例子中得出来的)

 注释8
 原文:这里的标识我们想表达的是,在数学上指两个表达式相同或等价
 修改:这里的恒等式表达的是,在数学上指两个表达式相同或等价

页数:91
 //将 fascinating 翻译为了“醉了吧” 与整本书的语言风格不一致,删除。
 原文:醉了吧!从最后的法则可以看出对于map而言unit明显是个多余的细节。
 修改:从最后的法则可以看出对于map而言unit明显是个多余的细节。

 原文:由于我们得到的这个第二法则或定理是自由的,仅仅是因为map的参数态(parametricity),它有时也被称为自由定理(free theorem)。
 修改:由于我们得到的这个第二法则或定理是免费的,仅仅是因为map的参数态(parametricity),它有时也被称为免费定理(free theorem)。

 注释10
 原文:这和我们在代数方程式里做的简化过程一样。
 修改:这和我们在代数方程式里做的代换和简化过程一样。

 注释12 // 译注:在优化界有一个著名的定理叫“没有免费午餐定理”,这个论文的题目可能是想与之呼应
 原文:”自由定理”的观点来自于 Philip Wadler 的经典论文《Theorem for Free》(http://mng.bz/Z9f1)
 修改:”免费定理”的观点来自于 Philip Wadler 的经典论文《Theorem for Free》(http://mng.bz/Z9f1)

页数:103
 原文:某些情况下,Gen[A] 的空间(domain)足够小
 修改:某些情况下,Gen[A] 的定义域(domain)足够小

 注释2
 原文:这里的“空间”(domian)与函数空间一样
 修改:这里的“定义域”(domian)与函数定义域一样

页数:108
 // Refining 在编译器里更多翻译为”具化"
 原文:8.2.5 精炼 Prop 的数据类型
 修改:8.2.5 具化 Prop 的数据类型

页数:117
 注释13
 原文:回忆一下在第7章中我们曾介绍过的自由定理,
 修改:回忆一下在第7章中我们曾介绍过的免费定理,

页数:117
 // 风格不符
 原文:9.1 代数设计,走起
 修改:9.1 代数设计

页数:153
 注释3
 原文:因为借用了已经存在的数据抽象名称
 修改:因为借用了已经存在的数学抽象名称

页数:164
 // primitive翻译为原语,保持一致风格
 原文:State中(除了unit和flatMap)其他原始的操作
 修改:State中(除了unit和flatMap)其他原语操作

 原文:它们和monadic原始操作(unit和flatMap) 一起构成了State数据类型的所有操作。monad一般都是这样的,它们都包括unit和flatMap,并且每个monad又有自己额外的原始操作。
 修改:它们和monadic原语操作(unit和flatMap) 一起构成了State数据类型的所有操作。monad一般都是这样的,它们都包括unit和flatMap,并且每个monad又有自己额外的原语操作。

页数:172
 原文:Applicative构建了上下文自由的计算,
 修改:Applicative构建了上下文无关的计算,

页数:210
 原文:本地影响和可变状态
 修改:局部作用和可变状态

页数:212
 注释1
 原文:也无须每次在充分利用本地变更(local mutation)时使用。
 修改:也无须每次在充分利用局部变更(local mutation)时使用。

页数:214
 // primitive 统一翻译为原语
 原文:这里依旧采用组合子库加一些基元函数(primitive)的形式,其中关于可变内存单元的应有的基元函数有:
 修改:这里依旧采用组合子库加一些原语函数(primitive)的形式,其中关于可变内存单元的应有的原语函数有:

页数:217
 // primitive 统一翻译为原语
 原文:为此,我们需要先实现可变数组的基元组合子:
 修改:为此,我们需要先实现可变数组的原语组合子:

页数:218
 // 基元->原语,负责->复杂
 原文:有了这些基元函数,我们便可以实现更负责的数组函数了。
 修改:有了这些原语函数,我们便可以实现更复杂的数组函数了。

 原文:我们不如把这变成一个基元函数:
 修改:我们不如把这变成一个原语函数:

页数:219
 练习14.3
 原文:为 scala.collection.mutable.HashMap 实现一组最小的基元组合子。
 修改:为 scala.collection.mutable.HashMap 实现一组最小的原语组合子。

页数:240
 原文:链状混合(Zipping)是Tee特有的一种情况,
 修改:拉链式操作(Zipping)是Tee特有的一种情况,

页数:242
 原文:请用存在基元函数实现join,
 修改:请用已存在的原语函数实现join,

优雅关闭与session draining

最近看到nginx plus收费版里有个session draining的概念,在用nginx做反向代理,要优雅停止后端应用的话,需要先在nginx层控制请求不再发到相应的节点,但与此同时已经建立的链接则要继续保持,一直等这些链接都结束了再停止应用。

其实在免费版的nginx/tengine就能实现这个特性,在以前的这篇nginx反向代理对后端某个节点优雅下线的问题 文章里测试过后端tomcat有一个耗时很长的请求,当tengine端健康监测已发现后端节点不可用的情况下,该请求并不会被tengine中止,而会等后端响应结束后再返回给客户端。

优化关闭应用并不只是在负载均衡或反向代理层面就能完美解决的,后端的应用本身也要考虑很多细节。举一个例子,Actor怎么有序的关闭,可以参考之前分享的Governor模式,所有干活的Actor由Governor创建,在创建时就设定好优先级。

在应用关闭的时候,对master发送PoisonPill,示例代码如下:

// 在你的容器或微容器里监听到应用关闭事件时触发shutdown,最简单的情况下是在shutdownhook里
def shutdown(): Unit = {
    gracefulStopActors
    system.terminate
    Await.result(system.whenTerminated, Duration.Inf)
    springContext.close
}

private def gracefulStopActors {
    master ! PoisonPill
    signal.acquire //wait
}

在governor角色里,当收到Terminated消息后顺序结束子actor

...
case Terminated(actor) if actor == observable => {
  logger.info("===receive terminated message from: " + actor)
  // 先顺序的杀死所有托管的子actor,并将结果发给自己
  stopAll(managedActors.toList.sorted) pipeTo self
}
...

// 顺序的停止所有子actor
protected def stopAll(kids: List[OrderedActorRef]): Future[Any] = {
    kids match {
      case first :: Nil =>
        logger.info("===graceful stop: " + first.actor)
        gracefulStop(first.actor, stopTimeout).flatMap { _ =>
          Future { AllDead }
      }
      case first :: rest =>
        logger.info("===graceful stop: " + first.actor)
        gracefulStop(first.actor, stopTimeout).flatMap { _ =>
        stopAll(rest)
      }
      case Nil =>
        Future { AllDead }
    }
}

再举一个例子,应用里有一个线程一直轮询从redis里获取数据,你在关闭应用的时候如何优雅终止这个线程呢?下面用一段代码简单模拟这个场景;假设这个应用没有任何容器,结束就是靠收到kill信号,那么在ShutdownHook里,要阻塞的等待(一段时间)worker线程结束后再退出才算安全

public class Test {
  static volatile boolean running = true;

  public static void main(String[] args) {
  Thread worker = new Thread() {
      public void run() {
          while (running) {
              try {
                  System.out.println("i'm running.1");
                  Thread.sleep(1000);
                  System.out.println("i'm running.2");
                  Thread.sleep(1000);
                  System.out.println("i'm running.3");
                  Thread.sleep(1000);
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
          }
      }
  };
  worker.start();
  Runtime.getRuntime().addShutdownHook(new Thread() {
      public void run() {
          System.out.println("receive shutdown signal.");
          running = false;

          while(worker.isAlive()) {
              System.out.println("waiting for worker thread finish.");
              try {
                  Thread.sleep(1000);
              } catch (InterruptedException e) {
              }
          }
          System.out.println("wroker thread finished!");
      }
  });
 }
}

shell下精确的定位一个命令

之前在这篇SIGTTIN? 里提出了问题,但没有交代背景,为什么用交互式shell执行”which mvn”命令,是为了更准确的获取用户当前环境里所用的mvn命令到底是哪个。若用户对mvn做过alias,优先使用alias过的指令,然后再选择$PATH路径下的命令;这个函数的完整内容如下:

function get_mvn_cmd() {
  if [[ "$OSTYPE" == *cygwin* ]];then
    ppid=$( ps -ef -p $$ | awk 'NR==2{print $3}' )
    user_shell=$( ps -p $ppid | awk 'NR==2{print $8}' )
    #has some trouble with cygwin, while Ctrl-c cannot terminal
    set -m
  else
    ppid=$( ps -oppid= $$ )
    user_shell=$( ps -ocomm= -p $ppid )
  fi

  # while as login shell, it's -bash not bash
  if [[ "$user_shell" == "-"* ]];then
    user_shell=${user_shell:1}
  fi

  mvn=$( $user_shell -ic "alias mvn" 2>/dev/null | cut -d'=' -f2 | sed "s/'//g" )

  if [ -z "$mvn" ];then
    $user_shell -ic "which mvn" >/dev/null
    if [ $? -eq 0 ];then
      mvn=$( $user_shell -ic "which mvn" | head -1 )
    fi
  fi

  if [ -z "$mvn" ]; then
    echo "mvn command not found" >&2
    kill -s TERM $TOP_PID
  else
    echo $mvn
  fi
}

函数里先获取用户所使用的shell(即当前脚本的父进程),然后以交互式执行alias mvn看看是否又被用户别名过,没有的话再使用which mvn获取mvn命令。

为什么写的那么啰嗦是想兼容好几种用户环境,如果是linux或者使用的gnu-which,可以利用gnu-which的一些参数一次性按优先级依次从”alias, functions, commands”里查找命令

$ (alias; declare -f) | gwhich --read-alias --read-functions mvn

上面利用gnu-which的--read-alias--read-functions参数优先从前边aliasdeclare -f输出的结果里查找,最后再从$PATH里查找。实际上centos7的bash下which命令就被alias过了:

alias which='alias | /usr/bin/which --tty-only --read-alias --show-dot --show-tilde'    

简单而耐用的物件

这款型号GW-5600J的G-Shock电子表是我六年前买的,从未换过电池。当时主要为了在跑步时便于计时,所以选择了这款功能性手表。在这只手表之前我也带过好几块其他类型的,时间大都不长,要么被撞坏表壳、要么机芯不够稳定(当然也都不是太好的表),这款表算是佩戴时间最长的一款。尽管外形比较丑陋,但很耐用,陪我跑了很多次马拉松,汗水的腐蚀性对它影响也不大,跑完步用水冲一下就好。

最近表带坏了,我在犹豫是否换成一款智能手表,但无法接受智能手表的电池仅能维持几天时间。相比之下我还是更愿意这种简单耐用的传统物件。

SIGTTIN?

我有一段脚本自己的mac机器默认用zsh,运行时一直很正常,今天给别的同事用,在bash下有些异常,追查了一下这个问题,把问题简化后如下:

#!/bin/bash
zsh -ic "which mvn"
zsh -ic "which mvn"

上面的脚本执行没有问题,但将里面的zsh换位bash,就会出现问题:

#!/bin/bash
bash -ic "which mvn"
bash -ic "which mvn"

在mac上执行,第一次bash -ic "which mvn"是成功的,但第二次执行时就会挂住:

 ➜  ./b.sh
/usr/local/bin/mvn
[1]  + 24649 suspended (tty input)  ./b.sh

/tmp/dd   [23:43:21]
[jobs:1] ➜    

后来想到which在zsh里是一个内置命令,而在bash下则是一个外部命令,可能有所差异,将zsh执行的命令也声明为外部命令:

#!/bin/bash
zsh -ic "/usr/bin/which mvn"
zsh -ic "/usr/bin/which mvn"

执行时会在第二次阻塞住,即使Ctrl-C也无法停止脚本。

这个问题很奇怪,两次以交互式调用shell执行一段命令(必须是外部命令)的话,第一次会成功,第二次则会suspend住。猜测可能是shell在第一次交互式执行结束后改变了上下文的什么状态,导致第二次再执行的时候挂住。在linux上用strace跟踪了一下脚本,看样子是因为SIGTTIN信号量所致,但其中缘由并不清楚,我把代码和strace的信息贴在这里,希望明白的人解释一下

$ cat b.sh
#!/bin/bash
bash -ic "ls"
bash -ic "ls"

$ strace ./b.sh

[hongjiang@localhost dd]$ strace ./b.sh
execve("./b.sh", ["./b.sh"], [/* 31 vars */]) = 0
brk(0)                                  = 0x1175000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fa545ddf000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=32094, ...}) = 0
mmap(NULL, 32094, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7fa545dd7000
close(3)                                = 0
open("/lib64/libtinfo.so.5", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0@\316\0\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=174520, ...}) = 0
mmap(NULL, 2268928, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fa545995000
mprotect(0x7fa5459ba000, 2097152, PROT_NONE) = 0
mmap(0x7fa545bba000, 20480, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x25000) = 0x7fa545bba000
close(3)                                = 0
open("/lib64/libdl.so.2", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\320\16\0\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=19512, ...}) = 0
mmap(NULL, 2109744, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fa545791000
mprotect(0x7fa545794000, 2093056, PROT_NONE) = 0
mmap(0x7fa545993000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x2000) = 0x7fa545993000
close(3)                                = 0
open("/lib64/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\0\34\2\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=2107760, ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fa545dd6000
mmap(NULL, 3932736, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fa5453d0000
mprotect(0x7fa545586000, 2097152, PROT_NONE) = 0
mmap(0x7fa545786000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1b6000) = 0x7fa545786000
mmap(0x7fa54578c000, 16960, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7fa54578c000
close(3)                                = 0
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fa545dd4000
arch_prctl(ARCH_SET_FS, 0x7fa545dd4740) = 0
mprotect(0x7fa545786000, 16384, PROT_READ) = 0
mprotect(0x7fa545993000, 4096, PROT_READ) = 0
mprotect(0x7fa545bba000, 16384, PROT_READ) = 0
mprotect(0x6dc000, 4096, PROT_READ)     = 0
mprotect(0x7fa545de0000, 4096, PROT_READ) = 0
munmap(0x7fa545dd7000, 32094)           = 0
rt_sigprocmask(SIG_BLOCK, NULL, [], 8)  = 0
open("/dev/tty", O_RDWR|O_NONBLOCK)     = 3
close(3)                                = 0
brk(0)                                  = 0x1175000
brk(0x1196000)                          = 0x1196000
brk(0)                                  = 0x1196000
open("/usr/lib/locale/locale-archive", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=106065056, ...}) = 0
mmap(NULL, 106065056, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7fa53eea9000
close(3)                                = 0
brk(0)                                  = 0x1196000
getuid()                                = 1000
getgid()                                = 1000
geteuid()                               = 1000
getegid()                               = 1000
rt_sigprocmask(SIG_BLOCK, NULL, [], 8)  = 0
open("/proc/meminfo", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0444, st_size=0, ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fa545dde000
read(3, "MemTotal:        1017160 kB\nMemF"..., 1024) = 1024
close(3)                                = 0
munmap(0x7fa545dde000, 4096)            = 0
rt_sigaction(SIGCHLD, {SIG_DFL, [], SA_RESTORER|SA_RESTART, 0x7fa545405650}, {SIG_DFL, [], 0}, 8) = 0
rt_sigaction(SIGCHLD, {SIG_DFL, [], SA_RESTORER|SA_RESTART, 0x7fa545405650}, {SIG_DFL, [], SA_RESTORER|SA_RESTART, 0x7fa545405650}, 8) = 0
rt_sigaction(SIGINT, {SIG_DFL, [], SA_RESTORER, 0x7fa545405650}, {SIG_DFL, [], 0}, 8) = 0
rt_sigaction(SIGINT, {SIG_DFL, [], SA_RESTORER, 0x7fa545405650}, {SIG_DFL, [], SA_RESTORER, 0x7fa545405650}, 8) = 0
rt_sigaction(SIGQUIT, {SIG_DFL, [], SA_RESTORER, 0x7fa545405650}, {SIG_DFL, [], 0}, 8) = 0
rt_sigaction(SIGQUIT, {SIG_DFL, [], SA_RESTORER, 0x7fa545405650}, {SIG_DFL, [], SA_RESTORER, 0x7fa545405650}, 8) = 0
rt_sigprocmask(SIG_BLOCK, NULL, [], 8)  = 0
rt_sigaction(SIGQUIT, {SIG_IGN, [], SA_RESTORER, 0x7fa545405650}, {SIG_DFL, [], SA_RESTORER, 0x7fa545405650}, 8) = 0
uname({sys="Linux", node="localhost.localdomain", ...}) = 0
stat("/tmp/dd", {st_mode=S_IFDIR|0775, st_size=4096, ...}) = 0
stat(".", {st_mode=S_IFDIR|0775, st_size=4096, ...}) = 0
getpid()                                = 2945
open("/usr/lib64/gconv/gconv-modules.cache", O_RDONLY) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=26254, ...}) = 0
mmap(NULL, 26254, PROT_READ, MAP_SHARED, 3, 0) = 0x7fa545dd8000
close(3)                                = 0
getppid()                               = 2942
getpgrp()                               = 2942
rt_sigaction(SIGCHLD, {0x441090, [], SA_RESTORER|SA_RESTART, 0x7fa545405650}, {SIG_DFL, [], SA_RESTORER|SA_RESTART, 0x7fa545405650}, 8) = 0
getrlimit(RLIMIT_NPROC, {rlim_cur=3909, rlim_max=3909}) = 0
rt_sigprocmask(SIG_BLOCK, NULL, [], 8)  = 0
open("./b.sh", O_RDONLY)                = 3
ioctl(3, SNDCTL_TMR_TIMEBASE or SNDRV_TIMER_IOCTL_NEXT_DEVICE or TCGETS, 0x7fff54edec40) = -1 ENOTTY (Inappropriate ioctl for device)
lseek(3, 0, SEEK_CUR)                   = 0
read(3, "#!/bin/sh\n\nbash -ic \"ls\"\nbash -i"..., 80) = 39
lseek(3, 0, SEEK_SET)                   = 0
getrlimit(RLIMIT_NOFILE, {rlim_cur=1024, rlim_max=4*1024}) = 0
fcntl(255, F_GETFD)                     = -1 EBADF (Bad file descriptor)
dup2(3, 255)                            = 255
close(3)                                = 0
fcntl(255, F_SETFD, FD_CLOEXEC)         = 0
fcntl(255, F_GETFL)                     = 0x8000 (flags O_RDONLY|O_LARGEFILE)
fstat(255, {st_mode=S_IFREG|0775, st_size=39, ...}) = 0
lseek(255, 0, SEEK_CUR)                 = 0
rt_sigprocmask(SIG_BLOCK, NULL, [], 8)  = 0
read(255, "#!/bin/sh\n\nbash -ic \"ls\"\nbash -i"..., 39) = 39
rt_sigprocmask(SIG_BLOCK, NULL, [], 8)  = 0
rt_sigprocmask(SIG_BLOCK, NULL, [], 8)  = 0
stat(".", {st_mode=S_IFDIR|0775, st_size=4096, ...}) = 0
stat("/home/hongjiang/.local/bin/bash", 0x7fff54ede900) = -1 ENOENT (No such file or directory)
stat("/home/hongjiang/bin/bash", 0x7fff54ede900) = -1 ENOENT (No such file or directory)
stat("/data/program/scala/bin/bash", 0x7fff54ede900) = -1 ENOENT (No such file or directory)
stat("/usr/lib64/qt-3.3/bin/bash", 0x7fff54ede900) = -1 ENOENT (No such file or directory)
stat("/usr/local/bin/bash", 0x7fff54ede900) = -1 ENOENT (No such file or directory)
stat("/usr/bin/bash", {st_mode=S_IFREG|0755, st_size=960384, ...}) = 0
stat("/usr/bin/bash", {st_mode=S_IFREG|0755, st_size=960384, ...}) = 0
geteuid()                               = 1000
getegid()                               = 1000
getuid()                                = 1000
getgid()                                = 1000
access("/usr/bin/bash", X_OK)           = 0
stat("/usr/bin/bash", {st_mode=S_IFREG|0755, st_size=960384, ...}) = 0
geteuid()                               = 1000
getegid()                               = 1000
getuid()                                = 1000
getgid()                                = 1000
access("/usr/bin/bash", R_OK)           = 0
stat("/usr/bin/bash", {st_mode=S_IFREG|0755, st_size=960384, ...}) = 0
stat("/usr/bin/bash", {st_mode=S_IFREG|0755, st_size=960384, ...}) = 0
geteuid()                               = 1000
getegid()                               = 1000
getuid()                                = 1000
getgid()                                = 1000
access("/usr/bin/bash", X_OK)           = 0
stat("/usr/bin/bash", {st_mode=S_IFREG|0755, st_size=960384, ...}) = 0
geteuid()                               = 1000
getegid()                               = 1000
getuid()                                = 1000
getgid()                                = 1000
access("/usr/bin/bash", R_OK)           = 0
rt_sigprocmask(SIG_BLOCK, [INT CHLD], [], 8) = 0
lseek(255, -14, SEEK_CUR)               = 25
clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7fa545dd4a10) = 2946
rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
rt_sigaction(SIGINT, {0x43e500, [], SA_RESTORER, 0x7fa545405650}, {SIG_DFL, [], SA_RESTORER, 0x7fa545405650}, 8) = 0
wait4(-1, b.sh
[{WIFEXITED(s) && WEXITSTATUS(s) == 0}], 0, NULL) = 2946
rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=2946, si_status=0, si_utime=0, si_stime=0} ---
wait4(-1, 0x7fff54ede450, WNOHANG, NULL) = -1 ECHILD (No child processes)
rt_sigreturn()                          = 0
rt_sigaction(SIGINT, {SIG_DFL, [], SA_RESTORER, 0x7fa545405650}, {0x43e500, [], SA_RESTORER, 0x7fa545405650}, 8) = 0
rt_sigprocmask(SIG_BLOCK, NULL, [], 8)  = 0
read(255, "bash -ic \"ls\"\n", 39)      = 14
stat("/usr/bin/bash", {st_mode=S_IFREG|0755, st_size=960384, ...}) = 0
stat("/usr/bin/bash", {st_mode=S_IFREG|0755, st_size=960384, ...}) = 0
geteuid()                               = 1000
getegid()                               = 1000
getuid()                                = 1000
getgid()                                = 1000
access("/usr/bin/bash", X_OK)           = 0
stat("/usr/bin/bash", {st_mode=S_IFREG|0755, st_size=960384, ...}) = 0
geteuid()                               = 1000
getegid()                               = 1000
getuid()                                = 1000
getgid()                                = 1000
access("/usr/bin/bash", R_OK)           = 0
rt_sigprocmask(SIG_BLOCK, [INT CHLD], [], 8) = 0
clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7fa545dd4a10) = 2967
rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
rt_sigaction(SIGINT, {0x43e500, [], SA_RESTORER, 0x7fa545405650}, {SIG_DFL, [], SA_RESTORER, 0x7fa545405650}, 8) = 0
wait4(-1, 0x7fff54edea00, 0, NULL)      = ? ERESTARTSYS (To be restarted if SA_RESTART is set)
--- SIGTTIN {si_signo=SIGTTIN, si_code=SI_USER, si_pid=2967, si_uid=1000} ---
--- stopped by SIGTTIN ---

招聘资深Java/Scala工程师

我们在招Java/Scala的资深工程师(或技术专家),你将有机会跟我们共同参与一些挖财基础设施的开发。如果你有Akka的应用经验更好,不过掌握Scala/Akka并不是必要条件,基本的要求是功底扎实,对分布式有一定的了解。至少有三年以上的工作经验,本科或以上学历。工作地点在杭州。

关于挖财的技术你可以通过InfoQ或ArchSummit搜到一些相关的分享,业务开发语言以Java为主Scala为辅。现阶段技术人员有两百多人的规模,这几年早已从初创时期的个人英雄主义转变为中等规模的集团军作战方式。整体上我们算是偏中庸的一个技术团队,也形成了一套适合自己的技术栈,相对务实,没有太多花活。

业务当前发展比较快,对技术的要求也是不断在提升,对该职位感兴趣的请发邮件到: cuodao(at)wacai.com ,每封邮件一定回复。

scala雾中风景(26): 变量查找的问题

在Java/Scala的一个方法里,存在与全局变量同名的局部变量的话将会覆盖这个全局变量,但当在这个方法局部变量定义之前就引用这个变量,在Java和Scala的编译器里给出了不同的实现,先看Java里:

➜  cat B.java
public class B {
    static String name = "noname";
    public static void main(String[] args) {
      System.out.println(name);
      String name = "wang";
    }
}

➜  java B
noname

在main方法里第一行引用的name是全局变量,在同名的局部变量定义之前它从全局查找这个变量。而在Scala里:

 ➜  cat A.scala
object A {
    val name: String = "noname"

    def main(args: Array[String]) {
        println(name)
        val name = "wang"
    }
}

 ➜  scalac A.scala
A.scala:5: error: forward reference extends over definition of value name
    println(name)
            ^
one error found

编译时错误,它优先从方法内部的局部变量表类查找了。我更接受Java里的方式,而且这种方式已经深入人心了,对Scala为何这样做我不清楚是出于什么考虑,是有意这样还是实现上的bug?