node.js入門

node.jsとは

Node.jsはサーバー側で動作するJavaScriptであり、Web業界ではかなり注目されています。大量の処理に対応するために、ノンブロッキングI/Oというモデルを採用されている

いまアツいJavaScript!ゼロから始めるNode.js入門〜5分で環境構築編〜

node.js(イベントループ)

node.jsは大量のリクエストを処理していくための仕組みであるが、これまでもApacheのようにスレッドモデルの仕組みが使われていた。

スマホなどで膨大なリクエストが来た場合にどうしても対応できない問題が発生してきたのでnode.jsはイベントループという仕組みを採用した。

  • スレッドモデル

リクエストが来た時にスレッドという処理が立ち上がる。 次のリクエストが来た場合、スレッドは同時に一つのリクエストしか処理できないため新しいスレッドを作って対応することになる。 スレッドを立ち上げるにはメモリを消費するのでたくさんのリクエストが来ると、スレッドを立ち上げることができなくなり、リクエストが待ち状態になってしまう。   

  • イベントループ

そんなスレッドモデルに代わって出てきたのがイベントループ。メインスレッドとバックグランドで動いているスレッドがある。 メインスレッドには処理待ちのキューがあって、そこに処理がたまっている間、ループを回して別のリクエストが来た場合キューに登録していく。 そしてループが回ってキューに何かが登録されているのを見たら順にバックグラウンドの処理に回す。 ただ処理が終わる順番がわからない上、ループをブロックしないように書かなくてはならない。

Webサーバーを作成する

  • server.js
//httpとfsを読み込む
var http = require('http'),
    fs = require('fs');
//settingsファイルを読み込む
var settings = require('./settings');
var server = http.createServer();

//serverにリクエストが来た時の処理
server.on('request', function(req, res) {
    //ファイル読み込む
    fs.readFile(__dirname + '/public_html/hello.html', 'utf-8', function(err, data) {
        if (err) {
            res.writeHead(404, {'Content-Type': 'text/plain'});
            res.write("not found!");
            return res.end();            
        }
        //書き出す
        res.writeHead(200, {'Content-Type': 'text/html'});
        res.write(data);
        res.end();
    });
});
//serverの設定を読み込む
server.listen(settings.port, settings.host);
console.log("server listening ...");
  • settings.js
exports.port = 1337;
exports.host = '192.168.0.11'; //localhost
  • public_html/hello.html
<html>
<h1>Hello!</h1>
</html>

ターミナルにて以下コマンドを実行する

node server.js

f:id:suga-tech3:20160912234401p:plain

mysql × node.js

npm install mysqlでモジュールインストール

var mysql = require('mysql');

//mysql接続情報
var connection = mysql.createConnection({
  host  : 'localhost',
  user  : 'user_name',
  password : 'password',
  database : 'database_name' 
});

//sql作成
var sql = 'SELECT * FROM user WHERE id = ?;';

var userId = "1";

//接続する
connection.connect();

//プレースホルダー使用
var query = connection.query(sql, [userId]);

query
//error処理
.on('error', function(err) {
  console.log('err is: ', err);
})
//結果
.on('result', function(rows) {
    console.log('The res is: ', rows );
})
//終了
.on('end', function(rows) {
  console.log('end');
  connection.destroy(); //接続終了
});

上記ソースを以下コマンドで実行

$ node mysql.js
The res is:  RowDataPacket { id: 1, nickname: 'kosachan' }

上記データが取得できます。 node.jsまだ入門ですが次回はフレームワークexpressと合わせたコードを書いていけたらと思います。

MacにAzure入れてみた

Azureとは

Windows Azureは世界規模に展開されているクラウドサービスで、ユーザーが数人の小規模なサービスから、数百万人規模の大量にアクセスが発生するような大規模なサービスまで、さまざまなクラウドサービスに対応し、開発や提供することができます。

thinkit.co.jp

インストール方法

CLIインストール

Azure SDK とツールのダウンロード | Azure

  • npm パッケージのインストール
    • Node.js と npm がインストールされている必要があります

[Mac] Node.js と npm をインストールする方法 [Homebrew] | CodeNote.net

次のコマンドを使用して Azure CLI パッケージをインストールします。

npm install azure-cli -g

f:id:suga-tech3:20160910094349p:plain


azureコマンドが使うことができるか確認する

azure

f:id:suga-tech3:20160910094401p:plain

他にも azure help

azure version

でazureの情報を確認することができました。

Azure ログインを使用した対話型認証

引数なしで azure login コマンドを使用して、次のいずれかによる対話型認証を行います。

  • 多要素認証を必要とする職場または学校のアカウント ID (組織アカウントとも呼ばれる)

  • Microsoft アカウント ID (Resource Manager モード コマンドにアクセスする場合)

azure login                                                                                                                                                                                         
info:    Executing command login
info:    To sign in, use a web browser to open the page http://aka.ms/devicelogin. Enter the code XXXXXXXXX to authenticate. 

上記操作で表示されたコードをコピーし、ブラウザーを開いて http://aka.ms/devicelogin (または指定された場合は別のページ) にアクセスする。コードを入力すると、使用する ID のユーザー名とパスワードを入力するように求められます。このプロセスが完了すると、コマンド シェルによってログイン プロセスが完了します。

info:    Added subscription Visual Studio Ultimate with MSDN
info:    Added subscription Azure Free Trial
info:    Setting subscription "Visual Studio Ultimate with MSDN" as default
+
info:    login command OK

次にアカウントの発行設定ファイルをダウンロードするには、次のコマンドを使用します。

azure account download

f:id:suga-tech3:20160910095437p:plain

ここでサブスクリプションが取得できるはずがないというメッセージ ここでつまづきましたがmicrosoftのアカウントは作成しているがazureの無料試用版のアカウントは登録してないので取得できない。

azure.microsoft.com

以下URLで必要情報を入力し再度 azure account downloadをすると

ブラウザが立ち上がり f:id:suga-tech3:20160910100112p:plain

証明書のpublishsettingsファイルがダウンロードされる

次に 先ほどダウンロードしたファイルをimportする

azure account import <file>

次回は仮想マシンの作成から進めていきたいと思います。

GinでORM(xorm)を使ってみる

Gin API実装

エウレカさんのチュートリアルに沿ってやってみた

Go言語製WAF GinでWebアプリを作ってみる【準備編】 | eureka tech blog

モデル(Model)設計

modelsディレクトリにuser.goを作成する

  • Repository構造体定義
  • Repository構造体に「DBからデータを取得する処理」を定義
  • RepositoryからUser構造体を生成する処理を実装

ソースは以下のとおり

package models
 
import (
    "github.com/go-xorm/xorm"
    _ "github.com/go-sql-driver/mysql"
    "fmt"
)
 
var engine *xorm.Engine
 
 
// init ...
func init() {
    var err error
    engine, err = xorm.NewEngine("mysql", "root:pass@/golang")
    if err != nil {
        panic(err)
    }
}

// User is
type User struct {
    ID       int    `json:"id" xorm:"'id'"`
    Username string `json:"name" xorm:"'nickname'"`
}

// NewUser ...
func NewUser(id int, username string) User {
    return User{
        ID:       id,
        Username: username,
    }
}
 
// UserRepository is
type UserRepository struct {
}
 
// NewUserRepository ...
func NewUserRepository() UserRepository {
    return UserRepository{}
}
 
// GetByID ...
func (m UserRepository) GetByID(id int) *User {
    var user = User{ID: id}
    has, hage := engine.Get(&user)
    if has {
        return &user
    }
 
    return nil
}

xormとは

Go言語のORM ドキュメント

Documentation - xorm: Simple & Powerful ORM Framework for Go Programming Language

1.インストール

$ go get github.com/go-xorm/xorm

2.まずデータベースのためのEngineを作る

engine, err := xorm.NewEngine(driverName, dataSourceName)

user.goのソース上では

engine, err = xorm.NewEngine("mysql", "root:pass@/golang")

driveNameにDB、dataSourceNameにユーザ名:パスワード/ DB名をそれぞれ指定する。

3.ORM Methods(基本)

  • Insert
affected, err := engine.Insert(&struct)
//INSERT INTO struct () values ()
  • 1レコード参照
has, err := engine.Get(&user)
//SELECT * FROM user LIMIT 1
  • 複数レコード参照
sliceOfStructs := new(Struct)
err := engine.Find(sliceOfStructs)
// SELECT * FROM user
  • update文
affected, err := engine.Id(...).Update(&user)
// UPDATE user SET ...
  • delete文
affected, err := engine.Where(...).Delete(&user)
// DELETE FROM user Where ...
  • レコードcount
counts, err := engine.Count(&user)
// SELECT count(*) AS total FROM user

他にも複数条件や並び替えなどの指定は以下ドキュメントを参照する github.com/go-xorm/xorm - Go Walker

*Xormはまた、生のSQL実行をサポートしています

results, err := engine.Query("select * from user")

mysqlのドライバーもinstall忘れずに

$ go get github.com/go-sql-driver/mysql

次に

xorm:”‘カラム名'”とアノテーションを入れることで、構造体のフィールドとデータベースのカラムをマッピング

type User struct {
    ID       int    `json:"id" xorm:"'id'"`
    Username string `json:"name" xorm:"'nickname'"`
}

Controller実装

modelのNewUserRepositoryからGetByIDの中身を取得して返すcontrollerを作成

package controllers
 
import (
    "golang/models"
)
 
// User
type User struct {
}
 
// NewUser
func NewUser() User {
    return User{}
}
 
// Get 
func (c User) Get(n int) interface{} {
    repo := models.NewUserRepository()
    user := repo.GetByID(n)
    return user
}

main.go実装

main.goにuser_idユーザー情報を返します。

package main
 
import (
    "golang/controllers"
    "github.com/gin-gonic/gin"
    "reflect"
    "strconv"
)
 
// main ...
func main() {
    router := gin.Default()
    router.GET("/:id", func(c *gin.Context) {
        // Pramを処理する
        n := c.Param("id")
        id, err := strconv.Atoi(n)
        if err != nil {
            c.JSON(400, err)
            return
        }
        if id <= 0 {
            c.JSON(400, gin.H{"status": "id should be bigger than 0"})
            return
        }
        // データを処理する
        ctrl := controllers.NewUser()
        result := ctrl.Get(id)
        if result == nil || reflect.ValueOf(result).IsNil() {
            c.JSON(404, gin.H{})
            return
        }

        c.JSON(200, result)
    })
    router.Run(":8080")
}

ここまででhttp://localhost8080/:idを指定すると DBに入っている情報を参照してデータを返してくれることが分かりました。

ここまではチュートリアルなので次回はviewなどを作り簡易なサイトを作成できればと思います。

github.com

Mysqlをローカルに入れてみる(Mac)

Mysqlインストール

前回の記事でGoのwebフレームワークを使ってAPIを実装するにあたりローカルにMysqlを用意してMVCを実装してみる。

以下コマンドでインストール

$ brew install mysql

mysqlのバージョンを確認する

$ brew info mysql
mysql: stable 5.7.14 (bottled)
Open source relational database management system
https://dev.mysql.com/doc/refman/5.7/en/
Conflicts with: mariadb, mariadb-connector-c, mysql-cluster, mysql-connector-c, percona-server
/usr/local/Cellar/mysql/5.7.14 (13,467 files, 447M) *
  Poured from bottle on 2016-09-02 at 00:12:25

サーバーを開始してみる

$ mysql.server start
Starting MySQL
. SUCCESS! 

mysqlにログインしてみる

$ mysql -uroot
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 1
Server version: 5.7.14  Homebrew

Copyright (c) 2000, 2014, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> 

こんな感じでログインできたことがわかります。

次にパスワードを設定してみる

Mysqlでのパスワード設定

ここで少しつまづく

$ mysql_secure_installation

ここでpaswordの強度を聞かれる - Low - Medium - Strong

何も確認せずとりあえずMediumを使って半角英数字小文字と数字で設定すると 以下エラーが出る

ERROR 1819 (HY000): Your password does not satisfy the current policy requirements

MySQL 5.7.8以降のrpmパッケージではvalidate_passwordプラグインがデフォルトで有効になっているらしい

mysql> SHOW VARIABLES LIKE 'validate_password%';
+--------------------------------------+--------+
| Variable_name                        | Value  |
+--------------------------------------+--------+
| validate_password_dictionary_file    |        |
| validate_password_length             | 8      |
| validate_password_mixed_case_count   | 1      |
| validate_password_number_count       | 1      |
| validate_password_policy             | MEDIUM |
| validate_password_special_char_count | 1      |
+--------------------------------------+--------+

どうやら特殊文字と半角大文字が一文字ずつ必要だったらしい

今回はローカルでしか使わないのでセキュリティレベルを落としてパスワード作成

mysql>  SET GLOBAL validate_password_policy=LOW;
Query OK, 0 rows affected (0.00 sec)

そしてもう一度以下コマンドで設定していくとパスワードを設定することができます。

$ mysql_secure_installation

ログインしてみる

$ mysql -u root -p
Enter password: 
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 14
Server version: 5.7.14 Homebrew

Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql>

無事ログインできました。

おまけ(mysqlセキュリティポリシー)

  • LOW ポリシーは、パスワードの長さのみテストします。パスワードは少なくとも 8 文字の長さでなければなりません。

  • MEDIUM ポリシーは、パスワードが最低 1 つの数値文字を含み、1 つの小文字および大文字を含み、1 つの特殊文字 (英数字以外) を含む必要があるという条件を追加します。

  • STRONG ポリシーは、パスワードの 4 文字以上の部分文字列が、(辞書ファイルが指定された場合に) 辞書ファイル内の単語と一致してはならないという条件を追加します。

MySQL :: MySQL 5.6 リファレンスマニュアル :: 6.1.2.6 パスワード検証プラグイン

Gin入門編やってみた(Mac)

Ginとは

Go言語のWebFrameworkの一つです。

参考

エウレカさんのチュートリアルを途中まで再現してみました。 APIですこしつまづいているのでAPI実装は次回の記事で

Go言語製WAF GinでWebアプリを作ってみる【準備編】 | eureka tech blog

Goのインストール

$ brew install go

macでは一発でインストールできますがwindowsではすこし手間がかかりますが簡単にインストールできます。

goのバージョンを確認

$ go version
go version go1.6.2 darwin/amd64

Ginインストール

Goの触れる環境を作成したら次にGinをインストールしてみる。 まずはGOPATHとGOROOTの設定

export GOPATH="$HOME/repos/gohome"
export GOROOT="/usr/local/opt/go/libexec"
export GOBIN="$GOPATH/bin"
export PATH="$GOBIN:$GOROOT/bin:$PATH"

設定が反映されているか確認してみる

$ go env
GOARCH="amd64"
GOBIN="/bin"
GOEXE=""
GOHOSTARCH="amd64"
GOHOSTOS="darwin"
GOOS="darwin"
GOPATH="/Users/home/code/golang"
GORACE=""
GOROOT="/usr/local/opt/go/libexec"
GOTOOLDIR="/usr/local/opt/go/libexec/pkg/tool/darwin_amd64"
GO15VENDOREXPERIMENT="1"
CC="clang"
GOGCCFLAGS="-fPIC -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fno-common"
CXX="clang++"
CGO_ENABLED="1"

ここまでできたらGinをインストールしてみる

$ go get github.com/gin-gonic/gin

これでGinがインストールされます。

HelloWorld

ブラウザで確認してみる 1.まずは開発用のディレクトリを作成し main.goファイルを作成する。

package main
 
import (
    "github.com/gin-gonic/gin"
)
func main() {
    r := gin.Default()
 
    r.GET("/", func(c *gin.Context) {
        c.String(200, "Hello world")
    })
 
    r.GET("/hoge", func(c *gin.Context) {
        c.String(200, "fuga")
    })
    r.Run(":8080")
}

サーバー起動

$ go run main.go
[GIN-debug] GET    /                         --> main.main.func1 (3 handlers)
[GIN-debug] GET    /hoge                     --> main.main.func2 (3 handlers)
[GIN-debug] Listening and serving HTTP on :8080
[GIN] 2016/09/01 - 22:42:19 | 200 |      94.169µs | ::1 |   GET     /
[GIN] 2016/09/01 - 22:42:27 | 404 |       1.202µs | ::1 |   GET     /fuga
[GIN] 2016/09/01 - 22:42:38 | 200 |       6.408µs | ::1 |   GET     /hoge

localhost:8080にアクセスすると f:id:suga-tech3:20160904175557p:plain

localhost:8080/hogeでアクセスするとfugaがでてきます。

サーバにアクセスするごとにコマンドラインでGETリクエストがきていることがわかります。

次回はつまづいているAPIを実装してみようと思います。

1週間Go言語を勉強して登壇してみた vol.2

Go言語 × SlackAPI

この記事ではGo言語とslackAPIを用いてメッセージを送ってみようと思う

Slackとは

主にビジネス向けのチャットツールを使っている Slackは2013年8月のリリース後、わずか2年半でDAU200万を突破、2016年5月時点ではDAU300万を突破した、アメリカ発のサービスである。

背景

このままではLTでGoの文法紹介になってしまうため 何かものを作ろうと思った。

台風が最近多く天気が不安なことがあるのでlivedoorの天気予報RSSを利用して明日の天気と気温を通知してくれるサービスを作ってみた。

SlackAPI(Incomming WebHooks)を使用する(LT登壇まであと3日)

  • Incomming WebHooksとは?

=Slackが発行するURLへPOSTするとSlackにメッセージが投稿される簡単にメッセージを送ることができるAPIである。

Incoming Webhooks | Slack

上記からTokenを取得する。

Goを使ってslackに通知してみた(LT登壇まであと2日)

http.clientを使ってGETリクエストを送信してみた。

実際のソースは以下である

package main

import (
        "bytes"
        "net/http"
        "net/url"
        "fmt"
)

var(
        token  string = ""//Token追記
        apiUrl string = "https://slack.com/api/chat.postMessage"
)


func main() {
        data := url.Values{}
        data.Set("token",token)
        data.Add("channel","#general")
        data.Add("username","testBot")
        data.Add("text", "test")

        client := &http.Client{}
        r, _ := http.NewRequest("POST",  fmt.Sprintf("%s",apiUrl), bytes.NewBufferString(data.Encode()))
        r.Header.Add("Content-Type", "application/x-www-form-urlencoded")

        resp, _ := client.Do(r)
        fmt.Println(resp.Status)
}

実行するとこれだけでslackにメッセージを送ることができます。

Goでslackに天気予報通知してみた(LT登壇まであと1日)

登壇まであと残すところあと1日

以下のlivedoorRSSを用いて東京の天気予報を取得する。

http://weather.livedoor.com/forecast/rss/area/130010.xml

package main

//packageをimport
import (
        "bytes"
        "net/http"
        "net/url"
        "fmt"
        "encoding/xml"
        "io/ioutil"
        "log"
)
//変数宣言
var(
        //SlackAPIトークン
        token  string = ""
        //SlackApiのメッセージリクエストURL
        apiUrl string = "https://slack.com/api/chat.postMessage"
        //livedoor天気予報のRSS(東京)
        FEED_URL string = "http://weather.livedoor.com/forecast/rss/area/130010.xml"

)

//構造体
type WeatherHack struct {
    Title string `xml:"channel>title"`
    Description []string `xml:"channel>item>description"`
}


func main() {
        //getWeather関数にRSSのURLを渡す
        wh, err := getWeather(FEED_URL)

        //error処理
        if err != nil {
            log.Fatalf("Log: %v", err)
            return
        }

        //URLに生成するデータを追加
        data := url.Values{}
        data.Set("token",token)
        data.Add("channel","#general")
        data.Add("username","天気予報Bot")
        data.Add("icon_url","http:~~~.jpg")
        data.Add("text", wh.Description[2])
        fmt.Println(data)

        //http.clientを使用してGETリクエスト送信
        client := &http.Client{}
        r, _ := http.NewRequest("POST",  fmt.Sprintf("%s",apiUrl), bytes.NewBufferString(data.Encode()))
        r.Header.Add("Content-Type", "application/x-www-form-urlencoded")

        resp, _ := client.Do(r)
        fmt.Println(resp.Status)
}

//天気情報を取得する
func getWeather(feed string) (p *WeatherHack, err error) {

        //http.Getメソッドを利用してGETリクエストを送る
        res, err := http.Get(feed)
        if err != nil {
            return nil, err
        }

        b, err := ioutil.ReadAll(res.Body)
        if err != nil {
            return nil, err
        }
        wh := new(WeatherHack)
        err = xml.Unmarshal(b, &wh)

        return wh, err
}

上記をターミナルにて以下コマンドを実行する

go run ファイル名.go

実行ができたら 200 OKと返ってくる

slackに通知が来ているので確認してみると 以下のように天気が通知されていることがわかります。

f:id:suga-tech3:20160827175318p:plain

無事本田翼さんから天気予報が届いたことが確認できました。

Goをしてみたまとめ

  • コンパイル型を使ったことがそんなに多くないので型宣言を忘れがちになった

  • 1週間でここまでしたがまだまだメソッドなど多く奥が深い

  • GETリクエスト以外にもPOSTだけで5通りもの方法がある

  • エラーがわかりやすいため学習が比較的スムーズにできた

Goを一週間してきたことをまとめてみましたが まだまだここからがスタートなので次は並列処理、ポインタの理解をより深めてマスターして記事を公開していきます。

1週間Go言語を勉強して登壇してみた vol.1

Go言語

「Go」は2009年にGoogleによって発表されたコンパイラ型のプログラミング言語である。

YouTubeDropboxUberなどでもサービスのクリティカルな部分にGo言語を採用されている界隈で有名な言語となっている 特にGo言語は処理速度がとても速いため、APIのバックエンドやバッチ処理など「パフォーマンスがネックとなる部分」に採用されることが多いです。Androidの開発にも使える。

Goを始めた理由

  • もともとGoに興味があったがしていなかった。
  • 社外の人との合同LT会(勉強会)で登壇することに
  • LTまでの1週間の間に新言語をどれだけ習得できるのかやってみたかった(5日間は別仕事してるので仕事を終えてからです)

GoTour始めてみた(LT登壇まであと7日)

先輩エンジニアの人に勧めていただいたGoTourを3日で終わらようと決意

A Tour of Go

f:id:suga-tech3:20160827163406p:plain

phpと違うなと感じたこと(LT登壇まであと6日)

GoTourを文法を一通り終わらせてbasicまでやってみて気づいたことを書いてみる。

  • コンパイル型であること
  • 並列処理ができること
  • 変数名の 後ろ に型名を書く
  • 変数に暗黙的な型宣言ができる etc.(k := 3)
  • Goはポインタを扱うことができる
  • defer ステートメントという呼び出し元の関数の終わり(returnする)まで遅延させるものがあること。
  • 特有の構造体を持っていること

goにwhile文がなくfor文でループすることができる。

  • 標準のfor文
func main() {
        for i := 0; i < 10; i++ {
        }
}
  • whileのfor文での記述
func main() {
        for sum < 1000 {
        }
}

  • 並列処理とは
func main() {
        go func() {
        //このブロックは並列して実行される
        }()
        //このブロックは上の記述と同時に実行される
}

go特有の構造体

Goはクラスや継承の機能がないが以下のこれらを用いることでオブジェクト指向プログラミングが可能になる。

  • メソッドの定義
  • 構造体埋め込み

構造体の定義と初期化 - Goの場合、大文字から始まる名前 (関数名、型名、フィールド名) は、他のパッケージからアクセス可能になる

//構造体定義
type User struct {
  name string
  age  int
}
 
//User構造体にアクセス
var u User
u.name = "taro"             
u.age = 30                  
fmt.Printf("name:%s", u.name)
  • Goの構造体は以下のような初期化関数を作るのが一般的である
func newUser(name string, age int) *User {
    u := new(User)
    u.name = name
    u.age = age
    return u
}
 
var user *User = newUser("taro",30)

急いで学ぶGo lang#5 構造体 | Developers.IO

Goで何を作ろうか考える(LT登壇まであと4日)

長くなってきたので次の記事へ続く...。