ipkg, opkg 与 openWrt

ipkg 是 Itsy Package Management System 的缩写, 是类似与 Debian 的 dpkg 一样的, 专门用在嵌入式设备的轻量级包管理工具, 层被用在 OpenWrt, NSLU2, webOS 等操作系统. 如今, ipkg 这个项目已经停止更新, 曾经使用它的许多项目转而使用它的变种: opkg.


opkg 是 OPKG Package Manager 的缩写, 由 ipkg 演变而来, 所以也是用于嵌入式设备轻量级的包管理工具, 用来下载, 安装, 删除, 显示 OpenWrt 的包. 于 ipkg 不同, opkg 可以对 root 文件系统进行全部的包管理, 而不仅限于安装软件到特定的目录, 如 /opt.

opkg 的配置文件一般是 /etc/opkg.conf
搜索某个 package 的关键字: opkg info '*mysql*'
安装某个 package: opkg install nginx
安装的位置会参照/etc/opkg.conf 中配置的 dest的值, 你也可以指定: opkg install -d mnt nginx

src/gz attitude_adjustment http://downloads.openwrt.org/attitude_adjustment/12.09/brcm47xx/generic/packages
dest mnt /data
dest ram /tmp
arch all 100
arch brcm4709 200
arch brcm47xx 300

以上是修改小米路由器的 opkg.conf, 第一行指定 repo 的 URL, 2,3行指定安装可能的路径, 3,4,5指定可以兼容的包, 后面的数字, 100代表优先级最高, 200次之, 300最低, 但是也可以兼容安装.

参考:
ipkg in wikiipedia
opkg on openWrt
OpenWrt version history
Broadcom BCM47xx 系列

JavaScript 遍历页面链接, 并全部打开

接上一篇 路由器上 nginx 博客添加 fastcgi 缓存, 虽然有了缓存, 但是第一次访问的时候, 仍然比较慢, 怎么才能让用户永远点不到第一次呢? 就是服务器自己要 warmup, 如何让自己的博客系统 warmup 呢, 可以自己先遍历页面一遍.

打开 chrome 浏览器的 console, 执行如下代码(按需修改):

// 打开新的浏览器 tab, 并且返回 tab 的句柄
function openInNewTab(url) {
  var win = window.open(url, '_blank', "left=300,screenX=100,resizable,scrollbars,status");
  win.focus();
  return win;
}

var newLink;
var most = 10;  //最多并存的 tab
var winArr = new Array();   // 存放 tab 句柄的数组
var allLinks = $$("a");   // 得到所有的页面链接, 不是 jQuery方式哦
var cur = 0;
var urlMap = {};

// 间隔时间执行的函数
function loopStart() {
    try {
        newLink = allLinks[cur++].href;

        if (urlMap[newLink]) {
            // 已经打开过, do nothing, just continue
        } else {
            urlMap[newLink] = "yes"; //放入 map,表示处理过
            if (most < winArr.length) {  //达到最多, 关闭最早
                winArr.shift().close();
            }
                        // 只处理本站的链接
            if (newLink.indexOf("tianxiaohui.com") > 0) {
                console.log(newLink);
                winArr.push(openInNewTab(newLink));
            }
        }
        setTimeout(loopStart, 1000);
    } catch (ex) {
        console.log(ex);
    }
}

setTimeout(loopStart, 1000);

可能会遇到一个问题, 就是新页面打不开, 会出现如下状况, 这个页面默认被 block 了, 需要手动解封:
请输入图片描述
请输入图片描述

浏览器控制台 $ 符号

接上一篇 检查 jQuery 是否 load, 否则重新加载 , 做了一个加载 jQuery 的实验, 找了一个未加载 jQuery 的页面, 再控制台检查 window.jQuery, 显示 undefined, 但是如果直接输出 $, 却是有输出的(默认以 chrome console 为例):

>window.jQuery;
undefined
>$;
function $(selector, [startNode]) { [Command Line API] }

这里就有点迷惑了, 为什么不加载 jQuery, $ 也是 JavaScript 的对象呢? 难道加载了 extJS 等 JavaScript library? 换一个空白页面, 仍然是上面的输出. 真奇了怪了.
那么我们先看看如果加载了 jQuery, 输出又是什么样子呢?

>if(!window.jQuery)
{
   var script = document.createElement('script');
   var protocol = (("https:" == document.location.protocol) ? " https" : " http");
   script.src = protocol + "://code.jquery.com/jquery-1.9.1.min.js";
   script.type = "text/javascript";
   (document.getElementsByTagName('body')[0] || document.getElementsByTagName('head')[0]).appendChild(script);
}
<script src=​" https:​/​/​code.jquery.com/​jquery-1.9.1.min.js" type=​"text/​javascript">​</script>​
>window.jQuery;
function (e,t){return new b.fn.init(e,t,r)} 
>$;
function (e,t){return new b.fn.init(e,t,r)} 

这里 window.jQuery 和 $ 都有了, 值是一样的, 并且 $ 和上面的输出是不一样的.
为了解开迷惑, 就 google 之, 于是发现了这个非常有趣的帖子, 大致翻译过来如下:

问: 几年前, 哥也曾是 JavaScript/jQuery 开发攻城狮, 如今重操旧业. 清醒的记得, $符号是用在 jQuery 的库里面的, 如果不用 jQuery, $这逼, 啥都不是. 
今天, 我打开一个不包含任何 JavaScript 库的空页面, 然后在控制台输出 $, 竟然输出了一些东西, 不是 undefined, 是一个 function. 
1) 以前$符号是没有赋予任何函数的, 难道是我记错了?
2) 如果没有 load jQuery 等类库, 那么$符号代表神马?

答: 
1) 以前你是对的, 现在依然是对的;
2) Firefox and Chrome implement $, $$ and several others as helper commands. Both will set $$ to document.querySelectorAll(), and set $ to document.querySelector if window.$ hasn't been defined.

那么谜底解开, 在 firefox 和 chrome 的控制台, 这些符号默认被赋予了一些特定的函数, 所以即使没有 load jQuery 等类库, 你也可以用他们来操作 DOM.

  1. $() 对于给定的表达式进行评估, 如果找到对应的 element, 就返回第一个元素, 相当于 document.querySelector();
  2. $$() 评估表达式, 返回找到的所有 node 的数组, 相当于 document.querySelectorAll();
  3. $x() 评估 xPath 表达式, 返回找到 node 的数组;

还有更多, 参考官方这里: Firefox, Chrome.

检查 jQuery 是否 load, 否则重新加载

刚才看到一个很好的 javascript 用来检测 jQuery 是否已经 load, 如果没有 load, 那么重新 load. 尤其是某些嵌入式的脚本, 它可能一开始并不知道这一页面有么有 load jQuery.

if(!window.jQuery)
{
   var script = document.createElement('script');
   script.type = "text/javascript";
   script.src = "path/to/jQuery";
   (document.getElementsByTagName('body')[0] || document.getElementsByTagName('head')[0]).appendChild(script);
}

window.jQuery 用来检测当前页面有么有 load jQuery.
document.getElementsByTagName('head')[0] 查看当前 DOM 是不是有 head 元素;
document.getElementsByTagName('body')[0] 查看当前 DOM 是不是有 body 元素;

如果找到(getElementsByTagName 会返回一个数组), 就追加到第一个元素的里面的最后面.

路由器上 nginx 博客添加 fastcgi 缓存

openWRT 的路由器上架上了博客, 虽然路由器CPU, 内存都很弱, 但是假设一个博客系统还过得去. 最近发现一旦访问量大一点, 路由器CPU 就飙的很高.
为了解决这个问题, 在启动 fastcgi 的时候, 已经添加了多进程:

#运行PHP环境
PHP_FCGI_CHILDREN=2
PHP_FCGI_MAX_REQUESTS=30
export PHP_FCGI_CHILDREN
export PHP_FCGI_MAX_REQUESTS
/opt/bin/spawn-fcgi  -a 127.0.0.1 -p 9000 -f /opt/bin/php-fcgi

一开始设置PHP_FCGI_CHILDREN 为4, 但是发现还是有时候 CPU 飙升, 都是 fast_cgi 占用. nginx 的 CPU 占用率都很低. 那么瓶颈在于 php, 就要优化 php 这边.
博客系统的一个特性在于绝大部分只是看, 只有非常少的部分是修改. 那么就可以考虑设置缓存. 这里主要设置 fastcgi 的 cache, 当然也可以设置 nginx 的 cache, 或者其他的. fastcgi 的相对来说, 比较简单.
修改nginx 的http 部分, 添加如下设置:

fastcgi_cache_path /opt/var/nginx/tmp/fastcgi_cache levels=1:2 keys_zone=cache_zone:8m inactive=1d max_size=100m;
fastcgi_temp_path  /opt/var/nginx/tmp/fastcgi_temp;

fastcgi_cache cache_zone;
fastcgi_ignore_headers "Cache-Control" "Expires" "Set-Cookie";
fastcgi_cache_key "$scheme$request_method$host$request_uri$is_args$args";
fastcgi_cache_valid 200 301 302 1d;
fastcgi_ignore_client_abort on;

其中fastcgi_cache_path, 和 fastcgi_temp_path 必须设置在 http 部分, 其他设置可以设置在 server 或者 location 部分, 以缩小范围.
网上有很多教程, 但是比较容易出错的一个点是: fastcgi_ignore_headers, 如果不设置这一项, fastcgi cache 就不起作用, 因为一般的 web http 请求都是使用后面的这些值做缓存的. 参考:这里
设置好之后, nginx -t 测试修改是否正确, 然后重启或者 reload.

那么再重新 top, 启动多线程测试, 你就会发现现在是 nginx 占用 CPU 了, 但是也非常低.
另外要注意的就是 purge, 万一你修改了博客, 或者有用户添加了评论, 那么就需要重新从数据库里拿了, nginx 的 fastcgi 模块有这个指令: fastcgi_cache_purge, 可是它是从version 1.5.7 开始生效的, 所以这里就没配置.

对于某些不想缓存的文档, 可以这么设置:

set $no_cache 0;
if ($request_uri ~* "/(administrator)")
{
    set $no_cache 1;
}
fastcgi_cache_bypass $no_cache;

关于 nginx fastcgi 指令的全部描述, 参看这里