2012年10月28日 星期日

網站架構思考


最近在研究 Ember.js,開始在思考網站的架構.....
在講故事前,先名詞解釋一下
前端技術:就是瀏覽器端的語法,包含 HTML,CSS,JavaScript...等
後端技術:就是伺服器端的技術,包含 Servlet、PHP、ASPX...等

在1990s與2000s初那美好單純的古代時,前端技術很單純,Html就那幾個 tags ,
JavaScript 也只是用來作做簡單的動態效果,人們還特地替它起了個名字,叫 DHTML,呵呵!
CSS??好像還沒發明....
所以哪有什麼前端工程師,碼都是一個人全部包
網頁Layout??那是美術的工作!!

後來發明了CSS,AJAX技術,一整個前端的 loading 突然加重起來
叫美術寫 CSS ,嗯...簡單的可以,複雜的...再看看
叫美術寫 AJAX,使用 JQuery,他(她)應該會回你,讓我死了吧~~
這幾年CSS與JavaScript技術的應用益加複雜,JavaScript的framework都比Android版本還多了
CSS也邁入CSS3,再加上HTML5(New HTML tags, New javaScript 語法)
再叫一個RD做後端兼做前端,不但不人道,也會使得效率低下
畢竟頻繁地切換撰寫的語言,是一件很累人的事,對語言也不容易精進
所以工作切開來了,有前端工程師,負責 CSS、JavaScript、HTML等前端語言的撰寫
後端工程師,寫 Web Server 端 的Code

故事就從這裡展開....

傳統的網站流程方式如下

A: 使用者(Browser)向 Server 發出要求
B: Server 端接受使用者端傳來的要求,相關的參數
C: Server 處理要求,可能是經過一段運算,向 DB 查詢相關資料
D: 將資料填入版型檔中,以 Java 來說就是使用 JSP 或 FreeMarker 等技術
E: 將產生的檔案(HTML) 回傳
F: 顯示在使用者端

Tempate 是版型檔,用 FreeMarker舉例就像這樣

FreeMarker Engine讀取了Template file,然後 parsing 裡面的freeMarker語法
比如${name},它就會去記憶體中找名為 name 的物件,然後將它的值取出,
取代掉 template 中的 ${name} 字樣,最後輸出 HTML

發現了嗎?HTML是在Server端產生的,然後吐回去前端瀏覽器
HTML應該是前端工程師負責的,但是卻在Server端產生,
而Server端的Code是後端工程師負責的

所以這一段到底應該誰寫,前端工程師的話,那他要再去學一個 Template 語法(好辛苦呀~~)
後端工程師的話,他(她)就變成要先等前端工程師網頁寫好,然後再去修改它,多做一次功

另一種網站流程如下,基本上分兩個 Phase
Phase1取得HTML的框
Phase2取得裡面的肉,然後將肉填入框內,肉就是 Content

A: 發出一段要求
B: 取得 HTML 框,以及取得肉的 Url與相關的JavaScript code
C: 發出 AJAX要求
D: Server接到要求,作處理,查詢資料...新增資料...anyway
E: 將結果回傳,一般是 JSON格式,用來簡化資料量
F: 前端將Phase 1取得的框,解析後,將Data填入該填的位置,顯示出來

聽不懂????
沒關係,我舉個例子
比如說是個書店網站,首頁想要顯示這個月新進的書
網址是 http://www.coolbookstore.com
然後回傳


這是 Phase 1 ,Browser取得了HTML框架,裡面還包含了接下來該做的動作,
放在 dom ready 裡面
Phase2 就是呼叫 $.get(...) ,Sever收到後處理,回傳新書資料,
Browser取得新書資料,然後與 Phase 1 收到的相結合變成完整的資料
呈現給使用者

這種流程有什麼好處?
Serer工程師可以專注在打造API,API就做它該做的事,不需理會 HTML
http://www.coolbookstore.com/getnewbooks就查詢新書資料,然後將資料使用JSON回傳
http://www.coolbookstore.com/deletebook 就刪除書籍資料,然後執行結果(成功/失敗)回傳
http://www.coolbookstore.com/editbook 就修改書籍資料,然後執行結果(成功/失敗)回傳
等等 API
Server工程師專注在打造API,專注在API的執行效率,幾毫秒內回傳,專注在load balance,cache等 issue
不需管HTML,不需管前端的Device是什麼,前端就算是一般的應用程式也行,呼叫API後Pasing JSON,顯示出來即可
網頁前端工程師專注在前端的Code,不用學Server端的 template 語法
(實際上還是要學新東西來幫助Parsing JSON更方便,題外話....)
整合也很容易,兩邊先講好回傳的格式,前端工程師可以先寫假資料


parsing 假資料就可以模擬最後的 UI 呈現,等後端工程師 API 寫好,再去呼叫 API 取得真的資料即可
後端工程師因為吐出來的東西都是JSON,自我驗證也相當方便,只要在瀏覽器打 API 網址即可驗證功能是否正確

難道沒有缺點嗎??
有!!!
1. 過多的 request 呼叫,影響效率
2. 前端 code 破碎,影響維護性
3. Cache Issue

剛剛的例子是在首頁顯示新書列表,但是首頁可能有許多資訊要顯示,
訂單狀態、會員資料、廣告...一堆
每一個都是一個API,一個API就表示需要一個 AJAX request 呼叫
傳統流程,一個 request,Server處理完,回傳 HTML,前端顯示,打完收工
這個流程,可能前前後後需要發出7,8個request,才可以完成顯示首頁的工作
人數一多,天!request的數量是首頁人數*7

前端的需要負責 parsing JSON,組合HTML,程式碼就會很零碎,不好維護修改
目前看到的是可以用HandleBars、Ember.js 等技術來解決

Cache Issue,講得是 Server 端的 Cache
傳統的流程,Server可以將處理最後產生的結果網頁 html 存成檔案
比如首頁其實是查詢了10個Table所產生的結果
Server可以將產生的結果存成 HTML 檔案
瀏覽器端讀的是這個 html 檔,html 是靜態檔案
瀏覽器端會自動快取
這樣只要 Server 端的 html 檔沒有改變的情況下(檔案時間與Local端的檔案時間相同)
瀏覽首頁時,瀏覽器會自動使用Local端的cache,可以減輕 Server 的負擔
但是AJAX是動態結果,它不是個檔案,它沒有靜態檔案的快取機制可以利用

很兩難是吧!我也還沒想出個解答來....@@

2012年10月19日 星期五

Play framework 開發自己的module

因為網路上的資料都寫的不清不楚,索性自己來寫一篇!

什麼是 Play framework ?
什麼是 module ?

去 google 一下,官網都會講得很清楚。
本篇重點在如何開發及設定自己的 module
我會一步一步來
先教大家怎麼new module
怎麼將 module load 到自己的 application
最後再教怎麼寫 module
  • 環境說明:
  • OS: Win 7
  • Play Version : 1.2.5
前提假設:原有專案目錄在 D:/playcode
  1. 開啟命令列,在 D:/playcode 下,鍵入命令 play new core-app
    這將會建立 core-app application
  2. 在 D:/playcode 下,鍵入命令 play new-module auth
    這將會建立 auth module
  3. 在 D:/playcode/auth/ 下鍵入命令 play build-module
    這將會編譯 auth module
    產生 auth-0.1.zip 位於 D:\playcode\auth\dist\ 下
  4. 在 D:/playcode/core-app 下 建立資料夾 module
  5. 複製 auth-0.1.zip 到此資料夾下
  6. 編輯 D:/playcode/core-app/conf/dependencies.yml 檔案
  
# Application dependencies

require:
    - play
    - auth -> auth

repositories:
    - My modules:
        type:       local
        artifact:   ${application.path}/../[module]
        contains:
            - auth 
  
別忘了存檔 !!
7.最後在 D:/playcode/core-app 下鍵入命令 play dependencies
這樣 application core-app 就成功載入 auth module 了

OK !! 先休息一下!! 怎麼寫 module 等我試完在補上!!

2012年9月26日 星期三

景氣燈號與股市--奇怪的現象

今天看到了一則新聞


文中說明天要發佈的景氣對策訊號,預期將出現連續第10個月的藍燈,比金融海嘯時期的連9個月藍燈還要多
一般而言,當景氣好的時候股市也會好。反之亦然, 股市好的時候,代表景氣就不錯。
但是這是一般情況,但是,現在這個世界完全不能用一般情況去理解
讓我們看一下股市的月K線圖。
下圖顯示金融海嘯時加權股價從9千多跌到近4千,這是燈號連9藍的時候。
現在燈號連10藍,股價應該要比當年金融海嘯時更低才對,
但是現在股價位於7~8千點之間,幾乎是金融海嘯時期的兩倍!
完完全全的不合理。
簡單講就是,『股價高高掛,人民苦哈哈!』


至於為什麼會有這種現象呢?老實說我也不是很清楚,只能猜測是國際的熱錢炒作出來的股價,
因而沒有反應真實的經濟情況。
國際熱錢哪來的,美國的QE1,QE2與QE3
當美國今年底大選後,台股可能會面臨一波大修正潮,來符合實際的經濟情況。

2012年8月9日 星期四

Tomcat Data Source 設定


Tomcat Data Source 設定

Data Source

DataSource的概念來自於當網站程式在操作資料庫之前,需要先跟資料庫取得一個連線,然後使用此連線做 query, insert, update, delete 等操作,操作完成後就將此連線歸還給資料庫。這種動作少的話還沒關係,但是一旦頻繁起來,因為跟資料庫取得連線需要時間,就會有效能的考量。所以就有人想說,何不先跟資料庫要求一定數量的連線,放在某個稱為Connection Pool的容器內,當網站程式需要操作資料庫時就跟此容器要求連線即可,用完時就將連線歸還予此容器,省卻每次都要跟資料庫要求連線的時間耗損,增進程式效率。
資料庫只要有JDBC的驅動程式,Tomcat就可以透過JDBC跟資料庫溝通,來取得連線。因此首先要確認使用的資料庫是否有實作JDBC的函式庫可以下載,接著就到$CATALINA_HOME\conf\server.xml 內設定 Global JNDI 資源,設定帳號,密碼,連線位置等相關資訊,最後到$CATALINA_HOME\conf\Catalina\localhost\你的Web應用程式名稱.xml,設定 ResourceLink,連結到剛剛設定的 Global JNDI 資源即可。

Tomcat 設定

server.xml

主要就是設定 server.xml 裡頭的 Global JNDI Resource
  1. <?xml version='1.0' encoding='utf-8'?>
  2. <Server port="8005" shutdown="SHUTDOWN">
  3. <Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
  4.  <!--略 --> 
  5.  <GlobalNamingResources>
  6.     <Resource name="cosmoDS" 
  7.       略..."/>
  8. </GlobalNamingResources>
  9.  <!--略 --> 
  10. </Server>
在 GlobalNamingResources 內定義一個 Resource 元素,Resource 內有一些值需要設定,簡易的設定如下
  1.     <Resource name="cosmoDS" 
  2.       auth="Container" 
  3.       description="Cosmo DB Connection JNDI"      
  4.       type="javax.sql.DataSource"
  5.       username="root"
  6.       password="mypass"
  7.       driverClassName="com.mysql.jdbc.Driver"
  8.       url="jdbc:mysql://127.0.0.1:3307/cosmo?zeroDateTimeBehavior=convertToNull&amp;noDatetimeStringSync=true" 
  9.       maxActive="100"
  10.       maxIdle="10"
  11.       maxWait="10000"/>
  • name: 資源名稱
  • auth: 填入"Container"
  • description: 說明
  • type: JNDI 型態,在這裡是DataSource,因此填入"javax.sql.DataSource"
  • username: DataBase登入帳號
  • password: DataBase登入密碼
  • driverClassName: JDBC Driver Class Name
  • url: 連線字串
  • maxActive: Pool 內所能容納 DB Connection 的最大數量,-1 表示無上限
  • maxIdle: Pool 內所能容納狀態為 idle 的 DB Connection 的最大數量,-1 表示無上限
  • maxWait: 等待 Connection 變為 available 的最大時間(ms),範例中為10秒,-1 表示無上限
簡易的設定會動,但是在實際上線時可能會遇到一些問題,最常見的就是 abandoned connection。當程式向 connection pool 要求一個 connection,使用完後卻沒有close,此 connection就會變為 abandoned ,它在 connection pool 內佔了一個位子,但是卻沒有程式可以再向 connection pool 要求此 connection 來用,假如這種情形持續發生,最後 abandoned connection 就會到達設定的 maxActive 數值,程式就當了,pool 內沒有任何一個 connection 可以再被使用。另外也有可以改善 performance 的方式,比如當 connection 放在 pool 過久時可以將它還給 DB,因為 DB 也是有設定連線數的上限;程式向 pool 要求 connection 時先測試此 connection 使否可用...等。
所以建議的設定為
  1. <Resource name="cosmoDS"
  2.           auth="Container"
  3.           description="Cosmo DB Connection JNDI"   
  4.           type="javax.sql.DataSource"
  5.           username="root"
  6.           password="mypass"
  7.           driverClassName="com.mysql.jdbc.Driver"
  8.           url="jdbc:mysql://127.0.0.1:3307/cosmo?zeroDateTimeBehavior=convertToNull&amp;noDatetimeStringSync=true"
  9.           maxActive="100"
  10.           maxIdle="10"
  11.           maxWait="10000"
  12.           initialSize="10"
  13.           removeAbandoned="true"
  14.           removeAbandonedTimeout="20"
  15.           logAbandoned="true"
  16.           validationQuery="select 1"
  17.           testOnBorrow="true"
  18.           testOnReturn="false"
  19.           timeBetweenEvictionRunsMillis="1800000"
  20.           minEvictableIdleTimeMillis="3600000"          
  21.           numTestsPerEvictionRun="10"          
  22.           testWhileIdle="true"
  23.           />
  • initialSize: 當 pool 初始化時會創建的 Connections 數量,預設為 0
  • removeAbandoned : 會刪除 abandoned Connections,預設為 0
  • removeAbandonedTimeout: 一個 Connection 變為 abandoned 後多少秒,此 Connection可以被移除
  • logAbandoned: log which code open connection but not close it, log in catalina.out
  • validationQuery: SQL query that can be used by the pool to validate connections before they are returned to the application
  • validationQueryTimeout: Timeout in seconds for the validation query to return. Default: -1 (infinite)
  • testOnBorrow: true or false: whether a connection should be validated using the validation query each time it is borrowed from the pool. Default: true
  • testOnReturn: true or false: whether a connection should be validated using the validation query each time it is returned to the pool. Default: false
  • timeBetweenEvictionRunsMillis: The number of milliseconds between consecutive runs of the evictor. Default: 30*60*1000 (30 minutes)
  • numTestsPerEvictionRun: The number of connections that will be checked for idleness by the evitor during each run of the evictor. Default: 3
  • minEvictableIdleTimeMillis: The idle time in milliseconds after which a connection can be removed from the pool by the evictor. Default: -1 (disabled)
  • testWhileIdle: true or false: whether a connection should be validated by the evictor thread using the validation query while sitting idle in the pool. Default: false

web application

假如你的web application名為 myapp,那麼就必須有一個相關的設定檔 $CATALINA_HOME/conf/Catalina/localhost/myapp.xml
  1. <Context path="/myapp" docBase="myapp" reloadable="true" crossContext="false">        
  2.     <ResourceLink name="myDS" global="cosmoDS" type="javax.sql.DataSource" />
  3. </Context>
ResourceLink 意思就是定一個此 web application 用的 resource,此 resource 連結自 global resource 的。在此例中就是定一個名為 myDS 的 resource ,連結自名為 cosmoDS 的 global resource, resource 的型態為 java.sql.DataSource。這樣的好處是數個不同 web applications 可以共用同一份 global resource 的設定,一旦 global resource 改了,所有 web applications 參考到的東西也就變了,你無需去每一個 web application 底下去修改其設定,這樣比較好維護,因為是有可能去修改其設定,比如說將資料庫由 mysql 改為 oralce ,或是資料庫搬家了, ip addresss 變了,套用上述的設定方式,你只需要去 server.xml 去修改 global resource 即可。

程式寫法

全部都設定好後,在程式中要取得connecton,寫法為
  1. Context inictx = new InitialContext();
  2. Context m_envctx = null;
  3. DataSource ds = null;
  4. Connection conn = null;
  5. Object tempObj = inictx.lookup("java:comp/env");
  6. if( null != tempObj)
  7.   m_envctx = (Context)tempObj;
  8. Object tempObj = null;
  9. try {
  10. 	tempObj = m_envctx.lookup("myDS");
  11. 	if(null!=tempObj)
  12. 	{
  13.   	  ds = (DataSource)tempObj;	  
  14. 	  conn = ds.getConnection();
  15. 	}				
  16.    } catch (Exception e) {
  17. 	e.printStackTrace();
  18.    }

MySQL 設定

  • 下載最新版的JDBC Driver
  • 將mysql-connector-java-x.x.x-bin.jar 放到 $CATALINA_HOME/lib/ 下
  • 設定 server.xml 的 Resource
  driverClassName="com.mysql.jdbc.Driver"
  url="jdbc:mysql://{ip address of DB}:{port number of DB}/{schema name}

MS SQL Sever 設定

  • 下載最新版的JDBC Driver
  • 將 sqljdbc4.jar 放到 $CATALINA_HOME/lib/ 下
  • 設定 server.xml 的 Resource
driverClassName="com.microsoft.sqlserver.jdbc.SQLServerDriver"
url="jdbc:sqlserver://{ip address of DB};databaseName={schema name};" 

  • 查詢 MySQL 的 系統變數
 show variables;
其中 maxConnection 為可以允許的同時連線數量
  • 單純查詢指令為
 select @@global.max_connections; 
  • 修改指令為
  set @@global.max_connections = 1250;