rsong
2018-09-17
6339浏览量
最新使用 Fun 工具简洁版操作: https://yq.aliyun.com/articles/721594
@Deprecated
这篇文章适合所有的PHP开发新手、老鸟以及想准备学习开发 PHP 的程序猿。众所周知,PHP 是 Web 编程最流行的编程语言,如果有人告诉你,有 Serverless 的 PHP WEB 开发新模式,你是不是会感到好奇和兴奋?在介绍 Serverless Web 开发新模式之前,我们先了解下将 PHP Web Serverless 化的好处:
本文以部署 WordPress 工程在函数计算环境中为例,向您讲解如何使用阿里云函数计算快速构建或移植基于 PHP 框架开发的 Web ,通过本文,您将会了解以下内容:
在本教程中,我们讲解如何利用函数计算一步一步来构建 Web 的 Server 端,该案例是把一个 WordPress 部署到函数计算,本文旨在展示函数计算做 Web Backend 能力,具体表现为以下几点:
MYSQL
, NAS
体验地址: http://1986114430573743.cn-hangzhou.fc.aliyuncs.com/2016-08-15/proxy/share/wp-func/
自定义域名 WordPress : http://fcdemo.mofangdegisn.cn/
正常来说,用户开发 Server 端服务,常常面临开发效率,运维成本高,机器资源弹性伸缩等痛点,而使用 Serverless 架构可以很好的解决上述问题。下面是传统架构和 Serverless 架构的对比:
阿里云函数计算是一个事件驱动的全托管计算服务。通过函数计算,您无需管理服务器等基础设施,只需编写代码并上传。函数计算会为您准备好计算资源,以弹性、可靠的方式运行您的代码,并提供日志查询,性能监控,报警等功能。借助于函数计算,您可以快速构建任何类型的应用和服务,无需管理和运维。
从上面的示例图中,整体架构十分简单明了, 用 FC 替代了 Web 服务器,但是换来的是免运维,弹性扩容,按需付费等一系列优点
从上面原理示意图我们可以看出,Web 服务器根据conf 中 location将 PHP 脚本交给 php-fpm 去解析,然后将解析后的结果返回给 client 端
/mnt/www
目录对于 WordPress 入口函数代码就是这么简单, 建议您先了解下 PHP Runtime
<?php
use RingCentral\Psr7\Response;
function startsWith($haystack, $needle) {
$length = strlen($needle);
return (substr($haystack, 0, $length) === $needle);
}
function handler($request, $context): Response{
$uri = $request->getAttribute("requestURI");
$uriArr = explode("?", $uri);
// default php / or /wp-admin/
if (preg_match('#/$#', $uriArr[0]) && !(strpos($uri, '.php'))) {
$uriArr[0] .= "index.php";
$uri = implode($uriArr);
if (startsWith($uri, "/2016-08-15/proxy/share/wp-func/wp-admin/")) {
// wordpress admin entrypoint
$request = $request->withAttribute("requestURI", $uri);
}
}
$proxy = $GLOBALS['fcPhpCgiProxy'];
$root_dir = '/mnt/www';
//php script
if (preg_match('#\.php.*#', $uri)) {
$format = '%s.%s.fc.aliyuncs.com';
$host = sprintf($format, $context['accountId'], $context['region']); // maybe user define domain
$resp = $proxy->requestPhpCgi($request, $root_dir, "index.php",
['SERVER_NAME' => $host, 'SERVER_PORT' => '80', 'HTTP_HOST' => $host],
['debug_show_cgi_params' => false, 'readWriteTimeout' => 15000]
);
return $resp;
} else {
// static files, js, css, jpg ...
$filename = $root_dir . explode("?", $uri)[0];
$filename = rawurldecode($filename);
$handle = fopen($filename, "r");
$contents = fread($handle, filesize($filename));
fclose($handle);
$headers = [
'Content-Type' => $proxy->getMimeType($filename),
'Cache-Control' => "max-age=8640000",
'Accept-Ranges' => 'bytes',
];
return new Response(200, $headers, $contents);
}
}
其中函数计算为用户提供了一个 $GLOBALS['fcPhpCgiProxy']
对象用来和 php-fpm 进行交互,对
PHP 工程中的 php 文件进行解析,该对象提供了两个重要的接口:
requestPhpCgi
requestPhpCgi($request, $docRoot, $phpFile = "index.php", $fastCgiParams = [], $options = [])
$request
: 跟 php http invoke
入口的参数一致$docRoot
: Web 工程的根目录$phpFile
: 用于拼接 cgi 参数中的 SCRIPT_FILENAME 的默认参数$fastCgiParams
: 函数计算内部尽量根据$request给您构造 default cgi params
, 但是如果您不是想要的,可以使用$fastCgiParams
覆盖一些参数 (reference: cgi)$options
: array类型,可选参数, debug_show_cgi_params 设为 true ,会打印每次请求 php 解析时候的 cgi 参数, 默认为 false ;readWriteTimeout 设置解析的时间, 默认为 5 秒本文编写的时候由于工具链不够完善, 后续章节的操作都是基于控制台,如果您喜欢命令行,具体操作可以参考: https://yq.aliyun.com/articles/721594, 使用 Fun 工具可以大大提高配置部署效率。
由于函数运行时的 IP 是不固定的,您需要设置 RDS 允许所有 IP 访问。但是这样会有风险,不建议这样做。在本教程中,我们将创建一个 RDS MYSQL 数据库,并将它置于一个专有网络 VPC 环境内,函数计算支持 VPC 功能,用户可以通过授权的方式安全地访问 VPC 中的资源(同时包含本示例中的 NAS )。
share
), 配置准备 vpc config
, nas config
和日志服务,比如案例体验的Service配置如下图:|-- index.py
|-- www
index.py代码:
# -*- coding: utf-8 -*-
import logging
import os
file = "/mnt/www/2016-08-15/proxy/share/wp-func"
def mkdir(path):
folder = os.path.exists(path)
if not folder:
os.makedirs(path)
def lsDir():
os.system("ls -ll /mnt/www/2016-08-15/proxy/share/wp-func/")
def handler(event, context):
mkdir(file)
os.system("cp -r /code/www/* /mnt/www/2016-08-15/proxy/share/wp-func/")
print(lsDir())
return 'ok'
基于上述代码创一个函数 move-wp-nas
, 执行函数,将 WordPress 工程包移动到 NAS 的/mnt/www/2016-08-15/proxy/share/wp-func
目录。
/2016-08-15/proxy/share/wp-func
这么奇怪的目录?http://${account_id}.${region}.fc.aliyuncs.com/2016-08-15/proxy/$(seevice_name}/{function_name}/
,为了保证从一个页面跳转到另外一个页面的时候,能自动带上/2016-08-15/proxy/$(seevice_name}/{function_name}/
,我们需要建立这样目录和设置 cgi 相关参数达到 PHP 框架内部自动跳转正确的目的。/2016-08-15/proxy/share/wp-func
这么奇怪的目录?wp-func
(对应上面步骤中的 /mnt/www/2016-08-15/proxy/share/wp-func
), 给函数设置 http trigger ,类型为 anonymous
, 类型都选上。 <?php
use RingCentral\Psr7\Response;
function startsWith($haystack, $needle) {
$length = strlen($needle);
return (substr($haystack, 0, $length) === $needle);
}
function handler($request, $context): Response{
$uri = $request->getAttribute("requestURI");
$uriArr = explode("?", $uri);
// default php / or /wp-admin/
if (preg_match('#/$#', $uriArr[0]) && !(strpos($uri, '.php'))) {
$uriArr[0] .= "index.php";
$uri = implode($uriArr);
if (startsWith($uri, "/2016-08-15/proxy/share/wp-func/wp-admin/")) {
// wordpress admin entrypoint
$request = $request->withAttribute("requestURI", $uri);
}
}
$proxy = $GLOBALS['fcPhpCgiProxy'];
$root_dir = '/mnt/www';
//php script
if (preg_match('#\.php.*#', $uri)) {
$format = '%s.%s.fc.aliyuncs.com';
$host = sprintf($format, $context['accountId'], $context['region']); // maybe user define domain
$resp = $proxy->requestPhpCgi($request, $root_dir, "index.php",
['SERVER_NAME' => $host, 'SERVER_PORT' => '80', 'HTTP_HOST' => $host],
['debug_show_cgi_params' => false, 'readWriteTimeout' => 15000]
);
return $resp;
} else {
// static files, js, css, jpg ...
$filename = $root_dir . explode("?", $uri)[0];
$filename = rawurldecode($filename);
$handle = fopen($filename, "r");
$contents = fread($handle, filesize($filename));
fclose($handle);
$headers = [
'Content-Type' => $proxy->getMimeType($filename),
'Cache-Control' => "max-age=8640000",
'Accept-Ranges' => 'bytes',
];
return new Response(200, $headers, $contents);
}
}
http://${account_id}.${region}.fc.aliyuncs.com/2016-08-15/proxy/$(seevice_name}/{function_name}/
for example:
http://1986114430573743.cn-hangzhou.fc.aliyuncs.com/2016-08-15/proxy/share/wp-func/
|-- index.py
|-- www
index.py代码:
# -*- coding: utf-8 -*-
import logging
import os
file = "/mnt/www/"
def mkdir(path):
folder = os.path.exists(path)
if not folder:
os.makedirs(path)
def lsDir():
os.system("ls -ll /mnt/www/")
def handler(event, context):
mkdir(file)
os.system("cp -r /code/www/* /mnt/www/")
print(lsDir())
return 'ok'
基于上述代码创一个函数 move-wp-nas
, 执行函数,将 WordPress 工程包移动到 NAS 的/mnt/www/
目录。
同时入口函数做下小改动,改成如下代码:
<?php
use RingCentral\Psr7\Response;
function endsWith($haystack, $needle) {
$length = strlen($needle);
return $length === 0 ||
(substr($haystack, -$length) === $needle);
}
function handler($request, $context): Response{
$uri = $request->getAttribute("requestURI");
$uriArr = explode("?", $uri);
// default php / or /wp-admin/
if (preg_match('#/$#', $uriArr[0]) && !(strpos($uri, '.php'))) {
$uriArr[0] .= "index.php";
$uri = implode($uriArr);
}
$proxy = $GLOBALS['fcPhpCgiProxy'];
$root_dir = '/mnt/www';
//php script
if (preg_match('#\.php.*#', $uri)) {
$host = "fcdemo.mofangdegisn.cn"; // your define domain
$resp = $proxy->requestPhpCgi($request, $root_dir, "index.php",
['HTTP_HOST' => $host, 'SERVER_NAME' => $host, 'SERVER_PORT' => '80'],
['debug_show_cgi_params' => false, 'readWriteTimeout' => 60000]
);
return $resp;
} else {
// static files, js, css, jpg ...
$filename = $root_dir . explode("?", $uri)[0];
$filename = rawurldecode($filename);
$handle = fopen($filename, "r");
$contents = fread($handle, filesize($filename));
fclose($handle);
$headers = [
'Content-Type' => $proxy->getMimeType($filename),
'Cache-Control' => "max-age=8640000",
'Accept-Ranges' => 'bytes',
];
return new Response(200, $headers, $contents);
}
}
注意: 绑定自定义域名之后,不用使用控制台来进行调试,就只能使用浏览器来触发函数,日志服务来进行调试。
函数计算有如下优势:
除了上面所列的优势,FC 可以做为 Web Backend,只需要编写一个函数实现传统 Web 服务器中的 conf 中的逻辑,就可以将一个完整的 Web 工程迁移到 FC ,从而从传统的 Web 网站运维,监控等繁琐的事务中解放出来。
最后欢迎大家通过扫码加入我们用户群中,搭建过程中有问题或者有其他问题可以在群里提出来。
函数计算官网客户群(11721331)。
版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。