Built in sharding in PostgreSQL

Published on:
Tags: postgresql

PostgreSQL 内建 sharding 支持,粗略翻译自 https://wiki.postgresql.org/wiki/Built-in_Sharding

Introduction

内建支持 sharding 最大的挑战是,如何用最小的代码修改实现。大部分社区的 sharding 修改支持都修改了很多 PostgreSQL 的代码,这也导致这些不能被 Postgres 社区那些不需要 sharding 的人接受。有了 FDW 之后,就有了在有限代码修改情况下实现内建 sharding 支持的可能。

基于 FDW 的这种 sharding 设计,是基于 NTT 开发的 Postgres-XC,大概已经有 10 年了。Postgres-XL 是基于这个设计的一种更加灵活的实现。

Enhance Existing Features

  • 已完成?提升 FDW 的基础设计和 postgres_fdw。特别的,好的性能要求合理的把一些操作推送到子节点(foreign shards)。在 Postgres 9.6 中,join, sort, update, delete 都可以推送到字节点了。聚合的 pushdown 将在 Postgres 10 中支持。FDW 表已经可以作为继承表出现。
  • 提升分区支持有效提升 existence of shards。幸运的是,单节点的分区支持也需要重构才能提升性能和更多优化。例如,executor-based partition pruning.
  • 给 FDW 请求增加并行支持。这样能允许节点并行执行,这个可能会通过多个异步的链接来实现。

New Subsystems

还需要开发一些子系统:

  • 允许表可以复制到所有节点,以允许更多的 join pushdown。这个可以通过 trigger 或者逻辑复制来完成。
  • 实现一个子模块,以使用新的分区系统表来提交符合提交的查询的 FDW 查询。
  • 实现一个子模块收集 FDW 查询的结果返回给用户。
  • 实现全局事务管理器以便更加高效的允许子节点原子的提交事务。这个可能会通过 prepared 的事务来实现,还有某种在 crash 之后清理那些 preapared 的事务的事务管理器。例如 XA。
  • 实现全局快照管理器,以允许子节点可以看到一致性的快照。(是不是 serialisable 事务模式会避免跨节点快照冲突?pg_export_snapshot() 或者 hot_standby_feedback 是不是会有帮助?) 多节点的备份的一致性也需要这个支持。
  • 实现支持 create, manage, report on shards 这些用户 API。

Use Cases

有四种可能的用户案例和不同的需求:

  • 跨节点在只读节点上面执行只读聚合查询,例如数据仓库
    这种是最简单的场景,不需要全局事务管理,全局快照管理,并且因为聚合,所以子节点返回的数据量也是最小的。
  • 跨节点在只读节点上面执行只读非聚合查询
    这种会给调度节点压力,需要收集和处理很多子节点返回的数据。这种也能看到 FDW 传输机制等级。
  • 跨节点在可读写节点执行只读查询
    这个需要全局快照管理来保证子节点返回数据的一致性
  • 跨节点执行读写查询
    这个需要全局快照管理器和全局事务管理器

申请 google voice

Published on:
Tags: google

昨天晚上突然想申请一个 google voice 帐号。有了之后就可以作为国外的号码使用了,可以打电话收短信,想想好像还有点用。比如我就可以用他注册一个微博帐号用来关联一些无聊的服务了。。。

申请的过程网上很多,不细说了。主要注意下面几点。

  1. 需要美国 ip 打开 https://www.google.com/voice ,否则会跳转到一个帮助页面。这个从网上搜一些免费的代理就可以了。我是搜索到了一些 ss 帐号,然后配合 surge 来做的。
  2. 需要一个能接电话的美国号码来接受 google voice 的验证电话。这个我是通过 http://textnow.com 来做的。登录 textnow.com 注册一个免费的帐号,其中有一步是需要输入一个美国区号,这个要注意,我第一次注册的时候,输入的是 213 (洛杉矶地区的),然后就不行,后来又申请了一个 517 的就可以。所以最好搜索一下别人申请成功的区号有哪些,另外还好就是只要有不同的邮箱,就可以多次注册来换号码,如果遇到不行的,可以换个邮箱重新注册一下。
  3. 在 google voice 里面填入电话之后。google voice 会打电话给那个号码。我用 textnow 的 ios 客户端接的电话(建议不要用他们的 web,他们的 web 必须要你的浏览器支持 flash 才行,很恶心),我遇到的情况下,前几次接到的电话没有对方的声音,自己尝试直接输入号码也不行。后来多试几次就好了。
  4. 后面就是选号了。选好号码之后,点提交一般都会遇到一个错误,「There was an error with your request. Please try again」, 遇到这个是正常的。需要你多次点击那个按钮提交,有人用按键精灵点了几个小时搞定了。我这是打开 chrome 的 dev tools,然后看 network 里面发的请求,每次点击都会有一个 post 请求,在上面按右键,选择 「copy as curl」,然后在命令行写一个简单的程序,while true; do 这里把复制的内容贴过来 ;sleep 1.5s; done,复制到命令行,不停的重试就可以了,直到收到邮件说你的号码开通了。要注意的是命令行也得设置代理,一般是通过 export。

新与旧

Published on:
Tags: heart

北京有个古北水镇,运营上据说是杭州乌镇的那拨人。乌镇没去过,去过古北水镇。在一片山里面,长城下面,一群建筑。里面还开了一些模仿古代的商店,比如酒家,染坊这些可以参观。

这个地方离北京大概是 2 个多小时的车程(全程高速不怎么堵车的情况下)。周末过去之后会发现停车场停满了车,还有好多旅游车,水镇上面也人山人海。

这里面的建筑都是后期开发商人工开辟的。景区里面的酒店基本都是1k起,并且还得提前预约。据说夜景很漂亮,不过我没看过。

北京是个古老的城市,如果城区里面的历史建筑都留着,现在可够你转几个月的,光就绕着北京城墙走一圈,估计一天都不一定可以。曾经在西安的城墙上面走过一圈,感觉还不错,我记得还收了门票的。

北京有个南锣鼓巷,过去转你会发现并没有什么能让你理解回味京味的东西,卖的也是羊肉串奶茶这些,排好长的队买一串拿着边吃边走完了,其实可能也挺没意思的。

都说台湾是中华传统保持比较好的地方。去了台北第一印象就是,破房子挺多的。那边房子是私产,所以拆迁很难。然后个人又不一定有能力翻盖,所以就有破烂的房子。给我们开车的台湾人还说去过北京,说羡慕那边的高楼大厦,到处都很新,很时髦的样子。

台湾有很多夜市,去过几个,有的比较商业化的,你会发现周边也都是高楼了。有的就是普通的,真的是一条路白天行车,晚上就堵起来开始摆摊。我记得某个夜市里面有个摊位,说是开了好多年了,现在物价高,不得已只好比早年涨了几毛钱。看着都震惊了,涨几毛钱还废什么话,况且本身人家卖的也不贵,都是良心价。

地价涨没那么快,可能各种基础花费都会比较稳定,否则地价涨了房租涨了,那物价必定会涨。所以10年前我在北京长椿街那上班的时候,一份盖饭,大概是 7,8 块钱。到了现在,估计是翻一倍。另外这种店还越来越少,因为卫生条件,房租这些要求导致价格底了不好赚钱。

台湾夜市里面也经常能碰见那种几十年的老店,那真的是几十年一直在做那个生意。几十年价格也没有变化太多。想起来前门的那个面馆前段时间关门了,没有办法,涨价没法涨,收入基本不变的情况下,地价变化太大,只好关门了。

就目前这个房地产的情况,北京还能有多少真正的老字号,有多少真正的老街,估计基本不会有了。后面估计会有更加多人工的景区了,费用估计还不能便宜了。

想起来凤凰古城了,大家不愿意拆,那就某天一把火烧了,这下都同意了吧,这可是天意。

python 的 decorator 学习

Published on:

最近学习了一下 python 的 decorator(装饰器),看的是这篇,Python修饰器的函数式编程, 觉得挺有意思的,写点东西记录一下。

装饰器简单讲就是返回一个函数的函数/类。看个简单的例子。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#!/usr/bin/python
# -*- coding: utf-8 -*-
def dec1(fn):
print('inside dec1')
def wrapper():
print('inside wrapper')
return fn()
return wrapper
@dec1
def f1():
print('inside f1')
if __name__ == '__main__':
print('begin exec')
f1()
print('end exec')
# 执行结果:
# inside dec1
# begin exec
# inside wrapper
# inside f1
# end exec

看上面例子能看到,装饰器生效有 2 个步骤,第一个是装饰,第二个是执行。上面装饰器的效果,和下面的代码的效果是一样。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#!/usr/bin/python
# -*- coding: utf-8 -*-
def dec1(fn):
print('inside dec1')
def wrapper():
print('inside wrapper')
return fn()
return wrapper
# @dec1
def f1():
print('inside f1')
if __name__ == '__main__':
print('begin exec')
dec1(f1)()
print('end exec')
# 执行结果:
# begin exec
# inside dec1
# inside wrapper
# inside f1
# end exec

可以看到除了 「begin/end exec」,其他部分执行结果是一样的。所以理解装饰器,就把 @dec1 换成 dec1(fn)() 这么理解就可以了。

有时候会看到类也可以作为装饰器使用。其实理解起来也类似。举个例子。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#!/usr/bin/python
# -*- coding: utf-8 -*-
class dec1(object):
def __init__(self, fn):
print('inside dec1')
self.fn = fn
def __call__(self):
print('inside wrapper')
return self.fn()
@dec1
def f1():
print('inside f1')
if __name__ == '__main__':
print('begin exec')
f1()
print('end exec')
# 执行结果:
# inside dec1
# begin exec
# inside wrapper
# inside f1
# end exec

这里和上面类似,把 @dec1 理解成 dec1(fn)(),不过是这里的 dec1 是个类,那么 dec1(fn) 其实是调用的 dec1.__init__(fn),那么后续的 dec1(fn)() 就是调用产生的对象的 dec1.__call__() 了。

有时候还能看到加了参数的装饰器。加了参数的是怎么回事呢。再看下面的例子。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#!/usr/bin/python
# -*- coding: utf-8 -*-
def dec1(name):
print('inside dec1')
def real_dec1(fn):
def wrapper():
print('inside wrapper')
return fn()
return wrapper
return real_dec1
@dec1(name='1')
def f1():
print('inside f1')
if __name__ == '__main__':
print('begin exec')
f1()
print('end exec')
# 执行结果:
# inside dec1
# begin exec
# inside wrapper
# inside f1
# end exec

看懂了没有,就是多了个嵌套而已。遇到加了参数的,那就是把之前的没有参数的部分返回回来就可以了。等价的例子就不贴了,这个等价于 dec1(name='1')(fn)()

如果是类装饰器,并且有参数,那等价于 dec1(name='1')(fn)(),其中 __init__(self, name) 先处理第一层参数,然后 __call__(fn) 处理第二层,然后需要在 __call__ 里面再定义一个 wrapper 返回。

说明白没有?呵呵。

cookie 的一点研究

Published on:
Tags: cookie http

这几天搞了一下 python 里面 cookie 相关的东西。我的目的是想要尝试用 python 登录某个网站,并且保持登录状态直到过期。因为 http 协议是无状态的,所以一般来讲,网站想要用户保持登录,那么网站在用户登录之后,必须要和用户端协商好怎么来证明这个用户已经登录过了。

用户端如果使用浏览器,那么网站就可以利用浏览器对 cookie 的支持来让用户在不知情的情况下,让网站在用户登录后发的一个 token 在用户后续的请求里面都包含上。

用户端如果不是浏览器,比如是个 python 程序,那么网站可以和用户协商每次请求里面都包含某个下发的 token(当然,甚至要求客户端每次请求都带着用户名密码也是可行的)。

但是如果网站本身只是给浏览器用户准备的,那么通过用程序来「模拟」浏览器行为,把必要的 token 保存并在后续的请求里面都带上,也是可行的。

python 里面,发送 http 请求可以简单的使用 urllib.request.urlopen(url),但是如果想要定制一下请求,比如修改一些 header 信息,那么就得使用 urllib.request.Request 这个 class 先构造一个 Request 对象,然后传递给 urlopen 了。

如果要处理 cookie,那就需要使用 http.cookiejar.CookieJar 了,有了 Cookiejar 对象,就可以把网站下发的 cookie 保存到这个变量里面,然后在必要的时候,可以返回给服务器端了。如果想要保存到文件,那么可以使用 http.cookiejar.LWPCookieJar 或者 http.cookiejar.MozillaCookieJar,也可以基于 http.cookiejar.FileCookieJar 自己实现一个子类,来用自己的办法保存和加载 cookie,比如保存到数据库什么的,这样就可以多台机器之间共享 cookie 了。

urlopen 本身不支持自定义 cookiejar 逻辑,得使用 opener = build_opener(HTTPCookieProcessor(cookiejar=Cookiejar对象)) 来先构造一个自定义的 openner, 然后使用 opener(Request对象) 来发送请求。

如果不定义自己的 cookie policy,那么会使用默认的 http.cookiejar.DefaultCookiePolicy,也可以自己基于 http.cookiejar.CookiePolicy 实现自己的逻辑。只需要 override set_okreturn_ok 这两个方法就可以。

http cookie 其实有很多属性,比如 domain, expire, path 等常见属性,也有 httponly, secure 等几个不常见的。这些属性都是浏览器处理的。就是说,浏览器把 cookie 返回给服务器端的时候,如果 domain 不匹配,或者已经过了 expire 时间等等一些不符合浏览器制定的 cookie 逻辑的时候,浏览器就不会把 cookie 发送给服务器端。就比如,服务器产生 cookie 的时候,声明了 domain=a.com,那么如果是来自于 b.com 的请求,浏览器是根本不会给他发送这个 cookie。再比如,服务器端产生 cookie 的时候,声明了 1 天后过期,那么 1 天之后,浏览器也不会再给服务器端发这个 cookie 了。

但是如果是我们自己实现客户端模拟浏览器的时候,其实我们是可以耍流氓的,可以制定自己的 cookie 逻辑,也就是上面提到的 cookie policy。比如我可以简单的在 return_ok 这个方法里面 return True,在任何情况下都把所有的 cookie 返回给服务器,这样服务器端如果不提前想明白,它是一点都不知道的。

所谓提前想明白就是想明白是不是需要针对这种情况做处理。如果本身我们系统也没有那么严格要求,那么不处理也可以。但是如果是某个比如金融系统,那么是必须要考虑的。否则如果完全依赖 cookie 的话,如果我通过某些手段弄到了用户的 cookie,那么我就可以骗过服务器端,让他认为我就是那个用户。

我想了一下,貌似被盗窃 cookie 这种事情服务器端不太好防范,但是可以做的是防止浏览器耍流氓。比如我们把 cookie 加密,并里面增加一个发送 cookie 的时间。收到客户端发过来的 cookie 之后,我们解密看看时间有没有过期,这样就可以在服务器端让 cookie 失效了。

另外,也可以考虑使用 session。session 是把一些用户的状态保存在服务器端。但是 session 实际上也是依赖 cookie 的,因为前面说了 http 协议无状态,就算可以把用户状态保存在服务器端,但是总还是得识别用户才可以。那个识别的 cookie 就是所谓的 session cookie,其实就是某个用户的唯一标识。

对于 session cookie 被窃,好像也没有太好的办法,无非也是想办法比对之前用户的一些状态信息,比如 ip 和现在的信息是不是一致,不一致可以认为有被窃的怀疑,这个时候让用户再次验证用户信息,这都不能 100% 保证,但是至少会增加窃贼的成本。

上面说到这些,都可以自己测试一下,测试也并不一定需要搭一个服务器端配合,以及使用复杂的抓包专鉴,其实使用 nc 就可以。

使用 nc -l 9999 就可以启动一个监听在 9999 端口的 socket 服务器。之后使用 python 或者 curl 之类的程序请求,就能立刻看到请求发送过来的 http 信息,这个对于学习 http 协议其实也很方便。

1
2
3
4
5
6
7
$ nc -l 9999
GET / HTTP/1.1
Accept-Encoding: identity
Connection: close
Cookie: QN2=test; QN1=ClbaCVfZF5lfszBALzTIAg==
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36
Host: localhost:9999

收到的上面这个请求,可以看到发送过来了 2 个 cookie。

如果还想测试数据返回的情况,那么可以写一个 test.resp 文件,内容如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ cat test.resp
HTTP/1.1 200 OK
Date: Sun, 18 Oct 2009 08:56:53 GMT
Server: Apache/2.2.14 (Win32)
Last-Modified: Sat, 20 Nov 2004 07:16:26 GMT
ETag: "10000000565a5-2c-3e94b66c2e680"
Set-Cookie: QN1=ClbaCVfZF5lfszBALzTIAg==; expires=Thu, 31-Dec-37 23:55:55 GMT; path=/
Set-Cookie: QN2=test; expires=Thu, 31-Dec-37 23:55:55 GMT; path=/; secure; httponly
Accept-Ranges: bytes
Content-Length: 44
Connection: close
Content-Type: text/html
X-Pad: avoid browser bug
<html><body><h1>It works!</h1></body></html>

然后使用 nc -l 9999 < test.resp 命令启动服务,客户端来请求的时候,就会返回上面 test.resp 里面的内容。

从双拼讲输入法

Published on:
Tags: input

上高中的时候,就流行五笔打字,不过那会家里都没有电脑,都是去网吧(或者叫打字社)去学习的,不过那会学习这个貌似唯一的用途就是录入用。

当时也背过五笔的码表,王旁青头兼五一,估计很多人都很熟悉。上了大学在有更多机会接触电脑的时候,上网什么的,自然就开始使用拼音输入法,因为基本不需要练习,会盲打知道键盘按键键位就可以使用。当时用的比较多的是智能 ABC,后来 2000 年左右写书的时候,用的是紫光拼音,紫光的词库比智能 ABC 好一点,所以用起来也好用很多。此后一直使用拼音输入法。

直到应该是大概 2008 年左右,ssslang 在使用二笔,就开始想尝试一下码表输入法。因为本身使用了这么多年的电脑输入和拼音输入法之后,对字形其实已经失去了概念,如果学五笔的话,肯定会是很痛苦的。

而二笔输入法一个字 4 个码,第一个码还是拼音的第一个字母,但是大部分常用字都是 2 个码就可以出,所以这样等于每个字都至少知道了一半的编码,这个还是很赞的。

二笔输入法也有很多编码方案,当时选的是哲豆二笔,这个方案只用了 26 个字母键,所以也可以在手机上面使用(其他方案用了一些符号键,比如 [ ],当时手机还都是流行硬键盘,一般都没有单独的符号键,而且貌似现在的软键盘也没有单独的符号键)。我当时用的 palm treo 650,输入还是蛮爽的。后来自己把一些词库导入了进去,用起来就更加舒服了。

但是后来的问题是慢慢的一些流行的智能机没有那么好的输入法自定义支持,就没法用了,慢慢切回来了拼音输入法。

二笔是码表输入法,就是每个字有自己的编码,联系的过程就是熟悉码表的过程,如果某个字不熟悉,那么其实你是需要轻微的思考的,想一下那个字怎么写,是哪个码。所以输入的速度取决于对这些字的熟悉程度,遇到不熟悉的会有卡顿。

最近看到了不少双拼的讨论,就想尝试一下双拼。因为如果反正都是用拼音输入法,那么对于 ing, eng 这些,如果可以按一个按键就输入进去,那么岂不是可以节省很多时间?并且这个还是基于拼音的,和那些需要记住字形的输入法不是一个套路,看着还不错。

我是这周一开始尝试的。mac 上面用的是鼠须管,ios 用的 touchpal。双拼也有很多方案,我选的方案是小鹤双拼,也没啥特别的原因,就是看用的人挺多的。

尝试的结果就是,输入了 4 天之后,周五我又换回了拼音输入法。。。。

因为我越用越发现,双拼也是个码表输入法。开始想得是节省拼音的输入时间,但是打字的时候,比如输入 hao 的时候,想到的不是输入 h 然后输入 ao 对应的字母 c,而是熟悉之后自然而然的按照 hc 来输入,下次需要输入 lao 的时候,还是需要想一下 ao 在字母 c,需要输入 lc。

这么看的话,和前面说的码表输入法有啥区别呢?所以我感觉双拼也是个码表输入法。既然是码表输入法,双拼的区分度那么差,何必折腾他呢,不如去用二笔呢。而且我用的时候最郁闷的是,二笔训练出来的键位记忆居然总会出来捣乱,这其实也更加说明这个就是码表输入法。。。

而且还有就是,普通拼音输入法里面,尤其是手机上面,容易按错键,比如 hao 输入成了 hso 的时候,手机上面会纠错,把「好」这个选项放在第一个。但是如果是双拼就没那么好搞了,如果也增加纠错,那会增加很多重码,体验不好。

另外还有个问题就是对于 sh, s, en, eng 这些不太清楚的人来讲,更加是个挑战,当然拼音可以用模糊音,按说双拼也可以想办法自定义模糊音。但是可能就失去了双拼的一些优势。

进化论

Published on:
Tags: tech

回忆一些没用的

我是 1998 年上的大学。小的时候就对各种电子产品比较感兴趣,当然其实没有什么机会的,当时主要就是游戏机,但是我家里是没有的。高中的时候曾经借同学的回家玩过,和我弟弟一起玩的挺嗨,但是对于增长知识应该是没啥用的。

高考完毕之后,其实最想要报的还是计算机系,记得暑假的时候在电视里面看电脑相关的讲座,讲的最多的好像就是五笔,那会 windows 用户也不多,都是用的 dos。ucdos 是最常用的。那会电脑最大的用途感觉就是打字输入,然后打印。

当时没有报计算机,因为闭塞的农村人认为计算机会和财经一样(当时财经类专业曾经火过一些时间,但是介于毕业的人都没啥靠谱工作,所以就认为一般了),过段时间就不行了,所以没有报这块,报的是应用物理专业(当然了,这个专业更屎)。

那个假期去我舅舅的办公室去玩玩电脑。我舅舅是微机专业毕业的,毕业后回我们那边做类似一个打字的工作(其实我也不知道具体做什么….)。我当时去那边就是熟悉各种 dos 命令,学习一下 foxbase。

当时我记得有一本 foxbase 的书,看里面东西看不懂,没有现在的论坛和方便的网络,没有人可以交流。我记得印象最深的是有一道题需要算 1+2+3+…+100 等于多少,不懂编程完全不知道怎么做这个事情,但是看了答案看不懂,因为不懂 i=i+1 是什么意思,那会不明白为什么 i 还可以在右边。现在看来,生在那个年代那个地方真的很悲哀。

进入大学之后,选修了一门 word 之类的课,开始接触 windows。然后宿舍同学熟悉之后,就计划一起买了一台电脑,每个人 1200,我们总共 6 个人,7200 一台电脑,包括音箱电脑桌。当时要上网只能是拨号,56k 猫,开始是用不起的,大概到了大2,3 才开始拨号。然后 6 个人轮流用。那是我们班的地一台电脑,当时辅导员要求大家写保证书,保证不玩游戏才允许买。当然,你们懂得,保证书有个卵用,所以后来某天被发现玩游戏之后,给我们把电脑格式化了。但是之后大家还是会玩。

上了大学之后,就有网络中心,第一次去网络中心的时候,有点懵逼,和 xh 两个人对着电脑不知道该干啥。旁边的人都在玩 mud,当时不懂,就是看着一个绿窗口觉得很神奇。当然后来我们玩 mud 到昏天黑地就不多说了。玩 mud 玩的多了,慢慢了解到了 mud 的机制,有时候自己搭一个开始研究里面的编程,不过还比较粗浅。当时学的也就是 fortrun,c 语言这些。

那会的搜索引擎是目录式的,就和黄页一样,除了去看一些门户,就是翻这些东西,希望发现一些有意思的东西。那会学习也基本是靠买书和看自带的文档(不如 msdn),和通过网络靠一些小论坛里面的一些交流。那会写的比较多的是 vc vb 这些,php 之类学的人不多,java 我记得好像也不多。我的毕业论文是一个 vb 的程序,现在看写的比较烂,也就是能用。

开始讲网站开发

前面废话太多,又不太舍得删掉,就分一下块吧,下面讲讲网站开发技术的进化。

dreamwaver & frontpage 时代

那会做网页比较多的应该是 asp,用 frontpage 写,用 iis 发布,都是图形界面的,linux 没有什么人会用。linux 下面那会各种问题,驱动输入法这些都得花费很大的精力去解决。我装的第一个 linux 应该是 xterm linux,11 张光盘,因为他把一些软件也刻录到光盘了。第一次装 linux 就把宿舍的电脑搞挂了,没选对直接重新分区了。不过也就当年折腾这些弄明白了分区表 mbr 这些东西。

那会还攒了一些书,有一本就是讲怎么用 dreamwaver 的,当时觉得这个东西真牛逼,抛弃了 frontpage。我还记得我用 frontpage 做的第一个自我感觉良好的网页给别人看的时候,人家的那个表情,基本就是想吐的那种。我现在想,典型的那些花背景底色,大字体,图片按钮,来回滚动的一些内容等等,都是很烂的。

基于对 dreamwaver 的熟悉,当时还去联想实习了一段时间,给他们解决了一些问题。

那会做网页一般也就是收个表单,存到数据库什么的,不会有太复杂的东西。做个论坛是最常见的了。那个年代 javascript 是忌讳的东西,一般也就是用来滚动个页面标题什么的,没人拿他做特牛逼的东西。

毕业之后,就进了出版社。那个时代桌面程序很流行,因为大部分电脑都不能上网的,我到了出版社凭着自己的兴趣,用 vb 做了一个库存管理的软件,其实就是入库多少,出库多少登记一下,简单的很。

后来也是自己的兴趣,找了一台没人用的电脑,windows 上面跑起了 java web 服务,那会还流行搞一些 java applet 跑。出版社最早的网站就是这么跑起来的。

在后来装了一个 linux,开始在上面跑 qmail,跑 apache,开始熟悉 linux。同时也有了 php 的网站,不是我开发的,跑在我那个 linux 机器上面。

ajax & mvc

后来大概有几年没有接触这些了,大概 08 年左右接触到了 ajax,javascript 算是就那几年走上神坛。当时老何演示了一个跑在本地浏览器里面的留言本程序,服务器端不用提供任何 html 相关的服务,只需要保存数据返回数据就好,逻辑都跑在本地浏览器里面。

不过那个时候我写的比较多的还是那种古老的表单方式,自己没去仔细了解这种新技术。后来大概 09 年左右有机会开始写 javascript 的时候了解了一些机制,感觉这种方式很有意思,渲染逻辑跑在浏览器,这样节省了一些服务器端的压力,并不是什么操作都需要提交到服务器让服务器去判断下一步怎么办。那会用到了 jquery,感觉真的好方便,并且他还提供一些 css 的效果,让不太熟悉的人也可以很方便的搞起来。而服务器端那会用 php 比较多。

php 当时也接触了一些框架,名字是不记得的。我这种野路子程序员,根本不明白人家的一些设计思路,代码和模板分离,mvc 这些都不懂,不过代码看多了也能明白一些。

我记得 09 年写这些的时候,一个遗留的 php 网站,需要增加一点功能,发现需要我一个人在 n 个文件里面修改,楞要把一个功能分散个 n 个文件让我很不理解,不是蛋疼么。当然后来明白这么做是为了分工合作为了灵活扩展,那么如果就一个人写的时候,我们是不是也需要这么做呢?

emberjs & angular & react & django & spring

后来大概 10 年左右的时候,我写代码还是用 jquery 的方式来做,因为当时觉得很好用。再后来接触了一些 emberjs angular 的一些知识。学习到了 emberjs 里面的命名惯例,比如一个路由叫 posts 那么就有对应的 PostsController,不用你显式的去让他们产生关系。学习 angular 学到了双向绑定的概念,你在定义了一个模型之后,在模板里面用到变量的地方会自动跟着这个模型的数据变化而变化,这就比用 jquery 的时候需要你自己去替换 dom 里面的内容方便多了。然后到了现在的 react 又提出了 virtual dom 的概念,让你不需要关心 dom 结构,你只需要操作你的虚拟 view 就好了。

当然前端其实还有好多的进步,比如 coffee script 来解决 javascript 这个语言本身的一些问题,哪里不严谨,一些最佳实践什么的就不用你操心了,只需要写 coffee script 就好了,编译成 javascript 之后,会自动处理好。

还有比如 css 不能 include,import,很多方案来解决这个问题。

后端这块,最近接触了 django,spring 这些。程序员要分 2 类的话,那就是 java 的和非 java 的,要分三类的话,就是 java ,javascript 和其他,呵呵。看过之后感觉 spring 真是不错的东西。难怪 java 程序员不去了解其他的,入了 java 坑就不用搞其他的了,库类很方便。并且类似 spring 这种东西,好多年前就有了,可以极大的方便 web 开发(不止 web 开发可以用)。如果能很好的驾驭 java,驾驭 tomcat jvm 这些东西,那么使用 java 其实是很不错的方案。通过 java 的 oo 特性,比如继承,重载 interface abstract 这些,可以很方便的定义好一些规范,也可以很好的把大家的工作划分开,架构,定义模型定义接口的人大家协同工作。

以前文件路径就是网页路径,现在有了 route 的概念(这个概念在 angular, emberjs 里面也有),做了 route mapping 之后,可以很方便的把不同的 route 映射到相同的方法上面,可以很方便复用代码逻辑。

总结下

上面讲了很多,整个就是个进化史。以前弄个网站出来需要花不少时间,还很难看。现在基于 bootstrap 还有这些开发框架,做一个出来很快外观还好看。这就是进步。

计算机技术的颠覆是很快的,如果打算一直从事这块工作,建议还是得保持一颗持续学习的心态,也得扩展自己学习的广度,这样可以吸收到各种技术的长处。

现在学习的手段也多了很多,我看视频直播网站都有开始讲课的了(不一定靠谱),还有各种学习视频,更别说各种可参考的学习文档了。实在是太方便了,只要愿意花时间,进步速度绝对很快。

LLD in zabbix

Published on:

如果需要监控的内容比较多的时候,手动管理报警信息就已经不使用了,加一批机器就需要忙活一阵子。也不能体现我们充满智慧的大脑的作用。

zabbix 支持 LLD(low level discovery) 方式来自动产生监控项目,包括 item, trigger 这些都可以自动添加。大概讲解一下可以利用这个东西做什么事情。

zabbix 收集数据的方式

zabbix 有很多收集数据的方法,这里重点讲 2 个,一个是 zabbix agent,一个是 zabbix traper。这两个方式可以和 nagios 里面的 active 和 passive 方式做类比。traaper 方式对应的就是 passive,就是 client 主动发送数据给 server。

对于 zabbix agent 方式,我们可以自己定义一些 userParameter 来添加自定义监控,这些网上很多例子。如果使用 trapper 方式,那么原则上面可以不用做任何自定义,就可以通过 zabbix-sender 或者自己模拟 sender 的协议,通过比如 python,java 等发送自己的监控信息。通过 python 发送的例子网上也有。

LLD

参考这里,LLD 主要的思路就是给服务器端发送一个 json 数据格式。例如下面这个。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{
"data":[
{ "{#FSNAME}":"/", "{#FSTYPE}":"rootfs" },
{ "{#FSNAME}":"/sys", "{#FSTYPE}":"sysfs" },
{ "{#FSNAME}":"/proc", "{#FSTYPE}":"proc" },
{ "{#FSNAME}":"/dev", "{#FSTYPE}":"devtmpfs" },
{ "{#FSNAME}":"/dev/pts", "{#FSTYPE}":"devpts" },
{ "{#FSNAME}":"/lib/init/rw", "{#FSTYPE}":"tmpfs" },
{ "{#FSNAME}":"/dev/shm", "{#FSTYPE}":"tmpfs" },
{ "{#FSNAME}":"/home", "{#FSTYPE}":"ext3" },
{ "{#FSNAME}":"/tmp", "{#FSTYPE}":"ext3" },
{ "{#FSNAME}":"/usr", "{#FSTYPE}":"ext3" },
{ "{#FSNAME}":"/var", "{#FSTYPE}":"ext3" },
{ "{#FSNAME}":"/sys/fs/fuse/connections", "{#FSTYPE}":"fusectl" }
]
}

这个数据里面,data 是必须的,里面包含里面发现的可监控数据,这可以是任何数据。例子里面是发现了可以用来监控的磁盘分区。data 是个数组,每个可监控项是一个数组元素。还有类似下面这样的数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{
"data": [
{
"{#HOST}": "Japan 1",
"{#COUNT}": "5"
},
{
"{#HOST}": "Japan 2",
"{#COUNT}": "12"
},
{
"{#HOST}": "Latvia",
"{#COUNT}": "3"
}
]
}

这个是发现了一些可监控的 host。

理解没有?发现是发现可监控的服务,并不是发现监控项。比如我们可以通过发现这机器上面有没有启动 ssh,发现有启动之后,我们就可以通过服务器端配置 discovery 自动添加一些监控规则。

1
2
3
4
5
6
{
"data": [
{ "{#SSH_PORT}": "22" },
{ "{#SSH_PORT}": "8022" }
]
}

比如上面这个,我们发现了 2 个 ssh 进程,一个是 22 端口,一个是 8022 端口。

所以重点是发现有什么可监控的服务,并不是发现监控项。

BUT,其实并不是不能发现监控项,也是可以的。不过是,这种被发现的监控项,除非对应的 trigger 也都是一样的,否则你会发现无法分别添加不同的 trigger 规则。

发现监控项

有了发现服务之后,就肯定需要对相应的服务的一些监控项做监控了。这个给 discovery 规则配置 item prototype 就可以了,不过这个里面有点坑需要填,后面会说,这里先不讲。

那么比如对于 ssh 服务,可以监控

  • 当前链接人数,conn.cnt
  • 配置文件的 md5,conf.md5(配合 zabbix trigger 可以用来监控文件是不是被修改了)

那监控数据就如下面

1
2
3
4
5
6
{
"22.conn.cnt": 4,
"22.conf.md5": "18492113fb263c9d0a33c9fea403eea1",
"8022.conn.cnt": 9,
"8022.conf.md5": "6cab272daa07202ccb57c4064c0dcfb8"
}

上面就是一个 discovery 项目,filter 是 {#SSH_PORT},和 2 个 item prototype,分别是 {#SSH_PORT}.cnn.cnt 和 {#SSH_PORT}.conf.md5。

复杂一点的 LLD

一个 LLD 还可以发现多个服务。比如下面这种。

1
2
3
4
5
6
7
8
{
"data": [
{ "{#SSH_PORT}": "22" },
{ "{#SSH_PORT}": "8022" },
{ "{#PG_PORT}": 5432 },
{ "{#PG_PORT}": 6432 }
]
}

这个除了我们前面讲的 ssh 服务,还发现了两个 pg 的服务。在服务器端,只需要添加两个 discovery 规则就可以了,分别使用 {#SSH_PORT} 和 {#PG_PORT} 这两个宏来过滤数据。

1
2
3
4
5
6
7
8
9
10
{
"data": [
{ "{#SSH_PORT}": "22" },
{ "{#SSH_PORT}": "8022" },
{ "{#PG_PORT}": 5432 },
{ "{#PG_PORT}": 6432 }
{ "{#MASTER_DB_PORT}": 5432, "{#SLAVE_DB}": "host1" },
{ "{#MASTER_DB_PORT}": 5432, "{#SLAVE_DB}": "host2" },
]
}

上面这个,除了有 2 个 db 之外,还有一个 db 是个 master,能看到他对应的 slave 有哪些。要注意,我们在新增加的这个发现项里面,不能再使用 {#PG_PORT} 这个宏了,因为如果使用了这个宏,就会和第3,4个项目无法区分了。所以我们改了一下名字。

到此为止,只是我们的构思,想要告诉 zabbix 我们想要监控什么。真正使用还需要走一些路。

如何发送数据

不管是 discovery 数据,还是 item 的监控数据,都可以通过 agent 和 trapper 方式发送。

对于 discovery 数据,使用 agent 发送就是上面讲的格式。

1
2
3
4
5
{
"data": [
{ "{#PG.OTHER}": "0" },
]
}

如果使用 trapper 方式发送,格式如下

1
2
3
4
5
6
7
8
9
10
{
"data": [
{
"host": "HOST1",
"value": "{\"data\": [{\"{#PG.OTHER}\": \"0\"}]}",
"key": "pg.discover"
}
],
"request": "sender data"
}

上面这个数据里面,data 和 request 是 zabbix sender 的固定格式。data 里面,包含了 host, value, key 三个字段。host 是被监控的 host,和将来服务器端的 host 对应。value 是发送的监控内容,可以看到也就是我们使用 agent 发送的内容。key 就是对应的监控项,这个监控项也就是 agent 方式发送对应的那个 userParameter。

使用 trapper 方式发送里面,是可以伪造被监控的 host 的,所以 trapper 方式并不要求一定要在被监控机器上面执行。

对于 item 监控数据,使用 agent 发送是下面这种格式。

1
2
3
4
{
"key1": 2,
"key2": "ok"
}

使用 trapper 方式发送,是下面的这种格式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
"data": [
{
"host": "HOST1",
"value": 1,
"key": "key1"
},
{
"host": "HOST1",
"value": "ok",
"key": "key2"
}
],
"request": "sender data"
}

zabbix 里面的限制

上面的例子很完美,但实际上 zabbix 是有一些限制的。比如 item 定义。

假如对于发现的 pg 服务,有一个监控项是连接数,比如 {#PG_PORT}.conn.cnt,此时你会发现在 zabbix 新建 item 的 Key 那个设置里面,这么写无法提交。需要使用假装类似 userParameter 的方式来写,比如 pg.[{#PG.PORT}.conn.cnt],假装那个 pg. 是个 userParameter 命令,[{#PG.PORT}.conn.cnt] 里面的内容是他的参数。当然,这个 pg. 可以基本可以是任何字符串,比如 abc,你自己觉得有意义就好了。

那么这个时候对于发现那块,我们基本不用动,需要动的是被发送的服务的监控项的命名上面。

比如以那个 ssh 的监控为例,原来发送的数据如下

1
2
3
4
5
6
{
"22.conn.cnt": 4,
"22.conf.md5": "18492113fb263c9d0a33c9fea403eea1",
"8022.conn.cnt": 9,
"8022.conf.md5": "6cab272daa07202ccb57c4064c0dcfb8"
}

我们只需要修改成这样

1
2
3
4
5
6
{
"ssh[22.conn.cnt]": 4,
"ssh[22.conf.md5]": "18492113fb263c9d0a33c9fea403eea1",
"ssh[8022.conn.cnt]": 9,
"ssh[8022.conf.md5]": "6cab272daa07202ccb57c4064c0dcfb8"
}

对应的 2 个 item prototype,key 分别修改为 ssh[{#SSH_PORT}.cnn.cnt] 和 ssh[{#SSH_PORT}.conf.md5]。那个 ssh 可以随意起。并且其实并不一定就得是这种模式,比如叫做 ssh.conf.md5[{#SSH_PORT}] 应该也可以,当然需要你发送的数据也做对应修改。

如何发送监控数据

咦?好像说过一次了?这次和上面不一样,呵呵。

设计好并写好监控之后,选择什么方式发送监控数据呢。我选择的是 discovery 数据通过 agent 方式获取,也就是在各服务器上面定义相同的一个 key,然后执行这个 key 的时候发送发现的服务信息。

而对于监控项数据则通过 trapper 方式发送。通过 trapper 方式发送,需要定时执行,可以通过 crontab 发送。我选择的是建立了一个 agent 类型的 item,执行这个 item 的时候发送监控数据。这样一方面可以针对这个发送动作建立一个监控,另外一方面调整很方便,zabbix 界面修改就可以。并且我把这个 item 建立到了模板上面,只要修改应用模板就可以了。

监控数据也可以用 agent 方式发送,如果用 agent 方式发送,对于上面的 ssh 服务,就需要真的建立那个 ssh 的 userParameter 了,然后接受比如 22.conf.md5 这样的参数,去返回对应的监控数据。我没有用这种方式,是因为这样做等于有多少个 item 就需要在监控周期内执行多少次那个命令,给服务器增加负担(虽然没多少)。而使用 trapper 方式的话,就可以一次把所有的监控数据都发过去了,命令只需要执行一次。

如何应对不同的部分

到此为止,应该可以很完美的发现服务,并且监控了。但是会发现其实并不是所有服务器的服务都是一样的,比如对于 pgsql,slow query 的界定对于不同的业务可能不一样。而因为 trigger 也是自动发现添加的,这样也有可能需要不同的机器上面的服务有不同的阈值,怎么解决呢?

先说监控项的阈值。因为我的监控数据其实是通过建立一个 agent 类型的 item 定期发送 trapper 数据来实现的,所以只需要在调用那个 item 的时候传送不同的阈值就可以了。实际上面我的 itme key 定义是这样的 pg.sendtrap[{$PG.DISCOVER.SETTINGS}] 。那个 pg.sendtrap 是对应到一个 userParameter 的 UserParameter=pg.sendtrap[*],/etc/zabbix/bin/zabbix_pg.py --check --sendtrap --settings $1,在 zabbix_pg.py 里面,会处理 settings 参数。如果有阈值,那就定义好 {$PG.DISCOVER.SETTINGS} 这个宏就可以了。template 上面可以定义默认的阈值,当然默认阈值在程序里面定义也可以。然后不同 host 可以定义 host 的阈值,会覆盖模板的配置。

其实 trigger 的阈值和这个思路类似,也是 template 里面定义一个宏,trigger 里面使用这个宏就可以了。如果 host 有不同的阈值,那就定义一个 host 的宏覆盖他就可以了。

目前的情况

配合 zabbix 的 auto registration 这个 action,可以做到新机器只需要执行一个 saltstack state,安装好我们的 zabbix agent,就可以自动注册 host,自动添加监控报警。

相当完美。

能不能成功取决于什么

Published on:
Tags:

随着年龄的增长,自己对一些事情的认识在改变,一直想总结一个给年轻同学的帖子,但是总是不能总结太好怎么讲,大概从想讲到现在,已经几个月了,我感觉不讲的话可能就讲不出来了,所以乱谈一下。

从全栈讲起

什么是全栈?我感觉肯定不是普通人,我觉得全栈至少得是绝顶聪明的人才可以做到,对于他们来讲,有很扎实的 CS 功底,并且知识面非常广泛,还有很多产出,并且这些产出是各个方面的。

某种观点

上面是李笑来老师对于全栈的看法,这里 有知乎用户对这个的讨论。

我只是关注到了他那句话里面的「不太笨」,如果他的意思是很聪明的意思,那观点就和我一致了,呵呵。

不得不承认,智商和你能掌握的内容是有关系的,智商不够再怎么努力也很难达到某种顶峰。当然这并不是说,智商不够的就没戏了,换句话说,如果智商不够还不努力,你还是就想想老婆孩子热炕头就算了。

学习别人

学习别人的成功经验,似乎是一种很好的办法,至少人家那条路是走通了。

这几年流行健身,是吧。看着别人一个一个健身房也好,路边跑步也好,还有奥森跑步的,是不是看着眼红但是又觉得自己周边不具备环境?健身房贵,路边有觉得没有好环境,奥森还有点远,所以我得找一个满足条件的才能去进行这个事情。

还有比如看到有人拿 kindle 看书,是不是觉得我要是有个 kindle 带着方便,肯定可以看好多书,比如一直想学习的 iOS 开发,还有 Java 编程思想,哎呀呀,编程能力大幅增长啊。所以一定要买个 kindle。

等等类似的事情吧,别的不多讲了。就是我想要干什么事情,但是呢最好满足个什么条件,会让我干的更好更有动力。

最后通常的结果呢?步顶多跑几次就会觉得没意思了,kindle 在家里吃灰,然后开始有其他的想法,比如看着人家的 iphone 不错唉,我要是有个,看个 pdf 什么的,比 kindle 方便啊,手机可是一直带着的,所以。。。。

听别人讲

欧洲人在放难民进来的时候,想的可是我们可怜你们让你们进来,你们应该很满足,不要闹事老实呆着。所以实际上呢?

父母亲戚常见的「我都是为了你好」这种说法,大家估计都听腻了吧。有用么?

微信朋友圈网上各种鸡汤文,比尔盖兹为什么成功,雷军马云的奋斗,这些文章看的时候让人激动人心,看完了貌似就忘记了,是不是?。。

关键在哪里?

我感觉关键就是一个字「懒」。

懒

2004 年左右在 irc 玩的时候,就有一个网站 let me google that for you。这个网站就是鄙视那些连 google 都懒得用的,稍微有点问题就问别人。知识都是别人的,把别人当 google 用。

很多人都有类似习惯。我们学习的时候,别人都是引进大门,如果自己不能养成自己知识持续更新的习惯,等到自己连年轻人都跟不上的时候,就很悲哀了。

这个都无关智商,所谓活到老学到老,别人都给你总结好了,懒字一上来,就混吃等死吧。

想要客服懒字,得自己给自己洗脑,让自己能坚持的下去。老罗的奋斗 里面,老罗讲自己决定要去新东方当老师之前,学习英语的时候,隔段时间就会学不下去,学不下去的时候,看看成功学的书,给自己打打鸡血,就又活蹦乱跳了。

我大概总结几项提升的方向,遇事情想想,应该有好处

  • 主动思考解决问题的最佳思路
  • 主动发现问题,改进承担
  • 主动推进事情进展
  • 不要限定自己的范围,不停挑战难点
  • 积极参与到别人的有激情的项目里面
  • 对技术保持强烈的好奇心

如何不花钱建立一个支持 https 的 blog

Published on:
Tags: blog

早年的时候要搞 blog 还得弄一个空间,现在,免费的东西越来越多了,感觉共产主义的实现还要靠资本家啊,不过羊毛出在羊身上。。。

要想弄一个免费的 blog,首先你的 blog 内容最好是纯静态网页,如果是类似 php 什么的,那就难找了。使用 jeklly, hexo 这些都可以把 markdown 文件渲染成 html。

然后注册一个 github 或者 gitcafe 等等支持 pages 服务的空间,搞定之后就能得到一个类似于 http://wd.github.io 这样的地址。

然后你注册一个域名(发现标题没起好,这个还是要收费的。。),然后注册 cloudflare,把你的域名的 dns 使用 cloudflare 的,然后在 cloudflare 配置一个 cname 到 wd.github.io。然后建立一个 page rule,强制你的域名使用 ssl。

ok 拉,整个过程就是域名花钱了。可以访问下 http://wdicc.com 看看效果,会自动跳转到 https://wdicc.com :D