2021年、ひさしぶりの JMeter

負荷テストをやろうとして Vegeta を使おうと思いましたが、multipart/form(つまり、ファイルアップロードをともなう)のリクエストがどうもうまくいきませんでした。 今っぽくないですが、昔からよく知ってる JMeter を使ってみました。

JMeterMac にインストール

homebrew で一発でした。いい時代だ。

brew install jmeter

f:id:hideoku:20210705180426p:plain
昔ながらの UI

実行ファイルの調整

なにか操作をしてみると、挙動がおかしい…コンソールに以下の例外が出力されています。

Uncaught Exception java.lang.IllegalAccessError: class com.github.weisj.darklaf.ui.filechooser.DarkFilePaneUIBridge$DetailsTableModel (in unnamed module @0x157632c9) cannot access class sun.awt.shell.ShellFolder (in module java.desktop) because module java.desktop does not export sun.awt.shell to unnamed module @0x157632c9 in thread Thread[AWT-EventQueue-0,6,main]. See log file for details.

jmeter の実行ファイルに調整を入れます。わたしの環境だと、以下のファイルでした。

/usr/local/Cellar/jmeter/5.4.1/libexec/bin/jmeter

このファイルの途中に JAVA9_OPTS=.... という行で —add-opens=xxx という形で列挙されているので、それに続くように末尾に --add-opens=java.desktop/sun.awt.shell=ALL-UNNAMED を追加します。

参考: https://stackoverflow.com/questions/67615212/why-am-i-not-able-to-click-on-open-icon-in-jmeter

注意点:負荷テスト実施するときは CLI から

JMeter を起動する際に以下のような Warning がでてます。

WARNING: package sun.awt.X11 not in java.desktop
================================================================================
Don't use GUI mode for load testing !, only for Test creation and Test debugging.
For load testing, use CLI Mode (was NON GUI):
   jmeter -n -t [jmx file] -l [results file] -e -o [Path to web report folder]
& increase Java Heap to meet your test requirements:
   Modify current env variable HEAP="-Xms1g -Xmx1g -XX:MaxMetaspaceSize=256m" in the jmeter batch file
Check : https://jmeter.apache.org/usermanual/best-practices.html
================================================================================

Don't use GUI mode for load testing ! とあるように GUI から負荷かけるのはだめな模様。 GUI でテストを流してみるとわかりますが、JMeter が固まったりします。テストケースを作って、サンプル的に実施試してみてよさそうなら CLI で実行しましょうということですね。

さいごに

数年ぶりに JMeter を使いましたが、ほとんど変わってなかったですね。 ファイルアップロードをともなうリクエストも簡単に送れました。

散歩のおともにオーディオブック

朝と夕方に散歩するのにはまっています。 1回30分から1時間くらい、自宅の周辺を歩き回っています。

散歩するためにオーディオブックを導入した

何の工夫もなく散歩しようとするとモチベーションが維持できないので、色々と考えてみた結果、オーディオブックを導入してみました。

音楽も考えたのですが、聴きたいから家を出ようというきっかけ作りにはならなさそうでした。

導入してみた結果は・・・かなりフィットしました。 オーディオブック聴けるから、散歩に出かけようという気持ちがわき、毎日散歩に出かけるモチベーションにつながっています。

オーディオブックアプリ

Amazon Audible アプリを選びました。

初回は無料で好きな書籍を購入できるので、お試しにはピッタリかと思います。 あとで、他のアプリも試してみましたが、使用感も Audible がいちばんよかったです。

聴いている書籍

『嫌われる勇気』を読んでいます聴いています。 結構有名な本なので、書籍でもよく見かけることが多いです。

嫌われる勇気―――自己啓発の源流「アドラー」の教え

嫌われる勇気―――自己啓発の源流「アドラー」の教え

一度読んでみたかったのと Audible でおすすめされたのもあって、この本にしました。

あと、聴いてみてからの感想になりますが、この本は対話形式で話が進みます。 哲学者と若者、ふたりの掛け合いが終始続くので、テンポがあり飽きずに聴けると思います。

聴いてみた感想

歩きながら聴くので、内容が頭に入ってこないのではないかと危惧していました。 しかし、やってみると意外と内容に集中することができています。

難しい言い回し、固有名詞のような語句については、理解に苦しむことはありますが、それがデメリットになることはありません。

オーディオブックの機能で、倍速再生があります。 1周目は何も考えずに 1.0 倍で聴いていました。

『嫌われる勇気』は聴き終わるのに7時間もかかりました。 さすがに2周目は倍速再生したいと思いましたし、内容を知っているので倍速再生でも理解が追いつくだろうという感触はありました。 ということで、1.75 倍で聴いています。2.0 倍だと速くてひっかかることが度々あり、ストレスになりそうなので遅くしました。

次はどうする?

オーディオブックは1冊だいたい 1,500円〜2,000円 します。 読み放題プランのオーディオブックアプリを試してみましたが、読み放題対象の書籍は個別の買い切り対象のものと比べると、やはり見劣りがします。

1冊で2週間くらいは持ちそうなので、2,000円は安いのではと考えています。なにより運動する機会を与えてくれます。 『嫌われる勇気』が飽きてきたら、次を探してみようと思います。

情報の洪水をシャットアウトする努力

『遅読家のための読書術』という本を読みました。
※この本を読んだことをきっかけに思ったことをまとめていますので、書籍レビューではないです。ご注意ください。

心に刺さった一節

私は遅読家、つまり本を読むのが遅い方ではないと思っていますが、手に取り買ってしまいました。 冒頭の一節が心に刺さったからです。

「読書量が減った」「本が読めなくなった」といっている人はそうした「新しい読み方」と「これまでの読み方」とのあいだで真っ二つに引き裂かれています。(P8)

「そう、それ!!」と激しく同意して、とりあえず買いました。

ここで言っている「新しい読み方」というのは、スマホSNSやウェブニュースといった圧倒的な量の情報を洪水のように流し込むことです。一方で、「これまでの読み方」というのは本の一言一句を深く読み込んでいくことですね。

この本では、ゆっくりと深く読み込んでいくのではなく、1日1冊くらいのペースで速く読むことをおすすめされています。 それが主題となっています。

本が読めなくなった

上記にあるように、ここ数年本当に本が読めなくなりました。1日1冊ではないですが、2日で1冊くらいのペースで読んでいた時期もありました。

この本を読んでいて、確かにスマホのせいだと改めて実感しました。
スキマ時間をスマホに奪われるだけでなく、情報のインプットも SNS に適したやり方に慣れてしまってます。その一方で、読書はこれまでの読み方で読みたいと(無意識に)思っていて、ギャップに苦しんでいます。

スマホからのインプットの洪水にさらされることから、積極的に回避していかないといけないと痛感しました。よく言われることですが、身にしみて再認識したということですね。

とはいえ、本の読み方も少しは変えていかないといけないとも思いました。

1時間ですばやく読んだほうが、本のポイントがしっかりと記憶に残っていることが多いのです。
逆に、ウンウンうなりながら1ヶ月くらいかけてようやく読み終えた本って、「結局、なにが書いてあったんだっけ?」ということになったりしませんか?

この差はどこから生まれているのか?

「読書密度の違い」と「全体観の有無」だと思います。長期間かけた熟読というのは、単位時間あたりの読書の密度が低いうえ、その本の全体像が見えづらくなりますから、非常に「薄い」読書体験しか提供しません。(P63)

SNSを除外したとしても、依然として多くの学習が必要ですので、こうしたテクニックも用いていかないといけないですね。

さいごに

読書術で必ずいわれることですが、この本でも読書においてアウトプットすることが大切だといっていましたね。
ということで、ひさびさにブログ書いてみましたとさ。

Angular で JointJS を動かすための最初の一歩

Angular に JointJS(https://www.jointjs.com/opensource)を組み込んでグラフ描画を実装しているのですが、日本語の情報がなくて苦労しました。
メモっておいたら役立つかもということで、まずは導入部分での工夫を今回まとめます。

使用しているバージョンはこちら。

パッケージ バージョン
@angular/core 5.2.9
@angular/cli 1.7.4
jointjs 2.0.1

公式サイトのチュートリアルに沿って、動かせる所までやってみます。
https://resources.jointjs.com/tutorial/hello-world

JointJS のパッケージをインストールする

npm install --save jointjs

ここは悩みどころはないはずです。

JointJS を使えるように Component に import する

import { Component, OnInit } from '@angular/core';

import * as joint from 'jointjs';

@Component({
  selector: 'app-simple',
  template: '<div id="myholder"></div>',
  styleUrls: ['./simple.component.css']
})
export class SimpleComponent implements OnInit {
  graph = new joint.dia.Graph;

  constructor() { }

  ngOnInit() {
  }
}

まずは上記のように import * as joint from 'jointjs'; としたいところですが、

node_modules/jointjs/dist/joint.d.ts(1395,34): error TS2503: Cannot find namespace 'JQuery'.

という具合にコンパイルエラーが発生します。

そこで、「import * as joint from 'jointjs';」の代わりに以下のようにして 'jointjs' を使えるようにしました。

declare function require(x: string): any;
const joint = require('jointjs/dist/joint.js');

TypeScript のコンパイラが以下のような require が見つからないとエラーを発生させるので
2行目のように require 関数を宣言してあげる必要があります。

const joint = require('jointjs/dist/joint.js'); ERROR in src/app/simple/simple.component.ts(6,15): error TS2304: Cannot find name 'require'.

Hello World サンプルコード全体

ということで、チュートリアルを動かすサンプルコード全体。
所々で TypeScript 向けに変更してます。

import { Component, OnInit } from '@angular/core';

declare function require(x: string): any;
const joint = require('jointjs/dist/joint.js');

import * as $ from 'jquery';  /* joint.dia.Paper の初期化で $ を使うため */

@Component({
  selector: 'app-simple',
  template: '<div id="myholder"></div>',
  styleUrls: ['./simple.component.css']
})
export class SimpleComponent implements OnInit {

  graph = new joint.dia.Graph;

  paper;

  constructor() { }

  ngOnInit() {
    this.paper = new joint.dia.Paper({
      el: $('#myholder'),
      width: 600,
      heigth: 200,
      model: this.graph,
      gridSize: 1
    });

    const rect = new joint.shapes.basic.Rect({
      position: { x: 100, y: 30 },
      size: { width: 100, height: 30 },
      attrs: { rect: { fill: 'blue' }, text: { text: 'my box', fill: 'white' } }
    });

    const rect2 = rect.clone();
    rect2.translate(300);

    const link = new joint.dia.Link({
      source: { id: rect.id },
      target: { id: rect2.id }
    });

    this.graph.addCells([rect, rect2, link]);
  }
}

参考サイト

ここがなかったら無理でした。 github.com

QQmlApplicationEngine と QtQuick2ApplicationViewer(QQuickView) の違い

Qt を少しずつ勉強していってます。 Qt(主に Qt 4)は書籍があるので勉強しやすかったけれど、 Qt Quick(QML)は書籍もなく、がっつりまとまったサイトもないため、苦戦中。

とりあえず今日ハマってしまったところをまとめてみる。

似たような、ふたつのクラス

QML の入門的なサイトを見てみると、main 関数のあるソース(main.cpp)のなかで、QtQuick2ApplicationViewer なるクラスを使っているサイトをちらほら見かける。

ところが、私が使っている Qt Creator(バージョン 4.0.3)では、 「Qt Quick アプリケーション」を選択して新規プロジェクトを作ってみても、 QtQuick2ApplicationViewer は使用されない。 代わりに QQmlApplicationEngine クラスが使用される。

同じようなロジックのようなのだが、相違はどうなんだろうと調べてみた。
(こんなことを調べずに早く先に進みたかったのだが)

QtQuick2ApplicationViewer とは

うまく言及してくれているサイトがあった。

tips.hecomi.com

以下、ちょうどいい箇所を抜粋。

QtQuick2ApplicationViewer は Qt Creator で Qt Quick 2 プロジェクトを生成すると生成され、プラットフォーム間の違いを吸収してくれる処理が書いてある QQuickView 派生クラス(元を辿ると QWindow 派生クラス)です

API のクラス一覧にも存在しないので、なんだろうと思っていたら…そういうことかと。

その元となる QQuickView は Qt 5.0 から実装されたクラス。
http://doc.qt.io/qt-5/qquickview.html

QtQuick2ApplicationViewer とは

QQmlApplicationEngine は Qt 5.1 から実装されたクラス。
http://doc.qt.io/qt-5/qqmlapplicationengine.html

これらのクラスの違いは?

QQuickView の API ドキュメントを見ると…

The QQuickView class provides a window for displaying a Qt Quick user interface.

一方で、QtQuick2ApplicationViewer の API ドキュメントは…

Unlike QQuickView, QQmlApplicationEngine does not automatically create a root window. If you are using visual items from Qt Quick, you will need to place them inside of a Window.

QtQuick2ApplicationViewer の方には、QQuickView と対比した説明がある。 アプリケーションを表示するにあたって、ウィンドウが自動的に作られるか、そうでないかで違うとのこと。

QQuickView は自動的にウィンドウが作られる、むしろ自分自身がウィンドウなので、QML で "Window" を配置する必要がない。 一方で、QQmlApplicationEngine は自動的に作られないので、QML で "Window" を配置してあげる必要がある。

実際にソースコードを書いてみた

まず、QML を2つ用意する。

① Window なしの QML(nowrapWindow.qml)

import QtQuick 2.7

Rectangle {
    width: 640
    height: 480

    Text {
        text: qsTr("Hello World")
        anchors.centerIn: parent
    }
}

外側に Window を配置せず、矩形(Rectangle)だけ。 QQuickView で使う想定。

② Window ありの QML(wrapWindow.qml)

import QtQuick 2.7
import QtQuick.Window 2.2

Window {
    visible: true
    width: 640
    height: 480
    title: qsTr("Hello World")

    Text {
        text: qsTr("Hello World")
        anchors.centerIn: parent
    }
}

Window を外側に配置しておく。QQmlApplicationEngine で使う想定。

起動元となる C++ ソースコード(QQuickView)

#include <QGuiApplication>
#include <QtQuick/QQuickView>

int main(int argc, char **argv) {
    QGuiApplication app(argc, argv);

    QQuickView *view = new QQuickView;
    view->setSource(QUrl(QStringLiteral("qrc:/nowrapWindow.qml")));
    view->show();

    return app.exec();
}

「Window なしの QML」を読み込んでも、きちんとウィンドウが現れて内容が表示される。

逆に「Window ありの QML」を読み込むと、ウィンドウがふたつ現れてしまう。
ひとつは空っぽのウィンドウ(QQuickView に対応?)で、もうひとつは中身が表示されたウィンドウ(QML 内の Window に対応?)。

起動元となる C++ ソースコード(QQmlApplicationEngine)

#include <QGuiApplication>
#include <QQmlApplicationEngine>

int main(int argc, char *argv[])
{
    QGuiApplication app(argc, argv);

    QQmlApplicationEngine engine;
    engine.load(QUrl(QStringLiteral("qrc:/wrapWindow.qml")));

    return app.exec();
}

QQmlApplicationEngine はウィンドウを自動的に作らないので、 「Window ありの QML」を読み込むことで、ウィンドウが現れて QQuickView と同様の表示を行うことができる。

一方で、「Window なしの QML」を読み込んだ場合、ウィンドウが作られずに何も表示されない状態になってしまう(アプリケーション自体は動いているが)。

まとめ

「(正規の API ではない)QtQuick2ApplicationViewer ってなんだ?」でハマり、時間を食ってしまったので、まとめてみた。
まとめてみたけど、わかってしまえば大したことない話…

この二つのクラス、さらに使い分けがありそうな雰囲気がするけれど、とりあえず今のところは無視しよう。

Qt と QML はやっぱり情報源が少ないのがつらいね、初心者には。