[Docker] 在Mac OSX上使用筆記

image

前言

自從之前把我的Macbook Air上面的空間加以清理之後.總算也空出了20~30G,可以好好的練習一下關於docker在swarm與其他在跨機器端的應用. 不過這裡稍微紀錄一些關於Mac上面使用docker的小筆記.

一些筆記

如何讓Docker Setting記錄在你所有的console

由於Docker在MacOSX 是使用VM的方式來架設,其實真正執行docker的機器是你Virtual Box裡面的Linux VM,所以去呼叫docker相關指令一般有兩個方式:

  • 透過Docker Quickterminal來跑
  • 另外一個也可以透過SSH的方式主要是鏈接ssh: 192.168.99.100:2376 帳號密碼是 docker/tcuser

不過這樣多少有些麻煩,有沒有可能讓我在我的console裡面(比如說iTerm2)裡面的每一個tab可以直接使用docker呢? 方法如下:

(假設你是使用bash) 打開 ~/.bash_profile 加入這行

eval "$(docker-machine env default)"

這樣你的預設使用者都可以找到正確的docker socket並且直接使用docker指令 (ex: docker run, docker build …)

如何讓root 使用相同docker環境

一般而言,原本的docker指令是需要透過sudo docker XXXX來執行.但是你會發現這件事情在Mac OSX下面會出現問題.

Cannot connect to the Docker daemon. Is the docker daemon running on this host?

一查才發現,上面設定的環境參數並沒有在root的環境變數裡面.而且docker-machine是找不到原先的設定(docker-machine預設會讀該使用者的docker socket),所以你不能像上面一樣使用docker-machine env來設定環境變數.

必須要依照以下的方式來設定(假設你root也是使用bash,如果不是可以使用chsh -s /usr/local/bin/bash root來變更:

  • 先在你安裝docker的使用者下執行docker-machine env,可能會跑出類似以下的資料:
export DOCKER_TLS_VERIFY="1"
export DOCKER_HOST="tcp://192.168.99.100:2376"
export DOCKER_CERT_PATH="/Users/USER/.docker/machine/machines/default"
export DOCKER_MACHINE_NAME="default"
  • 將以上的資料複製起來,編輯到root的.bash_profile.也就是執行sudo su 再來編輯 vi ~/.bash_profile
  • 把以上設定放在裡面,並且儲存
  • 呼叫sudo docker xxx 改成 sudo -i docker xxx

如此一來可以正常執行了.不過缺點是你只要用docker-machine 新增會刪除default的時候,就得重新設定.

[Golang] 學習Google Code Search 使用的Trigram Indexing

前言

一直以來,我有在追蹤許多優秀工程師的github(RD的臉書).其中尤其是dgryski. 因為他有許多有趣使用Golang來開發的演算法小專案,所以我也會一起學習一些演算法與特別的資料結構.

本週的課程是Trigram Indexing.一邊學習,一邊寫成小專案.

什麼是Trigram Indexing

直接打Trigram會找到一堆關於卦的資料.不過Trigram主要是由兩個字組合而成 Tri-gram 也就是三個字元(N-gram中的tri-gram).

其實Trigram很簡單,主要就是把一串文字透過三個三個來分組:

  • 將所有字元改成小寫
  • 把每個空白處理.這裡有些不太相同,不少人將空白作為分隔符號.而Google Code Search把空白當作其中字元放進去.
  • 把字元做成trigram

如何做把一個單字做 Trigram 拆解

舉例: Search

  • 變小寫 “search”
  • 開始拆解,三個三個為一組
    • sea
    • ear
    • arc
    • rch

如何在程式裡實作拆解trigram

其實在程式裡面實作拆解很簡單,只是重要的是要如何比對.因為如果你真的把"sea""ear" …. 存成字串,比對又是相當的消耗時間.所以不論是Google還是一般人在做Trigram的時候都會這樣拆解.

講一個個字元換成ascii的uint32並且透過位移方式存放.舉例而言s := "abc"就變成 uint32(s[0]), uint32(s[1]), uint32(s[2])也就是 97, 98, 99.並且透過位移存放.97<<16 + 98<<8 + 99 = 6382179. 這樣比較的速度就會快的更多,也很適合儲存.

	var trigrams []uint32 //存放所有拆解好的trigram
	s := "abc" //要拆解的字
	
	for i := 0; i <= len(s)-3; i++ {
		//透過將每個字元轉換成uint的方式,並且透過位移方式存放
		t := uint(uint32(s[i])<<16 | uint32(s[i+1])<<8 | uint32(s[i+2]))
		trigrams = append(trigrams, t)
	}

	//最後結果 6382179

如何在Code Search中使用

這邊開始很建議打開Russ Cox關於Google Code Search的介紹文章,雖然主軸是Regex 不過是透過Trigram Indexing的方式.

如同前面提到的,由於這個是”Code Search” 所以空白本身相當的重要.也就必須要將空白當作一個字元來作為Trigram Indexing的來源.

請注意: 本文重點在於討論Trigram Indexing,所以原先在Google Code Search針對Regex處理的部分就不討論.

透過簡單例子來了解

比如說,我們現在要輸入搜尋的是以下三段文字:

  1. Google Code Search
  2. Google Code Project Hosting
  3. Google Web Search

處理空白與加上Trigram Indexing

接下來,讓我們真的來處理一些字串,這裡結果就會有點複雜,所以我們只拿第一段文字舉例:

“Google Code Search”來做Trigram Indexing:

"Goo", "oog", "ogl", "gle", "le_", "e_c", "_Co", "Cod", "ode", "e_S", "_Se", "Sea", "ear", "arc", "rch"

在每一個Trigram上加上Document ID

Document ID就是你原本是第幾段文字(以這裡為例子),當然隨著文件變大,有可能是檔案或是磁碟代號. 這裡將以上的每個資料都加上 {1}

將Trigram 文字轉成數字方便比對

就像之前提到,要一個個比對文字 “Goo”比對 “Goo” 其實在CPU上面是比較慢的.而且存放成文字也是比較佔空間.所以比較好的方式就是都轉成Ascii的方式:

	"Goo" = uint("G") uint("o") uint("o") = 71 111 111

並且透過位移轉換,將他放成同一段數字uint32

	71 << 16 + 111<< 8 + 111 = 4681583

如此一來,之後再也不用比對文字"Goo"而是比對事不是數字4681583

如果一份文件中出現重複的trigram

前面沒有提到,不過我們還是要處理可能會重複出現的trigram.比如說"Gooood",就會拆解成"Goo", "ooo", "ooo", "ood".其中就有出現兩個"ooo". 所以針對這樣的時候,我們還需要記錄這個trigram在某個Document中出現的次數. 以這個例子而言:

	"Gooood"  = "Goo", "ooo", "ooo", "ood"
				=  4681583, 7303023, 7303023, 7303012
				= 4681583 -> {1, 1} //第一個doc 出現一次
				  7303023 -> {1, 2} //第一個doc 出現一次
				  7303012 -> {1, 1} //第一個doc 出現一次

可以透過這個方式來表示一個trigram indexing 的文件資料.

出現的次數可以作為trigram 刪除的時候參考,輸入的字串也必須要有複數個才能把該文件裡的trigram整個刪除.

如何在儲存這樣的資料

接下來要解釋,如何將Trigram Indexing結果做儲存.主要的方式是透過Golang 的map

	
	var trigramIndex map[uint32][]int
	
	// 這裡指的是透過 trigram -> document ID slice
	// 舉例而言: "Goo" 出現在 Document 1,2,3
	// "Goo"-> {1, 2, 3},假設 "Goo" 在第一個文件出現兩次,第三個文件出現三次
	//      -> { {1,2}, {2,1}, {3,3} }
	// 而 "Goo" 會變成 uint32 4681583
	// 就存放成 trigramIndex[4681583]-> []int= { {1,2}, {2,1}, {3,3} }

查詢 (Query)

透過以上的方式,將所有的文件(以這裡為範例就是三段文字). 做好Trigram Indexing之後,並且全部存成一連串的Doc ID之後. 我們就要來開始進行一個簡單的查詢:

假設我們要查詢"Code"這個字出現在哪些文件中,處理的流程如下:

  • "Code"拆解成 "Cod""ode"
  • 透過轉換變成44194287300197
  • 透過我們儲存好的mapping table,可以找到 4419428 -> {1,2}還有 7300197 -> {1, 2}
  • 講兩個結果做"AND",也就是 {1, 2} AND {1, 2} = {1,2}
  • 查詢結果該文字出現在文件1跟文件2

範例程式

以下寫好我自己的範例程式,大家有興趣可以進去參考看看.https://github.com/kkdai/trigram

相關鏈結

[Android][JNI]如何由JNI thread/callback去呼叫Java Method

image

前言

在執行Android JNI的時候或多或少都需要在JNI中去呼叫Java的函式.(不論是只有Android才能拿到的資料,或是需要做callback)

這裡就介紹如何在 JNI thread (或是 callback)中去呼叫 Java Method.

基礎觀念

先了解基礎概念,大部分而言會有兩種狀況需要由JNI呼叫Java.

  1. Java -> Jni -> 更新另外一個Java
  2. Jni的Callback or Thread -> 更新 Java

會需要這種狀況,大部分都是(2).當然也可能是(1).由於在(2)的狀況下限制比較多而且比較麻煩,所以只講解(2)的部分. (當然,一樣的方式也可以適合給(1)使用.

先講解可能的限制

由於我們是從jni裡面的thread 或是 jni 去呼叫C/C++ module的callback. 所以.. 你拿不到JNI Environment JNIEnv 跟原先呼叫或是你想要呼叫的Activity的jobject

所以這裡要先透過某個 JNI main thread的function.舉例而言,最基本的Hello World JNI 都會有個 stringFromJni() 透過那個main thread的JNI function.

相關流程

  1. 需要紀錄JVM (一般而言 JVM生命週期是每個App launch啟動)
  2. 也必須要記錄你要呼叫的 Activity 的 jobject
  3. 透過JVM取得目前的目前 JNIEnv (透過AttachCurrentThread)
  4. 依照一般流程呼叫CallVoidMethod ->GetMethodID -> CallVoidMethod(如果是呼叫void)
  5. 透過DetachCurrentThread回收相關資源

程式碼:

容易出錯的部分:

錯誤的jobject使用

這邊主要是提醒,由於JNI function 函式裡面的參數 jobject thiz的處理:

JNIEXPORT jstring Java_com_example_hellojni_stringFromJNI( JNIEnv *env, jobject thiz )

裡面的 JNIEnv *envjobject thiz主要解釋如下:

  • JNIEnv *env : 負責處理JNI的環境,千萬注意在thread裡面的JNIEnv會不同.所以要使用上面的AttachCurrentThread
  • jobject thiz: 主要是處理該JNI library owner (Activity) 的Java Object.
    • 這裡要主義的是這個jobject 主要是System.loadLibrary("hello-jni"); 的Java Activity,而不是呼叫JNI function 的Java Object.

所以如果你有一個Java Class (ex:JNIJava)專門處理JNI,另外一個Java Class (ex: MainActivity)去建立 JNIJava然後去呼叫他,如果你需要呼叫回去MainActivity的函式,記得要把該Activity 傳進來. (不然就是要用FindClass找到正確的)

沒有使用AttachCurrentThread,造成CALL_TYPE crash

如果在JNI native debugger 發現有CALL_TYPE error 在 jni.h.代表你再不是JNI main thread去使用main thread的JNIEnv.所以要注意.

相關鏈結

[iOS]安裝XCode的Plugin來操作CocoaPods

前言

其實一直以來有寫iOS的code,不過由於習慣上的因素.一直沒有使用Cocoapods. 不過最近想說弄個小iPhone App搭著Golang的專案. 所以開始玩,不過Cocoapods其實一開始沒有那麼容易入手,所以又看了些plugin希望能讓使用上更簡單.

原本CocoaPods使用方式:

原本使用方式,需要如下表:

  • 安裝CocoaPods sudo gem install cocoapods
  • 撰寫 Podfile (相關細節)
  • 打上 pod install來建立相關必要檔案
  • 打開產生的workspace

這其實還挺繁瑣的,所以去找了plugin

安裝XCode Plugin

###先安裝 Plugin Package Manager - Alcatraz

這個Package Manager在安裝的時候,會跳出一個plugin 沒有經過授權的警告,不過就跳過去吧.

安裝Alcatraz:

curl -fsSL https://raw.githubusercontent.com/supermarin/Alcatraz/deploy/Scripts/install.sh | sh

重新開啟XCode就可以.

###安裝CocoaPods Plugin for Xcode

安裝好Alcatraz重開之後,打開XCode

	[Windows] -> [Package Manager]

裡面選取,CocoaPods Plugin 就可以.

安裝好之後,要設定一下GEM_PATH,先確認一下你安裝Pod的位置,在Console打上:

dirname `which pod`

把結果填入

XCode -> [Product] -> [CocoaPods] -> [GEM_PATH]

就可以直接在XCode上面更新與修改Podfile

如果還有出現相關問題,可以看這裡The command path could not be resolved

相關鏈結:

[Mac OSX] 一些在console的快速鍵設定

前言:

主要是紀錄一下一些在命令列(console mode)下的快捷鍵,希望以後有需要用到可以給自己紀錄一下.

Console的快捷鍵列表

參考這裡來的.

以下兩個主要是一次移動一個字

  • alt ⌥+F to jump Forward by a word
  • alt ⌥+B to jump Backward by a word

這邊有其他的部分,移動字元,或是移動到一開始.

  • ctrl+A to jump to start of the line
  • ctrl+E to jump to end of the line
  • ctrl+K to kill the line starting from the cursor position
  • ctrl+Y to paste text from the kill buffer
  • ctrl+R to reverse search for commands you typed in the past from your history
  • ctrl+S to forward search (works in zsh for me but not bash)
  • ctrl+F to move forward by a char
  • ctrl+B to move backward by a char

在iTerm2 裡面編輯快捷鍵來使用一次跳一個字

參考這裡

  • 按下 cmd + ,
  • 選取 Profile -> Keys
  • 加入以下: 使用 Alt+ <-來向左跳一個字
    • Keyboard Shortcut: ⌥←
    • Action: Send Escape Sequence
    • Esc+: b
  • 加入以下: 使用 Alt+ ->來向右跳一個字 - Keyboard Shortcut: ⌥→
    • Action: Send Escape Sequence
    • Esc+: f

[Golang] 學習關於Twitter的Three-legged Authentication

前言

主要是因為Twitter上面有人提出來想寫一個小遊戲

當然,這時候就給了正在愁不知道要怎麼讓自己Project52新的專案的我有了想法來寫.

所以我的目標是寫一個Golang的Package可以去做server-side oauth,並且讀取一些Twitter上面的資訊.

關於Twitter的Three-legged Authentication

How To Implement Sign in With Twitter - Web Solutions Blog

上面這張圖清楚的顯示該如何做Twitter的OAuth Login,這裡講的主要都是Server-side的部分.簡單來說步驟主要分為以下數個:

  • 透過你在Twittter Dev App申請的App Consumer Key 跟 Consumer Secret連線到oauth/request_token 開始認證
  • Twitter Server會給你另外一個 token URL 會連線到另外一個網址(該網址為Twitter擁有)去輸入帳號密碼或是同意該App使用你的帳戶資料.
  • Twitter確認完畢後,會自動轉移到當初App設定好的Callback URL
  • 你所架設Callback URL會收到Twitter Server呼叫,並且給你以下三個資料確認你登入的狀況.
    • oauth_token
    • oauth_token_secret
    • oauth_callback_confirmed
  • 只要oauth_callback_confirmed是正確的,你就可以透過oauth_tokenoauth_token_secret去連接到該使用者的一些資訊.

設定正確的App資訊

只要到Twitter的Dev App 申請頁面,就會看到以下的幾個欄位.

不過最重要的欄位還是

  • Callback URL
  • Callback URL
  • Callback URL

很重要要講三次,因為很容易讓你卡很久就是這裡.

如果你要寫server-side的oauth的話,你必須要填入可以被接受的網址. 這裡定義可以被接受網址如下:

  • 必須不是 localhost
  • 必須有 https://

如果你沒有填入資料到Callback URL的話,你就會被當成是Desktop App而無法進行Three-legged Authentication

填好這一切的資訊,就可以拿到consumer keyconsumer secret來繼續以下的部分.

開始架設本地端測試流程

等等! 你剛剛不是說不能把Callback URL寫成 localhost嗎? 這邊就是要教導各位,如何在本地端測試與撰寫關於Server-side OAuth的流程.

  • 修改你的hosts,並且將localhost改成比較有意義的. (ex: 個人改成 testgoserver.com)
  • 記得testgoserver.com 填入Callback URL

這邊建議各位,直接參考這一段Twitter Server Go

跑起來後,記得在瀏覽器打上 http://testgoserver.com就可以了.

常見的錯誤

Desktop applications only support the oauth_callback value ‘oob’

詳細討論看這裡

表示App被認為是Desktop App,這時候需要去 https://apps.twitter.com/app/YOUAPPID/settings 設定Callback URL

此外,這邊的網址還不能使用localhost.所以你可能得寫成某個真正的網址.

不過建議不要lock,不然會出現以下錯誤

	This client application's callback url has been locked

小專案

最後,我還是整理整個架構與將幾個我馬上會用到的API成一個packagey.放在https://github.com/kkdai/twitter

參考鏈結