如果故障选择了你……

简介: 总以为混沌工程离你很远?但发生故障的那一刻不是由你来选择的,而是那一刻来选择你,你能做的就是为之做好准备。混沌工程在阿里内部已经应用多年,而ChaosBlade这个开源项目是阿里多年来通过注入故障来对抗故障的经验结晶。为使大家更深入的了解其实现原理以及如何扩展自己所需要的组件故障注入,我们准备了一个系列对其做详细技术剖析:架构篇、模型篇、协议篇、字节码篇、插件篇以及实战篇。

头图.png

作者 | 叶飞、穹谷

导读:总以为混沌工程离你很远?但发生故障的那一刻不是由你来选择的,而是那一刻来选择你,你能做的就是为之做好准备。混沌工程在阿里内部已经应用多年,而ChaosBlade这个开源项目是阿里多年来通过注入故障来对抗故障的经验结晶。为使大家更深入的了解其实现原理以及如何扩展自己所需要的组件故障注入,我们准备了一个系列对其做详细技术剖析:架构篇、模型篇、协议篇、字节码篇、插件篇以及实战篇。

原文标题《技术剖析 Java 场景混沌工程实现系列(一)| 架构篇》

前言

在分布式系统架构下,服务间的依赖日益复杂,很难评估单个服务故障对整个系统的影响,并且请求链路长,监控告警的不完善导致发现问题、定位问题难度增大,同时业务和技术迭代快,如何持续保障系统的稳定性和高可用性受到很大的挑战。

我们知道发生故障的那一刻不是由你来选择的,而是那一刻来选择你,你能做的就是为之做好准备。所以构建稳定性系统很重要的一环是混沌工程,在可控范围或环境下,通过故障注入,来持续提升系统的稳定性和高可用能力。

ChaosBlade(Github 地址:https://github.com/chaosblade-io/chaosblade) 是一款遵循混沌工程实验原理,提供丰富故障场景实现,帮助分布式系统提升容错性和可恢复性的混沌工程工具,可实现底层故障的注入,特点是操作简洁、无侵入、扩展性强。 其中 chaosblade-exec-jvm (Github 地址:https://github.com/chaosblade-io/chaosblade-exec-jvm )项目实现了零成本对 Java 应用服务故障注入。其不仅支持主流的框架组件,如 Dubbo、Servlet、RocketMQ 等,还支持指定任意类和方法注入延迟、异常以及通过编写 Java 和 Groovy 脚本来实现复杂的实验场景。

为使大家更深入的了解其实现原理以及如何扩展自己所需要的组件故障注入,分为六篇文章对其做详细技术剖析:架构篇、模型篇、协议篇、字节码篇、插件篇以及实战篇。本文将详细介绍 chaosblade-exec-jvm 的整体架构设计,使用户对 chaosblade-exec-jvm 有一定的了解。

系统设计

1.png

Chaosblade-exec-jvm 基于 JVM-Sanbox 做字节码修改,执行 ChaosBlade 工具可实现将故障注入的 Java Agent 挂载到指定的应用进程中。Java Agent 遵循混沌实验模型设计,通过插件的可拔插设计来扩展对不同 Java 组件的支持,可以很方便的扩展插件来支持更多的故障场景,插件基于 AOP 的设计定义通知Advice、增强类Enhancer、切点PointCut,同时结合混沌实验模型定模型ModelSpec、实验靶点Target、匹配方式Matcher、攻击动作Action

Chaosblade-exec-jvm 在由make build编译打包时下载 JVM-Sanbox relase 包,编译打包后 chaosblade-exec-jvm 做为 JVM-Sandbox 的模块。在加载 Agent 后,同时监听 JVM-Sanbox 的事件来管理整个混沌实验流程,通过Java Agent 技术来实现类的 transform 注入故障。

原理剖析

在日常后台应用开发中,我们经常需要提供 API 接口给客户端,而这些 API 接口不可避免的由于网络、系统负载等原因存在超时、异常等情况。使用 Java 语言时,HTTP 协议我们通常使用 Servlet 来提供 API 接口,chaosblade-exec-jvm 支持 Servlet 插件,注入超时、自定义异常等故障能力。本篇将通过给 Servlet API 接口 注入延迟故障能力为例,分析 chaosblade-exec-jvm 故障注入的流程。

对 Servlet API 接口/topic延迟3秒,步骤如下:

// 挂载 Agent
blade prepare jvm --pid 888
{"code":200,"success":true,"result":"98e792c9a9a5dfea"}// 注入故障能力
blade create servlet --requestpath=/topic delay --time=3000 --method=post
{"code":200,"success":true,"result":"52a27bafc252beee"}// 撤销故障能力
blade destroy 52a27bafc252beee// 卸载 Agent
blade revoke 98e792c9a9a5dfea

1. 执行过程

以下通过 Servlet 请求延迟为例,详细介绍故障注入的过程。

2.png

  1. ChaosBlade 下发挂载命令,挂载 Sandbox 到应用进程,激活 Java Agent,例如blade p jvm --pid 888
  2. 挂载 Sandbox 后加载 chaosblade-exec-jvm 模块,加载插件,如 ServletPlugin、DubboPlugin 等。
  3. 匹配 ServletPlugin 插件的切点、注册事件监听,HttpServlet 的 doPost、doGet 方法。
  4. ChaosBlade 下发故障规则命令blade create servlet --requestpath=/topic delay --time=3000 --method=post
  5. 匹配故障规则,如 --requestpath=/topic ,访问 http://127.0.0.1/topic 规则匹配成功。
  6. 匹配故障规则成功后,触发故障,如延迟故障、自定义异常抛出等。
  7. ChaosBlade 下发命令卸载 JavaAgent,如blade revoke 98e792c9a9a5dfea

2. 代码剖析

1)挂载 Agent

blade p jvm --pid 888

该命令下发后,将在目标 Java 应用进程挂在 Agent ,触发 SandboxModule onLoad() 事件,初始化           PluginLifecycleListener 来管理插件的生命周期,同时也触发 SandboxModule onActive() 事件,加载部分插件,加载插件对应的 ModelSpec。

// Agent 加载事件
public void onLoad() throws Throwable {ManagerFactory.getListenerManager().setPluginLifecycleListener(this);dispatchService.load();ManagerFactory.load();
}
// ChaosBlade 模块激活实现
public void onActive() throws Throwable {loadPlugins();
}

2)加载 Plugin

3.jpg

Plugin 加载时,创建事件监听器 SandboxEnhancerFactory.createAfterEventListener(plugin) ,监听器会监听感兴趣的事件,如 BeforeAdvice、AfterAdvice 等,具体实现如下:

// 加载插件
public void add(PluginBean plugin) {PointCut pointCut = plugin.getPointCut();if (pointCut == null) {return;}String enhancerName = plugin.getEnhancer().getClass().getSimpleName();// 创建filter PointCut匹配Filter filter = SandboxEnhancerFactory.createFilter(enhancerName, pointCut);// 事件监听int watcherId = moduleEventWatcher.watch(filter, SandboxEnhancerFactory.createBeforeEventListener(plugin), Event.Type.BEFORE);watchIds.put(PluginUtil.getIdentifier(plugin), watcherId);
}

3)匹配 PointCut

SandboxModule onActive() 事件触发 Plugin 加载后,SandboxEnhancerFactory 创建 Filter,Filter 内部通过 PointCut 的 ClassMatcher 和 MethodMatcher 过滤。

public static Filter createFilter(final String enhancerClassName, final PointCut pointCut) {return new Filter() {@Overridepublic boolean doClassFilter(int access, String javaClassName, String superClassTypeJavaClassName,String[] interfaceTypeJavaClassNameArray,String[] annotationTypeJavaClassNameArray) {// ClassMatcher 匹配ClassMatcher classMatcher = pointCut.getClassMatcher();...}@Overridepublic boolean doMethodFilter(int access, String javaMethodName,String[] parameterTypeJavaClassNameArray,String[] throwsTypeJavaClassNameArray,String[] annotationTypeJavaClassNameArray) {// MethodMatcher 匹配MethodMatcher methodMatcher = pointCut.getMethodMatcher();...};
}

4)触发 Enhancer

如果已经加载插件,此时目标应用匹配能匹配到 Filter 后,EventListener 已经可以被触发,但是 chaosblade-exec-jvm 内部通过 StatusManager 管理状态,所以故障能力不会被触发。

例如 BeforeEventListener 触发调用 BeforeEnhancer 的 beforeAdvice() 方法,在ManagerFactory.getStatusManager().expExists(targetName) 判断时候被中断,具体的实现如下:

public void beforeAdvice(String targetName, ClassLoader classLoader, String className,Object object,Method method, Object[] methodArguments) throws Exception {// 判断实验的状态if (!ManagerFactory.getStatusManager().expExists(targetName)) {return;}EnhancerModel model = doBeforeAdvice(classLoader, className, object, method, methodArguments);if (model == null) {return;}...// 注入阶段Injector.inject(model);
}

5)创建混沌实验

blade create servlet --requestpath=/topic delay --time=3000

该命令下发后,触发 SandboxModule @Http("/create") 注解标记的方法,将事件分发给 com.alibaba.chaosblade.exec.service.handler.CreateHandler 处理

在判断必要的 uid、target、action、model 参数后调用 handleInjection,handleInjection 通过状态管理器注册本次实验,如果插件类型是 PreCreateInjectionModelHandler 类型,将预处理一些东西。同是如果 Action 类型是  DirectlyInjectionAction,那么将直接进行故障能力注入,且不需要走 Enhancer,如 JVM OOM 故障能力等。

public Response handle(Request request) {if (unloaded) {return Response.ofFailure(Code.ILLEGAL_STATE, "the agent is uninstalling");}// 检查 suid,suid 是一次实验的上下文IDString suid = request.getParam("suid");...return handleInjection(suid, model, modelSpec);
}private Response handleInjection(String suid, Model model, ModelSpec modelSpec) {RegisterResult result = this.statusManager.registerExp(suid, model);if (result.isSuccess()) {// 判断是否预创建applyPreInjectionModelHandler(suid, modelSpec, model);}
}

ModelSpec

  • com.alibaba.chaosblade.exec.common.model.handler.PreCreateInjectionModelHandler预创建
  • com.alibaba.chaosblade.exec.common.model.handler.PreDestroyInjectionModelHandler预销毁
private void applyPreInjectionModelHandler(String suid, ModelSpec modelSpec, Model model)throws ExperimentException {if (modelSpec instanceof PreCreateInjectionModelHandler) {((PreCreateInjectionModelHandler)modelSpec).preCreate(suid, model);}
}
...

DirectlyInjectionAction

如果 ModelSpec 是 PreCreateInjectionModelHandler 类型,且 ActionSpec 的类型是 DirectlyInjectionAction 类型,将直接进行故障能力注入,比如 JvmOom 故障能力,ActionSpec 的类型不是 DirectlyInjectionAction 类型,将加载插件。

4.jpg

private Response handleInjection(String suid, Model model, ModelSpec modelSpec) {// 注册RegisterResult result = this.statusManager.registerExp(suid, model);if (result.isSuccess()) {// handle injectiontry {applyPreInjectionModelHandler(suid, modelSpec, model);} catch (ExperimentException ex) {this.statusManager.removeExp(suid);return Response.ofFailure(Response.Code.SERVER_ERROR, ex.getMessage());}return Response.ofSuccess(model.toString());}return Response.ofFailure(Response.Code.DUPLICATE_INJECTION, "the experiment exists");
}

注册成功后返回 uid,如果本阶段直接进行故障能力注入了,或者自定义 Enhancer advice 返回 null,那么后不通过Inject 类触发故障。

6)注入故障能力

故障能力注入的方式,最终都是调用 ActionExecutor 执行故障能力。

  • 通过 Injector 注入;
  • DirectlyInjectionAction 直接注入,直接注入不进过 Inject 类调用阶段,如果  JVM OOM 故障能力等。

DirectlyInjectionAction 直接注入不经过Enhancer参数包装匹配直接到故障触发 ActionExecutor 执行阶段,如果是Injector 注入此时因为 StatusManager 已经注册了实验,当事件再次出发后ManagerFactory.getStatusManager().expExists(targetName) 的判断不会被中断,继续往下走,到了自定义的 Enhancer ,在自定义的 Enhancer 里面可以拿到原方法的参数、类型等,甚至可以反射调原类型的其他方法,这样做风险较大,一般在这里往往是取一些成员变量或者 get 方法等,用于 Inject 阶段参数匹配。

7)包装匹配参数

自定义的 Enhancer,如 ServletEnhancer,把一些需要与命令行匹配的参数 包装在 MatcherMode 里面,然后包装 EnhancerModel 返回,比如  --requestpath = /index ,那么requestpath 等于 requestURI;--querystring="name=xx" 做自定义匹配。参数包装好后,在 Injector.inject(model) 阶段判断。

public EnhancerModel doBeforeAdvice(ClassLoader classLoader, String className, Object object,Method method, Object[] methodArguments)throws Exception {Object request = methodArguments[0];String requestURI = ReflectUtil.invokeMethod(request, ServletConstant.GET_REQUEST_URI, new Object[]{}, false);String requestMethod = ReflectUtil.invokeMethod(request, ServletConstant.GET_METHOD, new Object[]{}, false);MatcherModel matcherModel = new MatcherModel();matcherModel.add(ServletConstant.METHOD_KEY, requestMethod);matcherModel.add(ServletConstant.REQUEST_PATH_KEY, requestURI);Map<String, Object> queryString = getQueryString(requestMethod, request);EnhancerModel enhancerModel = new EnhancerModel(classLoader, matcherModel);// 自定义参数匹配enhancerModel.addCustomMatcher(ServletConstant.QUERY_STRING_KEY, queryString, ServletParamsMatcher.getInstance());return enhancerModel;
}

8)判断前置条件

Inject 阶段首先获取 StatusManage 注册的实验,compare(model, enhancerModel) 做参数比对,比对失败返回,limitAndIncrease(statusMetric) 判断 --effect-count --effect-percent 来控制影响的次数和百分比

public static void inject(EnhancerModel enhancerModel) throws InterruptProcessException {String target = enhancerModel.getTarget();List<StatusMetric> statusMetrics = ManagerFactory.getStatusManager().getExpByTarget(target);for (StatusMetric statusMetric : statusMetrics) {Model model = statusMetric.getModel();// 匹配命令行输入参数if (!compare(model, enhancerModel)) {continue;}// 累加攻击次数和判断攻击次数是否到达 effect count boolean pass = limitAndIncrease(statusMetric);if (!pass) {break;}enhancerModel.merge(model);ModelSpec modelSpec = ManagerFactory.getModelSpecManager().getModelSpec(target);ActionSpec actionSpec = modelSpec.getActionSpec(model.getActionName());// ActionExecutor执行故障能力actionSpec.getActionExecutor().run(enhancerModel);break;}
}

9)触发故障能力

由 Inject 触发,或者由 DirectlyInjectionAction 直接触发,最后调用自定义的 ActionExecutor 生成故障,如  DefaultDelayExecutor ,此时故障能力已经生效了。

public void run(EnhancerModel enhancerModel) throws Exception {String time = enhancerModel.getActionFlag(timeFlagSpec.getName());Integer sleepTimeInMillis = Integer.valueOf(time);// 触发延迟TimeUnit.MILLISECONDS.sleep(sleepTimeInMillis);
}

3. 销毁实验

blade destroy 52a27bafc252beee

该命令下发后,触发 SandboxModule @Http("/destory") 注解标记的方法,将事件分发给 com.alibaba.chaosblade.exec.service.handler.DestroyHandler 处理,注销本次故障的状态,此时再次触发 Enchaner 后,StatusManger判定实验状态已经销毁,不会在进行故障能力注入

// StatusManger 判断实验状态
if (!ManagerFactory.getStatusManager().expExists(targetName)) {return;
}

如果插件的 ModelSpec 是 PreDestroyInjectionModelHandler 类型,且 ActionSpec 的类型是 DirectlyInjectionAction 类型,停止故障能力注入,ActionSpec 的类型不是 DirectlyInjectionAction 类型,将卸载插件。

// DestroyHandler 注销实验状态
public Response handle(Request request) {String uid = request.getParam("suid");...// 判断 uidif (StringUtil.isBlank(uid)) {if (StringUtil.isBlank(target) || StringUtil.isBlank(action)) {return false;}// 注销statusreturn destroy(target, action);}return destroy(uid);
}

4. 卸载 Agent

blade revoke 98e792c9a9a5dfea
wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

该命令下发后,触发 SandboxModule unload() 事件,同时插件卸载,完全回收 Agent 创建的各种资源。

public void onUnload() throws Throwable {dispatchService.unload();ManagerFactory.unload();watchIds.clear();
}

总结

本文以 Servlet 场景为例,详细介绍了 chaosblade-exec-jvm 项目架构设计和实现原理,后续将通过模型篇、协议篇、字节码篇、插件篇以及实战篇深入介绍此项目,使读者达到可以快速扩展自己所需插件的目的。

ChaosBlade 项目作为一个混沌工程实验工具,不仅使用简洁,而且还支持丰富的实验场景且扩展场景简单,支持的场景领域如下:

  • 基础资源:比如 CPU、内存、网络、磁盘、进程等实验场景;
  • Java 应用:比如数据库、缓存、消息、JVM 本身、微服务等,还可以指定任意类方法注入各种复杂的实验场景;
  • C++ 应用:比如指定任意方法或某行代码注入延迟、变量和返回值篡改等实验场景;
  • Docker 容器:比如杀容器、容器内 CPU、内存、网络、磁盘、进程等实验场景;
  • Kubernetes 平台:比如节点上 CPU、内存、网络、磁盘、进程实验场景,Pod 网络和 Pod 本身实验场景如杀 Pod,Pod IO 异常,容器的实验场景如上述的 Docker 容器实验场景;
  • 云资源:比如阿里云 ECS 宕机等实验场景。

ChaosBlade 社区欢迎各位加入,我们一起讨论混沌工程领域实践或者在使用 ChaosBlade 过程中产生的任何想法和问题。

作者简介

叶飞:Github @tiny-x,开源社区爱好者,ChaosBlade Committer,参与推动 ChaosBlade 混沌工程生态建设。
穹谷:Github @xcaspar,ChaosBlade 项目负责人,混沌工程布道师。

 

原文链接
本文为阿里云原创内容,未经允许不得转载。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/515072.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

存储基础:磁盘 IO 为什么总叫你对齐?

‍‍来源 | 奇伢云存储头图 | 下载于ICphoto存储 IO 重要的一个知识点划重点&#xff1a;存储 IO 要对齐。资深存储人员为啥总叫你注意 IO 对齐的&#xff1f;机械磁盘 IO 为什么要 512 对齐呢&#xff0c;SSD 盘为啥要 4K 对齐&#xff1f;不对齐又会如何&#xff1f;重要的知…

如何理解这6种常见设计模式?

简介&#xff1a; 设计模式能够帮助我们优化代码结构&#xff0c;让代码更优雅灵活。有哪些常见的设计模式&#xff1f;如何合理运用&#xff1f;本文分享作者对工厂模式、单例模式、装饰模式、策略模式、代理模式和观察者模式的理解&#xff0c;介绍每种模式的模式结构、优缺点…

构建在线教育弹性高可用视频处理架构实战

简介&#xff1a; 对于负责建设视频处理系统的技术团队而言&#xff0c;这样的业务场景就留给了他们一系列的挑战。 前言 近些年&#xff0c;在线教育行业飞速发展&#xff0c;为整个社会的知识传播提供了前所未有的便利性。通过多种形式的在线教育平台&#xff0c;学员与教师…

一文解开java中字符串编码的小秘密

简介&#xff1a; 在本文中你将了解到Unicode和UTF-8,UTF-16,UTF-32的关系&#xff0c;同时你还会了解变种UTF-8&#xff0c;并且探讨一下UTF-8和变种UTF-8在java中的应用。 简介 在本文中你将了解到Unicode和UTF-8,UTF-16,UTF-32的关系&#xff0c;同时你还会了解变种UTF-8&…

Gartner数据劲爆:阿里全球第三,华为中国第二!

看了一份数据&#xff0c;非常振奋人心&#xff0c;给大家分享一下。国外著名信息分析公司 Gartner&#xff0c;4月21号发布了一份数据&#xff0c;瞬间引发了朋友圈是刷屏。这份数据是讲什么的呢&#xff1f;云计算&#xff01;可能由于疫情&#xff0c;很多公司上云的热情变得…

程序员:写作能收获什么?

简介&#xff1a; 很多程序员已经通过自己的个人博客或者公众号来进行技术沉淀&#xff0c;记录自己的成长。越来越多的程序员们也开始意识到了写作的重要性。程序员为什么需要写作&#xff1f;写作能带来什么收获&#xff1f;又有哪些额外的惊喜&#xff1f;本文介绍三位长期坚…

腾讯云~Redis6.2.6 伪集群 哨兵模式_搭建

文章目录一、redis准备3节点1. 创建目录2. 节点1~配置3. 节点2~配置4. 节点3~配置5. 启动redis二、新增sentinel配置1. sentinel_01.conf2. sentinel_02.conf3. sentinel_03.conf4. sentinel 启动5. sentinel 监控6. 哨兵验证一、redis准备3节点 1. 创建目录 mkdir /usr/loca…

教你 4 步搭建弹性可扩展的 WebAPI

简介&#xff1a; 本文整理自《Serverless 技术公开课》&#xff0c;关注“Serverless”公众号&#xff0c;回复“入门”&#xff0c;即可获取 Serverless 系列文章 PPT。 作者 | 萧起 阿里云云原生团队 本文整理自《Serverless 技术公开课》&#xff0c;关注“Serverless”公…

从 0 到 1,高德 Serverless 平台建设及实践

来源 | Serverless作者 | 邓学祥头图 | 下载于东方IC导读&#xff1a;高德从 FY21 财年开始启动 Serverless 建设&#xff0c;至今一年了&#xff0c;高德 Serverless 业务的峰值超过十万 qps 量级&#xff0c;平台从 0 到 1&#xff0c;qps 从零到十万&#xff0c;成为阿里集团…

看动画学算法之:排序-快速排序

简介&#xff1a; 快速排序也采用的是分而制之的思想。那么快速排序和归并排序的区别在什么地方呢&#xff1f; 归并排序是将所有的元素拆分成一个个排好序的数组&#xff0c;然后将这些数组再进行合并。 而快速排序虽然也是拆分&#xff0c;但是拆分之后的操作是从数组中选出一…

思考、创新、坚持——阿里做了七年前端,我的成长经验分享

在成长的未知道路上&#xff0c;我们总会遇到各种各样的问题&#xff0c;但是&#xff0c;所有的迷茫与逆境都能够帮助我们成长&#xff0c;我们要抓住每一个机会让自己进步&#xff0c;而不是徘徊不前。 淘系前端开发同学——林晚&#xff0c;今天就来和大家分享他这七年的成长…

存储进阶:怎么才能保证 IO 数据的安全?

来源 | 奇伢云存储头图 | 下载于视觉中国写成功了数据就安全了吗&#xff1f;思考一个问题&#xff1a;写数据做到什么程度才叫安全了&#xff1f;就是&#xff1a;用户发过来一个写 IO 请求&#xff0c;只要你给他回复了 “写成功了”&#xff0c;那么无论机器发生掉电&#x…

设计稿生成代码与 Serverless 的前世今生与未来!

简介&#xff1a; 云栖大会云上 Hello World 活动火热进行中&#xff01;每位参与者都可收获一份阿里云出品的全球唯一序列号纪念证书&#xff01; 一场脑洞实验 云栖大会云上 Hello World 活动火热进行中&#xff01;每位参与者都可收获一份阿里云出品的全球唯一序列号纪念证…

ARMS在APM工具选型中的实践

简介&#xff1a; 当前的系统在数字化转型需求以及互联网架构实施的影响下&#xff0c;越来越普遍地使用了微服务架构&#xff0c;我们在享受微服务带来的好处&#xff08;开发效率高&#xff0c; 独立部署&#xff0c; 水平扩展&#xff0c; 故障与资源隔离等等&#xff09;外…

无人机、IoT 设备都有漏洞?专访以色列老牌安全公司 Check Point|拟合

从无序中寻找踪迹&#xff0c;从眼前事探索未来。 2021 年正值黄金十年新开端&#xff0c;CSDN 以中立技术社区专业、客观的角度&#xff0c;深度探讨中国前沿 IT 技术演进&#xff0c;推出年度重磅企划栏目——「拟合」&#xff0c;通过对话企业技术高管大咖&#xff0c;跟踪报…

从零入门 Serverless | 函数计算的可观测性

简介&#xff1a; 本文主要分为三个部分&#xff1a;概述中介绍可观测性的基本概念&#xff0c;主要包括 Logging、Metrics、Tracing 三个方面&#xff1b;然后详细介绍函数计算上的 Logging、Metrics、Tracing&#xff1b;最后以几个常见场景为例&#xff0c;介绍在函数计算中…

宜家:打造新零售时代的智能客户身份管理系统

简介&#xff1a; 宜家选择了阿里云应用身份服务&#xff08;IDaaS&#xff09;来为其提供一个包括统一认证、统一账户管理的CIAM解决方案&#xff0c;为所有前端提供统一的安全、可扩展和可靠的身份认证服务&#xff0c;包括灵活的认证配置、单点登录、多因素认证、社交平台登…

生意参谋牵手Quick BI 让数据再次驱动店铺经营

刚刚过去的一周&#xff0c;超两百家店铺体验了阿里巴巴官方全渠道、全链路、一站式数据平台生意参谋推出的全新功能&#xff0c;自助分析。 作为生意参谋联合Quick BI的初次尝试&#xff0c; “自助分析”面向店铺提供自助分析解决方案&#xff0c;支持店铺个性化数据报表制作…

到底是谁发明了物联网?

来源 | 鲜枣课堂作者 | 小枣君头图 | 下载于视觉中国1965年的越南战场&#xff0c;美军正深陷战争泥潭。突然有一天&#xff0c;北越士兵在胡志明小道发现了一些奇怪的东西。这些东西看上去像树枝&#xff0c;但实际上由金属构成&#xff0c;里面包含一些神秘的电子元件。这些士…

八种经典排序算法总结

前言 算法和数据结构是一个程序员的内功&#xff0c;所以经常在一些笔试中都会要求手写一些简单的排序算法&#xff0c;以此考验面试者的编程水平。下面我就简单介绍八种常见的排序算法&#xff0c;一起学习一下。 一、冒泡排序 思路&#xff1a; 比较相邻的元素。如果第一…