net/httpパッケージを使ったWebサーバー

            こんにちは、daihaseです。

今日はGoを使った最も簡単なWebサーバーの構築を説明したいと思います。PHP, Java, Ruby... サーバーサイドを実装する言語は多くありますが、このGoを使ったWebサーバーの実装はその中でも最も簡単と言えるくらいです。

Goは非常に強力な言語で、標準パッケージだけでも十分Webサービスを作れてしまいます。今回はWebサーバーとして動かすためにその中の1つ、net/httpについて紹介したいと思います。

Macなど、クライアント側に予めGoの実行環境が整っている前提です。

 

net/httpパッケージ

HTTPサーバーとHTTPクライアントを実装するための必要な機能をパッケージ化したものです。この標準パッケージを実装するコード上でインポートしてやるだけで、後は決まった書き方をしてやるだけで簡単にWebサーバーを立てることが出来るのです。

それでは早速実際のコードを見てみましょう。(httpSample.goとファイル名をつけています)

package main

import (
    "io"
    "log"
    "net/http"
)

func MyHandleFunc(w http.ResponseWriter, req *http.Request) {
    io.WriteString(w, "hello, world!\n")
}

func main() {
    http.HandleFunc("/hello", MyHandleFunc)
    log.Fatal(http.ListenAndServe(":8000", nil))
}

 

はい、たったこれだけでWebサーバーとして動いちゃいます。 コードとしては以下のようなことを行なっています。

 

  1. /hello というルートを生成します。
  2. MyHandleFuncというハンドラーを生成します。
  3. リクエストがルート(/hello)に来るたびにハンドラ関数が実装されます。
  4. Hello, World! という文字列を表示するレスポンスが返ってきます。
  5. ポート8000でWebサーバーを立ち上げます。その際エラーがあればListenAndServeがそのログを返します。

 

パッとこんな感じで処理が行われます。

実際にこちらを実行してみましょう。

$ go run httpSample.go

 

特にターミナル上ではメッセージを出すようにもしてないので、何も動いてないようですが実際はサーバーが立ち上がっています。ブラウザで以下のURLを入力してみましょう。

http://localhost:8000/hello

 

ブラウザの画面に「hello, World!」と文字が表示されたはずです。 どうでしょう、呆気にとられるくらい簡単にWebサーバーが立ち上がったかと思います。

それでは良い開発ライフを〜

曜日や日付を簡単に取得するExtension

            こんばんは、daihaseです。1ヶ月ぶりくらいのブログです。

今日は大好きなSwiftネタを久々に。 Dateを使った日付表示とかはツール系のアプリでもよく出てきますね。 その度に毎回 let dateFormatter = DateFormatter() なんて書いてたら微妙なんで、ここはDateクラスをExtensionで拡張してどこからでも曜日や日付を取得出来るようにしてみましょう。

以下、Dateを拡張したDate+Ex.swiftになります。

import Foundation

extension Date {
    enum FormattedStyle {
        case longDate
        case longDateAndTime
        case shortDateAndTime
        case time
    }
    
    func format(with format: String) -> String {
        let formatter = DateFormatter()
        formatter.dateFormat = format
        formatter.locale = Locale(identifier: "en_US_POSIX")
        return formatter.string(from: self)
    }
    
    var formattedWeekday: String {
        let formatter = DateFormatter()
        formatter.locale = Locale(identifier: "en_US_POSIX")
        let symbol = formatter.weekdaySymbols[Calendar.current.component(.weekday, from: self) - 1] 
        switch symbol {
        case    "Sunday": return "(日)"
        case    "Monday": return "(月)"
        case   "Tuesday": return "(火)"
        case "Wednesday": return "(水)"
        case  "Thursday": return "(木)"
        case    "Friday": return "(金)"
        case  "Saturday": return "(土)"
        default: assertionFailure("error"); return ""
        }
    }
    
    func formattedDateWith(style: FormattedStyle) -> String {
        switch style {
        case .longDate:
            return format(with: "yyyy/M/d\(formattedWeekday)")
        case .longDateAndTime:
            return format(with: "yyyy/M/d\(formattedWeekday) H:mm")
        case .shortDateAndTime:
            return format(with: "M/d\(formattedWeekday) H:mm")
        case .time:
            return format(with: "H:mm")
        }
    }
}

 

formatは引数にセットする文字列のフォーマットに沿って日付データを返すメソッドです。formattedWeekdayコンピューテッドプロパティで、該当する曜日を文字列で返してくれます。最後にformattedDateWithenumで定義している内容をセットすれば、そのパターンに応じてフォーマットしてくれます。

使い方は簡単、ViewControllerでDateインスタンスを生成し、

let dateAndTime = date.formattedDateWith(style: .longDateAndTime)

 

こうしてやるだけで、2018/6/16(土) 23:24 などと表示されます。こうして拡張してやるだけで、どこからでも簡単に曜日や日付を出力出来るので便利ですよね。簡単なサンプルも以下に用意してみたので良かったら参考にしてみてください。

DateExtension Sample

 

それでは良い開発ライフを〜

Cannot allocate memory for the buffer pool でMySQLが落ちる

            こんにちは、daihaseです。

先日、実は当ブログが一時的に接続出来ない状態となっていました。

直ぐに復旧はしましたが、原因はタイトルにもある「バッファプールへのメモリ割り当てが足りていない」というものでした。

当初このブログもAWSのt1.microで動かしており、その際swap領域も割り当てたのですが以前より今はアクセスも多くなってそれだと厳しかったようです。

実際MySQLのログを見てみると以下のようにエラーが出ていました。

  • /var/log/mysqld.log
170310 21:54:57 InnoDB: Initializing buffer pool, size = 128.0M
InnoDB: mmap(137363456 bytes) failed; errno 12
170310 21:54:57 InnoDB: Completed initialization of buffer pool
170310 21:54:57 InnoDB: Fatal error: cannot allocate memory for the buffer pool
170310 21:54:57 [ERROR] Plugin 'InnoDB' init function returned error.
170310 21:54:57 [ERROR] Plugin 'InnoDB' registration as a STORAGE ENGINE failed.
170310 21:54:57 [ERROR] Unknown/unsupported storage engine: InnoDB
170310 21:54:57 [ERROR] Aborting

 

というわけで今回はswap領域の拡張と、エラーにもある足りてないinnodb_buffer_pool_sizeを再設定します。

 

 

swap領域の拡張

swapファイルの作成

以下コマンドでswapファイルを作成します。

$ sudo mkdir /var/lib/swap
$ cd /var/lib/swap
$ fallocate -l 4G swapfile

 

※今回当ブログを動かしているインスタンスタイプがt1.smallで物理メモリ2GBなため、4GBに設定しました。

 

実際作成されたか確認してみます。

$ ls -lh swapfile
-rw-r--r-- 1 root root 1.0G May 20 14:33 swapfile

 

この作成したファイルをrootでしか権限変更出来ないようにします。

$ sudo chmod 600 swapfile
$ ls -lh swapfile
-rw------- 1 root root 1.0G May 20 14:33 swapfile

 

次にこれをswap領域として設定します。

% sudo mkswap swapfile

 

最後にこのswap領域を有効化します。

% sudo swapon swapfile

 

ではfreeコマンドを使って実際に作成されたか見てみましょう。

 $ free -m
            total       used       free     shared    buffers     cached
Mem:          2003       1503        499          0         28        107
-/+ buffers/cache:       1367        635
Swap:         4095         0        4095

 

-mオプションで単位をMBにして見やすくしてます。ちゃんとSwap領域が4095MB作られていますね。

ちなみに余談ですが、作ったswapファイルは割り当てを解除したり削除もできるので、再利用したり不要になったら削除しましょう。

  • swapファイル割り当て解除
$ sudo swapoff swapfile

 

  • swapファイルの削除
$ sudo rm swapfile

 

これでSwapの拡張は終わりで、次はMySQL側のInnoDBを設定します。

 

InnoDBの設定

innodb_buffer_pool_sizeの変更

MySQLの初期設定では128MBとなっています。

my.confに以下のように追記してサイズを変更しまし。(デフォルトでは/etc/my.confに)

 

[mysqld]
...
innodb_buffer_pool_size=1000M

 

設定を反映するためにMySQLを再起動します。

$ /etc/init.d/mysqld restart

 

あとは実際にMySQLでログインし、以下のコマンドを打って確認します。

mysql> SHOW VARIABLES LIKE 'innodb_buffer_pool_size';
+-------------------------+------------+
| Variable_name           | Value      |
+-------------------------+------------+
| innodb_buffer_pool_size | 1048576000 |
+-------------------------+------------+
1 row in set (0.00 sec)

 

無事サイズが増えていますね。

 

基本的にはSwapが発生してるのであればインスタンスを上位に変更する方がいいのですが、如何せんそこまでアクセスの多いブログでもないため、ある程度これで様子を見てみます。

厳しそうであればもう一つ上位インスタンスへの変更もやむなし...ですかね。

 

なにはともあれ、良い開発ライフを〜

docker: Error response from daemon: driver failed programming external connectivity on endpoint xxx (xxx):x Error starting userland proxy: Bind for 0.0.0.0:80: unexpected error (Failure EADDRINUSE). が出たら...

            こんばんは、daihaseです。

はい、異様に長いタイトルですね。Dockerのエラーなんですが、最初にMacでコンテナを走らせようとすると1度くらいは遭遇するコレ。その解決方法をメモ。

※例えばnginxのイメージからport 80でコンテナ起動した際などよく起きますネ docker container run --name httpServer -d -p 80:80 nginx ←こういうのとか。

 

まぁエラーの内容そのままなんですが、あるコンテナを走らせようとした際に(Port80で)、すでに別のサービスで同じポートを掴んじゃってるため、このようなエラーが出ています。

Macの場合、デフォルトでApacheが入っていてそれがPort80で立ち上がってるため(ブラウザでlocalhost:80と叩いて It works と出たら動いちゃってます)、それを止めれば解決ですね。

一応以下のコマンドで確認。

$ sudo lsof -i -P | grep "LISTEN"

 

環境にもよりますが、自分の場合は以下のように出ました。

Password:
httpd        95           root    4u  IPv6 0x300ff72d6553f801      0t0    TCP *:80 (LISTEN)
xartstora   232           root    3u  IPv4 0x300ff72d63a4abe9      0t0    TCP *:61500 (LISTEN)
xartstora   232           root    4u  IPv6 0x300ff72d65540941      0t0    TCP *:61500 (LISTEN)
httpd       315           _www    4u  IPv6 0x300ff72d6553f801      0t0    TCP *:80 (LISTEN)
EEventMan   382    LibraStudio    5u  IPv4 0x300ff72d63a49929      0t0    TCP *:2968 (LISTEN)
postgres    384    LibraStudio    5u  IPv6 0x300ff72d66df5381      0t0    TCP localhost:5432 (LISTEN)
postgres    384    LibraStudio    6u  IPv6 0x300ff72d66df3101      0t0    TCP localhost:5432 (LISTEN)
postgres    384    LibraStudio    7u  IPv4 0x300ff72d63a4bea9      0t0    TCP localhost:5432 (LISTEN)
Dropbox     418    LibraStudio  119u  IPv6 0x300ff72d6553db41      0t0    TCP *:17500 (LISTEN)
Dropbox     418    LibraStudio  120u  IPv4 0x300ff72d70127be9      0t0    TCP *:17500 (LISTEN)
Dropbox     418    LibraStudio  156u  IPv4 0x300ff72d700fdbe9      0t0    TCP localhost:17600 (LISTEN)
Dropbox     418    LibraStudio  160u  IPv4 0x300ff72d70103be9      0t0    TCP localhost:17603 (LISTEN)
rapportd    419    LibraStudio    3u  IPv4 0x300ff72d63a4b549      0t0    TCP *:49158 (LISTEN)
rapportd    419    LibraStudio    4u  IPv6 0x300ff72d66df4dc1      0t0    TCP *:49158 (LISTEN)
cupsd     36042           root    5u  IPv6 0x300ff72d66df36c1      0t0    TCP localhost:631 (LISTEN)
cupsd     36042           root    6u  IPv4 0x300ff72d717e7be9      0t0    TCP localhost:631 (LISTEN)
LINE      36081    LibraStudio   10u  IPv4 0x300ff72d71966289      0t0    TCP localhost:10400 (LISTEN)

 

ちょうど上から2個目、httpd(これはapacheのプロセスの名称ですね)が80を掴んじゃってるのがわかります。

Macで起動してるapacheを止めちゃいましょう。

$ sudo apachectl stop

 

その後、再度同じ「docker container run --name httpServer -d -p 80:80 nginx」とコマンドを実行して以下のようなエラーが出る時があります。

docker: Error response from daemon: Conflict. The container name "/httpServer" is already in use by container "xxxx". You have to remove (or rename) that container to be able to reuse that name.

 

これも上で一度コンテナを走らせるコマンドを実行したため、既に同じ名前のコンテナが存在しちゃってるので消してしまえばOKです。

$ docker rm "コンテナID"

 

これでコンテナが削除されるので、次はちゃんとnginxのコンテナが起動するかと思います。

 

それでは良い開発ライフを〜

operation not permittedが発生する場合の対処法

            こんばんは、daihaseです。

先日新しいMacBookProを新調したのですが、その際タイトルにある問題が発生したのでその時の対処法をメモ。

自分の場合はcarthageで、gccシンボリックリンクを/usr/bin/gccに対して貼ろうとコマンドを叩く際にタイトルのエラーが出てしまいました。

sudoで実行してるのにも関わらず...

 

これは「rootless」という仕組みによるもので、これによってMacでは/usr/や/System/といった特定のパスに対して書き込みが行えないようになっています。

これを無効にするためには以下を実行。

 

  1. まず、Macを再起動し「command+R」リカバリーモード立ち上げ
  2. 画面上部のメニューから「terminal」を選択し起動
  3. csrutil statusと入力。System Integrity Protection status: enabled.と出るはずなので、これをdisableに変更する。 csrutil disableと改めて入力。
  4. 最後に成功したらrebootと入力し再起動。

 

あとは通常通り起動するので、先ほどエラーが出て怒られたコマンドも無事通るようになると思います。

 

それでは良い開発ライフを〜

MacでRailsのアップデート

            こんばんわ、daihaseです。

GWですね、みなさんGW満喫されてますか? まさか開発なんかされてないですよね? だとしたら相当有意義にお休みを使っておられるかと。

さて、話を戻して... 先月Rails5.2.0がリリースされましたね。そこで今日はRailsのアップデートをメモ。

まずMacということでhomebrewを更新してやります。

$ brew update
$ brew upgrade
$ brew cleanup

 

次にrbenvでついでにRubyのバージョンをあげておきます(※現時点で最新は2.5.1) ebenvはインストール済み前提で...

$ rbenv install 2.5.1
ruby-build: use openssl from homebrew
Downloading ruby-2.5.1.tar.bz2...
-> https://cache.ruby-lang.org/pub/ruby/2.5/ruby-2.5.1.tar.bz2
Installing ruby-2.5.1...
ruby-build: use readline from homebrew
Installed ruby-2.5.1 to /Users/LibraStudio/.rbenv/versions/2.5.1

$ rbenv global 2.5.1
$ rbenv rehash
$ rbenv versions
system
  2.0.0-p648
  2.2.0-rc1
  2.3.1
  2.5.0
* 2.5.1 (set by /Users/LibraStudio/.rbenv/version)

 

こんな感じですね。

ruby -v
ruby 2.5.1p57 (2018-03-29 revision 63029) [x86_64-darwin16]

 

rubyのバージョンを叩いてもちゃんと反映されてますね。最後にgemをアップデートしRailsを最新にします。

$ gem update --system
$ gem install rails -N
$ rails -v
Rails 5.2.0

 

自分も今日仕事でRailsプロジェクト弄った際に、諸々バージョンが古かったのでブログ書きながら更新しちゃいました。

 

ではではGWも良い開発ライフを〜

Swift製お絵描きライブラリを作ってみました

            こんばんは、daihaseです。

1年ぶりくらいにSwiftでOSSを作ってみました。

Sketch

こちら、実は既にアプリで導入実績のあるライブラリです。

Doodle Maker -写真にお絵描き&イラスト-

こちらのアプリはAndroid版もリリースされているのですが、合わせてまだ10万DLくらいしかいってなく若干お絵描きアプリもストアに大量にあるので、相当差別化していかないと難しい世界な気はしています...。

またAndroidの方も5年程前にJavaで作ったのですが、基本的にはこのSwift製ライブラリとロジックは同じ感じです。UndoやRedoの考え方なんかはまんま同じですね。あとは綺麗な曲線を描くとか。

おそらくお絵描きアプリの最初の登竜門はこの辺かと思いますので、そこをライブラリに全部任せてしまって、後はアイデア勝負でよりライブラリを活かして便利で使いやすいアプリを開発していけると一番いいですね。

なお上記アプリ(iOS)の方ではピンチによるズーム、塗り絵機能的なものや写真加工など色々他にも独自に組んでいるものが多く、今回のライブラリにはここまでは搭載されていません。

とはいえこれ系のは比較的簡単に作れますので是非挑戦してみてください。

話をライブラリに戻し、こちらですがStoryboardでもコードベースでもどちらでも簡単に設置し、すぐに使うことが出来ます。

GitHubREADMEにも説明がありますが、Storyboardを使う場合はUIViewを配置したいサイズで乗っけてやるだけで、いきなりペンで絵が描けるようになります。

コードでセットするのも超簡単!

import Sketch

   override func viewDidLoad() {
        super.viewDidLoad()

        let sketchView = SketchView(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height))
        view.addSubview(sketchView)
    }

こんな感じにSketchViewインスタンスを作ってやって、あとはViewにaddSubviewするだけです。インスタンスを生成する際に好きな縦横サイズを指定でき、その範囲内だけお絵描き可能なViewが出来上がる感じです。

サンプルを見てもらえればSwiftをちょっと触ったことのある方なら直ぐに直感で使えるようになっているかと思います。

実装方法を添えて、簡単に機能を紹介を紹介したいと思います。

色を変える

sketchView.lineColor = UIColor.red

ペンの種類を変える(ブラー、ネオン)

// ブラー
sketchView.drawingPenType = PenType.blur

// ネオン
sketchView.drawingPenType = PenType.neon

全消し

sketchView.clear()

モードを変更する(ペン、消しゴム、スタンプ、図形)

// ペン
sketchView.drawTool = SketchToolType.pen
// 消しゴム
sketchView.drawTool = SketchToolType.eraser
// スタンプ
sketchView.drawTool = SketchToolType.stamp
// 直線
sketchView.drawTool = SketchToolType.line
// 矢印
sketchView.drawTool = SketchToolType.arrow
// 四角
sketchView.drawTool = SketchToolType.rectangleStroke
// 四角(塗りつぶし)
sketchView.drawTool = SketchToolType.rectangleFill
// 丸
sketchView.drawTool = SketchToolType.ellipseStroke
// 丸(塗りつぶし)
sketchView.drawTool = SketchToolType.ellipseFill

Undo、Redo

// Undo
sketchView.undo()
// Redo
sketchView.redo()

画像読み込み

sketchView.loadImage(image: "sample.png")

ザッとあげるとこんな感じになります。 基本的にお絵描きアプリで必要な機能は、簡単にメソッド呼び出しやプロパティセットすることで対応出来るようになってます。

もともと子供向けのお絵描きアプリのために実装したコードで、その時は特にOSS化とか何も考えてなかったのであれですが、出来れば今後も色々機能追加していければと考えています。

後は個人的に他のペンの種類(例えばクレヨンとか...)を増やしたいと思ってはいますが、まだ良い方法が思い浮かばず...PRなど投げて誰か作ってくれると嬉しいです。

それでは良い開発ライフを〜