使用 Python BeautifulSoup4 快速获取网页内容

BeautifulSoup4 能够帮助我们从 HTML 或 XML 文件中提取数据

安装

pip install beautifulsoup4
解析器 使用方法 优势 劣势
Python 标准库 BeautifulSoup(markup, “html.parser”) Python 的内置标准库 执行速度适中 文档容错能力强 Python 2.7.3 or 3.2.2) 前 的版本中文档容错能力差
lxml HTML 解析器 BeautifulSoup(markup, “lxml”) 速度快 文档容错能力强 需要安装 C 语言库
lxml XML 解析器 BeautifulSoup(markup, “xml”) 速度快 需要安装 C 语言库
html5lib BeautifulSoup(markup, “html5lib”) 最好的容错性 以浏览器的方式解析文档 生成 HTML5 格式的文档 速度慢 不依赖外部扩展

使用

加载

from bs4 import BeautifulSoup
soup = BeautifulSoup(html)

Beautiful Soup 将复杂 HTML 文档转换成一个复杂的树形结构,每个节点都是 Python 对象,所有对象可以归纳为 4 种:

  • Tag,HTML 中的一个个标签,有 name 和 attr
  • NavigableString,标签中内容
  • BeautifulSoup, 文档全部内容
  • Comment,一个特殊类型的 NavigableString 对象

find_all() 方法和 select() 方法各有各的优劣,find_all 方法能支持正则,而 select 方法可以使用 CSS 属性选择器。

.string 和 .text 区别

Tag 对象上调用 .string 会返回 NavigableString 类型对象,而 .text 会获取所有子节点内容的组合,.text 返回的是 Unicode 对象。

对于

<td>Some Table Data</td>
<td></td>

在第二个 <td> 标签中 .string 会返回 None,而 .text 会返回空的 unicode 对象

对于 .string

  • 如果标签只包含文本,则直接返回文本
  • 如果标签包含一个单一的子标签,则返回子标签内容
  • 如果标签不包含标签,或者包含多个标签,则返回 None
  • 如果标签既包含文本,也包含子标签,并且二者文本内容不一致,则返回 None

而对于 .text 则简单很多,会返回子标签及所有文本的级联。

比如

<td>some text</td>
<td></td>
<td><p>more text</p></td>
<td>even <p>more text</p></td>

.string 会返回

some text
None
more text
None

.text 会返回

some text

more text
even more text

BeautifulSoup 不同解析器比较

BeautifulSoup 支持很多种 HTML 解析器,包括 Python 自带标准库,还有其他 lxml 等等第三方模块。

解析器 使用方法 优点 缺点
html.parser BeautifulSoup(markup,”html.parser”) Python 标准库,速度快,兼容性好(2.7.x 和 3.2.x) 无法在 2.7.3 之前和 3.2.2 之前版本使用
lxml BeautifulSoup(markup, “lxml”) 速度快,兼容性好 外部依赖
lxml’s XML BeautifulSoup(markup, ‘lxml-xml’) 或者 ‘xml’ 速度快,支持 XML 外部依赖
html5lib BeautifulSoup(markup, ‘html5lib’) 兼容性好,HTML5 合法 速度慢,外部依赖

个人一般使用 html.parser 但是如果遇到不兼容版本,那也只能 pip install lxml 然后使用了。

  • [[etree]]

reference


2016-06-21 python , beautifulsoup4 , crawler , html , parser

每天学习一个命令:iftop 流量监控

在类 Unix 系统中可以使用 top/htop 查看系统资源、进程、内存占用等信息。查看网络状态可以使用 netstatnmap 等工具。若要查看实时的网络流量,监控 TCP/IP 连接等,则可以使用 iftop。

iftop 是类似于 top 的实时流量监控工具。

官方网站:http://www.ex-parrot.com/~pdw/iftop/

iftop 可以用来监控网卡的实时流量(可以指定网段)、反向解析 IP、显示端口信息等。

安装

apt-get install iftop

或者使用源码编译安装

Debian 上安装所需依赖包:

apt-get install flex byacc  libpcap0.8 libncurses5

下载 iftop

wget http://www.ex-parrot.com/pdw/iftop/download/iftop-0.17.tar.gz

tar zxvf iftop-0.17.tar.gz
cd iftop-0.17
./configure
make && make install

常用的参数:

-i      设定监测的网卡,如:# iftop -i eth1
-B      以 bytes 为单位显示流量(默认是 bits),如:# iftop -B
-n      使 host 信息默认直接都显示 IP,如:# iftop -n
-N      使端口信息默认直接都显示端口号,如:# iftop -N
-F      显示特定网段的进出流量,如# iftop -F 10.10.1.0/24 或# iftop -F 10.10.1.0/255.255.255.0
-h(display this message),帮助,显示参数信息
-p      使用这个参数后,中间的列表显示的本地主机信息,出现了本机以外的 IP 信息;
-b      使流量图形条默认就显示;
-f      这个暂时还不太会用,过滤计算包用的;
-P      使 host 信息及端口信息默认就都显示;
-m      设置界面最上边的刻度的最大值,刻度分五个大段显示,例:# iftop -m 100M

交互快捷键,进入 iftop 画面后的一些操作命令(注意大小写)

按 h 切换是否显示帮助;
按 n 切换显示本机的 IP 或主机名;
按 s 切换是否显示本机的 host 信息;
按 d 切换是否显示远端目标主机的 host 信息;
按 t 切换显示格式为 2 行 /1 行 / 只显示发送流量 / 只显示接收流量;
按 N 切换显示端口号或端口服务名称;
按 S 切换是否显示本机的端口信息;
按 D 切换是否显示远端目标主机的端口信息;
按 p 切换是否显示端口信息;
按 P 切换暂停 / 继续显示;
按 b 切换是否显示平均流量图形条;
按 B 切换计算 2 秒或 10 秒或 40 秒内的平均流量;
按 T 切换是否显示每个连接的总流量;
按 l 打开屏幕过滤功能,输入要过滤的字符,比如 ip, 按回车后,屏幕就只显示这个 IP 相关的流量信息;
按 L 切换显示画面上边的刻度;刻度不同,流量图形条会有变化;
按 j 或按 k 可以向上或向下滚动屏幕显示的连接记录;
按 1 或 2 或 3 可以根据右侧显示的三列流量数据进行排序;
按 `<` 根据左边的本机名或 IP 排序;
按 `>` 根据远端目标主机的主机名或 IP 排序;
按 o 切换是否固定只显示当前的连接;
按 f 可以编辑过滤代码
按!可以使用 shell 命令
按 q 退出监控。

使用实例

直接运行:

sudo iftop

界面上面显示的是类似刻度尺的刻度范围,为显示流量图形的长条作标尺用的。

中间的<= =>这两个左右箭头,表示的是流量的方向。需要注意的是默认情况下显示的单位是 bit/s

  • TX:发送流量 Transmit
  • RX:接收流量 Receive
  • TOTAL:总流量
  • Cum:运行 iftop 到目前时间的总流量
  • peak:流量峰值
  • rates:分别表示过去 2s 10s 40s 的平均流量

监控指定网卡

先使用 ifconfig 查看当前机器网卡,然后使用 -i 参数

iftop -i eth0

以 MB 显示流量

为了可读性,可以使用 -B 来以 MB mega byte 字节来显示流量:

sudo iftop -B

reference


2016-06-06 linux , command , iftop , network , monitor

Ubuntu 下安装 Gradle 及简单使用

Gradle 的核心是基于 Groovy 的 领域特定语言 (DSL),目的是为了代替 XML 繁多的构建工具。

自动安装

可以使用这个 PPA

sudo add-apt-repository ppa:cwchien/gradle
sudo apt-get update
sudo apt install gradle

或者根据官网的教程 手动安装。

使用


2016-06-05 gradle , ubuntu , linux , usage , build-system , jvm , java

Kindle 使用小技巧及常见问题

整理 Evernote 笔记的时候偶然看到这篇文章,总结自己使用 Kindle 一年来的小小经验,以及一些 Tips。

注册 Kindle 邮箱

这个功能非常实用,不然能够节省连接数据线的时间,更重要的是这个活用这个邮箱能够自动化完成很多事情,可是遗憾的事,很多人并不知道这个福利。所以建议在拿到手之后的第一件事情就是查看这个邮箱,设置中 Send-to-KindleE-mail 中查看。

Kindle 可以享受的两个最容易被忽略的功能是:

  • 在线文档存储,亚马逊为每位用户提供至少 5G 的云存储空间
  • 在线文档格式转换,支持格式包括

    • Microsoft Word (.DOC, .DOCX)
    • HTML (.HTML, .HTM)
    • RTF (.RTF)
    • JPEG (.JPEG, .JPG)
    • Kindle Format (.MOBI, .AZW)
    • GIF (.GIF)
    • PNG (.PNG)
    • BMP (.BMP)
    • PDF (.PDF)
    • 附件大小不超过 50MB(压缩之前)
    • 附件中文档个数在 25 个以内
    • Kindle.com 收件人个数不得超过 15 个
    • 文档默认会保存在云端
    • 如果 60 天用户都没有下载,则会删除上传文件

如果有 WiFi 或 3G 的话,注册 Kindle 可以在 Kindle 设备上完成,打开 WiFi(Home-> Menu -> Turn Wireless On),然后在 Home -> Menu -> Settings-> Registration 中,按照提示完成即可。没有 WiFi 或 3G 的话,则享受不了这两个服务。

要使用这两个服务,需要两步。首先,要知道自己的 Kindle 邮件地址(姓名 @kindle.com),可以在 Home-> Menu-> Settings 的第二页里看到,在 Send-to-Kindle E-mail 选项里面。但是,为了保护用户的私有空间不被别人用垃圾填满,亚马逊还要求使用已经被用户许可的邮箱地址发往此邮箱,否则就会拒绝接收。许可邮箱的方法是:

  • 先到管理 Kinlde 页面,需要使用注册 Kindle 的账号登陆
  • 在左侧导航栏里单击 Personal Document Settings
  • 在右侧 Approved Personal Document E-mail List 标题的最后单击 Add a new approvede-mail address
  • 输入 email 地址后单击 Add Address 即可,可以添加多个邮箱

完成以上操作后,就可以享受这两个服务了,使用添加到许可列表的邮箱,以附件形式发送文档到自己的 Kindle 邮件地址(name@kindle.com),就可以把文档存储到云端。Kindle 在线时就会自动下载云端文件。如果想将文档转换为 Kindle 内置格式(mobi),需要在邮件标题内注明 convert(即邮件标题写“convert”即可),亚马逊就会为您转换为 Kindle 内置文档格式并发送到你的 Kindle 设备,阅读非常方便,转换过程可能会比较慢。

单击左侧导航栏内的 Personal Documents 可以管理云端文件。

使用剪贴板功能做笔记

使用过程中,读书最重要的就是记录了,阅读永远是别人的东西,如果没有经过大脑转变成自己的内容,那永远只是存在书本上。而 Kindle 上做笔记也是非常容易的,并且数字化让一切都能够被检索并标注,Kindle 让这个过程更加方便了。在 Kindle 中长按文字选中之后会弹出标注,笔记等几个选项,而所有这些操作的内容都会被保存到 Kindle 设备上”documents”里名叫“My Clippings.txt” 的文件中。这个文件以一定的格式记录所有的笔记内容,直接查看非常不方便,于是就有人做了这样的一个工具。

地址:https://www.clippings.io/

将笔记,标注导出的网站。从 Kindle 中找到 clippings 文件之后上传到该网站,就能够非常直观的查看所有笔记。

关闭 Kindle 屏保和主页界面的特惠信息

因为我使用的是美亚账号,自动出现的特惠信息大多数是我并不关心的,因此

> 设置 -> 设备选项 -> 个性化您的 Kindle -> 高级选项 -> 特惠

关闭即可。如果无法关闭,我记得当时我就是联系了亚马逊客服才关闭的:

联系亚马逊客服:https://www.amazon.cn/gp/help/customer/contact-us

禁止 Kindle 自动锁屏

正常情况下经过一段时间,Kindle 会自动锁屏,锁屏默认情况下是一些 Kindle 书店的推广,如果要禁止自动锁屏,可以在搜索框中输入

~ds

disable screensaver 的缩写。这个操作在重启之后就会失效。

Kindle 无法连接 WiFi

Kindle 不能连接 WiFi 的三个原因,虽然在没办法的情况下以下三个方法或许有用,但是大部分的情况其实就是 GFW 屏蔽了 Kindle 联网验证的地址,其实和 Android 在检测 WiFi 时屏蔽了 Google 服务器出现的感叹号一样,系统向一个 URL 请求,没有收到回复自然认为没有成功连接到互联网,于是就报错,所以在尝试以下三种方式之前,请确保翻墙状态。

  1. 路由器频段问题

这个问题实际上手机也有。有一阵子我手机也连不上我哥家的 WiFi,后来通过网络搜索才知道频段问题。那时候是说频段超过 11 手机就无法连接 WiFi,后来我把频段改小之后就解决问题了。而一般能用手机连接 WiFi,Kindle 不能连接的一般不会是这个问题。

如何改变频段,Google 之。基本现在这个年代,看一下无线路由器的说明书就会设置的。

技术资料见:

http://en.wikipedia.org/wiki/List_of_WLAN_channels

In the USA, 802.11 operation in the channels 12 and 13 is actually allowed under low powered conditions. The 2.4 GHz Part 15 band in the US allows spread-spectrum operation as long as the 50-dB bandwidth of the signal is within the range of 2400–2483.5 MHz which wholly encompasses both channels 12 and 13. A Federal Communications Commission (FCC) document clarifies that only channel 14 is forbidden and furthermore low-power transmitters with low-gain antennas may legally operate in channels 12 and 13.However, channels 12 and 13 are not normally used in order to avoid any potential interference in the adjacent restricted frequency band, 2483.5–2500 MHz, which is subject to strict emission limits set out in 47 CFR §15.205.

问题也许就是这样产生的:你笔记本所能搜到的 WIFI 信号来自正工作于 12/13/14 频段的路由器,因此你的 Kindle 搜不到无线信号。

为什么路由器工作于 12/13/14 频段呢?基于抗干扰的理由,人为指定的可能性很小,然而在无线路由器的设置中(至少是家用),频段这一项可以设为“自动选择”,这样每次路由器重启都回按照自己的算法随意选择一个频段,也许刚好就选在了“12/13/14”上。

这或许也是 WIFI 连网不稳定现象的根源,某些 Kindle 连不上无线网络,而折腾下路由器重启后,Kindle 又可以连网了。

经 GOOGLE 搜索,发现欧洲人也有类似的问题。见 http://www.mobileread.com/forums/showthread.php?t=100081

由此对 Kindle 连网问题做个小小的推测,如果真的能解决问题,请大家多转给需要的人

  1. DHCP 服务器地址池问题

听闻 DHCP 服务器是让路由器可以自动分配 ip 的东西,但是把地址池『个人理解为分配的 ip 的范围』如果在 192.168.1.100 以上,Kindle 就不能连接 WiFi『当然这是 Kindle 的问题,因为手机电脑都可以连接的,不过我不知道 Kindle 自身要怎么改,或许不能改,又或许可以通过在 Kindle 上设置静态 IP?]

其实解决这个问题有个更方便的方法,既然 Kindle 改不了,咱们就改下路由器的 DHCP 服务器地址池呗,把开始地址改为 192.168.1.2,结束地址改成 192.168.1.99『其实也不用固定这样,只要最后一个在 1-100 之间就可以了!

  1. 接下来就是运营商问题了

前两个问题我都解决后,我发现还是有时候会连接不上 WiFi,于是在又查了查,发现了一个方法,为什么这个方法能解决我不知道为什么,但是真的有效!!!

在 pc 上新建一个新文件,名为WiFi_NO_NET_PROBE,同时把后缀名删掉,让它变成一个无格式文件。Kindle 连接 pc,把新建的文件放进 kindke 的根目录,断开 Kindle 之后重启 Kindle。

关于充电

充电方法:Kindle 可以用数据线连接电脑充电。也可以用数据线连接充电头,在插座上充电。Kindle 电量不足时,灯是橙黄色的,充满以后灯会变成绿色。

充电时长:每次充电时间大约是 2-3 小时。首次充电充满即可,不需要充很长时间,有人充了一个晚上,十几个小时,然后就不能开机了,送修说主板电路烧了。平时使用时 Kindle 还剩差不多百分之二十的时候开始充电,对 Kindle 最好。因为 Kindle 很长时间不用不充电,可能会出现缺电现象,造成机器假死。充电的时候最好不要看书,不要使用 kindle,不然 kindle 电池不耐用。

更换字体

Kindle 自定义字体仅支持 OpenType(OTF)和 TrueType(TTF)这两种字体格式

字体文件复制到 Kindle 根目录下的“fonts”文件夹中

个人比较喜欢的一些用于阅读的字体,汉字作为方块字还是非常有美感的,通常情况下会选择楷书(在默认无法更换字体的情况下),而如果支持更换字体则会选择方正北魏楷书,而如果是宋体的话会选择,方正标雅宋体。

导入字典

Kindle 的字典一般都是 mobi 格式,需要注意。至于字典看个人喜好,这可以单独写另外一篇文章了,我个人一般用牛津和朗文,加上一部 GitHub 开源的收录词条很多的开源字典。

Kindle 字典下载到电脑本地后,导入 Kindle 字典的详细步骤:

  • 连接数据线,进入到 documents 文件夹,打开 documents 文件夹后,找到 dictionaries 文件夹,并打开
  • 将电脑本地的 Kindle 字典拖入到 dictionares 文件夹内
  • 最后,安全退出 kindle 盘符

截屏方法

Kindle 的截屏方法,不同 Kindle 不同,我只有 Paperwhite,所以:Kindle Paperwhite 截屏:先点上面出菜单,再同时左上 + 右下。屏幕会闪烁一下,说明截图成功。

截下来的图片会保持在 documents 这个文件夹里面,可以连接电脑拷贝出来。

以下未验证: Kindle3、Kindle DXG,截屏是同时按住:Alt+Shift+G。屏幕会闪一下,截屏就成功了。 Kindle4、Kindle5 截屏:同时按住键盘键和菜单键,屏幕会闪一下,截屏就成功了。 Kindle touch 截屏:按住 home 键,点屏幕,等几秒,反正 5 秒肯定可以了,松开 Home。

电子书格式

mobi, azw

mobi 和 azw 格式的推手主要是 Amazon,这两种电子书格式的发展很大程度上依靠 Amazon 这个巨大的内容提供商及其电子书阅读器 Kindle 的流行普及。它们同属亚马逊的私有格式,没有本质的区别,可以简单的这样理解,mobi 是比较老的一种格式,而 azw 只是 mobi 的另一种形式而已,也可以理解为 mobi 加了个壳,亚马逊利用它对电子书做 DRM 版权保护。

目前市面上的 mobi 文件大部分是来自两种途径:epub、pdf 或者 txt 转换成的 mobi,从 Amazon 商店流出来的 mobi。前者没什么好说的,后者要么是 Amazon 官方制作,要么就是自出版作者通过 KDP (Kindle Direct Publishing,作者可以绕过出版社直接在 Amazon 上发售电子书 ) 平台发布,通过 KDP 平台发布时,作者只需要上传 Word 文档,其他的事情也是 Amazon 官方来做,从而保证了,mobi 文件的规范程度。

azw3

azw3 的本质是 KF8,是随着 2011 年 Amazon 推出 Kindle Fire 平板时一起推出的。它填补了 Mobi 对于复杂排版支持的缺陷,支持很多 HTML5(目前尚不支持 HTML5 的视频和音频标签)和 CSS3 的语法,这就大大改善了原来 mobi 或 azw 内容排版上的一些缺陷,单纯从读者的角度来讲,是不输 epub 格式的。目前从 Amazon 购买的书,大部分已经是 azw3 格式了,而以前主流的 mobi 格式则越来越少,它正逐渐取代 mobi 成为 Kindle 电子书的主流格式。

epub

下面是维基百科对 epub 的一段定义:

EPUB(Electronic Publication 的缩写,电子出版)是一种电子图书标准,由国际数字出版论坛(IDPF)提出;其中包括 3 种文件格式标准(文件的附文件名为.epub),这个格式已取代了先前的 Open eBook 开放电子书标准。

epub 格式对于复杂的排版,图表,公式等元素的兼容性比 mobi 格式好很多,在脚本,公式,矢量图形的支持方面也强过 mobi 格式,现阶段 epub 格式的优势体现在图文混排、图片嵌入字体等,未来可预测的优势是 epub 格式对于声音,影像等多媒体内容互动的支持上。

epub 格式是开放标准,所以在开发工具上也会有更大的选择,像 Sigil、Calibre、Jutoh 等软件都可以让用户自助制作 epub 格式电子书,但因为良莠不齐的制作也导致一个问题:大量的 epub 文件其实是不符合标准,无法保证在所有支持 epub 的硬件和软件上都可以顺利阅读,这就和 iOS 系统和 Android 系统的区别有些相似。

找书技巧

Kindle 使用官方市场必然是件很不错的选择,但是其实有些方式来的更加方便,并且也能弥补官方市场书记不全的弊端。

Kindle10000

微信书籍推送:Kindle10000 注:该微信号已经不再能够推送书籍

自用上这个服务,Kindle 就活了起来,想起想看的书名,找到公众号,搜索推送,即使 Kindle 不在身边,下一次联网再同步即可。这个公众号在他们的简洁上这么写着:“一个被书籍改变命运的程序员领着志愿者做的免费项目”。而他的使用也非常简单,绑定 Kindle 邮箱之后,在聊天框输入书名查找,然后找到想要的书,点一下推送搞定,资源丰富。这个比我之前在一些 Kindle 资源网站上找或者百度搜方便多了。

Kindle 伴侣

地址:http://kindlefere.com

Kindle 伴侣,这是我至今也还一直订阅的少数 Kindle 相关网站之一,它的《每周一书》坚持更新也是很值得称赞的。

Kindle 饭

地址:http://www.kindlefan.cn/

Kindle 饭,有很多 Kindle 使用的文章,技巧,相关工具,很棒的网站,建议订阅。

160604 更新,这个网站竟然不存在了,我只能从 Web Archive 找找他们存在的痕迹,但真的感谢他们曾经的文字。

周读

地址:http://www.ireadweek.com/index.php/Index/index.html

都是百度网盘的资源

漫画

地址:http://www.pixvol.com/

推送漫画到 Kindle,还是非常全的

一下都是一些资源网站:

  • http://readfree.me/ 一个图书分享网站
  • https://book.einverne.info 我自己写的图书分享网站
  • https://www.mlook.mobi/ 精校电子书,资源下载
  • http://zaoshu.so/ 枣书,付费电子书价格对比,可以获取各大网站提供的免费公共电子书
  • https://www.cnepub.com/ epub 掌上书苑
  • http://www.jiumodiary.com/ 搜索电子书
  • http://readcolor.com/ 读远,电子书库
  • http://blah.me/ Google+ 郁也风整理的书籍
  • https://www.dogear.cn/ 狗耳朵 全文 RSS 和微信公众号推送

如果使用 InoReader 可以订阅下面我制作的 bundle , 我订阅了一些 Kindle 相关的文章。

地址:http://www.inoreader.com/bundle/0014cd6370e9

其他的地址我以后会在这篇文章 中更新。

reference


2016-06-04 kindle , book , 阅读 , amazon , ebook

Spring 中 ThreadPoolTaskExecutor 配置

The Spring Framework provides abstractions for asynchronous execution and scheduling of tasks with the TaskExecutor and TaskScheduler interfaces, respectively.

The Spring TaskExecutor abstraction

Spring’s TaskExecutor interface is identical to the java.util.concurrent.Executor interface.

接口 ExecutorService 的几个常用方法:

  • submit() 有返回值的任务使用
  • execute() 无返回值的任务使用
  • getActiveCount() 当前活跃线程数

TaskExecutor types

Spring 提供了一系列的预置的 TaskExecutor 的实现,几乎能满足日常的所有需求。

  • SimpleAsyncTaskExecutor 不复用任何线程,每次调用都新建线程。但是它提供并发数量限制,当调用超过限制时,会阻塞直到线程池中有空余。
  • SyncTaskExecutor 非异步执行,没有实现异步调用,主要用于简单的测试等等不需要多线程的场景
  • ConcurrentTaskExecutor 该类适配了 java.util.concurrent.Executor,只有在 ThreadPoolTaskExecutor 满足不了需求时才考虑用这个类。
  • SimpleThreadPoolTaskExecutor 这个类的实现实际上是 Quartz 的 SimpleThreadPool,它会监听 Spring 生命周期的回调。典型的使用场景是当你需要一个线程池需要和 Quartz 和 non-Quartz 组件共享
  • ThreadPoolTaskExecutor 最常用的线程池,它在 java.util.concurrent.ThreadPoolExecutor 的基础上暴露了一些 bean 的配置,并把它包装在 TaskExecutor 中。如果你要适配 java.util.concurrent.Executor,推荐可以自定义 ConcurrentTaskExecutor
  • WorkManagerTaskExecutor This implementation uses the CommonJ WorkManager as its backing implementation and is the central convenience class for setting up a CommonJ WorkManager reference in a Spring context. Similar to the SimpleThreadPoolTaskExecutor, this class implements the WorkManager interface and therefore can be used directly as a WorkManager as well.

ThreadPoolTaskExecutor Config

Spring 线程池 ThreadPoolTaskExecutor 通过 XML 方式配置:

<bean id="taskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
    <!-- 线程池维护线程的最少数量 -->
    <property name="corePoolSize" value="5" />
    <!-- 允许的空闲时间 -->
    <property name="keepAliveSeconds" value="200" />
    <!-- 线程池维护线程的最大数量 -->
    <property name="maxPoolSize" value="10" />
    <!-- 缓存队列 -->
    <property name="queueCapacity" value="20" />
	<!-- 线程名前缀 -->
	<property name="threadNamePrefix" value="taskExecutor-thread-"/>
    <!-- 对拒绝 task 的处理策略 -->
    <property name="rejectedExecutionHandler">
        <bean class="java.util.concurrent.ThreadPoolExecutor$CallerRunsPolicy" />
    </property>
</bean>

属性字段说明:

  • corePoolSize:核心线程数,线程池维护的最少线程数,不管创建后空闲与否,除非设置了 allowCoreThreadTimeOut
  • maxPoolSize:线程池维护线程的最大数量
  • keepAliveSeconds:存活时间,允许的空闲时间,如果经过 keepAliveTime 时间后,超过核心线程数的线程还没有接受到新的任务,那就回收
  • queueCapacity:缓存队列
  • rejectedExecutionHandler:对拒绝 task 的处理策略

    • AbortPolicy,用于被拒绝任务的处理程序,它将抛出 RejectedExecutionException。
    • CallerRunsPolicy,用于被拒绝任务的处理程序,它直接在 execute 方法的调用线程中运行被拒绝的任务。
    • DiscardOldestPolicy,用于被拒绝任务的处理程序,它放弃最旧的未处理请求,然后重试 execute。
    • DiscardPolicy,用于被拒绝任务的处理程序,默认情况下它将丢弃被拒绝的任务。

将任务添加到线程池时:

  • 如果线程池中的线程数量小于 corePoolSize,即使线程池中的线程都处于空闲状态,也要创建新的线程来处理被添加的任务。
  • 如果线程池中的线程数量等于 corePoolSize,但是缓冲队列 workQueue 未满,那么任务被放入缓冲队列。
  • 如果线程池中的线程数量大于 corePoolSize,缓冲队列 workQueue 满,并且线程池中的数量小于 maxPoolSize,建新的线程来处理被添加的任务。
  • 如果线程池中的数量大于 corePoolSize,缓冲队列 workQueue 满,并且线程池中的数量等于 maxPoolSize,那么通过 handler 所指定的策略来处理此任务。也就是:处理任务的优先级为:核心线程 corePoolSize、任务队列 workQueue、最大线程 maxPoolSize,如果三者都满了,使用 handler 处理被拒绝的任务。
  • 当线程池中的线程数量大于 corePoolSize 时,如果某线程空闲时间超过 keepAliveTime,线程将被终止。这样,线程池可以动态的调整池中的线程数。

SimpleAsyncTaskExecutor

SimpleAsyncTaskExecutor 每次都会 newThread()

protected void doExecute(Runnable task) {
	Thread thread = (this.threadFactory != null ? this.threadFactory.newThread(task) : createThread(task));
	thread.start();
}

SyncTaskExecutor

SyncTaskExecutor 在 spring-core-xxx.jar 包中。

SyncTaskExecutor 同步执行

@Override
public void execute(Runnable task) {
	Assert.notNull(task, "Runnable must not be null");
	task.run();
}

ConcurrentTaskExecutor

ConcurrentTaskExecutor 类在 spring-context-xxx.jar 包中。

ConcurrentTaskExecutor 类中通过 TaskExecutorAdapter 适配了 Executor

private Executor concurrentExecutor;
private TaskExecutorAdapter adaptedExecutor;

提交任务时直接通过 adapter 来提交:

public void execute(Runnable task, long startTimeout) {
	this.adaptedExecutor.execute(task, startTimeout);
}
@Override
public Future<?> submit(Runnable task) {
	return this.adaptedExecutor.submit(task);
}

@Override
public <T> Future<T> submit(Callable<T> task) {
	return this.adaptedExecutor.submit(task);
}

SimpleThreadPoolTaskExecutor

SimpleThreadPoolTaskExecutor 类在 spring-context-support-xxx.jar 包中。

SimpleThreadPoolTaskExecutor 继承了 Quartz 的 SimpleThreadPool,

@Override
public <T> Future<T> submit(Callable<T> task) {
	FutureTask<T> future = new FutureTask<T>(task);
	execute(future);
	return future;
}

队列的选择

ArrayBlockingQueue

数组实现的有长度限制的阻塞队列,FIFO 先进先出。

LinkedBlockingQueue

链表组成的有界队列,FIFO,默认长度是 Integer.MAX_VALUE,如果默认创建该队列一定特别小心容量问题。

PriorityBlockingQueue

优先级排序的无界队列,默认自然序,可自定义实现 compareTo() 方法来定义排序规则(不能保证同优先级的顺序)。

DelayQueue

使用 PriorityBlockingQueue 实现的延迟无界队列,创建元素时,可以指定延迟时间。

SynchronousQueue

不存储元素的阻塞队列,每一个 put 操作都需要等待 take。

LinkedTransferQueue

链表组成的无界阻塞队列,多了 transfer 和 tryTransfer 方法。

LinkedBlockingQueue

链表组成的双向阻塞队列,头部和尾部都可以添加或移除元素,多线程并发时可以将锁的竞争降一半。

选择

对于如何设置线程池中线程的数量,《Java 并发编程实战》中作者给出了一个公式:

 Number of threads = Number of Available Cores * (1 + Wait time / Service time)

说明:

  • wait time 用来表示任务中 IO 花费的时间,比如等待 HTTP 回应,这里的 wait time 也包括 thread 在 WAITING/TIMED_WAITING 状态的时间
  • service time, 任务真正处理的时间,比如解析 HTTP 回应内容等等

wait time / service time 这个比率又被称为 blocking coefficient。

对于 CPU 密集型任务,核心线程数可以设置为,CPU 核心数 + 1,在计算密集型的任务中,blocking coefficient 接近于 0,所以线程数约等于 CPU 核心数。但为什么要 +1 呢?

《Java 并发编程实战》一书中给出的原因是:即使当计算(CPU)密集型的线程偶尔由于页缺失故障或者其他原因而暂停时,这个“额外”的线程也能确保 CPU 的时钟周期不会被浪费。

在运行时可以通过 Runtime.availableProcessors 来获取 CPU 核心数。

int numOfCores = Runtime.getRuntime().availableProcessors();

如果任务是 IO 密集型,则可以适量的调大核心线程数,因为这个时候 wait time / service time 就会响应的增大。

举例

假如有一个工作线程来响应一个微服务,序列化一个 JSON,并执行一些规则。微服务的响应时间是 50ms,处理时间是 5ms,然后将应用部署到一台双核处理器的机器上,那么根据上面的公式:

2 * ( 1 + 50 / 5)  = 22    // 理想的线程池核心线程数

这个例子是一个极端简单的举例,除去 HTTP 线程池外,应用也还有可能有 JDBC 连接线程池,JMS 请求线程池等等。如果现实应用中也遇到各种不同的场景,可以针对不同的场景,使用多个线程池,然后针对不同的使用场景进行调优。

假使有多个线程池,可以在公式中新增一个 Target CPU utilization,取值范围是 [0-1], 1 表示线程池会充分利用处理器。

 Number of threads = Number of Available Cores * Target CPU utilization * (1 + Wait time / Service time)

Little’s Law

通过上面的解释,可以得到一个理想的核心线程数,可以得到一个理论上的核心数上限。但是我们如何知道并发的线程数如何影响延迟 (latency) 和吞吐量 (throughput)?

Little’s law 可以同来回答这个问题。这条定律认为,系统中的请求数等于它们到达的速度乘以处理单个请求所需的平均时间。我们可以利用该公式来计算需要多少并发线程来处理给定吞吐量并且要求延迟的场景。

L = λ * W

L - 并发处理的请求数
λ – 长期的平均到达率 long-term average arrival rate (RPS)
W – 处理单个请求的平均时间 the average time to handle the request (latency)

使用该公式,可以计算出系统的容量,需要多少实例同时运行才可以处理给定数量的请求,以及让处理的时间在一个稳定的范围。

回到上面的例子,我们有一个服务平均处理时间是 55ms,50ms wait time 和 5ms service time,核心线程数是 22 。

应用 Little’s Law 公式:

22 / 0.055 = 400 // the number of requests per second our service can handle with a stable response time

总结

上面提到的公式并不是银弹 (silver bullet) ,并不能解决所有遇到的问题。这个公式的问题在于,它注重于系统平均能够处理的请求数,所以并不适合于不同场景爆发式的流量情况。所以可以在设计系统时通过上面的公式计算出一个初始的设定,然后通过压测来调整。

reference


2016-05-26 spring , thread , thread-pool , queue

Spring BeanPostProcessor 使用

BeanPostProcessor 接口允许在 Spring Bean Factory 返回 Bean instance 时修改 Bean 的创建过程。这是影响 Bean 生命周期的一部分。

接口有两个方法:

public interface BeanPostProcessor {
    @Nullable
    default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
            return bean;
        }
    @Nullable
    default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }
}

Bean life cycle

要了解 BeanPostProcessor 接口就不得不提及 Bean 的生命周期。

Life cycle callbacks

Two groups:

  • Post-initialization
  • Pre-destruction

Life cycle:

  • Instantiation
  • Populate Properties
  • BeanNameAware’s setBeanName()
  • BeanFactoryAware’s setBeanFactory()
  • Pre-initialization BeanPostProcessors
  • InitializingBeans’ afterPropertiesSet()
  • Call custom init-method
  • Post-initialization BeanPostProcessors
  • Bean is ready to use ![[202101151422-how to become smarter]] Container is shutdown:

  • DisposableBean’s destroy()
  • Call custom destroy-method

Callback

Spring 提供了这些方法可以在生命周期过程中回调。

  • InitializingBean 和 DisposableBean
  • Spring 提供的一系列 *Aware 接口
  • 配置文件中自定义 init()distroy() 方法
  • 注解 @PostConstruct@PreDestroy

InitializingBean 和 DisposableBean

大致这样:

import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;

public class DemoBean implements InitializingBean, DisposableBean
{
	//Other bean attributes and methods

	@Override
	public void afterPropertiesSet() throws Exception
	{
		//Bean initialization code
	}

	@Override
	public void destroy() throws Exception
	{
		//Bean destruction code
	}
}

Aware interfaces

  • ApplicationContextAware 任何 bean 想要 ApplicationContext 启动时被通知可以实现该接口
  • ApplicationEventPublisherAware
  • BeanClassLoaderAware
  • BeanFactoryAware
  • BeanNameAware
  • BootstrapContextAware
  • LoadTimeWeaverAware
  • MessageSourceAware
  • NotificationPublisherAware
  • PortletConfigAware
  • PortletContextAware
  • ResourceLoaderAware
  • ServletConfigAware
  • ServletContextAware

Custom init() and destroy() methods

定义单个 Bean:

<beans>
 <bean id="demoBean" class="info.einverne.deme.DemoBean"
					init-method="customInit"
					destroy-method="customDestroy"></bean>
</beans>

全局定义:

<beans default-init-method="customInit" default-destroy-method="customDestroy">
    <bean id="demoBean" class="info.einverne.demo.DemoBean"></bean>
</beans>

@PostConstruct and @PreDestroy

  • @PostConstruct annotated method will be invoked after the bean has been constructed using default constructor and just before it’s instance is returned to requesting object.
  • @PreDestroy annotated method is called just before the bean is about be destroyed inside bean container.

BeanPostProcessor

@Slf4j
@Component
public class CustomBeanPostProcessor implements BeanPostProcessor {


  @Override
  public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
	log.info("BeanPostProcessor postProcessBeforeInitialization for:" + beanName);
	return bean;
  }

  @Override
  public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
	log.info("BeanPostProcessor postProcessAfterInitialization for:gg" + beanName);
	return bean;
  }
}

2016-05-26 spring , java , bean , spring-bean , spring-bean-lifecycle

gunicorn 使用

之前的文章使用 gunicorn 来部署 webpy 中简单的提到了 gunicorn 的使用。这篇文章就在官方文档的基础上学习下 gunicorn 的其他更多的用法。

基本的安装和参数就跳过了,这边讲下文档中很有用却不是常用的一些选项,如果要看基础使用可以去看之前的文章

配置文件

我们知道 gunicorn 能够直接使用命令行来启动,常见的参数

gunicorn -w 2 -b 0.0.0.0:5000 app:app --log-level info --access-logfile logfile.log --log-file error.log

当这样一路写下去就知道命令行非常难管理,所以 gunicorn 能够使用 config 文件来管理

gunicorn -c config.py app:app

这样就简单很多了。至于 config.py 文件格式如何,保证是一个 python 格式的文件,语法没有太大问题即可

import multiprocessing

bind = "127.0.0.1:8000"
workers = multiprocessing.cpu_count() * 2 + 1
backlog = 2048                           # int 范围在 64-2048 pending 的链接最大数
worker_class = 'gevent'
debug = True
pidfile = '/tmp/home.pid'
loglevel = 'info'
logfile = '/var/log/gunicorn/gun_debug.log'

其他的全部配置可以在 setting 中找到。

设计模式

gunicorn 的设计,官方这篇说明清楚的解释了 同步 worker 和 异步 worker 的区别,如果你的应用程序接口有大量的 IO 操作推荐使用 异步 worker。

重新加载配置

首先要知道 master worker 的 pid 发送 -HUP 信号

kill -HUP masterpid

如何测试性能,可以使用 hey,下面的命令发送了 10000 个请求,其中 100 是并发量。

hey -n 10000 -c 100 http://127.0.0.1:5000/

reference


2016-05-25 python , gunicorn , wsgi , http , unix , web , server

Pypi 国内镜像记录

官方 PyPI 源的 URL 为 https://pypi.org/simple

pip 临时换用国内的镜像

pip install -i https://pypi.tuna.tsinghua.edu.cn/simple some-package

或者设为默认:

pip install pip -U
pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple

或者修改配置文件:

vi ~/.config/pip/pip.conf

设为:

[global]
timeout = 60
index-url = https://pypi.tuna.tsinghua.edu.cn/simple

常用的国内 PyPI 镜像列表

  • 豆瓣 https://pypi.doubanio.com/simple/
  • 网易 https://mirrors.163.com/pypi/simple/
  • 阿里云 https://mirrors.aliyun.com/pypi/simple/
  • 清华大学 https://pypi.tuna.tsinghua.edu.cn/simple/

2016-05-23 pip , python , mirror

gnome do 技巧

自 Mint 开始才接触到 Gnome-do 这样一个神器,一句话介绍他的功能就是启动器,完全键盘操作的启动器。当然在启动应用之外还有很多扩展的功能,自开始使用 Gnome-do 开始几乎已经很少使用菜单开应用了。正如这篇 文章所讲使用了 Gnome-do 之后就会让 Windows 和 OS X 下的用户嫉妒不已。 不过 OS X 下貌似也有 Alfred 这样的神器。

安装与启动

Mint 下直接从软件管理里面搜索安装吧,如果想使用命令行,下面的也可以:

sudo apt-get install gnome-do

忘记了初始设置的启动快捷键是什么了,我自己一直使用 Alt + Space .

启动应用

最基本的功能就是启动应用, Alt + Space 之后,输入 “Chrome” ,找到 Chrome 之后回车就直接开启 Chrome。当然与此同时 Gnome-do 也回去搜索本地,查找相关的目录寻找与 Chrome 相关的内容,不关心跳过就行。用同样的方法可以开启本地的任何应用,当然要保证这些应用都在 Gnome-do 的搜索路径下。Gnome-do 会自动学习使用习惯,现在基本我输入 c 就能够找到 Chrome。

开启URL

打开 Gnome-do,输入网址 google.com 然后第一次可能需要使用 Tab 选择 Action: Open URL。然后回车, Gnome-do 会自动开启默认浏览器加载网页。

Plugin

以下插件都可以在 Preference 中找到,并启用,一些插件默认已经启用。

Alias

给应用程序或者其他命令重命名

Files and Folders

搜索本地文件及目录

GNOME Session Management

重启或者关机

Twitter

并不怎么用 Twitter ,不过可以实现 Twitter 发消息。

总结

大部分的情况都是启动应用,URL 或者搜索打开文件,其他的一些功能并不常用。


2016-05-20 Linux , gnome-do

snapseed 中的一些参数

照片编辑一些参数,了解一个 App 就能知道所有图像处理类 App 原理,比如 VSCO,泼辣修图,大到 Photoshop 等等。

Tools

Snapseed 中的常用调整工具

Tune Image

调整图像最常见的操作基本都能在这个 section 中找到。比如亮度、对比度、饱和度等等,细节部分可以单独调整暗部,或者亮部,还有图片整体色调。

Brightness

亮度,很容易理解的概念

Contrast

对比度,黑色像素和白色像素的对比度。利用S曲线的功能也能够实现,不过直接调节Contrast参数倒是来的更加直接。

Saturation

饱和度,色彩的饱和程度。往右为图像色彩变浓重,左反之。也称为色度色彩的三属性之一,广义讲,黑白灰的 色度=0 。

Ambiance

Ambiance 直译是 环境,气氛的意思,这边应该是调整环境的色彩。

Shadows

暗部,调整暗部,单独针对暗部调整往右为暗部更亮,往左为暗部更黑。

Highlights

高光,调整高光部分,更亮或者高光部分调整暗。

Warmth

色调,调整图片的色调,暖色或者是冷色,往右为暖色,往左为冷色。

Details

图片细节部分调整,基本上是图片锐度的调整。

Structure

图片纹理会变得清晰

Sharpening

色彩更加锐利。

Crop

裁剪比例,其实最喜欢的还是 1:1

Available crop aspect ratios:

Free - the aspect ratio can be set freely, without constraint

Original - the aspect ratio will be set based on the original image

1:1 - to create a square image

DIN - to set the aspect ratio according to European page aspect ratios (such as A4, A3, A2, etc.)

3:2 - to set the aspect ratio to what is typically found on D-SLRs

4:3 - to set the aspect ratio to what is typically found on DSCs

5:4 - to set the aspect ratio to what is found on many typical US page sizes (8×10, 16×20, etc.)

7:5 - to set the aspect ratio for 5×7 prints

16:9 - to set the aspect ratio to the aspect ratio of most HDTVs

其中值得一提的就是 DIN ,貌似是欧洲标准的 A4 纸张比例。

Rotate

这个比较好理解,就是旋转照片

Transform

变形,这是一个很棒的功能, VSCO Android 版有这个功能时间也不长,利用空间的变形可以将原本侧面拍摄导致的图片变形给纠正回来。

  • VerticalPerspective 垂直方向变形
  • Horizontal Perspective 水平方向变形
  • Rotation 旋转

Brush

笔刷

Dodge & Burn

完全不知道怎么翻译,感觉涂抹会产生红色像素

Exposure

调整曝光的画笔

Temperature

色温画笔

Saturation

饱和度画笔

Selective

选择点,单独对某个局部进行调整. 可以增加选择点,每个选择点可以使用单击,进行”剪切”,”复制”,”删除”和”撤销该选择点所有操作”.

而每个选择点则有”B”: Brightness, “S”:Saturation, “C”:Contrast . 这样三个可选操作.

而双指则可以选择该点影响的范围.

长按拖动则可以移动选择点.

Healing

修复,好像 PS 中的修复图章,只是这边只要轻轻一涂抹, Snapseed 将自动修复涂抹的区域.

Vignette

直译的话, Vignette 是装饰图案,小图案之类的意思. 而这边应该是指针对图片增加暗角,或者调高角落亮度的操作. 这里面又分为 “Outer Brightness”, 和 “Inner Brightness” 两种操作. 分别对应着暗角,和亮角。

Filters

一些滤镜,和工具不同的是,下面的内容都是在原有图片的基础上增加内容,而不是针对原始图片进行修改。

Lens Blur

这个词我也不知道怎么描述,似乎一直都是以英文描述。用官方的说法,就是增加镜头虚化效果,让注意力都集中到核心物体上。这个工具中又分成线性“linear”和椭圆”elliptical“两种模糊工具。

利用里面另外一个功能也可以定义虚化的图形,不单纯可以是圆形。

Blur Strength

模糊程度,更加模糊。向右滑动增加模糊的程度

Transition

增加周围虚化和中心物体的距离,让过度更加自然。

Vignette Strength

暗角强度,类似工具中的暗角工具。

Glamour Glow

光晕效果,直译过来就是”富有魅力的光晕“。其中每一种滤镜又可以单独调整。

Glow

光晕效果

Saturation

光晕效果色彩饱和度

Warmth

光晕效果色调冷暖。

Tonal Contrast

对比度

High Tones

增加高光部分对比度

Mid Tones

增加中间色对比

Low Tones

增加暗部色彩对比

Protect Shadows

防止暗部因为对比度的增加而损失细节

Protect Highlights

防止高光部分因为对比度增加而损失细节。

HDR Scape

High Dynamic Range 高动态范围成像

  • Filter Strength 滤镜的效果
  • Brightness 亮度
  • Saturation 饱和度

Drama

戏剧风格

  • Filter Strength 滤镜的效果
  • Saturation 饱和度

Grunge

色彩和纹理, 平行的线图标为纹理,交叉箭头为随机纹理与色彩。

  • Style 选择1500种不同的纹理
  • Brightness 亮度
  • Contrast 对比度
  • Texture Strength 纹理强度,0表示没有强度,等于没有增加纹理
  • Saturation 饱和度

Grainy Film

木纹理

  • Grain 自然的颗粒感
  • Style Strength 风格强度

Vintage

带来50,60,70年代的胶片感觉。

  • Brightness 亮度
  • Saturation 饱和度
  • Style Strength 风格强度

Vignette Strength

暗角的强度

Retrolux

创造复古,怀旧,有历史痕迹的胶片感觉

  • Brightness 亮度
  • Saturation 饱和度
  • Contrast 对比度
  • Style Strength 风格强度
  • Scrathches 划痕,value为0依然有效果
  • Light Leaks 漏光,alue为0依然有效果

Noir

Create moody, cinematic black and white images with darkroom-inspired toning and wash effects. 对胶片工业不是很了解,很不好翻译啊,总之就是模拟胶片在暗室中冲洗的效果。

  • Brightness 亮度
  • Wash 模拟darkroom 中over-processing
  • Grain 颗粒感,0为没有颗粒感
  • Filter Strength 滤镜强度

Black & White

黑白效果

  • Brightness 亮度
  • Contrast 对比度
  • Grain 颗粒感,0为没有颗粒感

Frames

调整设置不同边框,滑动调整边框的宽度。边框被应用之后就不能再对图片大小进行调整,所以调整图片大小之后添加边框。

其他可能用到的参数

HSL

HSL是色相、饱和度、明度的简称:

  • 色相是一种颜色区分于另外一种颜色的首要标准,例如红色与黄色。
  • 饱和度是衡量一种颜色鲜艳程度的标准。
  • 明度是衡量一种颜色明亮程度的标准。

曲线:

提亮 & 压暗曲线:提升或降低画面亮度。

左侧曲线可以调整图像中暗色区域,右侧则是高光部分

亮的地方更加亮,暗的地方更加暗一些,提高对比度. S曲线.

Reference


2016-05-19 Snapseed , PhotoEditing , Anroid , VSCO

电子书

本站提供服务

最近文章

  • Glance 个人自定义 Dashboard Glance 是一个可以自行架设的个人 Dashboard 以及 RSS 订阅信息面板。
  • Fileball 一款 iOS tvOS 上的媒体播放器及文件管理器 Fileball 是一款 iOS,tvOS 上的本地文件管理器,本地音乐播放器,本地视频播放器,以及文本编辑器,Fileball 可以在 iPhone,iPad,Apple TV 上使用。Fileball 可以连接网络共享,支持 SMB,FTP,SFTP,Synology,NFS,WebDAV 等,支持 Emby,Jellyfin 等,还可以连接百度网盘,Box,Dropbox,Google Drive,OneDrive,pCloud 等,可以作为 [[Infuse]] ,[[VidHub]] 等播放器的平替,高级版本价格也比较合适。Fileball 也支持 [[IPTV]]。
  • 在日本申请入台证材料及在线提交注意事项 本文记录入台证办理的材料及提交手续,以及在使用线上提交系统的时候需要注意的点。入台证是中华民国台湾地区出入境许可证的俗称,所有进入台湾的人都需要申请此许可证。
  • 从 Buffer 消费图学习 CCPM 项目管理方法 CCPM(Critical Chain Project Management)中文叫做关键链项目管理方法,是 Eliyahu M. Goldratt 在其著作 Critical Chain 中踢出来的项目管理方法,它侧重于项目执行所需要的资源,通过识别和管理项目关键链的方法来有效的监控项目工期,以及提高项目交付率。
  • AI Shell 让 AI 在命令行下提供 Shell 命令 AI Shell 是一款在命令行下的 AI 自动补全工具,当你想要实现一个功能,敲一大段命令又记不住的时候,使用自然语言让 AI 给你生成一个可执行的命令,然后确认之后执行。