dodoliu的折腾笔记

生命不息,折腾不止!

0%

Javascript中的call和apply让人觉得很神奇和好玩.有了它俩,js的面向对象编程方便了一大截.
下面通过两个例子来理解call和apply.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//有 egg 这样一个类
function egg() {}

//为 egg 设置属性和方法
egg.prototype = {
egg_type: '未知动物',
hatch: function(legs){
if (!legs) {
legs = 0;
}
console.log('孵化出一只 ' + this.egg_type + ',有 ' + legs + ' 条腿');
}
}

//实例化一个 egg 类
tmp_egg = new egg();
tmp_egg.hatch();
//p 孵化出一只 未知动物,有 0 条腿

//实例化一个 鸭蛋类
duck_egg = { egg_type: '小鸭子' };
tmp_egg.hatch.call(duck_egg,2);
//p 孵化出一只 小鸭子,有 2 条腿

//实例化一个 鳄鱼蛋类
crocodile_egg = { egg_type: '小鳄鱼'};
tmp_egg.hatch.apply(crocodile_egg,[4]);
//p 孵化出一只 小鳄鱼,有 4 条腿

多重继承

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
//有一个动物类
function animal(){
this.name = function (name) {
console.log('我是 ' + name);
}
}
//有一个 动物叫声类
function cry(sound){
this.sound = function(sound){
console.log('我的叫声 ' + sound);
}
}

//有一个 家禽类
function fowl()
{
//实现多重继承
animal.call(this);
cry.apply(this);
}

tmp_chicken = new fowl();
tmp_chicken.name('鸡');
tmp_chicken.sound('咕咕咕');
//p 我是 鸡
//p 我的叫声 咕咕咕

tmp_duck = new fowl();
tmp_duck.name('鸭');
tmp_duck.sound('嘎嘎嘎');
//p 我是 鸭
//p 我的叫声 嘎嘎嘎

总结:
call和apply功能相同,都是用来改变函数体内部 this 指向的.
call和apply的区别是,apply的第二个参数是个数组.

我有一个很奇葩的需求!
我需要在不改变现有MySql表结构的情况下使用Rails访问MySql.目前只需要用到ActiveRecord的查询功能.
为何会有这种奇葩需求呢?!
现有系统的数据架构是运行很久且稳定的,且现有系统不允许添加一些方便平时使用的小功能.那只能另辟蹊径了.又因为我想尝试下在Windows下搞Rails开发.于是乎,这个奇葩需求就产生了.
既然有想法了,那么就动手做吧!
windows下安装Rails环境就不说了,网上一堆.
访问MySql使用mysql2.如果在确认数据库配置没有任何问题的前提下,还是无法访问MySql.请尝试下载安装MySql的动态链接库文件libmysql.dll.这是下载地址
如果没有意外,安装完动态链接库后一切就正常了.如何还不正常…请问Google!
这个需求的唯一关键点就是设置Model的默认访问table名称.因为按照Rails的约定,Model是单数,数据库中的表必须是复数.比如有Account这个Model,如果数据库中的表是account,那么需要按照下面代码中的设置来创建Model.

1
2
3
class Account < ActiveRecord::Base
self.table_name = 'account'
end

这样设置之后,就可以愉快的使用ActiveRecord的强大功能了.

之前在做统计分析系统的时候需要跨域请求数据.Jquery已经封装了jsonp的实现,但是考虑到统计代码的大小,决定弃用jquery.自己实现一下.
jsonp的原理其实很简单,通过script标签发送一个callback参数到后端,后端返回数据时,使用这个callback函数包裹一下.这样,在前端接收到后端的返回后,就可以执行这个callback函数.
在使用jsonp时需要注意,jsonp只能发送get请求.
下面是jsonp的实现代码.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
var _ajax = {
ajax_jsonp: {
//获取当前时间戳
now: function () {
return (new Date()).getTime();
},
//获取16位随机数
rand: function () {
return Math.random().toString().substr(2);
},
//url组装
parse_data: function (data) {
var ret = "";
if (typeof data === "string") {
ret = data;
}
else if (typeof data === "object") {
for (var key in data) {
ret += "&" + key + "=" + encodeURIComponent(data[key]);
}
}
//加上时间戳
ret += "&_time=" + this.now();
return ret;
},
/**
* jsonp调用的方法
* @param {object} url 请求的url
* @param {object} data 请求的data
*/
jsonp: function (url, data) {
var name;
url = url + (url.indexOf("?") === -1 ? "?" : "&") + this.parse_data(data);

//检测callback的函数名是否已经定义
var match = /callback=([a-zA-Z\._]+)/.exec(url);
if (match && match[1]) {
name = match[1].replace(/\./g, "_");
}
else {
//如果url中没有callback,直接返回
throw new Error("invalid callback param");
}

//创建script标签
var script = document.createElement("script");
script.type = "text/javascript";
script.src = url;

//在head里面插入script元素
var head = document.getElementsByTagName("head");
if (head && head[0]) {
head[0].appendChild(script);
}
}
}
};

调用

1
2
3
4
5
6
7
_ajax.ajax_jsonp.jsonp(_url, data);
#比如_url中的callback是这样的参数: ?callback=window.getdata
#则在js中注册这个window.getdata函数即可
window.getdata = function(data)
{
console.log(data);
}

当我们使用Hexo方便快捷的搭建完静态Blog,并愉快的写完第一篇Blog发布后.可能会遇到这样的问题:
我的Blog源码怎么管理啊?!
官方的命令咋没有把源码一并提交到GitHub啊?!
我要是有多台电脑,还要再安装一遍不成?!
同志,不要怕,接下来请跟我一步一步做.

下面拿我的 dodoliu blog为例,以下所有的命令操作都假设你处在blog文件夹的根目录下,且你的系统是OSX

  • 先删除 dodoliu blog文件夹下的 .git 文件夹,该文件夹在windows下默认是隐藏的
    1
    2
    cd dodoliu
    rm -rf .git
  • 再删除 themes 主题文件夹下的 .git 文件夹,如果发现有 .github 文件夹也一并删掉.
    1
    2
    cd themes/next
    rm -rf .git .github
  • 然后回到dodoliu blog的根目录,创建git仓库
    1
    2
    cd dodoliu
    git init
  • 然后添加文件到git仓库,在添加前确认你blog根目录下的.gitignore文件内容像我这样
    1
    2
    3
    4
    5
    6
    7
    .DS_Store
    Thumbs.db
    db.json
    *.log
    node_modules/
    public/
    .deploy*/
  • 接下来把本地代码提交到本地git仓库
    1
    git add . && git commit -m 'init'
  • 然后copy 线上 github的仓库地址,添加到本地仓库(https://github.com/dodoliu/dodoliu.github.io.git是我的github仓库地址,请替换成你自己的)
    1
    git remote add origin https://github.com/dodoliu/dodoliu.github.io.git
  • 然后创建一个本地分支,并切换到该分支下
    1
    2
    git branch blog
    git checkout blog
  • 接下来,提交代码到github线上的blog分支中
    1
    git push origin blog
  • 提交到github时,如果不想每次都打这么多命令,可以进行这样的设置
    1
    2
    3
    git push --set-upstream origin blog
    #之后就用下面这个命令推送吧
    git push
    然后到github上看看的master和blog分支的区别吧!
    以后再次提交请在blog分支下进行操作.
    到这里,恭喜你!
    大功告成,开始你的下一篇blog吧!

项目中需要使用简单的年月日级联选择功能遇到了好几次.每次都要Copy,想想还是老老实实封装一下,方便复用.
功能展示
功能展示

源码地址

简介

使用KnockoutJS实现年月日三个下拉级联,方便复用
模式A(year_month_day_select_a): 完整的年月日控件
模式B(year_month_day_select_b): 只包含年和月的控件
基本参数的详细说明在 coffee中

编译

coffee -w -b -o . -c .

用法

引入

1
<script type="text/javascript" src="year_month_day_select.js" defer='defer'></script>

html中绑定

1
<div data-bind="component:'year_month_day_select_a' "></div>

1
<div data-bind="component:'year_month_day_select_b' "></div>

获取值

1
2
3
4
var year = $("#test_sl_year option:selected").val();
var month = $("#test_sl_month option:selected").val();
var day = $("#test_sl_day option:selected").val();
$("#span").text(year + '-' + month + '-' + day);

日期默认选择有三种形式

  • 模式1,默认选种 年月日
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
     new YearMonthDaySelect({
    initSelect: 2,
    yearDefault: 'year',
    monthDefault: 'month',
    dayDefault: 'day',
    yearInterval: 10,
    yearTitle: '年',
    yearID: 'test_sl_year',
    yearName: 'test_sl_year_name',
    yearClass: 'test_sl_year_class otherclassname',
    monthTitle: '月',
    monthID: 'test_sl_month',
    monthName: 'test_sl_month_name',
    monthClass: 'test_sl_month_class otherclassname',
    dayTitle: '日',
    dayID: 'test_sl_day',
    dayName: 'test_sl_day_name',
    dayClass: 'test_sl_day_class otherclassname'
    });
  • 模式2, 默认选中 当前 年月日
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    new YearMonthDaySelect({
    initSelect: 2,
    yearInterval: 10,
    yearTitle: '年',
    yearID: 'test_sl_year',
    yearName: 'test_sl_year_name',
    yearClass: 'test_sl_year_class otherclassname',
    monthTitle: '月',
    monthID: 'test_sl_month',
    monthName: 'test_sl_month_name',
    monthClass: 'test_sl_month_class otherclassname',
    dayTitle: '日',
    dayID: 'test_sl_day',
    dayName: 'test_sl_day_name',
    dayClass: 'test_sl_day_class otherclassname'
    });
  • 模式3, 默认选中 当前 年,1月,1日
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    new YearMonthDaySelect({
    initSelect: 3,
    yearInterval: 10,
    yearTitle: '年',
    yearID: 'test_sl_year',
    yearName: 'test_sl_year_name',
    yearClass: 'test_sl_year_class otherclassname',
    monthTitle: '月',
    monthID: 'test_sl_month',
    monthName: 'test_sl_month_name',
    monthClass: 'test_sl_month_class otherclassname',
    dayTitle: '日',
    dayID: 'test_sl_day',
    dayName: 'test_sl_day_name',
    dayClass: 'test_sl_day_class otherclassname'
    });
  • 无论处于何种模式下,只要传入了默认值则选中指定的年月日
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    new YearMonthDaySelect({
    initYear: 2013,
    initMonth: 3,
    initDay: 2,
    yearInterval: 10,
    yearTitle: '年',
    yearID: 'test_sl_year',
    yearName: 'test_sl_year_name',
    yearClass: 'test_sl_year_class otherclassname',
    monthTitle: '月',
    monthID: 'test_sl_month',
    monthName: 'test_sl_month_name',
    monthClass: 'test_sl_month_class otherclassname',
    dayTitle: '日',
    dayID: 'test_sl_day',
    dayName: 'test_sl_day_name',
    dayClass: 'test_sl_day_class otherclassname'
    });

    注意事项

    该扩展所需依赖
    jquery.js
    helper.js
    knockout.js
    只能同时存在一种模式
    必须 通过 new YearMonthDaySelect() 调用,否则无法正常使用

目的:

属性nginx的反向代理和负载均衡

要求:

使用nginx配置三个站点
虚拟机的主机能通过负载均衡访问这三个站点

环境:

VirtualBox: 5.0.20
CentOS: CentOS-7-x86_64-Minimal-1511
Nginx: 1.10.0
主机: win7

具体设置

三个站点分别为:

站点1:

路径: /home/www/blog_1
端口: 801
根目录文件: index.html
根目录文件内容: blog_1

站点2:

路径: /home/www/blog_2
端口: 802
根目录文件: index.html
根目录文件内容: blog_2

站点3:

路径: /home/www/blog_3
端口: 803
根目录文件: index.html
根目录文件内容: blog_3

VirtualBox的网络配置:

NAT模式下配置虚拟机和主机的端口映射

主机IP 主机端口 虚拟机IP 虚拟机端口
127.0.0.1 12080 10.0.2.15 80
127.0.0.1 12801 10.0.2.15 801
127.0.0.1 12802 10.0.2.15 802
127.0.0.1 12803 10.0.2.15 803

这样配置的目的是
通过访问 127.0.0.1:12080 访问负载均衡站点
通过 127.0.0.1:12801 ..12802 ..12803 访问各个站点

简单配置后的nginx.conf如下(所有原来注释的内容都被我删掉了):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
worker_processes  1;

events {
worker_connections 1024;
}

http {
include mime.types;
default_type application/octet-stream;

sendfile on;

keepalive_timeout 65;

upstream blog_server {
server 127.0.0.1:801 weight=1;
server 127.0.0.1:802 weight=1;
server 127.0.0.1:803 weight=1;
}

server {
listen 801;
server_name blog1;
root /home/www/blog_1;
index index.html;
}

server {
listen 802;
server_name blog2;
root /home/www/blog_2;
index index.html;
}
server {
listen 803;
server_name blog3;
root /home/www/blog_3;
index index.html;
}

server {
listen 80;
server_name localhost;

location / {

proxy_pass http://blog_server;
}

error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}

}
}

这样配置后,重启nginx
在主机中访问 127.0.0.1:12080 即可查看到负载均衡带来的效果.

配置项说明:

三个站点, 分配监听 801,802,803

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
server {
listen 801;
server_name blog1;
root /home/www/blog_1;
index index.html;
}

server {
listen 802;
server_name blog2;
root /home/www/blog_2;
index index.html;
}
server {
listen 803;
server_name blog3;
root /home/www/blog_3;
index index.html;
}
反向代理和负载均衡的配置
1
2
3
4
5
upstream blog_server {
server 127.0.0.1:801 weight=1;
server 127.0.0.1:802 weight=1;
server 127.0.0.1:803 weight=1;
}

关于该节点的详细说明(引用别人的)

定义负载均衡设备的 Ip及设备状态
1
2
3
4
5
6
upstream myServer {
server 127.0.0.1:801 down;
server 127.0.0.1:802 weight=2;
server 127.0.0.1:803;
server 127.0.0.1:804 backup;
}
upstream 每个设备的状态:

down 表示单前的server暂时不参与负载
weight 默认为1.weight越大,负载的权重就越大。
max_fails :允许请求失败的次数默认为1.当超过最大次数时,返回proxy_next_upstream 模块定义的错误
fail_timeout:max_fails 次失败后,暂停的时间。
backup: 其它所有的非backup机器down或者忙的时候,请求backup机器。所以这台机器压力会最轻。

访问入口的配置:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
server {
listen 80;
server_name localhost;

location / {

proxy_pass http://blog_server;
}

error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}

}

*** 主要是这个配置 ***
*** proxy_pass http://blog_server; #在这个节点下启用负载均衡 ***

目的:

熟悉nginx的安装与使用

环境:

VirtualBox: 5.0.20
CentOS: CentOS-7-x86_64-Minimal-1511
Nginx: 1.10.0

安装步骤:

Nginx下载地址: http://nginx.org/download/nginx-1.10.0.tar.gz
下载完成后解压: tar zxvf nginx-1.10.0.tar.gz
然后进入解压目录: cd nginx-1.10.0
编译安装:
./configure
make && make install

默认安装目录在: /usr/local/nginx

启动:

cd到 nginx下的 sbin目录
执行命令 ./nginx

停止:

使用ps命令查看 nginx的pid
ps -A | grep nginx
从容停止: kill -QUIT 查询到的nginx的pid
快速停止: kill -TERM 查询到的nginx的pid
强制停止: kill -9 查询到的nginx的pid

重启:

cd到nginx的sbin目录
./nginx -s reload (这个命令在我测试的时候发现不能完全重启…不知为何…之后我都选择先停止,再启动)

修改conf:

默认配置文件在nginx的根目录的conf中
进入该目录 vim nginx.conf 进行编辑.
编辑完成后一般需要先检测配置文件是否正常再重启系统nginx,以免配置文件错误.
检测命令(假如在nginx的sbin目录下): ./nginx -t

目的:

了解虚拟机的网络配置

要求:

虚拟机和主机能互通
虚拟机要能访问网络

限制条件:

公司环境下不允许分配独立IP

环境:

win7 or OSX: 10.11.4
VirtualBox: 5.0.20
CentOS: CentOS-7-x86_64-Minimal-1511

虚拟机要能访问网络:

CentOS7 默认使用ip查看IP地址
配置文件位置: /etc/sysconfig/network-scripts/
需要修改的配置项说明:
BOOTPROTO=dhcp #自动获取ip
ONBOOT=yes #开机启动网卡
重启网卡命令: service network restart
ip addr 查看ip信息

VirtualBox 网络设置为 NAT模式,没有意外的话CentOS可以直接访问网络了

虚拟机和主机能互通:

配置一个端口映射的规则即可
如下图
1

配置虚拟机的22端口映射

win下使用putty进行ssh登陆
mac下使用 ssh登陆 ssh -p 12022 root@127.0.0.1

主机IP 主机端口 虚拟机IP 虚拟机端口
127.0.0.1 12022 10.0.2.15 22

错误描述:

  FTP:远程服务器返回错误:(550)文件不可用(例如,未找到文件,无法访问文件)

分析:

  最近在开发多服务器FTP文件同步应用时遇到了该问题. 开始死活找不到问题所在.网上查了一下都说是检测根目录或者文件是否存在.
  最后发现是 FTP账号的权限问题,我没有配置对应账号的写权限…
  总结遇到这个问题的排错方式:

  1. 确定代码没有问题
  2. 检测文件或目录是否真的存在
  3. 检测FTP账号的可读可写权限
  4. 检测FTP账号的根目录