May
12th,
2015
Steps
Connect to Apple iTune
// iOS Http code
NSMutableDictionary *parameters = [NSMutableDictionary dictionary];
[parameters addEntriesFromDictionary:[credentials dictionary]];
// receipt is an object of my own making, but base64String just returns an
// NSString representation of the receipt data.
parameters[PURCHASE_RECEIPT] = [receipt base64String];
NSURLRequest *request =
[[AFHTTPRequestSerializer serializer]
requestWithMethod:@"POST"
URLString:urlString
parameters:parameters];
AFHTTPRequestOperation *operation =
[[AFHTTPRequestOperation alloc]
initWithRequest:request];
operation.responseSerializer = [AFJSONResponseSerializer serializer];
<snip>
[operation start];
// Json data
{"status":0,
"environment":"Sandbox",
"receipt":
{"receipt_type":"ProductionSandbox",
"adam_id":0,
"bundle_id":"<snip>",
"application_version":"1.0",
"download_id":0,
"request_date":"2013-11-12 01:43:06 Etc\/GMT",
"request_date_ms":"1384220586352",
"request_date_pst":"2013-11-11 17:43:06 America\/Los_Angeles",
"in_app":[
{"quantity":"1",
"product_id":"<snip>",
"transaction_id":"1000000092978110",
"original_transaction_id":"1000000092978110",
"purchase_date":"2013-11-12 01:36:49 Etc\/GMT",
"purchase_date_ms":"1384220209000",
"purchase_date_pst":"2013-11-11 17:36:49 America\/Los_Angeles",
"original_purchase_date":"2013-11-12 01:36:49 Etc\/GMT",
"original_purchase_date_ms":"1384220209000",
"original_purchase_date_pst":"2013-11-11 17:36:49 America\/Los_Angeles",
"is_trial_period":"false"}
]
}
}
Reference
Validating Receipts With the App Store
iOS7 - receipts not validating at sandbox - error 21002 (java.lang.IllegalArgumentException)
Validate Mac App Store receipt server side
Validating iOS In-App Purchases With Laravel
繼續閱讀
May
11th,
2015
##前言 為了要寫一個Web Service但是有可能會發生以下的需求: 不同Server,有不同的設定參數 不同的OS有不同的command set 想要快速的開啟或是關閉debug log 在有以上的需求的時候,一開始都是使用OS偵測或是configuration file來區隔.但是到後其實是希望能透過不同的build config能夠產生不同的binary. 決定研究了一下: go build 有以下兩種方式可以達到部分的效果. ##Go build -ldflags 這可以在go build的時候,先設定一些變數的名稱.通常我自己比較習慣透過OS環境變數來設定,然後程式裡面再去讀取. 在你的主程式裡面,可以先定義一個變數flagString: package main import ( "fmt" ) var flagString string func main() { fmt.Println("This build with ldflag:", flagString) } 透過外在go build來設定這個變數 go build -ldflags '-X main.flagString "test"' 這樣你的結果會是 >> This build with ldflag: test 這個方式可以直接設定參數,讓initialize value透過外部設定來跑. ##Go build -tags 透過go build -tags 可以達到加入不同的檔案在compiling time.由於這樣,你能夠放在這個檔案裡面的東西就有很多.可以是: 不同系列的define (達到ifdef的效果) 不同的function implement (達到同一個function 在不同設定下有不同實現) 以下增加一個簡單的範例,來達到不同的build config可以載入不同的define value. file: debug_config.go //+build debug package main var TestString string = "test debug" var TestString2 string = " and it will run every module with debug tag." func GetConfigString() string { return "it is debug....." } 請注意: //+build debug 前後需要一個空行(除非你在第一行) 另外,我們也有一般的設定檔 release_config.go //+build !debug package main var TestString string = "test release" var TestString2 string = " and it will run every module as no debug tag." func GetConfigString() string { return "it is release....." } 最後在主要的main裡面,可以直接去參考這兩個define value...
繼續閱讀
May
6th,
2015
(pic: source from here) 以上的圖是利用Redis 達到 Message Queue 的方式,也是Disque要達到的事情. ##前言 這一兩個月,比較常聽到大家討論的就是Disque的使用方式與疑問.本來我對於Message Queue的系統(尤其是backend那一塊)比較不熟.於是還是花了一點時間把Disque裝起來,並且把sample code跑了一下.希望對於基本架構有一些了解. ##關於Disque Disque 是 Redis原作者Salvatore Sanfilippo根據大家在Redis上面針對Message Queue處理的部份來加強,並且下去拿Redis的source code加以修改,改造出這套專門處理Message Queue的系統. 主要特色如下: (參考這裡) 消息發送可以選擇至少一次或者最多一次。 消息需要消費者確認。 如果沒有確認,會一直重發,直至到期。確認信息會廣播給擁有消息副本的所有結點,然後消息會被垃圾收集或者刪除。 隊列是持久的。 Disque默認只運行在內存裡,持久性是通過同步備份實現的。 隊列為了保證最大吞吐量,不是全局一致的,但會儘力提供排序。 在壓力大的時候,消息不會丟棄,但會拒絶新的消息。 消費者和生產者可以通過命令查看隊列中的消息。 隊列盡力提供FIFO。 一組master作為中介,客戶端可以與任一結點通信。 中介有命名的隊列,無需消費者和生產者干預。 消息發送是事務性的,保證集群中會有所需數量的副本。 消息接收不是事務性的。 消費者默認是接收時是阻塞的,但也可以選擇查看新消息。 生產者在隊列滿時發新消息可以得到錯誤信息,也可以讓集群非同步地複製消息。 支持延遲作業,粒度是秒,最久可以長達數年。但需要消耗內存。 消費者和生產者可以連接不同的結點。 ###優缺點與比較: 優點其實蠻容易被瞭解的: 容易安裝使用,而且小.本身就有資料庫(類似 redis) 當不需要太複雜的傳輸格式與介面的時候,disque效能應該不差 (base on Redis v.s. RabbitMQ) 幾個缺點,可能需要注意: disque 目前還是alpha disque 目前只有單線程 關於與Kafka的比較,Salvatore Sanfilippo在他的推特有以下有趣的回應: Salvatore Sanfilippo: Disque VS Kafka is the new Redis VS PosgreSQL which was the new Apple VS Orange :-) ##安裝與使用 ###安裝與使用Disque git clone https://github.com/antirez/disque make make test (make sure port and compiling result success.) cd src run disque-server ###簡單的操作 當你跑disque之後,就會自動連接local disque server. //Add a job by producer ADDJOB job1 "This is sample job1" 0 #create job1 with comment ->DI3b2954204f8f86168198266221515fb011a1eea005a0SQ #server response task id. //Get a job by consumer GETJOB from job1 ->1) 1) "job1" ->2) "DI3b2954204f8f86168198266221515fb011a1eea005a0SQ" ->3) "This is sample job1" 透過Golang 來測試job-queue 主要是使用go-disque的source code 來當作job-queue 的機制. 以下有三段程式碼,分別是兩個worker跟一個發送者,發送者(disque-enque)會發送兩個訊息排程給disque,當worker上線或是開始抓取訊息後,就會執行該訊息定義的事項. 主要程式碼並沒有做太大修改: Worker(Consumer 消費者): Downloader :...
繼續閱讀
April
23rd,
2015
給自己做個筆記….
關於 Heroku
以下指令都需要安裝Heroku Toolbelt後登入Heroku. heroku login
查詢Server 目前的log: heroku logs -t 其他更多指令在這裡
Heroku App/Worker crash如何重啟: heroku restart web.1 更多指令
如何在Heroku上面快速的rollback: 到control panel 下 [activity] 選擇你要的change旁邊有[roll back to here].
Performance Status: 可以透過add-on Librato來監測你目前在Heroku的webapp.
關於Golang
Golang 的scope是依照package,所以同目錄下面的檔案,只要package一樣都會被歸在一起.
init() 跑的順序是依照 alphabet axxx.go bxxx.go …… zxxx.go
關於go import 更多使用要看這(好像是FAQ)
import _ “xxx”: 代表的是使用 Init 但是不把整個package 套進來(不會有compiler error if no use)
import . “fmt”: 打所有function 不用加上namespace
import m “fmt”: fmt.Println 變成 m.Println
參考資料
Stackoveflow: heroku - how to see all the logs
Heroku: What to do when your dyno/worker crashes?
Heroku: Releases and Rollbacks
繼續閱讀
April
21st,
2015
前言:
架設Golang Server選擇可以有兩個方向,大部分的人可能選擇自己架設主機來跑Golang Server如果要能夠達到10k甚至是100k連線數的話.這邊把一些經驗記錄下來.
架設環境
這邊的伺服器主要是架設在以下的設備與軟體:
Ubuntu 14.04 X64
MySQL + Apache + Ejabberd
了解主機原先設定的上限與調整
先就不討論硬體架構的連線能力狀況下,還是有一些軟體設定需要調整.
MySQL 連線上限調整
首先會遇到的問題就會是MySQL query 卡住的問題,每一次的Query Request 都會卡在MySQL那一端.主要是以下的設定需要調高.max_connections原先設定只有100.設定方式如下:(細節參考這裡)
mysql> set GLOBAL max_connections=65536;
可能還是會遇到有一些問題,建議把另外一個max_connect_errors一併也調高
mysql> set GLOBAL max_connect_errors=65536;
改完MySQL 接下來可能出現的會是 Ubuntu 設定
改完了MySQL的設定之後,整個設定就可以變得相當的順暢.但是偶爾會出現一些問題,就得接下去追查原因.
問題: 一次能進入的TCP connection 太少
首先可能去做調整就是去/etc/sysctl.conf來增加TCP連線數,以下是參考設定.
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_tw_recycle = 1
net.ipv4.tcp_fin_timeout = 30
net.ipv4.tcp_keepalive_time = 1200
net.ipv4.ip_local_port_range = 1024 65000
net.ipv4.tcp_max_syn_backlog = 8192
問題: “too many open files”
解決了大量連線數無法接收的問題,卻發生了"too many open files"的error.
根據這個Stackoverlow,由於每一個TCP的Connection在Linux系統本身都被當作是一個file來存取,所以面對到大量connection的時候,可能遇到的問題就是依照前面的方式把TCP connection 打開到65000之後,但是由於每一個TCP connection 會對應到一個檔案的開啟,接下來就是檔案開啟的上限被打爆.於是就會出現….
Too many open files
這個問題可能是關於到Ubuntu原本的設定.
ulimit -n //console mode 可以顯示目前可以同時開啟起檔案的最大量. 預設: 1000
修改方式主要是打開 /etc/security/limits.conf
* hard nofile 500000
* soft nofile 500000
root hard nofile 500000
root soft nofile 500000
這樣應該可以初步的把server request產生到同時間至少有3000以上.
參考文章
MySQL Parameter Setting
Stackoverlow: dial error : ‘too many open files’
Linux Increase The Maximum Number Of Open Files / File Descriptors (FD)
Increase “Open Files Limit”
繼續閱讀
April
15th,
2015
前言: 學習架設Golang Web Services 的時候,也需要有後台網頁管理介面.這時候就需要有樣板來套.才能讓網站架設起來比較快. 筆記: 以下的部分會提到如何使用,我個人使用習慣. 不會將所有的程式碼都貼出來,但是會顯示部份的整理過的資料. 關於Martini的layout: 網頁上的資料部分,可以拆解成兩大部分: 不可以抽換,使用layout替換 (ex: layout.tmpl) 舉例: <HTML><HEAD> 甚至是CSS JS都可以. 需要用程式來表示的部分,用render來顯示 (ex: hello_world.tmpl, layout_hello.tmpl) 舉例: table content或是資料庫裡面的資料. Layout file layout.tmpl <!doctype html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Layout sample</title> </head> <body style="margin: 20px;"> <h1>This is a layout sample</h1> <!-- 可以被替換的部分放這裡 --> </body> </html> 其中 `` 將你要顯示的tmpl部分全部換到這邊. 接下來要處理的部分拆解到hello_world.tmpl. <CENTER> Hello </CENTER> 接下來另外一個需要處理的到layout_hello.tmpl. <CENTER> This is another layout Hello </CENTER> 接下來就要提到Golang處理layout的部分. 如何使用Layout: 提到Golang處理的部分,先看看在一開始martini啟動的部分 ..... func main() { m := martini.Classic() // 將所有預設的layout都先設定到"layout.tmpl" m.Use(render.Renderer(render.Options { Layout: "layout", })) ..... m.Run() } 這邊要記住,預設的目錄都會在templates下面. 要顯示的時候可以使用以下的方式. // 使用hello_world.tmpl m.Get("/", func(r render.Render, db *mgo.Database) { display_name := "John" r.HTML(200, "hello_world", display_name) }) 如果不同的entry point 要使用不同的layout呢? 有一些時候,網站開始變大之後.layout 的版型也開始變多了.這時候可能需要動態的去變你要套用的layout.原本的martini-render 就提供可以在render.HTML裡面加上 HTMLOptions.其實也就是加上你要指定的layout. // 使用hello_world.tmpl,並且套上layout_hello.tmpl m.Get("/", func(r render.Render, db *mgo.Database) { display_name := "John" r.HTML(200, "hello_world", display_name, render.HTMLOptions{Layout: "layout_hello"}) }) 相關鏈結: GoDoc: martini-render 這一篇是GoDoc不得不看.. GoDoc: template 關於http/template,不過martini-render是架設在他之上.可看可不看. GO TUTORIAL WITH MONGODB ON HEORKU 是一篇tutorial,不過對於martini-render有一些介紹. Hack martini-render 很推薦看這一篇,基本常識都會了解. 初學 Golang 30 天(二十八)Router,...
繼續閱讀