Wikipedia维基百科为什么不放广告,是因为版权原因吗?

答:不!维基百科不投放广告,不是因为版权原因。

https://en.wikipedia.org/wiki/Wikipedia:Funding_Wikipedia_through_advertisements 维基官方对投放广告的官方解释:17点理由不投放广告,没有一点提到版权。

通篇只有一处提到版权,是创始人Jimmy Wales的一段话:

I believe that if we looked at putting ads into the search results page (only), with the money earmarked for specific purposes (with strong community > input into what those would be, either liberation of copyrights ….
我相信,如果我们把广告放到搜索结果中,由此筹到的钱可用于特殊用途(社区的大量投入将是版权的解放)

有人说:

像这种权威的百科站点,例如莫种性病的解释词条边上出现一个莆田医院的广告,会怎样?难以想象!这种事也就百度做得出来吧。

上面链接中维基官方给出的不放广告的解释中就有几点的意思一致。
1. Ads cheapen the encyclopedia 会让维基掉价;
2. Threat to neutrality of content 影响内容中立;
3. Conflict of interest 和用户利益冲突.
4. Inappropriate ads 广告内容可能不恰当

上面的官方链接还有一条很强的证据能证明维基不放广告和版权无关。

The right to fork. It is inherent in the use of an open license that projects can fork. Thus far the largest fork away from Wikimedia was by a large part of the Spanish Wikipedia community who left because advertising was being considered. 

有权分叉(复制)。这个权利是维基的许可证所允许的。目前最大的分叉是西班牙语维基。他们离开了维基百科,因为他们想要投放广告。

维基百科的官方许可证页面 https://en.wikipedia.org/wiki/Wikipedia:Copyrights
维基百科的绝大多数内容的许可证是 Creative Commons Attribution-ShareAlike 3.0 和 the GNU Free Documentation License (GFDL)
这是两个非常宽松的许可证,意味着你可以随便复制并修改维基百科的内容(图片内容另有规定),商业化也是被允许的,当然包括投放广告。具体的条款,没时间细看了。

flask框架的回复中显示中文utf8字符,而不是unicode-escape字符串

今天在用flask框架写http服务的时候,发现调用flask的jsonify函数得到的http回复包中的字文是用unicode-escape string表示的。如下:

u5b57\u5173\u6ce8\u6211\u4eec\uff01

有些RESTful工具能够把unicode-escape字符串转成utf8,能够正常显示出中文。但很多编辑器和其它工具只能显示原本的unicode转义字符串,大大妨碍调试。解决方案是用flask的另一个函数:make_response解决这个问题。代码如下:

from flask import Flask, jsonify, make_response


def mk_utf8resp(js):
    '''
    传入一个字典,返回一个json格式的http回复。
    '''
    resp = make_response(json.dumps(js, ensure_ascii=False))
    resp.headers['Content-Type'] = 'application/json'
    return resp

把jsonify(js)换成mk_utf8resp(js),问题解决。

netstat -s统计输出的所有字段详细解释

今天工作上碰到一个问题需要知道udp的丢包数据。实际上我不相信能简单地得到udp的丢包精确数据。理由是,网卡负载太高时有些包连网卡都没收到,根本不可能来得及汇报给内核。另外,如果是路由器把udp丢了,那udp的目的端机器当然更不可能感知到有丢包了。

这时,同事说netstat -us (–statistic)可以看到udp的丢包。这里的u选项指的是只展示udp相关的统计,s选项自然表示的是统计了。如果不用u选项,则出显示所有统计数据。下面是我的机器上的输出。

Ip:
    203440255187 total packets received
    0 forwarded
    0 incoming packets discarded
    201612429535 incoming packets delivered
    1064529177 requests sent out
    15 fragments dropped after timeout
    3058122492 reassemblies required
    1230296840 packets reassembled ok
    15 packet reassembles failed
Icmp:
    14869220 ICMP messages received
    3965512 input ICMP message failed.
    ICMP input histogram:
        destination unreachable: 6054246
        timeout in transit: 687
        echo requests: 8570532
        echo replies: 243755
    12913011 ICMP messages sent
    0 ICMP messages failed
    ICMP output histogram:
        destination unreachable: 4097869
        time exceeded: 5
        echo request: 244605
        echo replies: 8570532
IcmpMsg:
        InType0: 243755
        InType3: 6054246
        InType8: 8570532
        InType11: 687
        OutType0: 8570532
        OutType3: 4097869
        OutType8: 244605
        OutType11: 5
Tcp:
    111681768 active connections openings
    4186820 passive connection openings
    24951865 failed connection attempts
    55064041 connection resets received
    275 connections established
    1033901799 segments received
    1776166765 segments send out
    12156205 segments retransmited
    6705 bad segments received.
    106348033 resets sent
Udp:
    198894689917 packets received
    472986510 packets to unknown port received.
    1146976531 packet receive errors
    116750744 packets sent
    110301286 receive buffer errors
    0 send buffer errors
UdpLite:
TcpExt:
    423 invalid SYN cookies received
    693 packets pruned from receive queue because of socket buffer overrun
    19 packets pruned from receive queue
    11309370 TCP sockets finished time wait in fast timer
    106 packets rejects in established connections because of timestamp
    10210477 delayed acks sent
    20811 delayed acks further delayed because of locked socket
    Quick ack mode was activated 8856 times
    17118697 packets directly queued to recvmsg prequeue.
    301717551 bytes directly in process context from backlog
    152118951904 bytes directly received in process context from prequeue
    104771733 packet headers predicted
    15179703 packets header predicted and directly queued to user
    218747377 acknowledgments not containing data payload received
    102637644 predicted acknowledgments
    7293 times recovered from packet loss by selective acknowledgements
    Detected reordering 40 times using FACK
    Detected reordering 27 times using SACK
    Detected reordering 1088 times using time stamp
    476 congestion windows fully recovered without slow start
    5287 congestion windows partially recovered using Hoe heuristic
    236 congestion windows recovered without slow start by DSACK
    151673 congestion windows recovered without slow start after partial ack
    1 timeouts after reno fast retransmit
    4 timeouts after SACK recovery
    10540 timeouts in loss state
    7232 fast retransmits
    649 forward retransmits
    1871 retransmits in slow start
    11612658 other TCP timeouts
    TCPLossProbes: 93185
    TCPLossProbeRecovery: 14667
    2431 packets collapsed in receive queue due to low socket buffer
    8814 DSACKs sent for old packets
    3350 DSACKs received
    1 DSACKs for out of order packets received
    90851 connections reset due to unexpected data
    214 connections reset due to early user close
    352 connections aborted due to timeout
    TCPDSACKIgnoredNoUndo: 1571
    TCPSpuriousRTOs: 7
    TCPSackShifted: 94
    TCPSackMerged: 131
    TCPSackShiftFallback: 21183
    TCPTimeWaitOverflow: 1876775
    TCPRcvCoalesce: 15711184
    TCPOFOQueue: 3194
    TCPChallengeACK: 2337394
    TCPSYNChallenge: 13608
    TCPSpuriousRtxHostQueues: 1982796
IpExt:
    InBcastPkts: 46443933
    InOctets: 44312451521655
    OutOctets: 1915626725817
    InBcastOctets: 6827280595

喂,要是转载文章。麻烦贴一下出处 blog.ykyi.net 采集爬虫把链接也抓走

这里面确实有两个疑似表示udp的丢包数的数据:

Udp:
    1146976531 packet receive errors
    110301286 receive buffer errors

于是,当然首先是看linux man page。结果netstat的man手册里居然没有这些字段的介绍。
跟住,问google。没想到,答案就是netstat -s的输出并没有准确的文档(pooly documented)。
这里有个贴子问了相同的问题 https://www.reddit.com/r/linux/comments/706wsa/detailed_explanation_of_all_netstat_statistics/
简单地说,回贴人告诉他,“别用netstat,而是用nstat和ip tools”“这是个不可能的任务,除非看完成吨源代码”。
blablabla …
事实上,看了google到的一些贴子后,还是大概知道了真相。

    1146976531 packet receive errors

这一句对应关于UDP的一个RFC标准的文档 中定义的字段 udpInErrors。

“The number of received UDP datagrams that could not be
delivered for reasons other than the lack of an application
at the destination port.”
udpInErrors表示操作系统收到的不能被投递的UDP包,不能投递的原因除了没有应用程序开启了对应的端口。

而这一行

    110301286 receive buffer errors

这一行对应 nstat -a -z (下文会再提到nstat)输出中的 UdpRcvbufErrors 字段。我没有找到RFC关于UdpRcvbufErrors字段的定义。
IBM官网上有个网页简单介绍了UdpRcvbufErrors: Number of UDP buffer receive errors. (UDP的缓冲收到错误的次数)。
再结合这篇文章: 为何udp会被丢弃Why do UDP packets get dropped。我非常有信心的认为 UdpRcvbufErrors 表示的是操作系统的内核tcp栈给udp socket分配的缓冲出错(缓冲满)的次数。至于网卡自己的缓冲,和操作系统的缓冲是两回事。网卡的缓冲出错不会被计入这个计数。udp经过的路由的丢包数当然只能够查看对应的路由器的统计数据了。

另外,因为netstat已经被废弃,不建议使用。而是用 nstat 和 ss 这两个新命令代替。
nstat的输出相当于netstat -s的输出。但nstat会输出比netstat -s更多的字段信息,且绝大多数字段名对应到RFC标准中用的字段名。

可任意转载本文,但需要注明出处!!!谢谢

Why do UDP packets get dropped: https://jvns.ca/blog/2016/08/24/find-out-where-youre-dropping-packets/
1: https://tools.ietf.org/html/rfc4113
2: https://www.ibm.com/support/knowledgecenter/STXNRM_3.13.4/coss.doc/deviceapi_response_2.html

Linux系统级/进程级最多打开文件数,FD文件描述符数

如何增加Linux系统最大文件打开数目呢?

查看系统最大可打开文件数

以下命令查看操作系统级最多可打开文件数,fd数目

$ cat /proc/sys/fs/file-max

我用ubuntu 16输出:

573738

怎么调整系统最大可打开文件数

# sysctl -w fs.file-max=1000000

需要用root用户执行以上命令,设最大为一百万。
但在命令行上修改了这个配置,会在下一次操作系统重启后重置为以前的值。要一劳永逸的改变系统最大打开文件数,需要修改 /etc/sysctl.conf 文件。

fs.file-max = 1000000

在/etc/sysctl.conf文件中增加上述一行。

ulimit命令查看/调整进程级最大文件打开数

$ ulimit -Sn
1024

这个命令查看一个ulimit的软极限值(soft limit),本用户起的进程的最大文件打开数的限制。我的ubuntu 16显示进程最多开1024个fd。如果要提高每个进程可同时打开的文件数,需要更改这个值。

$ ulimit -Sn 2048
bash: ulimit: open files: cannot modify limit: Invalid argument

但我想把每个进程可同时打开的文件数增加到以前的两倍时,报错了。这是因为,软极限值(soft limit)不能够越过(Hard Limit)。我看了一下Hard limit,也是1024

$ ulimit -Hn
1024

那没办法罗,需要以root用户登录更改Hard Limit。

怎么修改普通用户的硬极限hard Limit

用root账户登录后,编辑 /etc/security/limits.conf文件,假如普通用户名是kamuszhou。

kamuszhou hard nofile 10000
kamuszhou soft nofile 5000

以上配置修改kamuszhou普通用户的最多打开文件数的hard limit为10000,soft limit为5000。
对于ubuntu 16,如果使用图形桌面,还需要修改 /etc/systemd/system.conf 和 /etc/systemd/user.conf。
加上这么一行:

DefaultLimitNOFILE=10000

如何定制一个python的logging Handler

gevent貌似和logging冲害,如何定制一个日志File Handler

上个月用python的gevent协程库写了一个tcp服务。日志库使用python标准日志库logging。一个月后,发现一个偶发的bug。这个bug发生时,用python的标准日志库自带的FileHandler写的日志会发往socket占用的文件描述符fd。结果就是,客户端收到了本要打印到磁盘上的日志。花了不少时间定位排查这个bug,仍然没有结果。我开始怀疑是gevent协程库和python的标准日志库logging有冲突。协程库会错误的把logging打开的文件描述符fd关闭并分配给新创建的socket,于是日志就打印到socket占用的fd了。

后来,写了一个检测脚本用来监控这个情况发生的概率。该脚本每分钟会检查进程占用的所有fd,一但发现用来打印本地日志的文件fd不见了,就重启服务进程。核心代码如下:

lsof -p $pid | grep -q ${log_file_name}
if [ $? != 0 ] 
then
    # 报警代码 ...
fi

发现bug发生的频率大概是一个星期一次。但这毕竟根本上解决不了问题啊,又找不到bug的原因,怎么办?

那就自定义一个File Handler吧!

决定自定义一个File Handler,这个File Handler工作在另外一个单独的进程,这样无论如何日志用的fd都不会跟主进程的各种socket用的fd冲突了吧。
代码如下,主要用到的技术进程通讯和用python的__getattribute__魔法把截获类实例的方法调用。这样,只需要把旧的代码中的File Handler(我用了TimedRotatingFileHandler)换成自定义的Handler Class,所有其他旧代码都无需改动。

#!/usr/bin/env python3

'''
created by kamuszhou*AT*tencent.com, zausiu*AT*gmail.com http://blog.ykyi.net
Nov 20, 2018
'''

import logging
import logging.handlers
from functools import partial
from multiprocessing import Process, Queue


class MySpecialHandler(logging.StreamHandler):
    def __init__(self, *args, **kargs):
        self._q = Queue()
        self._p = Process(target=MySpecialHandler.__run, args=(self._q,))
        self._p.start()
        self._q.put(('__init__', args, kargs))

    def join(self):
        self._p.join()

    def __run(q):
        handler = None
        while True:
            # op, params = q.get()
            method, args, kwargs = q.get()
            if method == '__init__':
                handler = logging.handlers.TimedRotatingFileHandler(*args, **kwargs)
            else:  # 主进程的日志调用实际上被转到这里
                getattr(handler, method)(*args, **kwargs)

    def __proxy(self, name, *args, **kwargs):
        # 把调用的方法名和方法参数通过Queue传到专门的日志进程。
        self._q.put((name, args, kwargs))
        fun = logging.StreamHandler.__getattribute__(self, name)
        # print('call method: ', name, args, kwargs)
        # 如果是setLevel函数,再调用一次父类的方法
        if name in {'setLevel'}: 
            return fun(*args, **kwargs)

    def __getattribute__(self, name):
        '''
        Hook大法!截获所有方法
        '''
        attr = logging.StreamHandler.__getattribute__(self, name)
        if hasattr(attr, '__call__') and name not in {'join', 'emit'}:
            return partial(MySpecialHandler.__proxy, self, name)
        else:
            return attr


if __name__ == '__main__':
    handler = MySpecialHandler('/data/tmp/ttt.log', when='D', interval=1, backupCount=90)
    handler.setLevel(logging.DEBUG)
    formatter = logging.Formatter('%(asctime)s: %(levelname)s %(message)s')
    handler.setFormatter(formatter)
    logger = logging.getLogger(__name__)
    logger.propagate = False  # Don't propagate the logging to ROOT
    logger.setLevel(logging.DEBUG)
    logger.addHandler(handler)
    logger.debug('debug testtttttttt')
    logger.info('info testtttttttttt')
    logger.warn('warn testtttttttttt')
    logger.error('error testtttttttt')
    logger.critical('critical testttttt')
    handler.join()

这里自定义的日志进程类只是一个很粗糙的实现,一但跑起来,只能手动杀进程。反正我的使用场景是一个服务。所以,我也懒得加‘优雅的退出代码’。

另外,这里创建自定义日志Handler的父类是StreamHandler,它还有一个重要的函数是emit。如果想定制这么一个Handler,把日志发给kafka而不需要起进程。则子类需要重写父类的emit方法。比如:

    def emit(self, record):
        msg = self.format(record)  # 日志会以record的形式传入该函数,用format把它格式化
        self.kafka_broker.send(msg, self.topic)

伯努利(Binomial)分布和伯努利试验的区别,负伯努利分布及几何分布,超几何分布

我又重新把概率,统计书拿起来了。这次是真的,要把机器学习(统计学习)学好。显然,学好机器学习并不是一件容易的事情。需要要扎实的数学基础。好在我的数学储备还勉强可以,但远算不上“基础扎实”。所以,我有了一个长期远景规划,重新开始学习吧!

今天看了伯努力分布,负伯努利分布,超几何分布,几何分布这几个知识点。下面我一边写回顾今天学到的,一边写文章。

伯努利分布和伯努利试验的区别

伯努利试验(Binomial Experiment)指的仅仅是”一次试验”,或者成功,或者失败。伯奴利分布是一个常用分布,这是完全不同的两个概念。
每次伯努利试验的成功概率记为p,又把重复很多次伯努利试验称为伯努利过程(Binomial Process)。一个重复了n次伯努利试验的伯努利过程中,成功的试验次数记为X,这个X则是伯努利随机变量。X的概率分布被称为伯努利分布。
伯努利分布:

$$b(x;n,p) = {n \choose x} p^k (1-p)^{ n-k}$$

其中,n表示试验次数,p表示每次独立试验成功的概率,x表示成功次数。

什么是负伯努利分布(Negative Binomial Distribution)

伯努利分布和负伯努利分布的大多数设定都是一样的,都不断重复伯努利试验,要么成功要么失败。
不同点是:伯努利分布的随机变量X,表示的是在前n次伯努利试验中成功了x次;而负伯努利的随机变量x,表示的是要达成k次成功试验,需要试验x次(即第x次试验成功,前x-1次试验成功了k-1次)。伯努利分布限定了试验次数记为n,负伯努利限定了成功次数记为k。
负伯努利分布:

$$b^*(x;k, p) = {{x-1} \choose {k-1}}p^kq^x-k$$

几何分布和超几何分布

再以伯努利分布为讨论的基础,仅更改一处,即把伯努利过程中的放回抽样(with replacement)改成不放回抽样(without replacement),即可得到超几何分布。
以负伯努利分布为讨论的基础,仅更改一处,即k设为1,表示成功一次。即不断重复伯努利试验,直到第一次成功就结束,得到了几何分布。

伯努利分布,负伯努利分布的命名

  • 为什么叫伯努利分布
    因为伯努利展开式$$(q+p)^n$$的n+1个项对应伯努利分布取各个值时的情况。
  • 为什么叫负伯努利分布
    因为展开式$$p^k(1-p)^(-k)$$中的每个项对应$$b^*(x;k,p)$$取x=k, k+1, k+2, …时的情况。