そごうソフトウェア研究所

SOA、開発プロセス、ITアーキテクチャなどについて書いています。Twitterやってます@rsogo

homebrewでmysqlを8から5.7にバージョンを落としたときの対応

趣旨

Macにhomebrewで入れたMySQLを8から5.7にダウングレードした時の話をします。 事の発端は新しいMacBook Proに開発環境を移行しようとしたんですが、 homebrewでMySQLを入れるとバージョン8が入りました。

で、いろいろ変わっているようなので、今やりたいのはMySQL8対応じゃないし、問題を先送りすることにして、5.7にバージョンを落とすことにしました。 しかし、これはこれで小一時間はまりました。

発生した問題

homebrewは提供されていればバージョンを指定して過去のバージョンをインストールできます。brew uninstallして、brew installしましたが、起動が失敗します。

結論としては、アンインストールしても/usr/local/var/mysql/に各種ファイルが残っていて、それが原因で起動に失敗していました。/usr/local/var/mysql/以下のファイルをすべて削除することで解消しました。

やった内容

ここから先は、実際に試した内容です。

MySQL 8のuninstall

$ brew uninstall  mysql

MySQL 5.7のinstall

  • brew search {キーワード}で、どのパッケージが利用可能か確認
    ラッキーなことに5.5, 5.6, 5.7が使えそうです。
$ brew search mysql
==> Formulae
automysqlbackup              mysql++                      mysql-cluster                mysql-connector-c++          mysql-search-replace         mysql@5.5                    mysql@5.7 ✔
mysql                        mysql-client                 mysql-connector-c            mysql-sandbox                mysql-utilities              mysql@5.6                    mysqltuner

==> Casks
homebrew/cask/mysql-connector-python     homebrew/cask/mysql-shell                homebrew/cask/mysql-utilities            homebrew/cask/navicat-for-mysql          homebrew/cask/sqlpro-for-mysql
  • バージョンを指定してインストール
$ brew install  mysql@5.7
==> Downloading https://homebrew.bintray.com/bottles/mysql@5.7-5.7.23.high_sierra.bottle.tar.gz
Already downloaded: /Users/rsogo/Library/Caches/Homebrew/mysql@5.7-5.7.23.high_sierra.bottle.tar.gz
==> Pouring mysql@5.7-5.7.23.high_sierra.bottle.tar.gz
==> /usr/local/Cellar/mysql@5.7/5.7.23/bin/mysqld --initialize-insecure --user=rsogo --basedir=/usr/local/Cellar/mysql@5.7/5.7.23 --datadir=/usr/local/var/mysql --tmpdir=/tmp
==> Caveats
We've installed your MySQL database without a root password. To secure it run:
mysql_secure_installation

MySQL is configured to only allow connections from localhost by default

To connect run:
    mysql -uroot

This formula is keg-only, which means it was not symlinked into /usr/local,
because this is an alternate version of another formula.

If you need to have this software first in your PATH run:
  echo 'export PATH="/usr/local/opt/mysql@5.7/bin:$PATH"' >> ~/.bash_profile

For compilers to find this software you may need to set:
    LDFLAGS:  -L/usr/local/opt/mysql@5.7/lib
    CPPFLAGS: -I/usr/local/opt/mysql@5.7/include
For pkg-config to find this software you may need to set:
    PKG_CONFIG_PATH: /usr/local/opt/mysql@5.7/lib/pkgconfig


To have launchd start mysql@5.7 now and restart at login:
  brew services start mysql@5.7
Or, if you don't want/need a background service you can just run:
  /usr/local/opt/mysql@5.7/bin/mysql.server start
==> Summary
🍺  /usr/local/Cellar/mysql@5.7/5.7.23: 317 files, 234.4MB

これでうまく行ってくれれば良かったのですが、起動させると、次のようなエラーがでました。

$ mysql.server start
Starting MySQL
. ERROR! The server quit without updating PID file (/usr/local/var/mysql/rsogo-Mac-Book-2.local.pid).

PID fileは存在しないし、困ってしまったのですが、/usr/local/var/mysql/にある.errファイル(私の環境ではrsogo-Mac-Book-2.local.errというファイル名でした)を見ると、既にibdata1があるというFatalレベルのエラーが発生していました。

今回は初期構築なので、MySQLを一度アンインストール後、/usr/local/var/mysql以下をバッサリ削除して、再度インストールすることで、下記のように正常に起動するようになりました。

$ mysql.server start
Starting MySQL
. SUCCESS! 

実際にはこんなにすんなり行かず、特に下記のようにサービスとして起動するようにすると、これ自体は成功して、実際アクセスすると接続でエラーになるので、設定がミスっているのではないかとか、いろいろ試行錯誤しました。勘でいろいろやるんじゃなく、エラーログを調べるのは大事ですね。

brew services start mysql@5.7

あと今回いろいろ調べるなかでhomebrewで入れると/usr/local/Cellar/mysql@5.7/5.7.23みたいなパスにインストールされるということも理解できました。

Mule ESBのフロー定義中にでGroovyスクリプトでカスタムのロジックを埋め込む

そういえば、Salesforce.comによるMulesoftの買収が発表されましたね。今後、どのようになっていくのか注目したいと思います。

www.mulesoft.com

さて、今回はMuleのフロー定義にGroovyのスクリプトを埋め込んでみたいと思います。 これができるとMuleで予め用意されたメッセージの変換ルール関数以外の独自処理をスクリプトで行うことができます。 Groovy以外にもJavaScriptなどのスクリプトを呼び出せます。

フロー定義

全体像

HTTPのGETリクエストを受け取り、URLのPATHのlxに置き換えるという処理をしています。 処理は<scripting:transformer>の中でやっています。名前空間http://www.mulesoft.org/schema/mule/scriptingを使うためにhttp://www.mulesoft.org/schema/mule/scripting http://www.mulesoft.org/schema/mule/scripting/current/mule-scripting.xsd"を宣言に追加しています。

<mule
  xmlns="http://www.mulesoft.org/schema/mule/core"
  xmlns:doc="http://www.mulesoft.org/schema/mule/documentation"
  xmlns:http="http://www.mulesoft.org/schema/mule/http"
  xmlns:scripting="http://www.mulesoft.org/schema/mule/scripting"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://www.mulesoft.org/schema/mule/http http://www.mulesoft.org/schema/mule/http/current/mule-http.xsd
http://www.mulesoft.org/schema/mule/core http://www.mulesoft.org/schema/mule/core/current/mule.xsd
http://www.mulesoft.org/schema/mule/scripting http://www.mulesoft.org/schema/mule/scripting/current/mule-scripting.xsd">

  <http:listener-config name="HTTP_Listener_Configuration" host="0.0.0.0" port="8086" basePath="groovy" doc:name="HTTP Listener Configuration"/>

  <!-- GET -->
  <flow name="GetFlow">
    <http:listener config-ref="HTTP_Listener_Configuration" path="/*" doc:name="Recieve HTTP request" allowedMethods="GET">
      <http:response-builder reasonPhrase="#[flowVars['reason']]" statusCode="#[flowVars['statusCode']]"/>
    </http:listener>
    <logger doc:name="Log the payload" level="INFO" message="GET is called."/>

    <set-variable variableName="VarTableName"
                  value="#[message.inboundProperties['http.request.path']]"
                  doc:name="Variable"/>
                  
    <scripting:transformer name="stringReplaceWithParams">
      <scripting:script engine="groovy">
        <property key="oldStr" value="l" />
        <property key="newStr" value="x" />
        <scripting:text>
          return VarTableName.toString().replaceAll("$oldStr", "$newStr")
        </scripting:text>
      </scripting:script>
    </scripting:transformer>
  </flow>
</mule>

ステップバイステップで見ていきます。

HTTPリクエストのリッスン

8086ポートで、URLのベースのパスとして/groovyに来たリクエストを受け取る設定になっています。

<http:listener-config name="HTTP_Listener_Configuration" host="0.0.0.0" port="8086" basePath="groovy" doc:name="HTTP Listener Configuration"/>

GETメソッドで呼び出されたときに、下記のフローが動作します。

  <flow name="GetFlow">
    <http:listener config-ref="HTTP_Listener_Configuration" path="/*" doc:name="Recieve HTTP request" allowedMethods="GET">

URLパスの取り出し

HTTPリクエストのパス(ホスト名、ポート名は含まず、それ以降)を取り出し、VarTableNameという変数に入れています。

    <set-variable variableName="VarTableName"
                  value="#[message.inboundProperties['http.request.path']]"
                  doc:name="Variable"/>

Groovyのスクリプトで文字列操作

    <scripting:transformer name="stringReplaceWithParams">
      <!-- scriptのエンジンとして、groovyを指定 -->
      <scripting:script engine="groovy">
        <!-- 変数に値を設定 -->
        <property key="oldStr" value="l" />
        <property key="newStr" value="x" />
        <!-- 実際のGroovyのコード。replaceAllでURLパスの文字列を変換して、応答として返す -->
        <scripting:text>
          return VarTableName.toString().replaceAll("$oldStr", "$newStr")
        </scripting:text>
      </scripting:script>
    </scripting:transformer>

テストしてみましょう

リクエス

次のURLに対して、GETメソッドのリクエストを出します

http://localhost:8086/groovy/helloworld

レスポンス

URLのパス(/groovy/helloworld)のうち、lxに置換した結果が返ってきています。

/groovy/hexxoworxd

curlコマンドで確認したら、こんな感じ。

$ curl http://localhost:8086/groovy/helloworld
/groovy/hexxoworxd

うまくいきましたね。

AWS Lambdaの何が嬉しかったか。Excel生成WebサービスのLambda関数化

AWS Lambdaを使った開発をやってみて何が嬉しかったのかを書いておこうと思います。

Lambda関数化したのは以下のようなWebサービスでした

  • SPAなWebアプリからJSONでリクエストを受け取る
  • JavaExcelを扱うライブラリであるPOIでExcelのテンプレートファイルにJSONで受け取ったデータを埋め込む
  • Excelをクライアントに返す(ブラウザでダウンロードが始まる)

Excelを触る時点で、Web系の技術だけで完結するのは難しいので、 この部分だけJavaでやるというのは今後もあるんじゃないかなと思います。

で、次のような課題がありました

  • 普段は大丈夫だけど、たまにでかいデータが来て、Java側でOutOfMemoryが発生する
  • そもそもExcel作成リクエストはたまにしか実行されない(業務のひとまとまりの作業が終わって帳票を出したいときだけ)
  • なのに、でかいデータのためにそれなりのサイズのEC2を用意しておかないといけない

Lambda関数化したことで

  • 使うときのみリソースを割り当てるので、思い切ったリソースを割り当ててもコスト的には下がる。これは処理頻度が低いサービスでは大きいメリットだと思います
  • 副次的な効果として、これまで同じJavaアプリケーションサーバー上で実行していた他のアプリケーションがExcel作成WebサービスのOutOfMemoryの巻き添えを食っていたのですが、単独機能のサービスとして切り出されたことで、お互いに影響を受けることがなくなりました。マイクロサービス化ができたわけです。

他の用途ですと、Javaの立ち上がり時の遅さが気になったかもしれませんが、 今回のケースだと、そもそもExcelを生成するのに10秒くらいかかるような Webサービスだったので性能は気になりませんでした。

まとめ

AWS Batchも使ってみたいなーと思っているサービスで、 バッチという処理頻度が低い処理に対して、 実行されるときだけ大きいリソースを割り当てて(必要だったら並行でいっぱい立ち上げて)、 終わったら終了というのは非常にクラウドらしく、魅力的です。

もちろんサーバレスになることで、運用が楽になるか、スケールするとかいい事はあると思うのですが、 処理面での直接的な効果として頻度が低い処理に、思い切りリソースを割り当てられるというメリットがありました。

Lambda関数をAPI Gatewayで公開する(シンプル版)

先日、JavaでLambdaを作るエントリを書きましたが、今回、API Gatewayを使って、Lambdaで実装した処理をWebサービスとして公開したいと思います。

API GatewayはRESTfulなAPIを簡単に作れます。 RESTfulなので、まずはリソースを定義して、そのリソースに対するGET、POST、PUT、DELETEなどのメソッドを提供します。 後はどのバックエンドに接続するのかが、基本的な手順です。

手順はこちらをベースに進めます。

docs.aws.amazon.com

Lambda関数の作成

バックエンドはLambda関数を使います。 こちらで公開されているNode.jsのサンプルを、そのまま使って作成しておきました。

docs.aws.amazon.com

APIの作成

f:id:begirama:20180513002008p:plain

リソースの定義

最初にリソースを定義します。

f:id:begirama:20180513002455p:plain

  • リソース名: 任意の名前です。ここではproxyとしています
  • リソースパス: /proxy/{itemId}など個別のパターンを設定できますが、ここではワイルドカードになる{proxy+}を使います。これで、ルートのURL以下はなんでも受け取ります。

メソッドの定義

f:id:begirama:20180513002542p:plain アクションで、メソッドの作成を選びます。

f:id:begirama:20180513002702p:plain Lambda関数プロキシを選択します。 リージョンを選択して、Lambda関数名に何文字か入力すると、候補が表示されます。

f:id:begirama:20180515072400p:plain ここまでの状態はこんな感じ。

APIのデプロイ

f:id:begirama:20180513003023p:plain このままでは呼び出せないので、APIのデプロイを行います。

f:id:begirama:20180513003052p:plain ステージというのが設定できて、ステージごとに認証やスロットリングの設定を変更することができます。 例えば、社内用のAPIと、取引先用のAPIなんかで分けることもできそうです。

f:id:begirama:20180513003200p:plain

APIの呼び出し

デプロイが完了すると、こんな感じのURLが割り当てられます。

f:id:begirama:20180515070649p:plain

このまま呼び出すと、

{"message":"Missing Authentication Token"}

エラーが返ってきます。 エラーメッセージの内容がわかりにくいのですが、上のURLはルートのもので、子リソース(ここではproxy)が定義されている場合には、そのまま呼び出すとこのエラーが返ってきます。

そこで、https://dummy.execute-api.ap-northeast-1.amazonaws.com/dev/proxyのような子リソースを含むURLで呼び出すと、 欲しかった結果を得られます。

Install Node.js v8 and npm 5.6 for macOS High Sierra

以前、Node.js v5をMacにインストールする手順を書きましたが、MacBook Proを新調したので、新しくセットアップした手順をメモします。

begirama.hatenablog.com

公式サイト Node.js からMac用のpkgファイルをダウンロードします。2018/5/14時点で安定版の最新はv8.11.1でした。

pkgを実行して、Node.jsをインストールします。

セットアップ後のバージョンの確認は以下です。

$ node -v
v8.11.1

ついでにnpmのバージョンも確認します。

$ npm -v
5.6.0

npmを使って、gulpをインストールしてみます。

$ sudo npm install --global gulp-cli
Password:
/usr/local/bin/gulp -> /usr/local/lib/node_modules/gulp-cli/bin/gulp.js
+ gulp-cli@2.0.1
added 238 packages in 5.825s

bowerも入れてみます。

$ sudo npm install -g bower
Password:
/usr/local/bin/bower -> /usr/local/lib/node_modules/bower/bin/bower
+ bower@1.8.4
added 1 package in 2.983s


   ╭─────────────────────────────────────╮
   │                                     │
   │   Update available 5.6.0 → 6.0.1    │
   │     Run npm i -g npm to update      │
   │                                     │
   ╰─────────────────────────────────────╯

正常に動作していそうですね。

JavaでAWS Lambdaの実装をやってみる

各種情報

  • 開発者ガイド docs.aws.amazon.com

  • AWS Lambdaには設計書と呼ばれるテンプレートが用意されていて、テンプレートをカスタマイズすることでいろいろな機能を試すことができます f:id:begirama:20180415104106p:plain

チュートリアル

チュートリアルへのリンクが公開されています。 aws.amazon.com

開発者ドキュメントの中に含まれるチュートリアルdocs.aws.amazon.com

テストと実行結果の確認

Jarのデプロイ

Jarファイルをアップロードすることでデプロイができます。 デプロイ時に必要なのは次の項目の設定です。 - Zip/Jarファイルの直接アップロードか、S3からのアップロードか - ランタイム - Java8を指定します - ハンドラー - エントリポイントになるメソッドの{パッケージ名}.{クラス名}::{メソッド名}の形式で指定します(例: example.Hello::handleRequest

デプロイして実行時にエラーになった場合には、コンソールにエラーが出力されます。 これはLambdaはJavaのランタイムとして、Java8がサポートされているが、ビルド時にJava9でコンパイルしてしまったためエラーが発生しています。 f:id:begirama:20180415084134p:plain

テストと実行結果の確認

テストの設定を行って、テストを実行します。入力イベントを設定する前に試してみることができます。

  • テストの設定 f:id:begirama:20180415103536p:plain

  • テスト結果の確認 f:id:begirama:20180415103452p:plain

エラー時

f:id:begirama:20180415091308p:plain

CouldWatchにもエラーのログが出力されています。

f:id:begirama:20180415093108p:plain

Apache ActiveMQ ArtemisのLibrary artemis-native-64 not foundメッセージは問題あるのか調査

AppPot - 企業向けスマートデバイスアプリ開発のためのプラットフォーム」の内部では、Apache ActiveMQ Artemisを使用しています。 下記のようなメッセージが出るので調査をしてみました。

DEBUG [org.apache.activemq.artemis.jlibaio] (MSC service thread 1-2) Library artemis-native-64 not found!
DEBUG [org.apache.activemq.artemis.jlibaio] (MSC service thread 1-2) artemis-native-32 -> error loading the native library: java.lang.UnsatisfiedLinkError: no artemis-native-32 in java.library.path
        at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1867)
        at java.lang.Runtime.loadLibrary0(Runtime.java:870)
        at java.lang.System.loadLibrary(System.java:1122)
        at org.apache.activemq.artemis.jlibaio.LibaioContext.loadLibrary(LibaioContext.java:68)
        at org.apache.activemq.artemis.jlibaio.LibaioContext.<clinit>(LibaioContext.java:88)
        at org.apache.activemq.artemis.core.io.aio.AIOSequentialFileFactory.isSupported(AIOSequentialFileFactory.java:107)
        at org.wildfly.extension.messaging.activemq.ActiveMQServerService.start(ActiveMQServerService.java:155)
        at org.jboss.msc.service.ServiceControllerImpl$StartTask.startService(ServiceControllerImpl.java:1963)
        at org.jboss.msc.service.ServiceControllerImpl$StartTask.run(ServiceControllerImpl.java:1896)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
        at java.lang.Thread.run(Thread.java:748)

DEBUG [org.apache.activemq.artemis.jlibaio] (MSC service thread 1-2) Library artemis-native-32 not found!
DEBUG [org.apache.activemq.artemis.jlibaio] (MSC service thread 1-2) Couldn't locate LibAIO Wrapper
DEBUG [org.jgroups.protocols.FD_SOCK] (FD_SOCK pinger,swarm-jgroups,amapawsmbw02) amapawsmbw02: ping_dest is ip-10-236-2-198, pingable_mbrs=[ip-10-236-2-198, amapawsmbw02]
INFO  [org.wildfly.extension.messaging-activemq] (MSC service thread 1-2) WFLYMSGAMQ0075: AIO wasn't located on this platform, it will fall back to using pure Java NIO. Your platform is Linux, install LibAIO to enable the AIO journal and achieve optimal performance.

Apache ActiveMQ ArtemisのPersistenceのドキュメントに依ると、Java NIOとLinuxのネイティブの非同期IOライブラリ ( libaio ) を使う2つの実装が用意されているようです。

Persistence | ActiveMQ Artemis Documentation

libaioについては、更にこちらのドキュメントで、詳細に記述されています。

Libaio Native Libraries | ActiveMQ Artemis Documentation

libActiveMQAIO32.so または libActiveMQAIO64.so でlibaioを使うのですが、これがライブラリのパスに見つからないというメッセージがDEBUGレベルで出ています。 そのため、Java NIOを使いますよ、LibAIOを使ったほうが性能が良くなりますよ、というログがでているみたいですね。

動作させる上では問題なさそうですが、性能を向上させるためには対応したほうが良さそうです。

普通のWildFly10にはlibartemis-native-64.soが含まれているのを確認しましたが、 今このログがでているのはWildfly Swarmで実装しているアプリケーションででているので、 Wildfly Swarmのパッケージングを調査しようと思います。