運行環境?

基本環境?

新浪云 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 的相關功能。

禁用函數和類?

出于平臺安全性考慮,我們禁用了以下函數和類,禁用的標準主要有四點:

  1. 出于對安全性的考慮
  2. 出于對資源管理的考慮
  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

沙箱?

代碼和數據的隔離:每個應用在運行期間,只能“看”到自己的代碼和數據,即 A 應用無法訪問 B 應用的代碼和數據。注意,這里提到的在 Web 服務器上的數據,往往指一些中間處理過程的臨時數據,并非最終落地的數據,比如用戶上傳照片會臨時存儲到 TmpFS。

連接數的隔離:我們知道,程序寫的不好,很容易導致阻塞,并進一步導致連接數的飆升。單個應用過多占用 Apache 連接數,原因往往是多方面的,應用請求外部資源被阻塞是一個最為常見的因素,另外應用頁面過大瀏覽器下載慢也是常見因素之一。公有云平臺同一時刻往往運行著大量的應用,如果某一應用出現連接數異常,最直接的后果是整個平臺上的所有應用都將陷入癱瘓。新浪云平臺目前有設置“應用最大 HTTP 并發連接數”,目前這個值是 500,如果應用平均單個請求處理時長是 100ms,那么該應用每秒的 HTTP 并發連接將可以到達 5000,每天的請求超過 1 億沒有問題。但如果您的應用平均每個請求處理時長 2 秒,那么該應用每秒的 HTTP 并發連接只能到達 250,每天支撐的請求數將在千萬。總體而言,盡量迅速處理完請求對應用是有利的,而且也是平臺所鼓勵的。

內存隔離:目前新浪云平臺上對單個 PHP 腳本的處理,設置了 128MB 的上限 (max_memory,ini_set 不可修改),我們認為這個設置是一個相對很高的值,可以說能夠滿足絕大部分應用的需求。設想一臺服務器 8G 內存,如果每個 PHP 處理都消耗 64M 內存,那么該服務器最多只能同時運行 128 個 PHP 腳本。新浪云引入了”應用最大并發內存數“的概念,目前的設置是 4GB。如果應用程序單個請求的內存消耗平均在 16MB,那么可同時運行 256 個請求,這和上面的并發連接數的設定是基本一致的。

CPU 隔離:這主要是通過新浪云的配額系統來達到 CPU 時間的隔離。每個應用都有 CPU 時間消耗的分鐘速度限制,避免了某一應用過多非法獲取 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 語言的系統庫目錄。
  • 可寫 TmpFS
  • 為最大程度降低應用移植的難度,PHP 為 Storage、Memcached 提供了 wrapper 封裝,用戶可以像讀寫文件一樣來讀寫 Memcached 和 Storage。

其中 TmpFS 的路徑可以通過 SAE_TMP_PATH 這個全局變量獲取,該路徑具有寫權限,用戶可以往這個目錄下寫文件。

警告

  • 臨時文件的生存周期等同于 PHP 請求,也就是當該 PHP 請求完成執行時,所有寫入 TmpFS 的臨時文件都會被銷毀
  • TmpFS 是本地臨時文件,不是共享存儲,而新浪云是全分布式環境,所以不同請求之間無法通過 TmpFS 共享操作文件
  • TmpFS 操作的文件限于 SAE_TMP_PATH 目錄內,這個目錄對每個應用是不一樣的
  • TmpFS 的文件為純內存存儲

Wrappers?

PHP 自 4.3 版本以來,引入了 stream 流的概念,簡單說,就是可以用通用的 IO 讀寫函數來操作各種資源,比如:tcp、udp、http、ftp 等等,這樣做的好處是統一了接口的封裝。這就像在 Unix 中將各種設備都抽象成文件,你可以使用 read/write 來操作各種設備,這樣統一了操作接口,易于理解和使用。Wrappers 就是用來告訴 stream 流該如何處理(讀寫)特定的資源。

Wrappers 使用非常簡單,比如下面就是一個最常見一個使用 Wrapper 的語句:

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

這里就是使用 http:// Wrapper 實現抓取遠程內容并賦值給一個變量的操作。

由于新浪云的 PHP 運行環境并不提供持久性本地 IO 能力,所以 PHP 運行環境提供了提供了 Memcached,Storage,KVDB 的 Wrappers 來方便開發者遷移原有程序。

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

如果你的原有程序中,有文件存儲的需求,你原來可能是通過 NFS 或者就是單機提供的存儲服務,那么你可以方便地使用 saestor://saekv:// 來替換原來的存儲前綴,注意存儲的用途是用于文件落地的永久存儲,任何緩存、中間臨時交換數據的需求都是不適合使用 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 驅動是原生的,支持 mysql、mysqli 和 pdo_mysql 三個模塊。
Session 新浪云提供了 session cluster,使用標準的 session 相關函數即可。
Memcached 新浪云提供的 memcache 模塊,調用 memcache_connect() 時,會忽略傳入的地址參數,直接獲得連接句柄。Memcache 的 hash 使用的是一致性 hash。
GD 為保證兼容性,我們也提供了原生的 GD 模塊,但由于 GD 效率問題,我們并不很鼓勵使用。
cURL cURL 目前已經基本做到了基本兼容。注意新浪云的 cURL 是重載了 FetchURL 服務的,所以使用 cURL 本質上會不斷消耗帶寬資源。
XhProf 為方便開發者調試程序,我們也提供了 XHProf 模塊,具體使用見面板的”XHProf”即可。
Yar Yar 是一個輕量級的并行 RPC 框架。
Imagick Imagick 是用 ImageMagic API 來創建和修改圖像的 PHP 官方擴展。
PHP-Redis Redis 官方推薦的 PHP Redis 客戶端。

注解

其中,mysql 系列函數已經在 PHP 5.5 及以上版本中被廢棄,建議使用 PHP 5.6 環境的用戶使用 mysqli 和 pdo_mysql 進行數據庫連接。

日志系統?

新浪云提供了實時的日志查詢功能,方便開發者在線調試分析。

PHP 運行環境中輸出的日志類型有: Apache 的訪問日志,PHP 的錯誤日志,可以在日志中心面板中查看。

你可以使用 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 配置文件里應用配置(handle 段)一起使用,如果兩個一起使用,會導致配置錯亂。

應用配置?

應用可以通過應用版本目錄下的 config.yaml 來對 Apache 服務器做一些配置(類似于 Apache 的 htaccess 文件)。

通過配置,開發者可以很方便的實現以下功能:

  • 目錄默認頁面
  • 自定義錯誤頁面
  • 壓縮
  • 頁面重定向
  • 頁面過期
  • 設置響應頭的 content-type
  • 設置頁面訪問權限

注解

PHP 運行環境的 config.yaml 文件不會部署到代碼目錄中,而只是存在于代碼倉庫中。

應用配置寫在 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 編碼。此時要實現壓縮,可以通過應用配置,同時在 PHP 腳本中輸出相應頭的方式實現。

例如在應用配置中寫 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 表示重定向的目標 url,在 target_url 可以用 $N 的形式引用 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. 如果有形如 %{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 是白名單,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 地址,可以寫成子網掩碼形式,或者將多個 IP 之間加以空格。子網掩碼形式如下:
- 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 基礎認證?

語法:

- 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 資源。所有 curl 訪問的日志可以在日志中心查看。
  • fsockopen 通過 TCP Socket 訪問外網資源。

范例一,使用 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 列表:

  • 220.181.136.56
  • 220.181.136.57
  • 220.181.129.102
  • 220.181.129.119
  • 220.181.129.89
  • 220.181.129.126
  • 220.181.129.121
  • 220.181.129.92
  • 220.181.129.99
  • 220.181.84.185
  • 220.181.136.120
  • 220.181.136.141
  • 123.125.23.211
  • 123.125.23.212
  • 123.125.23.213
  • 123.125.23.214
  • 61.172.201.27
  • 61.172.201.28
  • 183.60.187.57
  • 183.60.187.58

如果你需要給訪問的外部接口添加 IP 訪問授權,建議添加以下六個 CIDR 規則:

  • 220.181.129.0/24
  • 220.181.136.0/24
  • 220.181.84.0/24
  • 123.125.23.0/24
  • 61.172.201.0/24
  • 183.60.187.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>