yoshikipom Tech Blog

開発ツール for Kafka (docker-compose, kcat)

Overview

  • docker-composeでの起動
  • CLI tool (kcat)

Environment

  • MacOS
  • docker, docker-compose

docker-composeでの起動

zookeeperも起動する必要があるようなのでdocker-composeで起動。

---
version: '3'
services:
  zookeeper:
    image: confluentinc/cp-zookeeper:7.0.1
    container_name: zookeeper
    environment:
      ZOOKEEPER_CLIENT_PORT: 2181
      ZOOKEEPER_TICK_TIME: 2000
  broker:
    image: confluentinc/cp-kafka:7.0.1
    container_name: broker
    ports:
      - "9092:9092"
    depends_on:
      - zookeeper
    environment:
      KAFKA_BROKER_ID: 1
      KAFKA_ZOOKEEPER_CONNECT: 'zookeeper:2181'
      KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,PLAINTEXT_INTERNAL:PLAINTEXT
      KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://localhost:9092,PLAINTEXT_INTERNAL://broker:29092
      KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
      KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: 1
      KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: 1

run

docker-compose up

CLI tool (kcat)

インストール

brew install kcat

コマンド

produce (interactive)

$ kcat -b localhost:9092 -t new_topic -P
test
# end by ctrl-D

produce (file)

$ kcat -b localhost:9092 -t new_topic -P -l test.json

consume

# topic list
$ kcat -b localhost:9092 -t new_topic -C
% Reached end of topic new_topic [0] at offset 0
test
test2
% Reached end of topic new_topic [0] at offset 2
test3
% Reached end of topic new_topic [0] at offset 3

topic list

# topic list
$ kcat -b localhost:9092 -L
Metadata for all topics (from broker 1: localhost:9092/1):
 1 brokers:
  broker 1 at localhost:9092 (controller)
 1 topics:
  topic "new_topic" with 1 partitions:
    partition 0, leader 1, replicas: 1, isrs: 1

参考

スーパーでの「来週なにつくる?」に答えを出すツールをReactで作った

なぜ作ったか

週1回の買い物時にできるだけ必要なものは買い揃えたい。 そのために何を作るか決める必要があるが、大体スーパーについてから考えてしまうのである程度自動化したい。

メイン機能

準備したリストの中からランダムな重複なしリストを取得するアプリケーション。 ここにデプロイしてあります。 https://yoshikipom.github.io/dinnerslot-web/

input
result

工夫点

  • 1アイテムだけチェンジする機能 -> 最近作った、材料がないなどでチェンジするための機能。全部チェンジするとなかなか理想の結果が得られないことがある。
  • 結果をLINEに出力するボタン(SHARE BY LINE ) -> 結果の永続化、シェア
  • 入力したリストはブラウザに保存 -> 入力時間削減

LINE share

利用技術

ソースコードと主要部分の実装

リポジトリ: https://github.com/yoshikipom/dinnerslot-web

ページは index.tsx のみ。 ページ内でListInput.tsx(リスト入力) と Slot.tsx(結果生成) を呼び出し、タブで出し分ける。

$ tree src 
src
├── components
│   ├── Footer.tsx
│   ├── LineShareButton.tsx
│   ├── ListInput.tsx
│   └── Slot.tsx
├── model
│   └── Food.ts
└── pages
    ├── _app.tsx
    └── index.tsx

結果生成 - 入力されたリストをコピー - ランダムに並び替え - 欲しい数だけ前から切り取る

    const slot = () => {
        const copiedFoodList = foodList.concat();
        for (let i = copiedFoodList.length - 1; i > 0; i--) {
            const r = Math.floor(Math.random() * (i + 1));
            const tmp = copiedFoodList[i];
            copiedFoodList[i] = copiedFoodList[r];
            copiedFoodList[r] = tmp;
        }
        updateResult(copiedFoodList.slice(0, Number(count)));
    }

一点チェンジ - 入力されたリストをコピー - 選ばれているアイテムを除去 - 候補がなければ終了 - ランダムで一点選択 - 上書き

    const slotOneItem = (index: number) => {
        const copiedFoodList: Food[] = foodList.concat();
        const remainingList: Food[] = copiedFoodList.filter((food) => !results.includes(food))
    
        if (remainingList.length == 0) {
            return;
        }

        const r = Math.floor(Math.random() * (remainingList.length));
        results[index] = remainingList[r];
        updateResult(results.slice(0, Number(count)));
    }

LINE Shareボタン - 「LINE URLスキーム」を利用 - ref https://developers.line.biz/ja/docs/line-login/using-line-url-scheme/ - 値はpropsで渡す

const LineShareButton = (props: any) => {
    return (
        <Button sx={{ mx: 2, my: 2 }} variant="outlined" href={`https://line.me/R/share?text=${encodeURI(props.message)}`}>
            Share by LINE
        </Button>
    );
};

入力リスト保存 - localStorage利用 - 取得は初回呼び出しのみ - 書き込みは入力(foodListInputRaw)が変更が変更するたび

    useEffect(() => {
        const foodListInputRaw = window.localStorage.getItem(STORAGE_KEY);
        if (foodListInputRaw !== null) {
            setFoodListInputRaw(foodListInputRaw);
        }
    }, []);

    useEffect(() => {
        const strList = foodListInputRaw.split('\n').filter(val => val);
        let index = 1;
        const newFoodList: Food[] = strList.map(name => ({ id: index++, name }));
        setFoodList(newFoodList);
        window.localStorage.setItem(STORAGE_KEY, foodListInputRaw);
    }, [foodListInputRaw, setFoodList]);

感想

localStorage、LINEシェアは初めて試したが簡単に使えてよかった。 技術の復習としてバックエンドも作りたかったが、要件的に不要になってしまい残念。 自分のほしいアプリケーションを作ると勉強したい技術が使えるとは

Webバックエンドチームのドキュメント構造

Overview

長期的な開発、スケーラブルなチーム構築のためにドキュメンテーションは必須。 整理されていないと各メンバーがドキュメントを見つけられない、どこに作ればいいかわからないといった問題が発生するため、ドキュメント構造のポリシーが必要である。 ここでは現時点の自分が必要だと思うドキュメント構造をまとめる。

前提

想定 - 開発・運用を両方行うWebバックエンドチーム - 継続的に新規開発を行なっている

書かないこと - ドキュメンテーションのツール - ドキュメントを定期的に更新する方法・工夫

外部向け vs 内部向け

更新の優先度付のため、これらは区別しておいたほうがよい。 当然外部向けのドキュメントは最新の情報が要求される。

外部向け

インターフェース/仕様

クライアントが自分たちのプロダクトをどう使えばよいかの情報を提供。 swaggerなど別のツールを利用しているならそれのリンクで十分。

SLA/SLO

稼働率、レイテンシ、スループット等。

利用手続き

アクセスキーの取得方法、その他申請方法など。

コンタクト

連絡先。

内部向け

開発プロセス

開発を進めるために誰がどのプロセスをどう進めるかを明らかにする場所。

例 - メンバーのロール・役割定義 - 開発の進め方の定義 - ミーティング

プロジェクト

新規開発や仕様変更、システム改善などのドキュメントをまとめるところ。 終わったらメンテナンスしない前提。

例 - バックグラウンド - プロジェクトを達成するための設計 - スケジューリング

システム変更した部分に関して、プロジェクトが完了したら次節のシステムのドキュメントに反映する。 プロジェクト段階では現状との差分だけ設計することが多々ある。

システム

稼働しているシステムの詳細情報。

例 - システム全体の設計 - データ設計 - 各コンポーネントの設計(API, DBなど) - 環境情報(APIのURL、DBのホスト名等)

コーディング

開発者間で揃えた方がよいルール。セットアップやAPI毎の詳細な設計などはGithubに書いた方が管理しやすいかも。

例 - ブランチ戦略 - レビュールール - コーディングルール - クラス命名規則 - エラーハンドリングの方針

テスト

どんな種類のテストを誰(CI/担当者)がどのタイミング(PR作成時/マージ後/リリース前/リリース後)で実施しているか明確にする。 開発者内で認識が揃っていないとテストピラミッドを構築するのが難しくなる。 事故の再発防止を考えるときにどこのテストを補強すべきかといった判断に使える。

例 - ユニットテスト - コンポーネントE2E - システムテスト

リリース

リリース作業方法の共有、リリース履歴の検索などに使う。

例 - リリース方法(ロールバック方法含む) - リリース履歴

オペレーション

こういうアラートが出たらこういうアクションをするというのがある程度定まっていると属人化を防ぎやすい。

例 - モニタリングツールの説明 - モニタリング項目 - アラート検知時の対応手順

メモ

なんでも置けるスペース。どこにおけばいいかわからないドキュメントや提案段階のドキュメントを気軽に置けると良い。