When
2016/12/02
At
今回のGitHubリポジトリ
ovrmrw/qiita-advent-calendar-2016-microservices-1
現在更新中
===
話す内容
- Azure Functionsの基本。
- Azure Functionsでサーバーサイドアプリを動かす。
自己紹介
ちきさん
(Tomohiro Noguchi)
Twitter: @ovrmrw
ただのSIer。
Angular Japan User Group (ng-japan)スタッフ。
アカウントIDの由来
- the day after tomorrow
- overmorrow(俗語)
- 略して
- ovrmrw
- 「先を見据えて」よりさらに先を、みたいな感じです。
(よく聞かれるので)
(ここから本編)
Azure Functionsの基本から
- AWS Lambda + API Gatewayみたいなもの。
- (Herokuみたいな)Web Appsと同じ仕組みのVMを起動するのでAWS Lambdaより遅い。(多分)
- ストレージIOがとても遅いのでモジュールを多数importする場合はWebpackでバンドルすべき。
- Node.js以外にもC#とかF#とかでも書けるらしい。
Rule1. サブフォルダの名前がエンドポイントになる。
root
|--function1
| |--function.json
| |--main.ts
|--function2
|--function.json
|--main.ts
生成されるエンドポイント。
https://my-azure-functions.azurewebsites.net/api/function1
https://my-azure-functions.azurewebsites.net/api/function2
Rule2. ファンクションフォルダにはfunction.jsonが必須。
HTTPエンドポイントの場合はこのように書く。
function.json
が存在しないとファンクションと見なされない。
{
"bindings": [
{
"authLevel": "anonymous",
"type": "httpTrigger",
"direction": "in",
"name": "req"
},
{
"type": "http",
"direction": "out",
"name": "res"
}
],
"disabled": false
}
Rule3. contextを引数に取る関数をexportする。
最高にシンプルなファンクションはこんな感じ。
export function azureFunction(context, req) {
context.res = {
status: 200,
body: {
message: 'Hello world.',
}
};
context.done();
}
Rules
- exportする関数は最低限
context
を引数に取ること。 -
context.res
にstatus
とbody
をセットして返すこと。 - 最後に
context.done()
に呼ぶこと。(あるいはPromiseを返すこと)
export async function azureFunction(context, req) {
context.res = {
status: 200,
body: {
message: 'Hello world.',
}
};
// context.done(); <-- Promiseを返す場合はdone()は呼ばなくていい。
}
Rule3-2. 複数ファイルで構成される場合はindex.tsを用意しておくとトラブルがない。
例えばこのような状況ではindex.ts
を作っておかないとNot Found沼に嵌ることがある。
root
|--function1
|--function.json
|--index.ts
|--main.ts
|--server.ts
index.ts
はファンクションをimportしてexport default
でexportするだけで良い。
import { azureFunction } from './main';
export default azureFunction;
(ここまでが基本編)
Azure Functionsのエンドポイントでサーバーサイドアプリ(Express, Hapi等)を動かすことができる。
Server-side app on Azure Functions
root
|--hapi-simple
|--function.json
|--index.ts
|--main.ts
|--server.ts
(example)
-
https://my-azure-functions.azurewebsites.net/api/hapi-simple
{ message: 'Hello world'}
-
https://my-azure-functions.azurewebsites.net/api/hapi-simple/Gotanda.js
{ message: 'Hello world, Gotanda.js'}
function.jsonにちょっと細工
"route":"hapi-simple/{*segments}"
を書くことでルーティングが可能になる。
{
"bindings": [
{
"authLevel": "anonymous",
"type": "httpTrigger",
"direction": "in",
"name": "req",
"route":"hapi-simple/{*segments}"
},
{
"type": "http",
"direction": "out",
"name": "res"
}
],
"disabled": false
}
server.ts
サーバーサイド(Hapi)のコードをそのまま持ち込んで、URIを生成する関数をPromiseでexportする。
import * as Hapi from 'hapi';
const server = new Hapi.Server();
server.connection({
host: 'localhost'
});
server.route([
{
method: 'GET',
path: '/',
handler: (req, reply) => {
const message = 'Hello world.';
reply({ message });
}
},
{
method: 'GET',
path: '/{name}',
handler: (req, reply) => {
const message = 'Hello world, ' + req.params['name'];
reply({ message });
}
}
]);
let uri: string | undefined;
export function createUri(): Promise<string> {
return new Promise<string>((resolve, reject) => {
if (uri) {
resolve(uri);
} else {
server.start((err) => {
if (err) { reject(err); }
uri = server.info.uri;
console.log('Server running at:', uri);
resolve(uri);
});
}
});
}
main.ts
Hapiサーバー起動後のURIを取得して、ファンクションに入ってきたrequestオブジェクトをリレーしてHapiサーバーにfetchする。
import { customFetch } from '../lib/utils';
import { createUri } from './server';
export async function azureFunction(context, req) {
try {
const uri: string = await createUri();
const res = await customFetch(uri, req);
if (res.status === 200) {
context.res = {
status: res.status,
body: res.myBody,
headers: res.myHeaders,
};
} else { // Not Found
context.res = {
status: res.status,
body: res.statusText,
}
}
} catch (err) {
context.res = {
status: 500,
body: err,
};
}
}
index.ts
main.ts
で定義したファンクションをexport default
でexportしているだけ。
import { azureFunction } from './main';
export default azureFunction;
1ドメインでこんなことも可能です。
root
|--express1
| |--function.json
| |--index.ts
| |--main.ts
| |--server.ts
|--express2
| |--function.json
| |--index.ts
| |--main.ts
| |--server.ts
|--express3
|--function.json
|--index.ts
|--main.ts
|--server.ts
生成されるエンドポイント。
https://my-azure-functions.azurewebsites.net/api/express1
https://my-azure-functions.azurewebsites.net/api/express2
https://my-azure-functions.azurewebsites.net/api/express3
(動作デモ)
- /api/hapi-simple
- /api/express1
- /api/hapi1
- まだJSONを返すAPIしか試作してないけど、HTMLを返すこともできるのでは?
- つまりAngular SSR on Azure Functionsをやりたい。
- ここ数日トライし続けているが光が見えない。
- 誰か作れたら教えてください。
===
- (最新情報) HTMLは返せた。でも画像が返せない。もうAzureの仕様なのでは…
- 画像は全て別サーバーから取得するようにしたらイケるかもしれない。