SpringのModelに共通な値(デフォルト値)を入れたいとき
開発環境依存の定数とかといったような共通な値をmodelに入れたいときがある。
(static fileのURLとか)
実は@ControllerAdvice
に@ModelAttribute
が使用できるみたい。
(@ExceptionHandler
とか@InitBinder
でしか使ったことなかった。)
@ControllerAdvice public class ControllerAdvice { @ModelAttribute public void addDefaults(Model model) { model.addAttribute("hoge", "hoge"); } }
今まで、Interceptor使って埋め込んだり、FreeMarkerConfigurerにsetFreemarkerVariablesとかしていたけど、こっちのほうがスッキリする気はする。
Redis Lua script(EVAL)はSLOWLOGにはどう出るのか
先日、RedisのINCRBYFLOATコマンドがSLOWLOGにはSETコマンドとして出て来るといったような話がありました。 https://github.com/antirez/redis/issues/3841
EVALコマンドでLua scriptを実行したときのSLOWLOGは、EVALとして出て来るのか。それともLua script内のRedisコマンド単位で出てくるのか気になるね〜みたいな話がでたので検証してみる次第です。
結果から言うと、(Redis 3.2.1 versionでは)EVALコマンドとして出て来るので問題ないです。
検証する
なんか適当にLua内でsleepさせてみれば良いわけであります。
しかし、RedisのLuaでは使用できるlibに限りがあるので、os.sleepとかは使えません。
どうしたもんかと考えた結果、SET, PEXPIRE, PTTLを以下のように使って20ms sleepさせてみました。
(Redisはデフォだと10msこえるとSLOWLOGに出てくる)
local tempKey = "temp-key" local cycles redis.call("SET", tempKey, "1") redis.call("PEXPIRE", tempKey, 20) for i = 0, ARGV[1] do local pttl = redis.call("PTTL", tempKey) cycles = i; if pttl == 0 then break; end end
準備できたので、実行してみるです。
EVALのとき
$ redis-cli EVAL "$(cat slow.lua)" 0 100000 (integer) 29409
$ redis-cli SLOWLOG GET 1 1) 1) (integer) 4 2) (integer) 1488512354 3) (integer) 19775 4) 1) "EVAL" 2) "local tempKey = \"temp-key\"\nlocal cycles\n\nredis.call(\"SET\", tempKey, \"1\")\nredis.call(\"PEXPIRE\", tempKey, 20)\n\nfor i = 0, ARGV[1] ... (127 more bytes)" 3) "0" 4) "100000"
お、ちゃんとEVAL単位で出てきます。
EVALSHAのとき
毎回Lua scriptをRedisに送るのは帯域の無駄なので、予めRedisにscriptを読ませておく方法があります。
そして、そのscriptのsha1を使ってこんな風に実行します。
EVALSHA sha1 numkeys key [key ...] arg [arg ...]
もし、SLOWLOGでこのコマンドで出てきても、いったいどのLua scriptなのかパッと見でわからんという問題があります。
というわけで、一応試します。
$ redis-cli SCRIPT LOAD "$(cat slow.lua)" 9dabe8fa13e49ee8c41467e697cd852e553ca36c $ redis-cli EVALSHA 9dabe8fa13e49ee8c41467e697cd852e553ca36c 0 100000 (integer) 35989
$ redis-cli SLOWLOG GET 10 1) 1) (integer) 6 2) (integer) 1488512445 3) (integer) 19238 4) 1) "EVAL" 2) "local tempKey = \"temp-key\"\nlocal cycles\n\nredis.call(\"SET\", tempKey, \"1\")\nredis.call(\"PEXPIRE\", tempKey, 20)\n\nfor i = 0, ARGV[1] ... (127 more bytes)" 3) "0" 4) "100000"
お、ちゃんとEVALとして出て来るので安心です。
まとめ
というわけで安心です。
Vue 2.0, vue-router, vuex で はてブ リーダーぽいやつ作った
最近サーバー側ばっかやっていて全然フロントエンド側触ってなくてやばい !っという理由と、 そういえば去年の秋頃に Vue 2.0 が出ていたし触ってみるか!っという理由で Vue 2.0 を触ってみた。
ただ触るだけじゃつまらないので、( SPA で)はてブ リーダーっぽいやつを作りました。
https://github.com/be-hase/vue-hatena-bookmark
Vue 公式が HackerNews リーダー的なものを sample として公開していたので、それじゃはてブ版でも作るかという軽いノリです。
“人気"と"新着"しかないぶん、わりとサクサク動くのでなかなか便利です。
使ってるもの
package.jsonに書いてあるとおりですが、
- Vue 2.0
- vue-router
- vuex
- vuetify
- Vue の Material Design componentsみたいなやつ
- axios
で作っています。
RSSとYQL
記事情報はRSSから取得しています。
さらにYQL(Yahoo Query Language) を使用しています。
YQLを使うと、RSSのようなweb上のリソースをSQLのように取得することができます。
例えば、はてブのテクノロジーの新着を見たいなら、
select * from feed where url='http://b.hatena.ne.jp/entrylist/it.rss'
みたいなYQLを書きます。
axiosを使用して取得するなら、こんな感じになります。
axios.get('https://query.yahooapis.com/v1/public/yql', { params: { q: `select * from feed where url='http://b.hatena.ne.jp/entrylist/it.rss'`, format: 'json', }, });
format=json を指定するとJSON で response を受け取れます。
ちなみにYQL, 特に認証しなくても使えるがcall数の制限はあります。 https://developer.yahoo.com/yql/guide/usage_info_limits.html
YQL, CORSできるので今回の例だと特にserver側で動的に書くことはなくて、static file置くだけで動きます。
所感
フロントエンドのフレームワーク、今までBackbone → Reactって触ってきましたが、一番お手軽感あって学習コストも低くて便利だなっていう感じです。
( React経由しているので学習コスト低く感じただけもしれないっていうのはありますが… )
ちょっと管理画面とかで複雑なことやりたいときとかに使ってみようかなという感じです。
LINE Notifyでたのでちょっと試してみる。ついでにGithub Enterpriseのwebhookを通知するやつを作ったぞ!!
LINE Notifyがリリースされたぞ!!
2016/09/29にLINE DeveloperDay 2016が開催され、いくつかの新機能などが発表されました。
http://developers.linecorp.com/blog/ja/?p=3779
その中のひとつに、LINE Notifyというものがあります。
一言でいうと、簡単にLINEにメッセージを送信することができるサービスです。
messaging APIと違って"LINE Notifyアカウント"からしか送信できないといった制限はありますが、その制限に耐えうるメリットがあるように思えます。
ちょっと試しにcurlから実行してみるぞ!!
なにはともあれ、ちょっと試しにcurlから実行してみます。
LINE Notifyにログインして、マイページにいきます。
https://notify-bot.line.me/my/
ここからPersonal Tokenを発行することができます。
tokenの名前と、送り先を選択するだけです。
発行したPersonal Tokenを用いて、
curl https://notify-api.line.me/api/notify -H 'Authorization: Bearer MY_TOKEN' -d 'message=hello'
とシェルで実行するだけでメッセージを送信することができます。
こういったことが使えるだけでも色々な用途が思いつきます。
(王道っぽいですが)例えば、
- depoyなどにフックさせて実行してみたり、
- process downしてるのを検知したら送るようにしてみたり、
- cron処理結果を送ったり、
- Jenkinsのビルド後の処理に上記コマンドをいれてみたり、
...どれも手軽に使えて便利っぽい雰囲気を感じます。
すでにこんな使い方もされているようですね。真似してみよう。
YappoLogs: LINE Notify で line command 作ると便利
OAuth認証を実装することで、webサービスとも連携することができるぞ!!
Personal Tokenだけでも便利なんですが、OAuth認証を実装することでwebサービスとも連携することができます。
WEBサービスでメール通知などをしていることが多いと思いますが、同じスキームでLINEに送信することができます。
実際にすでにIFTTT, Mackerel, Githubで連携が実装されています。
IFTTTと連携してみるぞ!!
例えば自分は、同居人と家事等をTrelloで管理して分担している。
いままではIFTTTの送り先をslackにして通知していたのですが、同居人はslackは使用していません。
わざわざこのためにslackをインストールさせてアカウントを作らせるのは面倒なので、自分だけで通知を享受して便利!って感じでした。
LINEだと、すでに同居人も使用しているのでちょっとこのgroupに入ってくれい!という感じで自然に誘導できて簡単便利だぞ、と思いました。
これでトイレットペーパーを買い忘れて冷や汗をかくこともなくなります。
IFTTT連携の仕方は、すでに他の方が記事が書かれているので参考になると思います。
netafull.net
Githubと連携してみるぞ!!
Github連携もしてみます。
LINE Notifyのtop pageにいくと、Githubのアイコンがあるのでそれをクリックします。
https://notify-bot.line.me/github/repos/select
すると、LINE NotifyとのGithub連携する画面が出てきますので、許可します。
許可をすると、このページに遷移します。
連携したrepositoryを選択し、LINEの通知先を選択するだけで完了です。カップヌードルができるよりも早くて楽チン!!
githubのwebhook eventを利用しているので、もしもこのeventは不要だなと思うのがあればGithubのwebhool設定ページのチェックを外しても良さそう。
ちなみに対応しているeventは、
- create
- delete
- commit_comment
- issues
- issue_comment
- push
- pull_request
- pull_request_review_comment
とのこと。
Mackerelと連携してみるぞ!
と、簡単に書こうと思ったのですがこちらのHelpページが参考になるので参考にしてください。
グラフとかも送られてくるの、すごい良さそう!
せっかくだし、なにかLINE Notifyを利用して作るぞ!!
さて、せっかくだしなにを作るぞ!
OAuthを実装するのはちょっとサクッと作るには面倒なので、Personal Tokenを利用するくらいが今回は無難そう。
考えた結果、Github Enterprise用のやつを作ってみようと思った。
LINE Notifyからはgithub.comとは手軽に連携できるのですが、Github Enterpriseとはできません。
会社の業務ではGithub Enterprise使っているので、Github Enterpriseでも使いたいぞ!というのがモチベーションです。
ghe-line-notify
そんなわけで、作ったやつがこれです。
https://github.com/be-hase/ghe-line-notify
※ Github Enterprise用ではありますが、github.comでも使用できます。
使い方
インストール/起動の仕方などは後述。
1. LINE NotifyでPersonal Tokenを発行する
LINE Notifyのマイページから作れます。
https://notify-bot.line.me/my/
tokenの名前(ghe-line-notifyとかで良い)と、送り先を選びます。
2. Personal TokenとWebhook Message Templateをghe-line-notifyに登録する
ヘッダーの"Add Token"をクリックすると、こんなform pageが表示される。
まずはBasicsの情報を入力する。
Github webhookのSecretを利用したい場合は、Secretも入力する。そしたら同じSecretをGithub Webhookにも設定すれば良いです。
例えば違う部署を跨いで複数人でghe-line-notifyを利用する場合は、勝手にwebhookを利用されないようにSecretを使っておくと良いと思います。
次にWebhook Message Templateを入力する。
各webhook eventで送られてるくるpayload jsonを使って、Jinja2テンプレートの記法で書けばです。
なんでJinja2なのかというと、ghe-line-notifyはPythonで実装しているからです。
ちなみに、webhook eventやpayload jsonはこのページを見ればわかります。
https://developer.github.com/webhooks/#events
各textareaには、とりあえず汎用的で便利そうなやつを予め入力してあるので、このまま登録しても良いです。
例えば、commit eventでは次のようなpayload jsonがGithubから送られてきます。(一部省略)
{ "ref": "0.0.1", "ref_type": "tag", "master_branch": "master", "description": "", "pusher_type": "user", "repository": { "id": 35129377, "name": "public-repo", "full_name": "baxterthehacker/public-repo", "owner": { "login": "baxterthehacker", "id": 6752317, .... "type": "User", "site_admin": false }, "private": false, "html_url": "https://github.com/baxterthehacker/public-repo", .... "watchers": 0, "default_branch": "master" }, "sender": { "login": "baxterthehacker", "id": 6752317, .... "site_admin": false } }
このcreate eventに対して次のようなtemplateを登録しているとします。
{{ sender.login }} created {{ ref_type }} {{ ref }} at {{ repository.full_name}} {{ repository.html_url }}
そうすると、LINEには次のようなテキストで送られるわけです。
baxterthehacker created tag 0.0.1 at baxterthehacker/public-repo https://github.com/baxterthehacker/public-repo
ちなみに、Jinja2でレンダリングした結果が、空文字である場合はLINEには送信しません。 これを応用することで、次のようなif Statementsをいれることで、ref_typeがbranch以外のやつはLINEには送信しないことも可能です。
{%- if ref_type == 'branch' -%} {{ sender.login }} created {{ ref_type }} {{ ref }} at {{ repository.full_name}} {{ repository.html_url }} {%- endif -%}
push eventのときはmaster branchのときだけ通知したり、pull request eventのactionが"opened"のときだけ送るなどといったカスタマイズが自由にできます。
自分自身もまだこの辺どうするのかは手探りなので、ちょっと手間はかかるけど自由にいろいろカスタマイズできるようにしています。
templateの挙動はヘッダーにある"Template Playground"ページから確認できます。
3. Githubのwebhookに登録する
Tokenの登録が完了すると、次のような画面が表示されます。
一意なcodeが生成され、Github webhookに登録するPayload URLが表示されるので、Github上に登録します。
Content-typeはapplication/jsonにします。
Secretを使った場合は、Secretも入力しましょう。
なお、登録したTokenは"Token List"に表示され、随時編集もできる。
やっぱりmessageを変えたいぞ!!ってときは変更できます。
インストール/起動方法とか
READMEに書いてあります。
https://github.com/be-hase/ghe-line-notify
すぐに動かして見たい場合は、READMEにあるherokuボタンを押せば完了です。
しかし、Github Enterpriseを使うような環境ではHerokuとは接続できないでしょう。
その場合は、READMEに書いてあるようにpython3をinstallしたサーバで、
git clone https://github.com/be-hase/ghe-line-notify cd ghe-line-notify pip install -r requirements/common.txt python manage.py db upgrade gunicorn -c gunicorn_config.py app:app
とかすれば良いです。
なお、python3.5.xでしか動作確認はしていないので、うまくいかなかったら最新版にあげてみてください...。
もしくは、Dockerfileも置いてあるのでDockerで動かしても良さそうです。
alembicを使ってDB migrateをしています。
デフォルトだと、sqliteを使用します。
失ったら死ぬ!といったデータでもないですし、sqliteで十分なのでは...と思っていますが、もしも変えたい場合は環境変数GHE_LN_DATABASE_URI
を指定すればいいです。
heroku上で立ち上げるときは、herokuのaddonで用意されているpostgresqlを使用するようにしています。
今後修正実装しようかなと思っていること
「gheのOAuth2連携できるようにして!」って同僚から言われたので、あとで作ってみようかなと思っている。
まとめ
- Personal Token発行するだけでサクっとcurlからでも使えるので便利!
- 今回紹介したghe-line-notifyみたいなツールもサクっと作ることができる!
- OAuth2実装すれば、自分のWEBサービスとかからも通知できる!
なにか他にも面白そうなのがあれば、githubとかにあげてこうかなと思っています。
Reluminでslowlog見れるようにした
やったこと
ReluminっていうRedis Cluster Admin Toolでslowlog見れるようにしてくれい!という依頼があったので、実装してみた。
こんな感じになります。
GitHub - be-hase/relumin: Redis cluster admin tool
横軸に時間とって、縦軸にslowlogの実行時間(micro seconds)をノードごとに色分けしてscatter plotしていっている。
hoverするとツールチップでslowlogのコマンドとかも見れる感じ。
graphの下には、tableで羅列している。
Redisみたいにシングルスレッドでイベントループしてるやつで長い実行時間のcommandあると危険なので、
頻発するようだったら対処してあげてみてください。
今後
時間と気合があったらやることリスト
- INFOコマンドで取得したmetricsに対してはalertできる機能あるので、slowlogもalertしてあげても良いかもしれない。
- Redis3.0.6くらいから付属しているredis-trib.rbには良い感じに均等にリバランシングする機能がついたので、Relumin上でも実装してワンクリックでリバランシングできるようにしても良いかもしれない。
- 今だとRelumin上でノードごとに複数回リシャードしないといけないので、少し面倒くさい。
- Jedis2.9からclusterでもpassword設定できるようになるので、その対応しておこうかな。
- でも他の言語のclientはまだpasswordのサポートしてなさそうだからあとでいいかな。
- React(SPA)で書いてるフロント部分、Typescriptで書きなおそうかな。
- metricsとかメタデータ保存しているstorage, RDBで実装するの面倒だったのでRedis使ってるんだけど明らかにお金もったいないのでRDBも使用できるようにしようかな。
- チーム内でこじんまりと使うぶんにはよかったけど...
Java(Maven) + Docker + CircleCI + CodecovなCI環境にしたメモ
1年位前に個人で開発していたReluminというRedis Cluster Admin Toolがあるんですが、
久しぶりに新しい機能いれるかってなって、ついでにtest + local env周りのちょっと残念な部分というか手抜きしていた部分を刷新してみた。
github.com (気が向いたら、Relumin自体のエントリもそのうち書こう)
以前の環境
個人で作っていたものだったので雑にVagrantでRedis cluster構築して、開発時とかunit testのときはそのVagrantのRedisに接続してごにょごにょとやっていた。
特にCI toolとかも回さず、手元でmvn testしてそれでいいや、みたいな感じ。
今の環境
Docker
Vagrantを捨てて、Dockerにした。 やっぱり起動早いし、使い捨てとかしやすいし、そのままCircleCIでも動かせたりするっていうのが理由。
Dockerfileはこんな感じ。
relumin/Dockerfile at develop · be-hase/relumin · GitHub
ちなみにやたらとEXPOSEしているけど、
- 9000
- meta data保存する用のRedis
- 10000 ~ 10005
- Redis Clusterのnode 6台
- 20000 ~ 20005
- Redis Clusterのnode間通信用 (gossip通信)
- これもちゃんとやらないとcluster作れないので注意。ついつい忘れがち。
- 10010 ~ 10015
- 普通のRedis。いまのところ使っていない。
という用途です。
慣れでそのままsupervisord使ってしまったが、centos7にしてsystemdとかで良いと思う。
CircleCI
最初は慣れているtravisを使用していたのだけどDocker imageのcacheがうまく出来なくて、途中からCircleCIにした。
毎回docker buildするのは時間かかって辛い。
最近会社でCircleCI Enterpriseが導入されそうっていうのも理由のひとつであったりするし、なによりもsshできるのは良い。
circle.ymlはこんな感じ。
relumin/circle.yml at develop · be-hase/relumin · GitHub
Docker imageのcacheはこのあたりでやっている。
dependencies: cache_directories: - "~/docker" override: - if [[ -e ~/docker/image.tar ]]; then docker load -i ~/docker/image.tar; fi - docker build -t relumin/relumin-test . - mkdir -p ~/docker; docker save relumin/relumin-test > ~/docker/image.tar
参考 : Continuous Integration and Delivery with Docker - CircleCI
ちなみに、
dependencies:overrideでmvn install -DskipTests
しているのは、mavenをcacheさせるため。
もう少し適切なgoalがある気はする。
Codecov
Coverage toolは色々あるけど、Codecovを使ってみた。
Codecovにした理由は、
documentの量とか他のものより多かったし、こっちのほうがUIが好みだったのと、カバレッジサービスのための依存lib(uploadするためのlib)がなかったから。
mavenの場合は、pomにjacocoのpluginを入れるだけで良い。 (jacoco自体は、javaで一般的なカバレッジツール)
<plugin> <groupId>org.jacoco</groupId> <artifactId>jacoco-maven-plugin</artifactId> <version>0.7.7.201606060606</version> <executions> <execution> <goals> <goal>prepare-agent</goal> </goals> </execution> <execution> <id>report</id> <phase>test</phase> <goals> <goal>report</goal> </goals> </execution> </executions> </plugin>
circle.ymlにこんな風に書いている。
test: override: - docker run -d --net="host" -p 9000:9000 -p 10000:10000 -p 10001:10001 -p 10002:10002 -p 10003:10003 -p 10004:10004 -p 10005:10005 -p 20000:20000 -p 20001:20001 -p 20002:20002 -p 20003:20003 -p 20004:20004 -p 20005:20005 -p 10010:10010 -p 10011:10011 -p 10012:10012 -p 10013:10013 -p 10014:10014 -p 10015:10015 relumin/relumin-test - mvn test jacoco:report -DargLine="@{argLine} -Dspring.profiles.active=ci" post: - mkdir -p $CIRCLE_TEST_REPORTS/junit - find . -type f -regex ".*/target/surefire-reports/.*xml" -exec cp {} $CIRCLE_TEST_REPORTS/junit/ \; - cp -r ./target/site/* $CIRCLE_TEST_REPORTS/ - bash <(curl -s https://codecov.io/bash)
mvn test jacoco:report -DargLine="@{argLine} -Dspring.profiles.active=ci"
でテストレポートが諸々作成される。
CircleCI上では、Spring bootの"ci" profileで実行したいので-DargLine="@{argLine} -Dspring.profiles.active=ci"
を付与している。
jacoco自体もargLineを利用しているため、@{argLine}をつけないと上書きされてしまってjacocoが上手く動かないので注意。 ハマった。
test reportを
find . -type f -regex ".*/target/surefire-reports/.*xml" -exec cp {} $CIRCLE_TEST_REPORTS/junit/ \
と
cp -r ./target/site/* $CIRCLE_TEST_REPORTS/
でCIRCLE_TEST_REPORTSに移動することで、CircleCI上のArtifactsで見れるようにしている。こんな感じ。
Continuous Integration and Deployment
最後にbash <(curl -s https://codecov.io/bash)
を実行することで、Codecovにtest reportを送信している。
他
test code自体もがっつり書き直した。
Mockitoで済むところはMocktioだけでいい。全部springかましたtestでやると遅くて辛い。
( springかますのは、最終的なintegrateなtestと、DAOくらいでいいかなと。 )
所感
仕事だとJenkinsでこういったことやってるけど、やっぱりこの手のツールのほうが楽だし簡単。UIも綺麗で嬉しい。
Prometheusのfluentd_monitor_agent_exporter書いた
最近身の回りでPrometheusが使われていて、僕も色々と試してみようと思い昨晩ビールでも飲みながらdocumentを眺めてた。
ほほう...なるほどと思い、次は適当になんかexporterでも書いてみっか!ってなって、ちょうど昨日fluentd meetupやってたのでfluentdのmonitor agentの情報をexportするやつ書いてみた。
fluentd monitor agent plugin使うと、buffer_queue_length, buffer_total_queued_size, retry_countが取得できるのでそれをexportしてる。 Monitoring Fluentd | Fluentd
各自のoutput pluginの設定に応じて、適切なalertとか飛ばしてあげるといいかなって感じです。