theme: channing-cyan
背景
Flutter APP交付前,因为逐渐接入部分生产数据进行测试,开发与生成环境分离迫在眉睫。从访问限制、启动方式、配置差异等方面优雅分离环境。先后应用两个方案的实践,最终应用了侵入性最小的方案。
方案一(配置文件分离)
根据网上提供的方案,把main.dart配置文件拆分成两个,一个应用于开发环境、一个应用于生产环境。启动方式变更为:
- flutter run --no-sound-null-safety --debug -t lib/main_dev.dart -v
- flutter run --no-sound-null-safety -t lib/main.dart -v
这样的方式完成了分离需求。还可以再进一步简化输入指令。使用IDE的功能:如果你在使用如VSCode这样的IDE进行开发,你可以通过修改launch.json
文件来指定在调试时使用的文件。例如,你可以将"args":["-t","lib/main_dev.dart"]
添加到launch.json
文件的configurations
数组中,以便在调试时使用main_dev.dart
文件。
使用一段时间后发现了一些问题。
- 业务开发中配置改造需要同时修正main_dev.dart/main.dart两个文件,分离影响到业务开发
- 即使简化过指令,和之前启动指令差距大,开发容易输错指令
能否精确区分出开发环境和生产环境配置上的差异,最小程度入侵业务。并且保持指令的简洁,维持开发人员固有习惯?方案二由此诞生
方案二(精准配置分离)
启动文件main.dart中根据原生包foundation.dart提供的Flutter运行模式来区分环境。
运行模式包括 Debug、Release 和 Profile。其中,Release 模式对应的是优化后的应用程序版本,用于最终发布到用户设备上。
利用运行模式,将开发与生产环境的差异性配置精准分离。配置包含:
- 日志等级
- APP标题
- APP Logo
- 访问的环境域名
import 'package:flutter/foundation.dart';
if (kReleaseMode) {
loggerConfig = LoggerConfig(level: LoggerLevel.info, storageType: StorageType.internal, persistDays: 7);
runApp(MultiProvider(
providers: [ChangeNotifierProvider(create: (_) => LoadingProvider(false))],
child: AppConfig(
appName: 'xxx',
apiBaseUrl: "https://wl.xxx.cn",
wssBaseUrl: "wss://wl.xxx.cn",
loggerConfig: loggerConfig,
child: MyApp())));
} else {
loggerConfig = LoggerConfig(level: LoggerLevel.debug, storageType: StorageType.external, persistDays: 2);
runApp(MultiProvider(
providers: [ChangeNotifierProvider(create: (_) => LoadingProvider(false))],
child: AppConfig(
appName: 'xxx测试',
apiBaseUrl: "https://wl-dev.xxx.cn",
wssBaseUrl: "wss://wl-dev.xxx.cn",
loggerConfig: loggerConfig,
child: MyApp())));
}
数据隔离
数据隔离采用最基础的域名隔离方式,把对接环境、数据库、配置文件隔离开。 DNS(Domain Name System)是域名系统,提前申请开发域名和生产域名,接入证书。
域名 平台 业务规划 openapi-dev.xxx.cn API开放平台 第三方系统接入 wl.xxx.cn 业务生产环境 wl-dev.xxx.cn 业务开发环境利用nginx.conf中的include
语法导入提前规划的.conf文件。隔离开二级域名、页面、API代理。
For Example (开放平台)
访问域名openapi-dev.xxx.cn,只提供部分开发API。没提供页面路由及私有API
/conf/dev/confd/openapi.conf
server {
listen 443 ssl;
server_name openapi-dev.xxx.cn;
ssl_certificate /usr/local/nginx/cert/xxx.cn.pem;
ssl_certificate_key /usr/local/nginx/cert/xxx.cn.key;
ssl_session_cache shared:SSL:1m;
ssl_session_timeout 5m;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
location ~ ^/(openservice|cmpy|auth|admin|order|waybill|bill|message|captcha) {
proxy_pass https://openservice;
}
}
nginx.conf
集成/conf/dev/confd/openapi.conf
http {
include mime.types;
default_type application/octet-stream;
#log_format main '$remote_addr - $remote_user [$time_local] "$request" '
# '$status $body_bytes_sent "$http_referer" '
# '"$http_user_agent" "$http_x_forwarded_for"';
#access_log logs/access.log main;
sendfile on;
#tcp_nopush on;
#keepalive_timeout 0;
keepalive_timeout 65;
#gzip on;
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
# 集成/conf/dev/confd/openapi.conf
include ../confd/*.conf;
}
根据系统访问限制,在多个二级域名上规划出各个系统访问入口。在功能、业务、部门等多个维度划分出产品。
数据克隆
数据克隆目的:将生产环境部分数据迁移到开发环境用于Bug复现、日志分析等。
因为环境完全隔离,数据库模型完全一致,只需要简单的SQL操作即可完成。
指令
根据启动方式的不同,优雅区分开启动的环境。
Run APP (默认模式->debug)
flutter run --no-sound-null-safety
打印日志
flutter run --no-sound-null-safety -v
Run (debug模式)
flutter run --no-sound-null-safety --debug
Run (release模式)
flutter run --no-sound-null-safety --release
Build Apk (默认模式->release)
flutter build apk --no-sound-null-safety --split-per-abi
Build Apk (debug模式)
flutter build apk --no-sound-null-safety --debug --split-per-abi
Build Apk (release模式)
flutter build apk --no-sound-null-safety --release --split-per-abi
后记
开发/生产环境分离其实核心旨在访问APIBaseUrl
的分离,分离的方式、辐射面的不同对日常开发维护、业务升级、数据迁移等后续诸多场景都会造成或多或少的影响。系统性的考虑分离才能做到优雅分离。