沐鸣总代平台为什么有效的DevOps更需要机动性而不是速度

围绕有效的DevOps的大肆宣传使其听起来像是方法论提供的真正价值来自于更快的部署时间。但Cognitect公司的企业架构师迈克尔•尼加德(Michael Nygard)认为,这忽略了围绕可操作性的真正好处。他说:“我们谈论了很多关于速度的东西,但没有太多关于加速度的东西,加速度指的是根据需要移动得更快或更慢的能力。”沐鸣总代平台

企业可以根据变化的条件加快或放慢发展速度,这比竞争更具可操作性。云计算使得基础设施可以任意使用,代码存储库使得代码可以任意使用。尼加德打趣道:“也许连球队都需要一次性的。”这与让人们变得可有可无不同,后者会打击士气

有效的DevOps意味着敏捷

真正的可操作性来自于让团队更容易地分解和快速启动项目。这是有效的DevOps。个人的价值来自于完成和启动项目的团队过程,而不是某个人在特定项目中的角色。Nygard指出,一些部队可以在几小时内解散并建立一个新的营地,而其他部队则需要几天的时间。这种差异来自于在成千上万个小决定之间的合作经验,比如如何以正确的顺序移动卡车,或者在哪里放置厕所。这意味着围绕版本控制和在企业中构建管道等问题开发一种共享的理解。沐鸣开户

团队成员还需要善于直觉,知道其他人可能会做出什么样的决定来应对不断变化的情况。军队里的一个小单位指挥官对其他指挥官如何做决定有很好的想法。这是在有效的DevOps团队中所缺乏的,这些团队根据功能和地理位置进行分散。“节奏是一种突现的属性,它来自于你的组织的某些特征,必须在每个层次上进行构建,”Nygard说。沐鸣注册网站

沐鸣:不断破坏您的CI/CD工作流的一件事

公司和团队想要快速行动。这包括频繁发布、不断更新产品以及让团队成员随时了解新的相关技术。这些需求导致了持续集成和持续交付实践的兴起。

目前对CI/CD周期的广泛理解为测试-构建-部署阶段增加了很多自动化功能,但是它遗漏了完整发布周期中的一个关键步骤。在接下来的文章中,我们将了解为什么CI/CD周期不会在部署之后结束,沐鸣:以及为什么将自动化添加到监视实践中很重要。我们来看看。

当前的CI/CD工作流包含了软件工程中最大的误解之一:如果许多团队在代码进入生产时遇到了相同类型的问题,那么在团队进行自动化和CI/CD的过程中,一定存在一些根本性的问题。

为了深入探究为什么会出现这种情况,我们在过去几年中与数百个开发团队进行了交流,并找到了一些共同点,解释了为什么在开发速度加快的同时,开发团队也会受到负面影响。

主要的问题是,当人们想到CI/CD时,他们通常会有意或无意地将其视为一个循环,从提交代码开始,直到将新代码部署到生产环境为止。最大的误解是,当将新代码部署到生产环境中时,CI/CD工作流就结束了。团队倾向于认为,当代码在野外出现时,自动化就停止了。这是完全错误的。

监视是CI/CD周期中不可分割的一部分,自动化部署需要更智能的监视。您想知道在不依赖于用户报告的情况下,何时发布了新的错误,并拥有修复这些错误所需的所有信息。

就连扎克伯格也改变了想法和工作流程。2014年,该公司将其座右铭从“快速前进,打破常规”改为“快速前进,拥有稳定的基础设施”。社交网络需要确保应用程序和功能是稳定的,没有生产中断的风险。沐鸣代理:

如何自动化根本原因分析?

克服CI/CD障碍的企业和初创公司的工程团队通过构建贯穿软件发布周期所有阶段的策略来实现——从构建、测试、部署和监视。当康卡斯特的工程团队面临着在发布后调试旗舰X1 XFINITY平台的挑战时,尽可能提高效率对他们来说至关重要。要了解更多康卡斯特是如何将自动错误解决策略引入他们的工作流程的,请加入我们的网络研讨会。

让我们试着理解为什么这么多的工程团队在实际部署代码之后会面临挑战。CI/CD方法可以帮助您的团队更快地进行创新,不断地发布更新,并为您的用户提供更好的产品。然而,如果您没有将自动化添加到您的监视过程中,那么这个周期可能会带来隐藏的成本,可能会破坏您的工作流。

这些成本有时会被忽视,直到为时已晚,提前承认并理解它们是很重要的:

  1. 生产出错率增加

期待意想不到的。即使是最彻底的测试、分期和QA过程也会让错误从裂缝中溜走。用户报告仍然是关于错误的最大信息来源,并且错误解决过程是反应性的。CI/CD加快了中断和代码中断的速度。

业务结果:糟糕的用户体验和流失的用户。即使是几分钟的失败交易,也会造成数十万美元的损失。

  1. 减少员工的效率

开发人员已经花费了20-40%的时间进行调试。除了对应用程序和服务质量的影响之外,工程师花在调试软件而不是构建新功能上的时间比例也在增加。试图更快地行动往往会导致相反的结果。

业务结果:生产力下降、员工流失和无法控制的支出。沐鸣5网站

3.糟糕的版本会使部署过程停止

仅依赖于日志记录和性能管理工具的过时的生产监视实践常常会使CI/CD过程停滞数日。需要采用一种明智的生产错误处理策略,以享受快速创新带来的好处,同时降低相关风险。

业务结果:产品路线图延迟,上市时间慢。错过最后期限和管理费用。

快速部署带来了巨大的责任

许多公司已经采用或正在采用CI/CD方法作为其工作流程的一部分,以更快地进行创新。快速的上市时间不仅仅是一个很好的能力;这是一个成功公司的基石。

从巨石到云沐鸣测速注册的原生构成与学徒达的辛克莱舒勒

在我们关于云本地计算的系列文章中,TheServerSide采访了该领域的一些专家,包括云本地计算基金会的一些成员。以下是Cameron McKenzie和da的Sinclair Schuller的采访录音。沐鸣测速注册

对da的Sinclair Schuller的采访

如何定义云本地计算?

Cameron McKenzie:在服务端探索传统的企业Java开发如何适应这个使用微服务、Docker、container和Kubernetes的云本地计算的新世界的过程中,我们找到了Sinclair Schuller。Sinclair Schuller是da的CEO。他也是Kubernetes的拥护者,也是云原生计算基金会(Cloud Native Computing Foundation)的董事会成员。

因此,我们想从Sinclair了解的第一件事是,如何定义云本地计算?

Sinclair Schuller:问得好。我想我会给你一个基于建筑学的定义。对我来说,云本机应用程序是一个应用程序,该应用程序有一个隐式或显式弹性资源能力锻炼它生活在一定程度的可移植性,便于运行在各种场景中,无论是在云还是云B或c,但在这些情况下,它能够理解和/或运动底层资源以一种弹性的方式可能是我会用最基本的定义。这与可能部署在服务器上的传统非云本地应用程序不同。他们不知道他们下面的资源是可替代的或有弹性的,沐鸣总代理所以他们永远不能利用这些基础设施的属性。这就是它们固有的不可扩展性,固有的不可弹性等等的原因。
Cameron McKenzie:现在,有很多关于应用服务器如何死亡的讨论,但是每当我看到人们为了管理容器和微服务而创建的这些环境时,他们都将所有这些工具结合在一起,来完成诸如监视、日志记录和编制等工作。最终,所有这些将导致某种仪表盘,让人们看到云原生架构中发生了什么。我的意思是,应用服务器真的死了吗?或者这只是要重新定义什么是应用服务器?
辛克莱·舒勒:我认为你说得很对。我认为,整个应用服务器是死的,不幸的是,这是劣质营销的结果,新供应商试图重新定位老供应商。这很公平,对吧?这就是这些事情的运行方式,但这与老的应用服务器的死机毫无关系。在很多情况下,他们仍然会将应用程序部署到容器中的Tomcat之类的东西上。所以这是会发生的,所以这本身并没有消失,即使你在建立新的微服务。

传统的重量级应用服务器消失了吗?是的。所以我认为这已经失宠了。以WebLogic或其他类似的老产品为例,在新的云本地开发中不再使用它们。但是你是对的,接下来会发生什么,我们可以肯定地看到,有一组相当松散耦合的工具开始围绕着这个新模型,在这方面它看起来很像一个应用服务器。

在这一点上,我不同意你的看法:我不知道是否会出现整合。但可以肯定的是,如果有的话,那么您最终得到的将是一个拥有所有工具的供应商,可以有效地交付一个云本地应用程序服务器。如果没有整合,并且该工具保持相当松散的耦合和碎片化,那么我们的结果会比传统的应用服务器模型稍微好一些。我们有能力以我们所需要的方式生产出最好的零工工具。沐鸣总代

云本地计算和UI开发

Cameron McKenzie:许多开发人员和架构师都在问的一个问题是,“在云原生计算的世界里,UI的角色是什么?”“它仅仅是像Angular UI这样的前端JavaScript框架开发的吗?”它是否实际嵌入到部署到容器的应用程序中?在云本地开发方面,UI开发领域到底发生了什么?

Sinclair Schuller:我认为在某种程度上,UI之战已经取得了胜利,对吗?似乎市场已经决定使用JavaScript和像Angular这样的库来为这些后端服务构建有效的前端。我想那边已经没有太多的骚动和争议了,这就是为什么你很少听到它。后端有点滞后,现在有了容器,这是革命性的。
Sinclair Schuller:我认为在某种程度上,UI之战已经取得了胜利,对吗?似乎市场已经决定使用JavaScript和像Angular这样的库来为这些后端服务构建有效的前端。我想那边已经没有太多的骚动和争议了,这就是为什么你很少听到它。后端有点滞后,现在有了容器,这是革命性的。所以我认为后端服务和容器所发生的事情与JavaScript非常相似,比如说,10年前它真正开始流行的时候。所以我认为JavaScript和HTML5扮演的角色已经很明确了,我们不会看到太多的变化。

我可以开发一个“hello world”应用程序。我可以把它写成微服务。我可以将其打包到Docker容器中,并将其部署到某种托管环境中。我不能做的是,我不能进入航空公司制造商的数据中心,分解他们的整体,把它变成微服务,弄清楚哪些应该是粗粒度的微服务,哪些应该是细粒度的微服务。我不知道我最终应该使用多少微服务,而且一旦我完成了所有这些,我就不知道如何在生产环境中安排所有这些服务。

在推进云本地计算方面,da的角色

你是怎么做到的?组织如何采用这种云本地架构和这种云本地基础设施和可伸缩性?

Sinclair Schuller:是的,你刚才描述了我们的业务。实际上,我们在市场上注意到的是,如果你把世界上最大的公司都建立在这些单一的应用程序上,他们面临的挑战是“我们如何分解它们,如何将我们的信息推进到现代时代?”如果我们可以,当然,一些应用程序不需要这些,我们如何至少在云环境中以更好的方式运行它们,这样我们就可以获得额外的效率,对吗?”

因此,我们关注的是为云原生平台提供一个平台,并确保它提供错误桥接功能(因为缺乏更好的术语)。Apprenda,我们建造的方式,我们的IP将允许您运行一个庞然大物在这个平台上,我们会乐器变化到应用程序和行为不同,它可以在一个基于云的基础设施环境,给这庞然大物一些云架构元素,如果你愿意。

为什么这很重要?如果你能做到这一点,您可以快速欢迎一群这些应用程序在云这样的本地平台和删除突然要求你必须改变的巨石,它迅速分解为谁知道多少microservices,你的观点,它提供一点松弛,所以这些企业的开发团队可以更深思熟虑的决定。他们可以选择一个整体的一部分,把它分离出来,作为一个真正的纯微服务来利用,并且仍然在同一个平台上运行并且与现在剩下的部分一起工作。

通过这样做,我们实际上鼓励企业加速采用云本地架构,因为这并不是一个如此突然的决定,也不是架构本身的如此突然的变化。所以对我们来说,我们对这一步充满热情。第二,我如何同时管理大量的数据?在我看来,像da这样的优秀云抽象,一个基于Kubernetes的优秀平台,其目标是让管理10个、1000个或N个微服务变得像运行1个或2个一样简单。

如果你能做到这一点,如果你能把运行1或2变成运行大量微服务的一种方式,你就能把这个成本从等式中去掉让企业更容易消化整个问题。所以我们非常强调这两件事。我们如何提供这种衔接能力这样你就不必经历这样的突然转变你可以在一个更适合你的时间轴上这样做这符合你的需要同时也能处理规模问题?

然而,最终解决规模问题的唯一方法是,云平台必须真正提取底层基础设施资源,并充当微服务层和这些基础设施资源之间的中介。如果它能做到这一点,那么扩展实际上就变得有点微不足道了,因为这是正确架构平台的结果。

卡梅隆·麦肯齐:你说的是抽象。你能谈谈抽象这一层的技术方面吗?

沐鸣娱乐怎么样?花太多时间调试会导致什么问题

我们询问了一些与我们一起工作的工程团队,他们的开发人员在调试过程上花费了多少时间。答案吗?平均而言,25%的开发人员的时间都花在了调试上。这意味着一周中有超过一天的时间专门用于故障排除和问题解决。沐鸣娱乐怎么样?

在前一篇文章中,沐鸣注册平台我们研究了这段时间最重要的5个方面(比如搜索日志文件、重现错误、“战争空间”类型的情况等等)。现在,我们需要讨论一下花在调试上的时间对公司的影响。沐鸣平台网址

沐鸣平台首页yield 和 yield*

代理 yield
我们先来看看原生语法中,代理 yield(delegating yield)的含义和用法。

首先是下面的代码(普通的 yield):沐鸣平台首页

function* outer() {
yield ‘begin’;
yield inner();
yield ‘end’;
}

function* inner() {
yield ‘inner’;
}

var it = outer(), v;

v = it.next().value;
console.log(v); // -> 输出:begin

v = it.next().value;
console.log(v); // -> 输出:{}
console.log(v.toString()); // -> 输出:[object Generator]

v = it.next().value;
console.log(v); // -> 输出:end
然后是下面的代码(代理 yield):

function* outer() {
yield ‘begin’;

/*
 * 这行等价于 yield 'inner';就是把inner里面的代码替换过来
 * 同时获得的rt刚好就是inner的返回值
 */
var rt = yield* inner();
console.log(rt);  // -> 输出:return from inner

yield 'end';

}

function* inner() {
yield ‘inner’;
return ‘return from inner’;
}

var it = outer(), v;

v = it.next().value;
console.log(v); // -> 输出:begin

v = it.next().value;
console.log(v); // -> 输出:inner

v = it.next().value;
console.log(v); // -> 输出:end
根据文档的描述,yield* 后面接受一个 iterable object 作为参数,然后去迭代(iterate)这个迭代器(iterable object),沐鸣注册平台官网同时 yield* 本身这个表达式的值就是迭代器迭代完成时(done: true)的返回值。调用 generator function 会返回一个 generator object,这个对象本身也是一种 iterable object,所以,我们可以使用 yield* generator_function() 这种写法。

啊,好绕。在我实际的使用过程中,yield* 一般用来在一个 generator 函数里“执行”另一个 generator 函数,并可以取得其返回值。

yield 和 co
我们再来看看 co 中 yield 的用法,代码如下:

co(function* () {

// yield promise
var a = yield Promise.resolve(1);
console.log(a); // -> 输出:1

// yield thunk
var b = yield later(10);
console.log(b); // -> 输出:10

// yield generator function
var c = yield fn;
console.log(c); // -> 输出:fn_1

// yield generator
var d = yield fn(5);
console.log(d); // -> 输出:fn_5

// yield array
var e = yield [
Promise.resolve(‘a’),
later(‘b’),
fn,
fn(5)
];
console.log(e); // -> 输出:[‘a’, ‘b’, ‘fn_1’, ‘fn_5’]

// yield object
var f = yield {
‘a’: Promise.resolve(‘a’),杏耀注册平台官网
‘b’: later(‘b’),
‘c’: fn,
‘d’: fn(5)
};
console.log(f); // -> 输出:{a: ‘a’, b: ‘b’, c: ‘fn_1’, d: ‘fn_5’}

function* fn(n) {
n = n || 1;
var a = yield later(n);
return ‘fn_’ + a;
}

function later(n, t) {
t = t || 1000;
return function(done) {
setTimeout(function() { done(null, n); }, t);
};
}

}).catch(function(e) {
console.error(e);
});
等等!为什么 co 可以直接 yield 一个 generator 而不用 yield*,更奇怪的是居然可以直接 yield 一个 generator function?

实际上,co 里面做了封装,在 co 的 toPromise 代码里有这样的判断:

if (isGeneratorFunction(obj) || isGenerator(obj)) return co.call(this, obj);
所以,其实我们的代码:

// yield generator function
var b = yield fn;
// yield generator
var c = yield fn(5);
实际上会变成类似这样:

var b = yield co(fn);
var c = yield co(fn_generator);
而 co 方法最后会返回一个 Promise,同时还会对传入的参数进行处理,如果是函数则直接调用这个 generator function 取得 generator,大致如下:

function co(gen) {
var ctx = this;
var args = slice.call(arguments, 1)

return new Promise(function(resolve, reject) {
if (typeof gen === ‘function’) gen = gen.apply(ctx, args);
// 其他代码
});
}
所以,再 yield 这个 Promise 后,保证了我们取到了正常结果。

其实,我们也可以直接用“原生”的语法写成:

var b = yield* fn();
var c = yield* fn(5);
因为 yield* 接受一个 iterable object 作为参数(比如:generator function 返回的 generator object),所以,我们需要调用 generator function 来返回一个 generator object。

所以,co 背后是封装了很多黑魔法的,这多创建了闭包,会有一点点点点的内存消耗。不是我打错了,根据这里的讨论:https://github.com/koajs/compose/issues/2,确实没有非常要命的影响。而我们的 TJ 大神非常不喜欢在 yield 和代理 yield 间切换,他觉得本来就应该是语言自己去帮我做代理的,反正我就是不想写 yield*。

不过嘛,这个 issue 最后的结果还是将 next 实现成了一个 generator object,所以我们才会在各种 koa 的中间件里面看到 yield* next 这种写法。

yield* 的作用
好了,在 co 的世界里,yield* fn “几乎”等价于 yield co(fn)。既然这样,那 yield* 到底有啥用啊?还是有用处的。具体有下面这几点:

用原生语法,抛弃 co 的黑魔法,换取一点点点点性能提升
明确表明自己的意图,避免混淆
调用时保证正确的 this 指向
第一点上面有提到,就不展开说了。

第二点我们来解释下。看看下面的代码,你能知道 yield 后面的到底是个什么鬼吗?

var v = yield later();
如果你不看 later 的实现,实际上你并不知道。later 方法完全可以实现成返回一个 Promise,返回一个 thunk,返回一个数组、对象,或者就是返回 generator object。

但是下面这样的代码:

var v = yield* later();
我们就很自信的知道 later 肯定是返回一个 generator object 了。

第三点也是 co 黑魔法的一个大坑。

比如我们有下面这样的代码:

function later(n, t) {
t = t || 1000;
return function(done) {
setTimeout(function() { done(null, n); }, t);
};
}
function Runner() {
this.name = ‘runner’;
}
Runner.prototype.run = function* (t) {
var r = yield later(this.name, t);
return ‘run->’ + r;
};

var runner = new Runner();
var result = yield runner.run;
console.log(result); // -> 输出:run->undefined
你会发现, result 是 run->undefined,这说明 this 的指向不对了。这是因为 co 实际上是类似这样执行上面的代码的:

var runner = new Runner();
var result = yield co(function() {
runner.run.call(this); // 这里的this变成了co的实例…
});
破解大法也很简单,就是使用原生语法:

var result = yield* runner.run();
console.log(result); // -> 输出:run->runner
那么,我就说完了。如果各位没有了解过 co 的黑魔法,看的可能会有点累,推荐大家看看参考资料的第一条。

浅谈 沐鸣注册平台Node.js 和 PHP 进程管理

众所周知,PHP 占据了服务端编程语言的半壁江山,正如汪峰在音乐圈的地位一般。随着 Node.js 逐渐走上服务端编程的舞台,关于 PHP 和 Node.js 孰优孰劣的争论也不曾间断。

垄断性的市场份额足以佐证 PHP 的优秀。并且 HHVM 虚拟机、PHP 7 的革新,也给 PHP 带来了跨越式的性能突破。然而,当我们为语言层面的性能差异喋喋不休时,却往往忽略了 Web 模型在性能表现中的权重。沐鸣注册平台

从 CGI 到 FastCGI
早期的 Web 服务,是基于传统的 CGI 协议实现的。每个发送到服务器的请求,都需要经过启动进程、处理请求、结束进程三个步骤,以至于访问量增大时,系统资源(如内存、CPU 等)开销也巨大,导致服务器性能下降甚至服务中断。

图 1:简单的 CGI 流程示意

在 CGI 协议下,解析器的反复加载是性能低下的主要原因。如果让解析器进程长驻内存,那么它只需启动一次,就可以一直执行着,不必每次都重新 fork 进程,这就有了后来的 FastCGI 协议。

如果 FastCGI 仅仅做到这样,那么和 Node.js 单进程单线程的模型是基本一致的:Node.js 进程启动后保持持续运行,所有的请求都由这个进程接收和处理,当某个请求引起未知错误时,才可能致使进程退出。

事实上 FastCGI 并没有那么简单,为了保证服务的稳定性,他被设计成了多进程调度的模式:

图 2:Nginx + FastCGI 执行过程

这个过程同样可以描述为三个步骤:

首先,初始化 FastCGI 进程管理器,并启动多个 CGI 解释器子进程;
接着,当请求到达 Web 服务器时,进程管理器选择并连接一个子进程,将环境变量和标准输入发送给它,处理完成后将标准输出和错误信息返还给 Web 服务器;
最终,子进程关闭连接,继续等待下一个请求的到来;沐鸣平台网址

从 child_process 到 cluster
我们回过头来看看 Node.js 的进程管理方式。

原生 Node.js 的单进程单线程模型是一个极易被喷的槽点。这种机制也决定了 Node.js 天生只支持单核 CPU,无法有效地利用多核资源,一旦进程崩溃,还会导致整个 Web 服务的土崩瓦解。

图 3:简单的 Node.js 的请求模型

和 CGI 一样,单一进程始终面临着可靠性低、稳定性差的问题,当真正服务于生产环境时,这样的弱点相当致命。如果代码本身足够健壮,倒可以在一定程度上避免出错,但同时也对测试工作提出了更高要求。现实中我们无法避免代码 100% 不出纰漏,有些东西容易编写测试用例,有些东西却只能依靠人肉目测。

所幸 Node.js 提供了 child_process 模块,通过简单 fork 即可随意创建出子进程。如果为每个 CPU 分别指派一个子进程,多核利用就完美实现了。于此同时,由于 child_process 模块本身继承自 EventEmitter 这个基础类,事件驱动使得进程间的通信非常高效。

图 4:简单的 Node.js master-worker 模型(扒的淘杰老湿的图)

为了简化庞杂的父子进程模型实现,Node.js 紧接着又封装了 cluster 模块,不论是负载均衡、资源回收,还是进程守护,它都会像保姆一样帮你默默地搞定一切。具体技术细节可以参考淘杰老湿的《当我们谈论 cluster 时我们在谈论什么(上)》和《当我们谈论 cluster 时我们在谈论什么(下)》,里面有所有关于 cluster 方案的推演和实现,这里不再赘述。

在 Node.js 里,要让应用跑在多核集群上,只需寥寥几行代码就万事大吉了:

var cluster = require(‘cluster’);沐鸣:
var os = require(‘os’);

if (cluster.isMaster) {
for (var i = 0, n = os.cpus().length; i < n; i ++) {
cluster.fork();
}
} else {
// 启动应用…
}
那么反观 FastCGI 协议,它又是如何处理这种模型的呢?

PHP-FPM 的天生缺陷
PHP-FPM 是 PHP 针对 FastCGI 协议的具体实现,也是 PHP 在多种服务器端应用编程端口(SAPI:cgi、fast-cgi、cli、isapi、apache)里使用最普遍、性能最佳的一款进程管理器。它同样实现了类似 Node.js 的父子进程管理模型,确保了 Web 服务的可靠性和高性能。

PHP-FPM 这种模型是非常典型的多进程同步模型,意味着一个请求对应一个进程线程,并且 IO 是同步阻塞的。所以尽管 PHP-FPM 维护着独立的 CGI 进程池、系统也可以很轻松的管理进程的生命周期,但注定无法像 Node.js 那样,一个进程就可以承担巨大的请求压力。

受制于服务器的硬件设施,PHP-FPM 需要指定合理的 php-fpm.conf 配置:

pm.max_children # 子进程最大数
pm.start_servers # 启动时的子进程数
pm.min_spare_servers # 最小空闲进程数,空闲进程不够时自动补充
pm.max_spare_servers # 最大空闲进程数,空闲进程超过时自动清理
pm.max_requests = 1000 # 子进程请求数阈值,超过后自动回收
和 JS 不一样的是,PHP 进程本身并不存在内存泄露的问题,每个进程完成请求处理后会回收内存,但是并不会释放给操作系统,这就导致大量内存被 PHP-FPM 占用而无法释放,请求量升高时性能骤降。

所以 PHP-FPM 需要控制单个子进程请求次数的阈值。很多人会误以为 max_requests 控制了进程的并发连接数,实际上 PHP-FPM 模式下的进程是单一线程的,请求无法并发。这个参数的真正意义是提供请求计数器的功能,超过阈值数目后自动回收,缓解内存压力。

或许你已经发现了问题的关键:尽管 PHP-FPM 架构卓越,但还是卡在单一进程的性能上了。

Node.js 天生没有这个问题,而 PHP-FPM 却无法保证,它的稳定性受制于硬件设施和配置文件的契合度,以及 Web 服务器(通常是 Nginx)对 PHP-FPM 服务的负载调度能力。

ReactPHP,事件驱动,异步执行,非阻塞 IO
对 PHP 7 的狂热掩盖了 Node.js 带来的猛烈冲击。当大家还沉醉在如何选择 HHVM 还是 PHP 7 的时候,ReactPHP 也在茁壮成长,它彻彻底底抛弃了 nginx + php-fpm 的传统架构,转而模仿并接纳了 Node.js 的事件驱动和非阻塞 IO 模型,甚至连副标题,都起得一毛一样:

Event-driven, non-blocking I/O with PHP.
鉴于大家都比较了解 Node.js,对 ReactPHP 的原理就不再赘述了,我们可以认为它就是个 PHP 版的 Node.js。拿它和传统架构(Nginx + PHP-FPM,公平起见,PHP-FPM 只开一个进程)去做对比,结果是这样的:

图 5:输出“Hello World”时的 QPS 曲线

图 6:查询 SQL 时的 QPS 曲线

我们可以看到,当事件驱动、异步执行、非阻塞 IO 被移植嫁接到 PHP 上后,即便没了 PHP-FPM 支撑,QPS 曲线依然不错,在 IO 密集型的场景下,性能甚至得到了成倍成倍的提升。

事件和异步回调机制真是太赞了,它巧妙地将大规模并发、大吞吐量时的拥堵化解为一个异步事件队列,然后挨个解决阻塞(如文件读取,数据库查询等)。

针对单进程模型的吐槽,或许有些偏激。不过显而易见的事实是,单进程模型的可靠性,在 Web 服务器和进程管理器层面是有很大的优化空间的,而高并发的处理能力取决于语言特性,说白了就是事件和异步的支持。

这两点想必是让 Node.js 天生骄傲的事情,但在 PHP 里没有得到原生支持,只能通过模拟步进操作的方式来支持类似 Node.js 的事件机制,所以 ReactPHP 其实也并没有想象中那么完美。

结束语
大部分时候,当我们比较语言优劣,容易局限在语言本身,而忽视了配套的一些关键因素。

就拿 PHP 来说,这两年听到了太多关于即时编译器(JIT)、opcode 缓存、抽象语法树(AST)、HHVM 等等之类的话题。当这些优化逐步完备,语言层面的问题,早已不再是 Web 性能的短板了。如果实在不行,我们还可以把复杂任务交给 C 和 C++,以 Node.js addon 或者 PHP 扩展的形式,轻轻松松就搞定了。

都说 PHP 是“世界上最好的语言”,既然如此,也是时候学习下 Node.js 事件驱动和异步回调,考虑考虑如何对 PHP-FPM 进行大刀阔斧的革新。毕竟不管是 Node.js 还是 PHP,我们所擅长的地方,终将还是 Web,高性能的 Web。

沐鸣怎么当代理?届 KISSY 杯羽毛球双打公开赛赛况

淘宝前端团队(FED)之首届 KISSY 杯羽毛球双打公开赛落下帷幕,樱空&左御 组合不负众望,成功斩获冠军奖杯,恭喜二位选手,也恭喜阿大教练组,感谢其他选手与观众的参与,感谢组办方和赞助商!沐鸣怎么当代理?

本届羽毛球比赛看点:
赞助商和组办方竟被痛下杀手,首轮即获败出局,伐开心!

唯一混双组合 站稳&晴苑 因为没有“站的很稳”,错失晋级良机,说好的要照顾妹子的呢,说好的要站稳的呢?

足球派&篮球派之黑马组没有一黑到底,最终被夺冠热门羽毛球派选手 樱空&左御 斩于马下,踢馆失败…

大热必死魔咒?羽毛球派另一夺冠热门 梧忌&驳是 遗憾提前出局,没能与冠军队胜利大会师!

OK,现在回顾选手对阵表和赛程:

参加本届羽毛球公开赛的 8 组选手由各个团队推选,均是各方好手,为荣誉而战!

8 组选手按照抽签结果被分为两小组直接进入淘汰赛,展开厮杀,友谊第一,比赛第二,come on!

we are the champions
牛逼闪闪的冠军组合长这样(奖杯很好吃吗,我也要尝尝)沐鸣娱乐怎么样?

一起全方位 360 度无死角回顾下冠军是如何诞生的

第一回合:樱空&左御 VS 叶斋&林谦
小插曲,左御有事,晚到了,是阿克同学代替取得这一回合的胜利!!!

阿克:请叫我指挥家…

林谦:哎呀妈,我的菊花…

第二回合:樱空&左御 VS 站稳&晴苑

左御:看我凌波微步

晴苑:看我凌波微步…

阿大深情地望着妹子…

第三回合:樱空&左御 VS 渔隐&牧云

观众:冠军会是哪一组呢?

牧云:其实我是篮球队的…

渔隐:其实我是足球队的…

我们是足球队与篮球队的羽毛球对决…

不收钱的羽毛球裁判不是好教练
你以为我是裁判:

其实我是教练:

其实我是最终boss:

冠军什么在我面前都是渣渣;
我是七念;
我为自己代言!!!

奉上伟大的赞助商表情包:

释然:人家十二岁,你说呢?

再来欣赏老板孤独的背影:

展炎:说好的带我玩的…你们这群骗子…

樱空:靠,你们又不是冠军,高兴个啥…

站稳:奖杯我好想要…

您老婆知道您可以跳这么高么…

驳是:都让开,我要放大招了!!!!

剑平:都闪开,主办方与赞助商上场了…

左御:猴儿们,跟大王走~~~

最后,谢谢各位选手,谢谢这么远来看的同学们。

我们是一个快乐的团队!!!

Node.js 探秘:沐鸣娱乐求异存同

前言
在 Node.js 探秘:初识单线程的 Node.js 中,我们了解到,Node.js 基于 libuv 实现了 I/O 的异步操作。所以,我们经常写类似下面的代码:沐鸣娱乐

fs.readFile(‘test.txt’, function(err, data) {
if (err) {
//error handle/
}

//do something with data.
});
通过回调函数来获得想要的结果。

在我们实际解决问题的时候,往往需要一组操作是有序的,比如:读取配置文件、编写命令行工具等。如果使用回调的方式,会使用很多的回调嵌套,使代码变得很难看。为了解决这个问题,我们引入 Promise、yield 等概念,但今天我们不讨论这些,我们讨论下最简单的解决办法, 同步执行 以及 Node.js 如何在异步的架构上实现同步的方法。沐鸣测速注册

使用同步方法
翻看下 Node.js 的文档,你会看到类似 **Sync 的方法,这些就是 ** 对应的同步版本。我们先简单看下如何使用这些方法。

一般读取文件内容,我们会像文章开头那样异步的处理。如果使用同步方法,则类似于下面这样:

try {
var data = fs.readFileSync(‘test.txt’);
//do something with data.
} catch(e) {
//error handle.
}
可以看到使用同步方法后,我们需要的数据会在文件操作后直接返回,而不存在 callback 的异步处理。需要注意的是,像上面那样使用同步方法时,异常内容不会在返回值中返回,它会被抛到环境中,需要使用 try…catch 来捕获处理。如果想在返回值中获取异常,可以传入一个 Buffer 实例来储存 raw data,这样返回值就变成了一个数组,第一个元素是字符串形式的结果,第二个元素是 Error 信息。沐鸣登录测速

另外,Node.js 在 v0.12 版本之后,实现了同步进程创建的一系列方法,例如:spawnSync,execSync 等,示例如下:

//异步版本
child_process.exec(‘ls’, function(err, data) {
if (err) {
//error handle
}

//do something with data
});

//同步版本
try {
var data = child_process.execSync(‘ls’);
//do something with data
} catch(e) {
//error handle
}

如何调试 Node.js 源码
在分析具体内容之前,我们先来做些准备工作。debug 是我们在开发时常用的手段,如果我们在看源码的时候,也能边看,边运行,然后在自己想要的地方停下来,或者按照自己的理解稍作修改,再运行,那一定会大大提高效率,下面笔者介绍下自己的方案(以 MacOS 为例):

首先需要有一份 Node.js 的源码,使用 git clone Github 的仓库 或者去 这里 下载压缩包,都可以。
(解压之后)进入源码目录,运行
./configure
make -j
-j [N],–jobs[=N] 参数用来提高编译效率,充分利用多核处理器的性能,并行编译,N 为并行的任务数。但它并不是万能的,如果有编译依赖,最好还是用单核编译。
使用你习惯的 IDE 来进行 debug,笔者使用的是 CLion。Debug 配置如下图所示:

将执行文件指定到 源码目录/out/Release/ 目录下的 node 执行文件,参数为你需要运行的脚本和相应的参数(比如这里我配置了可以手动调用 gc),之后去掉 Before Launch 中的 build。
然后运行 Debug 模式,就可以通过断点和 LLDB 来调试 Node.js 源码了,如图。

如果需要更改源码,在更改后再次运行 make 即可。

进入正题
从 Node.js API 文档中,可以看到,只有 File System 和 Child Process 中有同步的方法,但是两个模块的同步方法实现是不一样的,我们分开来说。

File System
File System 相关的方法实现都在 lib/fs.js 和 src/node_file.cc 中可以找到。每一个对应的文件操作方法,比如 read、write、link 等等,都有对应的封装。下面以 read 为例进行讲解:

在 lib/fs.js 中我们可以看到,fs.read(#L587) 和 fs.readSync(#L633) 两个方法实际是很相似的,只不过在调用 C/C++ 层面的 read 方法时,传入的参数不同。

//异步方法
fs.read = function(fd, buffer, offset, length, position, callback) {
//参数处理

var req = new FSReqWrap();
req.oncomplete = wrapper;

binding.read(fd, buffer, offset, length, position, req);
}

//同步方法
fs.readSync = function(fd, buffer, offset, length, position, callback) {
//参数处理

var r = binding.read(fd, buffer, offset, length, position);

}
可以看到,异步的方法创建了一个 FSReqWrap 对象,同时把回调包裹后赋值在它的 oncomplete 属性上。这个对象就是我们之前说到的请求对象,在 Node.js 中几乎所有异步操作都会传入这样一个对象,下文我们还会看到 ProcessWrap,除此之外还有很多其他类型的请求对象。它们的作用就是在和 libuv 相互调用时传递需要的数据。

在 src/node_file.cc #L1052 的 Read 方法中,会根据传入的第六个参数的类型来判断是同步调用还是异步调用。

static void Read(const FunctionCallbackInfo& args) {
//参数处理

req = args[5];

if (req->IsObject()) {
ASYNC_CALL(read, req, fd, &uvbuf, 1, pos);
} else {
SYNC_CALL(read, 0, fd, &uvbuf, 1, pos)
args.GetReturnValue().Set(SYNC_RESULT); //调用返回值为错误码,没有则为 0
}
}
可以看到,这里面使用了两个宏定义 ASYNC_CALL 和 SYNC_CALL,它们的具体内容在这里:#L281 和 #L295。对于宏定义,可以理解为代码替换,它会在编译时根据传入的参数生成代码到相应的位置。

//ASYNC_CALL
FSReqWrap* req_wrap = FSReqWrap::New(env, req.As

构造函数还沐鸣登录是静态工厂方法?

我相信Joshua Bloch在他的一本非常好的书《有效的Java》中首先提到了这一点:与构造函数相比,沐鸣开户静态工厂方法是实例化对象的首选方法。沐鸣登录我不同意。不仅因为我相信静态方法是纯粹的邪恶,更主要的是因为在这个特殊的情况下,它们假装是好的,让我们认为我们必须爱它们。沐鸣首页

从巨石到云的沐鸣代理原生构成与学徒达的辛克莱舒勒

在我们关于云本地计算的系列文章中,TheServerSide采访了该领域的一些专家,包括云本地计算基金会的一些成员。以下是Cameron McKenzie和da的Sinclair Schuller的采访录音。沐鸣代理

对da的Sinclair Schuller的采访

如何定义云本地计算?

Cameron McKenzie:在服务端探索传统的企业Java开发如何适应这个使用微服务、Docker、container和Kubernetes的云本地计算的新世界的过程中,我们找到了Sinclair Schuller。Sinclair Schuller是da的CEO。他也是Kubernetes的拥护者,也是云原生计算基金会(Cloud Native Computing Foundation)的董事会成员。

因此,我们想从Sinclair了解的第一件事是,如何定义云本地计算?

Sinclair Schuller:问得好。我想我会给你一个基于建筑学的定义。对我来说,云本机应用程序是一个应用程序,该应用程序有一个隐式或显式弹性资源能力锻炼它生活在一定程度的可移植性,便于运行在各种场景中,无论是在云还是云B或c,但在这些情况下,它能够理解和/或运动底层资源以一种弹性的方式可能是我会用最基本的定义。这与可能部署在服务器上的传统非云本地应用程序不同。他们不知道他们下面的资源是可替代的或有弹性的,所以他们永远不能利用这些基础设施的属性。这就是它们固有的不可扩展性,固有的不可弹性等等的原因。沐鸣平台网址

Cameron McKenzie:现在,有很多关于应用服务器如何死亡的讨论,但是每当我看到人们为了管理容器和微服务而创建的这些环境时,他们都将所有这些工具结合在一起,来完成诸如监视、日志记录和编制等工作。最终,所有这些将导致某种仪表盘,让人们看到云原生架构中发生了什么。我的意思是,应用服务器真的死了吗?或者这只是要重新定义什么是应用服务器?

辛克莱·舒勒:我认为你说得很对。我认为,整个应用服务器是死的,不幸的是,这是劣质营销的结果,新供应商试图重新定位老供应商。这很公平,对吧?这就是这些事情的运行方式,但这与老的应用服务器的死机毫无关系。在很多情况下,他们仍然会将应用程序部署到容器中的Tomcat之类的东西上。所以这是会发生的,所以这本身并没有消失,即使你在建立新的微服务。

传统的重量级应用服务器消失了吗?是的。所以我认为这已经失宠了。以WebLogic或其他类似的老产品为例,在新的云本地开发中不再使用它们。但是你是对的,接下来会发生什么,我们可以肯定地看到,有一组相当松散耦合的工具开始围绕着这个新模型,在这方面它看起来很像一个应用服务器。

在这一点上,我不同意你的看法:我不知道是否会出现整合。但可以肯定的是,如果有的话,那么您最终得到的将是一个拥有所有工具的供应商,可以有效地交付一个云本地应用程序服务器。如果没有整合,并且该工具保持相当松散的耦合和碎片化,那么我们的结果会比传统的应用服务器模型稍微好一些。我们有能力以我们所需要的方式生产出最好的零工工具。沐鸣娱乐业务:

云本地计算和UI开发

Cameron McKenzie:许多开发人员和架构师都在问的一个问题是,“在云原生计算的世界里,UI的角色是什么?”“它仅仅是像Angular UI这样的前端JavaScript框架开发的吗?”它是否实际嵌入到部署到容器的应用程序中?在云本地开发方面,UI开发领域到底发生了什么?

Sinclair Schuller:我认为在某种程度上,UI之战已经取得了胜利,对吗?似乎市场已经决定使用JavaScript和像Angular这样的库来为这些后端服务构建有效的前端。我想那边已经没有太多的骚动和争议了,这就是为什么你很少听到它。后端有点滞后,现在有了容器,这是革命性的。所以我认为后端服务和容器所发生的事情与JavaScript非常相似,比如说,10年前它真正开始流行的时候。
在推进云本地计算方面,da的角色

你是怎么做到的?组织如何采用这种云本地架构和这种云本地基础设施和可伸缩性?

Sinclair Schuller:是的,你刚才描述了我们的业务。实际上,我们在市场上注意到的是,如果你把世界上最大的公司都建立在这些单一的应用程序上,他们面临的挑战是“我们如何分解它们,如何将我们的信息推进到现代时代?”如果我们可以,当然,一些应用程序不需要这些,我们如何至少在云环境中以更好的方式运行它们,这样我们就可以获得额外的效率,对吗?”

因此,我们关注的是为云原生平台提供一个平台,并确保它提供错误桥接功能(因为缺乏更好的术语)。Apprenda,我们建造的方式,我们的IP将允许您运行一个庞然大物在这个平台上,我们会乐器变化到应用程序和行为不同,它可以在一个基于云的基础设施环境,给这庞然大物一些云架构元素,如果你愿意。

为什么这很重要?如果你能做到这一点,您可以快速欢迎一群这些应用程序在云这样的本地平台和删除突然要求你必须改变的巨石,它迅速分解为谁知道多少microservices,你的观点,它提供一点松弛,所以这些企业的开发团队可以更深思熟虑的决定。他们可以选择一个整体的一部分,把它分离出来,作为一个真正的纯微服务来利用,并且仍然在同一个平台上运行并且与现在剩下的部分一起工作。

通过这样做,我们实际上鼓励企业加速采用云本地架构,因为这并不是一个如此突然的决定,也不是架构本身的如此突然的变化。所以对我们来说,我们对这一步充满热情。第二,我如何同时管理大量的数据?在我看来,像da这样的优秀云抽象,一个基于Kubernetes的优秀平台,其目标是让管理10个、1000个或N个微服务变得像运行1个或2个一样简单。

如果你能做到这一点,如果你能把运行1或2变成运行大量微服务的一种方式,你就能把这个成本从等式中去掉让企业更容易消化整个问题。所以我们非常强调这两件事。我们如何提供这种衔接能力这样你就不必经历这样的突然转变你可以在一个更适合你的时间轴上这样做这符合你的需要同时也能处理规模问题?

然而,最终解决规模问题的唯一方法是,云平台必须真正提取底层基础设施资源,并充当微服务层和这些基础设施资源之间的中介。如果它能做到这一点,那么扩展实际上就变得有点微不足道了,因为这是正确架构平台的结果。

卡梅隆·麦肯齐:你说的是抽象。你能谈谈抽象这一层的技术方面吗?

Sinclair Schuller:是的,完全正确。有几件事。一个是,如果要抽象资源,通常需要找到某个层,该层允许您从堆栈中投射新标准或新类型的资源配置文件。这是什么意思呢?让我们以容器为例。

通常,我的基础设施的结构是刚性的,就像这个VM或这台机器有这么大的内存。它有一个OS实例。这个特定的网络布局,现在如果我想实际上抽象,我需要提出一个模型,可以坐在上面,给你的应用程序与某种有意义的能力,分配的方式不再是特别相关的基础设施。容器会处理这些,现在我们都明白了。

我们来看子系统。假设我正在构建一个应用程序我们将从一个非常简单的应用程序开始。比如日志,对吧?我的应用程序将数据记录到磁盘,通常将一些内容转储到某个文件中。如果我有一个已经在这么做的应用程序,我可能会将一堆日志信息写入到一个文件中。如果我在50个基础设施实例中有50个应用程序副本,那么在基础设施中就会有50个日志文件,谁知道它们在哪里。作为一个开发者,如果我想调试我的应用,我必须找到所有这些。