こんにちは、todeskingです。
ScalaMatsuri 2016将軍スポンサーが合同で開催した「Scala将軍達の後の祭り」 というイベントで、バイトコードの実行時最適化について発表してきました。
抽象化によってオーバヘッドが存在するコードを実行時にバイトコードレベルで最適化すれば、抽象化とパフォーマンスが両立出来てお得、という夢のある話です。
以下、補足など。
続きを読むこんにちは、todeskingです。
ScalaMatsuri 2016将軍スポンサーが合同で開催した「Scala将軍達の後の祭り」 というイベントで、バイトコードの実行時最適化について発表してきました。
抽象化によってオーバヘッドが存在するコードを実行時にバイトコードレベルで最適化すれば、抽象化とパフォーマンスが両立出来てお得、という夢のある話です。
以下、補足など。
続きを読むScala書けますで入社した筈なのにnewrelicみながらDBのチューニングばかりしてる、ぽんこつです。
今回はRedisをどうやって叩くと早くなるのかを、主にexpireまわりで適当にベンチしたりした。
私のDBの経験値がMySQLに偏っているのであまり手出ししてこなかった弊社のRedis環境だが、パフォーマンス絡みの問題を聞いたり見かけるようになってきたので、Redisのパフォーマンスを引き出す方法について調査をした。
弊社のサーバアプリケーションは主にScalaで書かれているので、ScalaからRedisに繋いだときのベンチマークが中心になる。
JMHはマイクロベンチマークツールの一種。JVMはJITによって、何度も同じ箇所を実行すると、徐々にコードを最適化して早く動くようになる、等々の要素を排除し、できるだけ正しいベンチマークをすることができるツール。
ScalaでJMHを使う場合はsbt-jmhを使うと良い。sbtのpluginとして使うことができる。今回はsbt-jmhを使っている。(ただしRedisの場合I/Oがからむので、JMH本来の正確さは期待できないと思われる)
とりあえず30万件データを突っ込むのに掛かる時間を調べる。jedisのオーバヘッドが大きい可能性を考慮してまずは最速パターン(と思われる)を計測してからjedisの計測に入る。
for i in `seq 1 10000` do redis-cli set ${i}key ${i}value done
適当に作ったが、あまりにも時間掛かり過ぎて計測不能だった。1コマンド毎にredis-cliを立ち上げるのは無謀だった模様。
一旦コマンドリストを生成して流し込んでもいいけど、今回はredis protocolを使ってみる。
http://redis-documentasion-japanese.readthedocs.org/ja/latest/topics/mass-insert.html
redis protocolをpipeで流せばいいらしい。該当ページのRubyスクリプトを参考に組んでみる。
def gen_redis_proto(*cmd) proto = "" proto << "*"+cmd.length.to_s+"\r\n" cmd.each{|arg| proto << "$"+arg.to_s.bytesize.to_s+"\r\n" proto << arg.to_s+"\r\n" } proto end count = ARGV.size > 0 ? ARGV[0].to_i : 300000 (1..count).each { |i| puts gen_redis_proto("SET","key%d" % i,"value%d" % i) } puts gen_redis_proto("FLUSHDB")
これで生成したredis protocolをredis-cliに渡す
ruby gen_proto.rb | time redis-cli --pipe
結果は1.303s。はやい。
Javaに対応したバインディングは幾つもあるが、今回は一番メジャーっぽいjedisを使う。社内で使ってるからという話もある。
@Benchmark @BenchmarkMode(Array(Mode.AverageTime)) def jedis(): Unit = { val client = new Jedis() (1 to 300000).foreach { i => client.set(s"key${i}", s"value${i}") } client.flushDB() }
Warm upを3回、Iterationを5回やって平均を出す。
jmh:run -i 5 -wi 3 -f1 -t1 org.mvrck.BulkInsertTest
結果は4.274s。ちょっと遅過ぎる気がする
コマンドを毎回発行してるから遅いと思われるので、Transactionを使ってみる
https://github.com/xetorthio/jedis/wiki/AdvancedUsage
今回からannotationは省略して書く
def setMulti(): Unit = { val client = new Jedis() multi(client) { t => (1 to 300000).foreach { i => t.set(s"key${i}", s"value${i}") } } client.flushDB() } def multi[T](jedis: Jedis)(f: Transaction => T): T = { val multi = jedis.multi() val res = f(multi) multi.exec() res }
0.541s。アイエェ!?redis protocolより早いナンデ!?timeの時間計測に問題あるのかな…?まぁ早いならいいかな…(適当)
と本番環境のnewrelic見て思ったので調査する。
まずはset時にexpireを設定する
def withExpire(): Unit = { val client = new Jedis() multi(client) { t => (1 to 300000).foreach { i => t.set(s"key${i}", s"value${i}", "NX", "EX", 100) } } client.flushDB() }
0.792s。割と遅くなる。
挿入する物によっては1コマンドでexpire設定できないこともあるので、それを模してみる
def splitExpire(): Unit = { val client = new Jedis() multi(client) { t => (1 to 300000).foreach { i => t.set(s"key${i}", s"value${i}") t.expire(s"key${i}", 100) } } client.flushDB() }
1.031s。更に遅くなる。ので、setするときにexpireが設定できないリスト型などで速度が気になりそう。
expireを設定したkeyが実際に削除されるタイミングで負荷が上がるのでは、という仮説があったので調査する。1msで揮発するようにすればそれっぽいテストができそう。
def withLittleExpire(): Unit = { val client = new Jedis() multi(client) { t => (1 to 300000).foreach { i => t.set(s"key${i}", s"value${i}", "NX", "PX", 1) } } }
1.002s。単純にexpireを設定した場合が0.792sなので遅くなってる。瞬間的に揮発のタイミングが重なるとちょっとあぶないかもしれない。
Redisの使用用途は主にcacheなので、使用頻度が低い奴は消えて欲しいけど高いやつは残って欲しい。良くある。そこでreadするときにexpireすればいいのでは!本当か?
比較対象としてreadの速度を計測する。
def read(): Unit = { val client = new Jedis() multi(client) { t => (0 until 100).foreach { i => t.set(s"key${i}", s"value${i}") } } multi(client) { t => (1 to 300000).foreach { i => t.get(s"key${i%100}") } } client.flushDB() }
0.286s。流石危険な程早いと言われるRedis。
expireを付けるとどうなるか。
def readWithExpire(): Unit = { val client = new Jedis() multi(client) { t => (0 until 100).foreach { i => t.set(s"key${i}", s"value${i}", "NX", "EX", 100)} } multi(client) { t => (1 to 300000).foreach { i => val key = s"key${i % 100}" t.get(key) t.expire(key, 100) } } client.flushDB() }
0.618s。倍ぐらい掛かるようになった。
倍ならまぁ、という気もするが、可能ならばRedisサーバ側の設定を変更し、maxmemory-policyのallkeys-lruなどで設定した方が良いと思われる。
Redisのexpireすごく遅くないか、という疑惑があり色々比較してみたけど、大体倍ぐらいだったので、maxmemory-policyの検討をしつつ必要なら使えば良いのでは、という結論になった。あとtransactionは強力なのでできるだけ使うようにしたい。
弊社での実測の話をすると、getの度にexpireした結果として、expireの処理時間が結構長いことがnewrelicで判明した。このため、getと同時にttlコマンドで残り期間を確認して、伸ばす必要があるときだけ伸ばすようにした結果、expireが激減して全体として高速化した。ただしその場合transactionが使えなくなるので今回は計測しなかった。
今回テストに使ったコードは以下にある。
https://github.com/mvrck-inc/sandbox/tree/master/redis-performance
計測方法にあまり自信が無いので、問題がありそうなら教えて欲しい。
今回の環境
OS: Arch Linux Redis: 3.0.7 JDK: OpenJDK 1.8.0_74 CPU: Core i5 4690T Memory: 16GB
今回はssl更新の記事です。
インフラの方々は作業されたことがあると思いますが私は初挑戦です!
macで作業します。
バージョンを確認
[astel@astel-no-MacBook-Pro ] $ openssl version OpenSSL 0.9.8zg 14 July 2015
秘密鍵作成のための乱数ファイルを生成します。
[astel@astel-no-MacBook-Pro ] $ dd if=/dev/urandom of=rand.dat bs=1k count=4 4+0 records in 4+0 records out 4096 bytes transferred in 0.000492 secs (8327615 bytes/sec) [astel@astel-no-MacBook-Pro ] $ ls rand.dat
作成した乱数ファイルから秘密鍵を生成します。
途中で秘密鍵のパスワードを入力します。
[astel@astel-no-MacBook-Pro ] $ openssl genrsa -rand rand.dat -des3 2048 > private_key.pem 4096 semi-random bytes loaded Generating RSA private key, 2048 bit long modulus ..........+++ .......+++ e is 65537 (0x10001) Enter pass phrase: (パスワード入力 Verifying - Enter pass phrase: (もういちど入力 [astel@astel-no-MacBook-Pro ] $ ls private_key.pem rand.dat
CSRを作成します。
[astel@astel-no-MacBook-Pro ] $ openssl req -new -key private_key.pem -out csr.pem Enter pass phrase for private_key.pem: (上記で入力した秘密鍵のパスワードを入力 You are about to be asked to enter information that will be incorporated into your certificate request. What you are about to enter is what is called a Distinguished Name or a DN. There are quite a few fields but you can leave some blank For some fields there will be a default value, If you enter '.', the field will be left blank. ----- Country Name (2 letter code) [AU]:JP State or Province Name (full name) [Some-State]:Tokyo Locality Name (eg, city) []:Shinjuku-ku Organization Name (eg, company) [Internet Widgits Pty Ltd]:Maverick Inc. Organizational Unit Name (eg, section) []:Development Common Name (e.g. server FQDN or YOUR name) []:*.mvrck.co.jp Email Address []: Please enter the following 'extra' attributes to be sent with your certificate request A challenge password []: An optional company name []: [astel@astel-no-MacBook-Pro ] $ ls csr.pem private_key.pem rand.dat
作成したCSR(csr.pem)を申請サイトへ貼り付けて申し込みます。
今回発行されたSSL証明書を_.mvrck.co.jp.crt
と名前を変えてサーバへ設置していきます。
対象サーバは全てnginxで動いてます。設定を見てみましょう。
$ grep ssl_certificate /etc/nginx/conf.d/mvrck.conf ssl_certificate /etc/ssl/nginx/_.mvrck.co.jp.crt; ssl_certificate_key /etc/ssl/nginx/_.mvrck.co.jp.key;
ふむふむ /etc/ssl/nginx/
以下に置かれているようですね。
ここで_.mvrck.co.jp.key
なんて作ってないぞ と思いましたがssl_certificate_key
... なるほど 秘密鍵??
疑問に思いながら最初の方に作成した秘密鍵 private_key.pem をリネームして設置しました。
$ mv private_key.pem _.mvrck.co.jp.key
また権限がnginxにしないとうまいこと動いてくれなかったのでchown nginxで設定します。
よしこれでとりあえず$ nginx -t
で確かめてみよう!
$ nginx -t Enter PEM pass phrase:
秘密鍵の入力を求められました。パスワードを入力して
syntax is ok test is successful
と設定自体はよさそう。restartさせてみる。
$ /etc/init.d/nginx restart * Checking nginx' configuration ... Enter PEM pass phrase: [ ok ] * Stopping nginx ... [ ok ] * Starting nginx ... Enter PEM pass phrase:
と,これでweb上で確認するとしっかり更新されていました!
このEnter PEM pass phrase:
ですが毎回入力せずにも設定できるようです。
例としてパスフレーズを聞かれないようにするには
$ openssl rsa -in private_key.pem -out _.mvrck.co.jp.key Enter pass phrase for private_key.pem: writing RSA key
とすることで_.mvrck.co.jp.key
というパスフレーズなしのkeyが作成されます。
設置し直して$ nginx -t
等で試してみるとパスフレーズなしでもできました。
私はweb上でssl証明書をみて更新されていると確認したのですがcuiでも出来るよということで教えてもらいました。
$ openssl s_client -connect www.mvrck.co.jp:443 < /dev/null 2> /dev/null | openssl x509 -noout -startdate -enddate
これで無事更新完了です。
無事といいつつ実はCommon Nameを間違えて発行して色々あったのですがそれはまた別のお話.....
みなさんこんにちは!
去る1月30(土)1/31(日)に行われたScalaMatsuri 2016を弊社で協賛させていただきました!
簡単にご紹介させていただきます 😘
日本で最大規模のScalaカンファです。
今年で2度目(Scala Conference in Japan 2013を含むと3度目)の開催となります。
弊社では去年度から協賛させていただいております!🍻
国際カンファだけあって参加者が多国籍な様相でドキドキしました!
昨年に引き続きニコニコ生放送でメイントラックの中継も行われていましたね。
知人がニコニコ生放送で視聴していましたが、プレミアム会員でないとなかなか見られない状況が続いてたそうです。
当日は日本のTwitterでハッシュタグがトレンドにあがっていましたねー!関心の高さが伺えます。
Scalaに興味はあるけれどご存知なかった方、予定の都合がつかなかった方、遠方等で参加できなかった方などは是非公開されている情報を振り返って現地の空気を感じていただけたらと思います。
行動規範マナー動画 in ScalaMatsuri 2016
このひと
通訳用レシーバは3F受付、または各会場入口にてご返却ください! #ScalaMatsuri 2016 #ScalaMatsuriUC pic.twitter.com/F8S3yj10uv
— scala_jp (@scala_jp) 2016, 1月 31
雑ですまん 💀💀💀
リファクタ系のお話しはなるほどみを感じながら拝見させていただきました!
弊社でブース出させていただきました!!
怪しげなクイズの紙を配布させていただいたのですけれど、ご覧になられた方いらっしゃいますかねー。
私もちょっと挑戦してみたのですけれどあきらめました٩(๑´3`๑)۶
今年のScalaMatsuri参加できなかったのでこの辺面白かったぜとか教えてもらえると嬉しいです 💋
2回目の登場のあすてるです。 サーバ上で叩いたコマンドっていつ終わるかわからないものがありますよね? それらを完了したらslackで通知させようと思った話です。
incoming hookを使用します。 https://my.slack.com/services/new/incoming-webhook にアクセスします
今回はランダムに送る程で Choose a channek... で #randam を選択して 下のAdd Incomingg WebHooks inteagration を押して進みます。
Webhook URLというものが出てくるのでこれをメモしておきましょう。 例として下記がでてきたとします。 https://hooks.slack.com/services/T0hogehoge/B0piyopiyo/HogePiyofooooooooooo
一番下の Save Settings を押して保存します。 これでslack上での作業は終了です。
ここからはサーバ上での操作です。 適当な場所にスクリプトを置きます。
#!/bin/bash url='https://hooks.slack.com/services/T0hogehoge/B0piyopiyo/HogePiyofooooooooooo' username='end_command' to="$1" message="$2" emoji=':end:' payload="payload={\"channel\": \"${to}\", \"username\": \"${username}\", \"text\": \"${message}\", \"icon_emoji\": \"${emoji}\"}" curl -m 5 --data-urlencode "${payload}" $url
urlには先ほどメモしたWebhook URLを入れてください。
chmod 755
などして実行権限をつけておくと良いでしょう。
第1引数にuser名かChannel名をいれて第2引数に通知したいコメントを入力します。 例
$ ./slack.sh "@astel" "test-message" ok% $ ./slack.sh "#random" "test-message" ok%
okと表示され、メッセージが届いていることが確認できました。 aliasなどで設定しておくと便利かもしれません。
実際は&&で渡して使用します。
簡単にセットアップできました。 userのアイコンを今回は:end:にしましたが自分で設定して面白いものもつくれそうです。 zabbixの障害通知やcronでセットしてbotなどにも使えそうですね。 問題としてはそもそも通知する前にこけてしまった場合は && だと動かないのでそこらへんは考える必要がありそうです。
どうも、初めて書きますnju33(じゅん)という者です。
よろしくお願いします。
書く順番が回ってきて何を書こうか迷ったのですが、今回はPostCSSについて書こうと思います。