生产就绪(Production Ready)
架构的可运维性主要和配置,监控,部署这些环节相关。其中监控包括日志监控,调用链监控。
经典软件工程阶段
互联网软件交付阶段
什么是生产就绪
SpringBoot如何实现分环境配置
Staffjoy环境
配置中心选项比较
动态配置更新
Apollo vs SpringCloud Config vs K8s ConfigMap
Apollo | SpringCloud | K8s ConfigMap | |
---|---|---|---|
配置界面 | 统一界面管理,不同环境和集群配置 | 无,通过Git操作 | Cli或Dashboard |
配置存储 | DB | Git | Etcd |
配置生效时间 | 实时推送+应用配合 | 近实时+应用配合 | 近实时+应用配置 |
动态配置 | 支持,实时推送 | 复杂+MQ | 支持发布更新 |
版本管理 | UI支持发布历史和回滚 | 无,通过Git操作 | 无,需自己管理 |
灰度发布 | 支持 | 不支持 | 支持灰度发布 |
授权/审计/审核 | UI操作,修改和发布权限分离 | 需要通过Git仓库设置 | K8s平台部分支持 |
实例配置监控 | 可见哪些客户端配置生效 | 不支持 | 可查询容器环境变量 |
客户端支持 | 原生Java/.Net,提供API,支持Spring标注 | Spring应用+标注支持 | 语言无关 |
参考样例
Apollo动态配置
https://github.com/ctripcorp/apollo-use-cases
SpringCloud集中配置
https://github.com/sqshq/piggymetrics
服务监控工具对比
监控平台的演进历史
CAT vs Zipkin vs Skywalking
CAT | Zipkin | Apache Skywalking | |
---|---|---|---|
调用链可视化 | 有 | 有 | 有 |
聚合报表 | 非常丰富 | 少 | 较丰富 |
服务依赖图 | 简单 | 简单 | 好 |
埋点方式 | 侵入 | 侵入 | 非侵入,运行期字节码增强 |
VM指标监控 | 好 | 无 | 有 |
告警支持 | 有 | 无 | 有 |
多语言支持 | Java/.Net | 丰富 | Java/.Net/NodeJS/PHP自动,Go手动 |
存储机制 | MySQL(报表),本地文件/HDFS(调用链) | 可选in memory,MySQL,ES(生产),Cassandra(生产) | H2,ES(生产) |
社区支持 | 主要在国内,点评/美团 | 文档丰富,国外主流 | Apache支持,国内社区好 |
国内案例 | 点评,携程,陆金所,拍拍贷 | 京东,阿里定制不开源 | 华为,小米,当当,微众银行 |
APM | Yes | No | Yes |
祖先源头 | eBay CAL | Google Dapper | Google Dapper |
同类产品 | 暂无 | Uber Jaeger,SpringCloud Sleuth | Naver Pinpoint |
亮点 | 企业级生产,报表丰富 | 社区生态好 | 非侵入,Apache背书 |
不足 | 用户体验一般,社区一般 | APM报表能力弱 | 时间不长,文档一般,仅限中文社区 |
Skywalking
https://skywalking.apache.org/
Skywalking Java Agent支持库
https://skywalking.apache.org/docs/#JavaAgent
Zipkin
Jaeger
Staffjoy依赖监控图
日志监控
结构化日志和审计日志
https://github.com/jacek99/structlog4j
import com.github.structlog4j.IToLog;
import lombok.Builder;
import lombok.Data;
@Data
@Builder
public class LogEntry implements IToLog {
private String currentUserId;
private String companyId;
private String teamId;
private String authorization;
private String targetType;
private String targetId;
private String originalContents;
private String updatedContents;
@Override
public Object[] toLog() {
return new Object[] {
"auditlog", "true",
"currentUserId", currentUserId,
"companyId", companyId,
"teamId", teamId,
"authorization", authorization,
"targetType", targetType,
"targetId", targetId,
"originalContents", originalContents,
"updatedContents", updatedContents
};
}
}
import com.github.structlog4j.ILogger;
import com.github.structlog4j.SLoggerFactory;
static ILogger logger = SLoggerFactory.getLogger(AccountService.class);
LogEntry auditLog = LogEntry.builder()
.authorization(AuthContext.getAuthz())
.currentUserId(AuthContext.getUserId())
.targetType("account")
.targetId(account.getId())
.updatedContents(account.toString())
.build();
logger.info("created account", auditLog);
集中异常监控和Sentry
官网:https://sentry.io
开源仓库:https://github.com/getsentry/sentry
Sentry云服务的使用
客户端配置
@Bean
public SentryClient sentryClient() {
SentryClient sentryClient = Sentry.init(staffjoyProps.getSentryDsn());
sentryClient.setEnvironment(activeProfile);
sentryClient.setRelease(staffjoyProps.getDeployEnv());
sentryClient.addTag("service", appName);
return sentryClient;
}
发送异常监控日志
public void handleError(ILogger log, String errMsg) {
log.error(errMsg);
if (!envConfig.isDebug()) {
sentryClient.sendMessage(errMsg);
}
}