2014年7月

Nginx的常用配置

www跳转到主域名

server {
    listen 80;
    server_name www.tool.lu;

    access_log off;
    error_log off;

    return 301 http://tool.lu$request_uri;
}

http跳转到https

server {
    listen      80;
    server_name tool.lu;
    return 301 https://tool.lu$request_uri;
}

fastcgi

    location ~ \.php$ {
        try_files $uri =404;
        fastcgi_pass 127.0.0.1:9000;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }

cdn

server {
    listen 80;
    server_name s1.tool.lu s2.tool.lu s3.tool.lu s4.tool.lu;

    root /data/html/tool.lu/static;
    index index.html;

    access_log off;
    error_log off;

    location / {
        concat on;
        concat_types text/css application/javascript;
        # 解决js语句不以分号结尾的第三方库
        concat_delimiter "\n\n";
        concat_max_files 20;

        expires 7d;
    }
}

参考

  1. Pitfalls - Nginx Community

按位操作应用实例

需求

用户登陆后有多个列表页面中都有搜索功能,由于搜索的条件比较多,所以加了个“更多”按钮隐藏了一部分条件,只把常用的条件直接显示出来。但有些用户会经常用到更多中的条件来搜索,所以希望当点击了“更多”按钮后,我刷新页面或者下次再登陆进来后那个更多中的条件默认就是显示的。

分析

一下子就想到用cookie来做,每个用户每个页面设置个不同的cookie。
但这样会有个问题就是cookie个数太多了。。,去搜索引擎搜索了下,每个站点的cookie是有数量限制的。
不过,即便没有限制,想想那么多个cookie也是件很恐怖的事情。

解决

以前学习mysql的时候,知道可以用无符号的tinyint来存储最多8个数据的0和1的状态,就是将255转换为二进制然后约定第几位代表固定的某个数据或某个物品,1和0来标识其状态。
那这里我想也可以用类似的方法。这样的话,每个用户只用一个相应的cookie就可以搞定了。

实现

下面这个是每个页面不同的部分:

<script type="text/javascript">
    var searchMoreMark = 1;//标识:页面一为1、页面二为2、页面三为4、页面四为8、页面五为16、页面六为32、页面七为64。。依次类推。
</script>

这里是公用的部分,需要先引入jQuery及jQuery.cookie

<script type="text/javascript">
    var searchMoreCookieExpire = 36500;//cookie有效时间
    var searchMoreCookieName = "searchmore_<?=$_SESSION['LOGIN_USER_ID']?>";//根据用户来区分
    jQuery(document).ready(function() {
        //点击展开操作
        jQuery('#do_search_more').click(function(){
            jQuery('#more_condition').css('display','block');
            jQuery('#do_search_more').css('display','none');
            jQuery('#do_search_less').css('display','block');
            jQuery(window).resize();
            //设置“更多”状态
            if(jQuery.cookie(searchMoreCookieName)==null || jQuery.cookie(searchMoreCookieName)==0){
                //如果没有设置过cookie,就设置cookie为当前页面的标识
                jQuery.cookie(searchMoreCookieName, searchMoreMark, {expires:searchMoreCookieExpire, path:'/'});
            }else if((jQuery.cookie(searchMoreCookieName) & searchMoreMark) == 0){
                //如果设置过cookie且不存在当前页面的标识的话就把当前页面标识加进过
                jQuery.cookie(searchMoreCookieName, parseInt(jQuery.cookie(searchMoreCookieName)) | searchMoreMark, {expires:searchMoreCookieExpire, path:'/'});
            }
        });

        //点击收缩操作
        jQuery('#do_search_less').click(function(){
            jQuery('#more_condition').css('display','none');
            jQuery('#do_search_more').css('display','block');
            jQuery('#do_search_less').css('display','none');
            jQuery(window).resize();
            if(jQuery.cookie(searchMoreCookieName)!=null){
                //去除本页面的标识
                searchMoreMark = jQuery.cookie(searchMoreCookieName) & (~searchMoreMark);
                //如果标识为0,那么就删除掉这个cookie
                if(!searchMoreMark){
                    searchMoreCookieExpire = -1;
                }
                jQuery.cookie(searchMoreCookieName, searchMoreMark, {expires:searchMoreCookieExpire, path:'/'});
            }
        });
        //默认为上一次的状态(收缩或展开)
        if(jQuery.cookie(searchMoreCookieName)!=null && (jQuery.cookie(searchMoreCookieName) & searchMoreMark) != 0){
            jQuery('#do_search_more').click();
        }
    }
</script>

python 魔术函数调用的实现

场景

实现一个python调用java接口的功能,java接口是以http方式提供的。为了实现比较舒服的调用的方式,我不准备以send({"method": "echo"})这种方式调用,而是api.echo()

但是python里面并没有提供类似php的__call__callStatic的函数

根据python的__getattr__来实现一个,但是这个解决方案不完美,有局限性。

实现

# encoding: utf-8

import json
import urllib, urllib2

class OPS(object):
    s = None

    def __init__(self, s):
        self.s = s

    def sendRequest(self, msg):
        postData = json.dumps(msg)
        print(postData)
        req = urllib2.Request(self.s, postData)
        try:
            resp = urllib2.urlopen(req)
        except Exception as e:
            return False
        cnt = resp.read()
        try:
            cnt = json.loads(cnt)
        except Exception as e:
            pass
        return cnt

    def __getattr__(self, name):
        def func(*args, **kwargs):
            # args 不处理 (由于是序列化成json传输的,python的dict是无序的,抛弃对list的处理)
            data = {
                'method': name,
                'parameter': kwargs
            }
            return self.sendRequest(data)
        return func

if __name__ == '__main__':
    ops = OPS('你的http接口调用地址')
    print(ops.hello(word="你好"))

关于MySQL的几个函数

find_in_set

+----+-----------+
| id | extra_ids |
+----+-----------+
| 1  | 1,2,3     |
| 2  | 4,5,6,1,3 |
| 3  | 7,3,1,2,5 |
+----+-----------+
-- 看到有人是用like做的,虽然这两种都会全表扫描,这样用会比like好。
select * from test where find_in_set(5, extra_ids);

group_concat

+----+----------+
| id | group_id |
+----+----------+
| 1  | 1        |
| 2  | 1        |
| 3  | 2        |
+----+----------+
-- group_concat(id)
select group_concat(id order by id desc separator '|') as ids from test group by group_id;
+-----+----------+
| ids | group_id |
+-----+----------+
| 2|1 | 1        |
| 3   | 2        |
+-----+----------+

tool.lu技术架构

背景

一个字,穷!在小流量的情况下,这个应该算是比较经济的解决方案了吧(各种容灾都没有,监控没有,服务的吞吐测试没有)。哈哈哈...

后端的业务处理和服务

整个的网站都放在aliyun的VPS上。

由于工具网站的后端处理比较耗资源,于是将业务处理服务部署到了两台VPS上。(aliyun +1 & 美国 +1)

Redis只是做了少量的缓存作用,所以图中并未给出。

Untitled.png

面对前端的一些优化

cdn 现在全部都放在aliyun的VPS上。

  1. 使用nginx的 nginx-http-concat 扩展合并多个文件请求。
  2. http_image_filter_module 进行一些图片的实时压缩计算

域名分别为 s1.tool.lu, s2.tool.lu, s3.tool.lu

爬虫

现在所有的爬虫均基于Scrapy编写,全部部署在 美国的vps上;数据储存在MariaDB。

虚拟化

主要用于一些不可信任代码的执行。

选型Docker,可限制CPU和Mem,不能限制Disk,但是Docker在CentOS6.x下的问题较多,各种坑;最近使用CentOS7搭建之后貌似很Happy。

jQuery data函数的坑

这个函数

<div id="test" data-id="1e3"></div>
<script>
console.log($('#test').data('id'));
// 1e3  jQuery 1.9.1
// 1000  jQuery 1.7.2
</script>

Every attempt is made to convert the string to a JavaScript value (this includes booleans, numbers, objects, arrays, and null). A value is only converted to a number if doing so doesn't change the value's representation. For example, "1E02" and "100.000" are equivalent as numbers (numeric value 100) but converting them would alter their representation so they are left as strings. The string value "100" is converted to the number 100.

When the data attribute is an object (starts with '{') or array (starts with '[') then jQuery.parseJSON is used to parse the string; it must follow valid JSON syntax including quoted property names. If the value isn't parseable as a JavaScript value, it is left as a string.

To retrieve the value's attribute as a string without any attempt to convert it, use the attr() method.

2014-07-16 23:02 更新

这个还是要看具体的jQuery版本而定的

jQuery.fn.jquery

我这测试,1.7.2 的是会自动转换的,1.9.1 是不会自动转换

而官方的文档的更新函数中,貌似并没有提及这一点

CodeMirror绑定快捷键

自定一个命令

CodeMirror.commands.runcode = function (cm) {
    // you can do something here.
};

绑定快捷键

// 判断是否为Mac
var mac = CodeMirror.keyMap.default == CodeMirror.keyMap.macDefault;
var runKey = (mac ? "Cmd" : "Ctrl") + "-Enter";
var extraKeys = {};
extraKeys[runKey] = "runcode";

初始化CodeMirror

CodeMirror.fromTextArea(code, {
    extraKeys: extraKeys
});