2017年3月31日星期五

开源美图 2017 04 01


Linuxeden 开源社区 --

The post 开源美图 2017 04 01 appeared first on Linuxeden开源社区.

http://ift.tt/2ok2TjB

构建 Facebook Live 直播流中的挑战


Linuxeden 开源社区 --

作者 Jan Stenberg ,译者 Rays

Facebook Live 直播项目启动于两年前的一次 黑客马拉松(hackathon),八个月后就匆匆上线。在近期的 QCon 伦敦大会 上,Facebook 视频架构主管 Sachin Kulkarni 做了 演讲 并指出,如何处理同一直播流中数量变化无法预测的观看者是 Facebook Live 的一个挑战。在报告中,Kulkarni 还介绍了构建 Facebook Live 直播流时所面对的架构上和设计上的挑战。

从较高层次上看 Facebook Live 架构,一旦一个观看者连接到最近的“ 接入点 ”(PoP,Point of Presence),就会启动一个 Live 直播流。PoP 连接会被依次提交给数据中心,在数据中心中完成直播流的编码。之后,数据中心会将直播流发送给多个不同的 PoP 和回放客户端。据 Kulkarni 介绍,PoP 负责视频的缓存、终止观看直播的客户连接,并通过 Facebook 自建网络将直播流传递给数据中心。自建网络传输更为可靠,并降低了往返时间。

针对在扩展性上的挑战,Kulkarni 指出,并发直播流的数量和全部直播流的总计观看者数量都是可预测的,因此并不存在多少挑战性问题。真正的问题在于,单一直播流上的观看者数量不定,从寥寥数人到门庭若市。因为观看者是不可预测的,也就无法对其做出规划。为解决该问题,Facebook 采用了缓存和直播流分配这两种方法。

Kulkarni 指出了相比于常规视频而言,Live 直播流中存在的其它一些挑战:

  • 因为直播内容是实时创建的,所以不可能预先建立缓存,任何形式的预缓存都是不可行的。
  • 对直播事件做预先规划并扩展资源,这一做法是存在问题的。
  • 难以预测由全球热点事件所导致的并发流和观看者的激增问题。

网络问题是直播流可靠性的主要挑战。为解决这些问题,Kulkarni 提出了三个建议:

  • 通过使用自适应码率进行调整,降低视频质量以适应较低的带宽,虽然这项技术常用于回放端,但他们也正在将其用于采集端了。
  • 使用客户端的临时缓存处理暂时性连接丢失。
  • 最坏的情况是带宽不够,这时需要将广播和回放转换成只提供音频。Facebook Live 认为,能听到要比能看到更重要。

Kulkarni 还给出了一些从项目中得到的经验教训:

  • 不积跬步,无以至千里。任何大型服务都是从细微之处开始的。动手去编写代码远胜于无休止地纸上谈兵。
  • 可靠性和可扩展性是设计中必须要考虑的问题。无论运行故障是否有规划,都应做出相应的设计。
  • 为交付大型项目,需要做出妥协。
  • 考虑未来的特性,应保持架构的灵活性,使得团队的演进可以超越架构的变更。

查看英文原文: Challenges Building Facebook Live Streams

转自 http://ift.tt/2nG8e1E

 

The post 构建 Facebook Live 直播流中的挑战 appeared first on Linuxeden开源社区.

http://ift.tt/2olpIDO

Zendesk 的 TensorFlow 产品部署经验


Linuxeden 开源社区 --

作者 Wai Chee Yau ,译者 尚剑

我们如何开始使用 TensorFlow

在 Zendesk,我们开发了一系列机器学习产品,比如最新的 自动应答(Automatic Answers)。它使用机器学习来解释用户提出的问题,并用相应的知识库文章来回应。当用户有问题、投诉或者查询时,他们可以在线提交请求。收到他们的请求后,Automatic Answers 将分析请求,并且通过邮件建议客户阅读可能最有帮助的相关文章。

Automatic Answers 使用一类目前最先进的机器学习算法来识别相关文章,也就是深度学习。 我们使用 Google 的开源深度学习库 TensorFlow 来构建这些模型,利用图形处理单元(GPU)来加速这个过程。Automatic Answers 是我们在 Zendesk 使用 Tensorflow 完成的第一个数据产品。在我们的数据科学家付出无数汗水和心血之后,我们才有了在 Automatic Answers 上效果非常好的 Tensorflow 模型。

但是构建模型只是问题的一部分,我们的下一个挑战是要找到一种方法,使得模型可以在生产环境下服务。模型服务系统将处理大量的业务,所以需要确保为这些模型提供的软件和硬件基础架构是可扩展的、可靠的和容错的,这对我们来说是非常重要的。接下来介绍一下我们在生产环境中配置 TensorFlow 模型的一些经验。

顺便说一下我们的团队——Zendesk 的机器学习数据团队。我们团队包括一群数据科学家、数据工程师、一位产品经理、UX /产品设计师以及一名测试工程师。

TensorFlow 模型服务

经过数据科学家和数据工程师之间一系列的讨论,我们明确了一些核心需求:

  • 预测时的低延迟
  • 横向可扩展
  • 适合我们的微服务架构
  • 可以使用 A/B 测试 不同版本的模型
  • 可以与更新版本的 TensorFlow 兼容
  • 支持其他 TensorFlow 模型,以支持未来的数据产品

经过网上的调研之后,Google 的 TensorFlow Serving 成为我们首选的模型服务。TensorFlow Serving 用 C++编写,支持机器学习模型服务。开箱即用的 TensorFlow Serving 安装支持:

  • TensorFlow 模型的服务
  • 从本地文件系统扫描和加载 TensorFlow 模型

TensorFlow Serving 将每个模型视为可服务对象。它定期扫描本地文件系统,根据文件系统的状态和模型版本控制策略来加载和卸载模型。这使得可以在 TensorFlow Serving 继续运行的情况下,通过将导出的模型复制到指定的文件路径,而轻松地热部署经过训练的模型。

(点击放大图像)

根据这篇 Google 博客 中报告的基准测试结果,他们每秒记录大约 100000 个查询,其中不包括 TensorFlow 预测处理时间和网络请求时间。

有关 TensorFlow Serving 架构的更多信息,请参阅 TensorFlow Serving 文档

通信协议(gRPC)

TensorFlow Serving 提供了用于从模型调用预测的 gRPC 接口。gRPC 是一个开源的高性能远程过程调用(remote procedure call,RPC)框架,它在 HTTP/2 上运行。与 HTTP/1.1 相比,HTTP/2 包含一些有趣的增强,比如它对请求复用、双向流和通过二进制传输的支持,而不是文本。

默认情况下,gRPC 使用 Protocol Buffers (Protobuf) 作为其信息交换格式。Protocol Buffers 是 Google 的开源项目,用于在高效的二进制格式下序列化结构化数据。它是强类型,这使它不容易出错。数据结构在.proto 文件中指定,然后可以以各种语言(包括 Python,Java 和 C ++)将其编译为 gRPC 请求类。 这是我第一次使用 gRPC,我很想知道它与其他 API 架构(如 REST)相比谁性能更好。

模型训练和服务架构

我们决定将深度学习模型的训练和服务分为两个管道。下图是我们的模型训练和服务架构的概述:

(点击放大图像)

模型训练管道

模型训练步骤:

  • 我们的训练特征是从 Hadoop 中提供的数据生成的。
  • 生成的训练特征保存在 AWS S3 中。
  • 然后使用 AWS 中的 GPU 实例和 S3 中的批量训练样本训练 TensorFlow 模型。

一旦模型被构建并验证通过,它将被发布到 S3 中的模型存储库。

模型服务管道

验证的模型在生产中通过将模型从模型库传送到 TensorFlow Serving 实例来提供。

基础结构

我们在 AWS EC2 实例上运行 TensorFlow Serving。Consul 在实例之前设置,用于服务发现和分发流量。客户端连接从 DNS 查找返回的第一个可用 IP。或者弹性负载平衡可用于更高级的负载平衡。由于 TensorFlow 模型的预测本质上是无状态操作,所以我们可以通过旋转加速更多的 EC2 实例来实现横向可扩展性。

另一个选择是使用 Google Cloud 平台提供的 Cloud ML,它提供 TensorFlow Serving 作为完全托管服务。 但是,当我们在大概 2016 年 9 月推出 TensorFlow Serving 时,Cloud ML 服务处于 alpha 阶段,缺少生产使用所需的功能。因此,我们选择在我们自己的 AWS EC2 实例中托管,以实现更精细的粒度控制和可预测的资源容量。

模型服务的实现

下面是我们实现 TensorFlow Serving 部署和运行所采取的步骤:

1. 从源编译 TensorFlow Serving

首先,我们需要编译源代码来产生可执行的二进制文件。然后就可以从命令行执行二进制文件来启动服务系统。

假设你已经配置好了 Docker,那么一个好的开端就是使用 提供的 Dockerfile 来编译二进制文件。请按照以下步骤:

  • 运行该 gist 中的代码以构建适合编译 TensorFlow Serving 的 docker 容器。
  • 在正在运行的 docker 容器中运行该 gist 中的代码以构建可执行二进制文件。
  • 一旦编译完成,可执行二进制文件将在你的 docker 镜像的以下路径中:/work/serving/bazel-bin/tensorflow_serving/model_servers/tensorflow_model_server

2. 运行模型服务系统

上一步生成的可执行二进制文件(tensorflow_model_server)可以部署到您的生产实例中。 如果您使用 docker 编排框架(如 KubernetesElastic Container Service),您还可以在 docker 容器中运行 TensorFlow Serving。

现在假设 TensorFlow 模型存储在目录/work/awesome_model_directory 下的生产主机上。你可以在端口 8999 上使用以下命令来运行 TensorFlow Serving 和你的 TensorFlow 模型:

<path_to_the_binary>/tensorflow_model_server — port=8999 — model_base_path=/work/awesome_model_directory

默认情况下,TensorFlow Serving 会每秒扫描模型基本路径,并且可以自定义。 此处 列出了可作为命令行参数的可选配置。

3. 从服务定义(Service Definitions)生成 Python gRPC 存根

下一步是创建可以在模型服务器上进行预测的 gRPC 客户端。这可以通过编译.proto 文件中的服务定义,从而生成服务器和客户端存根来实现。.proto 文件在 TensorFlow Serving 源码中的 tensorflow_serving_apis 文件夹中。在 docker 容器中运行以下 脚本 来编译.proto 文件。运行提交版本号为 46915c6 的脚本的示例:

./compile_ts_serving_proto.sh 46915c6

运行该脚本后应该在 tensorflow_serving_apis 目录下生成以下定义文件:

  • model_pb2.py
  • predict_pb2.py
  • prediction_service_pb2.py

你还可以使用 grpc_tools Python 工具包来编译.proto 文件。

4. 从远程主机调用服务

可以使用编译后的定义来创建一个 python 客户端,用来调用服务器上的 gRPC 调用。比如 这个例子 用一个同步调用 TensorFlow Serving 的 Python 客户端。

如果您的用例支持异步调用预测,TensorFlow Serving 还支持批处理预测以达到性能优化的目的。要启用此功能,你应该运行 tensorflow_model_server 同时开启 flag?—enable_batching。 是一个异步客户端的例子。

从其他存储加载模型

如果你的模型没有存储在本地系统中应该怎么办?你可能希望 TensorFlow Serving 可以直接从外部存储系统(比如 AWS S3 和 Google Storage)中直接读取。

如果是这种情况,你将需要通过 Custom Source 来拓展 TensorFlow Serving 以使其可以读取这些源。TensorFlow Serving 仅支持从文件系统加载模型。

一些经验

我们在产品中已经使用 TensorFlow Serving 大概半年的时间,我们的使用体验是相当平稳。它具有良好的预测时间延迟。以下是我们的 TensorFlow Serving 实例一周内的预测时间(以秒为单位)的第 95 百分位数的图(约 20 毫秒):

(点击放大图像)

然而,在生产中使用 TensorFlow Serving 的过程中,我们也有一些经验教训可以跟大家分享。

1. 模型版本化

到目前为止,我们已经在产品中使用了几个不同版本的 TensorFlow 模型,每一个版本都有不同的特性,比如网络结构、训练数据等。正确处理模型的不同版本已经是一个重要的任务。这是因为传递到 TensorFlow Serving 的输入请求通常涉及到多个预处理步骤。这些预处理步骤在不同 TensorFlow 模型版本下是不同的。预处理步骤和模型版本的不匹配可能导致错误的预测。

1a. 明确说明你想要的版本

我们发现了一个简单但有用的防止错误预测的方法,也就是使用在 model.proto 定义中指定的版本属性,它是可选的(可以编译为 model_pb2.py)。这样可以始终保证你的请求有效负载与预期的版本号匹配。

当你请求某个版本(比如从客户端请求版本 5),如果 TensorFlow Serving 服务器不支持该特定版本,它将返回一个错误消息,提示找不到模型。

1b. 服务多个模型版本

TensorFlow Serving 默认的是加载和提供模型的最新版本。

当我们在 2016 年 9 月首次应用 TensorFlow Serving 时,它不支持同时提供多个模型。这意味着在指定时间内它只有一个版本的模型。这对于我们的用例是不够的,因为我们希望服务多个版本的模型以支持不同神经网络架构的 A / B 测试。

其中一个选择是在不同的主机或端口上运行多个 TensorFlow Serving 进程,以使每个进程提供不同的模型版本。这样的话就需要:

  • 用户应用程序(gRPC 客户端)包含切换逻辑,并且需要知道对于给定的版本需要调用哪个 TensorFlow Serving 实例。这增加了客户端的复杂度,所以不是首选。
  • 一个可以将版本号映射到 TensorFlow Serving 不同实例的注册表。

更理想的解决方案是 TensorFlow Serving 可以支持多个版本的模型。

所以我决定使用一个“lab day”的时间来扩展 TensorFlow Serving,使其可以服务多个版本的时间。在 Zendesk,“lab day”就是我们可以每两周有一天的时间来研究我们感兴趣的东西,让它成为能够提高我们日常生产力的工具,或者一种我们希望学习的新技术。我已经有 8 年多没有使用 C++代码了。但是,我对 TensorFlow Serving 代码库的可读性和整洁性印象深刻,这使其易于扩展。支持多个版本的 增强功能 已经提交,并且已经合并到主代码库中。TensorFlow Serving 维护人员对补丁和功能增强的反馈非常迅速。从最新的主分支,你可以启动 TensorFlow Serving,用 model_version_policy 中附加的 flag 来服务多个模型版本:

/work/serving/bazel-bin/tensorflow_serving/model_servers/tensorflow_model_server 
— port=8999 — model_base_path=/work/awesome_model_directory — model_version_policy=ALL_VERSIONS

一个值得注意的要点是,服务多个模型版本,需要权衡的是更高的内存占用。所以上述的 flag 运行时,记住删除模型基本路径中的过时模型版本。

2. 活用压缩

当你部署一个新的模型版本的时候,建议在复制到 model_base_path 之前,首先将导出的 TensorFlow 模型文件压缩成单个的压缩文件。Tensorflow Serving 教程中包含了导出训练好的 Tensorflow 模型的步骤。导出的检查点 TensorFlow 模型目录通常具有以下文件夹结构:

一个包含版本号(比如 0000001)和以下文件的父目录:

  • saved_model.pb:序列化模型,包括模型的图形定义,以及模型的元数据(比如签名)。
  • variables:保存图形的序列化变量的文件。

压缩导出的模型:

tar -cvzf modelv1.tar.gz 0000001

为什么需要压缩?

  1. 压缩后转移和复制更快
  2. 如果你将导出的模型文件夹直接复制到 model_base_path 中,复制过程可能需要一段时间,这可能导致导出的模型文件已复制,但相应的元文件尚未复制。如果 TensorFlow Serving 开始加载你的模型,并且无法检测到源文件,那么服务器将无法加载模型,并且会停止尝试再次加载该特定版本。

3. 模型大小很重要

我们使用的 TensorFlow 模型相当大,在 300Mb 到 1.2Gb 之间。我们注意到,在模型大小超过 64Mb 时,尝试提供模型时将出现错误。这是由于 protobuf 消息大小的硬编码 64Mb 限制,如这个 TensorFlow Serving 在 Github 上的问题所述。

最后,我们采用 Github 问题中描述的补丁来更改硬编码的常量值。(这对我们来说还是一个问题。如果你可以找到在不改变硬编码的情况下,允许服务大于 64Mb 的模型的替代方案,请联系我们。)

4. 避免将源移动到你自己的分支下

从实现时开始,我们一直从主分支构建 TensorFlow Serving 源,最新的版本分支(v0.4)在功能和错误修复方面落后于主分支。因此,如果你只通过检查主分支来创建源,一旦新的更改被合并到主分支,你的源也可能改变。为了确保人工制品的可重复构建,我们发现检查特定的提交修订很重要:

  • TensorFlow Serving
  • TensorFlow(TensorFlow Serving 里的 Git 子模块)

期待未来加入的一些功能增强清单

这里是一些我们比较感兴趣的希望以后 TensorFlow Serving 会提供的功能:

  • 健康检查服务方法
  • 一个 TensorFlow Serving 实例可以支持多种模型类型
  • 直接可用的分布式存储(如 AWS S3 和 Google 存储)中的模型加载
  • 直接支持大于 64Mb 的模型
  • 不依赖于 TensorFlow 的 Python 客户端示例

阅读英文原文:How Zendesk Serves TensorFlow Models in Production

转自 http://ift.tt/2nUaipa

The post Zendesk 的 TensorFlow 产品部署经验 appeared first on Linuxeden开源社区.

http://ift.tt/2oJ02wZ

用 Async 函数简化异步代码


Linuxeden 开源社区 --

Promise 在 JavaScript 上发布之初就在互联网上流行了起来 — 它们帮开发人员摆脱了 回调地狱 ,解决了在很多地方困扰 JavaScript 开发者的异步问题。但 Promises 也远非完美。它们一直请求回调,在一些复杂的问题上仍会有些杂乱和一些难以置信的冗余。 随着 ES6 的到来(现在被称作 ES2015),除了引入 Promise 的规范,不需要请求那些数不尽的库之外,我们还有了 生成器 。生成器可在函数内部停止执行,这意味着可 把它们封装在一个多用途的函数中 ,我们可在代码移动到下一行之前等待异步操作完成。突然你的异步代码可能就开始看起来同步了。

这只是第一步。 异步函数 因今年加入 ES2017,已进行标准化,本地支持也进一步优化。异步函数的理念是使用生成器进行异步编程,并给出他们自己的语义和语法。因此,你无须使用库来获取封装的实用函数,因为这些都会在后台处理。 运行文章中的 async/await 实例,你需要一个能兼容的浏览器。

运行兼容

在客户端,Chrome、Firefox 和 Opera 能很好地支持异步函数。 (点击图片进行页面跳转) 从 7.6 版本开始,Node.js 默认启用 async/await。

异步函数和生成器对比

这有个使用生成器进行异步编程的实例,用的是 Q 库:

var doAsyncOp = Q.async(function* () {
  var val = yield asynchronousOperation();
  console.log(val);
  return val;
});

Q.async 是个封装函数,处理场景后的事情。其中 * 表示作为一个生成器函数的功能,yield 表示停止函数,并用封装函数代替。Q.async 将会返回一个函数,你可对它赋值,就像赋值 doAsyncOp 一样,随后再调用。 ES7 中的新语法更简洁,操作示例如下:

async function doAsyncOp () {
  var val = await asynchronousOperation();     
  console.log(val);
  return val;
};

差异不大,我们删除了一个封装的函数和 * 符号,转而用 async 关键字代替。yield 关键字也被 await 取代。这两个例子事实上做的事是相同的:在 asynchronousOperation 完成之后,赋值给 val,然后进行输出并返回结果。

将 Promises 转换成异步函数

如果我们使用 Vanilla Promises 的话前面的示例将会是什么样?

function doAsyncOp () {
  return asynchronousOperation().then(function(val) {
    console.log(val);
    return val;
  });
};

这里有相同的代码行数,但这是因为 then 和给它传递的回调函数增加了很多的额外代码。另一个让人厌烦的是两个 return 关键字。这一直有些事困扰着我,因为它很难弄清楚使用 promises 的函数确切的返回是什么。 就像你看到的,这个函数返回一个 promises,将会赋值给 val,猜一下生成器和异步函数示例做了什么!无论你在这个函数返回了什么,你其实是暗地里返回一个 promise 解析到那个值。如果你根本就没有返回任何值,你暗地里返回的 promise 解析为 undefined。

链式操作

Promise 之所以能受到众人追捧,其中一个方面是因为它能以链式调用的方式把多个异步操作连接起来,避免了嵌入形式的回调。不过 async 函数在这个方面甚至比 Promise 做得还好。 下面演示了如何使用 Promise 来进行链式操作 (我们只是简单的多次运行 asynchronousOperation 来进行演示)。

function doAsyncOp() {
  return asynchronousOperation()
    .then(function(val) {
      return asynchronousOperation(val);
    })
    .then(function(val) {
      return asynchronousOperation(val);
    })
    .then(function(val) {
      return asynchronousOperation(val);
    });
}

使用 async 函数,只需要像编写同步代码那样调用 asynchronousOperation:

async function doAsyncOp () {
  var val = await asynchronousOperation();
  val = await asynchronousOperation(val);
  val = await asynchronousOperation(val);
  return await asynchronousOperation(val);
};

甚至最后的 return 语句中都不需要使用 await,因为用或不用,它都返回了包含了可处理终值的 Promise。

并发操作

Promise 还有另一个伟大的特性,它们可以同时进行多个异步操作,等他们全部完成之后再继续进行其它事件。ES2015 规范中提供了 Promise.all(),就是用来干这个事情的。 这里有一个示例:

function doAsyncOp() {
  return Promise.all([
    asynchronousOperation(),
    asynchronousOperation()
  ]).then(function(vals) {
    vals.forEach(console.log);
    return vals;
  });
}

Promise.all() 也可以当作 async 函数使用:

async function doAsyncOp() {
  var vals = await Promise.all([
    asynchronousOperation(),
    asynchronousOperation()
  ]);
  vals.forEach(console.log.bind(console));
  return vals;
}

这里就算使用了 Promise.all,代码仍然很清楚。

处理拒绝

Promises 可以被接受 (resovled) 也可以被拒绝 (rejected)。被拒绝的 Promise 可以通过一个函数来处理,这个处理函数要传递给 then,作为其第二个参数,或者传递给 catch 方法。现在我们没有使用 Promise API 中的方法,应该怎么处理拒绝?可以通过 try 和 catch 来处理。使用 async 函数的时候,拒绝被当作错误来传递,这样它们就可以通过 JavaScript 本身支持的错误处理代码来处理。

function doAsyncOp() {
  return asynchronousOperation()
    .then(function(val) {
      return asynchronousOperation(val);
    })
    .then(function(val) {
      return asynchronousOperation(val);
    })
    .catch(function(err) {
      console.error(err);
    });
}

这与我们链式处理的示例非常相似,只是把它的最后一环改成了调用 catch。如果用 async 函数来写,会像下面这样。

async function doAsyncOp () {
  try {
    var val = await asynchronousOperation();
    val = await asynchronousOperation(val);
    return await asynchronousOperation(val);
  } catch (err) {
    console.err(err);
  }
};

它不像其它往 async 函数的转换那样简洁,但是 确实 跟写同步代码一样。如果你在这里不捕捉错误,它会延着调用链一直向上抛出,直到在某处被捕捉处理。如果它一直未被捕捉,它最终会中止程序并抛出一个运行时错误。Promise 以同样的方式运作,只是拒绝不 当作错误来处理;它们可能只是一个说明错误情况的字符串。如果你不捕捉被创建为错误的拒绝,你会看到一个运行时错误,不过如果你只是使用一个字符串,会失败却不会有输出。

中断 Promise

拒绝原生的 Promise,只需要使用 Promise 构建函数中的 reject 就好,当然也可以直接抛出错误——在 Promise 的构造函数中,在 then 或 catch 的回调中抛出都可以。如果是在其它地方抛出错误,Promise 就管不了了。 这里有一些拒绝 Promise 的示例:

function doAsyncOp() {
  return new Promise(function(resolve, reject) {
    if (somethingIsBad) {
      reject("something is bad");
    }
    resolve("nothing is bad");
  });
}

/*-- or --*/

function doAsyncOp() {
  return new Promise(function(resolve, reject) {
    if (somethingIsBad) {
      reject(new Error("something is bad"));
    }
    resolve("nothing is bad");
  });
}

/*-- or --*/

function doAsyncOp() {
  return new Promise(function(resolve, reject) {
    if (somethingIsBad) {
      throw new Error("something is bad");
    }
    resolve("nothing is bad");
  });
}

一般来说,最好使用 new Error,因为它会包含错误相关的其它信息,比如抛出位置的行号,以及可能会有用的调用栈。 这里有一些抛出 Promise 不能捕捉的错误的示例:

function doAsyncOp() {
  // the next line will kill execution
  throw new Error("something is bad");
  return new Promise(function(resolve, reject) {
    if (somethingIsBad) {
      throw new Error("something is bad");
    }
    resolve("nothing is bad");
  });
}

// assume `doAsyncOp` does not have the killing error
function x() {
  var val = doAsyncOp().then(function() {
    // this one will work just fine
    throw new Error("I just think an error should be here");
  });
  // this one will kill execution
  throw new Error("The more errors, the merrier");
  return val;
}

在 async 函数的 Promise 中抛出错误就不会产生有关范围的问题——你可以在 async 函数中随时随地抛出错误,它总会被 Promise 抓住:

async function doAsyncOp() {
  // the next line is fine
  throw new Error("something is bad");
  if (somethingIsBad) {
    // this one is good too
    throw new Error("something is bad");
  }
  return "nothing is bad";
} 

// assume `doAsyncOp` does not have the killing error
async function x() {
  var val = await doAsyncOp();
  // this one will work just fine
  throw new Error("I just think an error should be here");
  return val;
}

当然,我们永远不会运行到 doAsyncOp 中的第二个错误,也不会运行到 return 语句,因为在那之前抛出的错误已经中止了函数运行。

问题

如果你刚开始使用 async 函数,需要小心嵌套函数的问题。比如,如果你的 async 函数中有另一个函数 (通常是回调),你可能认为可以在其中使用 await ,但实际不能。你只能直接在 async 函数中使用 await 。 比如,这段代码无法运行:

async function getAllFiles(fileNames) {
  return Promise.all(
    fileNames.map(function(fileName) {
      var file = await getFileAsync(fileName);
      return parse(file);
    })
  );
}

第 4 行的 await 无效,因为它是在一个普通函数中使用的。不过可以通过为回调函数添加 async 关键字来解决这个问题。

async function getAllFiles(fileNames) {
  return Promise.all(
    fileNames.map(async function(fileName) {
      var file = await getFileAsync(fileName);
      return parse(file);
    })
  );
}

你看到它的时候会觉得理所当然,即便如此,仍然需要小心这种情况。 也许你还想知道等价的使用 Promise 的代码:

function getAllFiles(fileNames) {
  return Promise.all(
    fileNames.map(function(fileName) {
      return getFileAsync(fileName).then(function(file) {
        return parse(file);
      });
    })
  );
}

接下来的问题是关于把 async 函数看作同步函数。需要记住的是,async 函数 内部 的的代码是同步运行的,但是它会立即返回一个 Promise,并继续运行外面的代码,比如:

var a = doAsyncOp(); // one of the working ones from earlier
console.log(a);
a.then(function() {
  console.log("`a` finished");
});
console.log("hello");

/* -- will output -- */
Promise Object
hello
`a` finished

你会看到 async 函数实际使用了内置的 Promise。这让我们思考 async 函数中的同步行为,其它人可以通过普通的 Promise API 调用我们的 async 函数,也可以使用它们自己的 async 函数来调用。

如今,更好的异步代码!

即使你本身不能使用异步代码,你也可以进行编写或使用工具将其编译为 ES5。 异步函数能让代码更易于阅读,更易于维护。 只要我们有 source maps,我们可以随时使用更干净的 ES2017 代码。 有许多可以将异步功能(和其他 ES2015+功能)编译成 ES5 代码的工具。 如果您使用的是 Babel,这只是安装 ES2017 preset 的例子。

参与翻译  (3 人) : 边城 , BigEcho, snake_007
转自 http://ift.tt/2okWmoU

The post 用 Async 函数简化异步代码 appeared first on Linuxeden开源社区.

http://ift.tt/2ojBMFm

微软宣布 12 月 15 日关闭开源软件托管平台 CodePlex


Linuxeden 开源社区 --
CodePlex

据 Venturebeat 报道,微软今天宣布,将关闭开源软件托管平台 CodePlex。微软 2006 年推出这项服务,并决定在今年 12 月 15 日将其关闭。

微软公司副总裁布莱恩·哈里(Brian Harry)在博文中写道,人们将可以下载他们的数据档案,微软正与面向开源及私有软件项目的托管平台 GitHub 合作,给用户提供 “简化的进口体验”,代码和相关内容都将转移到 GitHub 上。简单来说,GitHub 赢了!

哈里写道:“过去多年中,我们已经看到许多惊人的选项起起落落。但是现在,GitHub 正成为开源共享和大多数开源项目托管事实上的中心。”过去几年里,微软越来越向 GitHub 倾斜。它已经于 2016 年将 CNTK 深度学习工具包从 CodePlex 转移到 GitHub 上。今天,微软的 GitHub 组织已经有 16000 多名开源贡献者。2016 年,GitHub 本身也为微软适应 GitHub 做出很多努力。

与此同时,CodePlex 则不断没落。在今年 2 月份,人们在这个平台上托管的开源项目还不到 350 个。

GitHub 是基于 Git 的开源版本控制软件,可以被多人用于追踪变化。人们可以移动代码到 Atlassian 的 Bitbucket 和微软 Visual Studio Team Services 等替代系统上。初创企业 GitLab 也为开源和私有项目提供托管服务。

2016 年,谷歌已经关闭了自己的托管服务 Google Code。哈里写道:“我们很荣幸能与 GitHub 密切合作,共同促进开源项目。”开源软件并非微软文化中的真正核心,但这种情况过去几年有所改变。该公司于 2015 年推出了开源项目 .NET 和 Visual Studio Code 开源文字编辑器,2016 年 8 月份推出 PowerShell。

哈里写道,人们可能无法在 CodePlex 上继续创造新的项目。该网站将在 10 月份变为只读,并在 12 月份关闭。届时,人们依然可以浏览上面的代码。

来源: 网易科技报道  作者:小小

转自 http://ift.tt/2nFLUoW

The post 微软宣布 12 月 15 日关闭开源软件托管平台 CodePlex appeared first on Linuxeden开源社区.

http://ift.tt/2ol3P7q

Ehcache 3.3.1 和 3.3.2 发布,Java 缓存框架


Linuxeden 开源社区 --

EhCache 是一个纯 Java 的进程内缓存框架,具有快速、精干等特点,是 Hibernate 中默认的 CacheProvider。

EhCache 3.3.1 是 Ehcache 3.3 的首个维护版本,更新如下:

下载地址和更多详情查看 发行日志

转自 http://ift.tt/2nFOcUU

The post Ehcache 3.3.1 和 3.3.2 发布,Java 缓存框架 appeared first on Linuxeden开源社区.

http://ift.tt/2oldPxO

Apache Flex BlazeDS 4.7.3 发布,Web 通讯工具


Linuxeden 开源社区 --Apache Flex
Apache Flex

Apache Flex BlazeDS 是基于服务器的 Java 远程和 Web 通讯工具,方便开发者连接后端分布式数据和实时推送数据到 Adobe(r) Flex(r) 和 Adobe AIR(tm) 应用,提供更丰富的网络应用 (RIA) 体验。

Apache Flex BlazeDS 4.7.3 添加了一个新的 blazeds-spring-boot-starter 模块,可以使用 Spring Boot 轻松设置 BlazeDS 服务器。

该版本还提供 Maven 原型,轻松创建使用 BlazeDS 的新的 spring-boot 项目。

同时,还做了很多微调的安全默认设置来制作 BlazeDS ,现在会更为安全。
转自 http://ift.tt/2nU3asy

The post Apache Flex BlazeDS 4.7.3 发布,Web 通讯工具 appeared first on Linuxeden开源社区.

http://ift.tt/2nstIOK

Apache Tomcat 8.5.13 和 9.0.0.M19 (alpha) 发布


Linuxeden 开源社区 --Tomcat
Tomcat

Apache Tomcat 8.5.13 和 Tomcat 9.0.0.M19 (alpha) 发布了。

Apache Tomcat 8.5.x 旨在代替 8.0.x,并包含从 Tomcat 9.0.x 开始的新功能。最低 Java 版本和实现的规范版本保持不变。

两个版本的主要更新内容均为:

  • – Various HTTP/2 improvements
  • – Fixes for sendfile related issues that could cause subsequent requests to experience IllegalStateExceptions
  • – Servlet 4.0 early access updates

下载地址:

http://ift.tt/1fIgDaX

http://ift.tt/1PBRVNF

转自 http://ift.tt/2oIKysH

The post Apache Tomcat 8.5.13 和 9.0.0.M19 (alpha) 发布 appeared first on Linuxeden开源社区.

http://ift.tt/2nU2eV5

每日文章精选 2017 03 31


Emacs 是否真的能煮咖啡?


Linuxeden 开源社区 --

 

坊间有传言曰:“emacs 无所不能, 甚至能够用来煮咖啡!”

煮咖啡何解?勾起了我的考究欲望。

上网搜索之, 得出几种初步结论如下:

1. 这只是一种好玩的说法,只是用来形容 emacs 功能无所不包而已。

2.Java 的标志就一杯咖啡, 用来形象的表示写代码

3.emacs 的确具 有煮咖啡的功能 ,有脚本能够控制自动咖啡机运行。

觉得第一种说法比较符合逻辑; 第二种说法感觉有点唐突,毕竟 Emacs 是 Richard Stallman(GNU 创始人) 所写,而 Java 是 Bill Joy(vi 作者) 等人完成的,这两派都差点上升到宗教冲突了, 这种解释有点差强人意; 第三种的如果是真的话就会变得很有趣。

于是就开始了探究,首先追本溯源,找到这段脚本代码的源头。发现已经地址已经失效, 终于在在 debian 的一个软件包里找到了副本。这是 emacs 常用脚本的一个打包。

代码如下:

这个脚本看起来还是煞有其事的,文中提到 “Submit a BREW request to an RFC2324-compliant coffee device”

能够向与 RFC2324 协议兼容的咖啡设备提交 BREW 请求 , 即兼容 Hyper Text Coffee Pot Control Protocol (HTCPCP/1.0) 协议。超文本咖啡壶协议, 光看这名字就够喜庆了,但这份协议写得很规范, 看不出一丝破绽。有细心的朋友 shrek.wang 提醒了我,注意看日期:

1998 年 4 月 1 号,愚人节!这个就让人产生了疑问。

通过查 wiki 发现,原来互联网国际标准机构也是很有才的恶搞高手。

这里列举其中几个好玩的

  • 2001 年,RFC 1149 由一班挪威 Linux 使用者协会的成员实现了。他们传送了 9 个封包到约 5 公里外的地方,每个封包由不同的鸽子携带,并有一个 ICMP 应答要求封包(ping)。他们收到 4 个回应,封包流失率是 55%,回应时间是 3000 至 6000 秒。[1]
  • RFC 1607 — 来自 21 世纪的观点 Vint Cerf 1994.
  • RFC 3091 — 圆周率数字产生协定 . H. Kennedy 2001 年

更多的可以看这里 wiki。

这里可以大胆地作出推断,这个协议只是 IETF 开的一个善意的 joke,而 coffee.el 的作者 Eric Marsden 也是一个幽默的程序员,于是就做了一个兼容 RFC2324 的脚本, 他也没想过要真正的控制咖啡机,所以这整个事情都源于程序员的冷幽默。

然而远程控制咖啡机还是有可能的,这里有一个开源咖啡机; 这里还有一个允许网络控制的咖啡机(据说还是兼容 RFC2324 的)。

估计 IETF 应该做梦也没想到自己开的一个玩笑竟然还真的有人做出了实物。 国外的牛人们还真是闲得蛋疼阿。可见 geek 们还是极富幽默感的。

朋友们,你的心中已经有答案了么?你们想要一台这样的咖啡机不?

转自 http://ift.tt/2ofcRTu

The post Emacs 是否真的能煮咖啡? appeared first on Linuxeden开源社区.

http://ift.tt/2nDJszm

天猫团队开源跨平台模块化 UI 界面开发框架 Tangram


Linuxeden 开源社区 --
Tangram UI

Tangram,七巧板,是天猫团队刚刚开源的跨平台模块化 UI 界面方案。据悉,之所以命名为 Tangram ,是希望它能像七巧板一样可以通过几块积木就搭出丰富多彩的界面。

什么是 Tangram

Tangram 不仅仅是一个 Native(iOS & Android)的界面开发框架,而是从日常工作中沉淀出的一套界面解决方案,涵盖了 Native SDK、GUI 操作台、后端逻辑容器、组件库机制的一整套方案。

Tangram 从手机天猫 – 首页方案抽象而来,是面向组件的界面方案,是开发团队不断权衡性能、稳定性、开发效率、灵活性和动态性多方面表现的结果。除了手机天猫首页外,还支撑了天猫 App 中的天猫直播、我的天猫、猜你喜欢等多个业务,并且在阿里星球等多个阿里系 App 中有所应用。

  

Tangram 关注的重点

Tangram 关注三个重点:面向业务、多端一致性和高性能。

1、面向业务

Tangram 来源于多次试错和方向的调整,最终站在业务角度出发,权衡多项技术指标的结果。所以面向业务是出发点,是整个 Tangram 体系的最基本原则。

基于这个原则,在端上 Tangram 始终坚持 粗粒度 组件。粗粒度意味着通用性和灵活性的下降,某种程度上还会对动态性造成影响,但在第 2 型业务中通用性、灵活性和动态性的需求是有节制的,在粗粒度上完全可以满足业务需求。而且,粗粒度还具有使用成本低,性能更好等优势。在端上重点精力则投入到提升组件库复用度,布局容器和组件的丰富性,从而推动业务发展。

除了端上的工作,另一部分重点工作在控制台和服务网关的建设上。作为一个面向业务的方案,控制台是业务方和产品的接口,控制台的主要目标是让业务方可以直接控制基于 Tangram 建设的产品——调整页面布局,切换页面数据,等。服务网关的建设目标是最大程度的降低业务创建 Tangram 页面的压力和成本。

2、多端一致性

在多年的业务开发经历中,屡次被多端表现不一致的问题困扰。为了实现业务诉求,不得不通过复杂的网关逻辑来兼容多端逻辑不一致情况,以实现表现一致。因此团队制定了两个 Tangram 端开发原则:

  • 任意新功能的提出都是不区分平台,在功能设计中必须同时考虑多端功能,具体的实现方案和逻辑必须多端统一 Review 以保证多端表现一致。
  • 任意一端的变更都必须在改动前把方案同步给其他端,而且变更必须多端同步发布。

3、高性能

面向业务 的原则之下,已经给高性能打下了一个良好的基础。而在高性能的思考上重点基于页面渲染效率和组件回收复用两方面。

  • 页面渲染——为了提升渲染效率,Tangram 将在视图渲染之前把大量的计算工作在 VM 中完成,并缓存在 VM 组成的树形结构里。
  • 回收和复用——Tangram 在 Android 和 iOS 平台上分别开发了 VLayout 和 LazyScroll 两个基础组件,通过一个双索引可见区域组件发现算法,实现了跨父节点组件的高效回收和复用。

此外,开发团队还表示,目前已经完成了新版 Tangram 2.0 的讨论,开始执行 2.0 版本的重构工作。在 Tangram 2.0 中出于适应业务形态的变化,将对开源的 Tangram 1.0 中基于布局和组件的二维结构进行进一步的抽象,用于支撑更复杂的流式布局。并且对于控制台和服务网关也将进一步升级,大幅提升新业务开发效率。在性能层面,对组件开发模型和渲染模式进行一次较大的升级,在渲染和滚动效率上将得到巨大提升。

转自 http://ift.tt/2nDhAvm

The post 天猫团队开源跨平台模块化 UI 界面开发框架 Tangram appeared first on Linuxeden开源社区.

http://ift.tt/2oiKILh

GitLab 9.0.2 发布,修复重要安全漏洞


Linuxeden 开源社区 --GitLab
GitLab

GitLab 社区版(CE)和企业版(EE)发布 9.0.2 版本了。

该版本包含了两个重要的安全修复程序:主要是针对 GitLab 9.0 最近推出的嵌套群组功能。建议运行 GitLab 9.0 版本的用户尽快升级。

这些安全漏洞不会影响 GitLab 9.0 之前的版本。详细更新内容如下:

更改子群组的路径以断开到该子群组项目中上传的文件的链接

内部审查代码时发现,当包含有项目的子群组被重命名时,GitLab 将不正确地尝试移动同名顶层项目的 uploads 目录。移动 upload 目录时,GitLab 没有正确引用子群组中的项目的完整路径。此漏洞可能会允许用户重命名他们没拥有的项目的 upload 目录,从而有效地打破了所有的 upload 链接。

私有群组的名称通过嵌套群组在 new/update 中的 parent_id 被泄露

有可能通过尝试在创建子群组名字的时候,获取私有群组的名称。此攻击可能需要识别私有群组的数字 ID,但这些数字 ID 是可预测的,而且也容易猜测。

受影响的版本

GitLab CE+EE 9.0.0 – 9.0.1

建议使用上述版本的用户都升级至 9.0.2

更多内容请参阅 发布主页

下载地址

转自 http://ift.tt/2ogWlCa

The post GitLab 9.0.2 发布,修复重要安全漏洞 appeared first on Linuxeden开源社区.

http://ift.tt/2ooXNzv

在 CentOS 7 中在 HHVM 和 Nginx 之上安装 WordPress


Linuxeden 开源社区 --

HHVM(HipHop Virtual Machine)是一个用于执行以 PHP 和 Hack 语言编写的代码的虚拟环境。它是由 Facebook 开发的,提供了当前 PHP 7 的大多数功能。要在你的服务器上运行 HHVM,你需要使用 FastCGI 来将 HHVM 和 Nginx 或 Apache 衔接起来,或者你也可以使用 HHVM 中的内置 Web 服务器 Proxygen。

在这篇教程中,我将展示给你如何在 Nginx Web 服务器的 HHVM 上安装 WordPress。这里我使用 CentOS 7 作为操作系统,所以你需要懂一点 CentOS 操作的基础。

先决条件

  • CentOS 7 – 64 位
  • Root 权限

步骤 1 – 配置 SELinux 并添加 EPEL 仓库

在本教程中,我们将使用 SELinux 的强制模式,所以我们需要在系统上安装一个 SELinux 管理工具。这里我们使用 setoolssetrobleshoot 来管理 SELinux 的各项配置。

CentOS 7 已经默认启用 SELinux,我们可以通过以下命令来确认:

# sestatus
# getenforce

验证 SELinux 运行状态

验证 SELinux 运行状态

如图,你能够看到,SELinux 已经开启了强制模式。

接下来就是使用 yum 来安装 setoolssetroubleshoot 了。

# yum -y install setroubleshoot setools net-tools

安装好这两个后,再安装 EPEL 仓库。

# yum -y install epel-release

步骤 2 – 安装 Nginx

Nginx (发音:engine-x) 是一个高性能、低内存消耗的轻量级 Web 服务器软件。在 CentOS 中可以使用 yum 命令来安装 Nginx 包。确保你以 root 用户登录系统。

使用 yum 命令从 CentOS 仓库中安装 nginx。

# yum -y install nginx

现在可以使用 systemctl 命令来启动 Nginx,同时将其设置为跟随系统启动。

# systemctl start nginx
# systemctl enable nginx

为确保 Nginx 已经正确运行于服务器中,在浏览器上输入服务器的 IP,或者如下使用 curl 命令检查显示结果。

# curl 192.168.1.110

我这里使用浏览器来验证。

Nginx 正确运行

Nginx 正确运行

步骤 3 – 安装并配置 MariaDB

MariaDB 是由原 MySQL 开发者 Monty Widenius 开发的一款开源数据库软件,它由 MySQL 分支而来,与 MySQL 的主要功能保持一致。在这一步中,我们要安装 MariaDB 数据库并为之配置好 root 密码,然后再为所要安装的 WordPress 创建一个新的数据库和用户。

安装 mariadb 和 mariadb-server:

# yum -y install mariadb mariadb-server

启动 MariaDB 并添加为服务,以便随系统启动。

# systemctl start mariadb
# systemctl enable mariadb

现在 MariaDB 已经启动了,还需要为 mariadb/mysql 数据库配置 root 用户密码。输入以下命令来设置 MariaDB root 密码。

# mysql_secure_installation

提示设置 root 用户密码时,输入新密码进行设置:

Set root password? [Y/n] Y
New password:
Re-enter new password:

Remove anonymous users? [Y/n] Y
 ... Success!
Disallow root login remotely? [Y/n] Y
 ... Success!
Remove test database and access to it? [Y/n] Y
Reload privilege tables now? [Y/n] Y
 ... Success!

这样就设置好了 MariaDB 的 root 密码。现在登录到 MariaDB/MySQL shell 并为 WordPress 的安装创建一个新数据库 wordpressdb 和新用户 wpuser,密码设置为 wpuser@。为你的设置选用一个安全的密码。

登录到 MariaDB/MySQL shell:

# mysql -u root -p

接着输入你刚刚设置的 root 用户密码。

创建数据库和用户:

MariaDB [(none)]> create database wordpressdb;
MariaDB [(none)]> create user wpuser@localhost identified by 'wpuser@';
MariaDB [(none)]> grant all privileges on wordpressdb.* to wpuser@localhost identified by 'wpuser@';
MariaDB [(none)]> flush privileges;
MariaDB [(none)]> \q

为 WordPress 的安装创建数据库和用户

为 WordPress 的安装创建数据库和用户

现在安装好了 MariaDB,并为 WordPress 创建好了数据库。

步骤 4 – 安装 HHVM

对于 HHVM,我们需要安装大量的依赖项。作为选择,你可以从 GitHub 下载 HHVM 的源码来编译安装,也可以从网络上获取预编译的包进行安装。在本教程中,我使用的是预编译的安装包。

为 HHVM 安装依赖项:

# yum -y install cpp gcc-c++ cmake git psmisc {binutils,boost,jemalloc,numactl}-devel \
> {ImageMagick,sqlite,tbb,bzip2,openldap,readline,elfutils-libelf,gmp,lz4,pcre}-devel \
> lib{xslt,event,yaml,vpx,png,zip,icu,mcrypt,memcached,cap,dwarf}-devel \
> {unixODBC,expat,mariadb}-devel lib{edit,curl,xml2,xslt}-devel \
> glog-devel oniguruma-devel ocaml gperf enca libjpeg-turbo-devel openssl-devel \
> mariadb mariadb-server libc-client make

然后是使用 rpm 安装从 HHVM 预编译包镜像站点 [1] 下载的 HHVM 预编译包。

# rpm -Uvh http://ift.tt/2okn3H5
# ln -s /usr/local/bin/hhvm /bin/hhvm

安装好 HHVM 之后使用如下命令按了验证:

# hhvm --version

为了能使用 PHP 命令,可以把 hhvm 命令设置为 php。这样在 shell 中输入 php 命令的时候,你会看到和输入 hhvm 命令一样的结果。

# sudo update-alternatives --install /usr/bin/php php /usr/bin/hhvm 60
# php --version

安装 HHVM

安装 HHVM

步骤 5 – 配置 HHVM

这一步中,我们来配置 HHVM 以系统服务来运行。我们不通过端口这种常规的方式来运行它,而是选择使用 unix socket 文件的方式,这样运行的更快速一点。

进入 systemd 配置目录,并创建一个 hhvm.service 文件。

# cd /etc/systemd/system/
# vim hhvm.service

复制粘贴如下配置到文件中去。

[Unit]
Description=HHVM HipHop Virtual Machine (FCGI)
After=network.target nginx.service mariadb.service

[Service]
ExecStart=/usr/local/bin/hhvm --config /etc/hhvm/server.ini --user nginx --mode daemon -vServer.Type=fastcgi -vServer.FileSocket=/var/run/hhvm/hhvm.sock

[Install]
WantedBy=multi-user.target

保存文件退出 vim。

接下来,进入 hhvm 目录并编辑 server.ini 文件。

# cd /etc/hhvm/
# vim server.ini

将第 7 行 hhvm.server.port 替换为 unix socket,如下:

hhvm.server.file_socket = /var/run/hhvm/hhvm.sock

保存文件并退出编辑器。

我们已在 hhvm 服务文件中定义了 hhvm 以 nginx 用户身份运行,所以还需要把 socket 文件目录的属主变更为 nginx。然后我们还必须在 SELinux 中修改 hhvm 目录的权限上下文以便让它可以访问这个 socket 文件。

# chown -R nginx:nginx /var/run/hhvm/
# semanage fcontext -a -t httpd_var_run_t "/var/run/hhvm(/.*)?"
# restorecon -Rv /var/run/hhvm

服务器重启之后,hhvm 将不能运行,因为没有存储 socket 文件的目录,所有还必须在启动的时候自动创建一个。

使用 vim 编辑 rc.local 文件。

# vim /etc/rc.local

将以下配置粘贴到文件末行。

# mkdir -p /var/run/hhvm/
# chown -R nginx:nginx /var/run/hhvm/
# semanage fcontext -a -t httpd_var_run_t "/var/run/hhvm(/.*)?"
# restorecon -Rv /var/run/hhvm

保存文件并退出 vim。然后给文件赋予执行权限。

# chmod +x /etc/rc.local

重新加载 systemd 服务,启动 hhvm 并设置为随系统启动。

# systemctl daemon-reload
# systemctl start hhvm
# systemctl enable hhvm

要确保无误,使用 netstat 命令验证 hhvm 运行于 socket 文件。

# netstat -pl | grep hhvm

Check the HHVM socket file

Check the HHVM socket file

步骤 6 – 配置 HHVM 和 Nginx

在这个步骤中,我们将配置 HHVM 已让它运行在 Nginx Web 服务中,这需要在 Nginx 目录创建一个 hhvm 的配置文件。

进入 /etc/nginx 目录,创建 hhvm.conf 文件。

# cd /etc/nginx/
# vim hhvm.conf

粘贴以下内容到文件中。

location ~ \.(hh|php)$ {
    root /usr/share/nginx/html;
    fastcgi_keep_conn on;
    fastcgi_pass unix:/var/run/hhvm/hhvm.sock;
    fastcgi_index  index.php;
    fastcgi_param  SCRIPT_FILENAME $document_root$fastcgi_script_name;
    include        fastcgi_params;
}

然后,保存并退出。

接下来,编辑 nginx.conf 文件,添加 hhvm 配置文件到 include 行。

# vim nginx.conf

添加配置到第 57 行的 server 指令中。

include /etc/nginx/hhvm.conf;

保存并退出。

然后修改 SELinux 中关于 hhvm 配置文件的权限上下文。

# semanage fcontext -a -t httpd_config_t /etc/nginx/hhvm.conf
# restorecon -v /etc/nginx/hhvm.conf

测试 Nginx 配置并重启服务。

# nginx -t
# systemctl restart nginx

记住确保测试配置没有错误。

步骤 7 – 通过 HHVM 和 Nginx 创建虚拟主机

在这一步中,我们要为 Nginx 和 hhvm 创建一个新的虚拟主机配置文件。这里我使用域名 natsume.co 来作为例子,你可以使用你主机喜欢的域名,并在配置文件中相应位置以及 WordPress 安装过程中进行替换。

进入 nginx 的 conf.d 目录,我们将在该目录存储虚拟主机文件。

# cd /etc/nginx/conf.d/

使用 vim 创建一个名为 natsume.conf 的配置文件。

# vim natsume.conf

粘贴以下内容到虚拟主机配置文件中。

server {
    listen       80;
    server_name  natsume.co;

    # note that these lines are originally from the "location /" block
    root   /var/www/hakase;
    index index.php index.html index.htm;

    location / {
        try_files $uri $uri/ =404;
    }
    error_page 404 /404.html;
    location = /50x.html {
        root /var/www/hakase;
    }

    location ~ \.php$ {
        try_files $uri =404;
        fastcgi_pass unix:/var/run/hhvm/hhvm.sock;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }
}

保存并退出。

在这个虚拟主机配置文件中,我们定义该域名的 Web 根目录为 /var/www/hakase。目前该目录还不存在,所有我们要创建它,并变更属主为 nginx 用户和组。

# mkdir -p /var/www/hakase
# chown -R nginx:nginx /var/www/hakase

接下来,为该文件和目录配置 SELinux 上下文。

# semanage fcontext -a -t httpd_config_t "/etc/nginx/conf.d(/.*)?"
# restorecon -Rv /etc/nginx/conf.d

最后,测试 nginx 配置文件以确保没有错误后,重启 nginx:

# nginx -t
# systemctl restart nginx

步骤 8 – 安装 WordPress

在步骤 5 的时候,我们已经为 WordPress 配置好了虚拟主机,现在只需要下载 WordPress 和使用我们在步骤 3 的时候创建的数据库和用户来编辑数据库配置就好了。

进入 Web 根目录 /var/www/hakase 并使用 Wget 命令下载 WordPress:

# cd&nbsp;/var/www/hakase
# wget http://ift.tt/zUVk1t

解压 latest.tar.gz 并将 wordpress 文件夹中所有的文件和目录移动到当前目录:

# tar -xzvf latest.tar.gz
# mv wordpress/* .

下一步,复制一份 wp-config-sample.php 并更名为 wp-config.php,然后使用 vim 进行编辑:

# cp wp-config-sample.php wp-config.php
# vim wp-config.php

DB_NAME 设置为 wordpressdbDB_USER 设置为 wpuser 以及 DB_PASSWORD 设置为 wpuser@

define('DB_NAME', 'wordpressdb');
define('DB_USER', 'wpuser');
define('DB_PASSWORD', 'wpuser@');
define('DB_HOST', 'localhost');

保存并退出。

WordPress 配置

WordPress 配置

修改关于 WordPress 目录的 SELinux 上下文。

# semanage fcontext -a -t httpd_sys_content_t "/var/www/hakase(/.*)?"
# restorecon -Rv /var/www/hakase

现在打开 Web 浏览器,在地址栏输入你之前为 WordPress 设置的域名,我这里是 natsume.co

选择语言并点击 继续 Continue

安装 WordPress - 语言选择

安装 WordPress – 语言选择

根据自身要求填写站点标题和描述并点击 安装 WordPressInstall WordPress“。

安装 WordPress - 配置管理员账号和站点标题

安装 WordPress – 配置管理员账号和站点标题

耐心等待安装完成。你会见到如下页面,点击 登录 Log In 来登录到管理面板。

安装 WordPress - 成功安装

安装 WordPress – 成功安装

输入你设置的管理员用户账号和密码,在此点击 登录 Log In

登录到 wordpress 管理面板

登录到 wordpress 管理面板

现在你已经登录到 WordPress 的管理面板了。

Wordpress 管理面

WordPress 管理面

WordPress 的主页:

Wordpress 默认主页

WordPress 默认主页

至此,我们已经在 CentOS 7 上通过 Nginx 和 HHVM 成功安装 WordPress。

参考链接


译者简介:

GHLandy[2] —— 划不完粉腮柳眉泣别离。


via: http://ift.tt/2iKHwEp

作者:Muhammad Arul[3] 译者:GHLandy[4] 校对:wxy[5]

The post 在 CentOS 7 中在 HHVM 和 Nginx 之上安装 WordPress appeared first on Linuxeden开源社区.

http://ift.tt/2npNHOb