{"id":29738453,"url":"https://github.com/tkote/fn-netty","last_synced_at":"2026-05-19T09:09:31.403Z","repository":{"id":188623126,"uuid":"347282675","full_name":"tkote/fn-netty","owner":"tkote","description":"Fn / OCI Function without FDK, with netty / reactor-netty","archived":false,"fork":false,"pushed_at":"2024-05-10T01:28:19.000Z","size":49,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-07-25T23:42:57.670Z","etag":null,"topics":["fdk","fn","netty","oci-functions","reactor-netty"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tkote.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2021-03-13T05:33:23.000Z","updated_at":"2024-05-10T01:28:23.000Z","dependencies_parsed_at":"2025-07-25T18:48:04.874Z","dependency_job_id":"45f66b8a-5597-4c6d-b2c2-39bbc489caeb","html_url":"https://github.com/tkote/fn-netty","commit_stats":null,"previous_names":["tkote/fn-netty"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/tkote/fn-netty","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tkote%2Ffn-netty","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tkote%2Ffn-netty/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tkote%2Ffn-netty/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tkote%2Ffn-netty/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tkote","download_url":"https://codeload.github.com/tkote/fn-netty/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tkote%2Ffn-netty/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33209605,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-19T07:54:09.561Z","status":"ssl_error","status_checked_at":"2026-05-19T07:54:08.508Z","response_time":58,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["fdk","fn","netty","oci-functions","reactor-netty"],"created_at":"2025-07-25T18:47:46.106Z","updated_at":"2026-05-19T09:09:31.388Z","avatar_url":"https://github.com/tkote.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Functions without FDK\n\n## Fn (OCI Functions) の Function を FDK を使わずに作成する方法\n\n* HTTP Server over Unix Domain Socket を実装する\n* POST /call で呼び出される、これをハンドルする\n* bind する ファイルのパスは環境変数 FN_LISTENER で渡される  \n  例: `/tmp/iofs/lsnr.sock`\n  - 直接 bind せずに 指定されたパスと同一ディレクトリの別のファイル名で bind する  \n    例: `/tmp/iofs/YvzDu6m9_lsnr.sock`\n  - このファイルに rw-rw-rw- のアクセス権限を設定する\n  - これに相対パスのシンボリックリンクを張る\n  - 結果、実行時にはこんなファイル構成となっている\n    ```\n    $ ls -l /tmp/iofs\n    lrwxrwxrwx. 1 root root 17 Mar  3 17:45 lsnr.sock -\u003e YvzDu6m9_lsnr.sock\n    srw-rw-rw-. 1 root root  0 Mar  3 17:45 YvzDu6m9_lsnr.sock\n    ```\n\n## How to create an Fn (OCI Functions) Function without FDK\n\n+ Implement HTTP Server over Unix Domain Socket\n+ Handle POST /call\n+ The file name to be bound is passed via the environment variable FN_LISTENER  \n  example: /tmp/iofs/lsnr.sock\n  - Bind socket to another file which is different from ${FN_LISTENER} located in the same directory  \n    example: /tmp/iofs/YvzDu6m9_lsnr.sock\n  - Set rw-rw-rw- permission to this file\n  - Create a symbolic link named ${FN_LISTENER} which is linked to the bound file as a relative path\n  - As a result, the placement of files is like this\n    ```\n    $ ls -l /tmp/iofs\n    lrwxrwxrwx. 1 root root 17 Mar  3 17:45 lsnr.sock -\u003e YvzDu6m9_lsnr.sock\n    srw-rw-rw-. 1 root root  0 Mar  3 17:45 YvzDu6m9_lsnr.sock\n    ```\n\n\n## 実装\n\nつまり Unix Domain Socket に対応した HTTP Server フレームワークを使うのが手っ取り早い  \nということで、今回は netty と reactor-netty で実装してみた  \n\n* netty 版  \n  [Netty HTTP Example の snoop](https://github.com/netty/netty/tree/4.1/example/src/main/java/io/netty/example/http/snoop) をベースにアレンジした\n\n* reactor-netty 版  \n  snoopと同じ出力になるように実装\n\n## netty 使用時の実装 tips\n* netty は Unix Domain Socket を扱うための native library (.so) が必要で、デフォルトでは実行時にダイナミックにファイルを配置するようで、Dockerコンテナ内で動作させるとうまく動作しない模様  \n  → imageのビルド時に特定のディレクトリに native library を配置して、起動オプションで `-Djava.library.path=xxx` を指定することによってこの問題を回避した、本来はちゃんと原因追及するべき...\n* OCI Functions のメモリー量の指定に気をつける (128 では小さくて何も吐かずに勝手に落ちた)\n\n## ビルド \u0026 実行\n\n環境変数 mainClass は最初にセットしておく\n\n```bash\n# netty 版\n$ export mainClass=org.example.netty.FnServer\n# reactor-netty 版\n$ export mainClass=org.example.reactor.FnServer\n```\n\n* ローカルでビルド \u0026 実行\n\n  ```bash\n  # build\n  $ mvn clean package\n\n  # run server\n  $ java -cp target/fn-netty.jar $mainClass\n  \n  # call function\n  $ curl --unix-socket /tmp/fnlsnr.sock -X POST -d 'Hello World!' http:/call\n  ```\n\n* Docker imageを作成 \u0026 実行\n  \n  ```bash\n  # build\n  $ docker build --build-arg mainClass=$mainClass -t fn-netty:0.0.1 .\n  \n  # run server\n  $ docker run --rm -it --name fn-netty -v /tmp:/tmp fn-netty:0.0.1\n\n  # call function\n  $ curl --unix-socket /tmp/fnlsnr.sock -X POST -d 'Hello World!' http:/call\n  ```\n\n* ローカル Fn Server で実行\n  \n  Fn CLIはインストール済みという前提で\n  \n  ```bash\n  # start Fn server\n  $ fn start\n\n  # setup Fn CLI\n  $ fn use context default\n  \n  # create app\n  $ fn create app funcapp\n\n  # deploy function\n  $ fn deploy -app funcapp --build-arg mainClass=$mainClass --local --no-bump -v\n\n  # call function\n  $ echo -n 'Hello World!' | fn invoke funcapp fn-netty\n  ```\n\n* OCI Functions にデプロイ \u0026 実行\n\n  OCI Functionsで アプリケーション funcapp が作成されている前提で\n\n  ```bash\n  # setup Fn CLI\n  $ fn use context XXXXXX\n\n  # deploy function\n  $ fn deploy -app funcapp --build-arg mainClass=$mainClass --no-bump -v\n\n  # call functions\n  $ echo -n 'Hello World!' | fn invoke funcapp fn-netty\n  ```\n\n## デモ\n\n* Local 環境\n\n  ```\n  $ curl --unix-socket /tmp/fnlsnr.sock -X POST -d 'Hello World!' http:/call\n  FN-NETTY (REACTOR) SERVER\n  ===================================\n  VERSION: HTTP/1.1\n  HOSTNAME: http\n  REQUEST_URI: /call\n  \n  HEADER: User-Agent = curl/7.29.0\n  HEADER: Host = http\n  HEADER: Accept = */*\n  HEADER: Content-Length = 12\n  HEADER: Content-Type = application/x-www-form-urlencoded\n  \n  CONTENT: Hello World!\n  END OF CONTENT\n  ```\n\n* Local Fn Server\n\n  ```\n  $ echo -n 'Hello World!' | fn invoke funcapp fn-netty\n  FN-NETTY (REACTOR) SERVER\n  ===================================\n  VERSION: HTTP/1.1\n  HOSTNAME: localhost\n  REQUEST_URI: /call\n  \n  HEADER: Host = localhost\n  HEADER: User-Agent = Go-http-client/1.1\n  HEADER: Transfer-Encoding = chunked\n  HEADER: Accept-Encoding = gzip\n  HEADER: Content-Type = text/plain\n  HEADER: Fn-Call-Id = 01F0MT4Q21NG8G00GZJ0000005\n  HEADER: Fn-Deadline = 2021-03-13T03:35:10Z\n  \n  CONTENT: Hello World!\n  END OF CONTENT\n  ```\n\n* OCI Functions (trace=ON)\n\n  ```\n  $ echo -n 'Hello World!' | fn invoke funcapp fn-netty\n  FN-NETTY (REACTOR) SERVER\n  ===================================\n  VERSION: HTTP/1.1\n  HOSTNAME: localhost\n  REQUEST_URI: /call\n  \n  HEADER: Host = localhost\n  HEADER: User-Agent = Go-http-client/1.1\n  HEADER: Transfer-Encoding = chunked\n  HEADER: Accept-Encoding = gzip\n  HEADER: Content-Type = application/json\n  HEADER: Date = Fri, 30 Jul 2021 20:53:55 GMT\n  HEADER: Fn-Call-Id = 01FBWK3SVZ1BT0H18ZJ000MVYQ\n  HEADER: Fn-Deadline = 2021-07-30T20:58:53Z\n  HEADER: Oci-Subject-Compartment-Id = ocid1.tenancy.oc1..xxxxxx\n  HEADER: Oci-Subject-Id = ocid1.user.oc1..xxxxxx\n  HEADER: Oci-Subject-Tenancy-Id = ocid1.tenancy.oc1..xxxxxx\n  HEADER: Oci-Subject-Type = user\n  HEADER: Opc-Compartment-Id = ocid1.compartment.oc1..xxxxxx\n  HEADER: Opc-Request-Id = /01FBWK3ST90000000000015JDG/01FBWK3ST90000000000015JDH\n  HEADER: X-B3-Spanid = 4d52a9209c1ea317\n  HEADER: X-B3-Traceid = 4d52a9209c1ea317\n  HEADER: X-Content-Sha256 = f4OxZX/x/FO5LcGBSKHWXfwtSx+j1ncoSt3SABJtkGk=\n  \n  CONTENT: Hello World!\n  END OF CONTENT\n  ```\n  \n  OCI Functions `Content-Type: application/json` になってる...\n\n\n* OCI API Gateway -\u003e OCI Functions (trace=ON)\n\n  ```\n  $curl -X POST -d \"[]\" -H \"Content-Type: application/json\" \\\n    https://xxxxxx.apigateway.us-ashburn-1.oci.customer-oci.com/fn-netty/\n\n  FN-NETTY (REACTOR) SERVER\n  ===================================\n  VERSION: HTTP/1.1\n  HOSTNAME: localhost\n  REQUEST_URI: /call\n\n  HEADER: Host = localhost\n  HEADER: User-Agent = lua-resty-http/0.14 (Lua) ngx_lua/10019\n  HEADER: Transfer-Encoding = chunked\n  HEADER: Content-Type = application/json\n  HEADER: Date = Fri, 30 Jul 2021 20:26:32 GMT\n  HEADER: Fn-Call-Id = 01FBWHHP481BT0J1GZJ000PM8T\n  HEADER: Fn-Deadline = 2021-07-30T20:31:43Z\n  HEADER: Fn-Http-H-Accept = */*\n  HEADER: Fn-Http-H-Cdn-Loop = fdJfCZhy618AGmi5huTgzQ\n  HEADER: Fn-Http-H-Content-Length = 2\n  HEADER: Fn-Http-H-Content-Type = application/json\n  HEADER: Fn-Http-H-Forwarded = for=xxx.xxx.xxx.xxx\n  HEADER: Fn-Http-H-Host = xxxxxx.apigateway.us-ashburn-1.oci.customer-oci.com\n  HEADER: Fn-Http-H-User-Agent = curl/7.29.0\n  HEADER: Fn-Http-H-X-Forwarded-For = xxx.xxx.xxx.xxx\n  HEADER: Fn-Http-H-X-Real-Ip = zzz.zzz.zzz.zzz\n  HEADER: Fn-Http-Method = POST\n  HEADER: Fn-Http-Request-Url = /fn-netty/\n  HEADER: Fn-Intent = httprequest\n  HEADER: Fn-Invoke-Type = sync\n  HEADER: Oci-Subject-Compartment-Id = ocid1.compartment.oc1..xxxxxx\n  HEADER: Oci-Subject-Id = ocid1.apigateway.oc1.iad.xxxxxx\n  HEADER: Oci-Subject-Tenancy-Id = ocid1.tenancy.oc1..xxxxxx\n  HEADER: Oci-Subject-Type = resource\n  HEADER: Opc-Request-Id = /6997BF9ED7E80B78B3834B7EB7054184/01FBWHHP3X0000000000019C89\n  HEADER: X-B3-Spanid = 5642ee33c9d0c82c\n  HEADER: X-B3-Traceid = 5642ee33c9d0c82c\n  HEADER: X-Content-Sha256 = T1PNoYwrqgwDVLtfmj7L5e0Sq02OEbqHPC8RFhICuUU=\n  HEADER: Accept-Encoding = gzip\n\n  CONTENT: []\n  END OF CONTENT\n  ```\n\n  `Fn-Http-xxx` でAPI Gatewayが受けっとったHTTPリクエストの内容が転送されているのが分かる\n\n* OCI API Gateway -\u003e OCI Functions (trace=ON)  \n  Functions の config で PRINT_ENV=TRUE に設定して環境変数も出力\n\n```\nFN-NETTY (REACTOR) SERVER\n===================================\nENV: FN_FN_NAME=fn-netty\nENV: PATH=/usr/local/openjdk-8/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin\nENV: OCI_RESOURCE_PRINCIPAL_RPST=/.oci-credentials/rpst\nENV: JAVA_HOME=/usr/local/openjdk-8\nENV: FN_APP_ID=ocid1.fnapp.oc1.iad.xxxxxx\nENV: FN_CPUS=125m\nENV: LANG=C.UTF-8\nENV: OCI_TRACE_COLLECTOR_URL=https://xxxxxx.apm-agt.us-ashburn-1.oci.oraclecloud.com/20200101/observations/public-span?dataFormat=zipkin\u0026dataFormatVersion=2\u0026dataKey=xxxxxx\nENV: FN_MEMORY=256\nENV: FN_LOGFRAME_NAME=01FD180P0F0000000000001CGW\nENV: PRINT_ENV=true\nENV: JAVA_VERSION=8u282\nENV: FN_LOGFRAME_HDR=Opc-Request-Id\nENV: OCI_REGION_METADATA={\"realmDomainComponent\":\"oraclecloud.com\",\"realmKey\":\"oc1\",\"regionIdentifier\":\"us-ashburn-1\",\"regionKey\":\"IAD\"}\nENV: FN_TYPE=sync\nENV: OCI_TRACING_ENABLED=1\nENV: OCI_RESOURCE_PRINCIPAL_VERSION=2.2\nENV: OCI_RESOURCE_PRINCIPAL_REGION=us-ashburn-1\nENV: OCI_RESOURCE_PRINCIPAL_PRIVATE_PEM=/.oci-credentials/private.pem\nENV: FN_FN_ID=ocid1.fnfunc.oc1.iad.xxxxxx\nENV: FN_APP_NAME=funcapp\nENV: HOSTNAME=308cc78a5d83\nENV: FN_FORMAT=http-stream\nENV: FN_LISTENER=unix:/tmp/iofs/lsnr.sock\nENV: HOME=/\n\nVERSION: HTTP/1.1\nHOSTNAME: localhost\nREQUEST_URI: /call\n\nHEADER: Host = localhost\nHEADER: User-Agent = lua-resty-http/0.14 (Lua) ngx_lua/10019\nHEADER: Transfer-Encoding = chunked\nHEADER: Content-Type = application/json\nHEADER: Date = Sat, 14 Aug 2021 02:34:49 GMT\nHEADER: Fn-Call-Id = 01FD1862RF1BT0J4GZJ000TGF8\nHEADER: Fn-Deadline = 2021-08-14T02:39:47Z\nHEADER: Fn-Http-H-Accept = */*\nHEADER: Fn-Http-H-Cdn-Loop = fdJfCZhy618AGmi5huTgzQ\nHEADER: Fn-Http-H-Content-Length = 2\nHEADER: Fn-Http-H-Content-Type = application/json\nHEADER: Fn-Http-H-Forwarded = for=xxx.xxx.xxx.xxx\nHEADER: Fn-Http-H-Host = xxxxxx.apigateway.us-ashburn-1.oci.customer-oci.com\nHEADER: Fn-Http-H-User-Agent = curl/7.29.0\nHEADER: Fn-Http-H-X-Forwarded-For = xxx.xxx.xxx.xxx\nHEADER: Fn-Http-H-X-Real-Ip = zzz.zzz.zzz.zzz\nHEADER: Fn-Http-Method = POST\nHEADER: Fn-Http-Request-Url = /fn-netty/\nHEADER: Fn-Intent = httprequest\nHEADER: Fn-Invoke-Type = sync\nHEADER: Oci-Subject-Compartment-Id = ocid1.compartment.oc1..xxxxxx\nHEADER: Oci-Subject-Id = ocid1.apigateway.oc1.iad.xxxxxx\nHEADER: Oci-Subject-Tenancy-Id = ocid1.tenancy.oc1..xxxxxx\nHEADER: Oci-Subject-Type = resource\nHEADER: Opc-Request-Id = /A22D916112C989A90997B447C703F90A/01FD1862QW000000000001G0XG\nHEADER: X-B3-Spanid = 3ebf93f7eca825ec\nHEADER: X-B3-Traceid = 3ebf93f7eca825ec\nHEADER: X-Content-Sha256 = T1PNoYwrqgwDVLtfmj7L5e0Sq02OEbqHPC8RFhICuUU=\nHEADER: Accept-Encoding = gzip\n\nCONTENT: []\nEND OF CONTENT\n```\n\nちなみに trace=OFF にすると、  \n`ENV: OCI_TRACE_COLLECTOR_URL=`  \n`ENV: OCI_TRACING_ENABLED=0`  \nとなる\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftkote%2Ffn-netty","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftkote%2Ffn-netty","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftkote%2Ffn-netty/lists"}