每天学习一个命令:df 查看磁盘剩余空间

之前也介绍过 di disk information,不过系统默认不带,需要自己安装,如果遇到没有权限安装时,就可以使用 df 来查看当前机器剩余磁盘空间。

df 全称 disk filesystem,用于显示 Linux 系统磁盘利用率。

使用

直接使用 df ,显示设备名称、总块数、总磁盘空间、已用磁盘空间、可用磁盘空间和文件系统上的挂载点。

Filesystem     1K-blocks      Used Available Use% Mounted on
udev             8126360         0   8126360   0% /dev
tmpfs            1629376     75432   1553944   5% /run
/dev/sdb1      240230912 185617700  42387064  82% /
tmpfs            8146864    546884   7599980   7% /dev/shm
tmpfs               5120         4      5116   1% /run/lock
tmpfs            8146864         0   8146864   0% /sys/fs/cgroup
/dev/loop1         83712     83712         0 100% /snap/core/4206
/dev/loop2           128       128         0 100% /snap/anbox-installer/17
/dev/loop4        259584    259584         0 100% /snap/electronic-wechat/7
cgmfs                100         0       100   0% /run/cgmanager/fs
tmpfs            1629376        72   1629304   1% /run/user/1000
/dev/sda3      723180576     70584 686351464   1% /media/user/add8bd89-da2a-4573-ac6e-7ec44f8c5414
/dev/loop5         84096     84096         0 100% /snap/core/4327
/dev/loop3         95872     95872         0 100% /snap/slack/6
/dev/loop6         88704     88704         0 100% /snap/core/4407

df -h 可以显示比较友好的输出

Filesystem      Size  Used Avail Use% Mounted on
udev            7.8G     0  7.8G   0% /dev
tmpfs           1.6G   74M  1.5G   5% /run
/dev/sdb1       230G  178G   41G  82% /
tmpfs           7.8G  534M  7.3G   7% /dev/shm
tmpfs           5.0M  4.0K  5.0M   1% /run/lock
tmpfs           7.8G     0  7.8G   0% /sys/fs/cgroup
/dev/loop1       82M   82M     0 100% /snap/core/4206
/dev/loop2      128K  128K     0 100% /snap/anbox-installer/17
/dev/loop4      254M  254M     0 100% /snap/electronic-wechat/7
cgmfs           100K     0  100K   0% /run/cgmanager/fs
tmpfs           1.6G   72K  1.6G   1% /run/user/1000
/dev/sda3       690G   69M  655G   1% /media/mi/add8bd89-da2a-4573-ac6e-7ec44f8c5414
/dev/loop5       83M   83M     0 100% /snap/core/4327
/dev/loop3       94M   94M     0 100% /snap/slack/6
/dev/loop6       87M   87M     0 100% /snap/core/4407

df -hT 其中 -T 参数显示文件类型 ext4 等等

Filesystem     Type      Size  Used Avail Use% Mounted on
udev           devtmpfs  7.8G     0  7.8G   0% /dev
tmpfs          tmpfs     1.6G   74M  1.5G   5% /run
/dev/sdb1      ext4      230G  178G   41G  82% /

df -ih 显示 inodes

Filesystem     Inodes IUsed IFree IUse% Mounted on
udev             2.0M   520  2.0M    1% /dev
tmpfs            2.0M   888  2.0M    1% /run
/dev/sdb1         15M  1.8M   13M   12% /

相关

查看磁盘占用 du


2017-04-12 linux , df , disk , 磁盘空间

Celery 使用介绍

Celery 简单来说就是一个分布式消息队列。简单、灵活且可靠,能够处理大量消息,它是一个专注于实时处理的任务队列,同时也支持异步任务调度。Celery 不仅可以单机运行,也能够同时在多台机器上运行,甚至可以跨数据中心。

Celery 中比较关键的概念:

  • worker: worker 是一个独立的进程,任务执行单元,它持续监视队列中是否有需要处理的任务;
  • broker: broker 消息传输中间件,任务调度队列,接收生产者发出的消息,将任务存入队列,broker 负责协调客户端和 worker 的沟通。客户端向队列添加消息,broker 负责把消息派发给 worker。
  • 任务模块:包含异步任务和定时任务,异步任务通常在业务逻辑中被触发并发往任务队列,定时任务由 celery beat 进程周期性发送
  • 任务结果 backend:backend 存储任务执行结果,同消息中间件一样,需要由其他存储系统提供支持

一个典型的 Celery 使用场景就是,当用户在网站注册时,请求可以立即返回而不用等待发送注册激活邮件之后返回,网站可以将发送邮件这样的耗时不影响主要流程的操作放到消息队列中,Celery 就提供了这样的便捷。

安装 Celery

直接使用 python 工具 pip 或者 easy_install 来安装:

$ pip install celery

安装 Broker

Celery 支持多种 broker, 但主要以 RabbitMQ 和 Redis 为主,其他都是试验性的,虽然也可以使用, 但是没有专门的维护者。如何在 RabbitMQ 和 Redis 之间选择呢?

RabbitMQ is feature-complete, stable, durable and easy to install. It’s an excellent choice for a production environment.

Redis is also feature-complete, but is more susceptible to data loss in the event of abrupt termination or power failures.

Celery 官方明确表示推荐在生产环境下使用 RabbitMQ,Redis 存在丢数据的问题。所以如果你的业务可以容忍 worker crash 或者电源故障导致的任务丢失,采用 redis 是个不错的选择,本篇就以 redis 为例来介绍。

Celery 对于 redis 的支持需要安装相关的依赖,以下命令可以同时安装 celery 和 redis 相关的依赖,但是 redis server 还是必须单独安装的。

$ pip install -U celery[redis]  # -U 的意思是把所有指定的包都升级到最新的版本

Celery 的配置和使用

Celery 本身的配置项是很多的,但是如果要让它跑起来,你只需要加一行配置:

BROKER_URL = 'redis://localhost:6379/0'

这一行就是告诉 celery broker 的地址和选择的 redis db,默认是 0。接下来用个很简单的例子来介绍 celery 是如何使用的:

# task.py
from celery import Celery

broker = 'redis://127.0.0.1:6379/0'

app = Celery('tasks', broker=broker)

@app.task()
def add(x, y):
   return x + y

上述代码创建了一个 celery 的实例 app,可以通过它来创建任务和管理 workers。在上面的例子中,我们创建了一个简单的任务 task,它返回了两个数相加后的结果。然后启动 celery 服务,通过它来监听是否有任务要处理。

$ celery worker -A task -l info
  • -A 选项指定 celery 实例 app 的位置,本例中 task.py 中自动寻找,当然可以直接指定 celery worker -A task.app -l info
  • -l 选项指定日志级别, -l--loglevel 的缩略形式

其他更多选项通过 celery worker –-help 查看

调用任务或者发送消息

然后我们再打开一个 shell 窗口,进入 python 控制台去调用 add 任务:

>>> from task import add
>>> add.delay(1, 2)
<AsyncResult: 42ade14e-c7ed-4b8d-894c-1ca1ec7ca192>

delay()apply_async 的简写,用于一个任务消息(task message)。我们发现 add 任务并没有返回结果 3,而是一个对象 AsyncResult,它的作用是被用来检查任务状态,等待任务执行完毕或获取任务结果,如果任务失败,它会返回异常信息或者调用栈。

我们先尝试获取任务的执行结果:

>>> result = add.delay(1, 2)
>>> result.get()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/lib/python2.7/dist-packages/celery/result.py", line 169, in get
    no_ack=no_ack,
  File "/usr/local/lib/python2.7/dist-packages/celery/backends/base.py", line 604, in _is_disabled
    'No result backend configured.  '
NotImplementedError: No result backend configured.  Please see the documentation for more information.

报错了:No result backend configured. 错误信息告诉我们没有配置 result backend。因为 celery 会将任务的 状态或结果保存在 result backend,result backend 的选择也有很多,本例中依然选用 redis 作为 result backend。

我们修改 task.py 的代码,添加上 result backend 的设置,保存后重启 celery worker。

# task.py
...
app = Celery('tasks', backend='redis://localhost', broker='redis://localhost//')
...

然后重新调用 add task:

>>> from task import add
>>> result = add.delay(1,2)
>>> result.get()
3

Celery Flower

flower 是一个 celery 的监控工具,它提供了一个图形用户界面,可以极大的方便我们监控任务的执行过程, 执行细节及历史记录,还提供了统计功能。

flower 安装

pip install flower

or:

easy_install flower

flower 使用简介,首先启动通过命令行启动 flower 进程:

flower -A proj --port=5555

然后打开浏览器 http://localhost:5555/

celery flower

Celery 任务类型

apply_async

调用一个异步任务,这也是最常用的任务类型之一,delay 与它的作用相同,只是 delay 不支持 apply_async 中额外的参数。该方法有几个比较重要的参数,在实际应用中会经常用到:

countdown: 任务延迟执行的秒数,默认立即执行; eta:任务被执行的绝对时间

crontab 计划任务

Celery 同样也支持定时任务:

from datetime import timedelta
from celery.schedules import crontab

app.conf.beat_schedule = {
    # Executes every Monday morning at 7:30 A.M
    'add-every-monday-morning': {
        'task': 'tasks.add',
        'schedule': crontab(hour=7, minute=30, day_of_week=1),
        'args': (20, 20),
    },
    # execute every ten minutes
   'every_ten_minutes': {
        'task': 'worker.cntv.cntv_test',
        'schedule': timedelta(minutes=10),
        'args': ('args1',),
        'options': {
            'queue': 'queue_name'
        }
    },
}

要启动定时任务,需要启动一个心跳进程,假设

celery beat -A celery_app.celery_config -s /path/to/celerybeat-schedule -l info

其中 -s 参数指定 celerybeat 文件保存的位置。beat 主要的功能就是将 task 下发到 broker 中,让 worker 去消费。

取消队列中任务

取消队列中任务,可以使用命令行,也可以导入 celery app 然后使用 control()

celery -A proj -Q queue_name purge      # 取消队列 queue_name 中的任务
# or
from proj.celery import app
app.control.purge()

From:stackoverflow

reference


2017-04-10 celery , python , queue , task , distribution

电影网站评分机制

年前的时候喉舌媒体批评豆瓣,猫眼等评分太低影响了票房,而导致16年的年度票房目标没有达到,广电很生气,后果很严重。可是豆瓣存在了那么多年,那么多的电影,在院线上映的,还是不上映的,从来也没有听说过 IMDB 或者 烂番茄的评分会影响到总体的票房。虽然得分的多少或多或少的会对票房有所影响,可这难道是豆瓣,或者 IMDB 或 烂番茄这样的影评网站应该承担的责任吗? 制片公司,发行商,甚至细化到导演,演员,剧本,在国内甚至可以拉上审查来负责,动不动删掉个14分钟,谁还愿意花了冤枉钱去大荧幕看一个不完整的片子呢?真正的影迷 大概会愿意花个机票钱去看一个完整版吧。

当然也不想过多的吐槽,或许被“认证”也才能证明豆瓣的评分也算良心吧。这里就看看国内玩几家影评站对网站打分的计分规则。其实早在很早就将计算的公式记录在了记事本里面,一直没有整理。而现在想要来整理一下,也是感觉豆瓣评分在一定程度上没有想象的真实,看过一部被恶意差评的国产片,看后感觉并不是5分多的水平,后来看评论才知道其中的某一位演员的黑粉恶意差评才导致这样的结果,而看一些长评论确实客观很多。或许是差评的人,没那么多的时间来写长评吧。所以就像那篇评论中说的那样,“中国电影市场的正常发展,不仅需要好的导演,好的编剧,好的演员,还需要好的观众”。

BGM,找资料时偶得,为某一期奥斯卡缅怀逝去的人时的背景音乐

豆瓣

先来说一说我使用最多的豆瓣,豆瓣也是评分规则中最简单的,豆瓣不人工干预评分,而一部电影的最终得分就是由每个用户的打分的加权平均,举个例子,一个用户打5星,一个用户打3星,一个用户打1星,那么这部片子就是(5+3+1)/3 也就是3星,6分。

豆瓣最后得分的具体公式1

其中, $x_1$ 表示打1颗星的人数,$x_2$ 表示打2颗星的人数,以此类推。由该公式能够看出,豆瓣的评分是很简单的计算,而至少一颗星(2分)的最低评分,也无形中提高了影片的评分,因为豆瓣根本不存在0分的电影,哦,不,还是有的。其实,豆瓣一直是一个满分8分的机制,那些超过8分的电影,是一定不会差的。所以曾经有段时间,找不到片子看的时候就直接找8分以上的片子看。

screenshot-area-2017-04-08-154156

豆瓣的评分机制简单粗暴,在降低用户打分思考的时候,也会造成用户对一部影片的看法截然不同,尤其是在恶意刷分时,会导致最后的评分波动较大。曾经有人开过玩笑说过豆瓣的评分图案,r 型(5星占大多数)的为口碑爆棚的好片,P 型为普通好片,b 型为普通烂片,而 C 型是水军刷出来的烂片,还有 L 型是多少水军都刷不出来的超级烂片。现在想来还是依然非常好玩。

时光网

时光网的存在感近两年被慢慢的抹去,但还依然半死不活的存在,时光网和豆瓣的评分机制一样,都是加权平均,只是时光网采用的是10分制,也就是用户有10个选择,用户需要话时间在评分的分数上,更多的选择,使得绝大部分用户选择中间段进行评分,因而导致最终的评分呈现中庸状态,同样无法真正体现出一部电影的真正得分。

而这样的十分制同样会导致在遭受大规模恶意打分(无论是好评还是差评)之后直接在最终结果中明显体现。

IMDB

IMDB 是国外最大的电影资料站,大家经常提到的 IMDB TOP 250,也就是在该站上评分最高的 250 名。他采用贝叶斯算法,具体的公式2

其中:

  • WR,加权得分 weighted rating
  • R,本影片的平均得分 rating
  • v,评分人数 votes
  • m,基准票数,进入 IMDB Top 250 的最小票数
  • C,站点所有电影的平均分

这个公式的目的是为了让得分更加偏向于平均分,如果投票越多,评分就越接近真实的平均分,否则就越接近所有电影的平均分。而这个公式的唯一人为设定的参数就是基准票数。而这个参数的设定也正是为了解决如何让冷门和热门影片在得分上具有可比性。冷门片不会因为爱好者而导致评分异常高,这个问题也是豆瓣经常遇到的问题,一些冷门韩综,日剧,韩剧在评分上都有一定的偏高。

而关于 IMDB 这个公式是怎么防止恶意刷分,有兴趣可以了解一下当年《蝙蝠侠》和《教父》的往事:

烂番茄

烂番茄主要是专业影评人士评价汇总,和 IMDB 和 豆瓣这样单纯由网名进行投票的评分制度有些不同。而烂番茄通过新鲜度来对电影进行评价,而这里的新鲜度并不是实际意义上的评分,而是由影评人对该影片正面打分的比例来决定的,若正面的评价超过60%以上,该部作品将会被认为是“新鲜”(fresh)。如果正面评价超过 75 % ,那么该作品会得到“Certified Fresh” 的评价,而如果一部作品的正面评价低于60%,那么该作品会被标示为“腐烂”(rotten)。影评人只有两个选项,正面和反面。

烂番茄和其他影评网站的最大区别是,他突出的是人群对一部电影持有的主流观点,而不是一个让每个人都感同身受的数值。

Metacritic

Metacritic 是一个综合性评定网站,影评只是该网站其中的一个小模块,该网站上影评人多以纸媒为主。 Metacritic 的评分主要从主流媒体和专业影评机构聚合而来,这些影评人和其供职的机构大多在影评方面具有公信力,比如《卫报》、《纽约时报》、《时代周刊》等等。但是并不是每一个机构和影评人都给出一个确切的分数。 Metacritic 具体做法是,如果来源有具体评分则使用来源评分,来源有星级打分则换算成百分制,如果来源影评只提供文字,然后他们自己去找人阅读影评,根据读完的感受给分。3

比较

各家网站都有各家的好坏,豆瓣的评分机制是最简单高效的,这也是绝大多数的系统惯常的做法。但正是这样的机制使得刷分异常容易,大批量的差评或者好评能在短时间内影响影片总体的分数。另外一个比较严重的问题就是,无法在冷门片和热门片之间比较,这也是豆瓣官方博客在文章中提及的,热门影片能在短时间内获得几十万的评分,但是一些冷门片,或者一些上映时间比较久远的电影可能难以达到这么多的评分,这样就会导致热门片和冷门片在评分上无法比较。口碑比较好的热门片可能因为观众口味不一而导致评分稍中庸,而冷门片可能因为资深影迷而导致评分过高。因此在豆瓣看评分时,一般还需要看一下评分人数。而最近我也会看一下长评论,毕竟愿意花时间来评价一部影片,远比花1秒打个评分要来的认真。

而 IMDB 的评分方式一定程度上解决了冷门影片和热门影片评分上的差异,但是选择基准票数却也需要经过不断的调整,IMDB 历史上也经历过变化,根据该数据,阈值从 3000 票提升到了 25000 票,这次变换也相应的造成了最后得分的变化,尤其是影响了得到25000票以下,并且得分较高的影片。可以说只有当影片的评分人数足够多时,基准票数的影响才会减至最小,而对于票数比较少的影片,就相当于一次洗牌。

而对于烂番茄和国内的猫眼专家评分,其实一定意义上说代表着专业领域的人士意见,这些评论都值得一读,但是更多的需要自己的看法,只有最后形成自己的世界观那部分东西才真正属于自己。所以豆瓣和IMDB 对于我来说,一方面提供给我足够的信息,包括导演,演员,编剧等等,另一方面也是让我远离烂片,毕竟看一部烂片浪费的是自己的时间。

最后,引用数位时代中的一句话,“在美国,佳片会收到它应得的票房和好评作为奖赏,烂片就算进了电影院也不可能躲得开差评—-无论在报纸、电台还是在网络上。在美国,对电影的批评,也是言论自由保护的一部分”。

若差评不自由,则高分无意义。


2017-04-08 Movie , Douban , IMDB

每天学习一个命令:lsof 列出打开的文件

lsof 用于列出当前系统打开的文件 (list open files),在 Linux 中,任何事物都以文件的形式存在,通过文件不仅仅可以访问常规数据,还可以访问网络连接和硬件。所以如传输控制协议 (TCP) 和用户数据报协议 (UDP) 套接字等,系统在后台都为该应用程序分配了一个文件描述符,无论这个文件的本质如何,该文件描述符为应用程序与基础操作系统之间的交互提供了通用接口。因为 lsof 命令需要访问核心内存和各种文件,所以需要 root 用户执行。

简单使用

比如可以使用 lsof 来查看当前系统中 80 端口是否被占用

sudo lsof -i tcp:80
COMMAND     PID USER   FD   TYPE   DEVICE SIZE/OFF NODE NAME
docker-pr 14863 root    4u  IPv6 38693061      0t0  TCP *:http (LISTEN)

然后获取到 PID 之后可以用 lsof 来查看进程

sudo lsof -p 14863
COMMAND     PID USER   FD      TYPE   DEVICE SIZE/OFF       NODE NAME
docker-pr 14863 root  cwd       DIR      8,0     4096          2 /
docker-pr 14863 root  rtd       DIR      8,0     4096          2 /
docker-pr 14863 root  txt       REG      8,0  3329080      17531 /usr/bin/docker-proxy
docker-pr 14863 root  mem       REG      8,0  1868984      20743 /lib/x86_64-linux-gnu/libc-2.23.so
docker-pr 14863 root  mem       REG      8,0   138696      11625 /lib/x86_64-linux-gnu/libpthread-2.23.so
docker-pr 14863 root  mem       REG      8,0   162632      10738 /lib/x86_64-linux-gnu/ld-2.23.so
docker-pr 14863 root    0r      CHR      1,3      0t0       8085 /dev/null
docker-pr 14863 root    1w      CHR      1,3      0t0       8085 /dev/null
docker-pr 14863 root    2w      CHR      1,3      0t0       8085 /dev/null
docker-pr 14863 root    4u     IPv6 38693061      0t0        TCP *:http (LISTEN)
docker-pr 14863 root    5u  a_inode     0,12        0       8082 [eventpoll]
docker-pr 14863 root   12r      REG      0,3        0 4026531993 net

由以上的信息就能看出来我的机器中的 80 端口是 docker 占用的,docker 的位置在系统中的 /usr/bin/docker-proxy

lsof 输出各列信息的意义如下:

  • COMMAND:进程的名称
  • PID:进程标识符
  • PPID:父进程标识符(需要指定 -R 参数)
  • USER:进程所有者
  • PGID:进程所属组
  • FD:文件描述符,应用程序通过文件描述符识别该文件。如 cwd、txt 等

FD 的取值

  • cwd:表示 current work dirctory,即:应用程序的当前工作目录,这是该应用程序启动的目录,除非它本身对这个目录进行更改
  • txt :该类型的文件是程序代码,如应用程序二进制文件本身或共享库,如上列表中显示的 /sbin/init 程序
  • lnn:library references (AIX);
  • er:FD information error (see NAME column);
  • jld:jail directory (FreeBSD);
  • ltx:shared library text (code and data);
  • mxx :hex memory-mapped type number xx.
  • m86:DOS Merge mapped file;
  • mem:memory-mapped file;
  • mmap:memory-mapped device;
  • pd:parent directory;
  • rtd:root directory;
  • tr:kernel trace file (OpenBSD);
  • v86 VP/ix mapped file;
  • 0:表示标准输出
  • 1:表示标准输入
  • 2:表示标准错误

一般在标准输出、标准错误、标准输入后还跟着文件状态模式:r、w、u 等

  • u:表示该文件被打开并处于读取 / 写入模式
  • r:表示该文件被打开并处于只读模式
  • w:表示该文件被打开并处于
  • 空格:表示该文件的状态模式为 unknow,且没有锁定
  • -:表示该文件的状态模式为 unknow,且被锁定

介绍

在有了基本的概念之后来看 lsof 的参数

lsof  [ -?abChKlnNOPRtUvVX ] [ -A A ] [ -c c ] [ +c c ] [ +|-d d ] [ +|-D D ] [ +|-e s ] [ +|-E ] [ +|-f [cfgGn] ] [ -F [f] ] [ -g [s] ] [ -i [i] ] [ -k k ] [ +|-L [l] ] [ +|-m m
   ] [ +|-M ] [ -o [o] ] [ -p s ] [ +|-r [t[m<fmt>]] ] [ -s [p:s] ] [ -S [t] ] [ -T [t] ] [ -u s ] [ +|-w ] [ -x [fl] ] [ -z [z] ] [ -Z [Z] ] [ -- ] [names]

能看到非常多的选项,因此也能看到 lsof 的强大。

-i 选项查看链接

默认 : 没有选项,lsof 列出活跃进程的所有打开文件
-i : 列出网络连接

使用 lsof -i 来显示所有的链接,可以用来代替 netstat

sudo lsof -i
COMMAND     PID     USER   FD   TYPE   DEVICE SIZE/OFF NODE NAME
sshd       2972     root    3u  IPv4 18883553      0t0  TCP *:22 (LISTEN)
sshd       2972     root    4u  IPv6 18883562      0t0  TCP *:22 (LISTEN)
docker-pr 14852     root    4u  IPv6 38693034      0t0  TCP *:https (LISTEN)
docker-pr 14863     root    4u  IPv6 38693061      0t0  TCP *:http (LISTEN)

输出结果有缩略,也能看出来 22 的 SSH 端口,然后 http 默认的 80 端口,和 https 使用的 443 端口。如果要查看 IPv6 的流量可以使用 lsof -i 6

同样如果要查看 TCP UDP 连接信息,lsof -iTCPlsof -iUDP。再比如查看和本地 22 端口的连接 lsof -i:22

显示来自特定主机的连接,lsof -i@1.2.3.4 ,指定主机和端口 lsof -i@1.2.3.4:22

-p 选项查看指定进程

使用 -p 查看指定进程 ID 已打开的内容

lsof -p 10075

列出用户打开的文件

lsof -u einverne

查看 java 项目依赖的 jar

比如说如果系统中依赖的一个 jar 被发现有漏洞,比如说可以查看 fastjson 在系统中有没有使用。

lsof -X | grep fastjson

2017-04-02 lsof , linux , file , command

如何找到一首歌的名字

“听歌识曲” 虽然听起来是一个简单的功能,却还依然发展了很多年。在无数的网站评论中看到求求某某片段中的背景音乐,其实绝大部分情况下都可以通过听歌识曲来找到,剩下的也绝大部分可以通过电视,电影的OST找到。所以这篇文章就是介绍下目前市面上我使用过比较好用的一些听歌识曲的应用,这些应用解决了我95%以上,找到喜欢的背景音乐的需求。

授人以鱼不如授人以渔

在电视、综艺、或者大街上听到一首喜欢的背景音乐却不知道歌名的时候,我下意识的会拿出手机来打开网易云音乐,当然这个能够解决一大半的问题,因为经过多年的使用,其实网易的识别还是有些准确的,至少对于绝大部分流行的中日韩欧美歌曲基本都能够识别出来,更甚至之前看请回答系列,识别出来了上世纪六七十年代发行的歌曲。但是听歌识曲有一些小小的弊端,一般情况下需要环境噪声比较小,并且经验给我的感觉是需要一段较长有歌词的完整的片段才能够快速的找到精确的歌。所以如果周围噪音比较大,或者电影中人物有对白时,尽量记住连续一段完整的歌词,然后通过搜索引擎搜索歌词来获取歌名,一般情况下我会加上 lyrics + 记住的歌词 来搜索,基本也能够找到想要的歌。

听歌识曲应用

网易云音乐

网易云音乐

SoundHound

soundhound

Shazam

shazam

Siri

siri

还有其他的酷狗,微信摇一摇啊,就不介绍了。

横向对比

应用名 支持平台 语种 特别功能
网易云音乐 Android/iOS 中韩英 识别出即可播放(当然要排除版权问题)
SoundHound Android/iOS 英中 可以通过哼唱来识别
Shazam Android/iOS 在低功耗情况下一直识别音乐
Siri iOS 随系统自带

2017-03-24 music , collection , bgm , song

使用 mitmdump 记录 HTTP 流量

之前一篇文章 主要是使用 mitmproxy 进行抓包,但是其实 mitmproxy 自带的 feature 远远不止于抓包,使用 mitmdump 可以自定义脚本来修改 response 返回,或者将请求结果 dump 到本地以便于之后的分析。

之前的那篇文章在 mitmdump 的时候只是简单的介绍了一下功能,并没有展开,所以有了这篇文章。mitmdump 可以理解为 mitmproxy 的命令行版本,他提供了 tcpdump 类似的功能来查看,记录,甚至编程改写 HTTP 流量。

保存流量

开启代理模式,并将所有的请求写入文件

mitmdump -w outfile

过滤保存的流量

-n 参数表示不开启代理, -r 表示读入 infile,然后将将所有 match ~m post POST 流量写入 outfile 文件中。

mitmdump -nr infile -w outfile "~m post"

关于过滤的规则,可以具体参考这里

客户端重放请求

使用 -n 参数不开启代理,然后 -c filename 参数进行重放。

mitmdump -nc outfile

甚至是可以重放请求,然后将结果保存到另外的文件中

mitmdump -nc srcfile -w dstfile

添加脚本

可以在启动 mitmdump 时添加自定义的脚本用来改写请求。

mitmdump -s examples/add_header.py

如果脚本文件带有参数,则需要在 -s 参数后面增加双引号,比如 mitmdump -s "add_header.py custom_header"

组合使用

将这些参数组合一起使用

mitmdump -ns examples/add_header.py -r srcfile -w dstfile

从 srcfile 文件中加载流量,然后使用特定的脚本改写,然后将结果写入 dstfile 文件中。

实例

将请求结果保存到本地文件

#!/usr/bin/env python
# -*- coding: UTF-8 -*-
import os
import sys

from mitmproxy import flowfilter, http, ctx


# events run ordr: start, request, responseheaders, response, error, done

class Filter:
    MOVIE_TOP250 = '/api/v2/subject_collection/movie_top250/items'

    def __init__(self, path):
        self.folder_path = path
        # 构造一个 HTTP response code
        self.http_code_ok = flowfilter.parse('~c 200')
        if not os.path.exists(self.folder_path):
            os.makedirs(self.folder_path)
        # 构造一个 URL 过滤器
        self.douban_path = flowfilter.parse(
            '~u frodo.douban.com/api/v2/subject_collection/movie_top250/items')

    # @concurrent  # Remove this and see what happens
    def request(self, flow: http.HTTPFlow):
        if flowfilter.match(self.douban_path, flow):
            if flow.request.host:
                ctx.log(
                    "handle request: %s%s" % (
                        flow.request.host, flow.request.path))

    def response(self, flow: http.HTTPFlow):
        if flowfilter.match(self.http_code_ok, flow):
            """只有 200 状态进入"""
            ctx.log('code %s' % flow.response.status_code)
            if flowfilter.match(self.MOVIE_TOP250, flow):
                if flow.response.content:
                    pretty_path = str(flow.request.path.rstrip())
                    pretty_path = pretty_path.replace('/', '_') \
                        .replace(':', '_') \
                        .replace('&', '_')
                    pretty_path = pretty_path[:250] + '.json'
                    res_content = flow.response.content.decode('utf-8')
                    path = os.path.join(self.folder_path, pretty_path)
                    with open(path, 'w+') as f:
                        f.write(res_content)


def start():
    if len(sys.argv) != 2:
        raise ValueError('Usage: -s "save_response.py path"')
    # 保存结果的 folder 路径
    return Filter(sys.argv[1])

将上面的脚本执行

/usr/local/bin/mitmdump -s "save_response.py /tmp/response_result/"

然后在结果路径中就能得到请求的豆瓣 Top250 电影结果,然后再对电影结果进行解析即可。

或者可以将请求的 webp 或者 jpg 的图全都保存到另外的文件夹中

pretty_url = flow.request.pretty_url
if pretty_url.endswith(".webp") or pretty_url.endswith('.jpg'):
    # ctx.log('pretty url %s' % flow.request.pretty_url)
    filename = os.path.join(self.folder_path,
                            os.path.basename(pretty_url))
    with open(filename, 'wb') as f:
        f.write(flow.response.content)

然后只要浏览过的图片就全都保存在本地的文件夹中了。

按规则过滤请求

mitm 的过滤都是依靠 flowfilter.py 来实现的,可以匹配的规则有如下

    The following operators are understood:

        ~q          Request
        ~s          Response

    Headers:

        Patterns are matched against "name: value" strings. Field names are
        all-lowercase.

        ~a          Asset content-type in response. Asset content types are:
                        text/javascript
                        application/x-javascript
                        application/javascript
                        text/css
                        image/*
                        application/x-shockwave-flash
        ~h rex      Header line in either request or response
        ~hq rex     Header in request
        ~hs rex     Header in response

        ~b rex      Expression in the body of either request or response
        ~bq rex     Expression in the body of request
        ~bs rex     Expression in the body of response
        ~t rex      Shortcut for content-type header.

        ~d rex      Request domain
        ~m rex      Method
        ~u rex      URL
        ~c CODE     Response code.
        rex         Equivalent to ~u rex

从这些匹配规则就能看出来过滤规则可以非常精细,比如过滤结果为 500 的请求,比如过滤 header 中 content-type 为某种类型的请求,比如按照正则去匹配 URL 等等。

#!/usr/bin/env python
# -*- coding: UTF-8 -*-
import sys

from mitmproxy import flowfilter, http, ctx


# events run ordr: start, request, responseheaders, response, error, done

class Filter:
    MOVIE_TOP250 = '/api/v2/subject_collection/movie_top250/items'

    def __init__(self):
        # 构造一个 URL 过滤器
        self.douban_path = flowfilter.parse(
            '~u frodo.douban.com/api/v2/elendil/home_timeline')
        # 构造一个 HTTP response code
        self.http_code_ok = flowfilter.parse('~c 200')
        # Domain
        self.my_domain = flowfilter.parse('~d douban.com')
        # Method
        self.filter_mathod = flowfilter.parse('~m POST')
        # content-type header
        self.filter_content_type = flowfilter.parse('~t json')

    # @concurrent  # Remove this and see what happens
    def request(self, flow: http.HTTPFlow):
        if flowfilter.match(self.douban_path, flow):
            if flow.request.host:
                ctx.log(
                    "handle request: %s%s" % (
                        flow.request.host, flow.request.path))

    def response(self, flow: http.HTTPFlow):
        if flowfilter.match(self.http_code_ok, flow):
            """只有 200 状态进入"""
            ctx.log('code %s' % (flow.response.status_code))
        if flowfilter.match(self.my_domain, flow):
            """只有匹配域名"""
            ctx.log('domain %s' % flow.response.text)
        if flowfilter.match(self.douban_path, flow):
            """只有 特定 url 可以进入"""
            ctx.log('douban text' + flow.response.text)
            ctx.log('douban reason ' + flow.response.reason)
            ctx.log('douban http version ' + flow.response.http_version)
        pretty_url = flow.request.pretty_url
        if flowfilter.match(self.MOVIE_TOP250, flow):
            if flow.response.content:
                res_content = flow.response.content.decode('utf-8')
                ctx.log("content: " + res_content)


def start():
    if len(sys.argv) != 2:
        raise ValueError('Usage: -s "dump.py"')
    return Filter()

reference


2017-03-16 mitmproxy , mitmdump , mitmweb , mitm , charles

推荐网站之快捷键:快捷键 ShortcutWorld Wiki

今天推荐的网站是 ShortcutWorld,同时也是整理笔记的一篇文章。

推荐网站也可以看看之前的推荐,AlternativeTo 可以寻找各种产品的代替品,现在产品关闭的越来越多了,很好用的产品被关闭非常心疼,还有很多通过图片寻找字体等等,都可以参考。而今天要推荐的是收集和整理了很多桌面应用,网页应用快捷键的 Wiki 网站。

官网地址: https://www.shortcutworld.com/

为什么会知道这个网站的呢?主要是当时寻找 Linux mint 的快捷键搜到了 这个页面 ,然后在这个页面上,竟然发现了看官方文档都没找到的录屏快捷键 Ctrl+Alt+Shift+R</kdb> ,竟然看这个 Wiki 的时候发现了。

不过其实用来找快捷键最快的方法还是官方文档和 Google 啦。这个网站适合不定时看一看来学习一下新的技能。


2017-03-11 website , 推荐网站

Openwrt 平均负载

Openwrt 在 Luci 后台很显眼的位置有三个不断刷新的数字,其实这个数字是“平均负载”(Load Average)的意思,这是 Linux 操作系统衡量系统负载和稳定性的重要参数。

平均负载

在 Linux 及各种 Linux 衍生版(包括 Openwrt)中,都可以使用如下命令查看系统平均负载。

uptime 命令:

root@OpenWrt:/# uptime
 21:22:57 up 19:21,  load average: 1.30, 2.44, 2.38

top 命令:

Mem: 119632K used, 6740K free, 0K shrd, 41348K buff, 48152K cached
CPU:   0% usr   0% sys   0% nic  72% idle   0% io   0% irq  27% sirq
Load average: 0.33 1.82 2.17 1/76 16075

w 命令,查看当前系统有谁登录,都在干什么:

$ w
 21:25:04 up 3 days, 11:07,  8 users,  load average: 0.48, 0.52, 0.59
USER     TTY      FROM             LOGIN@   IDLE   JCPU   PCPU WHAT
einverne tty7     :0               Tue10    3days  2:19m  2.39s cinnamon-session --session cinnamon
einverne pts/1    ev               Tue10   37.00s  2.45s  0.32s ssh root@192.168.1.1

直接查看 load average:

$ cat /proc/loadavg 
0.56 0.48 0.56 1/1264 5890

前三个数字表示平均进程数量外,后面一个分数,分子为正在运行进程数,分母表示系统进程总数,最后一个数字表示最近运行进程ID。

load average 显示的3个数字,分别表示:系统在过去1分钟、5分钟、15分钟内运行进程队列中的平均进程数量。 正常情况下的时候就是0到1之间,大于1的时候,表示系统已经没有多余资源了,有些队列就需要等待处理。

短时间大于1是没有影响的,特别是第一个一分钟的数据,但是如果后面两个数据,特别是最后一个,经常大于0.7,就说明,有可能路由器超负荷了。

交通流量来比喻

有一篇 Understanding Linux CPU Load 将负载比喻交通流量,很形象,非常值得一看。

具体来说:

  • 0.00-1.00 之间的数字表示此时路况非常良好,没有拥堵,车辆可以毫无阻碍地通过。
  • 1.00 表示道路还算正常,但有可能会恶化并造成拥堵。此时系统已经没有多余的资源了,管理员需要进行优化。
  • 1.00-*** 表示路况不太好了,如果到达2.00表示有桥上车辆一倍数目的车辆正在等待。这种情况你必须进行检查了。

多核CPU的话,满负荷状态的数字为 “1.00 * CPU核数”,即双核CPU为2.00,四核CPU为4.00。

reference


2017-03-10 Openwrt , Linux

每天学习一个命令:dig 查询 DNS 解析结果

dig 命令是一个用于询问 DNS 域名服务器的灵活的工具。它执行 DNS 搜索,显示从接受请求的域名服务器返回的答复。

常见用法

查找 www.google.com 的 A 记录

dig www.google.com

输出如下

; <<>> DiG 9.10.3-P4-Ubuntu <<>> google.com
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 34834
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4000
;; QUESTION SECTION:
;google.com.			IN	A

;; ANSWER SECTION:
google.com.		75	IN	A	172.217.24.206

;; Query time: 0 msec
;; SERVER: 127.0.0.1#53(127.0.0.1)
;; WHEN: Thu Jun 21 16:52:41 CST 2018
;; MSG SIZE  rcvd: 55

命令前两行显示 dig 版本,参数,以及隐式参数 +cmd 意味信息本身会被打印

  • DNS 应答消息数据, QUERY 未遇到错误 NOERROR ,还有 事务 id 34834
  • flags 行表示该消息是查询响应 (qr 标志),并且原始查询中期望使用递归 (rd 标志),而且由响应服务器 (ra 标志) 提供

指定 dns 服务器查找 www.google.com 的 A 记录

dig @8.8.8.8 www.google.com

查找 ip 地址对应的主机名

dig -x 8.8.8.8

外延 nslookup

使用 nslookup 查询域名 A 记录

nslookup www.google.com

2017-03-10 dig , linux , network , dns , command

Openwrt 设置

在上一篇中讲了如何刷Openwrt,这一篇主要讲一些 Openwrt 的东西,以及配置相关的内容。我有一个主路由器,设置分配的局域网地址为 192.168.1.x,给内网中分配的地址也是 192.168.1.x 开头。

但是 Openwrt 默认为 AP 模式,我想要从主路由器 LAN 口连出到新的这个 Openwrt 路由器上,那么便得设置 Openwrt 路由器为 Router 模式以便于级联。

在设置路由器模式之前先来看看这几个接口,否则怎么都不会明白怎么配置的。

br-lan, eth0, eth0.1

Openwrt 的接口名字太多,最早接触路由器的时候只知道 WLAN 口,LAN 口,后来接触 Linux 才慢慢知道 eth0, lo 等等接口,但是在 Openwrt 上接口中突然冒出来一堆看着名字熟悉,却不知道什么作用的接口。今天配置 LAN ,WAN 口时还差点把 MR12U 搞砖,幸亏昨天刷了不死 boot。

可以使用 ifconfig 来查看设备,常见的几个端口:

  • lo 虚拟设备端口,自身回环设备,一般指向 127.0.0.1
  • ra0 rai0 成对出现,无线设备,对应各自的 SSID,分别是 2.4G 和 5G
  • pppoe-wan 虚拟设备,常见的拨号宽带上网
  • eth0 物理网卡, eth0.1 或者 eth0.2 都是从此设备虚拟而出。
  • br-lan 虚拟设备,用于 LAN 口设备桥接,用来使得多个虚拟或物理网络接口的行为好像他们仅有一个网络接口一样。目前路由器普遍将有线LAN口(一般四个)和WIFI无线接口桥接在一起作为统一的LAN。可以使用 brctl show 来查看使用情况。
  • eth1 如果路由器有两块网卡,一般 eth1 作为 WAN 口
  • wlan0 一般是无线网卡,无线端口

可以使用如下命令来查看 br-lan 配置

brctl show

bridge name bridge id       STP enabled interfaces
br-lan      7fff.64098005e1bb   no      eth0.1 rai0 ra0

br-lan = eth0.1 + rai0 + ra0,即将有线LAN口和无线网统一划分为 LAN。

下面张图比较直观:

openwrt-interface

更改内网地址

LAN 是设置局域网内的相关属性,可以设置内网的IP,桥接的端口。比如我们默认使用192.168.1.1访问,可以修改为192.168.9.1,生效后内网的ip就会变掉。LAN口的协议为【静态地址】。下一次访问路由器管理页面就需要使用 192.168.9.1 了。

Openwrt 修改 LuCI 语言

System->Software->在Filter栏里面输入 -zh-cn 点击搜索

找到 luci-i18n-base-zh-cn 点击前面的安装。然后去设置语言即可。

设置路由器模式

路由器模式也就是最常见的无线模式,通过有线连接至外网并发射无线提供局域网络。由于默认只有 LAN 接口,我们需要添加 WAN 接口。

Openwrt interface screenshot-area-2017-03-08-212320

Openwrt morning配置只有上述图片的 LAN 口,下面的 WAN 口通过如下方法添加。

点击下方的“添加新接口”

screenshot-area-2017-03-08-212349

为了便于区分,接口名称建议使用 WAN。按照网络接入类型,选择 DHCP(从外网自动获取ip地址),静态ip或者PPPoE拨号即可。其它设置如图,请勿选择“在多个接口上创建桥接”,最后点击提交。

screenshot-area-2017-03-08-212405

提交后选择刚刚创建的 WAN 接口,点击“防火墙设置”,选择 WAN 并保存即可。

screenshot-area-2017-03-08-212422

这时需要再次回到 LAN 接口,点击编辑。

screenshot-area-2017-03-08-212808

选择“物理设置”,确保“桥接接口”为选中,接口中不选中“以太网适配器”。确认后保存并应用,至此所有配置完成,连接网线即可使用。

无线桥接模式

无线中继模式使用无线网络接入互联网,并生成一个新的 SSID。无线桥接模式无需更改有线连接接口设置。打开无线接口设置,点击搜索。在自动弹出的设置页面中,填写上级无线密码。新网络的名称使用默认 wwan 即可。防火墙区域选择 wan,在这里请勿选择“重置无线配置”。在保存并应用后就完成了所有设置。

无线AP模式

无线AP模式多应用于公共场所,所有无线设备将被桥接至以太网接口,由上级网关负责 DHCP。在设置完成后 AP 所在路由器将无法访问。

首先打开 LAN 接口或者 WAN 接口,选择“物理设置”,确保“桥接接口”为选中。在下方接口选中“以太网适配器”以及“无线网络”,保存并应用即可。

至于无线加密设置以及 DHCP 设置较为简单,自行在“网络”分类下查找即可。

reference

  • https://roov.org/2014/10/openwrt-setup-guide/
  • https://blog.phpgao.com/openwrt-interface.html
  • http://wizju.com/post/102/
  • http://wizju.com/post/94/
  • http://unix.stackexchange.com/questions/57309/how-can-i-tell-whether-a-network-interface-is-physical-device-or-virtual-alia
  • Linux 网络接口 https://wiki.openwrt.org/zh-cn/doc/networking/network.interfaces
  • https://wiki.openwrt.org/zh-cn/doc/uci/network/switch

2017-03-09 Openwrt , Linux , Opkg

电子书

Google+

最近文章

  • 由 WebM 格式学习常见的容器和编码格式 因为使用 YouTube 所以接触到了 WebM 格式,这个格式 Google 开源的一个媒体容器格式,常见的文件后缀名是 .webm,他设计的目标是为了给 HTML5 提供视频和音频。Google 发起的 WebM 项目还有一个姊妹项目 WebP 是提供图像编码的。BSD 协议开源。1 https://en.wikipedia.org/wiki/WebM ↩
  • Jupyter 简单使用 Jupyter 是一个为了支持多语言交互式编程的项目, Jupyter Notebook 是一个开源的网络程序,允许用户创建和分享包含代码,视图,方程式,文本的文档。
  • GraphQL 初识 在开发服务端接口的时候接触到 GraphQL 这个名词,故而有了这篇文章。因为初始,所以整理过程难免有些错误和疏漏,请留言告知。在我们面对一个新的名词,或者一门新的技术时,了解的过程可以分成这么几部分,他是什么,他解决了什么问题,他和目前同类型的技术相比优势在哪里,这样几个部分去看也就能够比较粗略,但是快速的了解一样新东西了。所以这篇文章的组织结构也以这样的方式进行。
  • go 语言学习笔记 1 并发 Go 语言在语言级别支持协程,叫 goroutine。Go 语言标准库提供的所有系统调用 (syscall) 操作,当然也包括所有同步 IO 操作,都会出让 CPU 给其他 goroutine
  • 跨平台开源卡片记忆工具 anki 一开始的时候我无法用一句话来形容这个软件,大部分人将他称为背单词软件,部分人有拿他作为知识笔记软件,甚至有人拿他来学习乐谱,诗歌,但总之如果要用简单的话来描述这个软件,那么跨平台必定是关键词,另外一个关键词就是卡片,在另外一个就是循环记忆,那么至于卡片上承载什么样的内容,就完全由用户来决定了。