顯示具有 Javascript 標籤的文章。 顯示所有文章
顯示具有 Javascript 標籤的文章。 顯示所有文章

2013年11月27日

Keep MongoDB connection in nodejs 筆記

因為是小 project 使用 native mongodb nodejs driver
首先,使用新的 MongoClient 取代 Db
雖然範例裡面都有乖乖 db.close() ,但是其實不需要這樣,
更好的方法是持續這個 mongodb connection ,並且讓需要 mongodb 的部分共用同一個 connection。
一開始我的寫法類似範例裡的方式
mongo = require '\mongodb' mongoClient = mongodb.MongoClient mongodbURI = 'mongodb://127.0.0.1:27017/test' route.get '/abc', (req, res) -> mongoClient.connect mongodbURI, (err, db) -> throw err if err db.collection('abc').insert({foo:'bar'}, (err, result) -> throw err if err console.log "succes insert abc" db.close() ) 這樣的配置 nginx - nodejs - mongodb 在第二天大量流量進來的時候會噴發
nginx: connect() failed (111: Connection refused) while connecting to upstream [Error: failed to connect to [localhost:27017]] 這個跟 localhost 或是 127.0.0.1 無關
由於 process 結束會自動斷掉 connection ,以下嘗試拿掉 db.close()
route.get '/abc', (req, res) -> mongoClient.connect mongodbURI, (err, db) -> throw err if err db.collection('abc').insert({foo:'bar'}, (err, result) -> throw err if err console.log "succes insert abc" ) 這樣 mongodb 會自動處理 mongo connection ,但是量大的時候依然會噴發 [initandlisten] connection accepted from 127.0.0.1:41822 #277824 (7998 connections now open) 失去 nginx - nodejs 可以處理 C10K problem 的優勢
嘗試改成一開始就建立 mongodb connection 並且不關閉,
使用mongodb 預設 maxPoolSize=5 mongoClient.connect mongodbURI, (err, db) -> throw err if err route.get '/abc', (req, res) -> db.collection('abc').insert({foo:'bar'}, (err, result) -> throw err if err console.log "succes insert abc" ) route.get '/def', (req, res) -> db.collection('def').insert({foo:'bar'}, (err, result) -> throw err if err console.log "succes insert def" ) 以上處理上千個併發沒問題, mongodb 會自動處理 connection pool,只需要依需求調整 maxPoolSize
實際上可以把 mongodb 提出來
mongo.coffee mongodb = require 'mongodb' mongoClient = mongodb.MongoClient mongodbURI = 'mongodb://127.0.0.1:27017/test' module.exports.init = (callback) -> mongoClient.connect mongodbURI, (err, db) -> console.log err if err console.log 'opened mongodb connection' callback(err, db)
server.coffee mongo.init (err, db) -> throw err if err ...
順代一提,Apache Benchmark ab test 連線數量多的時候連線數量並不準確,
像是 -n 1000 -c 10 可能實際上送了 1007 次 request 之類的




2013年6月25日

CORS and JSONP

第一次遇到cross origin resource sharing問題是在2010年的時候,我配合amazon affiliate program做了一個很陽春廣告聯播系統,我提供一組js,嵌入這個js的網站可以展示我選的商品,當時就是用JSONP做的,而最後其實也只放在我的一個網站上而已,那時我深信我挑選的商品CPA獲利會比google的廣告CPC還要好,不過後來證明我大大的錯了,一個商品都沒有賣出,自製的廣告聯播系統的開發也就沒有再開發了。


CORS

Cross origin resource sharing,CORS 主要是為了可以取得 cross-domain 的資源,阻止我們用javascript 直接取的 cross-domain 遠端資源的主因是 same origin policy,CORS 提供一種方法可以讓 server 決定是否允許這種 cross-origin 的 request ,這比較麻煩,但是比沒有 same origin policy 來的安全。

CORS 的實作方式比較麻煩,主因是一些特立獨行的瀏覽器的緣故,現行最新的瀏覽器都提供了 XMLHttpRequest 物件,但是IE8自創了一組 XDomainRequest 物件逼你使用,一般檢測方式是看 XMLHttpRequest 創造出來的物件實例是否含有 withCredentials 屬性,沒有的話就要撤退到XDomainRequest,要注意兩者 callback function 返回的參數不同,要另外處理。

再來就是更有古早味的瀏覽器Opera10, < IE 8, < FireFox 3.5,沒有這些功能,必須要用flash來hack,要是遇到沒裝flash的話,基本上就gg。所以 CORS 對於老瀏覽器的支援並不好。另外在server端也要配合,處理這樣的 request,就是我們以前會看到的 crossdomain.xml ,放在domain 根目錄,e.g mydomain.com/crossdomain.xml。

crossdomain.xml 要設定

<?xml version="1.0"?> <!DOCTYPE cross-domain-policy SYSTEM "http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd"> <cross-domain-policy> <allow-access-from domain="*" /> <allow-http-request-headers-from domain="*" headers="*" /> </cross-domain-policy> 

並且需要修改request 回應的 header


Access-Control-Allow-Methods: GET, POST, OPTIONS Access-Control-Allow-Credentials: true Access-Control-Allow-Origin: http://domain-of-the-resource.com Access-Control-Allow-Headers: Content-Type, *

要注意的是Access Control Allow Origin不能使用 wildcard ,IE說不行


JSONP

JSON-P 支援所有瀏覽器,他是利用呼叫遠端的 javascript function 來達到 cross-domain ,這個 function  在回傳時候會被執行,我們可以藉此來或的包覆在其中的資料,要注意的是被包覆的資料不一定要是 JSON 格式,有時候我們不會使用 JSON ,而是返回字串,因為原生JSON 支援是在 IE8+, Firefox 3.1+, Safari 4+, Chrome 3+, Opera 10.5+ 才有的。

server 端的回應處理很簡單,用收到的 callback function name 消毒後把原本的回應包起來就可以了,這裡要小心回應的XSS攻擊,因為 callback function name 可能是任何字串,也可能是一段程式。

由於引用了一段 resource server 的程式碼,resource server 基本上可以對網站做任何事情,使用JSONP 必須要完全信任 resource server 的安全性。

另外要注意的是,JSONP只支援GET request,而且也要小心CSRF,登入的使用者在造訪惡意頁面的時候會被獲取和操作在 resource server 上的資料,由於使用者造訪的網頁不是 resource server 所產生的,所以並沒有一個簡單的方法來防止CSRF,因此第一,不要放敏感的資料,敏感資料使用一般方式傳遞,第二,通常的配合方法是你給這些 partner 網站一組public/private key pair,他們在 request 的時候把 public key 和加密過的訊息傳給你驗證,並且整個流程透過 ssl 來使用。







2013年4月12日

動態提供網頁字型開發筆記 - fonten

我在做SharePoster的時候遇到一個問題,我想讓使用者可以使用不同的字型在canvas上面寫字,但是中文字型檔太大,直接載入瀏覽器會太久,所以如何動態提供字型給canvas作圖使用呢?

問題可以分成幾個部分:

1. 動態切割字型檔
   
     一個完整中文字型檔大概要5~20mb,假如我們只使用幾個中文字就要下載全部檔案很不划算,所以如果可以讓server提供一個根據需要的文字來提供瘦身過的字型檔,這個字型檔可能只有幾kb,是原來千分之一的大小,專門用來顯示特定的文字所需要的字型。

     實際上已經有一些open source project提供這樣的切割能力,像是FontForge (Python), Sfntly (Java)。



2. 根據所需要的文字要求字型檔
 
     一般比較正常的作法是這樣,第一次送post給server告訴server準備哪些字,換取一個hash token,如果這裡是使用jQuery cross-domain post,發post之前會先發一個options詢問server是否允許哪些method的cross-domain request,確認許可之後才會發post,client根據這個token組成一個url交給css的font-face,server端必須藉由Access-Control-Allow-Origin和Access-Control-Allow-Method兩個header來回應cross-domain的請求。

     這裡需要一些改進,我原本以為字型可以像圖片一樣使用javascript拿到字型的object,或許html5要可以,事實上是無法的,規則的制定者看來沒有這樣的需求,我們只能夠組成url交給css去處理,其實這樣造成了request的浪費,比較好的狀態應該一次request就能夠拿到字型,所以在實作上我加了另一種方法,當需求文字少的時候,在client端組成font url就直接較給css處理,這樣就少了options和post這兩個request,只剩下css會發出的get request,但是換來的限制是url長度大概要在2000以下比較安全(不管舊版IE的話至少也要在4000以下)也就是encode 之前大概200個中文字比較保險。

     然後我們用一個jQuery plugin把這些方法包起來。



3. 使用字型

     還有一個問題,什麼時候可以使用字型了?

     字型載入需要時間,而且我們沒有任何獲得css font request的callback或類似onload事件的方法,如果在字型載入之前使用該字型,我們只會得到瀏覽器備用的字型(fallback font),不過或許我們可以利用這個機制來測試字型是否載入了,無法拿到event就polling吧,當字型載入的時候文字寬度和fallback字型的寬度會不同,就用這樣來測試字型是否載入了。

     這個方法也把它包在jQuery plugin裡面。


綜合以上這是一個我目前正在開發的開源項目fonten,他同時包含一個可上傳字型的server和一組API以及一個比較方便的jQuery plugin。

fonten on Github



2013年2月25日

HTML5 Canvas 畫圖 Image and Text 筆記

我想要讓使用者可以使用網站上提供的圖片或是上傳圖片後,可以在圖片上加上文字組合成一個情境,正在想要怎麼做。

1. 可以在server上完成圖片合成,好處是沒有瀏覽器問題,字體的支援也很好控制,缺點是很可能不是WYSIWYG,斷行的位置可能會有點難處理,考慮到不同語言和字體,在server比較難模擬一段文字在瀏覽器斷行的位置,使用javascript抓斷行位置需要用span把每個字包起來計算,麻煩;還有沒辦法直接做功能延伸,比如說增加功能讓使用者用畫筆作畫,這部份到時候還是要改用HTML5 canvas來做,還有文字的邊框python image library沒有直接支援,要用ImageFilter來做。


2. 在client 使用html5 canvas來作圖,缺點是ie9之後才有支援canvas,瀏覽器兼容問題可能會很棘手,還有輸出圖片的時候可能還有一些canvas的security要處理,要轉成base64或是先傳到server,字體會有環境限制,要如何知道目前有什麼字體可以使用呢,尤其是非英語系的文字,有看過分體系用fallback機制+字體長度計算,也有用flash外掛計算的(wordmark),斷行位置還是一個問題,使用cavas作圖之後無法移植到非瀏覽器環境。

在client也可以使用true font

@font-face { font-family: 'KulminoituvaRegular'; src: url('http://www.miketaylr.com/f/kulminoituva.ttf'); }

2013年1月18日

AngularJS service, factory, provider 的差別


$provide.provider

$provide.provider is the most spohisticated method of registering blueprints, it allows you to have a complex creation function and configuration options.

You will be provided with new ProviderFunction().$get(). The constructor function is instantiated before the $get method is called.


$provide.factory

$provide.factory is a simplified version of $provide.provider when you don't need to support configuration options but still want to have a more sophisticated creation logic.

You will be provided the value that is returned by invoking the function reference passed to module.factory


$provide.service

$provide.service is for cases where the whole creation logic boils down to invoking a constructor function.

You will get an instance of the function. In other words new FunctionYouPassedToService().



範例:
http://jsbin.com/ohamub/1/edit

2012年10月19日

增加網頁讀取速度

這篇主要討論靜態檔案下載,首先先裝Google PageSpeed ,有chrome版和firefox版,而且有貼心的正體中文,也可以用一些測速網站工具像是http://tools.pingdom.com/fpt/來當做評量參考,真的只要參考就好,拿個90分就可以了

簡單說增加網頁讀取速度不外乎平行下載、壓縮、快取和把檔案放近一點

先挑一個dynamic js loader來做平行下載
超多的,百家爭鳴,但是每個套件製作的背景目的不同,也適用不同場合,大部分套件都可以幫忙做到parallel loading, conditional executing,有些還會動態幫忙讀取css,任君挑選,不會選就先用$LAB爽一下,有在用RequireJS的當然繼續用,需要fall back機制可以用YepNope

這裡我有一個問題,傳統上認為靜態檔案合併比較好,因為節省訪問和處理時間,但是當平行下載是可行的(瀏覽器現在根本都原生支援平行下載),在給定延遲時間和反應時間下,應該會有一個檔案大小的臨界點,超過這個臨界點之後整體下載速度就會輸給平行下載了吧?所以這個臨界點是我們需要關心的嗎?

另外檔案合併有一個明顯的壞處,當修改部分檔案的時候,使用者必須重新下載整份檔案,喪失快取的好處,所以什麼時候要合併哪些檔案還是要自己思考一下

『壓縮』的部份主要是compress和gzip

compress css 大家應該都用scss/sass, compass或是less了,當然也可以用線上工具像是
http://www.csscompressor.com/

compress javascript首推uglify-js,自己寫個bash來做combine,也有一些線上工具可以使用http://jscompress.com/

gzip和cache就交給nginx來設定。設定cache的時候有個兩難,一般來說會設定有query_string的request做cache,不然萬一檔案需要修改,又清除不了瀏覽器cache,可能要改路徑或檔名了,但是另一方面有些proxy對於有query_string的請求不會做cache,所以也是要思考一下

『檔案放近一點』原來說的是用類似Google CDN的服務,只有非常有名的套件像是jQuery之輩有救援到。自從Amazon S3+CloudFront出現後,一般平民也可以是可以享用高級全球化部屬,其中js和css等當然也可以壓縮後放在S3上面,但是有個小問題就是S3到目前還不會做gzip,摳憐喔,解決方法就是直接先gzip -9之後再丟上去,順便改一下Accept-Encoding就行了,但是衍生的問題是有些使用者還在用民國初年不會解壓縮的瀏覽器,這部分就是需要偉大的開發者使用愛心判斷式+fall back strategy來救使用者於水火了。

以上方法僅供參考,沒事不需要做網頁讀取加速,一方面網站會慢通常不是靜態檔案在慢,而且沒用的網站也不會因為加速2秒後就突然變得有用了。




2012年7月30日

比較Backbone.js, Angular.js, Ember.js, Knockout.js 心得

還記得第一次寫網站的時候,我無意間寫成了 SPA(single page application),當時還沒有SPA這個詞,後來因為廣告主需要不同 url location 頁面的廣告展示,只好把部分網站改成一般網頁。但是一直以來我對於提高整體溝通效率以及使用端的回應效率抱持相同的想法,希望能夠把更多回應放到 client 處理,讓server 做好 api 提供者的角色,可以讓網站回應更即時而親切,這篇心得文不會詳細走訪每個技術層面,而是提供概括的入門使用情況。

現在client-side javascript MV*(MVVM, MVC等) framework實作產量豐富,Backbone.js, Spine.js, Angular.js, Ember,js, Knockout.js, Batman.js, Javascript MVC, SproutCore.js, Cappuccino.js 好像每個高手都要發明一套才行,不過其實各框架的目的不盡相同,其中我比較感興趣的是Backbone.js和Knockout.js 還有強大的Angular.js和 Ember.js,大家在玩的時候可以比較一下他們的todo list是怎麼做的,todo list範例已經是新框架誕生時要遵守的"行規"了。



Backbone.js

Backbone.js大家絕對都聽過,有廣大的社群支持者,文件和教學很完整,使用他第一次讓我可以把javascript event整理成清楚好維護的方式。雖然我比較常獨立栽種程式碼,但是我相信一個網頁前端團隊合作用Backbone.js會是很輕鬆的,module的切割加上他幾乎沒有改變html原有的寫作方式,不會嚇跑負責html和css的夥伴或任何人,可以有乾淨的工作區分。

Backbone.js在功能上可以說是提供了比較...基礎的功能協助,其實可以說什麼都沒做吧...,相較於在使用其他框架時,會面臨需要思考如何把強大的功能"降級",這很痛苦,Backbone.js讓我可以build from scratch,這往往更容易完成我想要的功能也更容易除錯,也很容易改造一些原始運作,但是我有時候發現程式碼變得非常龐大,而且事必躬親,事情似乎被複雜化了,我常想這是必要的代價嗎?  使用到後來發現我愛的其實是Underscore.js吧,Underscore.js程式碼的品質很好。

其他Backbone.js相對比較好的地方像是collection的概念,還有整個框架檔案很小,缺點(特點)就是沒有data-binding,有人試圖幫他做了一個,最後效能太差收掉了。

補充一下基於Backbone.js的兩個成熟框架,Marionettejs和Chaplin。由於Backbone幾乎只滿足了前端框架的最基本需求,這兩個框架則解決實際的使用上的問題,像是清除event-listener(lifecycle),還有更好的應用架構支援和Controller,而不是只有單純的Router獨挑大梁,以及有更完整的View繼承和預設render方式,和整合AMD支援等等。



Angular.js

Angular.js和Ember.js讓我第一次發現原來View和Model可以做binding,而且是透過非常簡單的方式,簡直美好,雖然Knockout.js也有data binding,但是就是沒有Angular.js用起來直覺。

Angular.js提供的template樣版也是在我用過的javascript MV*當中最喜歡的,用起來有點像是Jinja2的感覺。

Angular.js還提供簡單的方式讓你自己做directive,可以想像在UI當中加入一個小東西就能完成常用的功能。

Angular.js的routing也非常簡單易懂,但是太過簡單以至於只能用作者希望的方式來用,加上他本身用singleton模式,只能有一個app instance,讓我無法在一個route中同時作出複雜的動作,或是更新多個View,目前無法符合我的需求(更新:後來我透過directives完成了,directives是一個能夠讓你做出類似網頁widget的強大功能),不過作者說不適合在1.0時釋出太複雜的東西,所以我們就持續關注吧。

令人驚訝的是,Angular.js自帶一組強大的end to end testing工具,而且非常的簡單好懂。

另外Angular.js和Ember,js, Knockout.js同樣潛在的缺點(特點)是,有些人可能不希望程式的邏輯跑進html中,但是我認為簡單繁瑣的邏輯應該允許放在最合適的地方,進一步了解可以看一下Angular.js作者在Quora上的見解,這點就見仁見智。

整體而言我對Angular.js的未來非常看好,現在在Google的CDN上也可以取得了,可見Google對他的重視,預計九月初會釋出1.2版。


Ember.js

一樣是後起之秀,Ember.js是從SproutCore.js出來又從Amber.js改頭換面成了Ember.js。這是和我初始想法最接近的Javascript client-side framework,寫起來很有感覺,有時候寫程式真的不需要選什麼大家都在用還是評價最高的東西,選一個和自己頭腦思考方式最接近的就是最好的,這點也是因人而異的。

Ember.js的Model是我最喜歡的方式,Backbone.js相對顯得囉嗦和貧乏,Model中的computed property可以選擇連動的屬性,one-way or two-way binding可以自由搭配,他使用的Handlebars.js template比Underscore.js和Mustache.js提供的都要更好用,我建議Backbone.js使用者也可以看看Handlebars.js,他在功能和簡單兩邊取得了平衡,算是非常不錯的javascript template引擎。

雖然我舉雙手贊成以後template盡量都移到client-side來做,但是我必須說,現在所有的client-side template功能和server-side經年累月琢磨出來的template都還比不上,其實Model和Controller也是,還有很多可以加強的功能,因為還要考慮client裝置的能力和網路環境的限制,不能像server端的東西包山包海。至少現在Angular.js和Ember.js看起來是站在巨人的肩膀上。Ember.js在attribute binding功能上更勝一籌,Angular.js目前的1.0版則較為簡單易用。

Ember.js還有一個非常特別的地方是Finite State Machine Routing,他原本一直沒有提供routing功能,後來才透過他的State實做了routing,目前官網還沒有很好的文件。和其他框架都不同的是,他的routing是可以和location脫鉤的finite state machine,但是如果你希望,他也可以在改變state時自動幫你更新location,非常強大,我只能說Ember.js野心太大,希望不要搞砸,可以看一下原本幫Ember.js寫RouteManager外掛的作者的這篇來了解中間的不同,State Machine的設計概念我是支持的,軟體流程可以做的比較清楚,但是目前整體routing語法太冗長、贅字太多這點有待改進,View裡面的outlet的局限性也是我用起來覺得不順手的地方,算是個缺點吧。

另外Ember.js最大的缺點就是文件不齊全或沒跟上最新版,學習時間比其他框架要長一點。



Knockout.js

Knockout.js 很久以前就被facebook推薦做為mobile web前端的框架,我覺得Knockout.js算是比較成熟的MVVM框架,他不試著包山包海,他迷人之處就是他專心做好data-binding,然後在這之上搞好template,因為他很專一和成熟所以他有很多細心的地方,像是你可以在data-binding的過程中很容易的界接jquery的aniamtion,很多其他框架就只是幫你即時做binding而,還有如何從jquery中調用Knockout.js的value和context, Knockout.js 給使用者很大的自由,甚至他還想到怎樣做可以更方便的把UI上資料同步遠在server的資料庫(for Rails),另外在template上也有許多巧思可以體會到他設想周到。

Knockout.js的缺點就是我不喜歡他的語法,雖然自己進去改一些東西可能也能解決,但這會讓我覺得我和作者的靈魂不夠接近,這是很嚴重的問題,而且他太成熟了,不是一個追逐最新潮流的碼農可以拿出來炫耀的工具,這也是很嚴重的問題,哈。

由於Knockout.js 沒有routing功能(有routing功能的框架通常官網也會直接展示),我們可以使用Crossroads.js, Director.js或是Sammy.js,三個都很不錯,Director.js是Server和Client兼具,Sammy.js是我一直在關注的工具,他一樣專注在一件事情上,我很喜歡他的語法,用起來有jquery和server-side routing結合的感覺,不像很多框架在語法上都喜歡自成一格,模仿jQuery語法相當明智,而且現在也有html5 history support了,可以和Knockout.js結合的很好。

另外還有個有趣的東西叫做Knockback.js,他是Backbone.js和Knockout.js產下的混血兒, Knockback.js 把Backbone.js的ORM, routing, serialization和Knockout.js的MVVM data-binding各取所長的結合在一起,我是不敢用基因改良的東西啦,我怕會有基因突變,期待有人分享心得了。



延伸閱讀:

如果不怕英文的人我建議看Javascript Web Application
另外所有寫Javascript的人我都強烈建議要讀Javascript 設計模式












2011年12月26日

Longest Common Subsequence

沒想到有一天實際上會用到這個問題
用wiki上面的演算法實作javascript

var LCSTable = function(X, Y){
//Longest Common Subsequence
X.splice(0,0,0);
Y.splice(0,0,0);
var C = new Array(X.length);
for(var i=0; i<X.length; i++){
C[i]= new Array(Y.length)
for(var j=0; j<Y.length; j++){
if( i*j === 0){
C[i][j] = 0
}else if(X[i] === Y[j]){
//element in common
C[i][j] = C[i-1][j-1] +1;
}else{
C[i][j] = Math.max(C[i][j-1], C[i-1][j]);
}
}
console.log(C[i])
}
return C;
}
var LCSTraceback = function(C, X, Y, i, j){
console.log(i +','+ j)
if(i === 0 || j === 0){
return []
}else if(X[i] === Y[j]){
var LCS = LCSTraceback(C, X, Y, i-1, j-1)
LCS.push(X[i])
return LCS
}else if(C[i][j-1] >= C[i-1][j]){
return LCSTraceback(C, X, Y, i, j-1)
}else{
return LCSTraceback(C, X, Y, i-1, j)
}
}

X=[2,3,4,5,8,9,8,7]
Y=[4,5,6,9,8,5,7]
C = LCSTable(X,Y)
LCS = LCSTraceback(C, X, Y, X.length-1, Y.length-1)
console.log(LCS)
[4, 5, 9, 8, 7]