关于控制台:上线控制台降低使用门槛|Higress-100-RC-版本发布

前言历时 5 个多月,Higress 推出了首个 RC (Release Candidate,即正式公布的候选)版本 1.0.0-rc,用户能够通过控制台,开箱即用地应用云原生网关。 选用 Higress 作为云原生网关的外围劣势如下:• 易用性“云原生”曾经不再是一个陈腐词,但企业对云原生技术的学习应用老本仍有许多顾虑,对云原生新规范的追赶又有很多焦虑;Higress 同时提供了本地装置/生产部署的 quickstart[1],能够一键部署,并通过控制台操作疾速上手;基于简略易用的控制台,Higress 能够封装 Ingress/Gateway API 的规范细节,根治技术追赶焦虑。 残缺内容请点击下方链接查看: https://developer.aliyun.com/article/1197835?utm_content=g_10... 版权申明:本文内容由阿里云实名注册用户自发奉献,版权归原作者所有,阿里云开发者社区不领有其著作权,亦不承当相应法律责任。具体规定请查看《阿里云开发者社区用户服务协定》和《阿里云开发者社区知识产权爱护指引》。如果您发现本社区中有涉嫌剽窃的内容,填写侵权投诉表单进行举报,一经查实,本社区将立即删除涉嫌侵权内容。

May 9, 2023 · 1 min · jiezi

Spark-Relational-Cache实现亚秒级响应的交互式分析

本场视频链接:https://developer.aliyun.com/live/1548?spm=a2c6h.12873581.0.0.71671566Xloy3Z&groupCode=apachespark 本场PPT资料:https://www.slidestalk.com/AliSpark/SparkRelationalCache2019_57927 本次分享主要分为以下四个方面: 项目介绍技术分析如何使用性能分析一、项目介绍项目背景阿里云EMR是一个开源大数据解决方案,目前EMR上面已经集成了很多开源组件,并且组件数量也在不断的增加中。EMR下层可以访问各种各样的存储,比如对象存储OSS、集群内部自建的HDFS以及流式数据等。用户可以利用EMR处理海量数据和进行快速分析,也能够支持用户在上面做机器学习以及数据清洗等工作。EMR希望能够支撑非常大的业务数据量,同时也希望能够在数据量不断增长的时候,能够通过集群扩容实现快速数据分析。 云上Adhoc数据分析痛点在云上做Adhoc数据分析的时候,很难实现随着数据量的增长使得查询的延迟不会大幅度增加。虽然目前各种引擎不断出现,并且某些引擎在一些场景下运行很快,但是数据量变大之后,查询响应速度难免有所下降,因此希望在比较统一的平台之上获得较好的性能。与此同时,阿里云也希望能够提供云原生的解决方案。Spark是目前工业界使用较多的计算引擎,应用非常广泛,但是在处理Adhoc上还是存在很多不足之处,因此阿里云在Spark上做了大量优化,帮助用户满足Adhoc查询的需求。因此就会涉及到缓存方案,虽然Spark中很早就有了缓存机制,但想要满足云上Adhoc场景却存在很多不足之处,因此阿里云会在Spark上做大量优化,帮助用户优化Adhoc查询速度。但是如果把数据放到内存中,将所有数据全部用作缓存可能也不足够,因此就催生出了Spark Relational Cache。 Spark Relational Cache用户的SQL请求过来之后,到了Spark上面,会需要比较长的时间在数据来源上进行处理,这里下层的存储包括集群的HDFS以及远端的JindoFS和阿里云OSS等。当有了Spark Relational Cache之后,查询过来之后会查询是否能够用到存储在Relational Cache中缓存的数据,如果不能用到则会转发到原生路径上,如果能用到则会用非常快的速度从缓存里面将数据读取出来并将结果返回给用户。因为Relational Cache构建在高效存储之上,通过用户的DDL将数据变成Relational Cache。 Spark Relational Cache特点Spark Relational Cache希望能够达到秒级响应或者亚秒级响应,能够在提交SQL之后很快地看到结果。并且也支持很大的数据量,将其存储在持久化的存储上面,同时通过一些匹配手段,增加了匹配的场景。此外,下层存储也使用了高效的存储格式,比如离线分析都会使用的列式存储,并且对于列式存储进行了大量优化。此外,Relational Cache也是用户透明的特性,用户上来进行查询不需要知道几个表之间的关系,这些都是已经有过缓存的,不需要根据已有的缓存重写Query,可以直接判断是否有可以使用的Relational Cache,对于一个厂商而言只需要几个管理员进行维护即可。Spark Relational Cache支持自动更新,用户不需要担心因为插入了新的数据就使得Cache过时导致查询到错误的数据,这里面为用户提供了一些设置的规则,帮助用户去进行更新。此外,Spark Relational Cache还在研发方面,比如智能推荐方面进行了大量探索,比如根据用户SQL的历史可以推荐用户基于怎样的关系去建立Relational Cache。 二、技术分析阿里云EMR具有很多核心技术,如数据预计算、查询自动匹配以及数据预组织。 数据预计算数据在很多情况下都有一个模型,雪花模型是传统数据库中非常常见的模型,阿里云EMR添加了Primary Key/Foreign Key的支持,允许用户通过Primary Key/Foreign Key明确表之间的关系,提高匹配成功率。在数据预计算方面,充分利用EMR Spark加强的计算能力。此外,还通过Data Cube数据立方来支持多维数据分析。 执行计划重写这部分首先通过数据预计算生成预计算的结果,并将结果存储在外部存储上,比如OSS、HDFS以及其他第三方存储中,对于Spark DataSource等数据格式都支持,对于DataLake等热门的存储格式后续也会添加支持。在传统数据库中有类似的优化方案,比如物化视图方式,而在Spark中使用这样的方式就不合适了,将逻辑匹配放在了Catalyst逻辑优化器内部来重写逻辑执行计划,判断Query能否通过Relational Cache实现查询,并基于Relational Cache实现进一步的Join或者组合。将简化后的逻辑计划转化成为物理计划在物理引擎上执行。依托EMR Spark其他的优化方向可以实现非常快速的执行结果,并且通过开关控制执行计划的重写。 自动查询匹配这里有一个简单的例子,将三个表简单地Join在一起,经过过滤条件获得最终的结果。当Query过来之后先判断Spark Relational Cache是否能够符合需求,进而实现对于预先计算好的结果进行过滤,进而得到最终想要的结果。 数据预组织如果将数十T的数据存在存储里面,那么从这个关系中获取最终的结果还需要不少的时间,因为需要启动不少的Task节点,而这些Task的调度也需要不少的开销,通过文件索引的方式将时间开销压缩到秒级水平,可以在执行时过滤所需要读取的文件总量,这样大大减少了任务的数量,这样执行的速度就会快很多。因为需要让全局索引变得更加有效,因此最好让数据是排过序的,如果对于结构化数据进行排序就会知道只是对于排列在第一位的Key有一个非常好的优化效果,对于排列在后面的Key比较困难,因此引入了ZOrder排序,使得列举出来的每个列都具有同等的效果。同时将数据存储在分区表里,使用GroupID作为分区列。 三、如何使用DDL对于简单的Query,可以指定自动更新的开关,并起一个名字方便后续管理。还可以规定数据Layout的形式,并最终通过SQL语句来描述关系,后续提供给用户WebUI一样的东西,方便用户管理Relational Cache。 数据更新Relational Cache的数据更新主要有两种策略,一种是On Commit,比如当依赖的数据发生更新的时候,可以将所有需要添加的数据都追加写进去。还有一种默认的On Demand形式,用户通过Refresh命令手动触发更新,可以在创建的时候指定,也可以在创建之后手工调整。Relational Cache增量的更新是基于分区实现的,后续会考虑集成一些更加智能的存储格式,来支持行级别的更新。 四、性能分析Cube构建阿里巴巴的EMR Spark对于1T数据的构建时间只需要1小时。 查询性能在查询性能方面,SSB平均查询耗时,无Cache时查询 时间按Scale成比例增加,Cache Cube后始终保持在亚秒级响应。 ...

November 5, 2019 · 1 min · jiezi

容器服务Windows-Kubernetes使用阿里云日志服务来收集容器日志

目前,容器服务Windows Kubernetes支持将业务容器产生的stdout输出、日志文件同步到阿里云日志服务(SLS)进行统一管理。 支撑组件安装在Windows Kubernetes集群安装界面勾选使用日志服务,集群会安装支持日志收集的必要组件logtail。 集群安装完毕后,可以在日志服务控制台 查看到按k8s-sls-{Kubernetes 集群 ID}形式命名的工程。收集到的业务容器日志都会放在该工程下。 使用YAML模版部署业务容器YAML 模板的语法同 Kubernetes 语法,但是为了给容器指定采集配置,需要使用 env 来为 container 增加采集配置和自定义 Tag,并根据采集配置,创建对应的 volumeMounts 和 volumns。以下是一个简单的 Deployment 示例: apiVersion: extensions/v1beta1kind: Deploymentmetadata: labels: app: logtail-test name: logtail-testspec: replicas: 1 template: metadata: labels: app: logtail-test name: logtail-test spec: containers: - name: logtail image: registry-vpc.cn-hangzhou.aliyuncs.com/acs/windows-logtail:1809-1.0.0.4 command: ["powershell.exe"] args: [cmd /k "ping -t 127.0.0.1 -w 10000 > C:\log\data.log"] env: ######### 配置 环境变量 ########### - name: aliyun_logs_log-stdout value: stdout - name: aliyun_logs_log-varlog value: C:\log\*.log - name: aliyun_logs_log_tags value: tag1=v1 ################################# ######### 配置vulume mount ####### volumeMounts: - name: volumn-sls-win mountPath: c:\log volumes: - name: volumn-sls-win emptyDir: {} ############################### nodeSelector: beta.kubernetes.io/os: windows其中有三部分需要根据您的需求进行配置,一般按照顺序进行配置。 ...

June 20, 2019 · 1 min · jiezi

ECS事件通知之创建失败事件

ECS提供了批量实例创建接口(https://help.aliyun.com/document_detail/63440.html ),可以一次调用创建最多100台实例。批量创建接口可以完成批量实例的创建、启动、IP分配等流程,可以快速完成实例资源的扩容。 在实例的创建过程中(实际后台异步创建),库存和VSwitch中私网IP的可用数量是动态变化的,在创建过程中有可能资源不满足创建需求,导致实例的创建过程失败,实例创建失败后会有两种结果: 1.实例会被自动释放,某些典型的错误,例如库存不足,会触发实例的自动释放,实例释放后会从实例列表中消失,用户很难感知实例的创建失败以及原因。 2.实例会保持创建中状态,并且可以操作实例的释放(包年包月实例要通过工单申请),这种是对自动运维非常不友好的逻辑,实例的生命周期并没有闭环。 实例创建失败事件实例创建失败事件会对上面两种流程自动化运维,首先会进行实例的自动化释放(实例在这种状态下是被评估为无法创建成功的),开始释放流程后ECS会发出实例创建失败事件,此时实例在实例列表中是可以被查询到的;当实例最终被释放时,实例创建失败事件会变为已执行状态,之后实例在实例列表中无法查询(实例已经释放)。 对于已经发生了“创建失败事件”的实例,可以有两种选择: 一个是不进行任何操作,后续在10分钟内,实例会被自动释放;一个是可以主动进行释放,主动释放后,实例创建失败事件会被修改为已避免状态。之后实例的相关事件都可以在历史事件查询接口进行查询。 事件通知的内容格式实例创建失败事件的内容包含实例创建失败的时间点、完成时间、实例id等信息,消息体示例如下: { "status":"Executing", "ver":"1.0", "product":"ECS", "id":"4AB76E05-B5E2-4A00-A107-B0B6A5091967", "resourceId":"acs:ecs:cn-hangzhou:16343********0:instance/i-bp********gzahwqfvx", "level":"CRITICAL", "instanceName":"", "ruleName":"test", "userId":"16343********0", "name":"Instance:SystemFailure.Delete:Executing", "eventTime":"20190611T130915.171+0800", "regionId":"cn-hangzhou", "content":{ "notBefore":"2019-06-11T05:09:14Z", "executeStartTime":"2019-06-11T05:09:14Z", "publishTime":"2019-06-11T05:09:14Z", "eventId":"e-bp1ivh*********c", "instanceId":"i-bp1jfp*********x", "eventType":"SystemFailure.Delete", "eventStatus":"Executing" }}如何订阅消费用户可以在云监控控制台事件订阅完成事件的通知,请查看文档《设置事件通知》 用户可以通过对实例创建失败的消息订阅,在发生实例创建失败之后,马上感知到实例创建失败的发生,可以尽快调整实例的创建参数(实例规格、VSwitch)来避免库存不足和私网IP不足发生,提高实例创建的成功率。 本文作者:龙辚阅读原文 本文为云栖社区原创内容,未经允许不得转载。

June 18, 2019 · 1 min · jiezi

基于Knative开发应用

目录安装 Istio安装 Knative玩转 helloworld-goWordPress 实战创建 Kubernetes 集群确保 Kubernetes 集群创建的时候已经选择了启用日志服务确保 Kubernetes 集群和 OSS 在一个 regionKubernetes 集群创建的时候需要开启 kube-apiserver 公网访问提前帮用户配置好 kubeconfig 命令行安装 Istio安装 Istio 时注意以下几点: 默认要安装 gateway日志服务和 Xtrace 要提前开通,Istio 需要使用 ZipKin v1 向 Xtrace 汇报监控数据在容器服务集群管理页面可以直接在目标集群上部署 Istio 安装 Knative选择好目标集群使用一键部署功能直接安装即可, 安装文档 玩转 helloworld-go配置日志采集策略部署 Helloworld监控告警调用链压测数据展示日志管理日志服务控制台: https://sls.console.aliyun.com本示例以容器标准输出采集为例进行展示,详细设置步骤可以参考日志服务文档根据 Kubernetes 集群 ID 找到对应的日志服务 Project创建一个新的 Logstore设置数据导入方式 选择 Docker标准输出 配置容器标准输出日志采集策略{ "inputs": [ { "detail": { "IncludeEnv": { "K_SERVICE": "helloworld-go" }, "IncludeLabel": {}, "ExcludeLabel": {} }, "type": "service_docker_stdout" } ], "processors": [ { "detail": { "KeepSource": false, "NoMatchError": true, "Keys": [ "time", "level", "msg" ], "NoKeyError": true, "Regex": "(\\d+-\\d+-\\d+ \\d+:\\d+:\\d+)\\s+(\\w+)\\s+(*)", "SourceKey": "content" }, "type": "processor_regex" } ]}分别为相应的键值 time、level 和 msg 设置数据类型 ...

June 17, 2019 · 2 min · jiezi

Nacos-Committer-张龙Nacos-Sync-的设计原理和规划

与你同行,抬头便是星空。 本文整理自Nacos Committer 张龙的现场分享,阿里巴巴中间件受权发布。 随着 Nacos 1.0.0 稳定版的发布,越来越多的企业开始在测试/预演/生产环境中逐步部署 Nacos。目前,除了部分企业已处于转型分布式架构的过程中,会考虑直接使用 Nacos 上生产,但仍有不少企业会考虑一些比较现实的问题: 存量用户如何迁移注册中心到 Nacos?多区域注册中心之间如何同步?已有注册中心与 Nacos 如何并存使用?这里,我将通过对 Nacos Sync 的介绍,来回答这三个问题。 Nacos Sync 是什么?Nacos Sync 是一个支持多种注册中心的同步组件,基于 SpringBoot 开发框架,数据层采用 Spring Data JPA,遵循了标准的 JPA 访问规范,支持多种数据源存储,默认使用 Hibernate 实现,更加方便的支持表的自动创建更新。 下图是 Nacos Sync 系统的概念图,Nacos Sync 通过从各个注册中心拉取注册的服务实例数据同步到 Nacos,左右两边是不同的注册中心,绿色代表目前是可以进行双向同步的,蓝色代表暂时只能进行单向同步。 Nacos Sync 使用了高效的事件异步驱动模型,支持多种自定义事件,使得同步任务处理的延时控制在3s,8C16G的单机能够支持6K的同步任务。 除了单机部署,Nacos Sync 也提供了高可用的集群部署模式,作为无状态设计,支持将任务等状态数据迁移到了数据库,使得集群扩展非常方便。 系统模块架构下图是 Nacos Sync 目前的系统架构图,画的比较简单,只是把一些比较重要的模块做了描述。 Web Console: 提供给用户进行注册中心和同步任务进行相关界面操作 Processor Frame: 注册中心和任务的业务处理逻辑 Timer Manager: 定时轮询数据库获取同步任务进行处理 Event Frame: 异步事件来进行同步任务的同步以及删除 Extension: 对接各种注册中心客户端的扩展实现 整体调用流程我们来看一下 Nacos Sync 一次完整的调用流程: ...

May 13, 2019 · 1 min · jiezi

我在阿里云做前端

前言今年是我毕业的第10个年头,半路出家做了前端,title一直是前端,你可以说我很专注,有时候也有些遗憾。一直以来,当别人问起你是做什么的,我说前端或者全栈,别人说:哦,做页面的啊!心里难免有些失落。前端是个资源型角色,在认知里对业务的理解深度不够,加上通常负责业务领域很广,比较难有积累和沉淀。如果你问一个毕业10年的JAVA老司机,他跟你谈的一定是大流量下的分布式架构,而前端可能还是昨天茶余饭后讨论vue和react,或者是angular谁更强。如何突破,如何提供业务更多价值,前端们一直在苦苦探寻。网上很多文章,给人启发,但每个人面对的环境,负责的业务不同,不一定都适用。结合自己过去几年在阿里云的前端经验,也做个总结。1.0版本-工具&团队今年是我来阿里云的第五个年头了,从没有想过会在一个公司呆的如此之久,更没想过我能在一个岗位上沉淀4、5年。刚入职在阿里云控制台团队,主要负责云盾控制台、drds控制台等,开发过程中发现大部分场景是「表格」、「表单」,为了避免自己不断重复开发,封装了simpleForm以及控制台cli脚手架,可以做到新开发控制台一键敲定(这个脚手架直到去年还有人问我如何用……也是经久不衰)。这个时候也萌生了做个ide,可视化搭建UI视图,不过限于精力和当时团队的方向,且当时vscode还没今天这么流行,没有尝试,比较遗憾。不过做WebIDE这个点,算是在心里种下了。后由于组织结构调整,我从控制台团队独立出来,负责当时的网站运营方向,开始比较艰难的从0-1组件团队过程。当时业务相对比较简单,主要是:阿里云官网以及官网的Nodejs、云市场业务。由于在控制台团队主要用的angularjs,感觉上手成本比较大,在建立新团队,以及我自己可以选择的时候,React成了我的首选。当时vue还没成熟,其实能选的也只有react。新的技术体系,需要有配套的工程化体系,当时Def还处在1.0时期,为了稳定以及减少开发成本,很自然我们在xef上做了插件式开发,结合自己业务特性,分装了响应的模板,以及定制了开发周期。后由于xef1.0升级2.0,导致我们工具的不稳定,且改动非常大,逐步将我们的工程化体系独立,这就有了后续的DBL(当时叫屌爆了)。我跟老板做汇报时,老板觉得这名字上不了大雅之堂,还硬是憋了个比较有内涵的名字——实在不好记,我现在都记不起来了。这个阶段,我们做了很多技术上的尝试,团队成员都非常苦逼,也非常有激情。团队基础设施建设,我们一直在优化,随着Dawn的基于中间件式的pipeline方式设计,可以说是将工程化做到一个比较高的高度,未来不管是webpack升级到多少,或者新的打包工具出现,对我们来说影响都比较小。面向未来的模式,让我们只需要修改内核,使用者无感知。新的工程化方案也积极跟阿里云其他团队沟通和交流,之前跟风驰和释然也达成一致意见作为阿里云统一构建工具推进,不过落地的不是很好。同时,新的方案也完全遵循集团的标准,跟淘宝阿大团队无缝对接。另外还有一个好处是:dawn不局限在react,你可以使用任何框架;dawn不局限在redux,你可以使用任何你喜欢的数据管理,实际上我们自己有用mota,mobx,graphql-apollo等等。Dawn连接:https://github.com/alibaba/dawn讲完工程化,其实应该讲讲组件化,这是个无法回避的问题,但这对我们来说也是个艰难的过程。15年的时候,我们用过antd(已开源),但是在上层做了一层业务封装;后来fusion开始盛行,在跟ued沟通后,考虑到集团统一,用了一段时间的fusion(已开源);最后我们还是选择了自建组件库,这是一个很无奈的举动。具体细节不表,其中一个重要原因是我们的前台业务需要「阿里云自己的设计元素」,在经过团队很长时间的建设,APS组件库已经作为团队组建库的基础,在其之上构建了业务组件,并在之上构建了业务解决方案。除了折腾传统前端领域,也尝试做了很多跨栈的事情。在我所负责的业务中,由于「端」业务的确实,我们更多的是偏「全栈」。前端同学做全栈,讲实话是不行的——绝大部分前端同学在架构、运维部署方面还是经验偏少,考虑更多的是跟展现层相关。在全栈路径上,由于我们负责的是核心交易链路,我们没有用大家熟悉的nodejs,而选择跟后端一样的语言——Java。做这个决策,其实是挺困难的,也是有故事的。原先有个系统,前端同学用Nodejs写的,但由于业务非常复杂,加上前端一直是个资源瓶颈的角色,一个人干三个人的活,所以这个同学最后搞不定,离职了。这么个系统就变成了后端想接无法接,前端「没人力」接的状况,非常尴尬。从那以后,业务系统中就决定了直接使用Java。在全栈路上,我们主要有两个策略:大前端下自己写部分业务的Java利用serverless通过代理统一配置化转大前端写Java,阿里云其实非常多的前端都已经具备了这个能力,我自己也有独立开发几个系统从0-1上线,分布式部署且支持国际化的经历。做了一段时间后发现,其实效率上还好,并没有传说中nodejs比Java要高效很多的体感.利用serverless通过代理统一配置化转,有段时间看社区有部分人提到Graphql,对此产生了兴趣,就顺便了解了下,通过代理的方式可以将后端数据转换成前端需要的格式,非常吸引人,也就一下子扎进去。我自己也同时做了Nodejs的版本给团队同学普及,同时做了Java版本的demo给后端普及。同时了解到b2b的Mbox平台跟我们想要的能力比较像,找过他们给我们分享,但由于业务系统整个搭建在他们平台有一定得风险,于是决定了自建代理平台,这也是「云查询」平台的背景。云查询主要是战锋主导并推进落地的,在集团内取得了不错的影响力,很多BU很多部门去做过分享。在业务上,通过云查询,我们实现了不用管应用的运维和部署,实现业务逻辑和接口的转换,并自动扩容。尤其是营销体系,在元策&隐天和战锋得协同下,取得了比较大的效率提升,并支持了阿里云去年的双十一。具体「云查询」文章介绍可以看我另外一篇文章。云查询经过一段比较长时间的发展,我们已经逐步将它作为基础能力下沉,在云查询的serverless之上长出了不同的「轻应用」,以支持不同的垂直业务场景。比如:可视化搭建领域「页橱」、基于权限&角色的中后台解决方案「Flex」等;还记得我之前说过5年前我想做WebIde,没有开始;2年前,看到其他云厂商有WebIDE,我们由于业务压力,业务压力没有搞成;今年我们总算是有一点启动,已经和appStudio的同学在共建,基于appStudio基础之上把我们的dawn、云查询做打通,做云端集成化、一站式的研发体验。通过以上的技术基础建设,已经为我们构建了很好的基础基础。业务布局对应着团队、组织的建设。过去几年,团队从0-1建设,到目前xx个在岗同学,形成了4个梯队,三个3业务方向&一个技术架构方向,一路走来,感觉带团管理水很深,很多时候不是说你带的人越多越好,最近看到一本书提到一个词「情感成熟」,这个非常重要。一个技术好,做业务的好手未必能管理好团队,在不同阶段需要适应不同角色的要求,最重要的是时刻保持忧患意识、保持持续学习。在团队建设时,需要重点区分manager和leader,尤其是业务团队我们更希望成为leader,去带着做业务,而不仅仅是做绩效管理。2.0,也就是过去一年,我们在业务思维指导下,owner了部分业务,并利用横向的技术打通、横向的业务思维,取到了一些成果,接下来进入2.02.0业务思维-以横向视角推进业务赋能我们通常把组织中的人分为:一字型、|字型、T字型、+字型。前端正好是—字型团队,负责的业务非常广,而前端又是个非常稀缺的岗位,招聘很困难,所以盘子越大资源瓶颈越明显。「一字型」角色团队,典型的问题就是对业务的深度理解不够,单纯从技术层面去做营销的搭建、中后台的可视化,结果都不尽如人意。这么说起来,可能你觉得没法往下看了,天花板在那里,如何突破其实并没有太多可参考的例子。我写这篇总结,正是有些这样的感悟,希望给大家做一些输入,帮助大家去思考。「一字型」虽然从业务上看我们的深度不够,但从专业技能看我们是标准的「|字型」。前端经过这10来年的发展,语言、框架、工具已经逐步趋于稳定,各种端的性能也越来越流畅,生态非常活跃,任何你碰到的困难相信社区都已经有比较成熟的方案。前端生态快速发展的10年,也验证了我们这些人有着非常强大的学习能力,7天一个框架、3天一个数据库估计都不是太大难事(略夸张,但表达的是这么个意思)。前端直接对接客户,对客户体验的敏感、对流程数据化的敏感、对业务逻辑流程的感知,都是我们生存的根,也是我们独一无二的能力,这个根我们不能丢。有句话叫:饱暖思淫欲,不太恰当,姑且一比。在前端纵深领域的深耕,让我们成为了「紧缺资源」,随着工具的完善,前端们也更希望利用技术为业务赋能。如何赋能?挡在我们面前的是「意识」。在营销中,认知、需求、品牌、品类、价格五个要素中,「认知」最为重要。比如阿里是做电商的、腾讯是做社交的、百度是做搜索的,bat在自己主营业务范畴不断布局,构建了庞大的生态,做过很多尝试,但看起来最终还是围绕本身的基因做生态投资成功率要高一些。那我们想要业务赋能,瓶颈就在于「你个切页面」也要赋能吗?好好做好体验、提效不好吗?我认为「体验、提效」这是前端最核心的能力,也是毕生都努力要实现的目标,坦白讲我们没法马上解决资源瓶颈问题,毕竟现在毕业生都在应聘算法、ai、人工智能;我们也没办法搞一轮体验提升计划;这是个很漫长的过程。但如果我们能以业务的角度出发,去发现问题进而辅助以技术手段解决,并沉淀平台,应对未来千变万化的需求,可能更为实际一些。做为团队的TL,除了在专业上给与同学「|」型的能力纵深,也更希望带着团队同学获得更多业务体感。离开业务谈技术、谈中台都是空中楼阁;离开业务谈前端,注定只能是重复造轮子,而这种低水平的重复正在发生,且可能会持续很久。在很长一段时间里,我都试图把我们「一字型」业务广度做个抽象和融合,希望把「点状」形成「线」,进而形成整体「面」解决方案。我所负责的业务中,主要有4个大方向:官网&营销—for长尾商业化流程后台-for 小二核心售卖流程—核心能力层销售、合作伙伴官网&营销:主要包含获客、激活、转换、留存几个节点,构建高效的「人」、「货」、「场」。很长一段时间里,阿里云的内容维护、营销大促都是基于集团CMS来的。传统大促会场、卡片的方式,前端挖坑后运营编辑内容,而阿里云的「商品」跟淘系有着比较大的差别,另外我们也没有招商、选品的体系,导致这种简单人肉运行的大促方式存在很多弊端,比如不高效、不复用、不能做个性化、数据流程监控力度不够精细等。此外「投放」能力的建设还不够,没有办法做到精细化的人群做精准的营销内容投放。为了解决这些问题,去年开始,由前端团队和pd一起推进完善的营销体系建设:在原有商品的基础上,构建了「营销商品」的概念。更抽象的「货」,且可视化在线配置直接拉取了实时价格和库存。通过我们1.0工具建设的dawn,打通开发流程,使得整个开发链路一致,成本更低。可抽象的货匹配上专门为货打造的UI视图,形成场景能力沉淀。构建ACE(Alibaba Cloud Experience)架构体系,打通搭建体系,通过技术降级打通各类「场」,更好的承载好营销商品的投放。通过全链路场景的曝光,点击,转化,以及最终成交的商品信息等数据的积累,生成用户画像,提供内容场景化方案(在不同场景中精确得向用户展示商品或信息)完善「人」的定位。商业化商品配置:上面提到「营销商品」时提到「基础商品」。目前阿里云基础商品主要分为:「公有云商品」和「技术输出型」。过去很长一段时间我们偏公有云的能力建设,今年年初才开始逐步融入专有云体系。在商业化系统中,我们的小二需要配置售卖规则、价格,需要定义商品模型;同时复杂的规格之间的约束,异常复杂。如何提高商业化的输入和输出的强体验,我们还有很长的路要走。结合今年的专有云项目,从模板的方式出发,将一类产品做个聚合,简化商品模型配置的步骤,大大提高了配置效率,提高体验。销售&合作伙伴:15年刚开始组建团队(这里指的都是前端团队,不是业务团队),15年-18.3月大部门的核心kpi是营收、是首购用户数,主打的是中长尾客户,获得了非常高速的市场增长。后来团队cover范围不断扩大,也负责销售&合作伙伴体系,围绕着「市场营销」、「商机培育」、「商机转化」、「合同履约」构建了我们自己的销售crm系统。toC的业务通常比较好理解,毕竟我们也是c的一员。这段toB的经历,结合业务一号位的培训班,让了解到销售系统的核心,除了工具,最想要的是解决方案,是产品能力的丰富。大概介绍了各个方向的业务,回到我们讨论的主题来——借助横向优势,整合资源&架构提供业务赋能。为了分析他们之间的共性,我们经过很多次的讨论,终于汇聚得到我们的业务流程大图(对外脱敏后的示意图):从这个流程大图中,各个分支最后都需要依赖「售卖能力」,这个售卖能力表现在营销中是「弹窗buy(减少跳出,直接购买)」、购物车(多产品交叉购买、数据算法推荐)、套餐(多产品打包优惠售卖)、提货券(下单和生产分离的售卖能力);表现在销售链路中是「产品配置清单」、「采购单」、「CBM提供给大客户的CTO价格计算器」表现在商品商业化链路中是「模板化」配置清单能力在一大团子中找到业务的共性「售卖能力」,在经历一段时间比较耗资源、耗时的烟囱式开发方式后,抽象出了售卖的核心支持层——紫金阙。这一层,我们定位为业务中台,偏前端层面,也是大前端的领域范畴。唯一需要指出的是,我们用的是Java,没有用nodejs,无其他差别。最后架构如下(脱敏,细节忽略):新的架构模式下,我们减少了大量的前后端沟通,比如「分销采购市场」以传统开发方式需要1-2个月,我们2周就搞定了。新的架构模式,在可预见的未来,可以很好的支持各种营销新玩法,也可以支持销售和合作伙伴的『解决方案』。我想说的是,如果没有我们全量业务的横向视角,我们的抽象方案不会这么通用,这是前端团队的优势。如果没有大前端稳定的技术生态,我们也没机会去做业务赋能。这才是前端的未来,利用横向优势整合,结合某个领域做深做透,形成垂直深度,为业务提供价值,也让我们的技术方案「有的放矢」。前端经常是围绕一个点做需求,得到工具,但无法提供解决方案,因为没有业务属性;唯有结合业务特性,做好沉淀,工具变成平台才能释放更大价值。3.0探索以技术能力为业务提供增值「云计算」核心是解决企业成本的问题,用低成本获得超强的计算、存储能力,获得高并发下弹性扩容的能力。云计算提出了很多概念:IAAS、PAAS、SAAS。。。相对前端角色来讲,体感并不是很强。但是BAAS的出现,让前端眼前一亮。试下想,原先我们需要大量后台开发的应用,逐步都沉淀成领域能力,提供baas服务给前端调用,前端再也不用考虑部署、运维,只关心业务代码,想想也是心动。目前市面上提供类似服务的公司很多,有专门做后台数据存储的如Leancloud、有做数据分析的、有做消息推送的等。所以,Baas会是前端的春天吗?这个拭目以待。扯了理想,我们也说说现状。目前阿里云大概是Buy In Aliyun,我们售卖的是IAAS层的资源,用户核心的业务流程还是基于自己的研发体系。在前端这个纵深领域内,基于云打造「云端一站式研发流程」,将企业前端变成:Work In Aliyun or Dev In Aliyun。通过对企业前端生命周期的分解,通过WebIDE来承载整个流程:1. 将创建关联阿里云的code2.阿里云前端构建工具dawn作为基础构建能力,可定制化团队构建的中间件(webpack、lint、server、mock等)、构建stage(init、dev、test、publish);基于工程化化能力提供统一的规范,提供各种不同应用框架的初始化模板。3.代码点击发布后,自动编译,并发布到cdn。在此基础流程之上,我们提供serverless相关能力,通过调用BaaS领域服务能力,以及FaaS网关触发能力,实际上我们可以完全一站式,且是前端主导的应用开发。还记得我前面提到我们的serverless应用「云查询」,这一层我们逐步进行能力下沉,变成serverless基础能力。各公司几乎都有营销搭建体系,过去搭建的玩法不够多样,主要依托cms能力自行开发,随着现在各种「端」能力的延伸、多样性化,营销搭建也变得越来越复杂。而我们基于「云查询」之上沉淀出的「页橱」搭建体系,完全可以借助「云端一站式研发流程」提供很好的SAAS化服务。这是我们的优势,「云端前端解决方案」也只有我们适合做这个,这里只列举了其中一个场景,类似的机会还有很多。总体感觉,一云多端借助serverless前端的春天已然来临。抓住我们核心的竞争力,并同时发现业务中的问题,跨端推进解决,这是最好的出路。你问我做什么的,我…… 我就是阿里云CPO(首席页面仔啊)ps:阿里云智能业务中台&阿里通信招P6-P8前端,欢迎来撩。base可北京可杭州,杭州工位在美丽的西溪园区哦。旁边挨着的都是UED妹子&测试妹子。xiaoming.dxm@alibaba-inc.com本文作者:城池cc阅读原文本文为云栖社区原创内容,未经允许不得转载。

March 28, 2019 · 1 min · jiezi

如何利用 Webshell 诊断 EDAS Serverless 应用

本文主要介绍 Serverless 应用的网络环境以及 Serverless 应用容器内的环境,了解背景知识以及基本的运维知识后可以利用 Webshell 完成基本的运维需求。Webshell 简介用户可以通过阿里云控制台直接获取 ECS 的 Shell,从而完成自己的运维需求。如果 ECS 内开启了 SSH 服务,且 ECS 存在弹性公网 IP,那么用户也可以在本地通过 SSH 服务获取 ECS 的 Shell 完成运维需求。由于 EDAS Serverless 特殊的架构以及网络环境,用户暂时无法直接从本地通过 SSH 服务获取应用容器的 Shell。在 Serverless 场景中,容器是一个暂态的、供应用运行的环境,一般来说不需要进入运维。为了方便用户进行线上问题定位排查,EDAS 在控制台提供了一个简单的Webshell,供用户查看调试自己的容器。EDAS 默认给出的 Jar War 类型应用的容器基础镜像主要是面向应用运行时,不带有冗余的排查工具,因此对运维人员可能不够友好。对于用户自身的镜像,不需要镜像中启动 SSH 服务,只需要带有可执行的/bin/bash即可。用户自己的镜像可以带上必须的运维工具方便排查。目前 Webshell 不支持 Windows 镜像。EDAS 应用节点的网络环境EDAS 应用节点处于用户自己购买的阿里云 VPC 内。在 EDAS 中,还额外提供了一层中间件服务调用隔离的手段:EDAS 命名空间。EDAS 命名空间与 VPC 内的 VSWITCH 是绑定关系,一个 EDAS 命名空间对应一个 VSWITCH,一个 VSWITCH 可以对应多个EDAS命名空间。VPC 的原理以及基本的产品情况可以在阿里云VPC官方文档了解。简单来讲,VPC 内的 IP 地址为局域网地址,不同 VPC 内的2层以上数据包无法路由到目的地。EDAS 命名空间主要做中间件逻辑隔离,不同命名空间内的应用在中间件层面是隔离的,如服务发现以及配置下发等。由于 VPC 的产品特性以及当前的 EDAS Serverless 的产品特性,容器无法直接触达 VPC 外的服务(阿里云产品除外,如 OSS、镜像服务等)。在没有额外配置的情况下,你的容器运行在网络“孤岛”环境。了解了基本的网络情况,现在可以明白为什么用户无法直接触达自己的容器了。容器内需要访问公网服务,可以通过购买 NAT,并配置 VPC 内 VSWITCH 的SNAT规则即可,详见阿里云Serverless文档。SNAT规则可以让VPC内地址访问公网地址,从而使用公网暴露的服务,获取到公网的资源。EDAS 构建的镜像的方案基于阿里云容器镜像服务,EDAS 集成了为用户构建以及管理镜像的功能。用于构建的基础镜像为centos:7,在此基础上为用户配置好了时区、语言与编码方式、Open JDK 运行环境。容器存在的目的是为了让应用运行起来,EDAS 不可能以占用所有用户运行时资源为代价,集成过多的工具,对于容器内工具有需求的用户,建议自行构建镜像,或者按需从 OSS 拉取。常见的分析手段线上容器的运维一般是不必要的。如果你确定需要进入容器进行运维,请务必了解你的操作对线上业务的风险:对于单点应用,你的行为可能导致容器 OOM,从而导致分钟级别的业务中断,而对于多点部署的业务,上述现象可能造成业务秒级中断。诊断 EDAS 应用一般从这几个方面入手:常规检查,上传搜集的日志。常规检查常规检查的方法比较多,以 Java 应用为例,一般是检查进程、线程以及 JVM 的健康状态。首先执行命令ps -ef | grep java检查你的 Java 进程是否还存在。这里必须特别说明的是,容器内一般需要使用主进程启动你的应用,这样一旦你的应用被kill掉,容器也会退出,EDAS 会将退出的容器重新启动,防止业务中断。如果进程不见了,可以执行命令dmesg | grep -i kill查看OOM相关日志。如果存在日志,那么说明你的应用进程被 kill 掉了,接着检查工作目录下hs_err_pid{PID}.log日志文件,定位具体的原因。Java 类型应用的在线分析可以使用阿里巴巴开源软件 Arthas 解决,建议在测试镜像中集成Arthas工具进行常规诊断。Arthas可以很方便地实时查看类加载情况,观察方法出入参,环境变量等。# 接入arthas,需求打通公网wget https://alibaba.github.io/arthas/arthas-boot.jarjava -jar arthas-boot.jar对于网络层的诊断,在了解上述EDAS应用节点网络情况的前提下,一般可以通过curl -v {host/ip} {port}检查域名解析以及连通性,通过tcpdump抓包观察分析网络调用情况。日志上传解决方案受限于容器内工具的匮乏,比较推荐的方案是将容器内搜集到的日志上传到云端,然后下载到本地进行分析。目前,EDAS 暂时没有提供容器内日志的下载功能,这里给出一种基于阿里云 OSS 服务的解决方案。OSS 打通了阿里云生态几乎所有的网络环境,你几乎可以在任何网络环境下上传以及下载 OSS 上的文件。首先在容器内部安装OSS命令行工具。以64位centos系统,root下没有打通公网的情况下可以选择在本地下载,然后将这个文件上传到oss,然后取oss的vpc内地址进行下载wget http://gosspublic.alicdn.com/...chmod 755 ossutil64* 然后配置你的 OSS 命令行工具,附上当前 region VPC 内的endpoint(VPC内的上传不要求打通公网,也不消耗公网带宽流量,更加经济),填写用于接收上传文件的账号的AK/SK,然后查看已经创建的Bucket,来检查你的OSS服务是否可用。请先确保账号(不必是当前账号,任意开通阿里云oss服务的账号均可)已开通 OSS 服务按照提示配置你的 AK SK endpoint信息,ststoken 不需要填写./ossutil64 config检查账号是否可用,如果报错则配置错误,如果没有bucket,则建议前往oss控制台创建,命令行工具也支持创建./ossutil64 ls这里创建一个模拟的日志文件,用于上传echo “Hello” > edas-app.log./ossutil64 cp edas-app.log {bucket-address,例如:oss://test-bucket,可以从上述命令"./ossutil64 ls"中查看}* 从 OSS 控制台或其他工具中找到你的日志文件,下载到本地,并使用你熟悉的工具进行分析。本文作者:落语(阿里云智能中间件技术开发工程师,负责分布式应用服务 EDAS 的开发和维护。)<hr>本文作者:中间件小哥阅读原文 ...

March 27, 2019 · 1 min · jiezi

利用Serverless Kubernetes和Kaniko快速自动化构建容器镜像

摘要: 本文介绍了一种新的面向开发者的简单镜像构建实践,基于阿里云Serverless Kubernetes容器服务,可以自动化而且低成本的构建容器镜像,以便让开发者了解如何使用Serverless运行CI/CD和自动化任务。前言:在云原生时代中,容器镜像是一切应用分发的基础载体,除了dockerhub作为流行的镜像仓库外,各大公有云厂商也都提供了功能丰富镜像仓库服务,如ACR(Aliyun Container Registry), GCR(Goolge Container Registry),构建容器镜像已是所有开发者必须掌握的基础实践能力。无论开发者选择在本地使用docker完成基本的镜像构建,还是使用CI/CD服务(如Jenkins),本质上都是遵循“pull -> build -> push”的过程,完成镜像的构建、分发和同步等操作。本文介绍了一种新的面向开发者的简单镜像构建实践,基于阿里云Serverless Kubernetes容器服务,可以自动化而且低成本的构建容器镜像,以便让开发者了解如何使用Serverless运行CI/CD和自动化任务。why serverless kubernetes?容器镜像的构建是需要计算资源的,开发者在本地使用docker pull/build/push时,其计算资源是本地开发机器,如果开发者在传统kubernetes集群中部署Jenkins或Gitlab-runner服务,其计算资源也是需要持续运行。但是,容器镜像的构建基本属于高度动态的行为,往往是定时或者条件触发引起的操作,所以为了动态的构建操作而维护一个固定的计算资源池对成本是不利的。Serverless Kubernetes不同与传统基于节点的k8s集群,serverless集群中只有pod运行时才会收费,意味着只有在构建镜像时用户才需要付费,当构建结束时,也就停止计费。所以在成本上与传统k8s集群或ecs部署的方式相比显著减少。Kaniko在Serverless Kubernetes集群中,pod没有privileged权限,无法访问主机上的docker daemon,也就无法使用docker in docker方案进行镜像的操作,那么如何在kubernetes集群中不依赖宿主机的Docker情况下构建镜像呢?显然这是一个通用需求,社区也有了推荐的方案:kaniko。kaniko的工作原理与docker build类似,但是不依赖docker daemon,在pod中完成Dockfile各层的解析和build,这使得pod不需要privileged权限也可以完成镜像的pull/build/push。实践示例:定时同步容器镜像下面让我们使用Serverless Kubernetes和Kaniko实现一个简单的镜像build实验:定时同步镜像到国内ACR。步骤1: 创建Serverless Kubernetes集群登陆容器服务控制台, 5s即可完成Serverless集群创建。(如果是国外的源镜像仓库,可以选择美西区域)步骤2: 创建secret,配置ACR的用户名密码,用于推送镜像到ACR可登陆cloudshell操作如下命令。#docker login registry.cn-hangzhou.aliyuncs.com…#kubectl create secret generic regsecret –from-file=/root/.docker/config.json…步骤3: 创建定时任务CronJob在控制台选择模版创建如下定时任务,每小时同步busybox镜像到ACR。apiVersion: v1kind: ConfigMapmetadata: name: dockerfile-cmdata: Dockerfile: | FROM busybox:latest—apiVersion: batch/v1beta1kind: CronJobmetadata: name: kaniko-builderspec: schedule: “*/60 * * * *” jobTemplate: spec: template: spec: containers: - name: builder image: gcr.io/kaniko-project/executor:latest imagePullPolicy: Always args: - “–dockerfile=/configs/Dockerfile” - “–destination=registry.cn-hangzhou.aliyuncs.com/jovizhangwei/busybox:latest” volumeMounts: - name: dockerfile readOnly: true mountPath: “/configs/” - name: secrets readOnly: true mountPath: “/root/.docker/” volumes: - name: dockerfile configMap: name: dockerfile-cm - name: secrets secret: secretName: regsecret restartPolicy: OnFailure待job执行后,可查看ACR镜像仓库,确认镜像已同步。用户可以按照需求定制此模版文件,比如修改需要同步的镜像,添加build步骤等,也可以设置pod的资源限制(vcpu 0.25/0.5/1等), 以最小的计算成本完成同步任务。结束通过上面的示例,我们看到基于Serverless Kubernetes的按需付费特性,可以使用很低的成本运行一些定时和CI/CD任务,而不用维护一个固定的计算资源池,其同样适用于压力测试、数据计算、工作流处理等其他场景。Happy Hacking!更多参考信息Serverless Kubernetes快速部署jenkins环境及执行流水线构建: https://yq.aliyun.com/articles/685219kaniko intro:https://cloud.google.com/blog/products/gcp/introducing-kaniko-build-container-images-in-kubernetes-and-google-container-builder-even-without-root-access本文作者:贤维阅读原文本文为云栖社区原创内容,未经允许不得转载。 ...

March 15, 2019 · 1 min · jiezi

高可用服务 AHAS 在消息队列 MQ 削峰填谷场景下的应用

在消息队列中,当消费者去消费消息的时候,无论是通过 pull 的方式还是 push 的方式,都可能会出现大批量的消息突刺。如果此时要处理所有消息,很可能会导致系统负载过高,影响稳定性。但其实可能后面几秒之内都没有消息投递,若直接把多余的消息丢掉则没有充分利用系统处理消息的能力。我们希望可以把消息突刺均摊到一段时间内,让系统负载保持在消息处理水位之下的同时尽可能地处理更多消息,从而起到“削峰填谷”的效果:上图中红色的部分代表超出消息处理能力的部分。我们可以看到消息突刺往往都是瞬时的、不规律的,其后一段时间系统往往都会有空闲资源。我们希望把红色的那部分消息平摊到后面空闲时去处理,这样既可以保证系统负载处在一个稳定的水位,又可以尽可能地处理更多消息,这时候我们就需要一个能够控制消费端消息匀速处理的利器 — AHAS 流控降级,来为消息队列削峰填谷,保驾护航。AHAS 是如何削峰填谷的AHAS 的流控降级是面向分布式服务架构的专业流量控制组件,主要以流量为切入点,从流量控制、熔断降级、系统保护等多个维度来帮助您保障服务的稳定性,同时提供强大的聚合监控和历史监控查询功能。AHAS 专门为这种场景提供了匀速排队的控制特性,可以把突然到来的大量请求以匀速的形式均摊,以固定的间隔时间让请求通过,以稳定的速度逐步处理这些请求,起到“削峰填谷”的效果,从而避免流量突刺造成系统负载过高。同时堆积的请求将会排队,逐步进行处理;当请求排队预计超过最大超时时长的时候则直接拒绝,而不是拒绝全部请求。比如在 RocketMQ 的场景下配置了匀速模式下请求 QPS 为 5,则会每 200 ms 处理一条消息,多余的处理任务将排队;同时设置了超时时间,预计排队时长超过超时时间的处理任务将会直接被拒绝。示意图如下图所示:RocketMQ Consumer 接入示例本部分将引导您快速在 RocketMQ 消费端接入 AHAS 流控降级 Sentinel。1. 开通 AHAS首先您需要到AHAS 控制台开通 AHAS 功能(免费)。可以根据 开通 AHAS 文档 里面的指引进行开通。2. 代码改造在结合阿里云 RocketMQ Client 使用 Sentinel 时,用户需要引入 AHAS Sentinel 的依赖 ahas-sentinel-client (以 Maven 为例):<dependency> <groupId>com.alibaba.csp</groupId> <artifactId>ahas-sentinel-client</artifactId> <version>1.1.0</version></dependency>由于 RocketMQ Client 未提供相应拦截机制,而且每次收到都可能是批量的消息,因此用户在处理消息时需要手动进行资源定义(埋点)。我们可以在处理消息的逻辑处手动进行埋点,资源名可以根据需要来确定(如 groupId + topic 的组合): private static Action handleMessage(Message message, String groupId, String topic) { Entry entry = null; try { // 资源名称为 groupId 和 topic 的组合,便于标识,同时可以针对不同的 groupId 和 topic 配置不同的规则 entry = SphU.entry(“handleMqMessage:” + groupId + “:” + topic); // 在此处编写真实的处理逻辑 System.out.println(System.currentTimeMillis() + " | handling message: " + message); return Action.CommitMessage; } catch (BlockException ex) { // 在编写处理被流控的逻辑 // 示例:可以在此处记录错误或进行重试 System.err.println(“Blocked, will retry later: " + message); return Action.ReconsumeLater; // 会触发消息重新投递 } finally { if (entry != null) { entry.exit(); } } }消费者订阅消息的逻辑示例:Consumer consumer = ONSFactory.createConsumer(properties);consumer.subscribe(topic, “*”, (message, context) -> { return handleMessage(message);});consumer.start();更多关于 RocketMQ SDK 的信息可以参考 消息队列 RocketMQ 入门文档。3. 获取 AHAS 启动参数注意:若在本地运行接入 AHAS Sentinel 控制台需要在页面左上角选择 公网 环境,若在阿里云 ECS 环境则在页面左上角选择对应的 Region 环境。我们可以进入 AHAS 控制台,点击左侧侧边栏的 流控降级,进入 AHAS 流控降级控制台应用总览页面。在页面右上角,单击添加应用,选择 SDK 接入页签,到 配置启动参数 页签拿到需要的启动参数(详情请参考 SDK 接入文档),类似于:-Dproject.name=AppName -Dahas.license=<License>其中 project.name 配置项代表应用名(会显示在控制台,比如 MqConsumerDemo),ahas.license 配置项代表自己的授权 license(ECS 环境不需要此项)。4. 启动 Consumer,配置规则接下来我们添加获取到的启动参数,启动修改好的 Consumer 应用。由于 AHAS 流控降级需要进行资源调用才能触发初始化,因此首先需要向对应 group/topic 发送一条消息触发初始化。消费端接收到消息后,我们就可以在 AHAS Sentinel 控制台上看到我们的应用了。点击应用卡片,进入详情页面后点击左侧侧边栏的“机器列表”。我们可以在机器列表页面看到刚刚接入的机器,代表接入成功:点击“请求链路”页面,我们可以看到之前定义的资源。点击右边的“流控”按钮添加新的流控规则:我们在“流控方式”中选择“排队等待”,设置 QPS 为 10,代表每 100ms 匀速通过一个请求;并且设置最大超时时长为 2000ms,超出此超时时间的请求将不会排队,立即拒绝。配置完成后点击新建按钮。5. 发送消息,查看效果下面我们可以在 Producer 端批量发送消息,然后在 Consumer 端的控制台输出处观察效果。可以看到消息消费的速率是匀速的,大约每 100 ms 消费一条消息:1550732955137 | handling message: Hello MQ 24531550732955236 | handling message: Hello MQ 91621550732955338 | handling message: Hello MQ 49441550732955438 | handling message: Hello MQ 55821550732955538 | handling message: Hello MQ 44931550732955637 | handling message: Hello MQ 30361550732955738 | handling message: Hello MQ 13811550732955834 | handling message: Hello MQ 14501550732955937 | handling message: Hello MQ 5871同时不断有排队的处理任务完成,超出等待时长的处理请求直接被拒绝。注意在处理请求被拒绝的时候,需要根据需求决定是否需要重新消费消息。我们也可以点击左侧侧边栏的“监控详情”进入监控详情页面,查看处理消息的监控曲线:对比普通限流模式的监控曲线(最右面的部分):如果不开启匀速模式,只是普通的限流模式,则只会同时处理 10 条消息,其余的全部被拒绝,即使后面的时间系统资源充足多余的请求也无法被处理,因而浪费了许多空闲资源。两种模式对比说明匀速模式下消息处理能力得到了更好的利用。Kafka 接入代码示例Kafka 消费端接入 AHAS 流控降级的思路与上面的 RocketMQ 类似,这里给出一个简单的代码示例:private static void handleMessage(ConsumerRecord<String, String> record, String groupId, String topic) { pool.submit(() -> { Entry entry = null; try { // 资源名称为 groupId 和 topic 的组合,便于标识,同时可以针对不同的 groupId 和 topic 配置不同的规则 entry = SphU.entry(“handleKafkaMessage:” + groupId + “:” + topic); // 在此处理消息. System.out.printf(”[%d] Receive new messages: %s%n", System.currentTimeMillis(), record.toString()); } catch (BlockException ex) { // Blocked. // NOTE: 在处理请求被拒绝的时候,需要根据需求决定是否需要重新消费消息 System.err.println(“Blocked: " + record.toString()); } finally { if (entry != null) { entry.exit(); } } });}消费消息的逻辑:while (true) { try { ConsumerRecords<String, String> records = consumer.poll(1000); // 必须在下次 poll 之前消费完这些数据, 且总耗时不得超过 SESSION_TIMEOUT_MS_CONFIG // 建议开一个单独的线程池来消费消息,然后异步返回结果 for (ConsumerRecord<String, String> record : records) { handleMessage(record, groupId, topic); } } catch (Exception e) { try { Thread.sleep(1000); } catch (Throwable ignore) { } e.printStackTrace(); }}其它以上介绍的只是 AHAS 流控降级的其中一个场景 —— 请求匀速,它还可以处理更复杂的各种情况,比如:流量控制:可以针对不同的调用关系,以不同的运行指标(如 QPS、线程数、系统负载等)为基准,对资源调用进行流量控制,将随机的请求调整成合适的形状(请求匀速、Warm Up 等)。熔断降级:当调用链路中某个资源出现不稳定的情况,如平均 RT 增高、异常比例升高的时候,会使对此资源的调用请求快速失败,避免影响其它的资源导致级联失败。系统负载保护:对系统的维度提供保护。当系统负载较高的时候,提供了对应的保护机制,让系统的入口流量和系统的负载达到一个平衡,保证系统在能力范围之内处理最多的请求。您可以参考 AHAS 流控降级文档 来挖掘更多的场景。本文作者:中间件小哥阅读原文本文为云栖社区原创内容,未经允许不得转载。 ...

March 5, 2019 · 2 min · jiezi

配置管理 ACM 在高可用服务 AHAS 流控降级组件中的应用场景

应用配置管理(Application Configuration Management,简称 ACM)是一款应用配置中心产品。基于ACM您可以在微服务、DevOps、大数据等场景下极大地减轻配置管理的工作量,同时保证配置的安全合规。ACM 有着丰富的使用场景,本文将介绍其在 AHAS 流控降级 中的应用。什么是 AHAS 流控降级AHAS 流控降级 是面向分布式服务架构的专业流量控制组件,主要以流量为切入点,从流量控制、熔断降级、系统保护等多个维度帮助您保障服务的稳定性,同时提供强大的聚合监控和历史监控查询功能。在 AHAS 流控降级中,我们需要针对不同的资源(比如服务接口、方法)配置不同的规则(流控规则、降级规则、系统保护规则等)。由于流量的不确定性,我们的规则也需要根据流量的实时情况进行动态管理。AHAS 流控降级使用了 ACM 作为动态配置中心,借助其实时动态推送的能力达到规则实时推送的效果。如何使用 ACMAHAS 流控降级分为两部分:客户端(基于开源的 Sentinel)以及AHAS 控制台。用户使用时只需要引入 AHAS Sentinel 客户端相关依赖 ahas-sentinel-client 并在启动时指定相关参数即可接入到 AHAS 流控降级控制台,在 AHAS 控制台上查看监控、配置规则。Sentinel 抽象出了动态数据源接口,可以方便地对接任意配置中心。Sentinel 推荐使用 push 模式的动态规则源,推送流程为 Sentinel 控制台 → 配置中心 → Sentinel 数据源 → Sentinel,非常清晰:AHAS 流控降级客户端提供了 ACM 动态规则源适配,实现了监听远程规则变更的逻辑,而 AHAS 流控降级控制台实现了相应的规则推送逻辑。用户在 AHAS 流控降级控制台保存规则的时候,AHAS 控制台会在保存规则后将规则推送至 ACM 相应的坐标上,ACM 会实时地将规则 push 到接入端。AHAS 流控降级客户端的动态配置源会自动注册当前应用对应坐标的监听器监听规则变化,当监听到变更时就将其加载到 Sentinel 的规则管理器中,新的规则就生效了。以上就是 ACM 在 AHAS 流控降级中的应用场景,有关 ACM 的更多信息可以参考官方文档。本文作者:中间件小哥阅读原文本文为云栖社区原创内容,未经允许不得转载。

March 5, 2019 · 1 min · jiezi

Spring Cloud Alibaba迁移指南(一):一行代码从 Hystrix 迁移到 Sentinel

摘要: 本文对Hystrix、Resilience4j、Sentinel进行对比,并探讨如何使用一行代码这种极简的方式,将Hystrix迁移到Sentinel。 Hystrix 自从前段时间 宣布停止维护之后,社区推荐了 resilience4j。自 Spring Cloud 官方宣布 Spring Cloud Netflix 进入维护状态后,我们开始制作《Spring Cloud Alibaba迁移指南》系列文章,向开发者提供更多的技术选型方案,并降低迁移过程中的技术难度。第一篇,我们对Hystrix、Resilience4j 和 Sentinel 三个开源项目进行对比,并探讨如何使用一行代码这种极简的方式,将Hystrix迁移到Sentinel。Hystrix 自从前段时间 宣布停止维护之后,社区推荐了 resilience4j。这 3 款产品各有优劣势,具体的功能差异参考下表(该表来源 Sentinel Wiki): SentinelHystrixresilience4j隔离策略信号量隔离(并发线程数限流)线程池隔离/信号量隔离信号量隔离熔断降级策略基于响应时间、异常比率、异常数基于异常比率基于异常比率、响应时间实时统计实现滑动窗口(LeapArray)滑动窗口(基于 RxJava)Ring Bit Buffer动态规则配置支持多种数据源支持多种数据源有限支持扩展性多个扩展点插件的形式接口的形式基于注解的支持支持支持支持限流基于 QPS,支持基于调用关系的限流有限的支持Rate Limiter流量整形支持预热模式、匀速器模式、预热排队模式不支持简单的 Rate Limiter 模式系统自适应保护支持不支持不支持控制台提供开箱即用的控制台,可配置规则、查看秒级监控、机器发现等简单的监控查看不提供控制台,可对接其它监控系统目前 Sentinel 在 Spring Cloud Alibaba 项目中已经适配了 Spring Cloud 体系,可以用来完全替代 Hystrix 的功能了。Spring Cloud Alibaba Sentinel 功能介绍Spring Cloud Alibaba Sentinel 主要是为了整合 Sentinel 和 Spring Boot/Cloud 技术栈。目前完成了如下功能:自动适配 Servlet 容器。只需要配置 url-pattern(默认拦截所有请求),即可对拦截的这些 url 进行限流降级操作整合了 RestTemplate,使用 RestTemplate 进行操作的时候会被限流降级整合了 Feign,兼容了原先的 Hystrix 支持 Feign 的编程模型数据源可配置化,只需在配置文件中配置数据源信息,即可动态加载规则Endpoint 暴露各种元数据,比如配置信息,规则数据HealthIndicator 检查 Sentinel 健康状态 (整合中)支持 Spring Cloud Gateway 和 Zuul (整合中)Spring Cloud Alibaba Sentinel 代替 Hystrix想要使用 Spring Cloud Alibaba Sentinel,需要加上依赖信息:<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-alibaba-sentinel</artifactId> <version>0.2.1.RELEASE</version></dependency>0代码修改兼容 Feign加上 Feign 的依赖:<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> <version>${latest.version}</version></dependency>把 hystrix 的配置改成 sentinel 的即可使用 Sentinel 的限流降级功能:# feign.hystrix.enabled: truefeign.sentinel.enabled: true@FeignClient(name = “service-provider”)public interface EchoService { @RequestMapping(value = “/echo/{str}”, method = RequestMethod.GET) String echo(@PathVariable(“str”) String str); @RequestMapping(value = “/echo/save”, method = RequestMethod.POST) String save(Foo foo);}对于这个 EchoService,echo 方法对应的资源名是 GET:http://service-provider/echo/{str}, save 方法对应的资源名是 POST:http://service-provider/echo/save。只需配置这些规则,限流降级操作即可立即生效。一行代码支持 RestTemplateSentinel 还跟 Spring 生态的 RestTemplate 做了整合,可以对 RestTemplate 请求过程进行限流和降级操作,只需要在构造 RestTemplate 的时候加上 @SentinelRestTemplate 注解即可:@Bean@SentinelRestTemplatepublic RestTemplate restTemplate() { return new RestTemplate();}@SentinelRestTemplate 注解还暴露出了对应的属性可进行限流降级后的自定义错误,默认的行为是返回 “RestTemplate request block by sentinel” 信息。关于 @SentinelRestTemplate 的详细信息可以参考 Wiki。本文作者:中间件小哥阅读原文本文为云栖社区原创内容,未经允许不得转载。 ...

February 26, 2019 · 1 min · jiezi

微服务架构下,解决数据一致性问题的实践

随着业务的快速发展,应用单体架构暴露出代码可维护性差、容错率低、测试难度大和敏捷交付能力差等诸多问题,微服务应运而生。微服务的诞生一方面解决了上述问题,但是另一方面却引入新的问题,其中主要问题之一就是:如何保证微服务间的业务数据一致性。本文将通过一个商品采购的业务,来看看在Dubbo的微服务架构下,如何通过Fescar来保障业务的数据一致性。本文所述的例子中,Dubbo 和 Fescar 的注册配置服务中心均使用 Nacos。Fescar 0.2.1+ 开始支持 Nacos 注册配置服务中心。业务描述用户采购商品的业务,包含3个微服务:库存服务: 扣减给定商品的库存数量。订单服务: 根据采购请求生成订单。账户服务: 用户账户金额扣减。业务结构图如下:库存服务(StorageService)public interface StorageService { /** * deduct storage count / void deduct(String commodityCode, int count);}订单服务(OrderService)public interface OrderService { /* * create order / Order create(String userId, String commodityCode, int orderCount);}账户服务(AccountService)public interface AccountService { /* * debit balance of user’s account */ void debit(String userId, int money);}说明: 以上三个微服务均是独立部署。8个步骤实现数据一致性Step 1:初始化 MySQL 数据库(需要InnoDB 存储引擎)在 resources/jdbc.properties 修改StorageService、OrderService、AccountService 对应的连接信息。jdbc.account.url=jdbc:mysql://xxxx/xxxxjdbc.account.username=xxxxjdbc.account.password=xxxxjdbc.account.driver=com.mysql.jdbc.Driver# storage db configjdbc.storage.url=jdbc:mysql://xxxx/xxxxjdbc.storage.username=xxxxjdbc.storage.password=xxxxjdbc.storage.driver=com.mysql.jdbc.Driver# order db configjdbc.order.url=jdbc:mysql://xxxx/xxxxjdbc.order.username=xxxxjdbc.order.password=xxxxjdbc.order.driver=com.mysql.jdbc.DriverStep 2:创建 undo_log(用于Fescar AT 模式)表和相关业务表相关建表脚本可在 resources/sql/ 下获取,在相应数据库中执行 dubbo_biz.sql 中的业务建表脚本,在每个数据库执行 undo_log.sql 建表脚本。CREATE TABLE undo_log ( id bigint(20) NOT NULL AUTO_INCREMENT, branch_id bigint(20) NOT NULL, xid varchar(100) NOT NULL, rollback_info longblob NOT NULL, log_status int(11) NOT NULL, log_created datetime NOT NULL, log_modified datetime NOT NULL, ext varchar(100) DEFAULT NULL, PRIMARY KEY (id), KEY idx_unionkey (xid,branch_id)) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;DROP TABLE IF EXISTS storage_tbl;CREATE TABLE storage_tbl ( id int(11) NOT NULL AUTO_INCREMENT, commodity_code varchar(255) DEFAULT NULL, count int(11) DEFAULT 0, PRIMARY KEY (id), UNIQUE KEY (commodity_code)) ENGINE=InnoDB DEFAULT CHARSET=utf8;DROP TABLE IF EXISTS order_tbl;CREATE TABLE order_tbl ( id int(11) NOT NULL AUTO_INCREMENT, user_id varchar(255) DEFAULT NULL, commodity_code varchar(255) DEFAULT NULL, count int(11) DEFAULT 0, money int(11) DEFAULT 0, PRIMARY KEY (id)) ENGINE=InnoDB DEFAULT CHARSET=utf8;DROP TABLE IF EXISTS account_tbl;CREATE TABLE account_tbl ( id int(11) NOT NULL AUTO_INCREMENT, user_id varchar(255) DEFAULT NULL, money int(11) DEFAULT 0, PRIMARY KEY (id)) ENGINE=InnoDB DEFAULT CHARSET=utf8;说明: 需要保证每个物理库都包含 undo_log 表,此处可使用一个物理库来表示上述三个微服务对应的独立逻辑库。Step 3:引入 Fescar、Dubbo 和 Nacos 相关 POM 依赖 <properties> <fescar.version>0.2.1</fescar.version> <dubbo.alibaba.version>2.6.5</dubbo.alibaba.version> <dubbo.registry.nacos.version>0.0.2</dubbo.registry.nacos.version> </properties> <dependency> <groupId>com.alibaba.fescar</groupId> <artifactId>fescar-spring</artifactId> <version>${fescar.version}</version> </dependency> <dependency> <groupId>com.alibaba.fescar</groupId> <artifactId>fescar-dubbo-alibaba</artifactId> <version>${fescar.version}</version> <exclusions> <exclusion> <artifactId>dubbo</artifactId> <groupId>org.apache.dubbo</groupId> </exclusion> </exclusions> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>dubbo</artifactId> <version>${dubbo.alibaba.version}</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>dubbo-registry-nacos</artifactId> <version>${dubbo.registry.nacos.version}</version> </dependency>说明: 由于当前 apache-dubbo 与 dubbo-registry-nacos jar存在兼容性问题,需要排除 fescar-dubbo 中的 apache.dubbo 依赖并手动引入 alibaba-dubbo,后续 apache-dubbo(2.7.1+) 将兼容 dubbo-registry-nacos。在Fescar 中 fescar-dubbo jar 支持 apache.dubbo,fescar-dubbo-alibaba jar 支持 alibaba-dubbo。Step 4:微服务 Provider Spring配置分别在三个微服务Spring配置文件(dubbo-account-service.xml、 dubbo-order-service 和 dubbo-storage-service.xml )进行如下配置:配置 Fescar 代理数据源<bean id=“accountDataSourceProxy” class=“com.alibaba.fescar.rm.datasource.DataSourceProxy”> <constructor-arg ref=“accountDataSource”/></bean><bean id=“jdbcTemplate” class=“org.springframework.jdbc.core.JdbcTemplate”> <property name=“dataSource” ref=“accountDataSourceProxy”/></bean>此处需要使用 com.alibaba.fescar.rm.datasource.DataSourceProxy 包装 Druid 数据源作为直接业务数据源,DataSourceProxy 用于业务 SQL 的拦截解析并与 TC 交互协调事务操作状态。配置 Dubbo 注册中心 <dubbo:registry address=“nacos://${nacos-server-ip}:8848”/>配置 Fescar GlobalTransactionScanner<bean class=“com.alibaba.fescar.spring.annotation.GlobalTransactionScanner”> <constructor-arg value=“dubbo-demo-account-service”/> <constructor-arg value=“my_test_tx_group”/></bean>此处构造方法的第一个参数为业务自定义 applicationId,若在单机部署多微服务需要保证 applicationId 唯一。构造方法的第二个参数为 Fescar 事务服务逻辑分组,此分组通过配置中心配置项 service.vgroup_mapping.my_test_tx_group 映射到相应的 Fescar-Server 集群名称,然后再根据集群名称.grouplist 获取到可用服务列表。Step 5:事务发起方配置在 dubbo-business.xml 配置以下配置:配置 Dubbo 注册中心同 Step 4配置 Fescar GlobalTransactionScanner同 Step 4在事务发起方 service 方法上添加 @GlobalTransactional 注解@GlobalTransactional(timeoutMills = 300000, name = “dubbo-demo-tx”)timeoutMills 为事务的总体超时时间默认60s,name 为事务方法签名的别名,默认为空。注解内参数均可省略。Step 6:启动 Nacos-Server下载 Nacos-Server 最新 release 包并解压运行 Nacos-serverLinux/Unix/Macsh startup.sh -m standaloneWindowscmd startup.cmd -m standalone访问 Nacos 控制台:http://localhost:8848/nacos/index.html#/configurationManagement?dataId=&group=&appName=&namespace若访问成功说明 Nacos-Server 服务运行成功(默认账号/密码: nacos/nacos)Step 7:启动 Fescar-Server下载 Fescar-Server 最新 release 包并解压初始化 Fescar 配置进入到 Fescar-Server 解压目录 conf 文件夹下,确认 nacos-config.txt 的配置值(一般不需要修改),确认完成后运行 nacos-config.sh 脚本初始化配置。sh nacos-config.sh $Nacos-Server-IPeg:sh nacos-config.sh localhost 脚本执行最后输出 “init nacos config finished, please start fescar-server.” 说明推送配置成功。若想进一步确认可登陆Nacos 控制台 配置列表 筛选 Group=FESCAR_GROUP 的配置项。修改 Fescar-server 服务注册方式为 nacos进入到 Fescar-Server 解压目录 conf 文件夹下 registry.conf 修改 type=“nacos” 并配置 Nacos 的相关属性。 registry { # file nacos type = “nacos” nacos { serverAddr = “localhost” namespace = “public” cluster = “default” } file { name = “file.conf” }}type: 可配置为 nacos 和 file,配置为 file 时无服务注册功能nacos.serverAddr: Nacos-Sever 服务地址(不含端口号)nacos.namespace: Nacos 注册和配置隔离 namespacenacos.cluster: 注册服务的集群名称file.name: type = “file” classpath 下配置文件名运行 Fescar-serverLinux/Unix/Macsh fescar-server.sh $LISTEN_PORT $PATH_FOR_PERSISTENT_DATA $IP(此参数可选)Windowscmd fescar-server.bat $LISTEN_PORT $PATH_FOR_PERSISTENT_DATA $IP(此参数可选)服务端口 PATH_FOR_PERSISTENT_DATA: 事务操作记录文件存储路径(已存在路径)$IP(可选参数): 用于多 IP 环境下指定 Fescar-Server 注册服务的IPeg: sh fescar-server.sh 8091 /home/admin/fescar/data/运行成功后可在 Nacos 控制台看到 服务名 =serverAddr 服务注册列表:Step 8:启动微服务并测试修改业务客户端发现注册方式为 nacos同Step 7 中[修改 Fescar-server 服务注册方式为 nacos] 步骤启动 DubboAccountServiceStarter启动 DubboOrderServiceStarter启动 DubboStorageServiceStarter启动完成可在 Nacos 控制台服务列表 看到启动完成的三个 provider:启动 DubboBusinessTester 进行测试注意: 在标注 @GlobalTransactional 注解方法内部显示的抛出异常才会进行事务的回滚。整个 Dubbo 服务调用链路只需要在事务最开始发起方的 service 方法标注注解即可。通过以上8个步骤,我们实现了用户采购商品的业务中库存、订单和账户3个独立微服务之间的数据一致性。参考链接:本文 sample 地址: https://github.com/fescar-group/fescar-samples/tree/master/nacosFescar: https://github.com/alibaba/fescarDubbo: https://github.com/apache/incubator-dubboNacos: https://github.com/alibaba/nacos本文作者:清铭,社区昵称 slievrly,Fescar 开源项目发起人之一,阿里巴巴中件间 TXC/GTS 核心研发成员,长期从事于分布式中间件核心研发工作,在分布式事务领域有着较丰富的技术积累。有关 Fescar 的更多信息:分布式事务中间件 Fescar - RM 模块源码解读关于开源分布式事务中间件Fescar,我们总结了开发者关心的13个问题本文作者:中间件小哥阅读原文本文为云栖社区原创内容,未经允许不得转载。 ...

February 25, 2019 · 3 min · jiezi

阿里云容器服务DaemonSet实践

DaemonSet 保证在每个 Node 上都运行一个容器副本,常用来部署一些集群的日志、监控或者其他系统管理应用。下面以日志收集 fluentd 为例,看下如何使用阿里云容器服务控制台创建DaemonSet。准备Kubernetes环境在阿里云容器服务控制台中创建Kubernetes 集群(1.11.5),3 master,3 worker安装fluentd1、选择应用->守护进程集->使用镜像创建填写应用名称,选择部署集群、命名空间,进入下一步2、选择镜像并进行相应配置注意:这里挂载了配置项fluentd-conf,用来覆盖镜像中的默认配置,需要提前创建出来,内容如下:apiVersion: v1kind: ConfigMapmetadata: name: fluentd-conf namespace: kube-systemdata: td-agent.conf: | <match fluent.> type null </match> <source> type tail path /var/log/containers/.log pos_file /var/log/es-containers.log.pos time_format %Y-%m-%dT%H:%M:%S.%NZ tag kubernetes. format json read_from_head true </source> <filter kubernetes.> type kubernetes_metadata verify_ssl false </filter>否则会遇到pod 启动问题[error]: config error file="/etc/td-agent/td-agent.conf" error=“Invalid Kubernetes API v1 endpoint https://172.21.0.1:443/api: SSL_connect returned=1 errno=0 state=error: certificate verify failed"3、设置更新策略可以在高级配置中选择升级方式:滚动升级(RollingUpdate):更新 DaemonSet 模版后,自动删除旧的 Pod 并创建新的 Pod替换升级(OnDelete):更新模板后,只有手动删除了旧的 Pod 后才会创建新的 Pod4、指定节点调度只选择worker节点安装。设置节点亲和性如图。5、创建完成点击创建,可以看到创建成功。6、问题排查与更新按着上述步骤可以看到在3个worker节点分别起了对应的pod,但pod并没有成功启动。选择其中的一个容器,查看一下日志发现如下错误:config error file="/etc/td-agent/td-agent.conf” error=“Exception encountered fetching metadata from Kubernetes API endpoint: pods is forbidden: User cannot list pods at the cluster scope"Google后发现需要设置ClusterRoleapiVersion: v1kind: ServiceAccountmetadata: name: fluent-account namespace: kube-system—apiVersion: rbac.authorization.k8s.io/v1beta1kind: ClusterRoleBindingmetadata: name: fluent-accountroleRef: kind: ClusterRole name: view apiGroup: rbac.authorization.k8s.iosubjects: - kind: ServiceAccount name: fluent-account namespace: kube-system创建成功后更新fluent-es 的yaml,编辑yaml,提交更新。Pod启动成功,日志已经可以正常采集了。总结使用阿里云容器服务控制台支持方便的创建DaemonSet,欢迎使用体验。https://cs.console.aliyun.com/本文作者:来随便逛逛阅读原文本文为云栖社区原创内容,未经允许不得转载。 ...

January 4, 2019 · 1 min · jiezi

手机秒变IoT设备?——巧妙利用阿里云物联网平台

摘要: 手机秒变IoT设备?什么?嵌入五行代码可以实现用户定位,用户在线数量统计,还能和云端消息交互?免费的MQ?谁用谁知道~作者序什么?嵌入五行代码可以实现用户定位,用户在线数量统计,还能和云端消息交互!没错得益于阿里云物联网套件创建设备免费,消息收费也只有百万消息三块六(先使用后付费,不满不要钱),尤其对于学生党来说,基本是用不了这么多,也就意味着免费!(学生党福音[手动滑稽])Talk is cheap,show me the codepublic static void main(String[] args) { //去阿里云官网上创建一个产品,设置好地理位置属性。可以变相用做分组 String productKey = “<your productKey>”; //english name only String deviceName = “anyDeviceName”; //自动创建设备的构造方法 Simulator simulator = new Simulator(productKey, deviceName); //如果已经有了三元组(productKey, deviceName, deviceSecret)可以使用下面的构造方法 // Simulator simulator = new Simulator("<your productKey>", “<your deviceName>”, “<your deviceSecret>”); simulator.connect(msgJson -> { System.out.println(“received: " + msgJson); //可以变相当成mq使用,执行收到消息之后的逻辑 }); //可以放在带有定位的设备中,手机,手表等等。通过api获取经纬度上报 UplinkMessage uplinkMessage = new UplinkMessage() .put(“GeoLocation”, new GeoLocation(122.250852f,30.193851f, 12f, 2)); //可以根据产品定义上报额外属性 //.put(“PM25”, 60) //.put(“CO2Value”, 123) //.put(“HeatSwitch”, 0) //.put(“LightSwitch”, 1); simulator.uplink(uplinkMessage); }(咳咳,精简一下差不多能五行。)同学们看一下代码里的注释,麻雀虽小,五脏俱全呢。整体目录结构是这样的,不久就放到github上,迫切需要的烙铁可以进钉钉群21939141艾特群主要代码How to use用法看起来是比较简单的,唯一需要的是三个关键key:productKey (传入到Simulator构造方法里)AppKey (配置到api.json)AppSecret(配置到api.json)这三个key不需要编码,上阿里云申请就可以。请看分解动作:productKey访问阿里云物联网控制台创建一个高级版产品(高级版也不收费,功能更强大), 分类选择自定义,后续我们为他定义功能。查看你的产品,在功能定义里添加一个地理位置功能(其他类型中搜索后任选一个)敲黑板!! 拿出小本本记住你的ProductKeyAppKey&AppSecret访问物联网开发者平台控制台创建一个自定义项目进入你的项目,点击左侧菜单栏软件开发>web应用, 创建一个web应用(前端应用)点击进入你的web应用,能看到AppKey和AppSecret敲黑板!! 拿出小本本记住你的AppKey和AppSecretReady?go!参数都设置好之后运行程序。在在空间数据可视化中创建一个二维场景,选择你之前创建的产品。一切顺利的话你可以看到如下画面不好意思设备名填了girlFriend - -,并设置到了海里。当然你也可以用来统计你的客户端数量(用户数)和消息收发情况enjoy!本文作者:侯吉阅读原文本文为云栖社区原创内容,未经允许不得转载。 ...

December 29, 2018 · 1 min · jiezi

TableStore实战:DLA+SQL实时分析TableStore

一、实战背景什么是DLA(DataLake Analytics数据湖)?他是无服务器化(Serverless)的云上交互式查询分析服务。作为分布式交互式分析服务,是表格存储计算生态的重要组成之一。为了使用户更好的了解DLA的功能、使用方式,创建了这一实战样例。基于DLA可以不用做任何ETL、数据搬迁等前置过程, 实现跨各种异构数据源进行大数据关联分析,并且支持数据回流到各个异构数据源,从而极大的节省成本、 降低延时和提升用户体验。基于JDBC,表格存储的控制台将SQL查询直接做了集成,数据为公共实例,用户不用开通服务也可免费体验表格存储的实时SQL分析、查询功能,样例如下所示:__官网控制台地址:__项目样例需求场景:黑五交易数据本实战案例中,我们从 https://www.kaggle.com/mehdidag/black-friday 上获取数据, 存储到TableStore,然后基于DLA做分析,带你切身感受下数据的价值!“Black Friday”,即“黑色星期五”,是美国人一年中购物最疯狂的日子,类似于中国的“双十一”购物狂欢节。一般黑色星期五的活动主要在线下,但逐渐也有往线上发展的趋势,比如Amazon就有针对黑色星期五做的线上销售活动, 与天猫双十一很相似。同样的,这样的活动会产生大量有意义的商业数据。我们在DLA中定义了一个叫blackfriday50w的表,映射到TableStore中的一个表,用来描述用户购买商品的。如下为示例数据的表结构、与真实数据截图二、表格存储(TableStore)方案准备工作若您对于DLA实时在线分析TableStore的功能感兴趣,希望开始自己系统的搭建之旅,只需按照如下步骤便可以着手搭建了:1、开通表格存储通过控制台开通表格存储服务,表格存储即开即用(后付费),采用按量付费方式,已为用户提供足够功能测试的免费额度。表格存储官网控制台、免费额度说明。2、创建实例通过控制台创建表格存储实例。3、导入数据该数据共有53.8万行,12个列,我们通过SDK将全量数据存储在TableStore的表。用户可通过控制台插入2条测试数据;开通DLA服务DLA服务开通用户进入产品介绍页,选择开通服务:https://www.aliyun.com/product/datalakeanalytics通过DLA控制台开通TableStore数据源开通数据源后创建服务访问点(择经典网络,若已有vpc,可选择vpc)登录CMS(账密会在开通服务后发送站内消息,消息中查看)创建DLA外表1)创建自己的DLA库(相关信息从上述过程中查找):mysql> create database hangzhou_ots_test with dbproperties ( catalog = ‘ots’, location = ‘https://instanceName.cn-hangzhou.ots-internal.aliyuncs.com’, instance = ‘instanceName’);Query OK, 0 rows affected (0.23 sec)#hangzhou_ots_test —请注意库名,允许字母、数字、下划线#catalog = ‘ots’, —指定为ots,是为了区分其他数据源,比如oss、rds等#location = ‘https://xxx’ —ots的endpoint,从实例上可以看到#instance = ‘hz-tpch-1x-vol'2)查看自己创建的库:mysql> show databases;+——————————+| Database |+——————————+| hangzhou_ots_test |+——————————+1 rows in set (0.22 sec)3)查看自己的DLA表:mysql> use hangzhou_ots_test;Database changedmysql> show tables;Empty set (0.30 sec)4)创建DLA表,映射到OTS的表:mysql> CREATE EXTERNAL TABLE tableName ( pk1 varchar(100) not NULL , pk2 int not NULL , col1 varchar(100) NULL , col2 varchar(100) NULL , PRIMARY KEY (pk1, pk2));Query OK, 0 rows affected (0.36 sec)## tableName —- TableStore中对应的表名(dla中会转换成小写后做映射)## pk2 int not NULL —- 如果是主键的话,必须要not null## PRIMARY KEY (pk1, pk2) —- 务必与ots中的主键顺序相同;名称的话也要对应5)查看自己创建的表和相关的DDL语句:mysql> show tables;+————+| Table_Name |+————+| tablename |+————+1 row in set (0.35 sec)6)开始查询和分析(用户可以分析自己的数据,符合mysql的语法)mysql> select count(*) from tablename;+——-+| _col0 |+——-+| 25 |+——-+1 row in set (1.19 sec)这样,一个TableStore在DLA中的关联外表创建成功,用户便可以通过JDBC、或者CMS控制台,根据自己的需求实时分析自己的TableStore表了。三、表格存储控制台展示如下为控制提供的SQL场景,用户可以仿照控制台中实例自己写一些需求SQL,开来尝试吧!最畅销的top10产品和销售量中高端产品占总体GMV的比例不同年龄段的消费客单价趋势高消费人群的性别和年龄趋势本文作者:潭潭 阅读原文本文为云栖社区原创内容,未经允许不得转载。 ...

December 28, 2018 · 1 min · jiezi

使用Terraform创建托管版Kubernetes

目前,阿里云容器服务已经可以创建托管版Kubernetes集群了。相比于默认的Kubernetes集群,托管版本会主动替您运维一套高可用的Master组件,免去了默认版本集群中三个节点,从而节约所需的资金成本及维护时的人力成本。在容器服务控制台,我们为您提供了便捷使用的可视界面一步一步引导式地创建该类型集群。但当您需要反复创建托管版集群,大批量创建集群,或者您就是天生抗拒控制台手工操作的那一类人,可以了解并尝试使用一下Terraform了。Terraform是一款Infrastructure作为Code的工具,可以将云端资源代码化。关于Terraform的基本介绍本文不再赘述,有兴趣的同学可以参考“云生态下的基础架构资源管理利器Terraform”等云栖社区的优秀文章。目前我们一直在支持阿里云Terraform Provider,已经实现了阿里云上面绝大部分的云产品的对接。在2018年圣诞节来临之前,阿里云Terraform Provider已经发布v1.26.0版本,其中已经支持了创建托管版Kubernetes集群,下面我们来一起看下如何实现命令行快速部署一个这样的集群。创建托管版Kubernetes集群首先我们打开“阿里云Terraform Provider文档 - 托管版Kubernetes”的帮助文档,可以看到该资源资源提供的参数列表。参数分参入参数和出参属性。入参列表内包含了必填参数以及可选参数,例如name和name_prefix就是一对必填参写,但它们互斥,即不能同时填写。如果填了名,集群名就是名的值,如果填了name_prefix,集群名会以name_prefix开头自动生成一个。我们对照文档中的参数列表Argument Reference,先草拟出一个集群的描述,为了方便起见,我把填写每个参数的理由都注释在代码中。# 引入阿里云 Terraform Providerprovider “alicloud” { # 填入您的账号 Access Key access_key = “FOO” # 填入您的账号 Secret Key secret_key = “BAR” # 填入想创建的 Region region = “cn-hangzhou” # 可选参数,默认不填就使用最新版本 version = “v1.26.0”}# 必要的资源标识# alicloud_cs_managed_kubernetes 表明是托管版 Kubernetes 集群# k8s 代表该资源实例的名称resource “alicloud_cs_managed_kubernetes” “k8s” { # 集群名称,可以带中划线,一个账户内的集群名称不能相同 name = “test-managed-kubernetes” # 可以从 ECS 控制台上面查询到可用区信息,以及对应的 ECS 实例类型库存 # 以下代表 Worker 节点将部署在 cn-hangzhou-h 这个可用区,采用 ecs.c5.xlarge 这个机型。 availability_zone = “cn-hangzhou-h” worker_instance_types = [“ecs.c5.xlarge”] # 配置该集群 Worker 节点数为 2 个,该数字后续可以再扩容 worker_numbers = [2] # Worker 节点使用高效云盘 worker_disk_category = “cloud_efficiency” # 默认为 true,会在 VPC 内创建一个 Nat 网关用于 ECS 连上互联网 new_nat_gateway = true # 配置所有 ECS 的默认 Root 密码,此处也可以用密钥对 key_name 代替,但需要提前创建 password = “Test12345” # Kubernetes 集群内所有 Pod 使用的子网网段,不能与 service_cidr 和 ECS 所在网段冲突 # 默认创建的 VPC 是 192.168.0.0/16 这个网段内的,所以 pod_cidr 和 service_cidr 可以使用 172 网段 # 请参考 VPC下 Kubernetes 的网络地址段规划 pod_cidr = “172.20.0.0/16” service_cidr = “172.21.0.0/20” # 安装云监控插件 install_cloud_monitor = true}我们可以将以上的配置保存为一个main.tf描述文件,在该文件的当前目录下执行terraform init和terraform apply。xh4n3@xh4n3:/ops/terraform-example% terraform init –get-plugins=true -upgradeInitializing provider plugins…- Checking for available provider plugins on https://releases.hashicorp.com…- Downloading plugin for provider “alicloud” (1.26.0)…Terraform has been successfully initialized!You may now begin working with Terraform. Try running “terraform plan” to seeany changes that are required for your infrastructure. All Terraform commandsshould now work.xh4n3@xh4n3:/ops/terraform-example% terraform applyAn execution plan has been generated and is shown below.Resource actions are indicated with the following symbols: + createTerraform will perform the following actions: + alicloud_cs_managed_kubernetes.k8s id: <computed> availability_zone: “cn-hangzhou-h” install_cloud_monitor: “true” name: “test-managed-kubernetes” name_prefix: “Terraform-Creation” new_nat_gateway: “true” password: <sensitive> pod_cidr: “172.20.0.0/16” security_group_id: <computed> service_cidr: “172.21.0.0/20” vpc_id: <computed> vswitch_ids.#: <computed> worker_disk_category: “cloud_efficiency” worker_disk_size: “40” worker_instance_charge_type: “PostPaid” worker_instance_types.#: “1” worker_instance_types.0: “ecs.c5.xlarge” worker_nodes.#: <computed> worker_numbers.#: “1” worker_numbers.0: “2"Plan: 1 to add, 0 to change, 0 to destroy.Do you want to perform these actions? Terraform will perform the actions described above. Only ‘yes’ will be accepted to approve. Enter a value:从上述日志中可以看到,terraform init会把我们用到的提供者插件下载好,terraform apply会根据我们的main.tf描述文件计算出需要执行的操作,上述显示将会创建一个alicloud_cs_managed_kubernetes.k8s的资源,需要我们输入是来确认创建。确认创建后,创建大约会耗时五分钟,terraform会输出类似下面的日志。# 以上省略Do you want to perform these actions? Terraform will perform the actions described above. Only ‘yes’ will be accepted to approve. Enter a value: yesalicloud_cs_managed_kubernetes.k8s: Creating… availability_zone: "” => “cn-hangzhou-h” install_cloud_monitor: "" => “true” name: "" => “test-managed-kubernetes” name_prefix: "" => “Terraform-Creation” new_nat_gateway: "" => “true” password: “<sensitive>” => “<sensitive>” pod_cidr: "" => “172.20.0.0/16” security_group_id: "" => “<computed>” service_cidr: "" => “172.21.0.0/20” vpc_id: "" => “<computed>” vswitch_ids.#: "" => “<computed>” worker_disk_category: "" => “cloud_efficiency” worker_disk_size: "" => “40” worker_instance_charge_type: "" => “PostPaid” worker_instance_types.#: "" => “1” worker_instance_types.0: "" => “ecs.c5.xlarge” worker_nodes.#: "" => “<computed>” worker_numbers.#: "" => “1” worker_numbers.0: "" => “2"alicloud_cs_managed_kubernetes.k8s: Still creating… (10s elapsed)alicloud_cs_managed_kubernetes.k8s: Still creating… (20s elapsed)alicloud_cs_managed_kubernetes.k8s: Still creating… (30s elapsed)# 以上省略alicloud_cs_managed_kubernetes.k8s: Creation complete after 6m5s (ID: cc54df7d990a24ed18c1e0ebacd36418c)Apply complete! Resources: 1 added, 0 changed, 0 destroyed.当出现申请完成!资源:1添加字样的时候,集群已经成功创建,此时我们也可以登录控制台后在控集群列表中看到集群。修改托管版Kubernetes集群在Terraform Provider中,我们提供了一部分参数的修改能力,一般情况下,所有非Force New Resouce(强制新建资源)的参数都可以被修改。下面我们修改部分参数,注释内容为更新的项目。provider “alicloud” { access_key = “FOO” secret_key = “BAR” region = “cn-hangzhou” version = “v1.26.0”}resource “alicloud_cs_managed_kubernetes” “k8s” { # 更换集群的名称为 test-managed-kubernetes-updated name = “test-managed-kubernetes-updated” availability_zone = “cn-hangzhou-h” worker_instance_types = [“ecs.c5.xlarge”] # 修改 worker_numbers 为 3,可以扩容一个 worker 节点 worker_numbers = [3] worker_disk_category = “cloud_efficiency” new_nat_gateway = true password = “Test12345” pod_cidr = “172.20.0.0/16” service_cidr = “172.21.0.0/20” install_cloud_monitor = true # 导出集群的连接配置文件到 /tmp 目录 kube_config = “/tmp/config” # 导出集群的证书相关文件到 /tmp 目录,下同 client_cert = “/tmp/client-cert.pem” client_key = “/tmp/client-key.pem” cluster_ca_cert = “/tmp/cluster-ca-cert.pem”}同创建集群一样,修改集群时使用的命令也是terraform apply。执行后我们得到以下日志输出,输入是并回车,我们就可以把该集群的名称改为test-managed-kubernetes-updated,worker节点扩容至3节点,同时将导出证书和连接文件到本机的/ tmp目录。xh4n3@xh4n3:~/ops/terraform-example% terraform applyalicloud_cs_managed_kubernetes.k8s: Refreshing state… (ID: cc54df7d990a24ed18c1e0ebacd36418c)An execution plan has been generated and is shown below.Resource actions are indicated with the following symbols: ~ update in-placeTerraform will perform the following actions: ~ alicloud_cs_managed_kubernetes.k8s client_cert: "” => “/tmp/client-cert.pem” client_key: "" => “/tmp/client-key.pem” cluster_ca_cert: "" => “/tmp/cluster-ca-cert.pem” kube_config: "" => “/tmp/config” name: “test-managed-kubernetes” => “test-managed-kubernetes-updated” worker_numbers.0: “2” => “3"Plan: 0 to add, 1 to change, 0 to destroy.Do you want to perform these actions? Terraform will perform the actions described above. Only ‘yes’ will be accepted to approve. Enter a value: yesalicloud_cs_managed_kubernetes.k8s: Modifying… (ID: cc54df7d990a24ed18c1e0ebacd36418c) client_cert: "” => “/tmp/client-cert.pem” client_key: "" => “/tmp/client-key.pem” cluster_ca_cert: "" => “/tmp/cluster-ca-cert.pem” kube_config: "" => “/tmp/config” name: “test-managed-kubernetes” => “test-managed-kubernetes-updated” worker_numbers.0: “2” => “3"alicloud_cs_managed_kubernetes.k8s: Still modifying… (ID: cc54df7d990a24ed18c1e0ebacd36418c, 10s elapsed)alicloud_cs_managed_kubernetes.k8s: Still modifying… (ID: cc54df7d990a24ed18c1e0ebacd36418c, 20s elapsed)alicloud_cs_managed_kubernetes.k8s: Still modifying… (ID: cc54df7d990a24ed18c1e0ebacd36418c, 30s elapsed)# 以上省略alicloud_cs_managed_kubernetes.k8s: Modifications complete after 4m4s (ID: cc54df7d990a24ed18c1e0ebacd36418c)Apply complete! Resources: 0 added, 1 changed, 0 destroyed.Terraform适用于运行成功后,控制台中显示的集群信息已经表明现在集群已经变成了我们期望的状态。在本机上,我们也通过导出的连接文件,用kubectl连接到集群。附录控制台创建托管版Kubernetes集群帮助文档https://help.aliyun.com/document_detail/95108.html云生态下的基础架构资源管理利器Terraform https://yq.aliyun.com/articles/215592阿里云Terraform提供者代码库https://github.com/terraform-providers/terraform-provider-alicloud阿里云Terraform提供商文档https://www.terraform.io/docs/providers/alicloud/index.html阿里云Terraform Provider文档 -托管版Kubernetes https://www.terraform.io/docs/providers/alicloud/r/cs_managed_kubernetes.htmlVPC下Kubernetes的网络地址段规划https://help.aliyun.com/document_detail/86500.htmlTerraform部署容器服务Kubernetes集群及WordPress的应用https://yq.aliyun.com/articles/641627本文作者:予栖.阅读原文本文为云栖社区原创内容,未经允许不得转载。 ...

December 26, 2018 · 4 min · jiezi

JSON数据从MongoDB迁移到MaxCompute最佳实践

摘要: 本文为您介绍如何利用DataWorks数据集成直接从MongoDB提取JSON字段到MaxCompute。数据及账号准备首先您需要将数据上传至您的MongoDB数据库。本例中使用阿里云的云数据库 MongoDB 版,网络类型为VPC(需申请公网地址,否则无法与DataWorks默认资源组互通),测试数据如下。{ “store”: { “book”: [ { “category”: “reference”, “author”: “Nigel Rees”, “title”: “Sayings of the Century”, “price”: 8.95 }, { “category”: “fiction”, “author”: “Evelyn Waugh”, “title”: “Sword of Honour”, “price”: 12.99 }, { “category”: “fiction”, “author”: “J. R. R. Tolkien”, “title”: “The Lord of the Rings”, “isbn”: “0-395-19395-8”, “price”: 22.99 } ], “bicycle”: { “color”: “red”, “price”: 19.95 } }, “expensive”: 10}登录MongoDB的DMS控制台,本例中使用的数据库为 admin,集合为 userlog,您可以在查询窗口使用db.userlog.find().limit(10)命令查看已上传好的数据,如下图所示。 此外,需提前在数据库内新建用户,用于DataWorks添加数据源。本例中使用命令db.createUser({user:“bookuser”,pwd:“123456”,roles:[“root”]}),新建用户名为 bookuser,密码为 123456,权限为root。使用DataWorks提取数据到MaxCompute新增MongoDB数据源进入DataWorks数据集成控制台,新增MongoDB类型数据源。 具体参数如下所示,测试数据源连通性通过即可点击完成。由于本文中MongoDB处于VPC环境下,因此 数据源类型需选择 有公网IP。 访问地址及端口号可通过在MongoDB管理控制台点击实例名称获取,如下图所示。 新建数据同步任务在DataWorks上新建数据同步类型节点。 新建的同时,在DataWorks新建一个建表任务,用于存放JSON数据,本例中新建表名为mqdata。 表参数可以通过图形化界面完成。本例中mqdata表仅有一列,类型为string,列名为MQ data。 完成上述新建后,您可以在图形化界面进行数据同步任务参数的初步配置,如下图所示。选择目标数据源名称为odps_first,选择目标表为刚建立的mqdata。数据来源类型为MongoDB,选择我们刚创建的数据源mongodb_userlog。完成上述配置后, 点击转换为脚本,跳转到脚本模式。 脚本模式代码示例如下。{ “type”: “job”, “steps”: [ { “stepType”: “mongodb”, “parameter”: { “datasource”: “mongodb_userlog”, //数据源名称 “column”: [ { “name”: “store.bicycle.color”, //JSON字段路径,本例中提取color值 “type”: “document.document.string” //本栏目的字段数需和name一致。假如您选取的JSON字段为一级字段,如本例中的expensive,则直接填写string即可。 } ], “collectionName //集合名称”: “userlog” }, “name”: “Reader”, “category”: “reader” }, { “stepType”: “odps”, “parameter”: { “partition”: “”, “isCompress”: false, “truncate”: true, “datasource”: “odps_first”, “column”: [ //MaxCompute表列名 “mqdata” ], “emptyAsNull”: false, “table”: “mqdata” }, “name”: “Writer”, “category”: “writer” } ], “version”: “2.0”, “order”: { “hops”: [ { “from”: “Reader”, “to”: “Writer” } ] }, “setting”: { “errorLimit”: { “record”: "" }, “speed”: { “concurrent”: 2, “throttle”: false, “dmu”: 1 } }}完成上述配置后,点击运行接即可。运行成功日志示例如下所示。 结果验证在您的业务流程中新建一个ODPS SQL节点。 您可以输入 SELECT * from mqdata;语句,查看当前mqdata表中数据。当然这一步您也可以直接在MaxCompute客户端中输入命令运行。 本文作者:付帅阅读原文本文为云栖社区原创内容,未经允许不得转载。 ...

November 28, 2018 · 1 min · jiezi