CloudflareWorkersとは

エイチームフィナジーのアドベントカレンダー3日目は、[sakupa80](https://qiita.com/sakupa80) が担当します。

# はじめに
きっかけとしては、ただコンテンツをキャッシュさせる為のCDNサービスとしてCloudflareを使い始めましたが、Cloudflareの利便性に感銘を受け、今ではその他の様々なCloudflareサービスを利用しております。
今回はその中でも`Cloudflare Workers`についてまとめてみました。

公式ドキュメント: https://developers.cloudflare.com/workers/

# Cloudflare Workersとは
エッジサーバーでスクリプトを実行してくれるサーバーレスのサービスです。
JavaScript(V8)を実行することが可能です。
ServiceWorkerが動くので、FetchAPI、CacheAPIなどを使うことが出来ます。
Cloudflare Workersの名前もService Workerから取ったそうな。

Service Worker API: https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API

では、エッジサーバーで動くJavaScript、ServiceWorkerAPIを使ってどのようなことが出来るのでしょうか。

# Cloudflare Workersで出来ること

エッジサーバーにてコードを実行するので、オリジンサーバーに到達する前に実行したい独自処理を得意とします。
例として下記のようなことが可能です。

* キャッシュコンテンツに対する独自コントロール
* User-Agentごとにリダイレクト
* 全く異なる2つのバックエンド間でA/Bテストを実現
* 独自のアクセスフィルタリング
* 独自のフェイルオーバーの実装
* ログの収集

などなど。活用の仕方次第で色々出来ることでしょう。
Cloudflare Workersの活用方法については、公式サイトにてexamplesがあるので、いくつか紹介しようと思います。
https://developers.cloudflare.com/workers/examples

## Redirect
>  https://developers.cloudflare.com/workers/examples/redirect
 
> ```js
> const destinationURL = "https://example.com"
> const statusCode = 301
> 
> async function handleRequest(request) {
>   return Response.redirect(destinationURL, statusCode)
> }
> 
> addEventListener("fetch", async event => {
>   event.respondWith(handleRequest(event.request))
> })
> ```

こちらはシンプルにリクエストを受け取って、301リダイレクトを返す例となります。
event.requestでリクエストデータを受け取ることが可能です。
addEventListenerでfetchイベントにフックして処理を行います。

## Debugging logs
> https://developers.cloudflare.com/workers/examples/debugging-logs
> 
> ```js
> // Service configured to receive logs
> const LOG_URL = "https://log-service.example.com/"
> 
> function postLog(data) {
>   return fetch(LOG_URL, {
>     method: "POST",
>     body: data,
>   })
> }
> 
> async function handleRequest(event) {
>   let response
> 
>   try {
>     response = await fetch(event.request)
>     if (!response.ok) {
>       const body = await response.text()
>       throw new Error(
>         "Bad response at origin. Status: " +
>           response.status +
>           " Body: " +
>           //Ensure the string is small enough to be a header
>           body.trim().substring(0, 10),
>       )
>     }
>   } catch (err) {
>     // Without event.waitUntil(), our fetch() to our logging service may
>     // or may not complete.
>     event.waitUntil(postLog(err.toString()))
>     const stack = JSON.stringify(err.stack) || err
> 
>     // Copy the response and initialize body to the stack trace
>     response = new Response(stack, response)
> 
>     // Shove our rewritten URL into a header to find out what it was.
>     response.headers.set("X-Debug-stack", stack)
>     response.headers.set("X-Debug-err", err)
>   }
>   return response
> }
> 
> addEventListener("fetch", event => {
>   //Have any uncaught errors thrown go directly to origin
>   event.passThroughOnException()
>   event.respondWith(handleRequest(event))
> })
> ```

こちらはサードパーティのロギングサービスにエラーログを送る例です。
Cloudflareはログの収集方法としてCloudflare Logsというサービスがありますが、こちらは現在エンタープライズプランでないと使用することが出来ません。
Cloudflare Logs: https://www.cloudflare.com/products/cloudflare-logs/

ですが、例のようにCloudflare Workersを利用して自前でロガーを実装すれば、その代替とすることが可能となります。

## Return HTML
> https://developers.cloudflare.com/workers/examples/return-html

> ```js
> const html = `<!DOCTYPE html>
> <body>
>   <h1>Hello World</h1>
>   <p>This markup was generated by a Cloudflare Worker.</p>
> </body>`
> 
> async function handleRequest(request) {
>   return new Response(html, {
>     headers: {
>       "content-type": "text/html;charset=UTF-8",
>     },
>   })
> }
> 
> addEventListener("fetch", event => {
>   return event.respondWith(handleRequest(event.request))
> })
> ```

ただの静的なHTMLを返すだけならCloudflare Workersだけで完結可能です。
例えば、ドメイン全てに対して一時的に適用したいページなどがあれば、この要領で簡単に実装できますね。

ちなみにNext.jsをモデルに作られた「[Flareact](https://github.com/flareact/flareact)」というOSSも最近公開されました。
これで上記の例のようなシンプルな静的HTMLだけでなく、Reactアプリをエッジサーバーにてレンダリング可能です。
Rendering React on the Edge with Flareact and Cloudflare Workers:
 https://blog.cloudflare.com/rendering-react-on-the-edge-with-flareact-and-cloudflare-workers/

エッジサーバーからReactアプリを配信、さらにCloudflareキャッシュと組み合わせれば、高いページパフォーマンスが出せるサイトをサーバーレスに作ることが出来るでしょう。

このようにユーザーからのリクエストをプロキシする形で様々な独自処理を行うことが可能です。
ログイン無しで使用することが出来るプレイグラウンドも用意されています。
https://cloudflareworkers.com/

# Cloudflare Workersの強み
> ![スクリーンショット 2020-12-01 16.20.05.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/205207/e4d36cf2-8f57-168b-575e-9c3a7cd153a3.jpeg)
> https://workers.cloudflare.com/

Cloudflare Workersを採用する理由として、高いパフォーマンスと拡張性があげられます。
それらの`強み`を少し説明していきます。

## 高速デプロイ
[wrangler](https://github.com/cloudflare/wrangler)というRust製のCLIを使って簡単にデプロイが可能です。
さらに公式ブログでは全てのエッジサーバーに対して`30秒以内`にデプロイが完了すると書かれています。
プレビューモードでの開発時も、高速デプロイのおかげで動作確認の際のストレスもありません。

## 高速実行
エッジでのスクリプト実行となると気になるのは応答速度だと思いますが、Cloudflare Workersでは`0msコールドスタート`が可能で、サーバレスサービスによく見られるコールドスタート時の実行が遅い問題が発生しません。

これはCDNの強みにもなりますが、エッジサーバーで実行されるということはユーザーから近いサーバーにて実行されるので、Flareactなどを使ってエッジサーバーにてReactアプリの実行を可能とする設計(エッジサーバーにフロントを寄せる設計)にしておけば、高いサイトパフォーマンスを発揮することが出来ます。

# 終わりに
紹介したCloudflare Workers以外にも、WAF、ネットワーク最適化、RateLimit機能などの手軽に設定できて、サイトをより良くしてくれるサービスがCloudflareにはあります。
自分がエッジコンピューティングの素晴らしさを知ることが出来たのは、Cloudflareのおかげでした。
ただ、最近でも新たなCloudflareのサービスが出てきている一方、まだまだネット上での日本語の情報が少なく、追いついていない印象を受けるので、日本でもっと利用者が増えてくれることを願っております。

# 参考
https://workers.cloudflare.com/
https://blog.cloudflare.com/introducing-cloudflare-workers/
https://blog.cloudflare.com/cloudflare-workers-unleashed/