シェルスクリプトマガジン

香川大学SLPからお届け!(Vol.72掲載)

著者:重松 亜夢

今回は、「gRPC」という通信プロトコルを使った、Webアプリケーションの作成方法を紹介します。gRPCを使うことで、通信量が減らせます。最近注目のマイクロサービスの連携にも活用できます。

シェルスクリプトマガジン Vol.72は以下のリンク先でご購入できます。

図3 「pb/picture.proto」ファイルに記述する内容

syntax = "proto3";
option go_package = "example.com/user_name/sample/pb/go/picture";
package picture;
service Picture {
  rpc GetPictures (GetPicturesRequest) returns (GetPicturesReply) {}
}
message GetPicturesRequest {
  uint32 num = 1;
}
message GetPicturesReply {
  repeated bytes pictures = 1;
}

図4 「pb/protoc-web/Dockerfile」ファイルに記述する内容

FROM node:15-buster
WORKDIR /pb
RUN npm i rimraf -g
RUN curl -L -O https://github.com/protocolbuffers/protobuf/releases/download/v3.15.8/protoc-3.15.8-linux-x86_64.zip
RUN curl -L -O https://github.com/grpc/grpc-web/releases/download/1.2.1/protoc-gen-grpc-web-1.2.1-linux-x86_64
RUN unzip protoc-3.15.8-linux-x86_64.zip && cp ./bin/protoc /usr/local/bin/. && chmod +x /usr/local/bin/protoc
RUN cp protoc-gen-grpc-web-1.2.1-linux-x86_64 /usr/local/bin/protoc-gen-grpc-web && chmod +x /usr/local/bin/protoc-gen-grpc-web

図5 「pb/scripts/picture-compile.sh」ファイルに追記する内容

docker build protoc-web -t streaming-protoc-web
mkdir -p js/picture
docker run -v "$(pwd):/pb" -w /pb --rm streaming-protoc-web \
  protoc --proto_path=. picture.proto \
    --js_out=import_style=commonjs:js/picture \
    --grpc-web_out=import_style=typescript,mode=grpcwebtext:js/picture
mkdir -p ../services/client/src/pb
cp -r ./js/* ../services/client/src/pb/

図6 「Makefil e」ファイルの変更内容

proto: pb/js/picture/picture_pb.js
	# make .proto
pb/js/picture/picture_pb.js: pb/picture.proto
	bash ./pb/scripts/picture-compile.sh

図8 「docker-compose.yaml」ファイルに追加する内容

  proxy:
    container_name: sample-proxy-container
    image: envoyproxy/envoy-dev:1f642ab20b8975654482411537a6bdc5e2f6c4f6
    ports:
      - "8080:8080"
    volumes:
      - ./services/proxy/envoy.yaml:/etc/envoy/envoy.yaml

図9 「services/client/src/components/Picture.tsx」ファイルの内容

import { useState } from 'react';
import { GetPicturesRequest, GetPicturesReply } from "../pb/picture/picture_pb";
import { PictureClient } from "../pb/picture/PictureServiceClientPb";
import { Error } from 'grpc-web';
export const Picture = () => {
  const [num, setNumber] = useState(1); // 枚数の指定
  const [pictures, setPictures] = useState<JSX.Element[]>([]);
  const jspb = require('google-protobuf');
  const client = new PictureClient(http://${window.location.hostname}:8080/server, {}, {});
  const getPictures = () => {
    if (num <= 0) return;
    const request = new GetPicturesRequest();
    request.setNum(num);
    client.getPictures(request, {}, (err: Error, response: GetPicturesReply) => {
      if (err || response === null) { throw err; }
      setPictures(jspb.Message.bytesListAsB64(response.getPicturesList())
                  .map((images: string, index: number) => (
        <img key={${index}} width="200px"
             src={data:image/jpg;base64,${window.atob(images)}}
             alt="pictures" />
      )));
    });
  }
  const onChange = (event: React.ChangeEvent<HTMLInputElement>) => {
    const n = event.target.valueAsNumber;
    if (!isNaN(n)) { setNumber(n); }
  };
  return (
    <div>
      <input type="number" min="1" defaultValue="1" onChange={onChange} />
      <button onClick={getPictures}>GetPictures</button>
      <div className="getPictures">{pictures}</div>
    </div>
  );
}

図10 「services/client/src/App.tsx」ファイルに記述する内容

import {Picture} from './components/Picture'
import './App.css';
function App() {
  return (
    <div className="App">
      <header className="App-header">
        <Picture/>
      </header>
    </div>
  );
}
export default App;