Becomin' Charles

算法 | LNMP | Flutter | Mac

Becomin' Charles

SOCKS 代理协议是网络上使用非常普遍的一种协议,最近因为想要自己搭建一个穿透局域网安全网关的代理,所以,顺便仔细学习了一下 SOCKS 的相关资料,还算有点意思,特此记录一下。

SOCKS 协议的故事

话说,一个叫做 David Koblas 的系统管理员,他在 MIPS Computer Systems 公司工作。就是这个人发明了 SOCKS 代理协议。MIPS 公司在 1992 年,被一家叫做 Silicon Graphics 的公司给控制了(应该是收购了吧),这家公司后来把自己的品牌名改为了 SGI(一家很高端的软硬件制造商,2009年4月破产了)。也就是在这一年 1992 年,David 在 Usenix Security Symposium 安全研讨会上,发布了一篇论文,名字就叫 SOCKS,将此协议公之于众。

最早,整个互联网采用的是盲目信任的方式进行连接和组织的。当时,专家们在聚会的时候,讨论的都是怎么让网络更加简单和高效,一台主机在网络上到底怎么才能更可靠的被别的主机所连接。一直到 1988 年,一个叫做 Morris 的蠕虫,给整个互联网一记重拳,后来大家讨论将一个子网络接入到互联网的时候,安全也成了一个必须要讨论的重要话题。

不过,提升网络的安全性可没有什么容易的办法,想出来的大部分办法都是通过减少内网服务器向公网暴露的机会,来最小化攻击的概率,也就是我们常喜欢用的“最小原则”,如果没有必要,就不进行授权。很多网络在接入互联网的时候,都选择了单一出入口的方式。将本地的子网络,置于防火墙的保护之后,再接入到互联网,如此一来就极大减少了被攻击的机会,网络的安全性自然就提高了。

但是本地网络置于防火墙之后,再访问外部网络的资源就会非常不便。为了解决这个问题,人们想出了各种方案。内外网隔离(笨拙,使用极其不便,但是维护成本很低),单一机器授权(只有一台机器可以和外网进行双向访问,使用仍然非常不便,而且维护成本高昂,因为有很多用户的访问权限要分配和收回),还有安全路由器(常见的一种策略是允许所有的出口流量,但是对进口流量禁止所有 1024 以下端口的访问,这种策略的问题是,一旦路由被攻破,整个网络就置于威胁之下)。

OSI 7层网络模型

在这些方案之外,代理防火墙(Proxy Firewall)解决方案就被提出来了。路由器是在 OSI 模型的“网络层”进行安全过滤,可以降低客户端的成本,但是不够完善,维护起来也比较困难,估计当年的硬件不像现在可以随意修改配置吧。代理防火墙的方案,平衡了使用的便捷和维护的复杂度,是一种折衷。

代理防火墙工作在 OSI 模型的“会话层”,也就是“传输层”和“应用层”中间的地方。著名的 SOCKS 就是这样一种“解决方案”。它是一种非常轻薄的解决方案,在客户端,提供了一套开发类库,叫 SOCKS 库,对照着标准 socket 的 API,提供了五个 API 函数,名字跟 socket 的一样,只是用 R 作为前缀。在服务器端,提供了一个叫做 sockd 的伺服软件,这个软件部署在防火墙系统所在的一台主机上,通过简单的配置文件就可以完成应用层的过滤,允许或拒绝哪些目的地址和端口被接入,是非常容易维护的,给网络管理员带来了极大的方便。

在 1992 年公布之前,SOCKS 解决方案已经在 MIPS 内部使用了长达三年之久,没有遇到明显的问题和瓶颈,所以 David 认为这是一个久经烤验的成熟方案。

SOCKS 的版本发展

上面的故事里,我们可以看到 SOCKS 最初提出的时候,其性质是一个解决方案,包含一个类库和一个服务端后台伺服程序。但是,NEC 公司的李英达?(Ying-da Lee),看到了它的优美之处,把它提炼出来,发展成了一种协议,变得更加通用,而且,大家都可以根据协议提出自己的实现。这位李同学,提出了 SOCKS 协议的第四个版本。也是流传非常广泛的版本。

从李同学开始,SOCKS 的定位也变得非常明确,就是在防火墙服务器上,提供一种 TCP 会话的转发,允许用户可以透明的穿透防火墙的阻拦。这种协议的优势在于,它完全独立于应用层的协议,可以用在很多的场景,telnet,http,ftp 都不在话下,并且可以在 TCP 会话开始之前,完成访问权限的检查,之后只要做来回往复的转发即可。而且由于此协议完全不关心应用层的协议,所以应用层通信可以加密,保护自己通信的内容不被代理所看到。

SOCKSv4 开始,这个协议得到了非常广泛的应用,所以,广大群众对这个协议进行了扩展,就有了 SOCKSv4a 版本,这个版本只对 v4 进行了比较小的改动,主要是允许在协议头里填充域名,代替IP地址,用以防止客户端不能正确解析出目标IP的场景。(举个例子,某个局域网有一台服务器,域名是 test.oa.com,与之对应的 IP 地址也是内网的,这种情况下,外部用户是很难知道到底 IP 地址是什么的,因为没有登记在公网的 DNS 服务里面)到这里后,SOCKS 就成了一种非常通用的双向通信的轻量级代理协议了。也有了大量的标准实现。得到了海量的需要网络连接的应用程序的支持,成为了“电路级网关”(circuit-level gateway)的事实标准。

现在更加流行的 SOCKSv5 版本就是一个更加完善的版本了,主要增加了:

  • 强力的身份验证方案
  • 验证方法的协商机制
  • 地址解析的代理
  • UDP协议应用的代理支持

SOCKS 协议的内容

SOCKSv4的内容

上面的插图展示了SOCKS协议第四版的内容,可以看见,这里展示的是前两个包体,客户端发起,服务器回复,连接成功后,后续的操作就透明了。

SOCKSv4a 对第 4 版的扩展,非常小,就是增加了一个域名,只要在 IP 那个字段,前三个字节都填 0x00,就会由服务器来负责解析正确的地址,并利用回包空闲的两个字段来返回正确的 IP 地址和端口号码。

到了现在普遍使用的 SOCKSv5,协议变复杂了很多。首先,因为有强力的验证,而且支持多种验证方法,就有了一个协商验证方法的过程,然后,进行身份验证,最后再进行通信指令。

SOCKS5 的协商过程

上面的图,展示了 SOCKS5 连接的协商过程。协议的具体内容,对应着上面的握手环节,增加了几种不同的包体:

SOCKS5 的协议包体结构

SOCKS 协议的不足

从上面的 SOCKS5 的时序图上,我们可以看到,由于承担了额外的验证协商的功能,导致 SOCKS5 在建立的时候,需要额外消耗多达三次握手,如果不需要验证身份,也需要两次握手,这就增加连接时候的延迟。

另外,因为 SOCKS5 协议本身完全不关注应用层的内容,所以,客户端和目标服务器的通信,加密完全依赖客户端和服务器的通信协议,如果服务器使用的是 HTTP 协议,那么通过代理走的流量,就相当于是明文在内网传输。安全性上不是很高。

不过,我们可以通过在外面包裹一层 TLS 来解决这个问题,就形成了 SOCKS5 over TLS 的解决方案,有效加密了通信的内容。TLS 负责加密连接,SOCKS5 负责代理协议控制。

互联网上已经公布了 SOCKSv6 的草案,主要内容就是SOCKS5的问题修复,第一个就是多次握手已经不太适用于移动互联网和卫星通信等网络环境,需要被优化。客户端会尽可能多的发送信息给服务器,并且要求创建 socket 之前,不等待验证的结论;连接请求模仿 TCP Fast Open 的语义,具体就是在连接请求的包体里,带上一部分载荷,连同第一个SYN包一同发给服务器;根据选项可以不向后兼容;可选支持 0-RTT 的验证方法(一个IP包体到达服务器再返回客户端的过程消耗,叫 1-RTT)。

不过,目前来说,还没有得到广大软件厂商的支持。而且据说 TLS 1.3 也在积极探索 0-RTT 的解决方案,不过也还在协议草案的早期。

参考文献:

以前,我也注意到,不在现有项目中引入框架是有原因的,而且,尤其不能选用 Yii 框架。

“继承”噩梦

你所有的 controller,都继承自 CController,其又继承自 CBaseController,这个又继承自 CComponent。

所有你的 model,都继承自 CActiveRecord 或者 CFormModel,它们又继承自 CModel,这个 CModel 也继承自 CComponent。

这两者的继承链条,都包含了静态变量,并且执行了其他很多类的静态方法。这两个因素造成调试过程变得极为困难和冗长。

全局状态

有多种形式的全局状态。PHP 程序员熟知的就是全局变量。但是这并不是唯一的一种形式。类中的静态变量,也是一种全局状态,它(总是)能够造成看起来没有关系的类实例神秘地交互。

(Yii 框架)对全局状态的使用是其核心机制。在代码中,你到处都能看到静态方法的调用,而且,Yii 的配置文件,离了全局状态就没法工作。

每次你调用 Yii::app()的时候,你都在访问或者改变它。

这使得统一测试你的 Yii 应用,成为了不可能。而调试工作,则变成了练习在你的整个项目中使用 grep。

紧耦合

当你使用 Yii 创建一个应用的时候,如果不启动整个框架,你是不可能只运行其中一部分的。大多数时候,取决于你最终在你的代码里用了静态方法。

每次你在你的代码里添加了一个静态方法的调用,这段代码就跟那个静态方法的类耦合起来了。这就是紧耦合。

你可能已经注意到了(我希望你注意到了),有其他的方法可以实现一样的效果,使用 new 操作符。这是另一个方法让你的代码跟某个特定的类耦合起来。没有接口。

无论 Yii 项目的配置文件看起来有多吓人,这个配置文件的设计仍然是用心良苦的。这是在本已经一团糟的代码里,再度引入外部代码并代替已经存在的组件的伤害最小的一种方式。

然而,它将(Yii 框架)缺少接口和存在耦合带来的问题带到了聚光灯下。

很多程序员想要替换的组件之一是 CUrlManager,大多数是因为你能够传递额外的参数(给它)的方式的原因。

OOP 中的接口,明确了两个实例间的约定。由你来定义实例的能力,能被别人调用的方法。如果一个大的代码里,没有接口的话,你就不得不猜测,哪些方法是必须的,哪些不是。

在 Yii 组件里面,问题甚至更加严重,因为使用了静态调用和深度继承。上文提到的 CUrlManager 继承了 CApplicationCompnent,它又继承了 CComponent。而 CUrlManager 的同一个文件里,还定义了 CUrlRule 和 CBaseUrlRule。

当你编写一个他们的替代品的时候,你必须写一些代码,将他们插入到配置文件中,并在你自己的 Application 中测试,使用这种方式,你才知道下一个需要你填写的方法是什么。

基本上来说,这是一种“保存,然后看看什么东西爆掉了”的研发方法。

这绝不是 MVC!

Yii 并没有实现 MVC 或者任何受 MVC 启发的设计模式。它叫做“MVC”的东西,实际上可以用“ActiveRecord-Template-Logic”模式来描述。

Yii 框架的作者,实现了一组 active record 和表单封装,而不是一个恰当的模型层,这导致应用的逻辑必须写在 controller 里面。

另一方面,你有被美化的模板,而不是包含展示逻辑的适当视图实例。这在一定程度上被小部件的使用所缓解,但小部件反而遭受单一职责原则(SRP)的侵犯,因为它们被迫包含部分展示逻辑并执行部分渲染。剩余的展示逻辑又一次最终在控制器中。

更糟糕的是,“控制器”还必须处理授权问题。这通常意味着,每当你更改访问方案时,你将不得不检查每一个CController的实例,以确定是否也需要更改。

这不是MVC。这是一个混乱,名字是从MVC设计模式中借来的,随意贴在一些组件上。
所有的小事情…

框架也有一些小问题,不值得单独成章:

定义一个文件中多个类:

这很快就会变得令人烦恼,因为有些类被硬塞到完全不相关的文件名中。这意味着,调试往往需要使用搜索。

随意添加的“模块”:

看起来,这些模块是在事后添加到框架中的。这就是为什么,当你需要设置默认模块时,你将不得不在配置文件参数中设置,该参数被称为’defaultController’。

网站 502,看 log

发现 mysql storage engine error 28,不明白原因

google 被提醒磁盘满了

du -sm * 查看哪个目录占空间大,发现无法执行,因为 /tmp 目录计算不出来

ls /tmp wc -l 发现计算不了,主要 ls 执行不了

怀疑文件过多,google 找方法,因为 ls 默认排序导致

ls -f /tmp
ls -U /tmp
ls –sort=None /tmp

等方法,发现文件有 145万个

都是 sess_xxxx 为 PHP 的 session 文件

删空 sess 文件,处理 sess 文件的自动清理问题

全部做完发现,磁盘仍然是满的

du -sm *

发现是 log 过大

查看 log 是因为 WP 报错导致,原因是数据库 user 没有权限

授权后,问题解决,但是牵扯了更多的问题,有待解决

  1. wordpress 主从同步方案的缺陷
  2. 服务器监控报警

Yii 2.x 最底层的抽象是 Object,但是,窃以为,最重要的基础抽象,仍然是 Component。这点与 Yii 1.x 相比,并没有本质的变化,就是,我仍然没有参透,为什么要多出来一个 Object 的抽象,表面上看,这个 Object 比 Component 少了不少东西,如果没有必要的话,显然继承 Object 会更加合算一点,但是这么一点好处的话,简直微不足道。

Yii 1.x 的 CComponent 的回顾

我们先来回顾一下 Yii 1.x 的 CComponent 到底提供了一些什么样的核心抽象呢?

  1. 属性的抽象,支持 getter 和 setter
  2. 事件,以及配套的事件处理器
  3. Behaviour

所有 Yii 框架的对象几乎都会继承 CComponent,也即在 Yii 框架里,几乎所有的对象,潜在都会支持这几个特性。属性,我们在 Object 的文章里,已经谈及了,这个抽象,可以说是面向对象程序设计的基础,其实是对成员变量的一层封装,在成员变量基础上,提供了更灵活的可见性控制。不过,Yii 1.x 的属性实现,是有点奇怪的,这个怪异性我本来是无感知的,看完了 Yii 2.x 的实现后,我才明白它怪在哪里。1.x 的属性,在判定 hasProperty 的时候,只承认有 getter 或者 setter 方法的属性是属性,如果用公有成员变量的话,不被承认为属性。其实,我们从效果上看,公有成员变量,其实看起来就比较像是一个读写都开放的属性。使用 __set 和 __get 的魔术方法,无非是想让一个属性,用起来更像是一个公有成员变量那样,哪怕它并不存在实体,但是,一个公有成员变量真的存在时候,哪怕它用起来像是一个属性,但是竟然不被承认是属性,这确实有点奇怪。

Yii 1.x 的事件机制,我认为实现得并不好,其主要原理是,使用 CComponent 类的一个私有成员变量 _e 来记录事件处理器,所以,就预示着一个特点,就是事件的绑定,必须在对象创建之后。这就带来了极大的局限性,什么局限性呢,限制了事件绑定的位置。例如,我想要捕获一个事件,必须在产生这个事件的对象创建之后,这个事件发生之前去绑定我的处理器。按照我们一般的经验,这个,最好就是在对象创建刚刚完毕的时候,其实也就只能写在 __construct 函数的最后一行。如弱你要不想把事件绑定给写到对象类里面,那就更难用了。这么实现,一个缺点,是会把事件的绑定,散布到各个类的实现里面,另一个,就是当目标对象的类,不能修改的时候,会很痛苦,比如,是某个类库或者别的团队维护的类,那就很难看了。只能继承它,然后覆盖构造函数,但是如果对方的模块没有提供替换目标类的方法,你基本就束手无策了,所以,这种设计,对整个应用全局的代码设计,都提出了非常高的要求,极大的降低了易用性。

此外,还有一个比较难看的点是,事件的附着句柄,是一个以 on 前缀的方法名,也就是你想出发一个叫 afterBuy 的事件的时候,你必须在你订单类里面,定义一个方法,叫 onAfterBuy,而这个方法可能除了触发这个事件外,别无其他业务了,所以,如果事件比较多,就会在你的类实现里面,出现很多的只有一行的方法。至少从视觉上是比较难看的。

第三个东西,就是 Behaviour,这个我特意没有翻译成中文,因为字面意思是“行为”,但是这两个字,是无从表达这个东西的实质的。在 Yii 框架的文档里,会提到这个东西的别名,是 Mixin,字面意思是“混入”,但是,这个也比较难以说明问题。从现象上理解,就是一个类可以调用本不属于自己的成员方法,通过 __call 这样的 Magic Method 来实现的,利用了 PHP 语言动态的特性做到的。但是,这个Behaviour 也好,Mixin 也好,到底带来了什么好处,这也是我在整个应用框架过程中,所不能体会的一个点。这个 Behaviour 的实现,如果我没猜错,我觉得,也是有一定的问题的,实现得并不那么方便,因为,每个具体的对象,通过一个 behaviours() 方法来注册 Behaviour,但是,并不是每个品种的对象,都支持这个特性的。事实上,我搜了一下,在 Yii 1.x 里面,只有 7 个类,缺省激发了 Behaviour 机制。

它们是:CModule,CApplication,CController,CConsoleCommand,CActiveRecord,CFormModel,CApplicationComponent。这个机制,一般,都在 __construct 方法里面被激活,但是,也有例外的,比如 CApplicationComponent 在 init 方法里被激活,确实比较奇怪。除了这 7 个类,以及它们的子类,Yii 1.x 框架里,别的对象,虽然也预置了这个机制,却不是默认被激活的。

我简单查了一下维基百科,这个 Mixin 的思想,本质上,还是提供了一种抽象的颗粒度,比类更加细小,复用程度更高。另一方面,Mixin 的机制,可以部分弥补没有多重继承的烦恼,而用不着引入多重继承的复杂性。其实,PHP 在语言层面提供了支持,就是一个叫 traits 的特性,比框架带的这个 Mixin 强很多,但是,这个在 PHP 很高的版本才出现的,在语言版本没有更广泛的扩散前,还是框架自己实现比较好。

这个东西实现的缺点,和事件机制,有点异曲同工。都是绑定点必须在目标类实现里面去写,才比较合适,一旦不能改目标类实现,就会浑身都蛋疼了。在整个 Yii 1.x 使用经验中,我用到这个特性的机会少之又少,第一,是没太看懂这个东西的本质是什么,另一个是,这个东西也确实用起来没那么方便,假使回到从前我能有现在对这个特性的领悟,说不定我能够用起来吧,无聊的感慨啊~

Yii 2.x 的 Component 实现

在了解历史的基础上,在回到现实,可能就多了很多的便利。我们现在来看看 Yii 2.x 提供的核心抽象有哪些改进。

从整个 Component 提供的公有成员函数列表来看,到了 2.x Yii 框架的核心理念,并没有什么巨大的变化。仍然是三个主流的东西,属性、事件、Behaviour。

第一个不同,是 Component 不再是最底层的抽象了,底下还有 Object,所以 Component 是继承自 Object 的。但是,我们仔细看 Component 的方法实现,就会发现,Component 几乎没有从Object 继承任何东西回来。当然,还是有一丁点东西的,就是对象生命周期的概念,是继承过来了,因为 Component 里面没有构造函数和 init。除此以外,Object 提供的其他特性,Component 都涵盖了,而且完全覆盖了父类的方法,真是一种奇怪的思路(与其这样,再写一遍构造不是好了么,这么玩,仅仅为了 DRY 么?),但是我参悟也不是很深,也不便过多去评价这个。

属性,作为组件的第一个抽象特性,有了一些变化。第一个是上文提到的,公有成员是否被认定为属性的问题。在 2.x 里面得到了统一,公有成员变量,是被认定为属性的。第二个是,上文提到了一个 Mixin 的东西,在 2.x 里面,Mixin 这个特性被进一步向语言提供的 traits 的能力对其了,不光是方法可以被“混入”,现在属性也可以被“混入”了,也就是你可以通过一个 Behaviour,来向一个对象注入属性了。这已经比较逼近 traits 的能力了。而且,“混入”的属性,在进行 hasProperty 判定的时候,也会被判定为是 true。

事件,有了比较大的变化。第一个,是取消了使用方法来做事件句柄的做法,这样,你不必为了出发一个事件,来定义一个 on 前缀的方法,可以在随便一个字符串作为事件的名字去绑定事件,另一方面想要触发事件时候,可以用 trigger 方法随意触发任意事件了。整体来说,更加灵活了。但是,事件的处理器,仍然是用对象的私有成员变量 _events 来存储的,这就使得那个事件绑定点的问题,仍然存在。不过,还是部分被解决了,且看下文。

Behaviour,如果说,Component 里面变化最大的东西,莫过于是 Behaviour 了。从 PHP 5.4.0 开始,官方语言层面加入了对 Traits 的支撑。可见,这个东西在面向对象里面,渐渐成为了一种趋势了,大家虽然憎恨多重继承的复杂性,但是对与比继承更细粒度的代码复用,还是有所渴求的,所以这个轻量级的 Traits,被直接写入了语言。而 Yii 2.x,我觉得也是在深化这种复用形式的功能。一个是引入了除方法外,属性的“混入”,第二,将 Behaviour 的绑定,全面深化到 Component 层面,也即,这个特性,缺省在所有 Yii 2.x 的对象里都打开了,不像 1.x 那样,只有 7 个类是打开的。它被更加深入的当成一种代码复用手段,被全面地鼓励使用了。

通过 Behaviour,解决了我说的那个问题,必须把事件绑定给写到目标对象类的实现里面,这次不用了,你可以写到一个 Behaviour 里面,然后通过配置文件的形式去“混入”到目标对象里面,就可以达到目的,但是,仍然没有解决那个必须找到对象构造点的问题,却用另一些新的东西给解决了,比如DI,比如Service Locator等,且看其他文章的介绍了。

在使用 nginx 的时候,配置 gzip 模块,可以让服务器的伺服更加高效,对于文本类型的数据,传输量可以压缩一半左右。

官方建议我们创建一个叫 conf.d 的文件夹,使用 include 语句,将 gzip 的配置文件插入到 http 区段。

阅读全文 »

不知道从哪一届开始,老罗开锤子手机发布会的时候,会当场捐赠一款开源软件,第一次是捐赠给 OpenSSL,第二次是 OpenResty。那次发布会开完,OpenResty 在程序员圈子里火了一把,成为一种热门技术了。其实,OpenResty 在后端程序员世界里,早就享有盛名。当你不得不去面对高并发的场景的时候,你一定会发现 Nginx 的巧妙和强大,然后你会想到用 Nginx 解决问题,然后你一定会发现 OpenResty。

阅读全文 »

Yii 2.0 base 包里部分类的类图

Yii 框架从 2.0 开始,底层抽象上面发生了一些变化。比如,多了 DI 和 Service Locator 等设计模式相关的抽象。这两天,专门看了下代码,写下一点我的想法。

上面的图是,是我用 Visual Paradigm 画的类图。从图里我们看到,2.0 版本以来,最根部的抽象是一个叫 Configurable 的接口,其主要作用就是说明所有的类都可以用类似 Key-Value 结构来进行配置。除了一些 Helper 的类,绝大部分对象都是 Configurable 的实现。

阅读全文 »

我是2010年参加工作的,当时,有件小事,我是印象相当深刻的。那时,公司为我指定了一位导师,负责辅导我快速进入角色并胜任工作。我便跟着导师合作项目,以他为主导,后来我发现,每次他说他做好了什么功能时候,我总是在代码里找不到相应地功能,等我去问他,他说,“哦,我没有提交代码”,如此数次后我问他,为什么不提交代码,导师回答我,那样会产生太多的版本号(当时我们使用的是SVN,会产生revision号)。听到这句话,即便经验浅薄,我还是震惊了!

事实上,我接受到的本科教育,都是通用教育,进入到工作岗位上后,也没有接受过公司提供的关于代码版本控制的培训,好像公司方面觉得,这是每个程序员天然应该会的东西,而事实呢,大部分程序员,甚至连版本控制的核心理念都不知道,更别提与之相关的最佳实践,工程项目管理等等。

在这里,我不得不重申,版本控制的第一个理念,是保护程序员创造性的劳动成果,简单来说,便是:只要写下来的代码,就应该被妥善保管。无论这些代码是庞大的,细小的,功能完备的,还是有缺陷,不完整的,都应该避免丢失。大家在从事编码实践过程中,最害怕的应该是写好的代码丢了,而不是别的什么。别的问题,都有别的方法来解决。

时代发展到今天,软件工程项目,大都非常复杂,可能一个程序员,一整天,都没法完成一个功能。如果,因为代码无法运行,而不敢提交到版本库,那这段代码就面临丢失的风险。但是,不提交代码,肯定是不对的。

应该有一个地方,保管总是可以正确运转的代码,就是我们常说的“主线”(trunk)或者在 Git 里叫 master(现在 master 遭到了一部分人唾弃,叫 main 也是可能的);而与此同时,应该另有一个地方,保管开发到一半的代码,或试验性的代码。这就要用到“分支”(branch)这个功能。分支,其实是一个相对的概念,因为相对分支的“主干”其本质上也就是一个特殊的分支而已。

分支是一种很有效的工具,用来保存代码的所有变更。一般也都会提供“比较”和“合并”等功能,主要用于将一个开发完毕的分支,重新合并到主干。在我们使用SVN的时候,因为版本库在云端,所以,新建一个分支,就是发往云端的一个命令。程序员的本地环境,只是服务器的一个工作拷贝,如果,需要在某个分支工作的话,一般需要“切换(switch)”。

而在Git里面,分支的使用成本无限地降低了。因为 Git 的分布式设计,我们的所有版本库,都在本地有个完整的备份,所以,分支得创建就是一个本地动作,而且在 Git 里面使用文件的 hash 来唯一标记一个文件的,如果没有修改的文件,基本可以认为不会占用额外的存储空间。切换分支也变成了一个非常简单而且低成本的动作。

在 Git 里面是非常鼓励使用分支的。如果你有一些进行到一半的工作,你最好将其保存在一个分支里,你要决定的是,是否把这个分支“推送”到公共的版本库去,如果确实是一个 broken 的功能,无法正常运行,你完全可以不要推送到大家共同 share 的中央库去,保证了代码的私密性。

对于像整个团队或者整个社会开放的公共版本库,里面的每个分支是否可以正确运转,可能取决于这个项目的一些约定。但是这不会影响一个程序员在本地创建和使用分支。这就给分支的使用带来了极大的自由度,也关注了代码的安全。

甚至,在没有与其他人共享的时候,你可以随意修改你分支里的 commit,合并一些 commit,像文初我第一位导师那样担心自己的 commit 过多的极度洁癖也轻松可以解决。时代真的是不同了,而且进步了。

— 完 —

如何知道自己用的是Linux还是Unix?
答:执行uname -a命令。

如何知道自己用的是哪个Linux发行版?
答:执行lsb_release -d命令。

如何知道当前系统时间?
答:执行命令date。

什么是UTC,CST和EDT?
答:UTC(=Universal Time Coordinated),协调世界时(过去曾用格林威治平均时GMT来表示);EDT(=Eastern Daylight Time),美国东部夏令时间;CST(=Central Standard Time),美国中部地区标准时间,不过我个人认为还有一个意思(China Standard Time)就是中国标准时间,因为有时候,你会看到中国时间后面也会带个CST的。

如何修改系统时区?
答:各发行版不太一样,在最新的Ubuntu 11.04上,使用命令dpkg-reconfigure tzdata命令来修改时区,会弹出一个命令行下的字符图形界面,比较方便。在一般的Linux发行版上,可以尝试tzselect命令,可以通过命令行交互式地设置时区。

如何设定系统时间?
答:使用date –set=”Sun Aug 21 10:13:43 CST 2011” 命令来设置,引号中的字符串,可以先用date命令来输出,然后修改时间到正确值,再设回去。

不知道mysql的root密码,如何重设?
答:停止mysql服务器,重新启动,增加–skpi-grant-tables参数重启服务器,不需要密码就可以登陆服务器,登陆后,UPDATE user SET password=PASSWORD(‘new_password’) WHERE user=’root’;使用该sql语句修改密码,修改完后,不要忘记执行flush privileges,然后退出,正常重启mysql服务器。

最近,正好发生了一件大事,就是 GitLab 的运维同学不小心删除了生产的数据,虽然 GitLab 已经骇人听闻的准备了五种备份机制,但是,仍然导致他们丢失了将近 6 个小时的用户数据,尤其对他们声誉的损失,是根本无法估量的。反思一下,这个博客 Becomin’ Charles,也是没有完善的备份的,真是冷汗直冒啊,主要考虑到这是我的个人博客,但是想想已经坚持了快十年了,如果真的丢了的话,还是非常痛心的。

阅读全文 »