分類
程式技術

有趣的iOS上Safari播放mp4問題

今天在測試一個 nginx reversed proxy 上, 代理 upstream server mp4 檔案時的播放問題.

之前沒特別有這個狀況, 但今天開始不能播放(感覺應該是在ios 12.4起發生), 十分有趣, 從 nginx server log 也看不出來原因, 只知道 nginx server 在 ios safari request 時, sent-byte 大小小於原始的 mp4 檔案, 所以也只能從 ios safari 的 developer mode 來著手.

先進行 ios safari developer mode, 可以參考:

https://unrealnavigation.com/blog/web-inspector-with-ios-simulator

然後發現原來 safari 會 request byte-range 0-1 這樣的狀況, 難怪都不會送出完整結果, 這樣一來原因就明確了, 實務上的狀況可以參考這篇:

https://www.stirtingale.com/guides/2018/10/mp4-not-working-cloudflare

若是原本的 server 沒有支援 byte-range 時, 會導致 status 200 而非 safari 預期的 206, 而導致失敗. 接下來就可以來找解決方案.

以 nginx proxy_pass 的反向代理結構來看, 可以利用這個方案:

https://stackoverflow.com/questions/22728016/nginx-is-not-accepting-range-of-bytes

使用 proxy_force_ranges on; 即可.

當然, 原本的 upstream 也需要有支援才能解決.

如此一來, 便能在 ios safari 於 request mp4 資源時, 下達的 byte-range 於快取或未快取內容皆可正常工作.

PS. 於查找過程找到一篇介紹 nginx revered proxy hash key 算法資料可供參考:
https://tomme.me/nginx-proxy-cache-server/

分類
FreeBSD/Linux

[nginx]Reverse Proxy with Cache SSL fails

一般我們在實作 Nginx 的 Reverse Proxy with Cache 時, 可以參考這篇:

https://www.nginx.com/resources/wiki/start/topics/examples/reverseproxycachingexample/

不過若是 proxy_pass 的 upstream 是 https://example.com/ 時, 會發生以下錯誤:

SSL_do_handshake() failed (SSL: error:14077410:SSL routines:SSL23_GET_SERVER_HELLO:sslv3 alert handshake failure) while SSL handshaking to upstream

從 log 來看, 其實原因很單純, 因為預設往 upstream 的 web request 會使用 ip 的方式連接, 而導致錯誤 (前端收到為 502 bad gateway), 解決方式只需要新增一個值:

proxy_ssl_server_name on;

如此即可, 請參閱:

分類
好用軟體

好用的縮圖與快取服務(images.weserv.nl)

在查找 imgproxy 專案時, 找到的一個圖片快取、調整大小的一個服務:

https://images.weserv.nl/

在網站應用上, 這種使用情境是很常見的需求, 當然, 自己寫或是用已有的專案都能解決, 如前面提到的 imgproxy 專案:

https://github.com/DarthSim/imgproxy
https://github.com/willnorris/imageproxy

不過像是 weserv 這樣的, 可以直接使用的服務就更方便了. 接下來來測試一下效果. 以這張圖為例:

https://www.flickr.com/photos/okilyt/35120121534/sizes/l

在 flickr 上會生成許多尺寸, 如下(連原始圖共12種類):

Original (4512 x 3008)
Large 2048 (2048 x 1365)
Large 1600 (1600 x 1067)
Large 1024 (1024 x 683)
Medium 800 (800 x 534)
Medium 640 (640 x 427)
Medium 500 (500 x 333)
Small 320 (320 x 213)
Small 240 (240 x 160)
Thumbnail (100 x 67)
Square 150 (150 x 150)
Square 75 (75 x 75)

利用 image.weserv.nl 來進行對應操作, 會使用到的參數就是 url, w, h, t (基本的參數), 先來介紹一下簡單用法, url 為必要參數, 因為是快取圖片來源的基礎, 不需要給 protocol, 從 host 開始即可, 如

http://sample.diary.tw/imgs/001.jpg 只需要給 sample.diary.tw/imgs/001.jpg (需要 url encode)

接下來是 w 寬度參數, 預設會成比例縮小, 若是 w, h 同時給, 則以較小的為約束邊, 進行縮小, 若是再給定 t=square 則用來限制縮小的圖為方形縮圖, 要了解更多就參考網站上的說明:

https://images.weserv.nl/#quick-reference

相信很快就能上手.

強大的HTML5離線作業

HTML5裡令人驚豔的一項特性就是可以離線瀏覽功能.

利用這項功能, 可以很容易地讓網頁在沒有網路的環境下, 也能順利讓使用者閱讀及使用, 而且實作上也相當單純, 只需要注意寫好更新網頁的邏輯及時間點即可.

一般來說, 只需要把 manifest 設定好, 基本的離線瀏覽功能就差不多完成了, manifest 的設定容易, 就是把”要”在離線瀏覽的網頁及內容, 寫在 manifest 裡, 例如網頁是

http://test.com/mypage.htm

內容有一個 js/myjs.js
另外還有兩張圖為 imgs/1.jpg 及 imgs/2.jpg

則在 mypage.htm 中的 html tag 裡, 多加上 manifest=”mypage.manifest” 而且在 mypage.manifest 中, 加上以下內容(純文字):

CACHE MANIFEST
mypage.htm
js/myjs.js
imgs/1.jpg
imgs/2.jpg

這樣就完成了. 有意思的地方是即使用 mypage.php 這樣的動態內容, 一樣可以利用這樣的方式將內容存在客戶端中.

如此一來, 即使在沒有網路的狀況下, 也可以順利瀏覽這個網頁內容 http://test.com/mypage.htm

接下來要說明的是更新方式, 一旦寫入到客戶端的內容, 即使原本的 mypage.htm 更新, 就算是有連線時, 該內容也不會更新(而且即使是動態的 aspx, php 等也都是一樣的), 簡單地說, 在 manifest 中的內容, 就是完全使用客戶端的內容, 不管 server 上的內容, 更新的方式就是更新 manifest 的檔案, 瀏覽器會檢視 manifest 內容來進行更新, 而且會自動更新(在線上時的第一次讀到 manifest 更新時, window.applicationCache.status 會變成狀態 4 (window.applicationCache.UPDATEREADY), 此時可以手動更新, 使用 window.applicationCache.update(); 即可做手動更新, 即使在這次的訪問不更新, 在下一次的訪問時, 瀏覽器也會自動更新.

利用這點, 就可以很容易地實作出離線瀏覽內容的網頁.

若希望只更新這些快取內容, 最簡單的作法是在 mypage.manifest 中, 多加個時間或版本號, 如下:

CACHE MANIFEST
#VER 000151

mypage.htm
js/myjs.js
imgs/1.jpg
imgs/2.jpg

其中的 # 代表著這行為註解, 所以可以自由寫入內容, 上例是寫個版本號 000151, 若要強迫客戶端更新時, 可以將這個 manifest 檔的版本號改為 000151 (若其他內容都可以), 這樣就可以方便容易地來維護在客戶端離線的內容更新.

這裡有一篇很清楚的介紹文, 可以參考
http://www.ibm.com/developerworks/cn/web/1011_guozb_html5off/

另外, 配合良好的設計結構, 可以將離線/在線的功能都實作出來, 方便在無法連線時, 也能使用的網頁, HTML5 的這個功能, 的確非常強大!

在實作測試時, 發現若是有設定 manifest 的網頁, 有些瀏覽器對有些 server request 即使沒有設定在 manifest 中, 也會有快取或存取的異常狀況, 建議在 manifest 的最後, 再多加上

NETWORK:
*

這兩行, 可以讓不正常工作的 server request 正常運作.

相關資料:
http://diveintohtml5.info/offline.html

w3c 的資料:
http://www.w3.org/TR/html5/offline.html

Apple Safari的資料:
http://developer.apple.com/library/safari/#documentation/iPhone/Conceptual/SafariJSDatabaseGuide/OfflineApplicationCache/OfflineApplicationCache.html

分類
blog服務

從Google Reader API中獲得RSS Cache的資料

相信很多人有用過 Google Reader, 其中有趣的地方, 是今天要介紹的 Google Reader API 中, 把 RSS Cache 的資料讀出來的部分, 先來參考一下文件:

http://code.google.com/p/pyrfeed/wiki/GoogleReaderAPI

其實很容易使用的, 例如:

http://www.google.com/reader/atom/feed/https://diary.tw/tim/rss?n=20
(需要 Google Account 登入)

就可以取得 https://diary.tw/tim/rss 下的資料, 什麼樣的應用情境呢? 例如某網誌的 RSS 只有提供 20 篇最新的資料, 但需要取得舊的資料(當然前提是有其他人在 Google Reader 中訂閱過才行, 也就是 Google Reader 有 cache 過它的 RSS 資料的狀況下), 就可以使用這個 API 了.

又或是原始的網站找不到了, 但在 RSS 中有資料, 就可以利用 RSS Cache 的資料來將原本的網站資料找出來.

使用方式就是上面的說明, 而要換頁的方式, 是使用 more token 的方式, 在 response 的內容中, 會有個 gr:continuation, 這個 tag 就是再下一頁的內容使用, 使用方式是用 c 參數, 如下:

http://www.google.com/reader/atom/feed/https://diary.tw/tim/rss?c=CLK9-LPAya0C

該 token 會依實際狀況改變, 請自行調整. 這樣就可以一直翻頁下去, 直到沒有 gr:continuation tag 時就是沒有更多的最後回應了.

[2012/9/14 18:24]
這裡有 Google Reader API 的資料可供參考:
http://blog.martindoms.com/2009/10/16/using-the-google-reader-api-part-2/

.net Cache物件的Add及Insert不同

最近又剛好寫到 .net Cache 物件的功能了, 之前有介紹過 .net Cache 物件: ASP.NET Cache物件使用方式及應用, 接下來說明一下他的 Insert 方法和 Add 方法的不同.

由於在使用 Cache 時, 通常是先 check Cache 的資料是否存在後, 再進行資料的 Cache 寫入, 然而寫入的方式有兩種, 一種為 Insert , 一種為 Add , 其中差異不少, Insert 方法有幾種不同的排列組合外, 尚有可以一直覆寫的功能, 也就是說, 在利用相同的 key 去 Insert 時, 他會利用最新的資料去覆寫掉原來的資料, 而 Add 方法則是不會覆寫, 也就是 Add 會加不進去, 但也不會產生什麼錯誤, 呼叫完 Add 方法後, 若該 Cache 的 data 仍為有效, 則將該 Cache 的 object 傳回, 而且不異動(更新)原來的 Cache .

上面的述描對於一般的應用都沒太大影響, 反而是 Insert 方法的彈性比較大, 不過由於 Insert 方法會有覆寫的問題, 所以若是大量訪問的網問上, 應該效能會略遜於 Add 方法, 因為 Add 方法在 Cache 尚未過期前, 就不會再去異動 Cache 的資料了, 這點可以再測試看看!

微軟 MSDN 的資料:
Cache.Insert 方法
Cache.Add 方法

eAccelerator安裝好囉

久聞 eAccelerator 大名, 今天同事在一台 BSD 的環境下安裝好 php 的 eAccelerator 的組件, 據說可以大幅提昇 php 效能, 可想見, 將 php build 好的 binary code cache 在目錄下, 應該對於 php 的效能有顯著提昇沒錯, 於是就開始著手安裝 eAccelerator 囉…

我是參考了這篇文章, http://www.tiec.tp.edu.tw/lifetype/post/102/5552, 感謝原作者用心說明, 接下來進行安裝, 由於我的環境是 appserv, 所以要先到 http://www.arnot.info/eaccelerator/ 找對應版本的 eAccelerator dll 版本, 下載後, copy 至 php 的 extension 目錄下, 並進行 php.ini 的調整(特別注意是eaccelerator.cache_dir 參數, 記得要先開好目錄, 這樣 eAccelerator 才能將 build 好的 binary code 存放在該位置), 完成後就直接重起 apache 即可, 接下來利用 phpinfo 來 check 一下(或利用指令 php -v 來觀察), 畫面中出現如下的 eAccelerator 字眼就代表安裝成功囉:

接下來就 check 看網頁工作是否正常, 原則上第一次會進行 build binary 的動作並存在指定的目錄下, 會稍微慢一點點, 但第二次之後, 就會跑得飛快, 相信你會滿意地.

ASP中的cache方法

之前介紹了在 ASP.NET 中的 cache 方式, 現在來看看 ASP 中的 cache 方式.

由於 ASP 中沒有內建的 cache 物件, 所以沒辦法利用 ASP 中的內建的方式來進行 cache, 如前篇所言, cache 類似全域物件的 Application , 所以實作 ASP 的 cache 方法, 就是利用 Application 來實作囉.

這裡利用了兩個變數, exp_”cacheToken” 及 data_”cacheToken” 兩個 Application 的變數來進行操作, 程式碼如下:

最近HEMiDEMi有點慢

由於在右側有引用了 HEMiDEMi 的最近書籤, 但因為在網路尖峰時間, HEMiDEMi 有點慢, 也會造成本網頁的負擔, 於是乾脆自行寫個快取機制來解決這個問題.

做法很單純, 我實作了一支小程式, 利用 php 去取得原來 javascript 內的網址內容:

http://www.hemidemi.com/digest/user/timhuang/bookmark/recent

再來就是存在本地檔, 利用了 filemtime 來取出檔案的修改時間, 並與現在時間比對, 若久於一定的時間(目前訂為 1200秒), 就重新再取, 否則就直接用快取檔案的內容, 如此一來便能大幅加速這個在尖峰時間的 javascript 下載慢速的問題.

所以原來的 <script type=”text/javascript” src=”http://www.hemidemi.com/digest/user/timhuang/bookmark/recent”></script> 就被改為引用實作好的 cache php 檔案: <script type=”text/javascript” src=”https://diary.tw/skin/customize/1/hemidemi.php”></script> 如此一來, 該 php 就會依上述工作方式來進行工作.

實作完成後, 覺得要多加個統計來找出 cache 的比例, 於是再利用了另一個檔案記錄 cache 和總使用次數, 並例在該 HEMiDEMi 區域, 如下:

其中前面的數字代表使用 cache 的, 後面為總使用次數, 這樣看起來蠻好的…

[2006/10/4 23:46]
再新增讓使用者知道是利用 cache 出來, 或是真實去 HEMiDEMi 取得內容的狀況, 會出現 Cached 及 Real 兩種說明:

[2006/10/9 16:46]
為了能讓 cache 效能更發揮, 調整 cache 時間為 6小時, 所以 Real 的機會更低囉..

 

分類
懶得分類

ASP.NET Cache物件使用方式及應用

在使用 asp.net 對於效能有相當幫助的 Cache 功能我特別感興趣, 這篇文章將針對 Cache 的用法做個介紹. (使用 c# 做範例程式語言)

Cache 是在 Page 中取得的一個屬性, 本身是一個在 System.Web.Caching.Cache 型態, 基本上可以想成一個共用的全域變數(很類似之前設在 Application 的變數), 然而, 其功能可以搭配許多相依性參數進行設置, 以期能達到網頁效能的提昇.

我們先來看一下 Cache 的基本使用方式:
(範例1)

protected void Page_Load(object sender, EventArgs e)
{
    String strC1 = (String) Cache["c1"];
    if (strC1 == null)        {
        strC1 = DateTime.Now.ToString("yyyy/MM/dd hh:mm:ss");
        Cache.Insert("c1", strC1);
    }
    Response.Write(strC1);
}