hiccLoghicc log by wccHipo日志

使用Dart开发你的serverless 后端

toc

Intro

Why ? 10M的Dart server镜像🤓

这是篇快文。😄

serverless时代,用什么开发你的后端?Go? NodeJs ? 其实随着Flutter的流行,我们还有个选项 -- Dart。

有什么优势让我们选用这个相对小众的语言?

还是它的核心优势,JIT + AOT

10M的镜像尺寸

  • JIT: 极佳的开发体验,特别是前端工程师,像Nodejs一样,甚至有类似express的server框架,而且Dart是强类型语言,最近也做到了null safe 更写起来跟省心,潜在bug少。
  • AOT:极小的构建镜像,10M的尺寸加上执行更快的编译后机器码,保证最高效率的使用云资源。(最小的node镜像也有50M了)

快速开始

创建项目

dart create -t server-simple hipo

入口代码

import 'dart:io'; import 'package:shelf/shelf.dart'; import 'package:shelf/shelf_io.dart' as shelf_io; import 'package:shelf_static/shelf_static.dart' as shelf_static; import 'package:dotenv/dotenv.dart' show load, clean, isEveryDefined, env; import 'src/common/db.dart'; import 'src/routers/user.router.dart'; Future main() async { load(); final port = int.parse(Platform.environment['PORT'] ?? '8080'); final isDev = env['DART_ENV'] == 'production'; final conf = { 'isDev': isDev, 'database': { 'host': env['DB_HOST'] ?? '127.0.0.1', } }; final cascade = Cascade().add(_staticHandler).add(userRouter); // init db final db = getDb(conf); final pipeline = Pipeline() .addMiddleware(logRequests()) .addMiddleware((innerHandler) => (req) async { final _req = req.change(context: {'db': db}); return innerHandler(_req); }) .addHandler(cascade.handler); final server = await shelf_io.serve( pipeline, InternetAddress.anyIPv4, // Allows external connections port); print('Serving at http://${server.address.host}:${server.port}'); } final _staticHandler = shelf_static.createStaticHandler('public', defaultDocument: 'index.html');

shelf 系列是Google 提供类似前端express中间件形式的开发库,用起来很好上手

路由代码

import 'package:hipo/mysql_utils.dart'; import 'package:shelf_router/shelf_router.dart' as shelf_router; import 'package:shelf/shelf.dart'; import 'dart:async'; final userRouter = shelf_router.Router() ..get('/api/helloworld', _helloWorldHandler); Future<Response> _helloWorldHandler(Request request) async { var data = await (request.context['db'] as MysqlUtils).getAll(table: 'user'); print('$data'); return Response.ok({'len': data.length}.toString()); }

mysql 数据库

可以使用mysql1mysql_utils 这两个库。

import 'package:hipo/mysql_utils.dart'; import 'package:mysql1/mysql1.dart'; final getDb = (Map<String, dynamic> conf) => MysqlUtils( settings: ConnectionSettings( host: conf['database']['host'], port: conf['database']['port'], user: conf['database']['user'], password: conf['database']['password'], db: conf['database']['db'], useCompression: false, useSSL: false, // maxPacketSize: 1024 * 1024, // characterSet: CharacterSet.UTF8, // timeout: const Duration(seconds: 10), ), // prefix: 'prefix_', pool: false, errorLog: (error) { print('db error |$error\n├───────────────────────────'); }, sqlLog: (sql) { print('db sql |$sql\n├───────────────────────────'); }, connectInit: (db1) async { print('whenComplete'); });

注意:

  • 貌似不支持mysql 8.0+的版本
  • mysql_utils需要变通下使用,作者写明了依赖flutter,实际上server也能用。

Dockerfile

# Official Dart image: https://hub.docker.com/_/dart # Specify the Dart SDK base image version using dart:<version> (ex: dart:2.12) FROM dart:stable AS build # Resolve app dependencies. WORKDIR /app COPY pubspec.* ./ RUN dart pub get # Copy app source code and AOT compile it. COPY . . # Ensure packages are still up-to-date if anything has changed RUN dart pub get --offline RUN dart compile exe bin/hipo.dart -o bin/hipo # Build minimal serving image from AOT-compiled `/hipo` and required system # libraries and configuration files stored in `/runtime/` from the build stage. FROM scratch ARG DART_ENV=production ENV DART_ENV=${DART_ENV} COPY --from=build /runtime/ / COPY --from=build /app/bin/hipo /app/bin/ # Include files in the /public directory to enable static asset handling COPY --from=build /app/public/ /public # Start server. EXPOSE 8080 CMD ["/app/bin/hipo"]

哇啦~~