วันจันทร์ที่ 17 กุมภาพันธ์ พ.ศ. 2557

Build : Oracle Call Interface สำหรับ Golang

คืออยากใช้นะครับ แต่แหม่... มีแค่ https://github.com/mattn/go-oci8 ที่ดูดีสุด แต่คนเขียนเหมือนจะเขียนแล้วหนีไปแอบที่ไหนไม่รู้ ไม่บอกอะไรเลย

อยากใช้ก็ต้อง งม.. สิครับ

OCI ก็ต้องใช้ downlaod instance client มาก่อน (ตามสูตร) ลงทะเบียนแล้ว download จาก http://www.oracle.com/technetwork/topics/intel-macsoft-096467.html ผมใช้ OSX (Maverick)

Download

  • instantclient-basic-macos_x64-11.2.0.3.0.zip
  • instantclient-sdk-macos_x64-11.2.0.3.0.zip
แล้วผมทำแบบนี้ครับ
  • unzip basic แล้ว move ไปไว้ที่ /usr/lib/instantclient_11_2
  • sym link 
    • ln /usr/lib/instantclient_11_2/libclntsh.dylib.11.1 /usr/lib/libclntsh.dylib.11.1
    • ln /usr/lib/instantclient_11_2/libocci.dylib.* /usr/lib/libocci.dylib.*
    • ln /usr/lib/instantclient_11_2/libociei.dylib /usr/lib/libociei.dylib
    • ln /usr/lib/instantclient_11_2/libnnz11.dylib /usr/lib/libnnz11.dylib
  • ดูว่าเครื่องติดตั้ง pkg-config รึยัง ถ้ายังติดตั้ง ถ้ายังใช้ MacPort ก็ได้ครับ
    • sudo port install pkgconfig
  • แล้วสร้าง oci8.pc ไว้ที่ /usr/lib/pkconfig ตามนี้ครับ (ไฟล์จากเครื่องผม)
    • prefix=/usr/lib/instantclient_11_2
      libdir=${prefix}
      includedir=${prefix}/sdk/include/

      Name: OCI
      Description: Oracle database engine
      Version: 11.2
      Libs: -L${libdir} -lclntsh
      Libs.private: 
      Cflags: -I${includedir}
  • เริ่ม  Build 
    • ไปที่ project workspace path 
    • $ export GOPATH=`pwd`
    • $ go get github.com/mattn/go-oci8
    • Go จะ Build และ link OCI ให้อัตโนมัติ โดยใช้ oci8.pc ที่เราสร้างไว้
  • ถ้าไม่มีอะไรผิดพลาด (error report) ถือว่าเสร็จเรียบร้อย
  • ทดสอบ
    • เข้าไปที่ $GOPATH/src/src/github.com/mattn/go-oci8/example
    • แก้ไฟล์ oracle.go บรรทัด 
      • db, err := sql.Open("oci8", "scott/tiger")
      • เป็น config ของ ORA ของเรา
      • ของผมเป็นแบบนี้ครับ
      • db, err := sql.Open(
        "oci8", "system/manager@192.168.1.40:1521/hello")
    • เรียก $go run oracle.go

ไม่พบ Error เสร็จ เรียบร้อย...
Modified และ Fix error จาก : https://github.com/Centny/Centny/blob/master/Articles/How%20build%20github.com:mattn:go-oci8.md 

เขียน Windows Services ด้วย Golang

ช่วงปีหลัง หนีไม่พ้นต้องเขียนอะไรที่เกี่ยวกับ Windows อย่างหลีกเลี่ยงไม่ได้ พอจะตัดสินใจใช้ Golang คำถามที่ตามมาคือแล้วถ้าต้องเขียน services ล่ะ ใช้ golang ได้รึเปล่า

คำตอบวันนี้คือ "ได้"

ผมเอา code จาก RESTful ของ Blog ที่แล้วมาใช้ทำ service โดยการ compile code บน windows ให้เป็น .exe ก่อน

> go build restmain.go

จะได้ restmain.exe จากนั้นผมใช้ NSSM - the Non-Sucking Service Manager (มาพร้อม source code และ execute binary) unzip แล้ว

> nssm McDuckService [path to]restmain.exe

เรียบร้อยลองเปิด service management อ่า... McDuckService มาแล้ว Double Click แล้ว Start เลย... 


[เพิ่มเติม]

  • เวลาจะลบ service ออกใช้
    • > sc delete McDuckService
  • command line ที่เกียวกับ service ต้องเป็น administrator right 
  • อ้างอิง http://sanatgersappa.blogspot.com/2013/07/windows-service-with-go-easy-way.html
    • เขียนก่อนผมเกือบปี ใช้ nssm เหมือนกันเลย





วันศุกร์ที่ 14 กุมภาพันธ์ พ.ศ. 2557

ลองเขียน REST API ด้วย Go (with framework)

ก่อนนี้ลองเขียน web application ด้วย pure Go มาแล้ว และได้เห็นพิษสงของ Go พอสมควร คราวนี้ขอลอง REST API สักหน่อย แต่อยากได้แบบ ง่ายมากๆ ประมาณ KISS เลย (Keep It Siimple & Studip) เลยขอลองใช้ Framework ในตลาดดูบ้าง

ผมเลือก http://github.com/ant0ine/go-json-rest มาลองเล่นเป็นตัวแรก เริ่มเลย...

สร้างไฟล์ชื่อ restmain.go (หรืออะไรก็ได้)
แทรก import ก่อน

import (
   "net/http"
   "github.com/ant0ine/go-json-rest"
)

แล้วสร้าง func main

func main() {
handler := rest.ResourceHandler{}
handler.SetRoutes(
rest.Route{"Get", "/users/:id", GetUser},
)
http.ListenAndServe(":8080", &handler)
}

อธิบายได้ว่า ประกาศ handler ด้าย rest ของ go-json-rest แล้ว SetRoutes ไปที่ url ที่เราต้องการ จากนั้นยัดเข้าท่ามาตรฐานของ net/http package ของ Go

ไปดูที่ handler กัน

func GetUser(w *rest.ResponseWriter, req *rest.Request) {
user := User{
Id:   req.PathParam("id"),
Name: "McDuck",
}
w.WriteJson(&user)
}

ทดลองง่ายๆ ด้วยการส่งอะไรมา (ผ่าน url parameter) ก็จัดการส่งกลับ (ด้วย json format) เราก็ต้องสร้าง struct สำหรับการเก็บข้อมูลก่อนแปลงเป็น json

type User struct {
Id   string
Name string
}

แล้วก็ assign (:=) user โดยมี Id = ค่า id ที่ส่งผ่านมาทาง url parameter และ name (อันนี้ใส่เอง) จากนั้นใช้ res.ResponseWriter เขียน Json กลับไปที่ resquester

ก่อน run set GOPATH ก่อน (path เดียวกับ ที่เราเขียน code ด้านบน...)
$ export GOPATH=`pwd`

(windows platform ไปหาทางเอาเอง นาทีนี้กำลังงอล Windows,  Hang อยู่ได้ แม่ม...)

จากนั้นเรียก
$ go get .
go จะ download source มาจาก github.com/ant0ine/go-json-rest ไว้ที่ src ให้เรา จากนั้น
$ go run restmain.go

แล้วเปิด browser ไปที่ http://localhost:8080/users/1001100
Result :

{
  "Id": "10010",
  "Name": "McDuck"
}

[ง่ายดีจังตังค์อยู่ครบ]

source restmain.go

//-------------------------------------------------------------
package main

import (
 "github.com/ant0ine/go-json-rest"
 "net/http"
)

type User struct {
 Id   string
 Name string
}

func GetUser(w *rest.ResponseWriter, req *rest.Request) {
 user := User{
  Id:   req.PathParam("id"),
  Name: "McDuck",
 }
 w.WriteJson(&user)
}

func main() {
 handler := rest.ResourceHandler{}
 handler.SetRoutes(
  rest.Route{"Get", "/users/:id", GetUser},
 )
 http.ListenAndServe(":8080", &handler)
}

//-------------------------------------------------------------

วันพุธที่ 12 กุมภาพันธ์ พ.ศ. 2557

Tough Week : Oracle ODBC บน Linux

เป็นอาทิตย์ที่ยากลำบากมากช่วงหนึ่งแต่ก็ผ่านมาได้ จนต้อง blog ไว้เผื่อมีประโยชน์กับ ตัวเองและคนอื่น :)

กว่าสองเดือนที่ผมได้เขียน Server สำหรับการทำ Semi-Realtime transaction processing server (ตั้งชื่อให้มันยากๆ ไปงั้นแหละ) โดยใช้ Erlang ในการพัฒนา โจทย์ก็คือต้องเก็บ transaction ที่ส่งผ่านเข้ามาเป็น XML จากนั้น Encode XML เก็บเข้า Oracle แล้ว Archive XML ไว้ใน Document Base สักตัว (ผมเลือกใช้ MongoDB)

แรกๆ ใช้ Erlang OCI (Oracle Call Interface) ก็ดูดี แต่พบว่า มีปัญหาหลายส่วน จึงตัดสินใจเปลี่ยนจากการ code โดยการ generate SQL statement ใน Erlang แล้วยิง Statement ผ่าน OCI มาเป็นการเรียกน Stored Procedure ผ่าน ODBC แทน

Code เสร็จแล้ว ถึงเวลาต้อง Deploy แล้ว... ผมเลือก Debian 7.4 ที่ต้องติดตั้งเพิ่มก็มี

  • Erlang (ใช้ R16B03-1) ติดตั้ง package เพิ่มเติมตามนี้
    • sudo apt-get install build-essential libncurses5-dev m4
    • sudo apt-get install openssl libssl-dev
    • sudo apt-get install unixodbc-dev
  • git
  • mongodb (ใช้ repo ของ 10 gen : http://docs.mongodb.org/manual/tutorial/install-mongodb-on-debian/ )
  • และ ODBC 
ปัญหาก็คือ Instance Client ของ Oracle (download จาก http://www.oracle.com/technetwork/database/features/instant-client/index-097480.html ) support .rpm ผมใช้ alien แปลง .rpm เป็น .deb ซึ่งก็ติดตั้งได้ แต่ไม่สามารถ connect กับ server ได้ เลยเปลี่ยนไปจาก Debian  ไปเป็น CentOS แทน เพราะไม่รู้ว่า ปัญหาจริงๆ คืออะไร

ย้ายบ้านมา CentOS (ขั่วคราว)
ข้อดีของ VMWare คือตรงนี้แหละ อยากลอง Distro ไหนง่ายนิดเดียว
ติดตั้ง CentOS เสร็จ ไม่ต้องคิดมาก download rpm จาก Oracle's instance-client site page มาเลย ไม่ต้องเยอะเอาแค่
  • oracle-instantclient12.1-basic-12.1.0.1.0-1.x86_64.rpm 
  • oracle-instantclient12.1-sqlplus-12.1.0.1.0-1.x86_64.rpm
  • instantclient-odbc-linux.x64-12.1.0.1.0.zip 
แล้ว $sudo rpm -ivh oracle-instanceclient12.1-basic-12.1.0.1.0-1.x86_64.rpm ไปก่อน ตากด้วย $unzip  instantclient-odbc-linux.x64-12.1.0.1.0.zip แล้ว copy libsqora.so.12.1 ที่ได้จากการ unzip ไปไว้ที่ /usr/lib/oracle/12.1/client64/lib

จากนั้นติดตั้ง unixODBC 
$sudo yum install unixODBC

export environment 

$ export ORACLE_HOME=/usr/lib/oracle/12.1/client64
$ export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$ORACLE_HOME/lib

$ export TWO_TASK=//xxx.xxx.xxx.xxx:1521/listener
(note : listener ต้องตรงกับ server นะครับ)

note : ผมสร้าง oracle.conf ที่มี 
/usr/lib/oracle/12.1/client64/lib
หนึ่งบรรทัด ไว้ที่ /etc/ld.so.conf.d แล้วเรียก 
$ sudo ldconfig
หนึ่งที ด้วย :D

จากนั้นก็ไป copy tnsnames.ora มาจาก server (server ผมเป็น windows 7) เอามาจาก c:/app/[username]/product/11.2.0/dbhome_1/network/admin (ลองไล่ๆ ดูนะครับ แต่ละเครื่องไม่เหมือนกัน อยู่ที่การติดตั้ง)
ผมแก้ tnsnames.ora นิดหน่อยครับ เหลือแต่ 


_SID_NAME_ =
  (DESCRIPTION =
    (ADDRESS_LIST =
      (ADDRESS = (PROTOCOL = TCP)
        (HOST = xxx.xxx.xxx.xxx)
        (PORT = 1521)
      )
    )
    (CONNECT_DATA =
      (SERVER = DEDICATED)
      (SERVICE_NAME = _SID_NAME_)
    )
  )

_SID_NAME_ ตรงกับ SID ที่ server 
ไปตามอ่านใน oracle document พบว่าให้สร้าง tnsnames.ora ไว้ที่ $ORACLE_HOME/network/admin ก็ไปสร้างไว้ตรงนั้นตามเอกสาร 

จากนั้นสร้าง ODBC's drive profile ใน /etc/odbcinst.ini
ตามนี้ครับ 

[OracleODBC-12g]
Description = Oracle ODBC driver for Oracle 12g
Driver64 = /usr/lib/oracle/12.1/client64/lib/libsqora.so.12.1
FileUsage = 1
Driver Logging = 7

เน้นนะครับ : Driver64 เพราะผมลง x86_64 ถ้า google จะเจอแต่ Driver เฉยๆ (งมตั้งนาน) 
แล้วชี้ไปที่ libsqora.so.12.1 ที่ได้จากตอนที่เรา unzip ตอนต้นๆ

จากนั้นสร้าง DSN ใน /etc/odbc.ini ตามนี้ครับ

[simple]
Driver = OracleODBC-12g
DSN = OracleODBC-12g
ServerName = _SID_NAME_
UserID = _USER_
Password = _PASSWORD_

Note : Driver ต้องตรงกับที่เรากำหนดไว้ใน odbcinst.ini, ServerName ตรงกับ _SID_NAME_ ใน tnsnames.ora ส่วน _USER_ _PASSWORD_ ก็ตรงกับ oracle server ที่เราตั้งไว้

ทดสอบ....
ทดสอบ DSN ที่ส้รางขึ้นด้วย 
$ isql simple
simple คือชื่อ DSN ที่เราสร้างขึ้นใน odbc.ini

ผลลัพท์
+---------------------------------------+
| Connected!                            |
|                                       |
| sql-statement                         |
| help [tablename]                      |
| quit                                  |
|                                       |
+---------------------------------------+

ผมเจอ error ไม่ต่ำกว่า 4-5 ตัว (แต่ละตัวทำเอามึน) เช่น
Error ORA-12154 :  TNS:could not resolve the connect identifier specified
อันนี้ตำแหน่ง tnsnames.ora อยู่ผิดที่ 

Debug ด้วย Log
หากพบปัญหาตอนเรียก isql ให้ใส่  -v เพื่อดู ORA error number แล้ว google error number ดู ถ้ายังไม่ได้อีก ผมใช้ strace ตามนี้

$ strace -fac -o isql.log isql -v simple

เช่นตอนกำหนดตำแหน่ง tnsnames.ora ผิด ผมเปิด isql.log พบว่า นั่นไง by default isql จะเปิด tnsnames.ora จาก 
"/home/[user]/.tnsnames.ora"
"/etc/tnsnames.ora"
"/usr/lib/oracle/12.1/client64/lib/network/admin/tnsnames.ora"
เป็นต้น

การใช้ sqlplus64 ตามด้วย custom tns connect ORA server ได้ ไม่ได้หมายความว่า ODBC จะ connect ได้ เช่น 
$ sqlplus64 user/password@//XXX.XXX.XXX.XXX/_SID_NAME_
connect ได้ query ได้ แต่ isql ไม่ works

การติดตั้ง sqleveloper Connect ORA server ได้ ไม่ได้หมายความว่า ODBC จะ connect ได้ เช่นกัน

อาจไม่ cover ทุก error ที่เจอ แต่ใคร install Oracle ODBC Driver บน Linux แล้วพบปัญหาลองแลกเปลี่ยนกันดูครับ 

ผมยังใหม่กับ Oracle มาก :D