寝ても覚めてもこんぴうた

プログラム書いたり、ネットワーク設計したり、サーバ構築したり、車いじったり、ゲームしたり。そんなひとにわたしはなりたい。 投げ銭は kyash_id : chidakiyo マデ

メールを送る(mailto)のリンクから起動したメーラーにSubjectやBodyを入れた状態にする

f:id:chidakiyo:20200204111707j:plain

Webコンテンツのリンクから、メーラーを起動したいケースもあると思いますが、
メーラーで起動した際に、 件名(Subject), 本文(Body)、CCとかも入っていると最高ですよね。
それ、できます!!

この記事は @yukinagae からの提供情報記事です。

HTML(mailto)の場合

aタグで以下のように定義します。

<a href="mailto:to_user@example.com?cc=cc_user@example.com&subject=【重要!】とても大事なメールタイトル&body=とても大事なメール本文">メールはこちらから</a>

このようなタグをhtmlとして用意すると、、
こんな感じの表示になります。

f:id:chidakiyo:20200204111001p:plain

これをクリックしてみるとメーラが立ち上がり、以下のように初期値が設定されます

f:id:chidakiyo:20200204111101p:plain

スクショは省略しますが、iPhoneでも同様にメーラーを起動することができました。

項目の説明

自明ではありますが一応書いておきます。

  • mailto: 直後は to(送信先)のアドレス
  • cc : ccですね
  • subject : 件名です
  • body : 本文

書いてて思ったけどこれは別に悩みませんねww

ではでは。

ElasticsearchをDocker(compose)で気軽に起動してみる

f:id:chidakiyo:20200127192442j:plain

Elasticsearchの振る舞いを気軽に確認したい場合、Docker(compose)で環境を作成すると非常に便利そうだったので、Macのローカルでdocker-composeで動くElasticsearchを構築してみた。

Dockerfile を作成する

ElasticsearchサーバのDockerのイメージを作成する。
日本語を利用する場合には kuromoji をインストールしたほうが良いのでインストールまで行う。

日本語を利用しない場合にはこのコンテナを作成するステップは不要かと思う。

FROM docker.elastic.co/elasticsearch/elasticsearch:7.5.1
RUN elasticsearch-plugin install analysis-kuromoji

docker-compose.yml を作成する

以下のようなファイルを docker-compose.yml というファイル名で作成する

version: '3'
services:
  es01:
    build: .
    container_name: es01
    environment:
      - node.name=es01
      - discovery.seed_hosts=es02
      - cluster.initial_master_nodes=es01,es02
      - cluster.name=docker-cluster
      - bootstrap.memory_lock=true
      - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
    ulimits:
      memlock:
        soft: -1
        hard: -1
    volumes:
      - esdata01:/usr/share/elasticsearch/data
    ports:
      - 9200:9200
    networks:
      - esnet
  es02:
    build: .
    container_name: es02
    environment:
      - node.name=es02
      - discovery.seed_hosts=es01
      - cluster.initial_master_nodes=es01,es02
      - cluster.name=docker-cluster
      - bootstrap.memory_lock=true
      - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
    ulimits:
      memlock:
        soft: -1
        hard: -1
    volumes:
      - esdata02:/usr/share/elasticsearch/data
    networks:
      - esnet

volumes:
  esdata01:
    driver: local
  esdata02:
    driver: local

networks:
  esnet:

container を build する

composeファイルがあると以下のコマンドでbuildできる

docker-compose build

Elasticsearch クラスタを起動する

以下のコマンドで起動

docker-compose up -d

done のような表示が出ればok。

curl経由で動作確認をする

Elasticsearch は 9200 番ポートで起動している。

動作確認のため、以下のようなコマンドを実行する

curl -X GET http://localhost:9200/

エラーにならず、jsonのレスポンスが返ってくれば成功。

かんたんですね

おまけ

docker-composeを停止する場合には

停止(消さない)

docker-compose down

停止(きれいに消す)

docker-compose down --rmi all

でok.

flutter-webやってみる

f:id:chidakiyo:20200106222849j:plain

ここ をベースに試してみます。

セットアップ

$ flutter channel beta
$ flutter upgrade
$ flutter config --enable-web

ここまで実行したらエディタを再起動しましょう

webが有効になると flutter devices コマンドの結果にChromeが出力されます(betaにしないと出てきません)

$ flutter devices
3 connected devices:

iPhone 11 Pro Max • 82AC52A8-9EC0-4495-B979-XXXXXXXXXXXX • ios            • com.apple.CoreSimulator.SimRuntime.iOS-13-3 (simulator)
Chrome            • chrome                               • web-javascript • Google Chrome 79.0.3945.88
Web Server        • web-server                           • web-javascript • Flutter Tools

また、IDEを再起動するとデバイスの一覧にChromeが表示されるようになります。

作成したアプリを実行する

すでに作成したアプリがある場合、以下のコマンドでwebとしてアプリを実行します。

flutter run -d chrome

おや、このままだと実行ができません。
webの設定がプロジェクトに反映されていないためなので以下のコマンドを実行し、既存のプロジェクトがwebとしても動作するようにします。

flutter create .

webディレクトリが作成されていればOKです。
再度以下のコマンドを実行しChromeで確認しましょう。

flutter run -d chrome

dartコードで開発をしつつ、ターミナルで r ボタンを押してChromeの画面を更新しつつ進めていくイメージだと思います。

WebのBuild

Webのbuildは以下のコマンドで実行します。

flutter build web

root/build/web 配下にhtmlやmain.dart.jsファイルなどが配置されます。

まとめ

Flutterで実装したアプリケーションがある場合、驚くほどかんたんにwebの画面を生成することができます。
デザイン的にもマテリアルデザインのものがきれいに出力されるので、手っ取り早くapp/webを両方作成したい人には良さそうです。

まだそれほど使い込んではいませんが、気づいたことなどはblogに残していこうと思います。

ではでは。

Goの標準機能で過去のバージョンをインストールする

f:id:chidakiyo:20191225141735j:plain

自分個人の環境はいつも最新のGoを利用しているので特にgoenvなどのようなバージョンを切り替えるツールを使っていないのですが、Goのバージョンを切り替えて使いたい人もいるみたい。

体感的にgoenvを利用している人が多そうではありますが、Go標準に複数のgoのバージョンをインストールする機能があるようなので、試してみます。

複数のgoバージョンでテストをしたいときなどに便利なのかもしれません。

内容としては ここ の公式ドキュメントの内容になります。

事前に必要なもの

  • goがインストールされていること

インストール方法

以下のようなコマンドで現在利用している以外のgoバージョンをインストールできます

$ go get golang.org/dl/go1.10.7

指定できるgoのバージョンは こちら に一覧があります。

別のGoバージョンの実行の仕方

インストールしたgoは以下のように実行ができます。

$ go1.10.7 version

別のバージョンの削除の仕方

$ go1.10.7 env

を実行し、GOROOTとして指定されているディレクトリと、実行した goX.Y.Z のバイナリを削除すればよいです。

ではでは!

GAE/goの東京リージョンからServerless VPC Access経由でMemorystoreへのアクセスをベンチマークしてみる

f:id:chidakiyo:20191211202210j:plain

つい先日、東京リージョンにも Serverless VPC Access がやってきたので、GAE/SEから Memorystore が利用できるようになりました。
Memorystoreは内部的にはRedisで、今までのGAE/SE 1st-genと呼ばれる環境ではmemcacheが提供されていましたが、2nd-genからはmemcacheが利用できないためMemorystoreを利用する必要がありました。

念願のMemorystoreが利用できるようになったのと、先日の こちらベンチマークを利用してGAE/GO環境からMemorystoreへのパフォーマンスを測定してみようと思います。

環境

  • GAE/go 1.13
  • インスタンスなどはデフォルトのまま
  • VPCは Min throughput: 200 Mbps、 Min throughput: 1000
  • リージョンはすべて東京

条件

  • redigo, go-redisの両方をテストしてみます。
  • redis の SET/GET の API 両方をベンチマークします

コード

以下のような感じです。

redigo

func put() testing.BenchmarkResult {

    conn := RedisPool.Get()
    defer conn.Close()

    result := testing.Benchmark(func(b *testing.B) {
        b.ResetTimer()
        for i := 1; i <= b.N; i++ {
            _, err := conn.Do("SET", key, value)
            if err != nil {
                b.Fatal(err)
            }
        }
    })
    return result
}

func get() testing.BenchmarkResult {
    conn := RedisPool.Get()
    defer conn.Close()

    result := testing.Benchmark(func(b *testing.B) {
        b.ResetTimer()
        for i := 1; i <= b.N; i++ {
            _, err := redis.String(conn.Do("GET", key))
            if err != nil {
                b.Fatal(err)
            }
        }
    })
    return result
}

go-Redis

func put() testing.BenchmarkResult {
    result := testing.Benchmark(func(b *testing.B) {
        b.ResetTimer()
        for i := 1; i <= b.N; i++ {
            cmd := RedisClient.Set(key, value, 0)
            if cmd != nil && cmd.Err() != nil {
                b.Fatal(cmd.Err())
            }
        }
    })
    return result
}

func get() testing.BenchmarkResult {
    result := testing.Benchmark(func(b *testing.B) {
        b.ResetTimer()
        for i := 1; i <= b.N; i++ {
            cmd := RedisClient.Get(key)
            if cmd != nil && cmd.Err() != nil {
                b.Fatal(cmd.Err())
            }
        }
    })
    return result
}

ベンチマークの実行

httpハンドラ経由で上記のベンチマークを実行し結果を取得します。

redigo

SET : 1383      1026929 ns/op
SET : 1086     1096002 ns/op
SET : 1076     1101032 ns/op

GET : 1237      974203 ns/op
GET : 1045     1134000 ns/op
GET : 952        1242443 ns/op

go-redis

SET : 986      1021182 ns/op
SET : 916        1185606 ns/op
SET : 1047     1066092 ns/op

GET : 1256      979747 ns/op
GET : 994        1145765 ns/op
GET : 1035     1082792 ns/op

事前にテストしたときは 800μsec だったのですが、
今日の計測ではSET/GETともに 1msec 程度のようでした。

まとめ

仕事ではAerospikeなど高速なKVSを使っているので、Redisで1msecだとちょっと遅いな、と感じちゃうところもありますが、GCPにでおまかせで使えると思えばすごく便利かも。

Memcacheが使えなくなってすごく困っていましたが、これならパフォーマンス気にせずガンガン使っていけそうです!

おわり。

Kindle fire 10 (19年モデル) を買ったのでGoogle Playをインストールする

f:id:chidakiyo:20191209164116j:plain

サイバーマンデーで10月頃に発売されたKIndle Fire HD 10が1万以下で出ていました。
子供がAmazon PrimeVideoを見ているとテレビを専有されるので子供のプライムビデオ用に買ってみました。

Kindle Fireを購入したのはこれで3台目で 7,8,10 インチとすべて揃った感じですが、
いままで買った全て Google Play をインストールするので惰性で今回もやってみます。

いろいろつないだりすると面倒なのでSilkブラウザだけで完結する方法を試してみます。

ちなみに以下の手順を試すのは 自己責任 です。ご注意ください

APKのインストールを許可する

「設定」 → 「セキュリティとプライバシー」 → 「不明アプリのインストール」

を選択します。

「Silkブラウザ」を選択し、このソースを許可のチェックを有効化します。

APKのダウンロード

以下のファイルを 順番に ダウンロードしてインストールします。

すべてダウンロードとインストールが完了するとGoogle Play Storeが利用できるようになります。

APKのインストールの許可を取り消す

先程Silkブラウザに不明アプリのインストールを許可したので、許可を取り消しておきましょう。
手順は先程と同じです。

これで、kindle firechromeなどがインストールできるようになりましたね。
以上です。

Goで特定の関数を持つか/実装されているかチェックする方法

f:id:chidakiyo:20191209094946j:plain

いまさらながら、なるほどーと思ったのでメモ。

go1.13のerrorsのUnwrap関数が以下のように実装されている

// Unwrap returns the result of calling the Unwrap method on err, if err's
// type contains an Unwrap method returning error.
// Otherwise, Unwrap returns nil.
func Unwrap(err error) error {
    u, ok := err.(interface {
        Unwrap() error
    })
    if !ok {
        return nil
    }
    return u.Unwrap()
}

error型を型アサーションして、okじゃなかったらUnwrapが実装されていないのでnilを返却する。
okだったらerrorのUnwrap関数を実行して返却する。

なるほどね。