在 Ubuntu 16.04 上安装 shadowsocks server

Shadowsocks 的是一个 Socks 代理服务器软件,这里不多说它是做什么用的。

由于速度和延迟的原因,我使用 shadowsocks 会偶尔换不同 VPS 供应商的服务器。这是第一个头疼的地方:需要找个速度稳定的服务商。这里我试过很多,改天有时间专门写篇文章可以简单介绍下。第二个头疼的地方就是,每次安装都需要现找安装方法,很是麻烦。所以这次我把安装的过程记录下来,希望能帮助看到这篇文章的读者们。

先找好云服务或者 VPS 提供商,开好实例。这里我选择用 Ubuntu 16.04。实例的大小其实按照最小的实例就足够用,有 512M 内存的就选 512M 内存的,没有就选 1G 的实例就好。各家服务商开实例的过程略有不同,这里略过不表。

我们从开好实例后 ssh 登录上服务器开始。下面假设使用 root 账户登录。
1. 安装 python:

确保 python 是 2.6 或 2.7 版本。

2. 安装 pip (python 的包管理程序)

3. 安装 shadowsocks

4. 创建 shadowsocks 的配置文件

上述命令会创建好配置文件。注意密码部分自己设置一个专用密码即可,监听的端口、加密方式和超时时间等参数,也可以根据自己的需要进行调整。

5. 启动 shadowsocks 服务

6. 查看服务是否启动成功

通过日志查看:

通过进程查看:

有上面的进程,就说明启动成功。

7. 设置自动启动

有的时候服务器实例可能会遇到维护重启,这个时候如果我们的 shadowsocks 服务能够随着系统启动,那会比较省事。

运行上面这个命令,我们会把启动 shadowsocks 的命令加到系统启动过程中。这样,不管什么时候,只要系统启动了,shadowsocks 服务就在。

至此,服务端配置完毕。

客户端的配置,就不多说了,和上面服务端的配置保持一致就可以。另外服务端 ip 记得要填写这个实例的公网 IP 地址。

 

工厂模式简介(一):简单工厂模式

现今在互联网圈,设计模式并不是那么容易被提起了,这恐怕和互联网的快速迭代和微服务的流行有一些关系。设计模式在函数式编程火热的时候还一度成为了 anti-pattern,被大家觉得古板、无用。当然,在这股函数式编程热潮中中枪的可能是面向对象编程,设计模式是作为面向对象编程中的精华而遭到了唾弃。

实际上再回过头想一下,这些设计模式是实际开发中提炼出的有用经验,在一定程度上确实可以提高程序的可读性和可扩展性;另一方面,设计模式作为固定的套路,也加深了代码在作者和读者之间理解的程度,在这个意义上,它可以作为一种沟通的媒介。当然,前提是在正确的场景下使用。所谓正确的场景,就是合适的场景,并非为了使用而生搬硬套,而是确实解决了某些问题。

这篇文章里,我来简单介绍一下一种最常见的设计模式:工厂模式,谈谈我对这个模式的理解。

… 

 

荒唐的 Golang 缩写词大写惯例

Golang 是在编程风格和代码组织等一些决定上是出了名的固执己见。尽管 Golang 提供了一个不错的 lint 工具 ,但是却没有提供任何对此工具的配置机制。Golang 的哲学便是要么按它的方式使用它,要么别用。

作为一个从 Java 世界过来开始接触 Go 语言的人,了解到 Go 使用驼峰式命名是很适应很高兴的。但是当了解到一些通用的缩写语需要全部大写时,却还是有些不太习惯,例如使用 userID 而非 userId。

你可能觉得这只是强迫症的矫情并不是什么大问题?好吧,举几个例子,假设你想在你的代码里起这些名字:makeHttpUrlConnection, getHttpApiUrl and isHttpsApiHost. 使用 go lint 之后这些命名会被建议为:makeHTTPURLConnection, getHTTPAPIURL and isHTTPSAPIHost. 是不是可读性大打折扣?

想配置一下 go lint 让它不提醒这些?对不起,go lint 不支持自定义配置。这些 lint 的字典表是写死在代码里的:https://github.com/golang/lint/blob/master/lint.go#L742 . 显然 Go 官方 team 根本不认为这是个问题,见 github issue 上的讨论:

.golintrc for enabling and disabling rules

 

线程非安全的 SimpleDateFormat

JDK 中的 SimpleDateFormat 是线程非安全的, 因此当在并发访问的情形下使用时一定要当心。尽管 Javadoc API 文档中明确指出了该类是未同步的,可能很多人在遇到了其导致的问题之前都不会注意到这点。

先看个例子。

上述示例程序启动了五个线程,各线程都使用共享的 SimpleDateFormat 用来解析时间戳字符串。多运行几次上面的程序,很可能地你可能会看到下述结果或异常信息中的一种:

  1. Exception in thread "Thread-4" java.lang.NumberFormatException: For input string: ""
  2. Exception in thread "Thread-1" java.lang.NumberFormatException: multiple points
  3. Thu Jun 27 10:33:09 CST 2024
  4. Exception in thread "Thread-2" java.lang.NumberFormatException: empty String
  5. Fri Jun 27 10:33:09 CST 2200
  6. ......

如上所示,有时得到的是异常(大部分情况是 NumberFormatException: For input string: ""),有时给出的是错误的结果。

为了避免以上问题,可以在每次使用其解析或格式化字符串时都创建一个新的  SimpleDateFormat 实例。显然,这种处理方式在并发量大时并不是一个很节约的方式。一种更优化的方式是使用 ThreadLocal 变量存放,使得每个线程都独立的有一份 SimpleDateFormat , 通常来说你还可以将创建的实例进行缓存。该方案的实现细节请参考这篇 Jesper’s blog post, 文中还有对各解决方案性能的对比。