企业级IaaS架构的深度解析

一、 关于云

1. 云产业分析

根据IDC的分析报告,美国和中国云计算产业发展差异巨大:美国以公有云为主,SaaS*大、IaaS*小;而中国截然相反,以私有云为主,IaaS占了大约50%的份额。

究其原因,跟中美两国云计算产业发展的阶段、成熟度有很大关系。

中国的公有云主要使用者是小微、创新企业等。我认为IaaS公有云已经或者将要巨头化,PaaS还有机会,SaaS会是云计算几大分类中的爆发点,但是要看准行业。

在诸多产业中,中国云计算私有云市场主要的客户来自:通讯、金融、政府。金融行业受安全、政策、法规的约束,几乎不会选择公有云,大型国有银行私有云的建设步骤也很谨慎、渐进式,会首先考虑迁移非核心应用;小金融相对对新技术比较开放,会实验一些开源的技术,如Openstack、ceph等。

政府由于“十二五”、“十三五”政策持续推动、对于政务云建设的现实需求、统一纳管基础设施资源、节省成本等考虑,对云计算的投入较大。

2. 国内有私有云需求的四类企业

a) 政策驱动

因为政策持续推动、创新补助、领导要求(政绩)等原因,需要上云的企事业单位、行政机关。

b) 人云亦云

不解释,:)。

c) 新技术跟进

看见新技术的发展、成熟,希望在其中分一杯羹,也包括IDC之类的转型企业。和上一类的区别一个是模糊的、被动的,一个是有自主想法、主动的。

d) 为业务而云

因为业务发展规模(含弹性)、统一上收资源、成本等考虑云。主要也分两类大型互联网企业和传统大型企业。

前者因为业务发展需要考虑云计算,从成本、技术可控性考虑,会采用大量的开源技术,同时会对硬件、软件提出改造要求,大力发展分布式、集群技术以适应其性能、可靠性等需求。典型的代表是阿里等。

而传统大型企业走的是另一条路,相对稳健,会选用成熟可靠的商业化解决方案为主,如虚拟化选择VMWare。另一方面,这类企业相对比较谨慎,会以规划咨询、POC、招标、建设、交付、运维相对固定的模式去建设云。典型的代表是大型央企。

3. IaaS、PaaS、SaaS分析

我把顺序放过来,先说SaaS,再说PaaS,IaaS。

SaaS我认为主要会在三种情况下出现:

a) 行业SaaS

有行业属性的SaaS,如教育、医疗、培训等。

b) 工具化SaaS

比如workday类似的管理工具、office365类似的文档工具等。

c) 大型企业(组织机构)内部SaaS

有些企业内部,各地/部门业务类型相对一致,使用SaaS软件统一上收权限,节省成本等。

如我之前所说,如果找准行业、方向,SaaS可能是创业的大风口。

PaaS的实现我认为有两种:

a) 基于商业化自动部署工具的

大型企业考虑人员技能、维护成本、可靠性等要求,较多选择类似方案。HP、IBM、BMC等都有类似的工具。自行实现的话,可以考虑流程引擎加上脚本执行器再加部署包。

b) 基于开源框架和软件的

选择cloudfundry、openshift框架,加docker等技术,目前随着相关技术的成熟,越来越受到关注。上述的几个大外企实际上也有类似的实现。

IaaS的情况比较复杂,我认为难度主要在理清几个头绪:

a) 业务对于底层资源的要求、约束

联想的架构师团队正在做一件事情,就是梳理业界存在的十几种主要的企业业务架构(如电商、搜索等等),分析和总结它们对于资源的各方面要求,如计算能力、IO等等。

b) 服务的设计、编排

需要从业务的承载要求、客户消费方式、业务系统架构、部署方式、虚拟化方式、集群、资源类型做统一的规划设计。根据对客户现有情况的分析,尤其是IT系统现状、痛点等,得出客户的期望,进而设计出客户需要的服务。

c) 服务与资源的关系

很多人搞不清楚什么是服务,什么是资源,甚至有个号称云架构师的人跟我说,他实习了对虚拟化的纳管、资源调度,就是完整的云。

资源(resource):在系统中, 基础设施、network设备,VM、host、OS、CPU、Memory、存储、software等等都被视作可分配资源。

服务是云计算的核心特征,根据业务要求等可以编排服务,使之能让客户消费,通常会绑定价格、SLA等一些附带属性。

d) 租户与组织的关系

要想清楚,根据客户现状,组织与租户怎么对应,是1对1,1对多,还是多对多。

e) 资源调度的原则

要考虑资源调度策略、资源类型、性能要求,同时要考虑弹性的时候如何伸缩。经常会有只能scale out,不能scale in,或者频繁scale out、in的情况出现。那么在考虑弹性判断条件、算法的时候,要综合几种监控告警数据,如业务、资源。

二、 IaaS方案

1. IaaS架构影响因素

如我之前在群里所说的,个人认为很多因素都会影响企业IaaS架构的选择,主要有以下一些:

a) 企业IT发展规划

b) 企业组织架构

c) 企业管理制度

d) 业务类型

e) 应用层次

f) 人员技能

g) 技术成熟度

h) 成本

i) 周期

j) 运维体制

k) 。。。

如果不考虑其中的某个因素,都有可能导致项目的失败。我曾经亲身经历过,因为管理和客户组织架构原因导致的云项目失败。客户在实施云计算建设之前,业务部门是强势部门,IT部门是支撑部门,而在规划和建设中忽略了客户组织架构的影响因素。IT部门变成了云平台的管理者,业务部门成为相对弱势的云服务消费者,导致客户内部组织架构重组、项目停滞。

2. 私有云IaaS平台构成

我这里讲的是广义的云平台,我一般认为分成几大部分:门户(管理和自服务)、服务层、统一资源层(含适配器层)、基础设施(含虚拟化),紧密相关的有BSS、OSS子系统;外部可能交互的系统有ITSM、CMDB、外部监控系统、4A系统和通知系统等。我画了一个主要部件的草图,方便大家理解:
%title插图%num

a) 门户分为管理和自服务,分别给管理员和普通用户提供服务;用于展示基础设施、平台及软件服务,并控制用户接入方式,对用户的访问范围、界面的展示方式做设定等。以便于管理员和普通用户获取服务的信息,申请并使用各类服务。

b) 服务层指服务构建与设计的逻辑组件,它负责定义服务的结构、流程等信息,组装原子服务,生成业务服务,发布到服务目录,监控服务运行状况等,形成完整的服务生命周期管理。业务用户可以通过服务管理层获取云计算服务;管理员可以通过服务管理层监控所有服务实例的整体状况;服务开发人员可以通过服务管理层定义和发布服务。服务管理层将以业务服务的形式对外发布所有的服务操作接口。

c) 资源层指管理和调度软硬件资源的逻辑组件,它负责构建资源池,生成简单资源供应的技术服务(原子服务),定义资源运维的操作流程。为了组成资源池,一般将同质的设备集中安装,相互连接,并通过一定的管理软件来监管和配置。资源池由同质的一组资源组成,用户可以通过资源管理层软件从资源池中申请资源,指定该资源实例的配置,并管理其运行。管理员可以监控每个资源池的资源使用率,健康状况和性能状况。资源管理层将以技术服务的形式对外发布所有的资源操作接口。这一层要屏蔽掉虚拟化等的差异,使得上层无法感知。

d) 基础设施包括计算、存储、网络,其中计算含各种异构虚拟化。

e) BSS和OSS源自电信行业的B和O,BSS负责营销、结算等功能;OSS负责监控、安全等。不展开了。

3. 虚拟化异构

能否支持X86虚拟化异构、异构的支持广度是衡量一个云资源管理平台(区别与云服务管理平台)的一个重要标准。目前主流的虚拟化软件有几种:

a) Vmware

b) Hyper-v

c) Xen

d) Kvm

e) 在kvm和xen上演化的各种版本

在此不考虑lxc等。

主要的实现思路是在资源层做统一纳管,用一套接口整合,也即适配器模式,每种使用一个适配器。在实际开发中,一般接口做二次抽象。

目前*常见的异构是VMWare和KVM(Openstack纳管),目前有几种途径:

a) 自己实现,调用vcenter或vsphere的接口

推荐使用这种方式。

b) 各企业商业发行版

如,mirantis、hp hellion os商业版、racespace等,基本上不尽成熟,或者高级功能有缺陷。

c) VIO(VMWare Intergrated Openstack)

很多人跟我推荐VIO,我反对,理由有几点:

1. 遗产系统接管。如果对于已有的VMWare虚拟化,VIO无法接管

2. 性能。VIO部署在虚拟机上,作为vcenter插件,性能无法保障。

3. VIO本质上还是Openstack的一个实现,没有高级功能。

4. 如果需要SDN,要集成NSX,成本等各方面都需要考虑。

4. 小机与X86异构

除了X86虚拟化异构,还要考虑小机(主要是IBM power)、物理机、虚拟机的供应,这时也要考虑小机的纳管需求。采用的方式也是在资源层统一纳管,但接口会有独特性,一般用流程引擎调HMC解决。

5. Openstack及其应用场景

Openstack现在持续火热,各大厂商都在积*参与,本人也参加过openstack峰会。结合工作中的实际,我认为Openstack长期来讲是个好东西,适合一定场景的应用范围,但并不普适。可以应用在:

a) 开发测试环境

b) 非关键业务

c) 科研实验环境

我认为Openstack需要解决的问题有:

a) 稳定性

b) 可升级

c) 高级功能,如HA等

d) 遗产接管

此外,我认为Openstack存在贪多求快的问题,面铺的广,不够扎实,主要使用的还是那几个核心模块。

6. SDN不是企业级私有云基本需求

我曾经设计了一个集成SDN和NFV(部分功能,如SLB、VFW等)对的拓扑设计器,但在具体的企业级客户中,并没有太多客户迫切需要SDN。都会提到、以后扩展到SDN的实现,而不是眼前。

我认为SDN主要应用在几个场景:

a) 公有云,租户定义私有网络

b) 私有云,需要频繁变更网络拓扑的环境,如开发测试、科研等

c) 电信、IDC等

7. 云管平台部署架构

云管平台的部署和普通的SaaS网站没有什么不同,都是SLB加HA,后端应用集群、数据库集群,一般没有很大的压力。

%title插图%num

三、 云不一定节省成本(我知道我说在这个可能很多同行要扔搬砖,可是作为一个驾狗狮,虽千万人吾往矣。。。)

1. 规划、设计和建设周期长。云平台要承载所有准备上云的业务系统,考虑因素较多,如前述。

2. 前期采购成本高,前期资源池建设采购的设备数量较多,占用大量的机房、电源等资源,投资和运维成本均较高,一定时间内会闲置。前期规划能力不足,也会造成资源浪费。

3. 对企业的组织管理制度可能会有调整、单体人员技能会有较高要求,造成行政和人员成本升高。

4. 管理维护成本高、维护力量无法分层:维护人员要分成不同的团队,分别管理云平台和业务,必须熟悉平台所涉及的所有的软硬件资源,维护效率不高

5. 人云亦云,并不少见,尤其是资源池较小的情况下,纯属浪费。
————————————————

原文链接:https://blog.csdn.net/wx_15323880413/article/details/100516721

利用hachoir获取媒体文件元数据

利用hachoir获取媒体文件元数据

概述
媒体文件(如jpg、mp4、avi等)均内置了大量元数据。Python内置的文件库只能读取少量文件元数据。
某些工具可以获取这些元数据,但是太重量级,效率不高。
如果只是想获取媒体文件元数据,hachoir是一不错的选择。

hachoir是一个二进制数据流查看、编辑工具,支持主流的压缩文件、音频文件、视频文件格式。
hachoir提供了Python API接口和四个命令行工具,命令行工具默认安装在Python的Scripts目录。

hachoir-grep: 从二进制文件中查找文本模式
hachoir-metadata:获取二进制文件的元数据
hachoir-strip: 删除文件元数据
hachoir-urwid: 以文本模式显示二进制文件内容
详细文档见https://hachoir.readthedocs.io/en/latest/

安装
pip install hachoir

案例:获取音频的播放时长
from hachoir.metadata import extractMetadata
from hachoir.parser import createParser

def get_metadata(file_path):
try :
metadata = extractMetadata(createParser(file_path))
return metadata
except:
print(file_path)

path = r’C:\Users\Administrator\Music\Floricienta – Flores Amarillas.mp3′
metadata = get_metadata(path)
# 输出文本格式的元数据
print(metadata)
# 输出字典格式的元数据
print(metadata.exportDictionary())
# 输出duration元数据
print(metadata.get(‘duration’))

命令行输出信息:

Metadata:
– Title: Flores Amarillas
– Author: Floricienta
– Album: Cris Morena ¨C Grandes ¨¦xitos
– Album: Cris Morena – Grandes Éxitos
– Duration: 3 min 43 sec 257 ms
– Music genre: Blues
– Track number: 9
– Channel: Joint stereo
– Sample rate: 44.1 kHz
– Bits/sample: 16 bits
– Compression rate: 11.0x
– Bit rate: 128.0 Kbit/sec (constant)
– Format version: MPEG version 1 layer III
– MIME type: audio/mpeg
– Endianness: Big endian
{‘Metadata’: {‘Album’: ‘Cris Morena – Grandes Éxitos’,
‘Author’: ‘Floricienta’,
‘Bit rate’: ‘128.0 Kbit/sec (constant)’,
‘Bits/sample’: ’16 bits’,
‘Channel’: ‘Joint stereo’,
‘Compression rate’: ‘11.0x’,
‘Duration’: ‘3 min 43 sec 257 ms’,
‘Endianness’: ‘Big endian’,
‘Format version’: ‘MPEG version 1 layer III’,
‘MIME type’: ‘audio/mpeg’,
‘Music genre’: ‘Blues’,
‘Sample rate’: ‘44.1 kHz’,
‘Title’: ‘Flores Amarillas’,
‘Track number’: ‘9’}}
0:03:43.257000

公有云、私有云及混合云应用场景辨析

近几年我们经常能看到一些研究公司(Gartner朋友Alice Yang语,“我们是研究公司”)、咨询机构发布关于公有云、私有云以及混合云的市场趋势分析,种种“公有云是未来发展趋势”、“混合云是主流”等观点层出不穷。在此,笔者试图抛开趋势,仅从应用场景角度分析,试图阐述当前国内各类云的客户群体、供应商、服务和技术特点。

公有云:

笔者在以前的文章描绘过国内外公有云和私有云的构成比例,美国以公有云为主,国内以私有云为主(一些数字认为国内公有云略大于私有云,见仁见智吧)。观察以国外的AWS、国内的阿里云为代表的公有云,走过的都是一条类似的路——*步先满足内部需求、第二步将多余的资源(计算能力)向外供应、*后以外部的IT能力供应为主。这种步骤实际上是允许内部试错、用内部需求支撑营收,同时练就相对过硬的技术、业务服务、运营和运维经验,目前比较成功。

从笔者观察到的情况来看,公有云的客户前百分之5-20%的大客户占了80%以上的营收,而群体数量上以中小型企业为主(SMB)。此类中小型企业具有小微、创新等特点,云相关的技术能力相对较弱、对于成本敏感,希望利用云计算快速构建业务的支撑环境,迅速发布和拓展业务。因此,公有云的服务供应相对标准化,通常同时具备IaaS(基本能力)、PaaS(以APaaS为主)和SaaS(基于IaaS封装行业应用、生态合作伙伴提供)各层次的服务。相对于私有云,通常公有云会更强调多租户、SLA、计费等特性,而虚拟化比会在一定程度上限制公有云提供商的成本。

公有云的供应商主要分为几类:三大运营商、ICT巨头、互联网巨头、IDC转型企业、特殊创新企业。

私有云:

目前国内私有云需求主要集中在金融、电信、政府、能源、教育、交通等行业。与公有云不同的是,私有云的客户主要是大中型企业。这些企业具有几大特点:有钱、有资源、有需求。有钱:上述相关行业中的大中型企业,具备充足的IT预算;有资源:此类企业在行业中所处位置使企业相对容易获取资源、人才;有需求:有国家政策推动、有创新扶持、有实际业务复杂度和性能弹性等需求、有安全类需求等。

私有云的服务通常会根据客户的特点体现较多的定制,如组织架构、内部审批流程、定制化的计算服务、统一资源管控、统一监控、自动化部署等。而在技术上,私有云往往会体现异构资源纳管(多种资源类型、多种虚拟化)、对接企业现有的4A、外部监控、ITSM、CMDB等系统。

私有云的供应商主要分为几类:传统ICT厂商、Openstack厂商、公有云厂商转做私有云。

混合云:

混合云目前并不是简单的“私有云+公有云”形态,在目前更多体现为“私有云+”,也即在构建完成私有云的基础上,借助公有云的能力形成混合云。混合云管理平台(CMP)会体现一些与私有云管理平台差异化的特征,如跨云的资源和服务编排、云费用分摊和成本优化等。

在此,插播一下广告,云星数据和RightScale合作为中国客户提供混合云管理平台RightCloud,:)。

如前文所述,混合云客户一般已经具备了私有云,希望通过对接公有云解决几方面的典型需求:

1. 跨国:某大型公司在美国销售电子类产品,需要在美国连接该企业的销售系统,通过本地私有云加AWS公有云实现;

2. 灾备:将一部分系统或者数据定期备份到公有云上;

3. 性能和高可用:利用公有云的CDN、全局负载均衡等能力提供更好可用性和性能;

4. 爆发:客户业务需要短时爆发(bursting),满足业务需求同时节省成本。

混合云供应商主要包含:公有云提供商提供混合云能力、私有云供应商提供混合云能力、混合云创新企业等。

 

request基本使用教程

request基本使用教程

request使用
一.基本用法
1.准备工作安装request库,pip安装或再pycharm内安装。
2.实例引入renquest库中方法清晰简单,获取网页直接使用get方法就能直接实现:

代码:

import requests
response = requests.get(‘http://www.baidu.com/’)
print(type(response))
print(response.status_code)
print(type(response.text))
print(response.cookies)

运行结果:

<class ‘requests.models.Response’>
200
<class ‘str’>
<RequestsCookieJar[<Cookie BDORZ=27315 for .baidu.com/>]>

request中我们使用其他方法也很简单:

r = request.post(‘url/post’)
r = request.put(‘url/put’)
r = request.delete(‘url/delete’)
r = request.head(‘url/get’)
r = request.post(‘url/get’)

**3.**GET请求
http中*常见的请求是get请求,对get请求进行详解。
首先是一个简单的get的请求,请求一个链接http://httpbin.org/get,该网站会判断如果客户端发起get请求的话,它会返回相应信息:
这是一个专门测试的网站 http://httpbin.org/

import requests
response = requests.get(‘http://httpbin.org/get’)
print(response.text)

运行结果:

{
“args”: {},
“headers”: {
“Accept”: “*/*”,
“Accept-Encoding”: “gzip, deflate”,
“Host”: “httpbin.org”,
“User-Agent”: “python-requests/2.25.1”,
“X-Amzn-Trace-Id”: “Root=1-6076af01-5222d2ca535f9b6039280a85”
},
“origin”: “18.167.102.111”,
“url”: “http://httpbin.org/get”
}

可以看到我们发起了请求,返回的结果能看到我们的请求头,链接,ip等信息。
我们还可以向get请求中传入参数,构造一个附加额外信息的get请求。
直接构造:

r = request.get(‘http://httpbin.org/get?name=germey&age=22’)
1
?后面接参数开始 &连接参数,显然这样太复杂因此我们引入params参数。
params参数示例:

import requests
data = {
‘name’: ‘germey’,
‘age’: ’22’
}
response = requests.get(‘http://httpbin.org/get’,params=data)
print(response.text)

运行结果:

{
“args”: {
“age”: “22”,
“name”: “germey”
},
“headers”: {
“Accept”: “*/*”,
“Accept-Encoding”: “gzip, deflate”,
“Host”: “httpbin.org”,
“User-Agent”: “python-requests/2.25.1”,
“X-Amzn-Trace-Id”: “Root=1-6076b273-24b1eebd6542e66659560793”
},
“origin”: “18.167.102.111”,
“url”: “http://httpbin.org/get?name=germey&age=22”
}

根据控制台返回的信息我们知道request已经自动为我们构造了一个url链接。我们还可以调用json方法,将字符串类型的json格式转换为字典类型的json。

print(response.json)

抓取网页
我们以知乎为例来抓取知乎的界面信息

import requests
import re
headers = {
‘cookie’: ‘填入你的cookies’,

‘user-agent’: ‘Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36’
}
r = requests.get(‘https://www.zhihu.com/’, headers=headers)
print(r.text)
print(r.status_code)

抓取二进制数据
我们抓取了一个知乎界面但其实我们知道它是一个html文档,如果我们向抓取二进制数据图片、视频、音频又应该怎么办呢。
我们以知乎的图标为例:

import requests
import re
headers = {
‘cookie’: ‘填入你的cookies’,

‘user-agent’: ‘Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36’
}
r = requests.get(‘https://static.zhihu.com/heifetz/assets/apple-touch-icon-152.a53ae37b.png’, headers=headers)
print(r.text) # 直接打印会乱码 因为将二进制强制以字符格式输出
with open(‘zhihu.png’,’wb’) as f : # 写入本地可再与.py文件相同的地址查看图片
f.write(r.content)

4.post请求
我们了解了*基本的get请求,post也是应该常见的请求方式,就是把相关信息返回。

import request
data = {‘name’: ‘germey’, ‘age’: ’22’}
r = requests.post(‘http://httpbin.org/post’,data=data)
print(r.text)

运行结果:

{
“args”: {
“age”: “22”,
“name”: “germey”
},
“headers”: {
“Accept”: “*/*”,
“Accept-Encoding”: “gzip, deflate”,
“Host”: “httpbin.org”,
“User-Agent”: “python-requests/2.25.1”,
“X-Amzn-Trace-Id”: “Root=1-6076f804-446c1b1d538e216f066e33ee”
},
“origin”: “111.17.194.60”,
“url”: “http://httpbin.org/get?name=germey&age=22”
}

我们可以看到数据的提交。
5.响应

import requests
headers = {
‘cookie’: ‘url’,
‘user-agent’: ‘Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36’
}
r = requests.get(‘https://www.zhihu.com/’, headers=headers)
print(‘状态码:’,r.status_code)
print(‘请求头:’,r.headers)
print(‘cookies:’,r.cookies)

运行结果:

状态码: 200
请求头: {‘Server’: ‘CLOUD ELB 1.0.0’, ‘Date’: ‘Wed, 14 Apr 2021 14:18:07 GMT’, ‘Content-Type’: ‘text/html; charset=utf-8’, ‘Vary’: ‘Accept-Encoding’, ‘set-cookie’: ‘tst=; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT; httponly, KLBRSID=ed2ad9934af8a1f80db52dcb08d13344|1618409886|1618409886; Path=/’, ‘content-security-policy’: “default-src * blob:; img-src * data: blob: resource: t.captcha.qq.com cstaticdun.126.net necaptcha.nosdn.127.net; connect-src * wss: blob: resource:; frame-src ‘self’ *.zhihu.com mailto: tel: weixin: *.vzuu.com mo.m.taobao.com getpocket.com note.youdao.com safari-extension://com.evernote.safari.clipper-Q79WDW8YH9 zhihujs: captcha.guard.qcloud.com pos.baidu.com dup.baidustatic.com openapi.baidu.com wappass.baidu.com passport.baidu.com *.cme.qcloud.com vs-cdn.tencent-cloud.com t.captcha.qq.com c.dun.163.com; script-src ‘self’ blob: *.zhihu.com g.alicdn.com qzonestyle.gtimg.cn res.wx.qq.com open.mobile.qq.com ‘unsafe-eval’ unpkg.zhimg.com unicom.zhimg.com resource: captcha.gtimg.com captcha.guard.qcloud.com pagead2.googlesyndication.com cpro.baidustatic.com pos.baidu.com dup.baidustatic.com i.hao61.net ‘nonce-395681d5-2009-4f24-ba9b-2f4de9719d15’ hm.baidu.com zz.bdstatic.com b.bdstatic.com imgcache.qq.com vs-cdn.tencent-cloud.com ssl.captcha.qq.com t.captcha.qq.com cstaticdun.126.net c.dun.163.com ac.dun.163.com/ acstatic-dun.126.net; style-src ‘self’ ‘unsafe-inline’ *.zhihu.com unicom.zhimg.com resource: captcha.gtimg.com ssl.captcha.qq.com t.captcha.qq.com cstaticdun.126.net c.dun.163.com ac.dun.163.com/ acstatic-dun.126.net”, ‘x-frame-options’: ‘SAMEORIGIN’, ‘strict-transport-security’: ‘max-age=15552000; includeSubDomains’, ‘surrogate-control’: ‘no-store’, ‘cache-control’: ‘no-cache, no-store, must-revalidate, private, max-age=0’, ‘pragma’: ‘no-cache’, ‘expires’: ‘0’, ‘x-content-type-options’: ‘nosniff’, ‘x-xss-protection’: ‘1; mode=block’, ‘X-Backend-Response’: ‘0.435’, ‘Referrer-Policy’: ‘no-referrer-when-downgrade’, ‘X-SecNG-Response’: ‘0.43999981880188’, ‘X-UDID’: ‘AFDupPWGbBCPTjG_3TfIUhnlEgcac_LzB2M=’, ‘x-lb-timing’: ‘0.441’, ‘x-idc-id’: ‘2’, ‘Content-Encoding’: ‘gzip’, ‘Transfer-Encoding’: ‘chunked’, ‘X-NWS-LOG-UUID’: ‘7093856299592433989’, ‘Connection’: ‘keep-alive’, ‘X-Cache-Lookup’: ‘Cache Miss’, ‘x-edge-timing’: ‘0.462’, ‘x-cdn-provider’: ‘tencent’}
cookies: <RequestsCookieJar[<Cookie KLBRSID=ed2ad9934af8a1f80db52dcb08d13344|1618409886|1618409886 for www.zhihu.com/>]>

当然还有其他的各种响应信息不再列举。

来源于《python3网络爬虫开发实战》笔记

腾讯云如何修改域名DNS服务器

当你在腾讯云购买域名后,如果 DNS 服务器不正确,要把域名 DNS 修改为提示的 DNS 地址,解析后才生效。

下面星速云说下操作步骤

一、通过以下步骤查看 DNS 服务器是否正确:

登录腾讯云控制台,选择 “云产品 > 域名与网站 > 云解析”,进入 “域名解析列表” 页面。

选择需要查看的域名,进入该域名的管理页面。

选择 “记录管理” 页签。若存在如下提示,说明 DNS 服务器不正确。如下图所示:
%title插图%num

注意:不同解析套餐对应的 DNS 地址不同,请根据提示来修改。

二、选择 “云产品 > 域名与网站 > 域名管理”,进入 “域名管理” 页面。

选择待修改 DNS 的域名,单击【管理】。如下图所示:

%title插图%num

在 “基本信息” 栏中,单击 “DNS 服务器” 的【修改】。如下图所示:

在这里插入图片描述
在弹出的 “修改 DNS 服务器” 窗口中,填写指定的 DNS 服务器地址,单击【提交】,完成修改。

%title插图%num

修改完 DNS地址后,接下来要建站了,然后可以开始选购云服务器,搭建网站了。

ios 模糊搜索支持首字母搜索、拼音搜索、汉字搜索

1 /**
2  *  汉字转拼音
3  *
4  *  @param aString: 需要转的字符
5  */
6 + (NSString *)transformToPinyin:(NSString *)aString {
7     //转成了可变字符串
8     NSMutableString * mutableString = [NSMutableString stringWithString:aString];
9     CFStringTransform((CFMutableStringRef) mutableString, NULL, kCFStringTransformToLatin, false);
10     mutableString = (NSMutableString *)[mutableString stringByFoldingWithOptions:NSDiacriticInsensitiveSearch locale:[NSLocale currentLocale]];
11     mutableString = [[mutableString stringByReplacingOccurrencesOfString:@” ” withString:@””] mutableCopy];
12     return mutableString.lowercaseString;
13 }

 

1     self.searchTextField = [[UITextField alloc]init];
2     self.searchTextField.borderStyle = UITextBorderStyleNone;
3     self.searchTextField.textColor = [UIColor whiteColor];
4     self.searchTextField.placeholder = @”输入搜索内容”;
5     self.searchTextField.delegate = self;
6     [self.searchTextField setValue:[UIColor whiteColor] forKeyPath:@”_placeholderLabel.textColor”];
7     [self.searchTextField setValue:[UIFont boldSystemFontOfSize:13] forKeyPath:@”_placeholderLabel.font”];
8     self.searchTextField.returnKeyType = UIReturnKeySearch;
9     self.searchTextField.font = [UIFont systemFontOfSize:13];
10     self.searchTextField.clearButtonMode = UITextFieldViewModeWhileEditing;
11     [searchView addSubview:self.searchTextField];
12     [self.searchTextField mas_makeConstraints:^(MASConstraintMaker *make) {
13         make.left.equalTo(searchView.mas_left).offset(33);
14         make.right.equalTo(searchView.mas_right).offset(-33);
15         make.top.equalTo(searchBackgroundView.mas_top).offset(0);
16         make.bottom.equalTo(searchBackgroundView.mas_bottom).offset(0);
17     }];
18     [self.searchTextField addTarget:self
19                              action:@selector(textFieldDidChange:)
20                    forControlEvents:UIControlEventEditingChanged]; // 监听事件

 

1 //监听UITextField改变
2 – (void)textFieldDidChange:(UITextField *)sender
3 {
4     if (sender.text.length == 0) {
5         self.applyRecordList = self.originalList;
6         [self.tableView reloadData];
7     } else {
8         [self searchTraversalDisplayData:sender.text];
9     }
10 }
11 #pragma mark – UITextFieldDelegate
12 – (BOOL)textFieldShouldReturn:(UITextField *)textField
13 {
14     [self searchTraversalDisplayData:textField.text];
15     return YES;
16 }
17 -(NSMutableArray *)searchArray {
18     if (!_searchArray) {
19         _searchArray = [[NSMutableArray alloc]init];
20     }
21     return _searchArray;
22 }
23 #pragma mark – 搜索遍历
24 -(void)searchTraversalDisplayData:(NSString *)text {
25     NSString * search_pinyin = [Tools transformToPinyin:text];
26     NSString * search_lower = [search_pinyin lowercaseString];
27     [self.searchArray removeAllObjects];
28     dispatch_queue_t globalQueue = dispatch_get_global_queue(0, 0);
29     dispatch_async(globalQueue, ^{
30         if (text!=nil&&text.length>0) {
31             for (NSInteger i = 0; i<self.originalList.count; i++) {
32                 NSDictionary * dict = self.originalList[i];
33                 NSString * car_no = self.originalList[i][@”carno”];
34                 NSString * car_pinyin = [Tools transformToPinyin:car_no];
35                 NSString * car_lower = [car_pinyin lowercaseString];
36                 if ([car_lower rangeOfString:search_lower].location != NSNotFound) {
37                     [self.searchArray addObject:dict];
38                 }
39             }
40         } else {
41             self.applyRecordList = self.originalList;
42         } dispatch_async(dispatch_get_main_queue(), ^{
43             self.applyRecordList = self.searchArray;
44             [self.tableView reloadData];
45         });
46     });
47 }

Python中的多线程

Python中的多线程—(超级详细)

程序代码怎么产生新线程呢?

应用程序必须 通过操作系统提供的 系统调用,请求操作系统分配一个新的线程。

python3 将 系统调用创建线程 的功能封装在 标准库 threading 中。

大家来看下面的一段代码:

print(‘主线程开始’)
from threading import Thread
import time

def child_thread(next):
print(f’子线程{next}开始’)
time.sleep(1)
print(f’子线程{next}结束’)
time.sleep(1)

for each in range(5):
thread = Thread(target=child_thread,args=(f”{each}”))
thread.start()
thread.join()

print(‘主线程结束’)

运行结果为:

%title插图%num
创建了一个Thread实例对象,其中,Thread类的初始化参数 有两个
target参数 是指定新线程的 入口函数, 新线程创建后就会 执行该入口函数里面的代码,
args 指定了 传给 入口函数threadFunc 的参数。 线程入口函数 参数,必须放在一个元组里面,里面的元素依次作为入口函数的参数。
注意,上面的代码只是创建了一个Thread实例对象, 但这时,新的线程还没有创建。
要创建线程,必须要调用 Thread 实例对象的 start方法 。也就是执行完下面代码的时候新的线程才创建成功,并开始执行 入口函数threadFunc 里面的代码。
有的时候, 一个线程需要等待其它的线程结束,比如需要根据其他线程运行结束后的结果进行处理。
这时可以使用 Thread对象的 join 方法

RBTools工具的使用

Linux 下开发的人员比较习惯于使用命令行工具,刚才网页上提交文件的方式感觉更适合windows 下的开发人员,那ReviewBoard 有没有提交代码的命令行工具呢,那就是RBTools了。

安装RBTools:

pip install RBTools

当前RBTools 版本是0.7.5,相应的命令是rbt,之前网上的教程中的命令大多是post-review,估计是比较旧的版本了,不过对应的参数都类似。

rbt 命令有很多参数,详细说明可以查看官方文档RBTools 文档,读者有兴趣可以自行查阅。

这里我们提交代码用到的是 post 参数,post 自身也有很多参数,使用rbt help post 便一目了然。

列举我们要用到的:

  1. -d 显示调试信息
  2. -p 提交之后立即发布,不用此参数则存为草稿
  3. –summary 指定摘要
  4. –diff-filename 指定要提交的diff文件(使用git diff生成)
  5. –description 指定描述
  6. –description-file 指定描述文件,当描述的内容过多的时候,先将内容写入文件,再用此参数指定文件。
  7. –target-groups 指定项目组
  8. –target-people 指定审核人员

代码提交默认的分支是 origin/master,如果你要提交到别的分支,比如develop,得这样用 --tracking-branch=origin/develop

*后在开始使用rbt 命令前,还需要在项目目录下新建一个配置文件.reviewboardrc,基本配置如下:

  1. REVIEWBOARD_URL=“http://127.0.0.1”
  2. REPOSITORY=“git@git.company.com:company/Project”
  3. USERNAME=“test_01”
  4. PASSWORD=“******”

终于配置完成,下面就可以使用了。

我们将更新的信息都写在命令行里,

rbt post -p -d --tracking-branch=origin/develop --diff-filename=a.diff --summary=init_blog --description="complete blog index" --target-group=Blog --target-people=reviewer_01

使用-d 参数可以直观的显示每一步的执行过程,执行成功后,我们在网页上查看一下。

summary

iOS11降级到iOS10.3.3/10.3.2教程

首先说一下iOS 11,在WWDC发布之后我对iOS11上面带来的诸多新特性感觉很好想体验一下,并在*时间更新了iOS11开发者预览版!

更新后体验一天后我的感想是卡卡卡卡,几乎任何一个反应,都有延迟,都要等,等得我好急促,已经严重影响到我的工作效率了!

以上所说都是我自己的iPhone6s 其他机型我不太清楚,也有朋友说自己的iPhone7 升级后感觉不是太卡!

下面进入正题降级 !!!

1.建议利用 iTunes 备份手机数据,万一降级失败,并且数据丢失,你可以重新恢复数据,运行 iOS11 的 iPhone 只能恢复 iOS 11 状态下备份的数据,运行 iOS 10 的 iPhone 只能恢复在 iOS 10 状态下备份的数据,所以你从 iOS 10 升级到 iOS 11 之前,也应该先备份数据;

2.先检查你的 iTunes 是否已经升级到*新版本,如果不是*新版本,请升级到*新版本;

3.在电脑上打开这个网站:https://ipsw.me/;如图下载iOS 10系统固件(本教程用iPhone 6s 作为演示,自行下载自己设备的固件):

%title插图%num

4.接着点击下面的 Download,把系统固件下载下来。

5.用充电线把 iPhone 或 iPad 或 iPod touch 连接到电脑,并打开 iTunes;

%title插图%num

6.如果你用 windows 电脑,按下键盘的 shift 不要放手,然后用鼠标点击 iTunes 右侧的「检查更新」,如果你用 Mac 电脑,按下 option 按键不要放手,同时用鼠标点击「检查更新」,点击之后可以放开键盘,这时系统会弹出一个对话框,选择你刚刚下载的系统固件,降级就开始了。

%title插图%num

7.等待15-20分钟左右会弹出您的iPhone已经被更新,接下来等待重启就行了,重启之后系统就被降级为iOS 10了!

8.降级之后发现手机短信被清空了(原因应该是iOS 11更新了短信App所以导致清空了),iOS 11的壁纸保存下来了还是不错的!

9.教程就到这里谢谢观看,希望对你有帮助!(本方法适用于任何系统的iOS 降级!)

使用Phabricator做为Code Review工具

0x10 概述

Phabricator是一套基于Web的软件开发协作工具,包括代码审查工具Differential,资源库浏览器Diffusion,变更监测工具Herald,Bug跟踪工具Maniphest和维基工具Phriction。Phabricator可与Git、Mercurial、Subversion集成使用。 Phabricator是开源软件,可在Apache许可证第2版下作为自由软件分发。 Phabricator*初是Facebook的一个内部工具,主要开发者为Evan Priestley。Evan Priestley离开Facebook后,在名为Phacility的新公司继续Phabricator的开发。

官网:https://www.phacility.com/

官网中的文档很多很全,但是如果对这个工具不太了解,或者对于Code Review不太了解的话,读起来可能会觉得没有头绪。这篇文章就自己的安装及使用过程做一个梳理,对于同样想用这个工具的读者,或许起到一些帮助作用。 对于本文中的一些章节,如果在官方文档有所提及,我会把官方文档地址附上,读者可以阅读一下官方文档,因为他们的语言和表达更优秀。

0x20 我的应用环境

  • 我:一个做了很多年Android的程序员啊 (所以当这篇文章有幸被所涉及领域的专家看到,又发现有的部分有所不妥,或者有更好的想法时,请主动联系我改进,多谢~~~ 有任何问题,欢迎评论交流)
  • 主机:Ubuntu 14.04 PC一台
  • 网络:内网
  • 代码库:GitHub
  • CI:Jenkins

0x30 路线图

%title插图%num

安装和使用路线大致如此图,下面开始详细说明。

0x40 安装

Phabricator是一个基于Web的工具软件,使用PHP语言编写的,为了能让他运行起来,我们需要搭建一个LNMP(Linux,Nginx,MySQL,PHP)的Web Server环境。搭建完LNMP的环境后,下载Phabricator源码,配置后即可使用。

  • 先看我 如果你不想读那么多文字,在Ubuntu环境下可以试试下面的脚本,这个脚本可以安装LNMP环境和下载Phabricator源码,执行完脚本并成功后,跳到 0x42 Phabricator源码下载及运行 查看如何让Phabricator跑起来。
  1. #!/bin/bash
  2. confirm() {
  3. echo “Press RETURN to continue, or ^C to cancel.”;
  4. read -e ignored
  5. }
  6. GIT=‘git’
  7. LTS=“Ubuntu 10.04”
  8. ISSUE=`cat /etc/issue`
  9. if [[ $ISSUE != Ubuntu* ]]
  10. then
  11. echo “This script is intended for use on Ubuntu, but this system appears”;
  12. echo “to be something else. Your results may vary.”;
  13. echo
  14. confirm
  15. elif [[ `expr match $ISSUE $LTS` -eq ${#LTS} ]]
  16. then
  17. GIT=‘git-core’
  18. fi
  19. echo “PHABRICATOR UBUNTU INSTALL SCRIPT”;
  20. echo “This script will install Phabricator and all of its core dependencies.”;
  21. echo “Run it from the directory you want to install into.”;
  22. echo
  23. ROOT=`pwd`
  24. echo “Phabricator will be installed to: ${ROOT}.”;
  25. confirm
  26. echo “Testing sudo…”
  27. sudo true
  28. if [ $? -ne 0 ]
  29. then
  30. echo “ERROR: You must be able to sudo to run this script.”;
  31. exit 1;
  32. fi;
  33. echo “Installing dependencies: git, nginx, mysql, php…”;
  34. echo
  35. set +x
  36. sudo apt-get -qq update
  37. sudo apt-get install \
  38. $GIT nginx mysql-server dpkg-dev \
  39. php5 php5-mysql php5-gd php5-dev php5-curl php-apc php5-cli php5-json
  40. # Enable mod_rewrite
  41. sudo a2enmod rewrite
  42. HAVEPCNTL=`php -r “echo extension_loaded(‘pcntl’);”`
  43. if [ $HAVEPCNTL != “1” ]
  44. then
  45. echo “Installing pcntl…”;
  46. echo
  47. apt-get source php5
  48. PHP5=`ls -1F | grep ‘^php5-.*/$’`
  49. (cd $PHP5/ext/pcntl && phpize && ./configure && make && sudo make install)
  50. else
  51. echo “pcntl already installed”;
  52. fi
  53. if [ ! -e libphutil ]
  54. then
  55. git clone https://github.com/phacility/libphutil.git
  56. else
  57. (cd libphutil && git pull –rebase)
  58. fi
  59. if [ ! -e arcanist ]
  60. then
  61. git clone https://github.com/phacility/arcanist.git
  62. else
  63. (cd arcanist && git pull –rebase)
  64. fi
  65. if [ ! -e phabricator ]
  66. then
  67. git clone https://github.com/phacility/phabricator.git
  68. else
  69. (cd phabricator && git pull –rebase)
  70. fi
  71. echo
  72. echo
  73. echo “Install probably worked mostly correctly. Continue with the ‘Configuration Guide’:”;
  74. echo
  75. echo ” https://secure.phabricator.com/book/phabricator/article/configuration_guide/”;
  76. echo
  77. echo “You can delete any php5-* stuff that’s left over in this directory if you want.”;

关于安装和配置,官方文档中有所提及(官方介绍的是 LAMP),请参考

https://secure.phabricator.com/book/phabricator/article/installation_guide/

0x41 LNMP环境的安装
  • 安装Linux
    关于Linux的安装,这里就不说了。
  • 安装Nginx
    sudo apt-get install nginx

    安装完成后,Nginx 的配置文件存放在 /etc/nginx 目录下。使用下面的命令可以启动Nginx

    sudo service nginx start

    在安装完并启动后,可以使用浏览器访问 http://127.0.0.1 试试是否可以跳转到Nginx欢迎页面

    %title插图%num

  • 安装MySQL
    sudo apt-get install mysql-server

    在安装过程中,会两次提示输入 root 用户密码。
    在安装完成后,打开终端,使用以下命令登录MySQL

    mysql -u root -p
  • 安装PHP
    Phabricator需要 PHP 5.2 或者更高版本,但是 不支持 PHP 7
    可以使用以下命令安装PHP

    sudo apt-get install -y php5 php5-fpm php5-mysql

    安装完成后,可使用以下命令查看是否安装成功

    php -v

    安装成功后,输出类似以下信息

    %title插图%num

  • 安装其它
    如果你使用 git 来管理代码库的话,你还需要安装 git

    sudo apt-get install git

    一些必要的PHP扩展

    mbstring, iconv, mysql (or mysqli), curl, pcntl(这些扩展一般会以 “php-mysql”“php5-mysql” 方式使用)

    一些可选的PHP扩展

    gd, apc(官方文档中有详细的介绍和安装说明), xhprof(如果你想自己开发Phabricator的话,你需要安装这个,官方文档中有详细的介绍说明)

0x42 Phabricator源码下载及运行

在成功安装LNMP环境后,需要下载Phabricator的源码并配置让它跑起来。

  • 源码下载
    在你想要存放Phabricator源码的位置(假设为 ./path_to_pha),执行这些命令

    1. git clone https://github.com/phacility/libphutil.git
    2. git clone https://github.com/phacility/arcanist.git
    3. git clone https://github.com/phacility/phabricator.git

    或者,你也可以直接点击上面的链接去GitHub下载压缩包,下载完成后解压。

  • Nginx配置
    在下载完成后,我们需要配置Nginx,让Phabricator跑起来。假设你想为Phabricator分配这个域名:http://pha.example.com
    /etc/nginx/conf.d 目录下创建文件 pha.example.com.conf,存放Phabricator代理配置信息,以下为我的文件内容(注意把 你存放Phabricator的路径 改为你的实际路径)
  1. server {
  2. listen 80;
  3. server_name pha.example.com;
  4. location / {
  5. index index.php;
  6. rewrite ^/(.*)$ /index.php?__path__=/$1 last;
  7. }
  8. #error_page 404 /404.html;
  9. # redirect server error pages to the static page /50x.html
  10. #
  11. error_page 500 502 503 504 /50x.html;
  12. location = /50x.html {
  13. root /usr/share/nginx/html;
  14. }
  15. # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
  16. #
  17. location ~ \.php$ {
  18. root /你存放Phabricator的路径/phabricator/webroot;
  19. fastcgi_pass 127.0.0.1:9000;
  20. fastcgi_index index.php;
  21. fastcgi_param SCRIPT_FILENAME /你存放Phabricator的路径/phabricator/webroot$fastcgi_script_name;
  22. include fastcgi_params;
  23. }
  24. }

配置完成后,重启Nginx

sudo service nginx restart

然后在你的 hosts 文件中,加入 pha.example.com 对应的IP

127.0.0.1 pha.example.com

打开浏览器,访问 http://pha.example.com,会跳转到Phabricator用户注册界面,在这个界面注册的*个用户,将会成为管理员用户。

0x50 配置

0x51 解决基本的配置问题

使用管理员账号登录,左上角会出现黄色感叹号图标,提示有一些配置问题未解决

%title插图%num

这些问题基本都是关于一些参数的设置。点击每一个问题,显示的界面中会有很详细的关于这个问题的描述,和如何解决。

0x52 设置用户登录认证方式

使用管理员账号登录,在左侧的菜单中选择 Auth ,然后点击右上侧 Add Provider,在列表中选则你需要的认证方式。

%title插图%num

我选择是 Username/Password 的方式,即用户自己注册Phabricator账号。为了保障安全,我设置了只允许公司邮箱地址注册:Config —> Core Settings —> Authentication —> auth.email-domains。你还可以选择 auth.require-approval ,即新注册用户需要管理员批准。

0x53 设置邮件发送服务参数

首先,配置 mail-adapter (邮件发送方式):Config —> Core Settings —> Mail —> metamta.mail-adapter,我选择的是 PhabricatorMailImplementationPHPMailerAdapter ,通过SMTP的方式发送邮件。在选择完之后,需要设置SMTP服务器地址、账号和密码:Config —> Core Settings —> PHPMailer —> metamta.mail-adapter,根据你自己邮箱的配置,相应的设置 phpmailer.smtp-hostphpmailer.smtp-portphpmailer.smtp-protocolphpmailer.smtp-userphpmailer.smtp-passwordphpmailer.smtp-encoding

0x54 配置代码仓库访问方式:SSH/HTTP
  • SSH
    (如果你不打算允许使用SSH的方式访问代码仓库的话,请忽略这部分)
    1)配置用户账号
    Phabricator需要三个用户账号(三种用户身份):两个用于基本运行,一个用于配置SSH访问。这些账号是指Phabricator所运行服务器系统的账号,不是Phabricator用户账号。
    三个账号分别是:
    www-user:Phabricator Web服务器运行身份。
    daemon-user :daemons (守护进程)运行身份。这个账号是唯一直接与代码仓库交互的账号,其它账号需要切换到这个账号身份(sudo)才能操作代码仓库。
    vcs-user:我们需要以这个账号SSH连接Phabricator。
    如果你的服务器系统中现在没有这三个账号,需要创建:
    www-user:大部分情况下,这个账号已经存在了,我们不需要理这个账号。
    daemon-user :一般情况下,我们直接使用 root 账号,因为会需要很多权限(当然这可能不安全)。
    vcs-user:可以使用系统中现有的一个用户账号,直接创建一个就叫 vcsuser。当用户克隆仓库的时候,需要使用类似 vcsuser@pha.example.com 的URI。
    2)配置Phabricator
    首先,设置 phd.userdaemon-user(root)

    ./path_to_pha/bin/config set phd.user root

    重启 daemons 以确认这个配置工作正常

    ./path_to_pha/bin/phd restart

    然后,配置SSH用户账号vcs-user(vcsuser 或其它你想用的用户)

    ./path_to_pha/bin/config set diffusion.ssh-user vcsuser

    3)配置 Sudo
    www-user
    vcs-user 需要能够使用 sudo 切换到 daemon-user 用户身份才能与仓库交互,所以我们需要配置更改系统的 sudo 配置。
    直接编辑 /etc/sudoers 或者在 /etc/sudoers.d 下创建一个新文件,然后把这些内容写到文件内容中

    www-user ALL=(root) SETENV: NOPASSWD: /usr/lib/git-core/git, /usr/bin/git, /var/lib/git, /usr/lib/git-core/git-http-backend, /usr/bin/ssh, /etc/ssh, /etc/default/ssh, /etc/init.d/ssh
    vcs-user ALL=(root) SETENV: NOPASSWD: /bin/sh, /usr/bin/git-upload-pack, /usr/bin/git-receive-pack

    当然,别忘了把 www-user 和 vcs-user 替换为你实际对应的用户。
    接下来,看看你文件中是不是有这行

    Defaults requiretty

    如果有的话,请用 # 注释掉。

    4)其它SSH配置
    我们还需要查看这两个文件 /etc/shadow/etc/passwdvcs-user 对应的配置是否正确。
    打开 /etc/shadow 文件,找到 vcs-user 对应的那行,看一下第二个字段(密码),是不是 !! ,如果是,请改为 空值(什么都不写) 或者 NP
    打开 /etc/passwd 文件,找到 vcs-user 对应的那行,如果有类似于这样的配置 /bin/false ,请修改为 /bin/sh,否则 sshd 无法执行命令。

    5)配置SSHD端口
    注意
    :Phabricator运行的服务器系统中 sshd 的版本 必须高于 6.2
    假设我们把Phabricator使用的sshd端口设置为 22,这样做的好处是我们不需要在仓库的URI中加入端口号,类似ssh://vcs-user@pha.example.com/xxx/xxx/xxx.git。当然,如果这样做需要我们更改系统已存在的sshd配置改为其它端口。下面来看一下配置的三个步骤:
    i)创建脚本 phabricator-ssh-hook.sh,并且把这个脚本放到类似 /usr/libexec/phabricator-ssh-hook.sh 的目录中(我直接放在 /etc/ssh/ 中,后面会要求变更这个脚本和它的父文件夹所有者,所以这个脚本和它的父文件夹所在的文件夹的所有者不正确的话可能会导致这个脚本执行失败),脚本内容如下

  1. #!/bin/sh
  2. # NOTE: Replace this with the username that you expect users to connect with.
  3. VCSUSER=“vcs-user”
  4. # NOTE: Replace this with the path to your Phabricator directory.
  5. ROOT=“/path_to_pha”
  6. if [ $1 != $VCSUSER ];
  7. then
  8. exit 1
  9. fi
  10. exec $ROOT/bin/ssh-auth” $@

注意把 VCSUSER 替换为你实际的用户,把 ROOT 值替换为你Phabricator源码路径。
创建完脚本后,需要把脚本和它的父文件夹所有者改为 root,并且赋予脚本 755 权限:

  1. sudo chown root /path/to/somewhere/
  2. sudo chown root /path/to/somewhere/phabricator-ssh-hook.sh
  3. sudo chmod 755 /path/to/somewhere/phabricator-ssh-hook.sh

如果你不这么做,sshd 会拒*执行 hook。

ii)为Phabricator创建 sshd_config
/etc/ssh 中创建文件名类似 sshd_config.phabricator 的文件,文件内容如下:

  1. # NOTE: You must have OpenSSHD 6.2 or newer; support for AuthorizedKeysCommand
  2. # was added in this version.
  3. # NOTE: Edit these to the correct values for your setup.
  4. AuthorizedKeysCommand /你的脚本路径/phabricator-ssh-hook.sh
  5. AuthorizedKeysCommandUser vcs-user
  6. AllowUsers vcs-user
  7. # You may need to tweak these options, but mostly they just turn off everything
  8. # dangerous.
  9. Port 你配置的端口号
  10. Protocol 2
  11. PermitRootLogin no
  12. AllowAgentForwarding no
  13. AllowTcpForwarding no
  14. PrintMotd no
  15. PrintLastLog no
  16. PasswordAuthentication no
  17. AuthorizedKeysFile none
  18. PidFile /var/run/sshd-phabricator.pid

注意把 AuthorizedKeysCommand 值替换为你在上一步中脚本实际路径,把 AuthorizedKeysCommandUserAllowUsers 替换为你实际的用户,把 Port 替换为你想配置的端口号。如果你的 Port 值为 22,在你进行下面的操作之前,请查看当前系统中 22 端口是已否占用

sudo netstat -atlunp | grep ssh

如果已经被占用,请修改使用 22 端口的 sshd 配置,一般它们会在 /etc/ssh 下,名称类似 sshd_config,修改完成后,请重启 ssh 服务

sudo /etc/init.d/ssh restart

在完成上面的步骤后,我们来启动Phabricator的 ssh 服务

sudo /path/to/sshd -f /你的Phabricator sshd配置路径/sshd_config.phabricator

一般情况下,sshd 路径为 /usr/sbin
在启动后,我们需要验证以下配置是否有效:
首先,请把你的公钥添加到Phabricator自己的账号中(你可以自己注册一个新的账号),注册完成后登录,然后 点击你的头像 —> 左侧菜单面板 Manage —> 右侧菜单面板 Edit Settings —> 左侧菜单面板 SSH Public Keys —> 右上角 SSH Key Actions —> Upload Public Key

%title插图%num

上传公钥后,执行下面的命令

echo {} | ssh vcs-user@phabricator.yourcompany.com conduit conduit.ping

如果出现类似下面的结果,说明配置有效

{"result":"phabricator.yourcompany.com","error_code":null,"error_info":null}

如果没有出现别的情况,请参考官方文档 Troubleshooting SSH 部分,官方文档地址如下

https://secure.phabricator.com/book/phabricator/article/diffusion_hosting/

接下来,看一下如何配置 HTTP

  • HTTP
    首先,请确认Phabricator的配置项 diffusion.allow-http-auth 设置为 true。可以在 左侧菜单面板 All Setttings 中查找 diffusion.allow-http-auth ,点击之后可设置,请设置为 Allow HTTP Basic Auth
    然后,所有用户需要使用 HTTP 访问仓库之前,需要设置自己的密码:点击你的头像 —> 左侧菜单面板 Manage —> 右侧菜单面板 Edit Settings —> 左侧菜单面板 VCS Password
%title插图%num

强烈建议不要把这个密码设置为你的Phabricator登录密码,因为 vcs 密码很容易泄露。
一般来说,不需要其它配置就可以使用 HTTP 了,如果有问题,请参考官方文档 Troubleshooting HTTP 部分

https://secure.phabricator.com/book/phabricator/article/diffusion_hosting/

配置完仓库访问方式后,我们来看一下如何使用 Phabricator 进行 Code Review。

0x60 使用Phabricator进行Code Review

在进行 Code Review 实践前,先说一些理论方面的东西(开头和 0x61 ,不喜欢可绕过)
Code Review,有时候就像打架一样:我提交了变更,你说不行,要修改;我又提交了一次,你说还是不行,还要改。我不知道你究竟要怎样,你也不知道我感觉受到了打击有多不爽。所以,大家需要对Code Review这件事抱有开放的态度:

  • 为什么我的代码需要其他人审查?
    因为我不是神,我会制造Bug,我会当局者迷。
  • 为什么我要审查其他人的代码?
    因为我要对我们的团队负责,我要保证我们产品的质量,我可能会看到他人代码的Bug,在这些Bug显示出它们的”威力”前,把它们弄死。

Code Review这件事,旨在创造一个共进的团队氛围(交流和技术等),在产品交付给用户(包括我们的测试人员)前,保证产品的质量。

在了解如何使用Phabricator进行Code Review前,我们先了解一下Phabricator Code Review的流程,对其有一个整体上的了解。

0x61 Phabricator Code Review工作流

Phabricator提供两种Code Review的方式:pre-pushpost-push
pre-push
是指审查发生在变更发布前;post-push 是指审查发生在变更已经被发布或者正在发布。
这里我们认为 pre-push 的方式更适合,所以接下来说一下 pre-push 的工作流:

Write, Review, Merge, Publish

从这篇文章,我了解到了这个流程

https://secure.phabricator.com/phame/post/view/766/write_review_merge_publish_phabricator_review_workflow/

如果你之前用过其它的Code Review工具,可能会对这样的流程感到不习惯。在其它工具中,变更(代码,资源文件或其它)会经历这样一个流程: Write, Publish, Review, Merge。首先,你做出一些变更(Write),然后把他们推送到远程仓库(Publish)等待审查者审查。一旦这些代码被审查(Review)并通过,变更会合并(Merge)到一个指定的功能分支。在这个流程中,被合并的变更恰好是被推送的变更(这句话有点模模糊糊,不痛不痒,接下来我们看一下Phabricator的流程,也许会清晰很多)。
接下来,我们看一下Phabricator略有不同的工作流:Write, Review, Merge, Publish。像上面一样,开始的时候,你做出一些变更。但是,接下来的流程就不一样了。
Phabricator认为在开发过程中审查(Review)是一个重要的步骤,对于那些没有审查过的变更,是不可以发布的。
理论上来说,没有审查过的变更不算数:这些变更可能只是临时的,易变的。可能方法上不对,可能缺少来龙去脉,可能根本就是解决错误的问题,等等。审查的参考基础是建立在开发人员和审查人员拥有一个共同认可的变更处理方式,并且这种处理方式是开发过程所有参与人员(项目管理、产品、开发)都期望的,而不仅仅是仅仅做到*终的产品看起来没问题。直到变更经过了这样的审查,我们才能得到稳固的版本。
这样的工作流跟其它工具的审查流程没有实质上的技术区别,但是存在明显的社交活动上的不同:由于变更必须经过审查才能被合并、发布,变更作者需要根据反馈对变更进行调整。另外,审查者根据粗略的草图(所有开发参与人员共同认可的变更方式)进行反馈,而不是简单的批判一件已经完成的变更工作。
Phabricator和其它工具的工作流都有着同样的目的:未审查的代码都只是临时的变更,没有长久或者明显的价值,直到通过审查。
Phabricator工作流的第二步是审查(Review),审查的对象是还没有发布的变更。没有发布的变更被发送到Phabricator等待被审查(通常我们使用 arc diff命令发送审查请求),然后审查者做出反馈。变更作者根据反馈进行修改,在修改过程中,作者不必担心版本、解决方式这些事情。作者可以自由的复位、使用、移除或者舍弃老的变更。在从变更提交审核到审核者反馈,以及作者再次修改整个过程中,没有那种审核者把作者推入一个必须接受或者只能做少量改变的默认发布状态。
一旦通过审核,变更会被合并(Merge)和发布(Publish)(通常,这两个步骤由一个命令完成 arc land)。
这里,Phabricator也与那些先 Publish 的工具不同:默认情况下,Phabricator会舍弃到达*后变更前的所做的中间过程,把*后变更的整个过程压缩成一次提交。总体来说,这意味着舍弃checkpoint commits, rebases, squash-merges, 并且把整个变更过程做为一次 fast-forward commit 提交到目标分支。
Phabricator在一定程度上能做到这些,是因为:什么都没有被发布,所以这种工作流可以以任何想要的方式发布变更。
有了这些,我们可以以我们想要的版本自由的rebase,fast-commit,这些是Phabricator默认的行为。

0x62 进行Code Review所用工具

做为一般用户,常用的工具有两个 DifferentialArcanist

  • Differential-审查代码的工作台
%title插图%num

我们在这里查看变更审查情况,对变更进行审查或评论等操作。
这是某次变更界面操作部分截图

%title插图%num

做为 审查人,可进行的操作有:
Comment:说点什么。可以针对某行代码进行评论,直接点击行号即可
Accept Revision:接受变更,这哥们代码写得不错,不需要改
Request Changes:不行,还要改
Resign as Reviewer:重新指定审查代码的人
Commandeer Revision:字面意思是将这个Revision据为己有的意思,实际上这个时候Reviewer的身份已经变为Owner的身份了,不能再进行Review了,但是Comment还是可以的
Add Reviewer:添加审查人
Add Subscribers:添加订阅者,CC

做为 作者,可进行的操作有:
Comment:说点什么。可以针对某行代码进行评论,直接点击行号即可
Abandon Revision:废除版本。废除后,这个版本就不需要再审核了
Plan Changes:计划变更,我自己发现了一些问题或者需求有变,正在改
Add Reviewer:添加其它审查人(除当前审查人外)
Add Subscribers:添加订阅者,CC

  • Arcanist – 命令交互
    我们用这个工具提交变更和审查请求,对变更做出更改,或者在通过审查后发布到远程仓库分支中。
    常用的命令有:
    arc diff:发送变更详情和审查请求
    arc land:推送变更(Git and Mercurial),当通过审查后使用这个命令
    arc list:显示变更处理的情况
    arc cover:查找*有可能审查变更的人
    arc patch:给版本打补丁
    arc export:从Differential下载补丁
    arc amend:更新Git commit
    arc commit:提交变更(SVN)
    arc branch:查看Git branches更加详细的信息

    在配置了 lint 和 unit test intergration后,可以用这些命令:
    arc lint:静态代码检查
    arc unit:单元测试

    与其它工具交互:
    arc upload:上传文件
    arc download:下载文件
    arc paste:创建和查看剪贴

    还有一些高级功能:
    arc call-conduit:执行 Conduit 方法
    arc liberate:创建或更新 libphutil 库
    arc shell-complete:激活 tab 补全

0x63 配置进行Code Review

一些基本的配置和安装 —> 写代码 —> 提交审查请求(arc diff) —> 审查(Differential) —> (审查通过后)合并提交(arc land)

  • 一些基本的配置和安装
    包括:
    配置代码仓库(Diffusion)
    把你本地的Git远程URL设置为Phabricator上代码仓库地址
    安装Arcanist
    配置Project信息
  • 配置代码仓库(Diffusion)
    在开始进行代码审查后,我们的代码是由Phabricator直接托管的,所以我们需要配置代码仓库。
    使用管理员账号登录Phabricator,点击左侧面板菜单 Diffusion ,然后点击右上侧 Create Repository ,选择你所使用的 Repository 类型,填写 Name 等信息,在创建完成后即可使用。如果没有什么特殊的需求,不需要进行特别的配置,这里列举两种你可能遇到的打算开始使用 Phabricator 时的场景:

    1、代码之前由 GitHub 或其它托管,现在我需要把之前的代码导入
    点击 Manage Repository ,点击左侧 URIs,点击 Add New URI,填写GitHub或其它托管系统对应仓库的 URI , I/O Type 选择 Observe,点击 Create Repository URI 添加新的 URI 。

    %title插图%num

    在添加完新的 URI 后,你还需要点击 Set Credential 设置访问新的 URI 的认证方式。
    如果你打算此时就开始使用 Phabricator ,请务必通知你的团队,暂停一下,不要再向GitHub等提交代码。如果你的 GitHub 等也设置了代码审查,请督促相关人员完成代码审查流程。
    稍等片刻,待 Phabricator 同步完之前的代码后,编辑你添加的 GitHub 或其它代码托管系统的 URI ,务必修改 I/O Type
    1)如果你不再需要使用之前的托管系统,选择 No I/O
    2)如果你想继续把代码备份到之前的代码托管系统,选择 Mirror,这时, Phabricator 代码仓库的变更会覆盖推送到之前的代码托管系统
    如果你不修改 I/O Type,向 Phabricator 代码仓库提交代码会失败,因为是只读的。
    当然,对于导入之前的代码,还有别的方式,例如直接把本地的代码再次向 Phabricator 代码仓库再提交一次。

    2、开始一个新的项目,创建一个新的仓库
    参考第1种场景,在 Phabricator 创建代码仓库。如果你希望把代码备份到其它的托管系统,只需要添加对应的 URI,并且把 I/O Type 选为 Mirror

  • 把你本地的Git远程URL设置为Phabricator上代码仓库地址
    git remote set-url 远程名称 新的url
  • 安装Arcanist

    https://secure.phabricator.com/book/phabricator/article/arcanist_quick_start/

  • 配置Project信息
    在你项目代码的根目录下,创建 .arcconfig 文件,内容如下:

    1. {
    2. “phabricator.uri” : “你Phabricator系统访问URL”
    3. }

    Windows系统下,创建类似这种文件名的文件可能很麻烦,可以使用这条命令创建:

    arc set-config phabricator.uri "你Phabricator系统访问URL"

    Windows系统下,还需要配置 Editor ,详情参考:

    https://secure.phabricator.com/book/phabricator/article/arcanist_windows/

    Arcanist 使用可参考:

    https://secure.phabricator.com/book/phabricator/article/arcanist_quick_start/
    https://secure.phabricator.com/book/phabricator/article/arcanist/

    在进行完基本的配置和安装后,可以开始 Code Review 了。

  • 写代码
    当然,不只是代码可以被审查,图标等资源文件的变更也可以被审查。
  • 提交审查请求(arc diff)
    一般情况下,我们直接使用 arc diff 即可,默认情况下,Arcanist 会把本地分支的 HEAD 与远程对应分支的 HEAD 进行对比,并生成差异对比发送到 Phabricator。当你所做的修改没有 commit 时,会提示你进行 commit。
    在一些情况下,我们并不希望与本地分支的 HEAD 进行比较,假设想要与上次的 commit 比较,上次 commit id 是 8ffc88dc05d31fffd28e3ff1129d1b8c321dffff,那么我们需要在 arc diff 后把这个 id 加上:arc diff 8ffc88dc05d31fffd28e3ff1129d1b8c321dffff
    执行这条命令时我们需要按照模板填写title(必填),summary(必填),Test Plan(必填,没有可写 N/A 之类的标识),Reviewers(必填,且必须为真实有效的用户名),Subscribers(可选),填写完成后关闭编辑器,Arcanist会自动提交审查请求。

  • 审查(Differential)
    做为 审查人,需要在 Differential 工作台完成代码审查工作,上面已经介绍了 Differential,这里就不再多说了。
  • (审查通过后)合并提交(arc land)
    做为 作者,在代码审查通过后,需要使用 arc land 把变更发布到远程分支。
    注意,在首次执行这条命令前(不一定非要到这个步骤,可以是这个步骤前的任意时刻,例如开始写代码前),建议请使用 git branch -u 远程名称/远程分支名称 把本地的分支与远程分支相关联,否则,在执行完 arc land 后,本地分支会被删除。如果你不想这样做,又需要保留现在分支的话,请使用 arc land --keep-branch
    默认情况下,Arcanist 会把变更推送到与本地分支相关联的远程分支,你可以使用 –remote –onto 参数推送到你想要的远程和远程分支。
    关于 arc land 的详细说明,可使用 arc land --help 查看。

0x70 与GitHub集成

这里的“集成”其实说的很心虚,因为在使用 Phabricator 后,GitHub 已经变为一个文件存储服务器了。常见的使用情况已经在 0x63 配置进行Code Review 配置代码仓库(Diffusion) 中写出,所以你懂的。

0x80 与Jenkins集成

在很久很久以前,我已经搭建了 Jenkins 用于自动构建,所以这次把 Phabricator 与 Jenkins 做了集成。
在 Jenkins 中安装完插件: Phabricator Differential Plugin 后,请参考这篇文章:

https://github.com/uber/phabricator-jenkins-plugin#phabricator-jenkins-plugin–

0x90 结束语

从配置完到开始试用已经有一个月左右,期间遇到了各种问题。经历了这些问题的洗*,算是对 Phabricator 使用入门了吧。
这篇文章时隔多日才完成,尽管我想把每个细节写的详尽,但是记忆总是像被虫蛀过的木头,难免有些疏漏。加之文笔水平有限,各位就凑合着看吧。
在安装和使用过程中遇到的问题,欢迎各位评论交流。

0xA0 Q/A

1、如何强制用户 Code Review?
再次强调一下前提:在开始 Code Review 流程前,请先确认团队成员的git remote url已经切换为Phabricator上对应仓库地址。
强制用户Code Review,需要创建Herald Rule。在创建时,New Rule for 选:Commit Hook: Commit Content.Rule Type选:Global,或者根据自己需要选择;Conditions中是组合条件,可以根据自己需要指定一些条件;Action 指定当前情况符合你指定的条件组合时执行的动作。示例如图:

%title插图%num

示例中定义了以下规则:在向develop分支提交代码时,所提交的代码必须是通过 Code Review 流程审查通过的,否则会被拒*;除非 Commit Message 中包含字符 @bypass-review 。
指定 Commit Message 中包含字符 @bypass-review 这种例外情况,主要考虑到在紧急修复一些问题,没时间等待审查时使用。

%title插图%num