お元気そうで残念です

ぼちぼち学習してるIT系の学生です

gatlingでconstantUsersPerSecをだんだん増やすためにjinja2にお祈りに行ったときの道中のこぼれ話

周囲が負荷テストでgatlingを使い始めて、自分も使ってみようと思い勉強してみました。

以下のような感じで負荷を徐々に増やして徐々に減らすテストを組もうとしたら案外苦戦したのでメモっときます。 もっと良い方法があったら教えてください!

f:id:joniy_joniy:20180511002147p:plain

injection単体ではどうか

普通にinjectionでひょいっとできると思って調査しましたが、微妙にニュアンスが違っていてダメでした。 前半の徐々に増加する部分に絞ってなぜダメだったか触れていきます。

1. rampUsers(nbUsers) over(duration)

期間durationに渡ってnbUsers数のリクエストをランプ関数に従って発射するというもの。

rampUsers(100) over(10 seconds)

とすると、10秒間で均等にリクエストを発射して、合計100リクエストになるように発射してくれます。

実行すると以下のようになります。

均等にリクエストを割り振ってくれるので、だんだん増やすことはこれ単体ではできません。

あと、もしリクエストが詰まって一気に消化される場合などランプ関数で想定していたよりも早くnbUsers数のリクエストが裁かれてしまった場合、それ以上のリクエストは生成されません。ちょっとやりたいことと違う。 f:id:joniy_joniy:20180511003055p:plain

2. rampUsersPerSec(rate1) to (rate2) during(duration)

初期値rate1数のリクエストを、期間durationに渡って、ランプ関数に従ってrate2数のリクエストへ徐々に増やしていくというもの。

これでいいやん、って思ったのですが試してみると1の問題点と同様の問題がありました。

rampUsersPerSec(100) to 500 during(10 seconds)を実行した結果が以下になります。

00:45:59から100リクエストずつで始まり、00:46:09で500リクエストで終わることを期待していましたが、同時リクエスト数は500まで上がる前に終わってしまいました。上がりきる前に計算したリクエスト数が終わってしまったようです。ちょっとやりたいことと違う。 f:id:joniy_joniy:20180511005247p:plain

3. splitUsers(nbUsers) into(injectionStep) separatedBy(duration)

duration期間ずつ間を挟みながら、injectionStepを繰り返して合計のリクエスト数がnbUsersになるまで発射するというもの。

splitUsers(1000) into (rampUsers(10) over (10 seconds)) separatedBy (10 seconds)

このシナリオだと、10秒渡って10リクエストを投げた後、10秒休むを100回繰り返します。 やりたいこととはだいぶ違っちゃいますね。ちなみに、実行した結果は以下のようになりました。長かった。

f:id:joniy_joniy:20180511014839p:plain

4. splitUsers(nbUsers) into(injectionStep1) separatedBy(injectionStep2)

injectionStep2を挟みながら、injectionStep1を繰り返して合計のリクエスト数がnbUsersになるまで発射するというもの。

splitUsers(1000) into (rampUsers(10) over (10 seconds)) separatedBy atOnceUsers(30)

このシナリオだと、10秒渡って10リクエストを投げた後、ドバっと30リクエストを投げるを100回繰り返します。 これも違いますね。

5. constantUsersPerSec(rate) during(duration)

期間durationの間常にrate数のリクエストを発射し続けてくれるもの。 基本的にはこれがやりたいことを満たしていますが、徐々に増やすためには似たようなコードをペタペタする必要があるのでちょっと...。具体的には以下のような感じです。これでやりたいことは満たしていますが、間隔やリクエスト数を変えると毎回弄るのが面倒なので一工夫挟みたいところです。

var waveInjections = Seq(
      constantUsersPerSec(10) during (10 seconds),
      constantUsersPerSec(20) during (10 seconds),
      constantUsersPerSec(30) during (10 seconds),
      constantUsersPerSec(40) during (10 seconds),
      constantUsersPerSec(50) during (10 seconds),
  )

  setUp(
    scn.inject(waveInjections).protocols(httpConf))

constantUsersPerSec(rate) during(duration)を自動生成する

Scalaでやってみる

scalaでシナリオを書けるのでforなりwhileなりでSeqに追加していけばいいのではと試してみました。
Ansibleで実行したいホストに設置するので、変数はAnsibleからとってくるようにしています。

var MAX_ACCESS = "{{ max_access }}".toInt   
var START_ACCESS = "{{ start_access }}".toInt   
var EACH_ACCESS = "{{ each_access }}".toInt 
var SEC = "{{ duration }}".toInt

略
var nb = START_ACCESS
var waveInjections = Seq(constantUsersPerSec(START_ACCESS) during (SEC seconds))
while ( nb <= MAX_ACCESS ) {
  nb = nb + EACH_ACCESS
  waveInjections = waveInjections :+ constantUsersPerSec(nb) during (SEC seconds)
}

SetUp(scn.inject(waveInjections).protocols(httpConf))

実行してみるとvalue during is not a member of Seq[Product with Serializable]と怒られてしまいました。
この書き方ではダメなようですが、Scalaを書くのが初めてなのでどう直せば良いのか分からない...

Jinja2で生成してみる

ならば慣れ親しんている(?)jinja2テンプレートを使って生成したものを配置してみようとやってみました。

略
  var waveInjections = Seq(
    {% set second = [duration * each_access /max_access] %}
    nothingFor({{ second[0] }} seconds),
    {% set nb = [start_access] %}
    {% for _ in range(1, 100) %}
      {% if nb[0] == max_access %}
      {% break %}
      {% endif %}
      constantUsersPerSec({{ nb[0] }}) during ({{ second[0] }} seconds),
      {% set _ = nb.append(nb.pop() + each_access) %}
    {% endfor %}
      constantUsersPerSec({{ max_access }}) during ({{ wait }} seconds),
    {% set nb = [max_access - each_access] %}
    {% for _ in range(1, 100) %}
      {% if nb[0] < start_access %}
      {% break %}
      {% endif %}
      constantUsersPerSec({{ nb[0] }}) during ({{ second[0] }} seconds),
      {% set _ = nb.append(nb.pop() - each_access) %}
    {% endfor %}
    nothingFor({{ second[0] }} seconds),
  )

  setUp(scn.inject(waveInjections).protocols(httpConf))

生成されたものが以下になります。
やりたかったことはできましたが、ゴリ推した感が強いですね...。
Scala勉強して再チャレンジしたいと思います。

  var waveInjections = Seq(
        nothingFor(2.0 seconds),
        constantUsersPerSec(10) during (2.0 seconds),
        constantUsersPerSec(20) during (2.0 seconds),
        constantUsersPerSec(30) during (2.0 seconds),
        constantUsersPerSec(40) during (2.0 seconds),
        constantUsersPerSec(50) during (10 seconds),
        constantUsersPerSec(40) during (2.0 seconds),
        constantUsersPerSec(30) during (2.0 seconds),
        constantUsersPerSec(20) during (2.0 seconds),
        constantUsersPerSec(10) during (2.0 seconds),
        nothingFor(2.0 seconds),
  )

  setUp(scn.inject(waveInjections).protocols(httpConf))

追記

こう書けば普通に動くので、Scalaだけで完結できました。スッキリ

  var nb = START_ACCESS
  var waveInjections = Seq(constantUsersPerSec(nb).during(SEC seconds))
  while ( nb <= MAX_ACCESS ) {
    waveInjections = waveInjections :+ constantUsersPerSec(nb).during(SEC seconds)
    nb = nb + EACH_ACCESS
  }

参考文献

Gatling Simulation Setup
Gatlingを使った負荷試験
制御構文
AnsibleのJinja2を活用する

Go言語でdeferを使った際のerror処理

Go言語のdefer文,便利ですよね.
でもdefer文を使うと返り値が無視されてしまうので,error処理をどうするべきか悩みます.

Goでdeferの処理中のエラーを返す書き方を工夫してみた
こちらのサイトでもいくつか検証して紹介されています.

対象の処理にもよりますが,個人的にはdefer文を使う時のerrorはキャッチせずにログに吐き出す程度で済まして良いんじゃないかと考えています.
例えば無名関数を使って以下のような感じです.

defer func() {
    err := resp.Body.Close()
    if err != nil {
        log.Println("can't close body!!", err)
    }
}()

close処理系は,すぐには大きな影響がないerrorが多い印象なのでpanicにしたり, mainまで拾い上げる必要は無いんじゃないかなと考えています.ものにもよると思いますが.

埋め込み構造体配列の初期化

Go言語で埋め込み構造体配列の初期化方法についてのメモ.

やり方

v := Foo {
  Bar: []Bar {
    {Value1: 1, Value2: 2},
  },
}

package main

import (
    "fmt"
)

type Foo struct {
    Bar []Bar
    ID int
}

type Bar struct {
    Name string
    Number int
}
func main () {
    m := Foo {
        ID: 1,
        Bar: []Bar {
            {Name:"hoge", Number:1},
            {Name:"poge", Number:2},
        },
    }

    fmt.Println(m)
}

https://play.golang.org/p/7K9SsJdk9E

実行結果

{[{hoge 1} {poge 2}] 1}

参考サイト

お気楽Go言語プログラミング

埋め込み構造体をjsonに変換したときに埋め込み構造体の構造体名を残して欲しい

Go言語の分からない部分についてのぼやき.

以下のように構造体を定義していたとする.

type User struct {
        Name string `json:"name"`
        Id   int    `json:"id"`
        Status
}

type Status struct {
        Power int `json:"power"`
        Speed int `json:"speed"`
}

以下のようにjson.Marshalを使って構造体を分割して出力すると

func main () {
        s1 := Status{5, 6}
        user1 := User{"hoge", 1, s1}
        user1.Status = s1
        j, _ := json.Marshal(user1)
        var buf bytes.Buffer
        buf.Write(j)
        os.Stdout.Write(j)
}

このように出力されるけど,

{"name":"hoge","id":1,"power":5,"speed":6}

できれば下のように出力して欲しい場面があった(送信先で構造体に戻したいときとか)

{"name":"hoge","id":1,"status":{"power":5,"speed":6}}

調べたらありそうな気がするけど,良い調べ方がわからなかった....

とりあえずメモっておく.


追記

type User struct {
Name string `json:"name"`
Id int `json:"id"`
Status `json:"status"`
}

で普通にできた

Javascriptで時間を足していくだけのアプリ

github.com

Javascriptの復習にドットインストール師に師事しながら
基本的なことをお勉強。ついでに 城ドラ の武具開発時間の
合計時間を求めたいなと思ってたので、時間を足していくだけのものを作ることに。

コードを見ながらおさらいしていきます。

DOMとは

Document Object Modelのこと。
今開いているWebページを指す。
DOMを弄って動的にページを改変できるのがJavascriptの強み。

要素取得

Javascriptでは、HTMLの各要素に振ったIDを利用して
要素を指定することが多いです。
IDで要素を取得する場合は、document.getElementById(id)
もちろん他にも色々方法はあります。
[JavaScript]あらゆる要素を取得する | Rondo
詳細はこちらなどで。

要素作成

新たな要素を作成したい場合は、document.createElement(element)を使います。 elementpdivなど使いたいHTMLタグを入力します。

要素追加

作成した要素や取得した要素を、Webページに追加したい場合は
document.body.appendChild(element)を使います。
これにより、指定した要素の子要素の末尾にelementを追加します。 document.body.appendChild(element)の場合は、body内の末尾に追加します。

target = document.getElementById('result');のように要素を取得後、
targetの子要素に追加するには、target.appendChild(element)です。

value取得

取得した要素の持つ文字列などのvalueを弄りたいときは、
value = document.getElementById(element).valueで取得できます。

値設定

取得した要素に値を設定したい場合は、
document.getElementById(id).innerTextでできます。

文字列連結

文字列連結はJavaなどと同様に+でできます。

hours = 6;
minutes = 30;
target = document.getElementById('result');
target.innerText = hours + ':' + minutes;

とすると、target要素には6:30と表示されます。

文字列分割

文字列を指定した文字で分割したいときは、
values = document.getElementId(element).value.split(':')で分割できます。
※ここでは:で分割しています。
分割した各文字列は配列要素としてvaluesに格納されます。

文字列から数値に変換

文字列から、数値に変換したい場合は、
parseInt(target, 10)を使います。
10は10進数の10です。第一引数を解析して、第二引数の基数による
整数に変換します。

parseInt() - JavaScript | MDN

JavaScriptにおける数値⇔文字列の型変換あれこれ - console.lealog();

ここをみるともっと簡略に変換する方法があるようですが、
今回後述するNaN判定にそれらが引っかかったので、結局parseInt()を使いました。

数値のNaN判定

NaNとはNot A Numberのことで非数字を示します。
今回のアプリケーションでは、入力値を数字に変換しますので、
数値以外の不正な入力が合った場合に弾く必要があります。
Number.isNaN(number)で判定を行います。

Number.isNaN() - JavaScript | MDN

終わりに

今までなんとなく忌避していたJavascriptですが、Web界隈では必須ですし、
なぜ今まで勉強しなかったんだ感。
触ってみると、それほど扱いづらい言語というわけでも無く、
C言語などで基礎ができていればなんら問題はなさそうです。
ただ、変数宣言を省略したり(ブラウザに依るかも)、型を指定しなくてもいいので、
変数の扱いには注意が必要ですねやっぱり。

今回は非常に簡単な文字列処理しか行っていないので、
詰まりやすいAjaxの非同期通信などには触れていません。
また、時間のあるときに何か作って身につけます。

家に帰るという思考整理方法

家に帰るという作業は非常に思考を整理する目的に適していると感じた。

私は、普段通学に原付を利用している。
そのため、移動中は死の危険が隣り合わせであるため、
注意力を外に向けておく必要がある。
しかしながら、原付の操作事態は非常に単純なので
そんなに集中しておく必要はない。

なので自分の思考全体を10とすると、
4くらいを運転に割いて、残りの6くらいは他のことを考えていられる。

考え事が多く思考がぐちゃぐちゃになっていると、
いくら考えてもまとまらないことが多い。
一つのことを考えていても、
頭の片隅には三つ四つと他の事が置かれていて、思考に割り込んでくる。

しかし、運転中であると他事まで考えるほどの思考の余白はないので、
一つの事に集中できるのだ。

また、今は冬なので寒さも思考をスッキリさせるのに役立ってくれる。

要は激おこぷんぷん丸でまともに考えられなくなっていても、
帰り道で考えているうちに「今日のところはこのぐらいにしておいてやろう!」
と名脇役のような思考に落ち着いて気持ちが楽になるということ。

Let's 勉強会

この記事は、 SLP KBIT Advent Calendar 2015 - Adventar の3日目の記事です。
2日目の記事と内容が酷似してるけど方向性は違うからまぁいいかなって。

そんなあなたに

今回は、大学入りたての人や、そうでもないけど
あまり自分に自信の人向けに書こうと思います。

自分なりにプログラミングの勉強とかしてるけど、
周りの人もっとすごいことやってるし、
全然レベルが違うなーと感じることが多々ありませんか?
そういう人にもおススメなのが勉強会です。

なんで勉強会?

勉強会なんか行ってもギークな話ばっかだったり、
知らない技術の話ばっかりだったりと
ちんぷんかんぷんで眠くなるだけじゃないの?
なんて思う人も多いでしょう。

確かに、ついていけない話がほとんどです。
どんなに一生懸命理解しようと集中しても、
知らない用語ばかりだと無理があります。

じゃあどうしろと

ですので、無理そうであれば、気になったキーワードや
重要そうなキーワードをメモする方向に聞き方を変えてみるのはどうでしょうか。

その場ではすぐに、理解出来なくても帰ってから自分で調べることで
だんだんと知識が増えていきます。
勉強会で使われたスライドはSlideShareなどで公開されていることが多いので
調べながらスライドを見返してみるのもおもしろいですよ。

最後に

自分なりに頑張ることや、周りに影響されて始めてみるのも大切ですが、
たまには気分を変えて自分の知らない世界にも触れてみるのも楽しいもんです。
そのときは、こんなもんどうせ使わないわーと思うようなことがあっても
うわ使うとき来たわ...まじか...となることも案外あります。
そんな感じで、色々な勉強会に参加してみましょうよっ
というのがこの記事で伝えたいことです。

ってことで、あとは勉強会探しが捗りそうなサイトや、
香川で開催される(た)勉強会について紹介していきます。
これが無いよ!ってコメントくれたら追加していきます。

勉強会探しがはかどるサイト集

IT勉強会を探すために捗るサイトまとめ【10選】 - Qiita

よく使われてそうなものだけ軽く触れます。

ATND

みなさん聞いたことがあるかと思います。
多くの勉強会が登録されているので、まずはここで探してみるのがいいでしょう。

compass

こちらも聞いたことがあるのではないでしょうか。
Twitterなどと連携することでフォローしている人が開催する勉強会が通知されるのが便利です。

Doorkeeper

継続的に開催される勉強会などが登録されていることが多いそうです。
参加登録を行うとコミュニティメンバーとして登録されて、次回開催の時も通知が来る仕様とのこと。

dots.

調べてみて初めて存在を知りました。
(ATNDとcompassとDoorkeeperしか知らなかったけど
エリアを選択することでそこに属する都道府県を全検索できる機能がとってもGOOD
各勉強会ページを開いてみるとATNDで参加登録待ってるよーってところが多いですね。

香川で開かれる勉強会

これから開催されることがわかっているものについて書きます。

Android Studio 開発講座 - connpass

2015年12月12日(日曜日)にe-とぴあかがわで開催されます。 日本Androidの会 香川支部さんが主催のAndroid studio勉強会です。
Androidでなんかアプリ作ってリリースしたい!
と常日頃思っている人もいるのではないでしょうか?
そして思いはしてるけど行動はしていない人も。
導入から基礎まで教えてくれるそうなので一歩踏み出してみてはどうでしょう。
セミナー終了後夜に懇親会も行うみたいですよ。

えれくら!ふつうのやつ ~電気電子工作系製作・交流会~ 第18回 : ATND

2016年1月17日(日曜日)にe-とぴあかがわで開催されます。
電子工作系みたいですね。
うちのサークルだとmindstormとかがありますよね。
RaspberryPiやNFCなど色々なものを扱うみたいです。
今回は初心者ハンズオンで作ったものを発表しあう流れのようですね。楽しそう。

えれくら!TypeR ~Raspberry Pi勉強会~ 第7回 : ATND

2016年1月23日(土曜日)に香川高専 高松キャンパスで開催されます。
上と同じくえれくら!さん主催の電子工作系勉強会です。
こっちはRaspberry Piを扱うみたいです。
完全な初心者向けでなく、今回は予習前提です(リンク先参照)。
Raspberry Pi2を持っていることが条件なので、持ってる人や、
使ってみたかったしこの機会に買うか!って人はどうでしょうか。

Go言語勉強会(タイトル未定)

2016年2月20日(土曜日)におそらくe-とぴあかがわで開催されます。
やるよーって話を聞いただけなので、まだ確定していないようですが、
日程は変わらないと思います。
こちらは、GDG Shikokuの方が開催します。
初心者セミナー&ハンズオンを行うそうです。

香川で開催された勉強会

定期的に開催されている勉強会や、過去に開催されたものを紹介します。
(私が把握しているものだけかもしれない)
また、いつか開催されるかもしれません。

定期開催

home [セキュリティうどん(かまたま)]

名前のとおりセキュリティ系の勉強会です。
最近学生の参加が増えているみたいです。(どこのせいだろう)
毎年(おそらく)春と秋に開催されています。
メールアドレスを登録しておくと開催前に連絡がきます。
あとは、Twitterアカウントをフォローしたり。

OSC

全国色々なところで頻繁に開催されている勉強会です。
オープンソースソフトウェアを題材に様々なブースやセミナーがあります。
前に香川で開催されたのは2011年なのでそろそろあるんじゃないかな?

えれくら!

先ほど挙げた電子工作系の勉強会です。
1~2ヶ月ごとぐらいに開催しているみたいですね。

過去に開催

四国出身者が支える、ニッポンのクラウド - connpass

今年の夏に開催されたものです。
もしかしたら来年もあるかも?
ネットワークインフラに興味のある方はぜひ。