1
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Cloud FunctionsをGo言語で動かす

Last updated at Posted at 2021-11-28

概要

firebase で Cloud Functions を使ったときに、JavaScript と golang で作った function とで性能に差がでるかを簡単に確認したのでそのメモ

firebase の Cloud Functions で Go言語使える?

らしい。のでやってみました。
やり方としては、以下参考の「go言語でCloud Functions + FireStoreをhttpで使ってみた」を参照しつつ、ざっくりとは次の手順で環境を構築しました

  • firebase のコンソールでプロジェクトを作成
  • firebase に javascript 版の Cloud Functions の関数をデプロイする
  • golang で Cloud Functions の関数を作成し、GCP にデプロイする
    • firebase のプロジェクトを使いまわして、GCP の Cloud Functions を作成するのがミソ

参考

測定結果

メモリは、なんとなく128MBに変更しました。測定は、vegeta を利用しました。
結果は、レイテンシが JavaScript 版が平均 5.138s に対して、Go 言語版が 161.252ms と 32倍の差がでました
あと、GCP のコンソールから目分量ですが、メモリとアクティブインスタンス数に大きな違いがでました

- JS Go 比較
メモリ 100MB 15MB 6.6倍
アクティブインスタンス 50 3 16倍

参考

JavaScript 版

10rps を 1秒間
gosshys@gosshysnoMacBook-Air perf % vegeta attack --rate 10 -duration=1s -targets=target.txt | vegeta report
Requests      [total, rate, throughput]         10, 11.10, 1.17
Duration      [total, attack, wait]             8.57s, 900.524ms, 7.669s
Latencies     [min, mean, 50, 90, 95, 99, max]  3.124s, 4.734s, 3.826s, 7.376s, 7.669s, 7.669s, 7.669s
Bytes In      [total, mean]                     5920, 592.00
Bytes Out     [total, mean]                     400, 40.00
Success       [ratio]                           100.00%
Status Codes  [code:count]                      200:10  
Error Set:

10rps を 10秒間
gosshys@gosshysnoMacBook-Air perf % vegeta attack --rate 10 -duration=10s -targets=target.txt | vegeta report
Requests      [total, rate, throughput]         100, 10.10, 6.08
Duration      [total, attack, wait]             16.45s, 9.901s, 6.549s
Latencies     [min, mean, 50, 90, 95, 99, max]  2.439s, 5.138s, 4.602s, 8.011s, 8.437s, 9.379s, 9.571s
Bytes In      [total, mean]                     59200, 592.00
Bytes Out     [total, mean]                     4000, 40.00
Success       [ratio]                           100.00%
Status Codes  [code:count]                      200:100  
Error Set:

image.png

golang 版

10rps を 1秒間
gosshys@gosshysnoMacBook-Air perf % vegeta attack --rate 10 -duration=1s -targets=target_go.txt | vegeta report
Requests      [total, rate, throughput]         10, 11.18, 9.01
Duration      [total, attack, wait]             1.11s, 894.571ms, 215.591ms
Latencies     [min, mean, 50, 90, 95, 99, max]  135.716ms, 259.473ms, 178.427ms, 563.063ms, 691.467ms, 691.468ms, 691.468ms
Bytes In      [total, mean]                     610, 61.00
Bytes Out     [total, mean]                     0, 0.00
Success       [ratio]                           100.00%
Status Codes  [code:count]                      200:10  
Error Set:
10rps を 10秒間
gosshys@gosshysnoMacBook-Air perf % vegeta attack --rate 10 -duration=10s -targets=target_go.txt | vegeta report
Requests      [total, rate, throughput]         100, 10.10, 9.96
Duration      [total, attack, wait]             10.044s, 9.9s, 143.807ms
Latencies     [min, mean, 50, 90, 95, 99, max]  142.071ms, 161.252ms, 148.575ms, 163.501ms, 184.75ms, 623.21ms, 942.147ms
Bytes In      [total, mean]                     6100, 61.00
Bytes Out     [total, mean]                     0, 0.00
Success       [ratio]                           100.00%
Status Codes  [code:count]                      200:100  
Error Set:

image.png

付録

測定に利用した functions

リクエストで指定された url にアクセスして、OGP(https://ogp.me/)を metaタグから抜き出して返却する関数

JavaScript

JS の書き方が悪いせいで遅くなっていたらすみません

const functions = require("firebase-functions");
const axios = require('axios');
const jsdom = require('jsdom');
const { JSDOM } = jsdom;

exports.getOgpFromExternalWebsite = functions.https.onCall(async (data, context) => {

  if (!data.url) {
    console.log("not exists url");
    return {};
  }

  const headers = {'User-Agent': 'bot'};
  const res = await axios.get(data.url, {headers: headers});
  const html = res.data;
  const dom = new JSDOM(html);
  const meta = dom.window.document.head.querySelectorAll("meta");
  const ogp = {};
  let i = 0, len = meta.length;
  for (; i < len; i++) {
    if (!meta[i].hasAttribute("property")) {
      continue;
    }
    const property = meta[i].getAttribute("property");
    const content = meta[i].getAttribute("content");
    ogp[property] = content;
  }

  if (Object.keys(ogp).length===0) {
    return {};
  }

  return ogp;

})

golang

package ogp

import (
  "fmt"
  "golang.org/x/net/html"
  "golang.org/x/net/html/atom"
  "log"
  "net/http"
)

type OGType int

const (
	Type OGType = iota
	Title
	Description
	URL
	Image
)

type OG struct {
	Type,
	Title,
	Description,
	URL, Image string
}

func findMeta(node *html.Node, og OG) OG {
	for c := node.FirstChild; c != nil; c = c.NextSibling {
		if c.Type == html.ElementNode {
			if c.DataAtom == atom.Meta {
				var val, t string
				for _, attr := range c.Attr {
					if attr.Key == "property" {
						t = attr.Val
						continue
					}
					if attr.Key == "content" {
						val = attr.Val
					}
				}
				switch t {
				case "og:type":
					og.Type = val
				case "og:title":
					og.Title = val
				case "og:description":
					og.Description = val
				case "og:url":
					og.URL = val
				case "og:image":
					og.Image = val
				}
			}
			if og.Type != "" && og.Title != "" && og.Description != "" && og.URL != "" && og.Image != "" {
				break
			}
			og = findMeta(c, og)
		}
	}
	return og
}

func OGPGet(w http.ResponseWriter, r *http.Request) {
	url := r.URL.Query().Get("url")
	if url == "" {
		w.Write([]byte("welcome"))
		return
	}
	resp, err := http.Get(url)
	if err != nil {
		fmt.Fprintf(w, "err: %+v", err)
		return
	}

	defer resp.Body.Close()
	node, err := html.Parse(resp.Body)
	if err != nil {
		log.Fatal(err)
	}

	og := OG{}
	og = findMeta(node, og)
	fmt.Fprintf(w, "url:%s, og:%+v", url, og)
}

GCP にデプロイするコマンド

gcloud functions deploy OGPGet --runtime go116 --trigger-http --project <firebase のプロジェクトID>

測定に利用したリクエスト (vegeta の targetsファイル)

JavaScript

※ 東京リージョンに作成したかったが、なぜか us-central1 に作成されたようです

target.txt
POST https://us-central1-<firebase のプロジェクトID>.cloudfunctions.net/getOgpFromExternalWebsite
Content-Type: application/json
@req.json
req.json
{"data":{"url": "https://yahoo.co.jp"}}

golang

target_go.txt
GET https://us-central1-<firebase のプロジェクトID>.cloudfunctions.net/OGPGet?url=https%3A%3A%2F%2Fyahoo.co.jp
1
4
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?