2015年4月15日 星期三

AWS 筆記 - 使用 Amazon ElastiCache - Memcached Cluster



由於服務是儲存在 RDBMS ,以此做出許多關聯設計。有朋友分享實際案例,對於線上服務盡量不要用 JOIN 等技術,例如把查詢結果定期儲存一張表上來使用,然而,在開發階段只能一切求快 XD 因此,快速開發後就想一下有沒有快一點的強化服務方式,於是乎就想到透過 memcache 來暫存儲存結果。於是乎,來使用一下 Amazon ElastiCache。

原先想自己在 EC2 上架設,接著就想到要維護 Cluster 的問題,所以就...先試一下別人的產品吧 XD 開幾台最便宜的 cache.t2.micro (555MB memory),然後開個兩台。

接著就可以得到一個入口點,如:

your_name.cfg.your_region.cache.amazonaws.com:11211

因此,有人幫你維護 cluster 也是件好事吧!有專人維護入口點,也有人維護節點,都可以在 AWS 控制界面看到。



以 PHP 程式範例就接用吧!

<?php

$memcache = new Memcache;
$memcache->connect('your_name.cfg.your_region.cache.amazonaws.com', 11211);

for ($i=0 ; $i<1024 * 1024 * 30 ; ++$i) {
        $key = "key-$i";
        $memcache->set($key, $i);
}


執行:

$ time php test.php

就可以在 Amzon ElastiCache 管理介面觀看那兩個 node 的情況,簡言之,兩個 node 是一直彼此 sync 的啦。且過程中也可以很便利的透過 Add Node 新增機器,而程式碼由於都寫定在一個入口點了,所以維護非常輕鬆。




若是把範例程式改成這樣,會發現跑一陣子後(約四千,大概跟 DNS Cache有關)會出現連不到的問題,我想,站在 cache 機制上,服務本身也不會出錯才對 :P

<?php
for ($i=0 ; $i<1024 * 1024 * 30 ; ++$i) {
        $key = "key-$i";

        $memcache = new Memcache;
        $memcache->connect('your_cache.cfg.region.cache.amazonaws.com', 11211);
        $memcache->set($key, $i);
        $memcache->get($key);
        $memcache->close();
        echo "finish $i\n";
}


$ php test.php
...

PHP Warning:  Memcache::connect(): php_network_getaddresses: getaddrinfo failed: Name or service not known in /path/test.php on line 6
PHP Stack trace:
PHP   1. {main}() /path/test.php:0
PHP   2. Memcache->connect() /path/test.php:6
PHP Notice:  Memcache::connect(): Server your_cache.cfg.region.cache.amazonaws.com (tcp 11211, udp 0) failed with: php_network_getaddresses: ge
taddrinfo failed: Name or service not known (0) in /path/test.php on line 6
PHP Stack trace:
PHP   1. {main}() /path/test.php:0
PHP   2. Memcache->connect() /path/test.php:6
PHP Warning:  Memcache::connect(): Can't connect to your_cache.cfg.region.cache.amazonaws.com:11211, php_network_getaddresses: getaddrinfo fail
ed: Name or service not known (0) in /path/test.php on line 6
PHP Stack trace:
PHP   1. {main}() /path/test.php:0
PHP   2. Memcache->connect() /path/test.php:6

2015年4月14日 星期二

與大老闆有約

最近跟幾位大老闆聊天,也回顧起前老闆的互動,有些經驗真的是自己親身體驗的,所以特別有感觸,有些則是像看著書看電影,不如聽人親口說出來:

  • 所謂的 startup 是個生活的方式,若是以音樂圈舉例,大概出門都想比個 rock 手勢 XD 整體上就一種工作生活的模式,並非 startup 就過得比較好、比較爽。
  • 有錢出錢,沒錢想出技術、時間,但是,所有的技術股本質上都是有對等的錢,有些工程師會想拿很大比例的股票,用的是技術股而非自己的錢去買,但技術股一開始規劃太多會讓投資人不敢投資。而技術股本身就是有等值的金錢價值,無論有沒有成功上市還是一張壁紙,在當下就是有對等價值。所以,有機會拿到技術股時,要想想這些股票其實是 co-founder 的錢,他們為了打造團隊忍痛分出來的
  • 有些新創會讓你把年薪的幾趴用技術股,但真正有價值的公司,股票是很貴的,能給錢就給錢,不會想給技術股,因為通常每一輪增資時,股價是飛快地成長,小則幾十倍,大則百倍、千倍 XD 你想想 co-founder 一開始手上的 1 萬,經過一輪變成一百萬時,你覺得他會想給你股票嗎?
  • 團隊永遠優於題目,對天使來說,投資的是你的團隊,而不是你的題目
  • 搞 startup 的方向有很多種,有人想要長期持有,也有人想要短期翻百倍、千倍脫手,並沒有不好之處
  • 公司願意給人高薪時,某個角度是資金狀況達到一個水準,某個角度是為了打造明星戰隊,好讓下一輪募資有更佳的價值,其實都是好事。當公司給高薪卻不給股票時,有時不要太貪心,至少拿到高薪了!換個角度來說,這可是 co-founder 早期的投資成果呢!不然你連高薪的機會都沒有。
  • 對天使來說,大概投資 25 間才會有一間成功,若早期報酬率有25倍,那只是剛好打平而已

至於進 startup 到底要幹嘛?基本上選擇當 co-founder 後,已經是一條不歸路了,無論成功或失敗,自身都是很有價值的,要記得最重要的永遠是人脈圈,練人脈等同練信任感,失敗並不會怎樣,至少天使會知道你下次不會再犯一樣的錯了。

不過,有一個非常現實的問題...連續創業可以創造價值沒錯,但是 co-founder 的股份初期再怎樣還是用等價的錢打造的,所以想連續創業...還是要有一些錢錢才可以進行。別忘了 co-founder 還是要盡量保持大量持股,因為每一輪的角色會稀釋,甚至最後要找專業經理人時,必須自己拿股份出來吸引專業經理人。這筆股份/錢,我想任何投資人都不想出的。

最後一提,若30歲了加入公司的角度還是想著練功,那永遠都無法跳脫員工的角度。30歲這個年紀,已經該去指揮別人了。

2015年4月13日 星期一

iOS 開發筆記 - 使用 Facebook SDK 4.0 分享至塗鴉牆與簡易的 publish_actions 權限判斷流程

大概都遵循 Facebook 文件,這邊指紀錄在一個 ViewController 中,啟動分享至使用者塗鴉牆的流程,包括判斷使用者是否安裝過 Facebook app、是否授權 publish_actions 等,以及選擇用 Facebook app 分享或是用 Facebook SDK 分享等。

#import <FBSDKCoreKit/FBSDKCoreKit.h>
#import <FBSDKShareKit/FBSDKShareLinkContent.h>
#import <FBSDKShareKit/FBSDKShareDialog.h>
#import <FBSDKShareKit/FBSDKShareAPI.h>
#import <FBSDKShareKit/FBSDKShareOpenGraphAction.h>

#import <FBSDKLoginKit/FBSDKLoginManager.h>
#import <FBSDKLoginKit/FBSDKLoginManagerLoginResult.h>

@interface ViewController () <FBSDKSharingDelegate>
@property (nonatomic, strong) NSString *url;
@end

@implementation ViewController

#pragma mark - FBSDKSharingDelegate

- (void)sharer:(id<FBSDKSharing>)sharer didCompleteWithResults:(NSDictionary *)results {
    UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"INFO" message:@"done" delegate:nil cancelButtonTitle:@"ok" otherButtonTitles: nil];
    [alertView show];
}

- (void)sharer:(id<FBSDKSharing>)sharer didFailWithError:(NSError *)error {
    UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"INFO" message:@"error" delegate:nil cancelButtonTitle:@"ok" otherButtonTitles: nil];
    [alertView show];
}

- (void)sharerDidCancel:(id<FBSDKSharing>)sharer {
    UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"INFO" message:@"cancel" delegate:nil cancelButtonTitle:@"ok" otherButtonTitles: nil];
    [alertView show];
}

#pragma mark - share methods

- (void)useFacebookApp {
    FBSDKShareLinkContent *content = [[FBSDKShareLinkContent alloc] init];
    content.contentURL = [NSURL URLWithString:self.url];
    [FBSDKShareDialog showFromViewController:self
                                 withContent:content
                                    delegate:nil];
}

- (void)useFacebookSDK {
    FBSDKShareLinkContent *content = [[FBSDKShareLinkContent alloc] init];
    content.contentURL = [NSURL URLWithString:self.url];
    [FBSDKShareAPI shareWithContent:content delegate:self];
}

#pragma mark - init action

- (void)doShare
{
    if (![FBSDKAccessToken currentAccessToken]) {
        FBSDKLoginManager *login = [[FBSDKLoginManager alloc] init];
        [login logInWithPublishPermissions:@[@"publish_actions"] handler:^(FBSDKLoginManagerLoginResult *result, NSError *error) {
            if (error) {
                // Process error
                NSLog(@"Process error");
            } else if (result.isCancelled) {
                // Handle cancellations
            } else {
                if ([result.grantedPermissions containsObject:@"publish_actions"]) {
                    [self useFacebookSDK];
                } else {
                    [self useFacebookApp];
                }
            }
        }];
        return;
    } else if ([[FBSDKAccessToken currentAccessToken] hasGranted:@"publish_actions"]) {
        [self useFacebookSDK];
    } else {
        [self useFacebookApp];
    }
    return;
}

@end

2015年4月12日 星期日

16個夏天

去年就留意到這個劇名,跟自己的 blog 高度相似 XD 要不是 Yahoo! 首頁正在推某家免費看台灣戲劇,我應該也不會去看吧!就這樣開心地線上看著免錢的正版戲劇,每次在家運動就看一集,好像也超過一個月了?!今天很悶不想寫程式,就坐在電視前面,一口氣連看了六集,不知不覺也就快看完了,果然好看的戲,應該靜靜地享受情感流串啊,這才是尊重導演、編劇啊。

原先被一開始的學生情懷牽動,直到有一天跑去看 wiki 才發現案情不單純 XDD 之後反而就沒那麼有興趣,因為這個年代對愛情議題比就無感 XD 但這部戲劇情編排不錯,戲中點滴牽動著台灣相關事件,像 921 大地震、SARS、金融海嘯,甚至hTC、無名小站、臉書等有,反而很對味,也想起周邊一些人、事、物。

想想看周邊人在幹嘛?有換工作的、有結婚的、有生小孩的,也有遲疑著人生的,也有挑戰人生的,更有剛裝潢好辦公室準備創業的,不過聊聊最貼切自己的資訊產業,真的有種說不出的微妙感。不知是不是 open source 使得門檻降低?不知道是不是慾望還貪念,不少人都在進行所謂的 side project 呢。大家還是常說台灣資訊業的不是,或是拿著過去台灣經濟起飛的時代評論著現在,轉個彎來想想:當時的人會知道那時將經濟起飛嗎?有一件事仍是不變的,大家都不想錯過什麼吧?就這樣繼續讓生命更加豐富吧!

如同 16 個夏天一樣,沒有過去,哪有現在,更別害怕失敗而止步啦。

2015年4月9日 星期四

iOS 開發筆記 - Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[__NSPlaceholderArray initWithObjects:count:]: attempt to insert nil object from objects[0]'

閒暇時,追了這個 bug 很久...

主因是 source code 又沒看到 NSPlaceholderArray 關鍵字,最後才發現這是只是單純的 NSArray 相關更動事件。

要找的關鍵處有幾個:
  • [arrayObject addObject:object];
  • [arrayObject addObjectsFromArray:arrayObject];
  • [NSArray arrayWithObject:object];
  • ...
結果漏了一個生成 NSArray 很好用的語法:
  • @[]
出包之處就是 @[] 相關動作啦,例如 @[obj1, obj2, obj3]; 其中有個 object 是 nil 啦