运行环境?

基本环境?

新浪云 PHP 运行环境目前的 Web 服务器使用的是:

  • CentOS-6.x
  • Apache-2.2.x
  • PHP-5.3.x / PHP-5.6.x

Web 服务器运行在 64 位 Linux 环境下。

Apache 运行在 Prefork 模式下,即每个请求都会对应一个 Apache 进程,请求结束后该进程才能服务于下一个请求。平台通过模块方式扩展了 Apache 和 PHP ?#21335;?#20851;功能。

禁用函数和类?

出于平台安全性考虑,我们禁用了以下函数和类,禁用的标准主要有四点:

  1. 出于对安全性?#30446;?#34385;
  2. 出于对资源管理?#30446;?#34385;
  3. 不常用的 API
  4. 我们提供更好替代方案的 API

禁用的函数:

  • symlink
  • link
  • exec
  • system
  • escapeshellcmd
  • escapeshellarg
  • passthru
  • shell_exec
  • proc_open
  • proc_close
  • proc_terminate
  • proc_get_status
  • proc_nice
  • dl
  • pclose
  • popen
  • stream_socket_server
  • stream_socket_accept
  • stream_socket_pair
  • stream_wrapper_restore
  • mail
  • mb_send_mail
  • posix_kill
  • apache_child_terminate
  • apache_lookup_uri
  • apache_reset_timeout
  • apache_setenv
  • virtual
  • socket_create
  • socket_create_pair
  • realpath_cache_get

禁用的类:

  • SQLiteDatabase
  • SQLiteResult
  • SQLiteUnbuffered
  • SQLiteException

沙箱?

代码和数据的隔离:每个应用在运行期间,只能“?#30784;?#21040;自己的代码和数据,即 A 应用无法访问 B 应用的代码和数据。注意,这里提到的在 Web 服务器上的数据,往往指一些中间处理过程的临时数据,并非最?#31456;?#22320;的数据,比如用户上传照片会临时存储到 TmpFS。

连接数的隔离:我们知道,程序写的不好,很容易导致阻塞,并进一步导致连接数的飙升。单个应用过多占用 Apache 连接数,原因往往是多方面?#27169;?#24212;用请求外部资源被阻塞是一个最为常见的因素,另外应用页面过大浏览器下载慢也是常见因素之一。公有云平台同一时刻往往运行着大量的应用,如果某一应用出现连接数异常,最直接的后果是整个平台上的所有应用都将陷入?#34987;尽?#26032;浪云平台目前有设置“应用最大 HTTP 并发连接数?#20445;?#30446;前这个值是 500,如果应用平均单个请求处理时长是 100ms,那么该应用每秒的 HTTP 并发连接将可以到达 5000,每天的请求超过 1 亿没有问题。但如果您的应用平均每个请求处理时长 2 秒,那么该应用每秒的 HTTP 并发连接只能到达 250,每天支撑的请求数将在千万。总体而言,尽量迅速处理完请求对应用是有利?#27169;?#32780;且也是平台所鼓励的。

内存隔离:目前新浪云平台上对单个 PHP 脚本的处理,设置了 128MB 的上限 (max_memory,ini_set 不可修?#27169;?#25105;们认为这个设置是一个相对很高的值,可?#36816;的?#22815;满足绝大部分应用的需求。设想一台服务器 8G 内存,如果每个 PHP 处理都消耗 64M 内存,那么该服务器最多只能同时运行 128 个 PHP 脚本。新浪云引入了”应用最大并发内存数“的概念,目前的设置是 4GB。如果应用程序单个请求的内存消耗平均在 16MB,那么可同时运行 256 个请求,这和上面的并发连接数的设定是基本一致的。

CPU 隔离:这主要是通过新浪云的配额系统来达到 CPU 时间的隔离。每个应用都有 CPU 时间消耗的分钟速?#35748;?#21046;,避免了某一应用过多非法获取 CPU 资源导致其它应用响应慢的问题。

目前新浪云平台上允许的“单请求最大存活时长”是 300 秒 。

注解

当应用并发超过限制,系统会返回 508 错误,并显示 Connections out of quota。当应用内存占用超过限制,系统会返回 509 错误,并显示 Memory usage out of quota。

环境变量?

您可以通过打印 PHP 的全局变量 $_SERVER 来获取跟新浪云相关的环境变量信息,每个环境变量的信息如下:

变量名 说明
HTTP_APPNAME 标志该请求属于哪个应用
HTTP_APPVERSION 标志该请求对应该应用的哪个版本
HTTP_ACCESSKEY 该应用访问各种服务资源的帐号
HTTP_SECRETKEY 该应用访问各种服务资源的密码
HTTP_APPCOOKIE 一些和 app 管理相关信息

警告

不要直接打印出 $_SERVER 变量,这样可能会造成应用的 AccessKey 和 SecretKey 的泄露。为了应用的安全考虑,请保护好自己的 AccessKey 和 SecretKey。

常用字体文件路径:

constant SAE_Font_Sun?

宋体字体文件路径

constant SAE_Font_Kai?

楷体字体文件路径

constant SAE_Font_Hei?

文泉驿正黑字体文件路径

constant SAE_Font_MicroHei?

文泉驿微米黑字体文件路径

全局函数?

is_https()?

判断客户端是以 http 还是以 https 的方式连接。

返回:如果是 https 连接返回 true,否则返回 false。

本地 IO?

考虑到安全和分布式问题,PHP 运行环境对本地的 IO 做了限制:

  • 应用可以以只读权限访问应用目录以及 PHP 语言?#21335;?#32479;库目录。
  • 可写 TmpFS
  • 为最大程度?#26723;?#24212;用移植的难度,PHP 为 Storage、Memcached 提供了 wrapper 封装,用户可以像读写文件一样来读写 Memcached 和 Storage。

其中 TmpFS 的路径可以通过 SAE_TMP_PATH 这个全局变量获取,该路径具?#34892;?#26435;限,用户可以往这个目录下写文件。

警告

  • 临时文件的生存周期等同于 PHP 请求,也就是当该 PHP 请求完成执行时,所?#34892;?#20837; TmpFS 的临时文件都会被销毁
  • TmpFS 是本地临时文件,不是共享存储,而新浪云是全分布式环境,所以不同请求之间无法通过 TmpFS 共享操作文件
  • TmpFS 操作的文件限于 SAE_TMP_PATH 目录内,这个目录对每个应用是不一样的
  • TmpFS 的文件为纯内存存储

Wrappers?

PHP 自 4.3 版本以来,引入了 stream 流的概念,简单说,就是可以用通用的 IO 读写函数来操作各种资源,比如:tcp、udp、http、ftp 等等,这样做的好处是统一了接口的封装。这就像在 Unix 中将各种设备都抽象成文件,你可以使用 read/write 来操作各种设备,这样统一了操作接口,易于理解和使用。Wrappers 就是用来告诉 stream 流该如何处理(读写)特定的资?#30784;?/p>

Wrappers 使用非常简单,比如下面就是一个最常见一个使用 Wrapper 的语句:

<?php
$c = file_get_contents("http://sae.sina.com.cn");
?>

这里就是使用 http:// Wrapper 实现抓取远程内容并赋值给一个变量的操作。

由于新浪云的 PHP 运行环境并不提供?#24535;?#24615;本地 IO 能力,所以 PHP 运行环?#31243;?#20379;了提供了 Memcached,Storage,KVDB 的 Wrappers 来方便开发者迁移原有程序。

如果你的原有程序中,使用了本地文件型缓存,那么你可以方便地使用 saemc:// 替换本地文件缓存的前缀。

如果你的原有程序中,有文件存储的需求,你原来可能是通过 NFS 或者就是单机提供的存储服务,那么你可以方便地使用 saestor://saekv:// 来替换原来的存储前缀,注意存储的用途是用于文件落地的永久存储,任?#20301;?#23384;、中间临时?#25442;?#25968;据的需求都是不适合使用 Storage 和 KVDB 存储的。

<?php
# 使用"saekv://"这个 Wrapper 将配置文件 config.php 的内容以"config.php"为 key 保存到 KVDB 中,
# 然后用 include 引用了这个文件
file_put_contents('saekv://config.php','');
include 'saekv://config.php';
?>

重要

使用 Wrappers 请要先初始化相应的服务,上例中 KVDB 服务必须是开启的状态。

核心模块?

MySQL 相关 新浪云提供的 MySQL 驱动是原生?#27169;?#25903;持 mysql、mysqli 和 pdo_mysql 三个模块。
Session 新浪云提供了 session cluster,使用标准的 session 相关函数即可。
Memcached 新浪云提供的 memcache 模块,调用 memcache_connect() 时,会忽?#28304;?#20837;的地址参数,直接获得连接句柄。Memcache 的 hash 使用的是一致性 hash。
GD 为保证兼容性,我们也提供了原生的 GD 模块,但由于 GD 效率问题,我们并不很鼓励使用。
cURL cURL 目前已经基本做到了基本兼容。注意新浪云的 cURL 是重载了 FetchURL 服务?#27169;?#25152;以使用 cURL 本质上会不断消耗带宽资?#30784;?/td>
XhProf 为方便开发者调试程序,我们也提供了 XHProf 模块,具体使用见面板的”XHProf”即可。
Yar Yar 是一个轻量级的并行 RPC 框架。
Imagick Imagick 是用 ImageMagic API 来创建和修改图像的 PHP 官方扩展。
PHP-Redis Redis 官方推荐的 PHP Redis 客户端。

注解

其中,mysql 系列函数已经在 PHP 5.5 及以上版本中?#29615;?#24323;,建议使用 PHP 5.6 环境的用户使用 mysqli 和 pdo_mysql 进行数据库连接。

日志系统?

新浪云提供了实时的日志查询功能,方便开发者在线调试分析。

PHP 运行环境中输出的日志类型有: Apache 的访问日志,PHP 的错误日志,可以在日志中心面板中查?#30784;?/p>

你可以使用 PHP 的 error_logtrigger_error 来写日志,更多使用方法请参见 PHP 官方文档: 错误处理和日志记录

注解

目前 PHP 运行环境单个请求最多输出 1000 条日志,超过限制的日志输出会被丢弃。

.htaccess 配置文件?

新浪云 PHP 运行环境支持 Apache 原生的 htaccess 配置文件格式,你可以直接使用应用根目录下的 .htaccess 文件来配置服务器。目前支持的指令包括:

  • SetEnvIf
  • SetEnvIfNoCase
  • Header
  • RequestHeader
  • RewriteEngine
  • RewriteRule
  • RewriteCond
  • AddType
  • AddEncoding
  • DirectoryIndex
  • ErrorDocument
  • FilterProvider
  • FilterChain
  • AddDefaultCharset
  • Options
  • Allow
  • Deny
  • Order
  • Satisfy
  • ExpiresActive
  • ExpiresByType
  • ExpiresDefault

警告

.htaccess 配置文件不能和 config.yaml 配置文件里应?#38376;?#32622;(handle 段)一起使用,如果两个一起使用,会导致配置错乱。

应?#38376;?#32622;?

应用可以通过应用版本目录下的 config.yaml 来对 Apache 服务器做一些配置(类似于 Apache 的 htaccess 文件)。

通过配置,开发者可以很方便的实现以下功能:

  • 目录默认页面
  • 自定义错误页面
  • 压缩
  • 页面重定向
  • 页面过期
  • 设置响应头的 content-type
  • 设置页面访问权限

注解

PHP 运行环境的 config.yaml 文件不会部署到代码目录中,而只是存在于代码仓库中。

应?#38376;?#32622;写在 config.yaml 文件的 handle 下,例如::

name: saetest
version: 1

handle:
- rewrite: if (!-d && !-f) goto "/index.php?%{QUERY_STRING}"

基本语法:

- OPTION: ARG1 ARG2 ...
- OPTION: if (CONDICTIONs) ACTION

其中 OPTION 为配置项,ARG1,ARG2 为参数,CONDITIONs 是一个或者多个 CONDITION,多个 CONDITION 之间使用 && 隔开。ACTION 是 if 条件满足后执行的动作。

CONDITION 可以是以下任意一种:

  • 使用 ==!= 运算符比较变量和字符串;
  • 使用 ~ (大小写敏感)和 ~* (大小写不敏感)运算符匹配变量和正则表达式。正则表达式可以包含匹配组,匹配结果后续可以使用变量 $1..$9 引用(正则匹配使用 PCRE 库,你可以在其主页或者 Wikipedia 找到其语法相关文档);
  • 使用 >>=<<= 比较变量和数字的大小;
  • 使用 -f!-f 运算符检查文件是否存在;
  • 使用 -d!-d 运算符检查目录是否存在;
  • 使用 -e!-e 运算符检查文件、目录是否存在;

appconfig 支持的变量:

  • %{REQ:HEADER_NAME} HTTP 请求头中的字段,如 %{REQ:HTTP_HOST}
  • %{RESP:HEADER_NAME} HTTP 响应头中的字段,如 %{RESP:CONTENT_ENCODING}
  • %{QUERY_STRING} 查询串,一般是 url 中问号后面的内容
  • %{REQUEST_URI} 请求路径,即用户请求的 url 去掉主机部分和查询串后剩下的部分

目录默认页面?

当访问 url 没有指定文件时,指定返回的文件。

语法:

- directoryindex: FILE [...]

directoryindex 在 config.yaml 文件中仅有一项

例子:

- directoryindex: aaa.php bbb.html

自定义错误页面?

语法:

- errordoc: httpcode error_file

httpcode 是诸如 404、302 之类的 http 响应码,error_file 是服务器以 httpcode 响应请求时响应的文件。errordoc 在 config.yaml 中可以配置多项。

例子:

- errordoc: 404 /path/404.html
- errordoc: 403 /path/403.html

压缩?

语法:

- compress: if (CONDICTIONs) compress

在 compress 中,CONDITIONs 只能有一个 CONDITION。

例子:

- compress: if (%{RESP:Content-Length} >= 10240) compress
- compress: if (%{REQ:Referer} == "gphone") compress
- compress: if (%{REQUEST_URI} ~ "/big/") compress

注解

通常情况,我们根据响应头 Content-length,判断是否需要压缩,例如:if (%{RESP:Content-Length} >= 10240) compress,这个静态页面,如 js,css,html 都是没有问题的。但是对 php 脚本,响应 header 中没有 Content-length 这个头,它使用 Transfer-Encoding: chunked, 这个头表示页面输出用 chunked 编码。此时要实现压缩,可以通过应?#38376;?#32622;,同时在 PHP 脚本中输出相应头的方式实现。

例如在应?#38376;?#32622;中写 if (%{RESP:Use-Compress} == “1”) compress,在需要压缩的 PHP 脚本中写 header(“Use-Compress: 1”)。

开发者可以通过检查是不是输出了响应头:Content-Encoding: gzip 来判断压缩是否生效。

URL 重写?

语法:

- rewrite: if (CONDITIONs) goto target_url

在 rewrite 中,CONDITIONs 支持多个 CONDITION。除 HTTP 响应 header(没办法根据响应 header 做重定向)外都可以出现在 rewrite 的 CONDITION 中。

target_url 表?#23616;?#23450;向的目标 url,在 target_url 可以用 $N 的?#38382;?#24341;用 CONDITION 中以正则匹配到的组。

例子:

# 强制使用 https 访问
- rewrite: if (%{REQ:X-Forwarded-Proto} != "https") goto "https://%{HTTP_HOST}%{REQUEST_URI}"

# 当 url 匹配 urldir/(.*) ,并且 输入 header referer 等于 sina 时,跳转至页面 /usr/$1,$1 表示刚刚匹配的 urldir/(.*) 中的 (.*) 部分。
- rewrite: if (%{REQUEST_URI} ~ "urldir/(.*)" && %{REQ:REFERER} == "sina") goto "/url/$1"

# 当 url 匹配 urldir/(.*),并且请求的是一个目录时,跳转至 /url/$1
- rewrite: if (-d && %{REQUEST_URI} ~ "urldir/(.*)") goto "/url/$1"

# 当 url 匹配 path,并且请求的不是一个文件时,跳转至 /url/query.php
- rewrite: if (!-f && %{REQUEST_URI} ~ "path") goto "/url/query.php"

# 当查询串等于 so,并且 url 以 zhaochou 结尾时,跳转至 /url/$1,$1 表示 query_string 匹配到的部分。
- rewrite: if (%{QUERY_STRING} ~ "^(so)$" && %{REQUEST_URI} ~ "zhaochou$") goto "/url/$1"

# 当查询串不包含 sohu,并且 url 以 zhaochou 结尾时,跳转至 /url/query.php?%{QUERY_STRING},%{QUERY_STRING}表示查询串。
- rewrite: if (%{QUERY_STRING} !~ "sohu" && %{REQUEST_URI} ~ "zhaochou$") goto "/url/query.php?%{QUERY_STRING}"

# 如果 url 既不是文件,也不是目录,跳转至 index.php?%{QUERY_STRING}
- rewrite: if (!-d && !-f) goto "/index.php?%{QUERY_STRING}"

警告

  1. 如果?#34892;?#22914; %{REQUEST_URI} ~ “^(.*)$”类的请求,一定要加上是否是目录或者文件,防止无穷的 rewrite。
  2. 在 goto 语句中,虽然某些时候可以不以 / 开头,但是强烈建议以 / 开头。

指定过期时间和头信息?

语法:

- expire: if (CONDITION) time seconds
- mime: if (CONDITION) type content-type

seconds 是秒数,content-type 是表示文档类型的字符串。

例子:

- expire: if (%{REQ:REFERER} ~ "sina") time 10
# 如果 url 请求文件的扩展名是 pdf2,设置 Content-Type 为 application/pdf
- mime: if (%{REQUEST_URI} ~ "\.pdf2$") type "application/pdf"
- mime: if (%{REQUEST_URI} ~ "\.pdf2$") type "application/pdf"
# 只要请求 header referer 包含字符串 sina,就设置 Content-Type 为 text/plain
- mime: if (%{REQ:REFERER} ~ "sina") type "text/plain"

if 语句支持单个 CONDITION。可以出现在 CONDITION 中的变量参考 Apache Docs ,只支持字符串和正则匹配。

基于主机的访问控制?

语法:

- hostaccess: if (CONDITION) deny IP
- hostaccess: if (CONDITION) allow IP

if 语句只支持单个 CONDITION。

IP 需要加引号,IP 可以是一个或多个 ip 地址、all(所有 IP 地址)、 CIDR (如 108.192.8.0/24),具体可以参考 Apache 配置,allow ?#21069;?#21517;单,deny 是黑名单。

例子::

# 禁止 127.0.0.1 访问 private 目录
- hostaccess: if (%{REQUEST_URI} ~ "/private/") deny "127.0.0.1"

# 只允许 127.0.0.1 访问.conf 结尾的文件
- hostaccess: if (%{REQUEST_URI} ~ "\.conf$") allow "127.0.0.1"

# 禁止 127.0.0.1 的所有访问(这个要慎用)
- hostaccess: deny "127.0.0.1"

# 对 cron 任务保护,防止被外部抓取,我们将 cron 任务放在 cron 目录下 (sae 中 cron 服务执行时,走的是内部网络)
- hostaccess: if (%{REQUEST_URI} ~ "/cron/") allow "10.0.0.0/8" 允许 10 打头的所有 IP

# 对于屏蔽一组 IP 地址,可以写成子网掩码?#38382;劍?#25110;者将多个 IP 之间加以空格。子网掩码?#38382;?#22914;下:
- hostaccess: if (%{REQUEST_URI} ~ "/cron/") deny "108.192.8.0/24" 屏蔽 108.192.8 打头的所有 IP

# 允许 108.134.13.24 和 108.122.122.13 这两个 IP
- hostaccess: allow "108.134.13.24 108.122.122.13"

HTTP 基础?#29616;?a class="headerlink" href="#http-ji-chu-ren-zheng" title="永久链接至标题">?

语法:

- passwdaccess: passwd "USERNAME:PASSWORD..."
- passwdaccess: if (CONDITION) passwd "USERNAME:PASSWORD..."

例子:

# 所有访问都要密码,允许用户 writer 用密码 123zxc 访问
- passwdaccess: passwd "write:123zxc"

# 访问 secret 目录需要密码,允许用户 test 用密码 123qwe 访问,用户 coder 用密码 123asd 访问
- passwdaccess: if (%{REQUEST_URI} ~ "/secret/") passwd "test:123qwe coder:123asd"

# 访问.text 结尾的文件需要密码,允许用户 writer 用密码 123zxc
- passwdaccess: if (%{REQUEST_URI} ~ "\.text$") passwd "writer:123zxc"

# 用户的网站后台程序都放在 admin 目录下,需要对 admin 目录做密码保护
- passwdaccess: if (%{REQUEST_URI} ~ "/admin/") passwd "admin:admin123"

if 语句中只支持单个 CONDITION ,%{REQ:HEADER_NAME}, %{REQUEST_URI}可以出现在 CONDITION 中,只支持字符串和正则匹配。

Session?

新浪云 PHP 环境默认提供了共享的 Session 存储,用户可以不用任何设置直接使用 PHP 提供的 Session 系列函数进行 Session 相关操作。

若用户需要使用特殊的 Session 存储,如将 Session 存入 MySQL,Memcached,KVDB 等服务中,可以参考: PHP: session_set_save_handler - Manual 实现自定义 Session Handler。 新浪云 提供了 Memcached 存储 Session 的方案,可通过如下代码将 Session 数据存入 Memcached 中。

例子:使用新浪云 Memcached 服务作为 Session 存储

<?php

//PHP 5.3 版本
$handler = new sinacloud\sae\MemcacheSessionHandler();
session_set_save_handler(
    array($handler, 'open'),
    array($handler, 'close'),
    array($handler, 'read'),
    array($handler, 'write'),
    array($handler, 'destroy'),
    array($handler, 'gc')
);

session_start();
...

//PHP 5.6 版本
$handler = new sinacloud\sae\MemcacheSessionHandler();
session_set_save_handler($handler, true);

session_start();
...

外网访问?

您可以通过以下方式来访问外网:

  • php curl 库 访问外网的 HTTP 资?#30784;?#25152;有 curl 访?#23454;?#26085;志可以在日志中心查?#30784;?/li>
  • fsockopen 通过 TCP Socket 访问外网资?#30784;?/li>

范例一,使用 curl 库抓取 http://www.sinaapp.com

<?php
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'http://www.sinanapp.com');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

范例二,使用 fsockopen 连接 IP 1.2.3.480 端口。

<?
$fp = fsockopen("tcp://1.2.3.4:80", 13, $errno, $errstr);
if (!$fp) {
    echo "ERROR: $errno - $errstr\n";
} else {
    fwrite($fp, "\n");
    echo fread($fp, 26);
    fclose($fp);
}

?>

范例三,使用 Socket 与外部网站进行 SSL 连接。

<?php

$fp = fsockopen("ssl://passport.baidu.com", 443, $errno, $errstr);
if (!$fp) {
    echo "ERROR: $errno - $errstr \n";
} else {
    $str="GET /?login HTTP/1.1\r\n";
    $str.="User-Agent: curl/7.19.6 (i686-pc-linux-gnu) libcurl/7.19.6 OpenSSL/0.9.8b zlib/1.2.3 libidn/0.6.5\r\n";
    $str.="Host: passport.baidu.com\r\n";
    $str.="Accept: */*\r\n\r\n";

    fwrite($fp, $str);
    while (!feof($fp)) {
        echo fgets($fp, 128);
    }
    fclose($fp);
}

?>

入口和出口IP?

注解

外网访问出口 IP 列表:

  • 123.125.23.211
  • 123.125.23.212
  • 123.125.23.213
  • 123.125.23.214
  • 123.126.57.32
  • 123.126.57.35
  • 61.172.201.27
  • 61.172.201.28
  • 183.60.187.57
  • 183.60.187.58
  • 14.116.224.3
  • 14.116.224.4
  • 14.116.224.5
  • 14.116.224.6
  • 14.116.224.7
  • 14.116.224.8
  • 14.116.224.9
  • 14.116.224.10

如果你需要给访?#23454;?#22806;部接口添加 IP 访问授权,建议添加以下六个 CIDR 规则:

  • 123.125.23.0/24
  • 123.126.57.0/24
  • 61.172.201.0/24
  • 183.60.187.0/24
  • 14.116.224.0/24

由于出口IP会变动,建议您使用 域名解析方式 获取出口IP地址:

  • dig iplist.sinacloud.com A
河南22选5开奖
  • <em id="xpjve"><ol id="xpjve"></ol></em>

          1. <em id="xpjve"><ol id="xpjve"></ol></em>