分类 默认分类 下的文章

区块链学习知识模块

1.Bitcoin,Ethereum,HyperLedger Fabric的区块链相关机制与原理以及理念;
2.共识算法,主要有PoW,PoS,DPoS,PBFT,Paxos,Raft等;
3.分布式架构,包含网络编程、分布式算法、加密签名、数据存储、p2p并发;
4.HTTP/2协议,gRPC框架,protobuf的开发等;
5.NoSQL数据库的原理与使用,尤其是KV型数据库:LevelDB,RocksDB等;
6.golang,C++,python等主流区块链系统开发语言;
7.钱包管理私钥;
8.交易所撮合系统;
9.linux操作系统,Docker容器技术的原理,部署和使用优化。

mysql innoDB使用聚族索引

https://dev.mysql.com/doc/refman/5.6/en/innodb-index-types.html
http://umumble.com/blogs/mysql/mysql-(innodb)-clustered-and-non_clustered-indexes-/
http://blog.csdn.net/jhgdike/article/details/60579883
http://blog.csdn.net/caomiao2006/article/details/52140953
http://www.cnblogs.com/xxmysql/p/5874803.html

使用swoole和redis做股票和区块链服务

PHP 的redis扩展是阻塞式 IO ,使用订阅/发布模式时,会导致整个进程进入阻塞。因此必须使用Swoole\Redis异步客户端来实现。

$server = new swoole_websocket_server("0.0.0.0", 9501);

$server->on('workerStart', function ($server, $workerId) {
    $client = new swoole_redis;
    $client->on('message', function (swoole_redis $client, $result) use ($server) {
        if ($result[0] == 'message') {
            foreach($server->connections as $fd) {
                $server->push($fd, $result[1]);
            }
        }
    });
    $client->connect('127.0.0.1', 6379, function (swoole_redis $client, $result) {
        $client->subscribe('kline1min');
    });
});

$server->on('open', function ($server, $request) {

});

$server->on('message', function (swoole_websocket_server $server, $frame) {
    $server->push($frame->fd, "hello");
});

$server->on('close', function ($serv, $fd) {

});

$server->start();
  • 在进程启动(onWorkerStart)时创建了Swoole\Redis客户端,连接到Redis服务器
  • 连接成功后,订阅msg_0主题的消息
  • 当有新的message时,Swoole\Redis会触发onMessage事件回调
  • 在这个回调函数中使用$server->connections遍历服务器所有的连接,发送消息

Mac OS普通用户启动80端口

由于系统限制非root用户不能启动1024以下端口,而我们平时使用Mac一般都是非root用户,所以如果想启动80端口必须用root用户,这时候用命令行就可以解决,但是拿Intellij Idea来说,可以在控制台却换到root权限用命令行来启动IDEA,但是这样的话很多信息都是和root用户相关的,比如mavan仓库地址,IDEA配置默认都在root用户目录下面了,而你登录的用户又是非root用户,查看修改和管理都相当麻烦,于是可以通过端口转发功能,把本地的80请求转发到你配置的1024以上的端口上,效果一样

sudo vim /etc/pf.conf

添加

rdr on lo0 inet proto tcp from any to 127.0.0.1 port 80 -> 127.0.0.1 port 8080

到 pf.conf文件的 rdr-anchor "com.apple/*" 这一行后面。
其中 lo0 通过 ifconfig 看自己那个设备绑定的是127.0.0.1, lo0是这个网络设备的名字。 8080是要转发的端口

sudo pfctl -f /etc/pf.conf sudo pfctl -e

邮箱小号Gmail sub-account

這篇文章介紹一個製造 Gmail “小號” 的方法,就是可以製造很多地址不同,但是實際導向同一個電郵地址的方法。

用這個方法,就可以在不註冊新的電郵地址的情況下,製造很多個 “小號”。一則在其他地方可以用不同電郵地址註冊多個帳號,二則方便管理透過不同地址發給自己的郵件。

本文所述的方法,其實網上已經很多人說過,而且都大概 5-10 年前的文章了,不過 Gmail 每年都有很多新使用者,所以現在重新介紹一下。

Gmail 跟其他常見的電郵地址最不同的地方,就是不支持底線 “_” ,相反卻支持點 “.” 和加號 “+” 。而這個製造小號的方法,就是有效利用 “.” 和 “+”。

為安全起見,以下的電郵地址,中間的 “@” 均以 “#” 代替。例如 abc123 # gmail.com。


法則一:gmail 可以改為 googlemail

以前有一些國家註冊的 Gmail 是以 googlemail.com 為地址的,作者本人都註冊過一個。不過現在兩個地址基本上完全通用了。

換句話說,對於 Gmail 而言,abc123 # gmail.com 和 abc123 # googlemail.com 是同一個地址。寄到後者的電郵,一律在前者的地址可以查看。

如此一來,由原來的一個地址,現在有兩個地址可以用了。


法則二:“@” 前面的部份,可以在任意位置加上任意個點 “.”

換句話說,對 Gmail 而言,有 “.” 和沒有 “.” 都是一樣的。

例如 abc123 # gmail.com,可以改為以下任何一個:
abc.123 # gmail.com
abc...123 # gmail.com
.a.b.c.1.2.3. # gmail.com
...abc...123... # gmail.com

對於 Gmail 而言,上面所有地址跟 abc123 # gmail.com 是沒有分別的。於是乎,這個方法可以為自己創造無限多個小號了。

不過需要注意,有些網站的註冊郵箱並不支持前面帶 “.” 的電郵地址,例如 .abc123 # gmail.com 不能用,但是可以用 abc.123 # gmail.com。


法則三:用戶名和 “@” 之間,可以用 “+” 插入任何字串,而且可以加插任意多個 “+”

換句話說,“+” 和 “@” 中間的任何字串,都會被 Gmail 忽略。

例如 abc123 # gmail.com,可以改為以下任何一個:
abc123+def456 # gmail.com
abc123+a+b+c+1+2+3 # gmail.com
abc123+Ax+By+C # gmail.com

真正的粉絲,還可以用這個 (誤) :
abc123+1s # gmail.com

用這個方法,可以讓自己在不同網站的註冊名稱不同,例如
abc123+XDA # gmail.com
abc123+Facebook # gmail.com

甚至可以作為臨時電郵地址,例如
abc123+temp+1 # gmail.com
abc123+temp+2 # gmail.com

這樣子,只要開始收到垃圾郵件,看看收件人就可以知道是那個網站出賣了你的個人資料了。

不過需要注意,不是所有服務也支持帶有 “+” 的電郵地址。例如酷安就不支持了。

這個方法,同樣可以為自己創造無限多個小號,而且比較多元化和比較有系統。


法則四:上面三個法則可以任意搭配

例如,abc123 # gmail.com 可以改為以下任何一個:
abc.123+Maki # googlemail.com
abc...123+Ma.ki+Ni.co # gmail.com
abc123.+.Nico.Nico.Ni. # gmail.com
abc.1.2.3+Yosoro.+.Zura # gmail.com

這樣,就可以創造多元化無限創意的小號了。

雖然本文的方法已經存在了好幾年,不過對於 Gmail 新手來說,應該還是有點用的。

原文:http://telegra.ph/Gmail-Cloning-08-20

nginx gzip压缩和gulp js压缩 的效果

-rwxr--r-- 1 forthxu forthxu 262K Jun 16 14:57 jquery-3.2.1.js //原始大小
-rw-r--r-- 1 forthxu forthxu 78K Jun 16 15:02 jquery-3.2.1.js.gz //原始文件gzip后的大小
-rwxr--r-- 1 forthxu forthxu 86K Jun 16 15:15 jquery-3.2.1.min-d6a2dcf9a6.js //javascript压缩后的大小
-rw-r--r-- 1 forthxu forthxu 30K Jun 16 15:16 jquery-3.2.1.min-d6a2dcf9a6.js.gz //javascript压缩后在使用gzip压缩的大小

gzip -c jquery-3.2.1.js > jquery-3.2.1.js.gz

gulp.task('testjs', function() {
    return gulp.src('../../public/test/jquery-3.2.1.js')
            //.pipe(concat('all.js'))// 合并
            .pipe(uglify())// 优化
            .pipe(rename(function(path) {// 重命名
                path.basename+='.min';
            }))
            .pipe(rev())//生成版本号
            .pipe(gulp.dest('../../public/test/'))
});

gzip -c jquery-3.2.1.min-d6a2dcf9a6.js > jquery-3.2.1.min-d6a2dcf9a6.js.gz

关于TCP可靠性的一点思考,借此浅谈应用层协议设计

本文主要讨论如何设计一个可靠的RPC协议。TCP是可靠的传输协议,不会丢包,不会乱序,这是课本上讲述了无数遍的道理。基于TCP的传输理论上来说都是可靠的,但是实际这也得看场景。当我做网络游戏的时候也是一直把它当一个可靠的传输协议来用,从没考虑过TCP丢包的问题。直到当我面临像网络存储、机器学习这样领域时,我发现TCP变得“不可靠”了。

具体来说:

发送方能不能知道已发送的数据对方是不是都收到了?或者,收到了多少?答:不能
如果怀疑对方没收到,有没有办法可以确认对方没有收到? 答:不能
我想发送的是“123”,对方收到的会不会是“1223”? 答:是的,会这样,而且无法避免。
​第一个问题看起来很傻,众所周知TCP有ACK啊,ACK就是用来对方通知接收到了多少个字节的。可是,实际情况是,ACK是操作系统的事儿,它收到ACK后并不会通知用户态的程序。发送的流程是这样的:

应用程序把待发送的数据交给操作系统
操作系统把数据接收到自己的buffer里,接收完成后通知应用程序发送完成
操作系统进行实际的发送操作
操作系统收到对方的ACK
问题来了,假如在执行完第二步之后,网络出现了暂时性故障,TCP连接断了,你该怎么办?如果是网络游戏,这很简单,把用户踢下线,让他重新登录去,活该他网不好。但是如果比较严肃的场合,你当然希望能支持TCP重连。那么问题就来了,应用程序并不知道哪些数据发丢了。

以Windows I/O completion ports举个例子。一般的网络库实现是这样的:在调用WSASend之前,malloc一个WSABuffer,把待发送数据填进去。等到收到操作系统的发送成功的通知后,把buffer释放掉(或者转给下一个Send用)。在这样的设计下,就意味着一旦遇上网络故障,丢失的数据就再也找不回来了。你可以reconnect,但是你没办法resend,因为buffer已经被释放掉了。所以这种管理buffer的方式是一个很失败的设计,释放buffer应当是在收到response之后。

Solution:不要依赖于操作系统的发送成功通知,也不要依赖于TCP的ACK,如果你希望保证对方能收到,那就在应用层设计一个答复消息。再或者说,one-way RPC都是不可靠的,无论传输层是TCP还是UDP,都有可能会丢。​

第二个问题,是设计应用层协议的人很需要考虑的,简单来说,“成功一定是成功但失败不一定是失败”。我想举个例子。假如你现在正在通过网银给房东转账交房租,然后网银客户端说:“网络超时,转账操作可能失败”。你敢重新再转一次吗?我打赌你不敢。

再举个例子,假设你设计了一个分布式文件存储服务。这个服务只有一条“Append”协议:

客户端向服务器发送文件名和二进制data。
服务器把文件打开(不存在则创建),写入数据,然后返回“OK”。中途遇到任何错误则返回“FAIL”
假设你现在有一个20TB的文件,你按照1 GB、1 GB的方式往上传。每发送1 GB,收到OK后,继续发送下1 GB。然后不幸的是中途遇到一个FAIL,你该怎么办?能断点续传吗?NO。因为服务器有可能在写入成功的情况下也返回FAIL(或者网络超时,没有任何回复)。所以你不能重发送未完成的请求。如果你选择从头传,而文件又特别大,那么你可能永远都不会成功。

Solution:采用positioned write。即在客户端发给服务器的请求里加上文件偏移量(offset)。缺点是:若你想要多个客户端同时追加写入同一个文件,那几乎是不可能的。​

​第三个问题:我想发送的是“123”,对方收到的会不会是“1223”?你想要支持重连、重试,那么你得容忍这种情况发生。

Solution:在应用层给每个message标记一个id,让接收者去重即可

接下来讨论下如何关闭连接。简单来说:谁是收到最后一条消息的人,谁来主动关闭tcp 连接。另一方在recv返回0字节之后close,千万不要主动的close。

在协议设计上,分两种情况:

协议是一问一答(类似于HTTP),且发“问”(request)的总是同一方。一方只问,另一方只答
有显式的EOF消息通知对方shutdown。
如果不满足以上两点的任何一点,那么就没有任何一方能判断它收到的消息是不是最后一条,那协议设计有问题,要改!

(p.s. Windows上还有一种方法,就是用半关连接shutdown(SD_SEND)来标志结束,但是操作起来比较复杂,还不如改协议来的快,容易debug)

原文:http://weibo.com/ttarticle/p/show?id=2309404060342857671095
扩展:http://www.ideawu.net/blog/archives/782.html

Python2字符编码问题小结

Python docs - Unicode HOWTO

Python docs - Built-in Types

Stack Overflow - Why does Python print unicode characters when the default encoding is ASCII?

理论

编码中的Unicode和UTF-8

Unicode是字符集,UTF-8Unicode的一种编码方式,并列的还包括UTF-16UTF-32等。

某个字符的Unicode通过查询标准得到,其UTF-8编码由Unicode码计算得到。

Python2中的str和unicode

strunicode是两个不同的类。

str存储的是已经编码后的字节序列,输出时看到每个字节用16进制表示,以\x开头。每个汉字会占用3个字节的长度。

>>> a = '啊哈哈'
>>> type(a)
<type 'str'>
>>> a
'\xe5\x95\x8a\xe5\x93\x88\xe5\x93\x88'
>>> len(a)
9
>>> a[2]
'\x8a'

unicode是“字符”串,存储的是编码前的字符,输出是看到字符以\u开头。每个汉字占用一个长度。定义一个Unicode对象时,以u
开头。

>>> b = u'哟呵呵'
>>> type(b)
<type 'unicode'>
>>> b
u'\u54df\u5475\u5475'
>>> len(b)
3
>>> b[2]
u'\u5475'

str可以通过decode()方法转化为unicode对象,参数指明编码方式。

>>> a.decode('utf-8')
u'\u554a\u54c8\u54c8'

unicode可以通过encode()方法转化为str对象,参数指明编码方式。

>>> b.encode('utf-8')
'\xe5\x93\x9f\xe5\x91\xb5\xe5\x91\xb5'

默认编码

Python2中的默认编码,有多个不同的变量。

  1. 代码文件开头的coding

     # -*- coding: utf-8 -*-
    

     # coding=utf-8
    

    指明代码文件中的字符编码,用于代码文件中出现中文的情况。

     % cat hello.py
     #! /usr/bin/env python
     # coding=utf-8
     print '泥壕'
     
     % python hello.py
     泥壕
    

    如果不设置,默认是ascii,当出现中文字符时就不能正常识别。

     % cat hello.py
     #! /usr/bin/env python
     print '泥壕'
     
     % python hello.py
         File "hello.py", line 2
     SyntaxError: Non-ASCII character '\xe6' in file hello.py on line 2, but no encoding declared; see http://python.org/dev/peps/pep-0263/ for details
    
  1. sys.stdin.encodingsys.stdout.encoding

    sdtinstdout输入输出使用的编码,包命令行参数和print输出,由locale环境变量决定。

    en_US.UTF-8的系统中,默认值是UTF-8

  2. sys.getdefaultencoding()

    文件读写和字符串处理等操作使用的默认编码。

    默认值是ascii

字符串拼接

unicodestr类型通过+拼接时,输出结果是unicode类型,相当于先将str类型的字符串通过decode()方法解码成unicode,再拼接。此时如果解码时没有明确指明编码类型,可能会出现错误。

>>> a = '啊哈哈'
>>> b = u'哟呵呵'
>>>
>>> a + b
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'ascii' codec can't decode byte 0xe5 in position 0: ordinal not in range(128)
>>>
>>> a.decode('utf-8') + b
u'\u554a\u54c8\u54c8\u54df\u5475\u5475'

错误提到'ascii' codec can't decode byte 0xe5,这是因为自动将str类型的变量按照默认的编码格式sys.getdefaultencoding()来解码,默认编码即ascii,而这个字符不在ascii的范围内,就出现了错误。

>>> import sys
>>> reload(sys)
<module 'sys' (built-in)>
>>> sys.setdefaultencoding('utf-8')
>>>
>>> a = '啊哈哈'
>>> b = u'哟呵呵'
>>> a + b
u'\u554a\u54c8\u54c8\u54df\u5475\u5475'

文件读取和json解析

读文件得到的结果是str类型,以\x开头的十六进制表示。

>>> f = open('t.txt')
>>> a = f.read()
>>> a
'{"hello":"\xe5\x92\xa9"}\n'

而经过json解析后会自动转为unicode

>>> json.loads(a)
{u'hello': u'\u54a9'}

输出

输出到文件

str类型可以输出到文件,而unicode类型必须先编码成str

>>> a = '啊哈哈'
>>> b = u'哟呵呵'
>>> a
'\xe5\x95\x8a\xe5\x93\x88\xe5\x93\x88'
>>> b
u'\u54df\u5475\u5475'
>>> 
>>> f = open('t.txt', 'w')
>>> f.write(a)
>>> f.write(b)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-2: ordinal not in range(128)
>>> f.write(b.encode('utf-8'))

unicode输出到文件时的错误是由于默认编码为ascii,无法自动完成编码过程。如果将sys.getdefaultencoding()编码设置成了utf-8就可以自动完成转换过程了。

>>> import sys
>>> reload(sys)
<module 'sys' (built-in)>
>>> sys.setdefaultencoding('utf-8')
>>>
>>> f.write(b)

计算md5

同样,md5计算也要求输入的unicode先编码。

>>> a = '啊哈哈'
>>> b = u'哟呵呵'
>>> import hashlib
>>> hashlib.md5(a).hexdigest()
'f38b302e2993ec3fdad79c4d76074b21'
>>> hashlib.md5(b).hexdigest()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-2: ordinal not in range(128)
>>> hashlib.md5(b.encode('utf-8')).hexdigest()
'c02dc06719bafeaf60505b11d3c0c90a'

输出到stdout

输出到stdout时,默认编码是sys.stdout.encoding,默认值取决于系统环境变量,所以print输出汉字时才可以不用指定utf-8

>>> import sys
>>> sys.stdout.encoding
'UTF-8'
>>> print u'\u54a9'
咩

而在zh_CN.GB2312的环境中,默认值不是utf-8,就不能正常输出了。

>>> import sys
>>> sys.stdout.encoding
'ANSI_X3.4-1968'
>>> print u'\u54a9'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeEncodeError: 'ascii' codec can't encode character u'\u54a9' in position 0: ordinal not in range(128)

命令行参数读取

通过sys.argvargparse得到的命令行参数都是编码后的str类型,以\x开头的十六进制表示。可以通过sys.stdin.encoding得到命令行传入的编码类型,解码成unicode

#! /usr/bin/env python
# coding = utf-8
import sys

print repr(sys.argv[1])
print sys.stdin.encoding
print repr(sys.argv[1].decode(sys.stdin.encoding))

输出结果。

~/workspace % python hello.py "哇嘿嘿"  
'\xe5\x93\x87\xe5\x98\xbf\xe5\x98\xbf'
UTF-8
u'\u54c7\u563f\u563f'

如果命令行环境已经改成GB2312等其他编码,python找不到与之匹配的编码类型,就会将默认编码sys.stdin.encoding设置成ascii,无法通过这种方法正常解码成unicode

带\u的字符串转unicode

可能会遇到汉字被转换成unicode编码的形式表示的情况,即一个汉字被表示成了\u????的形式。

>>> a = u'咩'
>>> a
u'\u54a9'
>>> b = '\u54a9'
>>> b
'\\u54a9'

上述b就是这样的情况。此时b是一个长度为6的字符串,而不是一个汉字。

要把b表示为汉字编码有两种方法。

  1. unicode-escape编码。

     >>> unicode(b, 'unicode-escape')
     u'\u54a9'
    

     >>> b.decode('unicode-escape')
     u'\u54a9'
    
  1. eval拼接。

     >>> eval('u"' + b.replace('"', r'\"')+'"')
     u'\u54a9'
    

网页正文提取算法和相似文章比较算法

两个比较好的网页正文提取算法:

国内
哈工大的《基于行块分布函数的通用网页正文抽取》该算法开源网址为http://code.google.com/p/cx-extractor/,文章中呈准确率95%以上,对1000个网页抽取耗时21.29秒。看了文章感觉不错,无需html解析,效率应该会高些。

Html2Article C# http://www.cnblogs.com/jasondan/p/3497757.html

国外
大名鼎鼎的arc90实验室的Readability,该算法已经商业化实现了firefox,chrome插件,及flipboard,并且已经集成进了safari浏览器。未详细测试,大致测试感觉准确率应该至少在90%以上。该算法需要解析DOM树,因此稍执行效率稍微慢一些。大致过程为,先解析DOM树,所有标签小写。然后去除所有“script”标签内容,再通过一对正则表达式的配合提取。具体算法还未看。其插件中包含算法JAVASCRIPT源码。
有热心人士已将其用c#和php实现,源码地址如下:

官方网站 http://www.readability.com/
c#实现一:https://github.com/marek-stoj/NReadability
c#实现二:https://github.com/marek-stoj/NReadability
php实现一: https://bitbucket.org/fivefilters/php-readability
php实现二: https://github.com/feelinglucky/php-readability 作者主页: http://www.gracecode.com/archives/3061/
node.js版:https://github.com/arrix/node-readability/

原文:http://www.cnblogs.com/phoenixnudt/articles/2382140.html

相似文章算法

『simhash算法』

simhash是google用来处理海量文本去重的算法。 google出品,你懂的。 simhash最牛逼的一点就是将一个文档,最后转换成一个64位的字节,暂且称之为特征字,然后判断重复只需要判断他们的特征字的距离是不是<n(根据经验这个n一般取值为3),就可以判断两个文档是否相似。

simhash 实现的工程项目

C++ 版本 simhash
Golang 版本 gosimhash

『百度的去重算法』

百度的去重算法最简单,就是直接找出此文章的最长的n句话,做一遍hash签名。n一般取3。 工程实现巨简单,据说准确率和召回率都能到达80%以上。

『shingle算法』

shingle原理略复杂,不细说。 shingle算法我认为过于学院派,对于工程实现不够友好,速度太慢,基本上无法处理海量数据。

『其他算法』
具体看微博上的讨论

原文:https://yanyiwu.com/work/2014/01/30/simhash-shi-xian-xiang-jie.html

libc.so.6误删后的修复方法

因为测试Selenium的chromedriver需要2.15以上的libc.so,因此自己编译安装,删除再做软链时发现ls、dir一系列命名不能使用的情况,提示:

rm: error while loading shared libraries: libc.so.6: cannot open shared object file: No such file or directory

才意识到动到了系统核心的动态库,补救方法:

LD_PRELOAD=/lib64/libc-2.12.so ln -sf /lib64/libc-2.12.so /lib64/libc.so.6

原理就是优先查找指定动态库。

扩展阅读:
libstdc++.so.6升级

https://github.com/FezVrasta/ark-server-tools/wiki/Install-of-required-versions-of-glibc-and-gcc-on-RHEL-CentOS

https://centos.pkgs.org/5/centos-x86_64/libstdc++-4.1.2-55.el5.x86_64.rpm.html

ftp://ftp.gwdg.de/pub/misc/gcc/releases/gcc-4.9.4/

http://www.mudbest.com/centos%E5%8D%87%E7%BA%A7gcc4-4-7%E5%8D%87%E7%BA%A7gcc4-8%E6%89%8B%E8%AE%B0/