Living a Simple Life is a Happy Life

有饭吃,自由自在,就非常开心

Javascript设计模式 - 笔记3

| Comments

javascript 里面的继承是个非常复杂的话题,一言蔽之,就是你要替解释器干点活。另外,javascript属于使用原型式继承的语言,这个比较少见,所以直觉上不好拐弯。

先回顾比较简单的办法

类式继承

一个简单的类

1
2
3
4
5
6
7
8
9
10
11
/* class Person */
function Person(name) {
    this.name = name;
}

Person.prototype.getName = functino() {
    return this.name;
}

var reader = new Person('brainzhang');
reader.getName();

好,下面定义一个它的子类

1
2
3
4
5
6
7
8
/* Class Author */
function Author(name, books) {
    Person.call(this, name);
    this.books = books;
}
Author.prototype = new Persion();  //set up the prototype chain
Author.prototype.contructor = Author; //set the constructor attribute to author
Author.getBooks = function() {return this.books;}

容易费解的是这两行:

1
2
Author.prototype = new Persion();  //set up the prototype chain
Author.prototype.contructor = Author; //set the constructor attribute to author

javascript中,每个对象都有一个原型对象,在创建一个对象时,javascript会自动将其原型对象设置为其构造函数的prototype属性所指的对象。 在访问对象的某个成员时,如果这个成员未见于当前对象,那么javascript会沿着原型链向上逐一访问每个原型对象(最顶端为Object.prototype对象),直到找到这个成员为止。 这意味着,为了让一个类继承另一个类,只需将子类的prototype设置为基类的一个实例即可。

第二行将prototype的constructor属性重新设置为Author。是因为: 定义一个构造函数时,其默认的prototype对象是一个Object类型的实例,其contructor属性会被设置为构造函数本身。如果手工将prototype设置为另一个对象,就要重新设置其constructor属性。

最后,为了简化类的声明,可以将这些工作封装在extend函数中:

1
2
3
4
5
6
function extend(subClass, superClass) {
    var F = function(){};
    F.prototype = superClass.prototype;
    subClass.prototype = new F();
    subClass.prototype.contructor = subClass;
}

作为改进,定义了一个新对象F,避免基类对象过大,创建实例浪费资源。

但是这样还有个小缺点,声明 Author的时候,还要显式的调用一下Person.call(),下面这个版本进一步做了改进:

1
2
3
4
5
6
7
8
9
10
11
function extend(subClass, superClass) {
    var F = function(){};
    F.prototype = superClass.prototype;
    subClass.prototype = new F();
    subClass.prototype.contructor = subClass;

    subClass.superclass = superClass.prototype;
    if(superClass.prototype.contructor == Object.prototype.constructor) {
        superClass.prototype.contructor = superClass;
    }
}

增加了一个superclass属性来直接访问基类,这样声明Author的时候可以这么写:

1
2
3
4
5
6
7
/* Class Author */
function Author(name, books) {
    Author.superclass.contructor.call(this, name);
    this.books = books;
}
extend(Author, Person);
Author.getBooks = function() {return this.books;}

原型式继承

TODO

Git Cheat

| Comments

整理一下经常忘记的Git命令

remote 同步

1
2
3
4
$ git remote -v                                                     // 查看当前远程版本库
$ git remote add cocos2d-x git://github.com/cocos2d/cocos2d-x.git   // 添加原始版本库
$ git fetch cocos2d-x                                               // 获取原始版本库的更新
$ git merge cocos2d-x/master                                        // 合并原始版本库的代码到当前版本库中,合并前确保当前分支是master

remote branch

1
2
3
4
5
6
$ git branch -a                                                     //查看所有分支
$ git checkout -b branches/xxx origin/branches/xxx                  //chekcout远程分支
$ git push origin --delete <branchname>                             //删除远程分支
$ git push origin --delete tag <tagname>                            //删除远程tag
$ git push origin :<branchName>                                     //推送一个空分支到远程分支,相当于删除分支
$ git push origin :refs/tags/<tagName>                              //推送一个空tag到远程,相当于删除tag

删除不存在对应远程分支的本地分支

假设这样一种情况:

  1. 我创建了本地分支b1并pull到远程分支 origin/b1;
  2. 其他人在本地使用fetch或pull创建了本地的b1分支;
  3. 我删除了 origin/b1 远程分支;
  4. 其他人再次执行fetch或者pull并不会删除这个他们本地的 b1 分支,运行 git branch -a 也不能看出这个branch被删除了,如何处理?
1
git fetch -p                                                        //在fetch之后删除掉没有与远程分支对应的本地分支

重命名远程分支

在git中重命名远程分支,其实就是先删除远程分支,然后重命名本地分支,再重新提交一个远程分支

1
2
3
$ git push --delete origin devel                                    //删除远程分支
$ git branch -m devel develop                                       //重命名本地分支
$ git push origin develop                                           //推送本地分支

把本地tag推送到远程

1
$ git push --tags

获取远程tag

1
$ git fetch origin tag <tagname>

合并branch上的指定文件

branches/A 上修改了一个文件A.h,新增了一个文件B.h,删除了一个文件C.h。

1
2
3
4
5
6
7
$ git checkout master                                               //首先切换到master分支

$ git checkout -p branches/A A.h                                    //不切换branch,把RemLanbranches/A上的A.h更新到当前分支

$ git checkout branches/A B.h                                       //去掉-p参数,新增该B.h文件

$ rm C.h                                                            //删除文件目前还没找到其他办法,但效果是一样的

分支的衍合

参考:http://git-scm.com/docs/git-rebase

diff

只显示两个分支间的差异,如果你想找出‘master’,‘test’的共有 父分支和’test’分支之间的差异,你用3个‘.’来取代前面的两个’.’ 。

1
$ git diff master..test

显示你当前的索引和上次提交间的差异;这些内容在不带”-a”参数运行 “git commit”命令时就会被提交。

1
$ git diff --cached

显示你工作目录与上次提交时之间的所有差别,这条命令所显示的 内容都会在执行”git commit -a”命令时被提交。

1
$ git diff HEAD

如果你要查看当前的工作目录与另外一个分支的差别,你可以用下面的命令执行: 这会显示你当前工作目录与另外一个叫’test’分支的差别。你也以加上路径限定符,来只 比较某一个文件或目录。

1
$ git diff test

显示你当前工作目录下的lib目录与上次提交之间的差别(或者更准确的 说是在当前分支)。

1
$ git diff HEAD -- ./lib

如果不是查看每个文件的详细差别,而是统计一下有哪些文件被改动,有多少行被改 动,就可以使用‘–stat’ 参数。

1
$ git diff --stat

Javascript设计模式 - 笔记2

| Comments

如何封装一个对象

门户大开型

最简单的办法就是按传统方法创建一个类,用一个函数来做其构造器。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var Book = function(isbn, title, author) {
    if (isbn === undefined) {
        throw new Error('Book constructor requires an isbn.');
    }
    this.isbn = isbn;
    this.title = title || 'No title specified';
    this.author = author || 'No title specified';
}

//define by attr
Book.prototype.display = function() {...};

//define by object literals
Book.prototype = {
    display: function(){...},
    checkIsdn: function(){...}
};
  • 优点:简单
  • 缺点:没有保护,需要加各种校验。但内部的成员还是有很大可能被修改的。

语法修饰增强型

用setattr,getattr等赋值取值方法及命名规范区别私有成员

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
var Book = function(isbn, title, author) {
    if (isbn === undefined) {
        throw new Error('Book constructor requires an isbn.');
    }
    this.setIsbn(isbn);
    this.setTitle(title);
    this.setAuthor(author);
}

//define by attr
Book.prototype.display = function() {...};

//define by object literals
Book.prototype = {
    _checkIsdn: function() {...}

    setIsbn: function(isbn) {
        if!(this._checkIsbn(isbn)) {
            throw new Error("Invalid isbn");
        }
        this._isbn = isbn;
    }

    getIsbn: function() {
        return this._isbn;
    }
    .......
};
  • 优点:简单,安全性也有所增强
  • 缺点:不是真正的私有成员,内部的成员还是有很大可能被修改的。

闭包实现私有成员

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
var Book = function(iisbn, ititle, iauthor) {

    //private attributes
    var isbn, title, author;

    //private method
    function _checkIsbn(iisbn) {
        ...
    }

    //privileged methods
    this.getIsbn = function() {
        return isbn;
    };

    this.setIsbn = function(iisbn) {
        this._checkIsbn(iisbn) ...
        isbn = iisbn;
    }

    .......

    //contructor code
    this.setIsbn(iisbn);
    this.setTitle(ititle);
    this.setAuthor(iauthor);
};

//public, non-privileged methods

Book.prototype = {
    display: fucntion(){},
    ....
}

这里应用了js的闭包特性,isbn等属性不再通过this来引用,而是放到函数的构造器里面。既要访问到私有成员,又要对外的方法放到函数的构造中,对私有成员没有依赖的函数用prototype。

  • 优点:比较完整的模拟了private特性
  • 缺点:private方法不再存在prototype里面,这样没生成一个新的对象实例都会为每个每个私有方法和特权方法生成一个新副本,耗费内存。

实现静态方法和属性

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
var Book = (function() {

    //private static attributes
    var numberOfBooks = 0;

    //private static method
    function checkIsbn(iisbn) {
        ...
    }

    //return the contructor
    return function(iisbn, ititle, iauthor) {
        //private attributes
        var isbn, title, author;

        //privileged methods
        this.getIsbn = function() {
            return isbn;
        };

        this.setIsbn = function(iisbn) {
            this._checkIsbn(iisbn) ...
            isbn = iisbn;
        }
        .......

        //contructor code
        numOfBooks++;
        this.setIsbn(iisbn);
    }
})();

//public, static methods
Book.converTotitleCase = function(){...};

//public, non-privileged methods

Book.prototype = {
    display: fucntion(){},
    ....
}

这里和闭包实现私有成员的区别就在于构造函数变成了一个内嵌函数,这样就创建了一个闭包,可以把静态的私有成员声明在最顶层。

实现常量

常量就设置为一个私有静态属性,用大写区分即可。我认为没有必要实现一个取值器去限制,用CONST前缀从代码风格上约束即可。

Javascript设计模式 - 笔记1

| Comments

富有表现力的javascript

弱类型语言

javascript中有三种原始类型:布尔型、数值型(不区分浮点数和整数)和字符串型。

此外,还有对象类型和包含可执行代码的函数类型。前者是一种复合类型(数组是一种特殊的对象)。

最后,还有空类型(null)和未定义类型(undefined)。

原始数据类型按值传送,其他数据类型则按引用传送。

函数是一等对象

  • 匿名函数
1
2
3
4
5
(function(){
    var foo = 10;
    var bar = 2;
    alert(foo * bar);
})();
  • 闭包
1
2
3
4
5
6
7
8
9
var baz;
(function(){
    var foo = 10;
    var bar = 2;
    baz = function(){
        return foo * bar;
    }
})();
baz(); //baz可以访问foo和bar,即使是在匿名函数外面执行
  • 作用域、嵌套函数和闭包

    • js中,只有函数具有作用域:

      在一个函数内部声明的变量,外部无法访问; 定义在一个函数中的变量在该函数的内嵌函数中是可以访问的

    • js中的作用域是词法性的:

      函数运行在定义他们的作用域中,而不是调用他们的作用域中; 可以利用这个特性定义静态方法和属性;

对象的易变性(mutable)和内省(introspection)

  • 易变性:js中可以对象前定义的类和实例化的对象进行修改
  • 内省:js中可以在运行时检查对象所具有的属性和方法

接口

  • 接口也是一种对象,判断一个类是否是实现了某类接口,就是传入这个接口,而后比较。

    java有专门的接口类,C++有虚基类,而C在linux kernel中的对象设计方法实际上也是一种接口实现,这都是接口在语言层面支持的体现

  • 封装(encapsulation)和信息隐藏:信息隐藏是目的,而封装则是藉以达到这个目的的技术

    java和C++有 private关键字作为支持,Python有’__‘的命名约定,js中一般用闭包来模拟

接口实现

用注释来模拟

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
/*
interface Composite {
    function add(child);
    function remove(child);
    ...
}

interface FormItem{
    function save();
}

var CompositeForm = function(id, method, action) {
    //implements Composite, FromItem
    ...
};

CompositeForm.prototype.add = function(child) {
    ...
}
CompositeForm.prototype.remove= function(child) {
    ...
}
CompositeForm.prototype.save= function() {
    ...
}

  • 优点:简单明了,代码体积小
  • 缺点:无法错误检查

用属性检查模仿接口

约定所有类明确声明实现了那些接口,和这些类打交道的对象可以针对这些声明做检查。

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
/*
interface Composite {
    function add(child);
    function remove(child);
    ...
}

interface FormItem{
    function save();
}

var CompositeForm = function(id, method, action) {
    //implements Composite, FromItem
    this.implementsInterfaces = ['Composite', 'FromItem'];
    ...
};
    ...

function addForm(formInstance) {
    if (!implements(formInstance, 'Composite', 'FormItem')) {
        throw new Error("object does not implement a required interface");
    }
}

function implements(objects) {
    for (var i = 1; i < arguments.length; i++) {
        var interfaceName = arguments[i];
        var interfaceFound = false;
        for (var j = 0; j < object.implementsInterfaces[j] == interfaceName) {
            interfaceFound = true;
            break;
        }
        if (!interfaceFound) {
            return false;
        }
    }
    return true;
}

  • 优点:有错误检查
  • 缺点:每次调用都要检查,啰嗦,另外防不住有说了实现但没有干活的

鸭式辨型模仿接口

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
var Composite = new Intreface('Composite', ['add', 'remove']);
var FormItem = new Interface('FormItem', ['save']);
var CompositeForm = function(id, method, action) {
    ...
};

function addForm(formInstance) {
    ensureImplements(formInstance, Composite, FormItem);
    ......
}

var Interface = function(name, methods) {
    if (arguments.length != 2) {
        throw new Error("Interface constructor called with " + arguments.length + "arguments, but expected exactly 2.");
    }
    this.name = name;
    this.methods = [];
    for (var i = 0; len = methods.length; i < len; i++) {
        if (typeof methods[i] !== 'string')  {
            throw new Error("Interface contructor expects method names to be " +
                            "passed in as string");
        }
    }
    this.methods.push(methods[i]);
}

Interface.ensureImplements = function(object) {
    if (arguments.length < 2) {
        throw Error("Functino Interface.ensureImplements called with " + arguments.length +
                    "arguments, but expected at leaset 2.");
    }

    for (var i = 1, len = arguments.length; i < len; i++) {
        if (interface.constructor !== Interface) {
            throw new Error("Function Interface.ensureImplements expects arguments" +
                            "two and above to be instances of Interface.");
        } 
    }

    for (var j = 1, methodsLen = interface.methods.length; j < methodsLen; j++) {
        var method = interface.methods[j];
        if(!object[method] || typeof object[method] !== 'function') {
            throw new Error("Function Interface.ensureImplements: object " +
                            "does not implement the " + interface.name)  +
                            "interface.Method " + method + " was not found.");
        }
    }
}

  • 优点:进一步加强了错误检查
  • 缺点:增大了调试难度

Macvtap Ethernet Support Tcpdump

| Comments

用macvtap模拟网卡时,用tcpdump抓包是抓不到的,后来发现3.14版本以上的内核修正了这一点。

参考这个提交:

https://github.com/torvalds/linux/commit/6acf54f1cf0a6747bac9fea26f34cfc5a9029523

Linux多网卡多路由设置

| Comments

折腾了半天,原始出处不知道了,转记一下。

比如如果一个linux服务器有三个口接三个不同的网络,假设对应的网络信息是如此

  • eth0是电信,ip地址为1.1.1.1/24,电信网关为1.1.1.254

  • eth1是网通,ip地址为2.2.2.2/24,网通网关为2.2.2.254

  • eth2是教育网,ip地址为3.3.3.3/24,教育网网关为3.3.3.254

传统情况下,如果是为了从内向外访问获得更好的速度,让访问电信走电信,访问网通走网通,那么配置是网关只能够配置一个。

比如以电信为主的,那么网关就只设置电信的1.1.1.254,而针对网通和教育网设置不同的路由,路由下一跳指向网通和教育网对应的 网关。

如果这样做的目的只是实现内部访问外面,那么是没问题了,但是如果是为了让外面的用户能够正常访问到服务器上的服务就会出问题。比如电信用户会无法访问网通和教育网的ip,网通用户会无法访问电信和教育网的ip。

要解决这个问题,思路就是由哪个网口进来的流量希望全部就由哪个回去。用lartc里面提到的方法就是来源的口不同,走不同的路由表。在默认的路由表基础上再建立三个路由表。

用 ip route show 可以看到默认有local,main,default三个路由表,这三个路由表的名称命名来自 /etc/iproute2/rt_tables ,这里先在这个配置文件里面添加三个不同的路由表表名,

1
2
3
echo “101 ChinaNet” >> /etc/iproute2/rt_tables
echo ”102 ChinaCnc“ >> /etc/iproute2/rt_tables
echo ”103 ChinaEdu“ >> /etc/iproute2/rt_tables

之后建立这三个路由表的内容,因为这三个路由表的只是用来响应来自不同接口的,而不是用来相应从哪个接口出去的,所以只需要每个路由表里面建立默认网关即可。

1
2
3
ip route add default via 1.1.1.254 dev eth0 table ChinaNet
ip route add default via 2.2.2.254 dev eth1 table ChinaCnc
ip route add default via 3.3.3.254 dev eth2 table ChinaEdu

之后再加上三条规则,使来自不同的口的走不同的路由表

1
2
3
ip rule add from 1.1.1.1 table ChinaNet
ip rule add from 2.2.2.2 table ChinaCnc
ip rule add from 3.3.3.3 table ChinaEdu

至此无论是电信还是网通还是教育网用户,访问三个ip的任意一个地址都能够连通了。即便是服务器上本身的默认路由都没有设置,也能够让外面的用户正常访问。

命令汇总:

1
2
3
4
5
6
7
8
9
10
11
12
13
ip route show

echo “101 ChinaNet” >> /etc/iproute2/rt_tables
echo ”102 ChinaCnc“ >> /etc/iproute2/rt_tables
echo ”103 ChinaEdu“ >> /etc/iproute2/rt_tables // 这里也可以直接通过Vi编辑

ip route add default via 1.1.1.254 dev eth0 table ChinaNet
ip route add default via 2.2.2.254 dev eth1 table ChinaCnc
ip route add default via 3.3.3.254 dev eth2 table ChinaEdu

ip rule add from 1.1.1.1 table ChinaNet
ip rule add from 2.2.2.2 table ChinaCnc
ip rule add from 3.3.3.3 table ChinaEdu //如果用数字,可以不要上面的“echo”过程

Libpcap PACKET_MMAP内存分配

| Comments

libpcap为了提高效率,调用setsockopt(handle->fd, SOL_PACKET, PACKET_RX_RING,(void *) &req, sizeof(req))时采用kmalloc分配内存。

可以参考:

https://www.kernel.org/doc/Documentation/networking/packet_mmap.txt

kmalloc底层依赖linux的slab内存分配机制,在2.6.22内核之后,slub取代slab成为默认的内存分配器。空间和时间上都有所提升。值得升级。

另外,centos5.9默认采用的2.6.18内核,编译的时候默认的KMALLOC_MAX_SIZE 设置为size-131072,这对于有很大内存的机器,分配效率是不高的。

最简单的办法就是用rpm包升级到2.6.33.9-rt31.86.el5rt,这个内核编译的时候已经将KMALLOC_MAX_SIZE设置为size-4194304。

这个问题是追踪libpcap的抓包程序,内存分配频繁失败发现的。

只能说,内核升级频繁,很多编译开关影响很大,要想全面发挥linux的性能,只能紧跟潮流啊。

Docker中的网络

| Comments

Docker的默认网络是非常弱的,他使用的是一个虚拟网桥和container中的veth pair通信,在container中,默认是没有对外的IP的,外部主机或容器只能通过NAT,或者自定义iptable来实现主机或容器间的互联互通。 这种局限性非常明显:

  • 如果我要配置一个sshd service,需要手工配置转发规则,非常不便

  • 无法使用DHCP

  • NAT无法在宿主机上用一个端口提供不同服务,所以有多个container绑定到一个物理网卡时,因为无法分配多个对外IP,所以诸如Http 这样的服务只能跑在同一IP的不同端口上。

  • 在Container中无法正常tcpdump

理想的容器内网卡应该像VMware的NSX那样,让你’基本上’感觉不到这是个虚拟的网卡,当然,这个和Docker的初衷有点不符了。但我们解决问题为先,工具是那一个,但不同人用法不同。

将Docker Container连接到本地网络,有四种搞法 (具体请参考:http://blog.oddbit.com/2014/08/11/four-ways-to-connect-a-docker/),下面简单描述下:

  • 采用官方默认的搞法,用NAT:
1
2
3
ip addr add 10.12.0.117/21 dev em1

docker run -d --name web -p 10.12.0.117:80:80 centos/simpleweb

这种方法简单,但也有上面所说的各种缺点

  • 建立自己的网桥和veth pair,为每个网桥分配一个IP,每个veth对绑定一个网桥,映射进docker容器,这样在容器内就得到了一个接近于真实的网卡。并且有能与本地网络的通信

    这种办法其实是对默认docker网络实现的一种升级,但是解决了原来的诸多局限,缺点是操作比较麻烦,另外容器内用tcpdump也会有问题

  • 使用Open vSwitch Bridge,模拟第二种办法。

    这种方法就是用Open vSwitch简化了操作,但是又引入了一套东西。

  • 建立macvlan虚拟网卡,容器启动后用nsenter工具映射到容器的network namespace中

    这种办法我觉得是最干净简洁的,而且采用macvlan,还意外获得了一种能力,就是你能在宿主机上创建子macvlan设备,从而能向容器内的macvlan设备打入精确的流量。 采用这种方法得到的container,可以在里面启动sshd,远程ssh上去各种操作,这样使用同一般的虚拟机没有多大差别。 另外,在容器内tcpdump包也很完美,如果想捕获二层协议包,可以用macvtap替换macvlan。

需要注意的是,如果想要tcpdump macvtap,需要linux kernel 3.14以上的支持,参见这里

最后推荐为了简化macvlan的操作,我写的一个小工具:dockerfly

参考:

  • Linux 上的基础网络设备详解

http://www.ibm.com/developerworks/cn/linux/1310_xiawc_networkdevice/index.html

  • Linux 上虚拟网络与真实网络的映射

http://www.ibm.com/developerworks/cn/linux/1312_xiawc_linuxvirtnet/index.html

  • 网络虚拟化技术: TUN/TAP MACVLAN MACVTAP

https://blog.kghost.info/2013/03/27/linux-network-tun/

  • Coupling Docker and Open vSwitch

http://fbevmware.blogspot.com/2013/12/coupling-docker-and-open-vswitch.html

  • four ways to connect a docker

http://blog.oddbit.com/2014/08/11/four-ways-to-connect-a-docker/

  • Docker containers should not run an SSH server

https://news.ycombinator.com/item?id=7950326

  • Proposal: Native Docker Multi-Host Networking

https://github.com/docker/docker/issues/8951

多个git账号之间的切换

| Comments

做过很多遍了,却总是记不住,这回从头来描述一下。

介绍

所谓多个git账号,可能有两种情况:

  • 我有多个github的账号,不同的账号对应不同的repo,需要push的时候自动区分账号

  • 我有多个git的账号,有的是github的,有的是bitbucket的,有的是单位的gitlab的,不同账号对应不同的repo,需要push的时候自动区分账号

这两种情况的处理方法是一样的,分下面几步走:

处理

  • 先假设我有两个账号,一个是github上的,一个是公司gitlab上面的。先为不同的账号生成不同的ssh-key
1
    ssh-keygen -t rsa -f ~/.ssh/id_rsa_work -c [email protected]
然后根据提示连续回车即可在~/.ssh目录下得到id_rsa_work和id_rsa_work.pub两个文件,id_rsa_work.pub文件里存放的就是我们要使用的key
1
    ssh-keygen -t rsa -f ~/.ssh/id_rsa_github -c [email protected]
然后根据提示连续回车即可在~/.ssh目录下得到id_rsa_github和id_rsa_github.pub两个文件,id_rsa_gthub.pub文件里存放的就是我们要使用的key
  • 把id_rsa_xxx.pub中的key添加到github或gitlab上,这一步在github或gitlab上都有帮助,不再赘述

  • 编辑 ~/.ssh/config,设定不同的git 服务器对应不同的key

1
2
3
4
5
6
7
8
9
10
11
12
# Default github user([email protected]),注意User项直接填git,不用填在github的用户名
Host github.com
 HostName github.com
 User git
 IdentityFile ~/.ssh/id_rsa_github

# second user([email protected])
# 建一个gitlab别名,新建的帐号使用这个别名做克隆和更新
Host 172.16.11.11
 HostName 172.16.11.11
 User work
 IdentityFile ~/.ssh/id_rsa_work

编辑完成后可以使用命令 ssh -vT [email protected] 看看是不是采用了正确的id_rsa_github.pub文件

这样每次push的时候系统就会根据不同的仓库地址使用不同的账号提交了

  • 从上面一步可以看到,ssh区分账号,其实靠的是HostName这个字段,因此如果在github上有多个账号,很容易的可以把不同的账号映射到不同的HostName上就可以了。比如我有A和B两个账号, 先按照步骤一生成不同的key文件,再修改~/.ssh/config 内容应该是这样的。
1
2
3
4
5
6
7
8
9
10
11
12
# Default github user([email protected]),注意User项直接填git,不用填在github的用户名
Host A.github.com
 HostName github.com
 User git
 IdentityFile ~/.ssh/id_rsa_github_A

# second user([email protected])
# 建一个gitlab别名,新建的帐号使用这个别名做克隆和更新
Host A.github.com
 HostName github.com
 User git
 IdentityFile ~/.ssh/id_rsa_github_B

同时你的github的repo ssh url就要做相应的修改了,比如根据上面的配置,原连接地址是:

[email protected]:testA/gopkg.git

那么根据上面的配置,就要把github.com换成A.github.com, 那么ssh解析的时候就会自动把testA.github.com 转换为 github.com,修改后就是

[email protected]:testA/gopkg.git

直接更改 repo/.git/config 里面的url即可

这样每次push的时候系统就会根据不同的仓库地址使用不同的账号提交了

一些题外话

我有一个repo,想要同时push到不同的仓库该如何设置?

很简单, 直接更改 repo/.git/config 里面的url即可,把里面对应tag下的url增加一个就可以了。例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[remote "GitHub"]
    url = [email protected]:elliottcable/Paws.o.git
    fetch = +refs/heads/*:refs/remotes/GitHub/*
[branch "Master"]
    remote = GitHub
    merge = refs/heads/Master
[remote "Codaset"]
    url = [email protected]:elliottcable/paws-o.git
    fetch = +refs/heads/*:refs/remotes/Codaset/*
[remote "Paws"]
    url = [email protected]:Paws/Paws.o.git
    fetch = +refs/heads/*:refs/remotes/Paws/*
[remote "Origin"]
    url = [email protected]:Paws/Paws.o.git
    url = [email protected]:elliottcable/paws-o.git

上面这个立即就是有4个远端仓库,不同的tag表示不同的远端仓库,最后的Origin标签写法表示默认push到github和codaset这两个远端仓库去。当然,你可以自己随意定制tag和url

我有一个github的repo,clone没有问题,push的时候总是报错:error: The requested URL returned error: 403 while accessing xxx

这个问题也困扰了我一段时间,后来发现修改 repo/.git/config 里面的url,把https地址替换为ssh就好了。

例如

url=https://[email protected]/derekerdmann/lunch_call.git

替换为

url=ssh://[email protected]/derekerdmann/lunch_call.git

参考

http://stackoverflow.com/questions/7438313/pushing-to-git-returning-error-code-403-fatal-http-request-failed http://stackoverflow.com/questions/849308/pull-push-from-multiple-remote-locations/3195446#3195446

科技进步是安全行业的最根本保障

| Comments

今天上网的时候突然发现有人邮件问什么是矿震。原来是知乎的注册号职业还是煤矿行业,知乎还是挺智能的~~~

查了一下,辽宁那又有矿难了,矿震引起煤尘爆炸。唉……

什么是矿震呢,矿上有叫岩裂的,有叫岩震的,说法不一。书上的说法是冲击地压。通俗点就是地底下挖空了,巷道两侧岩石受不了压力,就压裂塌陷了。

我是没遇见过,矿上听过一些老师傅讲岩裂的时候,最可怕的是两侧的石子像子弹一样弹出来,人在那个时候是没什么办法的,趴在地上逃过一劫,一般高瓦斯的矿也会引发爆炸之类的事故,这个时候就听老天爷的了。

又胡思乱想了很多事情。

我刚入行,到各个煤矿下过一些井的时候,有很多不明白的事。那时候我一边看书上的一些矿压理论,一边在井下看实际的生产,发现了很多可以改进的地方。比如离层监控,直接顶垮塌预警,还有支护距离、支护强度的选择~~~ 这些事情都是有现有理论支撑的,很多情况下找几位真正懂行的专家来,做好规划和基础建设,后期是能省下很多气力的,生产也能安全的多。

那个时候,我用很幼稚的办法写着一个支护专家系统,想着可以用电脑帮助预测离层压力,该如何选型液压支柱等,还想着要是有个论坛,供全国的煤矿从业者交流经验,提炼知识就好了。

后来,我又下了一段时间的井,有一个很心凉的发现或想法,现在也不能摆脱:

那就是,人命也许不是那么值钱,无论是在他人眼里,或是在自己眼里。

我记得那时候外来人员,像我这样的厂家人员下井,一般会买20几万的地险,我至今也没有去查过之前从业的公司有没有给我买工伤保险,有点可笑是不。可事实就是这样,在山东很多小县城,正规给工人交齐保险的小工厂几乎没有,相比下,富士康其实已经是非常良心的企业了。即使企业按最低标准给你交齐各种保险(这家企业还能正常运转),你换工作,流动到其他地方后,这些钱大部分还是要孝敬当地政府的,而且那点钱再转到外地还不够跑腿的功夫。可能一些工作稳定,长期在一个地方生活的人体会不多。但是当我辗转几个地方后,就发现交类似养老保险这种事无论对企业还是工人都是非常不划算的。

所以在很多地方,工人和工厂心照不宣,在没有保障的前提下尽量活着。

事实上,无论是工厂还是工人自己,对自己的命值多少钱,估量的数字都少得可怜。在非常好的条件下,一个青壮年在井下挂了,能得到的赔偿我估计就在30-40万之间。而且这还是”毛利”,为了得到这笔赔偿你还要杂七杂八的各种打点开销。这个估算就是以山西国有大矿为参照,像甘肃那样年产40万吨的小矿就不说了。

但更令我惊奇的是,甘肃那样的小矿,井下条件反而比山西的大矿好得多,井口的巷道会贴瓷砖,会有很明亮的灯,下井的工人也比大矿的工人心情更宽松。虽然硬件设施比不上人家财大气粗,但软件上的很多小细节都很贴心。

后来我明白了,就是西边的老矿区,一般还是把人当人来看的,说的不好听些,你真的埋在井下了,虽然赔的钱比不上大矿,但一般还有人情世故,给你家其他人安排个矿上清闲活,家里也有矿上的左邻右舍照顾着,这就是我们老派国企的作风。 像山西很多大矿,我觉得是市场化和国有企业的奇怪混合体。工人的价值,管理制度,硬件建设,都市场化了;说白了,就是这个投入下去,要比你的命便宜,就投,你的命贱,就不投。但煤炭销售,利润分配,还是要国家来管。

有很多瞎扯的专家讲这个制度,那个管理。似乎每次事故出来都是管理问题,制度问题……不错,肯定是管理问题,制度问题。但是,我下井的时候,干活很累的时候,不止一次想过:就是现在顶塌了,也无所谓。 人在极累的时候,什么也顾不上的。这是人不可克服的弱点。当工人连自己性命也轻忽的时候,你和他讲这个管理,那个制度,专家们,敢到井下去讲讲吗。

管理当然很重要,后来我觉得,安全行业最根本的保障在于科技进步。只有人远离危险的时候,才有闲情谈管理,谈制度。

听新闻说中国已经成为全世界最大的机器人消费市场,非常高兴。

有人说会有下岗,会有就业萎缩。

我只想问:人命有贵贱吗?人命有价格吗?

如果你不想回答这种无聊的问题,就不要再对更无聊的问题评头论足了。