[Ubuntu][Golang]關於Ubuntu Server連線能力設定

image

前言:

架設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以上.

參考文章

[Golang][Martini]在Martini-render上面的layout/template套用

image

前言:

學習架設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"})
      })  

相關鏈結:

[MongoDB]More study about mongodb and mgo 3 - logical operator and like

Preface

When we trying to use MongoDB, the requirement comes more and more complex and diversity. Here is some note during my implement.

Multiple condition in MongoDB Query

It is very easy to find data in MongoDB, but how about multiple condition such as “AND” and “OR” ?

AND OR in MongoDB

It is very easy to find “AND” support in MongoDB, but how to apply in mgo (MongoDB driver in Go)?

    // Find user name is John and Contry is US.
    var alldb []User
    UserCollection.Find(bson.M{"$and": []bson.M{bson.M{"name": "John"}, bson.M{"Contry": "US"}}}).All(&alldb)

Please note: the $and need combine an array of bson.M.

    // Find CONDITION_A and CONDITION_B
    bson.M{"$and": []bson.M{ CONDITION_A, CONDITION_B }}

So, it is similar with “OR” ($or), detail doc is here.

    // Find CONDITION_A or CONDITION_B
    bson.M{"$or": []bson.M{ CONDITION_A, CONDITION_B }}

Make it more clear in code.

    // Find user name is John or Tom.
    var alldb []User
    UserCollection.Find(bson.M{"$or": []bson.M{bson.M{"name": "John"}, bson.M{"name": "Tom"}}}).All(&alldb)

Using “like” or simple regular expression in MongoDB Query

When we trying to query data, sometime we need use “like”. In MongoDB it is easy to find data as follow:

db.users.find({name: /a/})  //like '%a%'

But the special charactor "/" will skip during Go programming, so we need change to use regular expression.

Collection.Find(bson.M{"name": bson.RegEx{"a", ""}}).All(&result) //like `%a%`

Query array size over “n” (great than “n”)

The major adanvantage of MongoDB is to store JSON directly. What if we want to find a array size which great than 2 ?

The first idea is to find if the array size great than 2 in the collection. So the query string should be follow:

Collection.Find(bson.M{"array": bson.M{"$size" : bson.M{"$gt" : 2}}}).All(&result) //like `%a%`

But it don’t work? How, so I find here is a discussion here.

The major idea is to get array element and check if exist. The easy way to get array index 1 (which mean second element of array aka. array[1])

Collection.Find(bson.M{"array.1": bson.M{"$exists" : true} }).All(&result) //like `%a%`

So, if you want to find array size great than 5, just change to array.4.

Reference

[Golang]關於Channels的控制一些要注意的事項(二)

image

##前言

前一篇的一些討論後,接下來有一些更容易出錯的部分可以討論.主要focus Goroutine跟 defer

###Goroutine Closure

主要是這一篇部落格帶出的問題:

    func main() {
        done := make(chan bool)
    
        values := []string{"a", "b", "c"}
        for _, v := range values {
            go func() {
                fmt.Println(v)
                done <- true
            }()
        }
    
        // wait for all goroutines to complete before exiting
        for _ = range values {
            <-done
        }
    }

根據以上的部分,印出的結果不會是 “a”, “b”, “c”.而是 “c”, “c”, “c” 原因是 goroutine 變數會參照到go func 跑的時候.

如果修改成以下就可以避免這個問題:

    func main() {
    	done := make(chan bool)
    
    	values := []string{"a", "b", "c"}
    	for _, v := range values {
    		go func(obj string) {
    			fmt.Println(obj)
    			done <- true
    		}(v)
    	}
    
    	// wait for all goroutines to complete before exiting
    	for _ = range values {
    		<-done
    	}
    }

由於他的順序會是 go func(v) 之後才執行,所以其變數內容會先傳過去而不是跑道fmt.Println(v)才取得. 更多跟goroutinem與closure有關的資訊請看這裡Go: FAQ

參考資料

[Android]關於JNI的學習筆記

image

前言

上個禮拜在Android Studio 1.1 把JNI搞定之後,接下來就是把JNI的內容去完成.其中有許多小技巧與眉眉角角的地方需要紀錄一下.

筆記內容

關於Android.mk 與 Application.mk 的部分

(2015/10/21 update) 關於Android Studio與JNI的發生ld.exe crash

如果在Android Studio要去load其他的jni static library,在Windows上面有時候會發生ld.exe crash的問題.這時候只要打開”Build Variants”-> 把”Build All”,改成看你現在需要哪一種(Android 手機就是 arm-Debug,模擬器就是x86-Debug). 就可以解決.

(2015/10/20 update) 關於STL部分

如果有用到STL的支援,主要都是修改Application.mk,不是改在Android.mk中. 這樣才能一次改到所有的檔案,避免A檔案過,B檔案不過.

	APP_STL := stlport_static

參考: Using the STL with Android NDK C++

增加 C++ 與 CPP11 支援

新增以下到 Application.mk

    APP_STL := stlport_static
    APP_CPPFLAGS += -std=c++11

讀取其他資料夾與一次讀取所有的cpp

新增以下到 Android.mk

LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/*.cpp)
LOCAL_SRC_FILES += $(wildcard $(LOCAL_PATH)/../../../../../../source/*.cpp)

關於JNI資料的轉換部分

C++ char* 與 JNI jstring 的轉換

*jstring to char **

    char* jstringTostring(JNIEnv* env, jstring jstr)
    {        
      char* rtn = NULL;
      jclass clsstring = env->FindClass("java/lang/String");
      jstring strencode = env->NewStringUTF("utf-8");
      jmethodID mid = env->GetMethodID(clsstring, "getBytes", "(Ljava/lang/String;)[B");
      jbyteArray barr= (jbyteArray)env->CallObjectMethod(jstr, mid, strencode);
      jsize alen = env->GetArrayLength(barr);
      jbyte* ba = env->GetByteArrayElements(barr, JNI_FALSE);
      if (alen > 0)
      {
        rtn = (char*)malloc(alen + 1);
        memcpy(rtn, ba, alen);
        rtn[alen] = 0;
      }
      env->ReleaseByteArrayElements(barr, ba, 0);
      return rtn;
    }

char* to jstring

    jstring stoJstring(JNIEnv* env, const char* pat)
    {
        jclass strClass = env->FindClass("Ljava/lang/String;");
        jmethodID ctorID = env->GetMethodID(strClass, "<init>", "([BLjava/lang/String;)V");
        jbyteArray bytes = env->NewByteArray(strlen(pat));
        env->SetByteArrayRegion(bytes, 0, strlen(pat), (jbyte*)pat);
        jstring encoding = env->NewStringUTF("utf-8");
        return (jstring)env->NewObject(strClass, ctorID, bytes, encoding);
    } 

其他更多的部分可以參考這裏..

指標(pointer)的傳遞

如果需要使用到 native (也就是C++裡面建議的的記憶體區塊)

//從C++的部分取得所建立的記憶體物件
void* obj = NULL;
jint ret_code = (jint) native.createObj(&obj);

//改成jlong回傳 java並且處理(避免記憶體位置過長)
jlong ret_addr = (jlong) obj;

之後要處理就傳進 jlong然後轉成 void*

//input jlong jobj_addr
void *obj = (void*) jobj_addr;

回傳native資料結構給java

很多時候 native C/C++裡面會回回一個資料結構,或者是說你需要多個回傳值而不僅僅只是string與integer的時候就需要用到. 首先要先了解流程如下:

  • 在java端先建立相對應的class (或是使用已經建立的class)
  • 在JNI曾透過 FindClass 與 FindMethod 去找到class與 method.
  • 將資料寫成jobject 並且回傳jobject給java

首先根據JNI Types and Data Structures 要先了解java與native資料轉換如下列表:

**Java VM Type Signatures **

Type Signature Java Type
Z boolean
B byte
C char
S short
I int
J long
F float
D double
L fully-qualified-class ; fully-qualified-class
[ type type[]
( arg-types ) ret-type method type

假設你要建立的java資料結構如下

    class TwoFieldResult {
        public int mError_Code;
        public String mResponse;
        public CCS_TwoFieldResult(int inFirst, String inSecond) {
            mError_Code = inFirst; mResponse = inSecond;
        }
    }

那麼你需要對應到constructor的部分就是:

    env->GetMethodID(theReturnType,"<init>","(ILjava/lang/String;)V");

以下為部分程式碼:

    // Struct in Java
    // class TwoFieldResult {
    //     public int mError_Code;
    //     public String mResponse;
    //     public CCS_TwoFieldResult(int inFirst, String inSecond) {
    //         mError_Code = inFirst; mResponse = inSecond;
    //     }
    // }
     
    jobject PrepareReturnObject(JNIEnv* env, int error_code, const char *result_string) {
        jclass theReturnType(env->FindClass("com/example/evan/hellojni/TwoFieldResult"));
        if (!theReturnType) {
            LOGD("java exception thrown: FindClass");
            return NULL; // java exception thrown
        }
        jmethodID constructor = env->GetMethodID(theReturnType,"<init>","(ILjava/lang/String;)V");
        if (!constructor) {
            LOGD("java exception thrown: GetMethodID");
            return NULL; // java exception thrown
        }
        jstring output_jstring = env->NewStringUTF(result_string);
        return env->NewObject(theReturnType, constructor, error_code, output_jstring);
    }

如果要參考回傳陣列結構,可以參考這一篇

容易出現錯誤部分

env->NewObject執行後Crash

之前卡在這裡,不過這裡crash主要有幾個需要檢查的部分

  • char * 轉 jstring會出現crash
    • 這個部分想不到compiler不會出現錯誤,而是直接在執行的時候才會出現.所以千萬注意要做jstring output_jstring = env->NewStringUTF(result_native_string);的轉換.
  • 透過[EnsureLoadCapacity] 來修復(http://stackoverflow.com/questions/19887763/how-to-fix-jni-crash-on-env-newobject)

如何在 Android Studio 1.1 裡面加入 .so

  • 其實非常簡單,只要把.so(包含目錄 jniLibs)檔案放在 app/src/main 就可以
  • 如果從Eclipse的專案轉過來的,可能得稍微研究一下.目前我解法是依照這樣的擺法.

Android Studio 移動專案目錄名稱會發生app下面資料全部不見

不確定是不是bug,其實只要gradle重讀就好

  • [Tools]->[Android]->[Sync PRoject with Gradle Files]

JNI裡面要讀取的Class 處理方式

  • 需要宣告為public class 幫助在其他的package裡面可以處理.
  • 建議使用new java class來產生.

NDK_TOOLCHAIN_VERSION 可能會造成的問題 [update 2015/11/04]

最近在整合一些跨平台的module到AS(Android Studio) 的時候,由於要編譯成很多個static library.所以我都是透過docker來編譯的.

這時候Android Studio 1.4之後,就不支援編譯static library,所以需要的是透過NDK Console編譯好之後再給Android Studio來跑.

但是不知道為什麼會一直遇到錯誤:

	failed: dlopen failed: unknown reloc type 160 ....

後來查了一下子,才發現跟我的某個static library的Application.mk設定有關:

	NDK_TOOKCHAIN	_VERSION=clang //要拿掉

拿掉之後就可以解決問題.

參考資料

[Golang][程式設計週記].. 2015第九週

##雜七雜八感言:

最近搞完一些Golang部分,有開始跑各個平台的cross platform的building solution.果然C++跨平台一點都不簡單,真是80%設定環境,20%寫跨平台的code….

##筆記:

[Golang] 關於Golang 有趣的網頁與小工具

[Apple] 關於 Apple 03/09 發表會

這個禮拜讓蘋果迷最興奮的就是三月的發表會. 裡面有包括了ResearchKitMacBook更新跟Apple Watch. 兩個跟硬體有關的就沒啥好評論….

ResearchKit 與 HealthKit

根據已經放出的消息,ResearchKit透過兩項(目前已知是手指點擊與聲音)的手機功能,可以提供一些簡單的診斷.並且把這些資料提供給醫療研究機關. 等到詳細資料出來應該會更仔細地研究一下,是否一般廠商有介入的空間.

[Android] 在Android Stuido 1.1 上面使用

自從Google 推出 Android Studio 正式版之後,自然而然也開始把Eclipse的力道放輕. 於是要弄新的SDK也變成是一個工程.

所以現在要開發新的cross platform module,所以也必須要在 Android Studio + NDK + JNI 去執行跨平台的 C++ module.

根據最新版的Android Studio 1.1 如果依照著一般的方式來部署NDK會發現以下的問題:

    WARNING [Project: :app] Current NDK support is deprecated.  Alternative will be provided in the future. 

找了一堆論壇,其實並沒有一個有系統地整理,直到看到這一篇文章,以下把方法整理一下:

  • 安裝 Android Studio 1.1
  • 下載並且安裝 Android NDK r10d
  • 先到一開始的畫面[Configure]->[settings]->[External Tools] 設定Gradle
  • 設定javah
  • 新增一個NDK Build 類別為NDK
    • Program:C:\ndk\ndk-build.cmd
    • Parameters:NDK_PROJECT_PATH=$ModuleFileDir$/build/intermediates/ndk NDK_LIBS_OUT=$ModuleFileDir$/src/main/jniLibs NDK_APPLICATION_MK=$ModuleFileDir$/src/main/jni/Application.mk APP_BUILD_SCRIPT=$ModuleFileDir$/src/main/jni/Android.mk V=1
    • Working directory:$SourcepathEntry$
  • 這樣設定好就可以
  • 複製ndk-r10d 目錄下的sample\hello-jni來使用
  • 會出現一個[c]在上面按下 右鍵跑 “NDK Build”,來製造出.so
  • [App]->[build.gradle]新增:

      sourceSets.main.jni.srcDirs = []
    
  • 然後sync gradle 這時候會發現[C]不見了,就可以跑App

之後要切換就是把上面的那段 comment掉才能build NDK