13种重要的云原生工具,让交付过程更快

SUSE收购Rancher

Pure Storage收购Portworx

Veeam收购Kasten

VMware收购Octarine

Mirantis收购Lens IDE

思科收购Banzai Cloud

New Relic收购Pixie Labs

云原生市场趋于整合

……..

以上是过去一年云原生行业发生的并购案,可以看到,各家厂商都在积*地拥抱云原生、抢占市场先机,云原生*大的释放了云的红利,代表着云的新发展趋势。正确的选择云原生技术有助于提高应用的开发速度,减少运维的复杂性,并提高基础设施的利用率。

本文将介绍13种实现云原生所需的工具。

%title插图%num

什么是云原生?

云原生是利用云的优势来更快地处理业务并降低IT成本,云原生计算基金会(CNCF)对云原生的官方定义是:

“云原生技术有利于各组织在公有云、私有云和混合云等新型动态环境中,构建和运行可弹性扩展的应用。云原生的代表技术包括容器、服务网格(Service Mesh)、微服务、不可变基础设施和声明式API。

这些技术能够构建容错性好、易于管理和便于观察的松耦合系统。结合可靠的自动化手段,云原生技术使工程师能够轻松地对系统作出频繁和可预测的重大变更。”

简而言之,云原生的目标就是根据需求快速、敏捷地向用户交付软件产品。

%title插图%num

为什么云原生很重要?

云原生应用程序只需要小型团队就可以快速构建,它部署的平台可进行扩展和硬件解耦,这为企业跨云环境提供了*大的敏捷性、灵活性和可移植性。

  • 竞争优势

云原生开发能够节省IT成本,这意味着团队有更多的精力去拓展业务。在软件时代,能够及时响应客户需求,快速构建和交付应用程序的企业更有竞争力。

  • 弹性扩展

传统基础架构在发生故障时,服务很可能会受到影响。在云原生系统中的弹性架构可以快速扩展,这有助于企业构建一种保持在线的系统,而不用考虑环境中的问题。

  • 灵活性

借助支持云原生开发的平台,企业无需修改即可构建任何云上运行的应用程序。不受时间空间限制,企业可以在*有商业意义的情况下运行应用程序和服务,避免了云锁定。

  • 运营始终与业务需求保持一致

通过使IT运营自动化,企业可以将精力更多地放在业务上,并且消除了由人为错误造成的失败风险。在堆栈的各个级别上自动化实时补丁和升级,消除了停机时间,并且节省了人工劳动力。

%title插图%num

云原生工具

以下是业界常用的云原生工具及方法。各大公司通过使用它们,实现了更快的产品交付,更少的摩擦,以及更低的开发与维护成本。

1.微服务

微服务是一种云原生架构方法,其中单个应用程序由许多松散耦合且可独立部署的较小组件或服务组成。在传统的部署中,通常只有一个网站服务来管理API和客户交互,若使用微服务,我们就可以将该网站分解为多种服务,例如结帐服务和用户服务,我们可以分别开发,部署和扩展这些服务。此外,微服务通常是无状态的,公司可充分利用云原生提供的灵活性。

推荐技术:Node.js

替代技术:Kotlin,Golang

2.CI / CD

CI / CD的主要概念是持续集成,持续交付和持续部署。这种方法简单而言就是在应用程序的整个生命周期(从集成和测试阶段到交付和部署)中引入了持续的自动化和连续监控。使用CI / CD,公司能够即使并且灵活地实施质量检验,例如单元测试,静态分析或安全性分析。总之,CI / CD是云原生生态系统中的基础工具,可以提高工程效率并减少错误。

推荐技术:Gitlab CI / CD

替代技术:Github Actions

3.容器

容器是云原生生态系统的核心,它通过简化开发人员的操作*大地提升了开发速度和质量。通过使用容器(例如Docker),团队可以指定其系统依赖关系,同时提供统一通用的执行层,该层使基础架构团队能够操作单个基础设施,例如容器编排工具(如Kubernetes)。工程团队可以将容器映像存储在容器注册表中,在大多数情况下,该注册表还提供漏洞分析和细粒度访问控制。这方面流行的服务有Docker Hub,Google Container Registry或Quay。

推荐技术:Docker

替代技术:Podman , LXD

4.容器编排

容器编排是用于启动和管理大量容器的工具,不需要特定语言或特定团队的部署策略。容器编排允许用户指定某个映像或某个映像组的配置,*后,编排器采用这些规范并将其转换为运行的工作负载。容器编排器使基础设施团队可以维护单个基础设施组件,该组件可以执行任何符合OCI规范的容器。

推荐技术:Kubernetes

替代技术:Google Cloud Run

5.基础架构即代码

基础设施即代码是一种通过代码来定义计算和网络基础设施的方法,它可以应用于任何软件系统中。这样的代码放在源代码管理中,具有可审查性、可重用型,并且符合测试惯例,还完全遵从持续交付的原则。

公司一般通过管理面板手动配置云资源,但是,手动配置很难跟踪更改。基础架构即代码通过将云资源定义为代码并将其置于版本控制之下来解决该问题。在代码中的基础设施配置中进行更改,并通过公司的部署过程(包括同行评审、CI/CD)进行升级。版本控制提供了一个审核日志,显示谁更改了哪些资源以及更改的时间。

推荐技术:Terraform

替代技术:Pulumi

6.隐私管理

隐私管理对于云原生解决方案至关重要,但总是被忽略。这里的隐私可以是任何私密的东西,例如密码,私钥和API凭据。成熟的解决方案支持发布临时数据库凭证或轮换凭证,使隐私管理更加安全。一个合适的隐私管理解决方案对于云原生应用非常重要,因为容器化服务可以水平扩展,并且可以在许多不同的设备上调度,忽略隐私管理的组织可能会增加数据泄漏的风险。

推荐技术:Vault

替代技术:Sealed Secrets

7.认证

通过TLS保证通信安全是必不可少的。这在基于容器的解决方案中尤其重要,因为许多不同的服务可能在同一台物理计算机上运行。如果不进行加密,攻击者就可以获得主机网络的访问权限,他们可以读取这些服务之间的所有流量。总之,手动更新云本地部署的证书是站不住脚的,这就是为什么某种自动化解决方案是必不可少的。

推荐技术:cert-manager

替代技术:Google managed certificates

8. API网关

API网关是反向代理,比起传统反向代理(例如Apache和NGINX),API网关还支持:

gRPC、HTTP/2和Websockets等协议

动态配置

双向TLS

路由

提供诸如速率限制和电路中断等云服务弹性功能

提供各类监控指标的可见化

推荐技术:Kong

替代技术:Ambassador

9.日志

日志是可观察性的基础。日志对于了解系统中正在发生的事情至关重要,而且对于调试系统也非常有帮助。云原生工具强调指标的时间序列,因为它们比存储日志更具成本效益。

推荐技术:EFK

替代技术:Loki

10.监控

我们可以为监控指标分配不同的维度,以深入了解单个计算机、操作系统、版本等性能。监视系统会将重要事件按时间顺序存储,但它不会存储所有事件。这使其对于云本机系统具有成本效益,用户可以为监视指标分配不同的维度,以深入了解单个计算机、操作系统、版本等的性能。

推荐技术:Prometheus、Grafana

替代技术:Datadog

11.警报

警报可将系统问题通知操作员,例如,当HTTP 500状态代码增加或请求持续时间增加时,警报就会通知团队。警报对于云原生系统非常重要,没有警报,公司就不知道出现了问题。

推荐技术:Prometheus Alertmanager

替代技术:Grafana Alerts

12.追踪

云原生技术减少了扩展服务的成本,因此,我们可以推出更多的服务。追踪技术能够监视服务之间的通信,并将整个事务中的每个阶段都进行可视化,当出现性能问题时,团队可以查看发生了哪些服务错误以及事务的每个阶段花费了多长时间。追踪技术使团队可以更快地调试问题,减少停机时间。

推荐技术:Jaeger

替代技术:Zipkin

13.服务网格

作为上述功能的综合,云服务网格可以提供动态路由、负载平衡、服务发现、网络策略,以及包括断路器和重试机制在内的云服务弹性功能。我们可以将服务网格视为云计算原生架构在负载平衡方面的一种演进。

推荐技术:Istio

替代技术:Consul

通过灵活地选用上述云原生工具,我们可以适当地提高云服务产品的交付速度,增加服务的可用性与性能,以及保持企业的竞争优势。

参考:

https://codersociety.com/blog/articles/cloud-native-tools

从开源视角分析,搞定边缘计算云原生方案选型

随着Kubernetes已经成为容器编排和调度的事实标准,各大公有云厂商都已经基于Kubernetes提供了完善的Kubernetes云上托管服务。同时也看到越来越多的企业、行业开始在生产中使用Kubernetes, 拥抱云原生。在各行各业数字化转型和上云过程中,公有云厂商也在主动拥抱传统线下环境,在思考各种各样的解决方案使云上能力向边缘(或线下)延伸。

而Kubernetes由于屏蔽了底层架构的差异性,可以帮助应用平滑地运行在不同的基础设施上的特性,云上的Kubernetes服务也在考虑拓展其服务边界,云原生和边缘计算结合的想法自然就呼之欲出了。

目前国内各个公有云厂商也都开源了各自基于Kubernetes的边缘计算云原生项目。如华为云的KubeEdge,阿里云的OpenYurt,腾讯云的SuperEdge。目前网上很少有从技术视角来介绍这几个项目优缺点的文章,本文试着从技术视角,从开源视角来分析这几个项目,希望可以给大家做项目选型时提供一些借鉴。

%title插图%num

比较思路

这几个项目都是云边一体,云边协同的架构,走的是Kubernetes和边缘计算结合的路数,因此决定从以下几点比较:

(1) 各个项目的开源状况:比如开源项目的背景、开源的时间、是否进入了CNCF等;

(2)Kubernetes架构:

  • 先对比与Kubernetees的架构差异:主要关注是否修改Kubernetes,和;Kubernetes一键式转换等
  • 根据架构差异对比和Kubernetes的能力增强点;主要关注边缘自治,边缘单元化,轻量化等能力
  • *后看一下架构差异可能带来的影响: 主要关注运维监控能力,云原生生态兼容性,系统稳定性等方面

(3)对边缘计算场景支持能力:

  • 主要关注是否具备端设备的管理能力

接下来以项目的开源顺序,从上述几个方面来介绍各个项目。

%title插图%num

边缘云原生开源项目对比

2.1 KubeEdge

(1)开源状况

KubeEdge是华为云于2018年11月份开源的,目前是CNCF孵化项目。其架构如下:

%title插图%num

(2)与Kubernetes的架构差异

首先从架构图可以看到,云端(k8s master)增加了Cloud Hub组件和各类controller,而在边缘端(k8s worker)没有看到原生的kubelet和kube-proxy,而是一个对原生组件进行重写了EdgeCore组件。

从架构图看EdgeCore是基于kubelet重构的,为了保证轻量化,裁剪了原生kubelet的部分能力,同时也增加了很多适配边缘场景的能力。具体如下:

  • Cloud Hub+EdgeHub模块: 抛弃了原生kubernetes 的组件间数据同步list/watch机制,改成基于websocket/quic协议从云端往边缘推送模式。
  • 节点元数据缓存模块(MetaManager): 把节点维度的数据持久化在本机的SQLite数据库中,当云边网络不稳定时Edged模块将从本地数据库中获取数据用于业务的生命周期管控。
  • DeviceController+设备管理模块(DeviceTwin): 把设备管理能力直接集成到EdgeCore中,为用户提供原生的设备管理能力。

上述的架构设计,对比Kubernetes的能力增强点主要有:

  • 边缘自治:通过增加节点元数据缓存,可以规避云边断网状态下,边缘业务或者节点重启时,边缘组件可以利用本地缓存数据进行业务恢复,这就带来了边缘自治的好处。
  • 轻量化: 削减了部分kubelet功能(如CSI,CNI等),从而使边缘EdgeCore组件相比原生kubelet组件更加轻量。同时因为节点上增加了SQLite数据库,所以节点维度相比原生节点是否轻量待确认,欢迎熟悉的同学提供数据。

架构差异可能带来的影响:

  • 云原生生态兼容性不足:
  1. 跟随社区同步演进挑战大: 由于对Kubernetes系统的侵入式修改,后续跟随Kubernetes社区的演进将会遇到很大挑战。
  2. 边缘节点无法运行Operator:因为云边通信机制的修改,Cloud Hub只能往边缘推送有限的几种资源(如Pod,ConfigMap等)。而Operator既需要自定义CRD资源,又需要list/watch云端获取关联资源,因此社区的Operator无法运行的KubeEdge的边缘节点上。
  3. 边缘节点不适合运行需要list/watch云端的应用: 因为云边通信机制的修改,导致原来需要使用list/watch机制访问kube-apiserver的应用,都无法通过hub tunnel 通道访问kube-apiserver,导致云原生的能力在边缘侧大打折扣。
  • 运维监控能力支持有限:

    因为目前云边通信链路是kube-apiserver –> controller –> Cloud Hub –>EdgeHub –>MetaManager等,而原生Kubernetes运维操作(如kubectl proxy/logs/exec/port-forward/attch等)是kube-apiserver直接请求kubelet。目前KubeEdge社区*新版本也仅支持kubectl logs/exec/metric,其他运维操作目前还不支持。

  • 系统稳定性提升待确定:
  1. 基于增量数据的云边推送模式:可以解决边缘watch失败时的重新全量list从而引发的kube-apiserver 压力问题,相比原生Kubernetes架构可以提升系统稳定性。
  2. Infra管控数据和业务管控数据耦合:Kubernetes集群的管控数据(如Pod,ConfigMap数据)和边缘业务数据(设备管控数据)使用同一条websocket链路,如果边缘管理大量设备或者设备更新频率过高,大量的业务数据将可能影响到集群的正常管控,从而可能降低系统的稳定性。

边缘计算场景支持能力

  • 设备管理能力: 这个能力直接集成在edged中,给iot用户提供了一定的原生设备管理能力。

2.2 OpenYurt

(1)开源状况

OpenYurt是阿里云于2020年5月份开源的,目前是CNCF沙箱项目。架构如下:

%title插图%num

(2)与Kubernetes的架构差异

OpenYurt的架构设计比较简洁,采用的是无侵入式对Kubernetes进行增强。在云端(K8s Master)上增加Yurt Controller Manager, Yurt App Manager以及Tunnel Server组件。而在边缘端(K8s Worker)上增加了YurtHub和Tunnel Agent组件。从架构上看主要增加了如下能力来适配边缘场景:

  • YurtHub: 代理各个边缘组件到K8s Master的通信请求,同时把请求返回的元数据持久化在节点磁盘。当云边网络不稳定时,则利用本地磁盘数据来用于边缘业务的生命周期管控。同时云端的Yurt Controller Manager会管控边缘业务Pod的驱逐策略。
  • Tunnel Server/Tunnel Agent: 每个边缘节点上的Tunnel Agent将主动与云端Tunnel Server建立双向认证的加密的gRPC连接,同时云端将通过此连接访问到边缘节点及其资源。
  • Yurt App Manager:引入的两个CRD资源: NodePool 和 UnitedDeployment. 前者为位于同一区域的节点提供批量管理方法。后者定义了一种新的边缘应用模型以节点池维度来管理工作负载。

上述的架构设计,对比Kubernetes的能力增强点主要有:

  • 边缘单元化:通过Yurt App Manager组件,从单元化的视角,管理分散在不同地域的边缘资源,并对各地域单元内的业务提供独立的生命周期管理,升级,扩缩容,流量闭环等能力。且业务无需进行任何适配或改造。
  • 边缘自治: 因为每个边缘节点增加了具备缓存能力的透明代理YurtHub,从而可以保障云边网络断开,如果节点或者业务重启时,可以利用本地缓存数据恢复业务。
  • 云边协同(运维监控):通过Tunnel Server/Tunnel Agent的配合,为位于防火墙内部的边缘节点提供安全的云边双向认证的加密通道,即使边到云网络单向连通的边缘计算场景下,用户仍可运行原生kubernetes运维命令(如kubectl proxy/logs/exec/port-forward/attach等)。同时中心式的运维监控系统(如prometheus, metrics-server等)也可以通过云边通道获取到边缘的监控数据。
  • 云原生生态兼容:
  1. 所有功能均是通过Addon或者controller形式来增强Kubernetes,因此保证来对Kubernetes以及云原生社区生态的100%兼容。
  2. 另外值得一提的是:OpenYurt项目还提供了一个YurtCtl工具,可以用于原生Kubernetes和OpenYurt集群的一键式转换,

架构差异可能带来的影响:

  • 原生Kubernetes带来的系统稳定性挑战:因为OpenYurt没有修改Kubernetes,所以这个问题也是原生Kubernetes在边缘场景下的问题。当云边长时间断网再次恢复时,边缘到云端会产生大量的全量List请求,从而对kube-apiserver造成比较大的压力。边缘节点过多时,将会给系统稳定性带来不小的挑战。
  • 边缘无轻量化解决方案: 虽然OpenYurt没有修改Kubernets,但是在边缘节点上增加YurtHub和Tunnel Agent组件。目前在*小的1C1G的系统上运行成功,更小规格机器待验证。

边缘计算场景

  • 无设备管理能力:OpenYurt目前没有提供设备管理的相关能力,需要用户以workload形式来运行自己的设备管理解决方案。虽然不算是架构设计的缺点,但是也算是一个边缘场景的不足点。

2.3 SuperEdge

(1)开源状况

SuperEdge是腾讯云于2020年12月底开源的,目前还是开源初期阶段。其架构如下:

%title插图%num

(2)与Kubernetes的架构差异

SuperEdge的架构设计比较简洁,也是采用的无侵入式对Kubernetes进行增强。在云端(K8s Master)上增加Application-Grid Controller, Edge-Health Admission以及Tunnel Cloud组件。而在边缘端(K8s Worker)上增加了Lite-Apiserver和Tunnel Edge,Application-Grid Wrapper组件。从架构上看主要增加了如下能力来适配边缘场景:

  • Lite-Apiserver: 代理各个边缘组件到K8s Master的通信请求,同时把请求返回的元数据持久化在节点磁盘。当云边网络不稳定时,则利用本地磁盘数据来用于边缘业务的生命周期管控。同时基于边缘Edge-Health上报信息,云端的Edge-Health Admission会管控边缘业务Pod的驱逐策略。
  • Tunnel Cloud/Tunnel Edge: 每个边缘节点上的Tunnel Edge将主动与云端Tunnel Cloud建立双向认证的加密的gRPC连接,同时云端将通过此连接访问到边缘节点及其资源。
  • Application-Grid Controller:引入的两个CRD资源: ServiceGrids和 DeploymentGrids. 前者为位于同一区域的业务流量提供闭环管理。后者定义了一种新的边缘应用模型以节点池为单位来管理工作负载。

与OpenYurt对比

  • 从SuperEdge的架构以及功能分析下来,发现SuperEdge从架构到功能和OpenYurt基本一致。这也从侧面印证,边缘计算云原生这个领域,各大厂商都在如火如荼的投入。
  • SuperEdge与Kubernetes的对比分析可以参照OpenYurt的分析,这里我们从代码角度分析SuperEdge和OpenYurt的差异:
  • YurtHub和Lite-Apiserver: YurtHub采取了完善的证书管理机制和本地数据缓存设计,而Lite-Apiserver使用的是节点kubelet证书和数据简单缓存。
  • Tunnel组件:OpenYurt的Tunnel组件是基于Kubernetes社区的开源项目ANP(github.com/kubernetes-s),同时实现了完善的证书管理机制。而SuperEdge的Tunnel组件同样也是使用节点证书,同时请求转发是基于自行封装的gRPC连接。OpenYurt底层的ANP相比原生gRPC,会更好适配kube-apiserver的演进。
  • 单元化管理组件: OpenYurt单元化管理支持Deployment和StatefulSet,而SuperEdge的单元化管理只支持Deployment。另外OpenYurt的NodePool和UnitedDeployment的API定义是标准云原生的设计思路,而SuperEdge的ServiceGrids和 DeploymentGrids的API定义显得随意一些。
  • 边缘状态检测,这个能力OpenYurt未实现,SuperEdge的设计需要kubelet监听节点地址用于节点间互相访问,有一定安全风险。同时东西向流量也有不少消耗在健康检查上。期待这个部分后续的优化。

2.4 对比结果一览

根据上述的对比分析,结果整理如下表所示:

项目 华为KubeEdge 阿里OpenYurt 腾讯SuperEdge
是否CNCF项目 是(孵化项目) 是(沙箱项目)
开源时间 2018.11 2020.5 2020.12
侵入式修改Kubernetes
和Kubernetes无缝转换 未知
边缘自治能力 有(无边缘健康检测能力) 有(无边缘健康检测能力) 有(安全及流量消耗待优化)
边缘单元化 不支持 支持 支持(只支持Deployment)
是否轻量化 是(节点维度待确认)
原生运维监控能力 部分支持 全量支持 全量支持(证书管理及连接管理待优化)
云原生生态兼容 部分兼容 完整兼容 完整兼容
系统稳定性挑战 大(接入设备数量过多) 大(大规模节点并且云边长时间断网恢复) 大(大规模节点并且云边长时间断网恢复)
设备管理能力 有(有管控流量和业务流量耦合问题)

%title插图%num

各个开源项目,整体比较下来的发现

KubeEdge和OpenYurt/SuperEdge的架构设计差异比较大,相比而言OpenYurt/SuperEdge的架构设计更优雅一些。而OpenYurt和SuperEdge架构设计相似,SuperEdge的开源时间晚于OpenYurt,项目成熟度稍差。

如果打算选择一个边缘计算云原生项目用于生产,我会从以下角度考虑:

  • 如果需要内置设备管理能力,而对云原生生态兼容性不在意,建议选择KubeEdge
  • 如果从云原生生态兼容和项目成熟度考虑,而不需要设备管理能力或者可以自建设备管理能力,建议选择OpenYurt

迁移到基于云的服务器的核心优势和注意事项

如果您找到了一种降低成本,利用规模经济并专注于核心业务的简单方法,该怎么办?通过升级到云中的服务器,您的组织可以避免过多的基础架构支出,因为它可以在高可用性环境中提高可靠性。

迁移到云可能意味着快速的技术进步,但基于云的服务器的真正威力在于为您的客户,您的员工和您的底线带来的好处。在云中部署业务有哪些主要优势?

迁移到云服务器的五大核心优势

可靠的安全性和灾难恢复

技术安全为各种规模的企业带来了重大的持续问题。当公司在本地硬盘上存储敏感数据(包括有关业务或其客户的专有信息)时,留下笔记本电脑的简单人为错误可能导致灾难。

基于云的服务器为企业提供更好的敏感数据保护,技术支持人员可以远程擦除错位的笔记本电脑和智能手机。当机器不可避免地出现故障时,组织可以进行即时备份,并且可以快速轻松地在另一台机器上启动和运行。

此外,将专用服务器虚拟化到云为灾难情景规划提供了强大的支持。遭受火灾,洪水,盗窃或恶意软件破坏的企业立即遭受损失,但长期停机和对客户关系的破坏会造成更大的危害。利用云中的所有重要数据,即使遇到灾难​​,企业也可以*大限度地减少关键任务服务的停机时间。

更广泛的竞争力

对于提供基于互联网的服务的公司,包括托管软件和在线销售,基于云的服务器是必须的。通过您在云中的运营,您可以在进入全球市场时为您的业务打开一个充满潜力的世界。当世界上任何人都可以通过网络浏览器访问您的产品和服务时,就像在您自己的后院中接触到全球客户一样容易。

除了超越地理界限,当您迁移到云时,您还可以通过其他方式提升您的业务。在基于云的服务器上,您可以访问*新的企业级基础架构和技术,从而为您的业务带来几乎无限的功能。小企业与主要企业竞争,客户只看到一流的服务。

增加灵活性,降低成本

如果您的业务需要灵活性和可扩展性,那么迁移到基于云的服务器将带来许多好处。随着您获得全球影响力 – 以及新客户 – 您可以轻松快速地扩展您的运营,以满足不断增长的需求。如果业务下降,您可以轻松缩小规模,同时将成本降至*低,并*大限度地减少基础设施的资金损失。

如果您的带宽需求季节性波动,您还可以在控制费用的同时快速扩大或缩小容量。凭借能够灵活适应商业环境变化的能力,您的企业仍然能够灵活地满足不断变化的需求 – 为您提供优于大型竞争对手的优势,这些竞争对手可能需要数月或数年才能适应市场的重大变化。

高效的团队成员

当您实施基于云的服务器时,您可以让您的员工和承包商几乎可以在任何地方工作。因此,您可以提供灵活的工作时间表和地点 – 被许多工人认为是一项重要的工作。通过提供远程工作的能力,您可以增强组织对全球高素质顶级候选人的需求。

可以选择远程工作的员工可以更好地平衡职业和个人生活,这可以对身心健康产生积*影响。灵活的工作环境使您的员工能够按照自己的条件保持高效。

提高客户满意度

部署基于云的服务器时,您可以利用让客户满意的优势。您的服务器软件可获得自动更新,无论您的业务应用程 无论您的客户是需要浏览您的网站,访问您的移动应用程序还是从您的数据库中提取信息,他们都可以通过您的云服务器通过互联网快速无缝地交付。

许多客户也更喜欢考虑其运营对环境影响的公司。通过使用基于云的服务器,您可以减少能源使用和不断变化的技术需求所带来的浪费。您的云存在使您可以为您的客户提供高效,环保的服务。

为什么要考虑迁移到基于云的服务器?从提高竞争力到更快乐的员工,更满意的客户和更低的成本,云运营的好处很快就会显现出来。即使您控制资本成本,您也可以提高覆盖全球客户的能力,并从*佳就业候选人中进行选择。凭借*大的灵活性,安全性以及您可以在任何灾难中保持正常运行的知识,您还可以获得*大的安心。

网站迁移到云服务器要注意什么

基于云的服务为企业带来了多项经济和技术优势。它们消除了传统服务器的硬件过时导致的成本,它们使应用程序和数据视图可通过移动应用程序和基于 Web 的应用程序从任何地理位置访问。他们还解决了随着业务的增长而出现的可伸缩性问题。使用云服务器部署在线业务,可以消除仅在高峰时段使用的购买和维护服务器容量的成本。但是,网站迁移到云服务器并不能解决的一个问题是,由于在线访问、管理和维护而导致的安全问题。在本文中,为您介绍云服务器的五个*大安全风险,以及如何避免这些风险。

一、政策监管问题

在互联网安全风险领域,监管制裁已成为影响企业盈利的一系列问题的一部分。它本身并不是安全风险,而是行业和法律框架,要求您适当地处理安全风险。这既是内部问题,也是外部问题。

您和您的合作伙伴需要遵守国家和行业制定的安全规范。在为您的企业选择云服务器时,您需要选择符合适用于您行业的法规的服务。这可能包括进行审核或委托独立研究。当您决定将网站或数据移至云服务器时,还需要进行风险评估。出于监管原因,您可能需要考虑使用私有云服务器。

二、数据丢失

数据丢失通常意味着数据损坏,减轻这些风险的主要方法是识别关键数据,并创建备份副本,这些备份副本将在数据因预见的事件而丢失时作为备用。云存储,常常被我们用来创建备份,但如果数据只存储在云上,则存在风险。一种解决方案是,额外选择一种云服务,其中包括对其服务进行数据的备份和还原。

三、数据泄露

对于大多数企业而言,*坏的情况是网络泄漏,其中数据被盗并在黑市上出售或向公众发布。数据泄露通常是外部参与者的工作,他们找到了一种途径来获得对公司网络的未经授权的访问,但是这也可能是员工无意间失去安全性的结果。因此,将云服务包含在企业的整体安全计划中,并分析其存在的漏洞,非常重要。

要规避这种风险,你可能需要在云服务器中运用不同类型的云应用程序,来隐藏您的网络隐藏,避免暴露给黑客。互联网可以访问公共云服务,而网络内部则存在私有服务。在确定哪种云服务*适合您的需求时应权衡安全风险。

四、内部欺诈

可以窃取敏感数据和通信的另一种方式是内部欺诈。在这种情况下,您的一名员工滥用了对您信息的访问权限。有时他们可能会为了报仇而发布内部信息,或者可能会受到外部人的贿赂以窃取客户数据。

对于云服务器,控制内部欺诈事件的*佳方法是实施监视和完善且强大的访问控制。

当您将基础架构外包给云服务器供应商时,您还需要研究他们拥有的控制措施,以保护您免受可能诱使您出售数据的员工的侵害。加密静态数据并控制谁可以访问它是阻止内部威胁的另外两种方法。

五、拒*服务攻击(DDoS)

当您将企业信息系统的一部分移至云服务时,访问它的主要方式是通过互联网。如果通过 DDoS 攻击使云服务器成功脱机,您将无法访问其托管的数据或应用程序。这并不意味着您不应该使用云服务器,但是在选择及其为您的业务托管时,您将不得不考虑这种额外的风险。

DDoS 攻击仅在连接到互联网的面向公众的云服务器上才有可能。 防止 DDoS 攻击风险的一种方法是将关键数据和应用程序放置在私有或混合云服务上。如果需要让公众可以使用该服务,那么还有一些安全服务可以抵御这些类型的攻击。

总之,从财务节省和运营带来的可伸缩性两方面来看,云服务器服务对您的业务而言都是有意义的,但是仔细评估安全风险很重要。在公共云服务器服务上托管敏感数据或关键应用程序可能会使您面临不必要的风险。值得庆幸的是,云安全性可以杜*大多数的威胁,并且你可以选择私有云来消除对 Internet 的访问。通过周密的计划和尽职调查,使用云服务器可以构成企业数字化转型的关键部分。

选择云服务器要注意七个方面

将业务迁移到云服务器,可以降低成本、简化工作流程,并消除对IT硬件的需求。

对小型企业来说,*大的好处就是能够专注于核心业务活动,让云服务器来解决维护、备份、可用性和数据安全等令人担忧的IT问题。

如果要追求性价比,在众多不断增长的云服务器供应商中,选择*合适的云服务器是一件复杂的事情。

下面就为您介绍选择云服务器要注意的七个方面,帮您筛选出潜在的*合适的云服务器供应商。

一、了解您需求的云服务类型
了解您的云计算需求将使您了解您所寻求的服务类型。

例如各种基于软件的云解决方案,包括用于照片、视频和文档存储,提供CRM服务等。

如果您需要的不仅仅是基本的数据存储,还有些云供应商会提供系列云计算解决方案,例如IT网络的基础架构以及按需访问软件,应用程序和虚拟服务器等。

二、企业数据及业务的安全问题
在将企业基础数据存储在云中时,在线安全性和安全性至关重要。

云计算解决方案提供商至少应该拥有几个标准的安全措施,并且需要不断更新,以防止每天发生的******等***。

寻求安全措施,如防病毒检测,防火墙,日常安全审计,以及数据加密和多重用户验证等。

您应该了解您的数据将保存哪个数据中心,并向云服务器供应商咨询如何保护其服务器免受自然灾害,如风暴,洪水,火灾和地震等。

此外,还要记得询问有关存储设施如何防盗,如何从物理和虚拟两个维度保护您的隐私信息。

三、云服务器的稳定性如何
首先我们要知道,即便是规模*大、*受信任的云供应商也会不时遭遇停机。

由于云服务器服务中断对于企业来说可能是代价高昂且相当具有破坏性的,因此*好选择系统相对稳定、宕机次数少的供应商,并了解清楚该云服务器供应商为确保在线率已采取的措施有哪些。

四、管理操作的便捷程度
云服务器通常都提供基于web的管理平台,只要使用账号登录即可随时随地访问和管理云服务器,并且支持使用PC端和多样化的移动端。

在选择云服务器时,可以先行试用,看看界面操作是否便捷、功能是否完善、能否满足日常使用需求,等等。

五、业务的升级、扩展是否灵活
主要看云服务商能否根据客户不断扩大的业务需求来调整其服务。

云服务器支持弹性资源伸缩是必须的。

此外您还需要了解额外扩展资源的*大容量花费。

正常情况下,在您原有基础上扩展资源,应该更加便宜。

六、提供哪些客户支持服务
可靠的云服务器供应商应当支持7*24小时技术服务,包括假期。

某些供应商免费提供这样的服务,某些供应商则收取不同的费用。

您还需了解其服务响应时间以及问题处理效率,具体可以从其官网客户反馈或业内口碑、用户满意情况来看。

七、计费方式和定价标准
很多云服务器供应商支持包年包月和按量计费等方式,您可以按需选择。

不过,您应该了解的不只是云服务器的基础租用费用,还要包括额外的技术支持服务费用等。

不同的云服务器供应商价格可能大不相同,取决于您的个人需求。

业务迁移到云服务器要考虑哪些因素

越来越多企业选择将业务部署到云环境中,并不是意味着每个人都应采用云环境,而是因为它是一种更灵活处理 IT 资源的*佳方法。由于无需备案、CN2 高速访问等优势,越来越多意图开拓跨境外贸市场的企业将其在线业务转移到恒创科技香港云服务器。但是,尽管云服务器在适应性、灵敏性和可用性方面具有优势,但其实你在将业务迁移至云服务器时要考虑以下因素:

一、迁移准备

在为将大规模业务迁移至云服务器做准备的时候,你需要考虑的是:你的公司员工是否了解这项重大举措的风险?你的员工是否具备管理全新的云服务器、云环境的知识和技能?此外,由于大规模迁移包含大量的数据和信息,你的安全框架是否在迁移期间能够规避潜在的安全隐患? 你的团队能否应对将资源放入云服务器中从而引起的变化?

二、迁移目的

将业务迁移至云服务器背后的目的:作为业务所有者,您应该对 “为什么要将业务迁移到云服务器” 背后的解释及其含义具有明确的了解。*有说服力的原因是:需要满足您的业务不断增长的对 IT 资源弹性分配的需求,这将促使更显著的盈利能力。其他原因可能包括:权威性举措的差异或需要重新校准库存的业务结构的变化。

三、云迁移的成本问题

不同的企业团队使用不同的成本计算方法。对少数人来说,由于业务规模较大,一次性投入大量资金来订购云服务器,从长远来看,是一种风险投资,他们希望对 IT 资源进行无限制的监督。但对其他人来说,难以接受一次性的入门成本,因此仅采用渐进式的云环境解决方案,运营成本更加合适。对于在一个月到一个月的前提中不断变化的 IT 资源需求场景下,这种选择可能非常合理,因为传统的服务器租用服务无法为他们提供这样快速灵活的适应性。无论采用哪种方法,在选择通常合适的云服务器之前,都必须考虑一下特定成本。*好的选择是从配置较低的低端云服务器开始入门,根据你的工作负载,按需扩展云服务器配置来应对日趋繁忙的流量高峰。

业务迁移到云服务器要考虑哪些因素

四、安全性和可用性

由于明显的安全性和可访问性问题,将您的大多数重要数据和信息移交给开放式的云服务器供应商可能会不堪重负。你需要确定你选择的云服务器供应商必须遵守严格的数据保护和保密约定,并且由于云服务器供应商拥有更多可访问的资源,因此与本地企业相比,它们可以实现并保持更高的安全级别。

五、迁移的必要性

在迁移到云服务器时,您需要关注每一个细微的地方,例如,哪些特定的数据、服务器或应用程序应当重新部署?您的业务是否需要大规模完全迁移,还是将您的一小部分数据迁移到云中?或者你是否可以将数据库放在本地存储,而将应用程序等计算性能交由云服务器供应?或许某些需求,在不需要迁移到云服务器的情况下就能得到解决。

六、IT 员工的顾虑

IT 环境中的任何变化都会使员工感到焦虑。您可能会这种转变会带来直接或间接的利益和技术优势,但真正的问题在于日后管理云服务器和云环境下所有应用和数据的 IT 团队。你需要确定你的 IT 团队已经具备完全无碍地管理云服务器和内部数据的技能和知识,你需要确保他们接受合理的培训,并为迁移过程中出现的任何问题预备解决方案。

七、日志分析和指标收集

将业务迁移到云服务器的企业*终不得不应对新的环境和条件,这需要另一种日志检查和指标收集。或许之前你经常遇到的服务器问题,在迁移到云服务器后将完全消失,或者以前在传统服务器上没有遇到的问题,在迁移到云服务器之后会出现新的困难和挑战。总之,你需要为这些适应全新环境而产生的不可控因素提前做好准备。

总之,公共云服务器的明显缺点是安全风险和控制失控。云服务器集成的众多智能化的一键式操作,在提升工作效率和智能化程度的同时,也很容易在 IT 员工的误操作之下造成恶劣影响。到目前为止,大多数支持云服务器的企业,都将获得更高的安全性、灵活性和可用性。但关键在于你的团队能够准备合理的运用云服务器并充分发挥其独特优势。

云计算安全威胁集中营

云计算是一种新的计算方式,它依托于互联网,以网络技术、分布式计算为基础,实现按需自服务、快速弹性构建、服务可测量等特点的新一代计算方式。然而,任何以互联网为基础的应用都存在着一定危险性,云计算也不例外,安全问题从云计算诞生那天开始就一直受人关注。当所有的计算行为和数据存储都散布在聚散无形虚无缥缈的云中的时候,人们将会普遍感到失控的恐慌。还记得《手机》这部电影吗?手机给生活提供了*大便利,但也给人带来了无尽烦恼,将人与人之间的距离拉得太近了,毫无隐私可言,让人都喘不过气来。云计算可以说比手机还甚,云计算中的数据对于数据所有者以外的其它云计算用户是保密的,但是对于提供云计算的商业机构而言确是毫无秘密可言。当然,这还不是问题关键,人们收入和资金方面有隐私,但并不妨碍人们乐于将资金放到银行里管理,*为关键的还是云计算自身安全技术的问题。云计算作为一种新计算技术,要面临更多安全威胁。云安全联盟是中立的非盈利世界性行业组织,致力于国际云计算安全的全面发展,也是云计算安全领域的权威组织。云安全联盟在2016年的报告中提出十二大云安全威胁,本文依据此并结合实际应用,总结出云计算的安全威胁“集中营”。

安全域无边界

在对外提供云计算业务之前,数据中心都是独立机房,由边界防火墙隔离成内外两块。防火墙内部属于可信区域,自己独占,外部属于不可信区域,所有的攻击者都在这里。安全人员只需要对这一道隔离墙加高、加厚即可保障安全,也可以在这道墙之后建立更多防火墙形成纵深防御;在开始提供云计算业务之后,这种简洁的内外隔离安全方案已经行不通了,通过购买云服务器,攻击者已经深入提供商网络的腹地,穿越了边界防火墙。云计算内部的资源不再是由某个用户独享,而是几万、几十万甚至更多互相不认识的用户共有,当然也包含一些恶意用户,这些用户可以轻而易举进入云计算内部,窃取私密信息。传统划分安全域做隔离已经行不通了,哪里有云计算提供的服务,哪里就需要安全,安全防护要贯穿到整个云计算的所有环节,这无疑将提升云计算安全部署的成本,即便这样防御效果还不见比以前好。

虚拟化难捕捉

云计算时代,一台服务器上可以运行十台虚机,这些虚机还可能属于十个不同用户。攻击者虚拟机可直接运行在这台服务器的内存里面,仅仅是使用一个虚拟层隔离,一旦攻击者掌握了可以穿透虚拟层漏洞,就可以毫不费力完成入侵,常见的虚拟化层软件如XEN、KVM都能找到类似安全漏洞。一般服务器是一台标准Linux服务器,运行着标准的Linux操作系统以及各种标准的服务,被攻击者攻破的通道很多。为了实现虚拟机自动迁移,虚拟化技术被应用于云计算网络中,这种虚拟网络是一个大二层架构,甚至是跨越机房、跨越城市的大二层架构。在这样一个超大的二层虚拟网络中,ARP欺骗、以太网端口欺骗、ARP风暴、NBNS风暴等二层内部的攻击问题,危害性都远远超过了它们在传统网络中的影响。

数据泄露量大

在传统网络中也存在数据泄露威胁,但在云计算环境中数据泄露严重性更高。由于云服务器上保存了大量客户的隐私数据,在多租户环境下,一个客户应用存在漏洞可能就会导致其他客户数据的泄露。数据泄露不仅导致商业机密、知识产权和个人隐私的泄露,而且对企业而言可能产生毁灭性打击。云计算依托的基础就是海量数据,只有在超大型的数据中心才能充分发挥作用,而海量数据若发生泄露,造成的损失很大,尤其是各种数据混杂在一起,做不好数据防护,很容易被人所窃取。恶意黑客会使用病毒、木马或者直接攻击方法永久删除云端数据来危害云系统安全。

攻击频率急剧增大

正所谓“树大招风”,没有部署云计算时,承载信息服务的各个数据中心散列在各处,即便被攻击,受影响的面都不会太大。现在的数据中心建设规模都很大,因为只有规模上去,云计算的优势才能更加明显。所以现在拥有数十万台服务器的数据中心屡见不鲜,这就将很多数据集中在一起,再交由云计算处理。拥有海量数据的数据中心,目标太大,很容易成为别人的目标。还有云计算用户多样性而且规模巨大,这样遭受的攻击频率也是急剧增大。以阿里云为例,平均每天遭受数百起DDoS攻击,其中50%攻击流量超过5GBit/s。针对WEB的攻击以及密码破解攻击更是以亿计算。这种频度的攻击,给安全运维带来巨大的挑战。

除此之外,不安全的接口和界面、新的攻击方式、混乱的身份管理、高级持续性威胁、审查不充分、恶意SaaS应用、共享技术问题等等,都是云计算要面临的安全威胁。解决好云计算安全威胁问题,将会为云计算的发展打下坚实的基础,让更多的人乐于接受云计算,将自己的信息数据安心放到云计算应用中来。在这里将云计算的安全威胁比作了集中营,就是希望放到集中营中的安全威胁越来越少,*后集中营能够解散掉,彻底消除各种云计算安全威胁。云计算的建设者已经意识到解决安全问题的急迫性,已经提出了不少抵御安全威胁的解决方案,这些都有待实践来验证。这个过程一定还有很长的路要走,需要依赖技术、管理和产业等各行业的共同努力才能实现。

未来,边缘计算的功能支柱是 Kubernetes

在数字化转型时代,5G网络是一个飞跃。5G正在推动边缘计算的发展,而Kubernetes则是5G与边缘计算之间的粘合剂。

%title插图%num

云计算的十年

云计算之于信息技术(IT)产业,就像石油之于汽车工业一样。云让数据触手可及,彻底改变了科技领域。本世纪初,亚马逊的EC2播下了云的种子。谷歌云和微软Azure紧随其后。然而,云计算的真正增长是在2010-2012年之后才突飞猛进的。

%title插图%num

Synergy Resarch Group云供应商增长图

未来的统计和趋势:

  • 到2021年,云计算将处理90%以上的工作负载(Cisco)
  • 企业将在私有云中运行大约41%的工作负载,在公有云中运行38%(RightScale)
  • 到2021年底,75%的计算实例和云工作负载将采用SaaS(Cisco)

%title插图%num

云原生

云原生的出现是云生态系统进化发展的一个标志。云原生架构的基本特性是对基础设施的多层抽象。这意味着云原生架构师必须通过代码定义这些层。在进行编码时可以加入一些功能,*大程度地提高业务价值。云原生还能够实现基础设施的可扩展性。

云原生容器技术为云原生生态系统的未来发展做出了显著贡献。它还在实现5G架构的实时能力方面发挥着重要的作用。借助以容器为中心的Web服务,5G网络公司能够提高弹性并更快地部署。容器使部署过程变得容易,它为5G基础设施提供了强大的动力,以扩展跨云网络的数据需求。组织可以利用容器来处理数据并进行大规模计算。

%title插图%num

来源:红帽

容器和DevOps的融合同样对于5G的发展起到了推动作用。引入这些松耦合服务将有助于5G供应商自动化应用部署,快速接收反馈,消除瓶颈,并持续改进。他们可以通过混合云按需提供资源,并进行统一管理。

电信行业已经点燃了“云原生之火”,未来十年将迅速蔓延。

%title插图%num

容器网络功能(CNF)的兴起

我们见证了容器网络功能(CNF)的兴起。CNF可以在跨多云或混合云基础设施的Kubernetes上运行。与传统上运行在VMware环境中的VNF相比,CNF是超轻量级的。CNF架构的一个关键因素是,它可以部署在裸机服务器上,从而大大降低了成本。

5G是电信领域的下一波浪潮,它有望提供具有自动化、弹性和透明的下一代服务。从需求架构来看,VNF(VMware环境)对于5G提供商而言不是理想的选择。从逻辑上讲,采用CNF是未来发展的必然选择。当然,完全抛弃VMware并非易事。因此,VNF和CNF的混合模型也是不错的选择。

%title插图%num

资料来源:Linux基金会

%title插图%num

边缘计算必须重塑方向

多个设备同时生成大量数据。让中心云来处理这些数据是一项艰巨的任务。边缘计算体系结构使基础设施靠近分布式环境中的数据设备,从而缩短了响应时间并降低了延迟。边缘计算对数据的本地处理简化了流程并降低了总成本。迄今为止,边缘计算一直是电信行业的催化剂。然而,随着5G的到来,这些界限都将被打破。

5G使宽带速度提高了10倍,并使设备密度骤降了大约100万台/平方公里。为此,5G需要超低延迟,这可以通过边缘计算支持的数字基础设施实现。

为确保5G的发展,边缘计算必须确保:

  • 更好的设备管理
  • 资源利用率高
  • 更轻量级的功能
  • 超低延迟
  • 增加安全防护,保障数据传输可靠性

%title插图%num

Kubernetes:在边缘为5G赋能

kubernetes(K8s)是一个开源的,用于管理云平台中多个主机上的容器化的应用。Kubernetes简化了跨分布式环境的底层计算,并在边缘赋予了一致性。Kubernetes通过自动化和快速部署以及更广泛的安全覆盖,帮助网络提供商*大化边缘容器的价值。用于边缘计算的Kubernetes将消除大多数劳动密集型工作负载,从而提高生产率和质量。

至少到目前为止,Kubernetes在释放5G的商业价值方面发挥着重要作用。唯一可以替代Kubernetes的是VMware,但由于空间和成本问题,目前暂时还无法做到。事实证明,Kubernetes架构可加速关键任务工作负载的自动化并降低5G部署的总体成本。

Kubernetes可以创建一个自我控制的闭环,从而确保所需数量的微服务被托管并控制在所需的级别。此外,Kubernetes的Horizontal Pod Autoscaler可以根据边缘的工作负载释放新的容器实例。

%title插图%num

KubeEdge:为Kubernetes提供优势

KubeEdge是一个基于Kubernetes的开源项目。*新版本KubeEdge v1.3对Kubernetes的功能进行了改进,可在边缘推动容器化应用程序的智能编排。KubeEdge通过对网络、应用程序、部署和元数据的基础设施支持,简化了边缘和云数据中心之间的通信。它还允许程序员创建自定义的逻辑脚本,以在资源受限的边缘设备通信。

%title插图%num

来源:Kubeedge.io

%title插图%num

未来

物联网、大数据和人工智能带来的设备激增将产生庞大的数据量。对于5G的而言,边缘计算必须处理这些复杂的工作负载并保持数据弹性。因此,Kubernetes将成为边缘计算的功能支柱,在编排容器化应用中赋予弹性。

原文链接:

https://dzone.com/articles/kubernetes-bridging-the-gap-between-5g-and-intelli

低代码,让人人都可以是开发者

今年2月23日,外国低代码平台提供商Creatio宣布获得6800万美元融资;2月22日,国内SaaS软件厂商黑湖智造宣布完成C轮近5亿元人民币融资。国内外的低代码开发平台备受投资方青睐,被各大软件和互联网媒体捧上“C位”。

但是,抛开冗长晦涩的专业术语,你真的懂低代码了吗?我想写一篇*接地气的文章,讲讲“低代码”到底是怎么一回事儿。

%title插图%num

“低代码”的起源和走过的路

低代码的故事要从上世纪80年代说起,当时计算机科学理论已逐步发展成熟,不少高级程序设计语言都逐渐开发完善。这时,编程界推出了“结构化语言”,即以功能指令为单位,把相应的代码封装好。当程序员要系统运行某个功能时,只需发出指令,计算机就知道要运行对应的代码。

到了2000年,“VPL”(可视化编程语言)出现了。顾名思义,就是在第四代编程语言的基础上,把系统运行的过程以更视觉化方式呈现,例如图标、表格、图表等形态。

随着高级编程语言不断发展成熟,以及国内外计算机人才的培养规模逐渐扩大,2010-2015年称得上是传统软件和SaaS软件兴起的时代,市场规模稳步增长。就是在这一时期,编程人员承接了许多软件开发项目。他们发现:软件的功能大同小异,重复度很高,导致很大部分的软件开发成本都浪费在重复的功能编程上。

而Forrester,一家国际知名的技术和市场调研公司,敏锐地发现了这一问题,并在2014年首次提出低代码和零代码的概念:只需用很少甚至几乎不需要代码就可以快速开发出系统,并可以将其快速配置和部署的一种技术和工具。随后在2018年,Gartner提出aPaaS(应用平台即服务)和iPaaS(集成平台即服务)的概念。

%title插图%num

在这两个概念出现并逐渐传播的时间里,国外软件厂商就陆续发布出低代码或零代码开发平台,探索并证明了这类产品成功的可能性。基于外国的成功初探,中国市场也掀起了“低代码/零代码”的热潮,并在近两年逐步形成完整的产品生态体系。

%title插图%num

图源见水印

%title插图%num

“低代码”为何而生?

低代码开发平台至今已发展得较为成熟,现在我们站在较高的“上帝视角”,回顾“低代码”诞生的合理性。其实,低代码平台除了击破重复编程的高成本痛点之外,还解决了两大难点:沟通隔阂和效率问题。

1、需求方与技术方之间的认知和沟通隔阂

传统的软件定制开发环节中,需求方往往会提一大堆业务流程、数据收录、界面设计等要求。经验丰富的技术员能理解甲方的业务流程,用正确的逻辑完成开发。而欠缺业务经验的技术员则照着“单子”来开发,这种粗暴的方式往往也埋下了不少系统逻辑不自洽、出bug、流程不通等隐患。技术方不懂业务怎么运转,需求方不懂系统语言和逻辑,双方存在认知和沟通隔阂。

低代码开发平台凭着自身可视化、易理解的配置功能,让业务人员更清楚如何用上面的功能来开发应用;开发人员也能借助平台的界面、功能使用指南,更轻松地让业务人员理解应用实施逻辑。现在市面上*大多数的低代码平台也在主张由业务人员自行实施应用,背后也是这个道理。

2、友好的操作界面提高应用实施、漏洞排查和修复效率

也是因为可视化、交互化、简洁的平台界面,应用开发者能更高效地实施开发,不用对着满满一屏幕的黑底白码埋头苦干。同样地,排查及修复bug的效率也因信息简化了而更容易提高效率。

%title插图%num

以轻流产品为例,展示数据表字段配置界面

%title插图%num

“低代码”的技术特点

谈完低代码是为降低软件开发的成本、沟通和实施效率而生,我们来看看它有哪些技术特点。

1、两种模式:基于表单或引擎驱动 以及 基于aPaaS平台

目前大部分低代码开发平台都属于下述模式的其中一类。

基于表单或引擎驱动 基于aPaaS平台
定义 通过建立多张表单,使用流程串联,定义报表输出方式,构建表单类轻应用 以应用开发平台为核心,承载各种开发工具和复杂技术手段,并将其可视化、低代码化来使用
优点 · 功能简单易用易学

· 具备基础的自动化流程运转能力

· 采购成本较低

· 功能更多元

· 应用细节的颗粒度更高

· 应用开发的灵活度更高

· 开发技术壁垒高

· 场景局限性弱,满足大中小客户的需求

· 基本可实现复杂的系统开发和对接

缺点 · 开发技术壁垒低,缺乏技术竞争力

· 难以实现复杂的系统对接和功能配置

· 场景局限性强,主要服务中小客户

· 对应用开发者有技术能力要求

· 采购和实施的各项成本较高

更适合的应用场景 表单类应用,如:人事行政、资料归档、OA审批、客户管理等 复杂场景应用,如:ERP、生产全流程管理、CRM、物联网等
典型代表产品 魔方网表、云表、活字格 ClickPaaS、氚云、宜搭

大家可能还感觉不到有啥区别,让我来举个对比例子:

%title插图%num

%title插图%num

表单驱动模式的低代码平台主要以表单的形式运转业务流程;而aPaaS模式能借助应用平台打造一个立体空间,让不同部门的不同业务线彼此交叉贯通,还可以对接外部的系统。

2、颠覆传统:“低代码”和传统企业管理系统架构的差异

低代码开发平台除了自身模式不同,和传统企业系统管理相比,在系统结构和管理理念上也有颠覆式差异。

“低代码”将多个“系统烟囱”归整为一个集大成者,更灵活敏捷地创建中台架构。

传统的企业系统中,每个部门有不同的系统需求,于是各自采购自己的系统。但这些系统彼此孤立,独立运作,导致企业采购的软件系统冗杂。低代码平台则让*大部分部门的业务系统都能在一个平台里搭建,彼此联系,打破信息系统孤岛,同时降本增效,提升内部生产力。

“低代码”重塑业务部和技术部的分工定位,为业务部赋予系统定制化的能力和自由。

重塑业务和技术的分工定位,主要在于宏观到微观的企业系统管理运维上。技术部负责统筹企业在低代码开发平台上的整体架构分布,维护系统运维的稳定性和安全性,修复漏洞。而业务部则有更多自由,利用“低代码”自主开发出业务所需的管理系统,并实现跨部门应用交互。另外,当重新定义了二者的分工后,企业技术部的价值才能从修电脑、装wifi、买服务器这些琐事中进阶,为公司数字化管理做建设性实事。

%title插图%num

“低代码”能否继续干得漂亮?

1、势头:稳定增长

2021年初,海外研究机构Infolob表示,低代码应用平台保持着40%的年复合增长率,预计到2022年,低代码应用程序市场总规模达212亿美元。Gartner预测2024年应用软件开发活动中,65%将通过低代码方式完成;75%的大型企业将用至少四种低代码开发工具开发应用。

“低代码”在国外发展势头强劲,无论是市场培育还是商业模式都渐趋成熟。在国内,它的表现也毫不逊色。2020年企业数字化浪潮让低代码市场规模迅速扩展,也因此鼓动了不少软件厂商转型做“低代码”。海比研究院预测,2021年至2025年,中国低代码市场将保持规模扩张的良好态势。

%title插图%num

%title插图%num

图源见水印

2、机遇:物联网和大数据也需要“低代码”

物联网和大数据都是时代的技术主旋律,而它们的发展也需要“低代码”助力。像物联网平台需要调度“云、管、边、端”各方资源,还要兼顾传感、语音等交互,并适应环境变化的状况——可想而知它的开发难度之大。“低代码”凭着灵活敏捷的开发功能,恰好能帮助降低物联网项目的开发门槛,缓解成本、人才等痛点。

众多物联网和地理信息大数据系统厂商,都在与低/零代码厂商明道云合作,并取得良效。低代码开发平台能抓住物联网和大数据的风口,挖掘自身产品在高精领域的协作可能性,是很聪明的差异化拓业策略。

3、挑战:客户观念尚未扭转

APICloud创始人刘鑫曾在一次访问中提到:“很多企业都说需要一个大数据、人工智能工具,但很少会说我需要一个开发工具。客户的需求并不是一个低代码平台,而是低代码能够产生的价值。“客户依然习惯性寻求贴身服务,观念尚未扭转,自身也难以培养低代码开发能力。“低代码”要真正普及,还需要继续教育市场和客户。

“低代码”的市场在时刻变化着,头部厂商在主动普及低代码教育,也有小众厂商探索该市场下的细分赛道,还有传统 ISV 躬身入局,加入战场。在机遇与挑战激荡的成长期里,我们尚且一起见证“低代码”的变迁。

%title插图%num

结语

日本富豪前泽友作买下马斯克火箭公司的*张绕月飞行船票,并赞助10位艺术家与自己同行。他说:“过去的人类宇宙史里,只有科学家能上太空。我希望让艺术家也能去看看太空,看看另一个曼妙的世界。”

低代码仿佛也有这样的力量,让不会代码的人也能通过可视化操作,感受开发一套应用软件的成就感、获得感。不少程序员觉得低代码、零代码开发平台就是个玩具,但我认为,低代码、零代码不是所谓的“低智盛行”,而是“人人平等”,人人都可以是开发者,去探索技术的宇宙。

android中MVC,MVP和MVVM三种模式详解析

我们都知道,Android本身就采用了MVC模式,model层数据源层我们就不说了,至于view层即通过xml来体现,而 controller层的角色一般是由activity来担当的。虽然我们项目用到了MVP模式,但是现在人们并没有总结出一种规范,所以MVP模式的写法并不统一,而至于MVVM模式看网上的呼声似乎也是赞同和拍砖的参半,所以对于这几种模式我也不发表意见了,适合自己的才是*好的。下面是我看到的关于这几种模式的几篇文章,整合了一下分享给大家。

相信大家对MVC,MVP和MVVM都不陌生,作为三个*耳熟能详的Android框架,它们的应用可以是非常广泛的,但是对于一些新手来说,可能对于区分它们三个都有困难,更别说在实际的项目中应用了,有些时候想用MVP的,代码写着写着就变成了MVC,久而久之就对它们三个的选择产生了恐惧感,如果你也是这样的人群,那么这篇文章可能会对你有很大的帮助,希望大家看完都会有收获吧!

文章重点:

(1)了解并区分MVC,MVP,MVVM。

(2)知道这三种模式在Android中如何使用。

(3)走出data binding的误区。

(4)了解MVP+data binding的开发模式。

本篇文章的demo我将会上传到我的github上。

水之积也不厚,则其负大舟也无力

正如庄子在逍遥游中说的,如果水不够深,那就没有能够担负大船的力量 。所以在真正开始涉及具体的代码之前,我们要先对MVC,MVP和MVVM做一个初步的了解。如果各位同学对此已经有所了解了,可以选择性跳过这一节。

MVC

MVC,Model View Controller,是软件架构中*常见的一种框架,简单来说就是通过controller的控制去操作model层的数据,并且返回给view层展示,具体见下图

%title插图%num

当用户出发事件的时候,view层会发送指令到controller层,接着controller去通知model层更新数据,model层更新完数据以后直接显示在view层上,这就是MVC的工作原理。

那具体到Android上是怎么样一个情况呢?

大家都知道一个Android工程有什么对吧,有Java的class文件,有res文件夹,里面是各种资源,还有类似manifest文件等等。对于原生的Android项目来说,layout.xml里面的xml文件就对应于MVC的view层,里面都是一些view的布局代码,而各种java bean,还有一些类似repository类就对应于model层,至于controller层嘛,当然就是各种activity咯。大家可以试着套用我上面说的MVC的工作原理是理解。比如你的界面有一个按钮,按下这个按钮去网络上下载一个文件,这个按钮是view层的,是使用xml来写的,而那些和网络连接相关的代码写在其他类里,比如你可以写一个专门的networkHelper类,这个就是model层,那怎么连接这两层呢?是通过button.setOnClickListener()这个函数,这个函数就写在了activity中,对应于controller层。是不是很清晰。

大家想过这样会有什么问题吗?显然是有的,不然为什么会有MVP和MVVM的诞生呢,是吧。问题就在于xml作为view层,控制能力实在太弱了,你想去动态的改变一个页面的背景,或者动态的隐藏/显示一个按钮,这些都没办法在xml中做,只能把代码写在activity中,造成了activity既是controller层,又是view层的这样一个窘境。大家回想一下自己写的代码,如果是一个逻辑很复杂的页面,activity或者fragment是不是动辄上千行呢?这样不仅写起来麻烦,维护起来更是噩梦。(当然看过Android源码的同学其实会发现上千行的代码不算啥,一个RecyclerView.class的代码都快上万行了呢。。)

MVC还有一个重要的缺陷,大家看上面那幅图,view层和model层是相互可知的,这意味着两层之间存在耦合,耦合对于一个大型程序来说是非常致命的,因为这表示开发,测试,维护都需要花大量的精力。

正因为MVC有这样那样的缺点,所以才演化出了MVP和MVVM这两种框架。

MVP

MVP作为MVC的演化,解决了MVC不少的缺点,对于Android来说,MVP的model层相对于MVC是一样的,而activity和fragment不再是controller层,而是纯粹的view层,所有关于用户事件的转发全部交由presenter层处理。下面还是让我们看图

%title插图%num

从图中就可以看出,*明显的差别就是view层和model层不再相互可知,完全的解耦,取而代之的presenter层充当了桥梁的作用,用于操作view层发出的事件传递到presenter层中,presenter层去操作model层,并且将数据返回给view层,整个过程中view层和model层完全没有联系。看到这里大家可能会问,虽然view层和model层解耦了,但是view层和presenter层不是耦合在一起了吗?其实不是的,对于view层和presenter层的通信,我们是可以通过接口实现的,具体的意思就是说我们的activity,fragment可以去实现实现定义好的接口,而在对应的presenter中通过接口调用方法。不仅如此,我们还可以编写测试用的View,模拟用户的各种操作,从而实现对Presenter的测试。这就解决了MVC模式中测试,维护难的问题。

当然,其实*好的方式是使用fragment作为view层,而activity则是用于创建view层(fragment)和presenter层(presenter)的一个控制器。

MVVM

MVVM*早是由微软提出的

%title插图%num

这里要感谢泡在网上的日子,因为前面看到的三张图我都是从它的博客中摘取的,如果有人知道不允许这样做的话请告诉我,我会从我的博客中删除的,谢谢。

从图中看出,它和MVP的区别貌似不大,只不过是presenter层换成了viewmodel层,还有一点就是view层和viewmodel层是相互绑定的关系,这意味着当你更新viewmodel层的数据的时候,view层会相应的变动ui。

我们很难去说MVP和MVVM这两个MVC的变种孰优孰劣,还是要具体情况具体分析。

纸上得来终觉浅,*知此事要躬行

对于程序员来说,空谈是*没效率的一种方式,相信大家看了我上面对于三种模式的分析,或多或少都会有点云里雾里,下面让我们结合代码来看看。

让我们试想一下下面这个情景,用户点击一个按钮A,获取github上对应公司对应仓库中贡献排行*的任的名字,然后我们还会有一个按钮B,用户点击按钮B,界面上排行*的那个人的名字就会换成自己的。

MVC

MVC实现是*简单的。

首先看对应view层的xml文件

[html] view plain copy 在CODE上查看代码片派生到我的代码片
  1. <?xmlversionxmlversion=”1.0″encoding=”utf-8″?>  
  2. <LinearLayoutxmlns:androidLinearLayoutxmlns:android=“http://schemas.android.com/apk/res/android”  
  3.     xmlns:tools=“http://schemas.android.com/tools”  
  4.     android:layout_width=“match_parent”  
  5.     android:layout_height=“match_parent”  
  6.     android:id=“@+id/container”  
  7.     android:orientation=“vertical”  
  8.     tools:context=“.ui.view.MainActivity”  
  9.     android:fitsSystemWindows=“true”>  
  10.     <Button  
  11.         android:text=“get”  
  12.         android:layout_width=“match_parent”  
  13.         android:layout_height=“wrap_content”  
  14.         android:onClick=“get”/>  
  15.     <Button  
  16.         android:text=“change”  
  17.         android:layout_width=“match_parent”  
  18.         android:layout_height=“wrap_content”  
  19.         android:onClick=“change”/>  
  20.     <TextView  
  21.         android:id=“@+id/top_contributor”  
  22.         android:layout_width=“match_parent”  
  23.         android:layout_height=“match_parent”  
  24.         android:gravity=“center”  
  25.         android:textSize=“30sp”/>  
  26. </LinearLayout>  

很简单,两个Button一个TextView

接着看对应controller层的activity

[java] view plain copy 在CODE上查看代码片派生到我的代码片
  1. public class MainActivity extends AppCompatActivity {  
  2.     private ProcessDialog dialog;  
  3.     private Contributor contributor = new Contributor();  
  4.     private TextView topContributor;  
  5.     private Subscriber<Contributor> contributorSub = new Subscriber<Contributor>() {  
  6.         @Override  
  7.         public void onStart() {  
  8.             showProgress();
  9.         }
  10.         @Override  
  11.         public void onCompleted() {  
  12.         }
  13.         @Override  
  14.         public void onError(Throwable e) {  
  15.         }
  16.         @Override  
  17.         public void onNext(Contributor contributor) {  
  18.             MainActivity.this.contributor = contributor;  
  19.             topContributor.setText(contributor.login);
  20.             dismissProgress();
  21.         }
  22.     };
  23.     @Override  
  24.     protected void onCreate(Bundle savedInstanceState) {  
  25.         super.onCreate(savedInstanceState);  
  26.         setContentView(R.layout.activity_main);
  27.         topContributor = (TextView)findViewById(R.id.top_contributor);
  28.     }
  29.     public void get(View view){  
  30.         getTopContributor(“square”, “retrofit”);  
  31.     }
  32.     public void change(View view){  
  33.         contributor.login = “zjutkz”;  
  34.         topContributor.setText(contributor.login);
  35.     }
  36.     public void getTopContributor(String owner,String repo){  
  37.         GitHubApi.getContributors(owner, repo)
  38.                 .take(1)  
  39.                 .observeOn(AndroidSchedulers.mainThread())
  40.                 .subscribeOn(Schedulers.newThread())
  41.                 .map(new Func1<List<Contributor>, Contributor>() {  
  42.                     @Override  
  43.                     public Contributor call(List<Contributor> contributors) {  
  44.                         return contributors.get(0);  
  45.                     }
  46.                 })
  47.                 .subscribe(contributorSub);
  48.     }
  49.     public void showProgress(){  
  50.         if(dialog == null){  
  51.             dialog = new ProcessDialog(this);  
  52.         }
  53.         dialog.showMessage(“正在加载…”);  
  54.     }
  55.    public void dismissProgress(){  
  56.         if(dialog == null){  
  57.             dialog = new ProcessDialog(this);  
  58.         }
  59.         dialog.dismiss();
  60.     }
  61. }

我们看一下get()方法中调用的getTopContributor方法

[java] view plain copy 在CODE上查看代码片派生到我的代码片
  1. public void getTopContributor(String owner,String repo){  
  2.     GitHubApi.getContributors(owner, repo)
  3.             .take(1)  
  4.             .observeOn(AndroidSchedulers.mainThread())
  5.             .subscribeOn(Schedulers.newThread())
  6.             .map(new Func1<List<Contributor>, Contributor>() {  
  7.                 @Override  
  8.                 public Contributor call(List<Contributor> contributors) {  
  9.                     return contributors.get(0);  
  10.                 }
  11.             })
  12.             .subscribe(contributorSub);
  13. }

 

熟悉rxjava和retrofit的同学应该都明白这是啥意思,如果对这两个开源库不熟悉也没事,可以参考给 Android 开发者的 RxJava 详解和用 Retrofit 2 简化 HTTP 请求这两篇文章。

对于这里大家只要知道这段代码的意思就是去获取github上owner公司中的repo仓库里贡献排名*的那个人。贡献者是通过Contributor这个java bean存储的。

[java] view plain copy 在CODE上查看代码片派生到我的代码片
  1. public class Contributor {  
  2.     public String login;  
  3.     public int contributions;  
  4.   @Override  
  5.     public String toString() {  
  6.         return login + “, ” + contributions;  
  7.     }
  8. }

 

很简单,login表示贡献者的名字,contributor表示贡献的次数。

然后通过rxjava的subscriber中的onNext()函数得到这个数据。

[java] view plain copy 在CODE上查看代码片派生到我的代码片
  1. private Subscriber<Contributor> contributorSub = new Subscriber<Contributor>() {  
  2.     @Override  
  3.     public void onStart() {  
  4.         showProgress();
  5.     }
  6.     @Override  
  7.     public void onCompleted() {  
  8.     }
  9.     @Override  
  10.     public void onError(Throwable e) {  
  11.     }
  12.     @Override  
  13.     public void onNext(Contributor contributor) {  
  14.         MainActivity.this.contributor = contributor;  
  15.         topContributor.setText(contributor.login);
  16.         dismissProgress();
  17.     }
  18. };

至于另外那个change按钮的工作大家应该都看得懂,这里不重复了。

好了,我们来回顾一遍整个流程。

首先在xml中写好布局代码。

其次,activity作为一个controller,里面的逻辑是监听用户点击按钮并作出相应的操作。比如针对get按钮,做的工作就是调用GithubApi的方法去获取数据。

GithubApi,Contributor等类则表示MVC中的model层,里面是数据和一些具体的逻辑操作。

说完了流程再来看看问题,还记得我们前面说的吗,MVC在Android上的应用,一个具体的问题就是activity的责任过重,既是controller又是view。这里是怎么体现的呢?看了代码大家发现其中有一个progressDialog,在加载数据的时候显示,加载完了以后取消,逻辑其实是view层的逻辑,但是这个我们没办法写到xml里面啊,包括TextView.setTextView(),这个也一样。我们只能把这些逻辑写到activity中,这就造成了activity的臃肿,这个例子可能还好,如果是一个复杂的页面呢?大家自己想象一下。

MVP

通过具体的代码大家知道了MVC在Android上是如何工作的,也知道了它的缺点,那MVP是如何修正的呢?

这里先向大家推荐github上的一个第三方库,通过这个库大家可以很轻松的实现MVP。好了,还是看代码吧。

首先还是xml

[html] view plain copy 在CODE上查看代码片派生到我的代码片
  1. <?xml version=“1.0” encoding=”utf-8″?>  
  2. <LinearLayout xmlns:android=“http://schemas.android.com/apk/res/android”  
  3.     xmlns:tools=“http://schemas.android.com/tools”  
  4.     android:layout_width=“match_parent”  
  5.     android:layout_height=“match_parent”  
  6.     android:id=“@+id/container”  
  7.     android:orientation=“vertical”  
  8.     tools:context=“.ui.view.MainActivity”  
  9.     android:fitsSystemWindows=“true”>  
  10.     <Button  
  11.         android:text=“get”  
  12.         android:layout_width=“match_parent”  
  13.         android:layout_height=“wrap_content”  
  14.         android:onClick=“get”/>  
  15.     <Button  
  16.         android:text=“change”  
  17.         android:layout_width=“match_parent”  
  18.         android:layout_height=“wrap_content”  
  19.         android:onClick=“change”/>  
  20.     <TextView  
  21.         android:id=“@+id/top_contributor”  
  22.         android:layout_width=“match_parent”  
  23.         android:layout_height=“match_parent”  
  24.         android:gravity=“center”  
  25.         android:textSize=“30sp”/>  
  26. </LinearLayout>  

 

这个和MVC是一样的,毕竟界面的形式是一样的嘛。

接下去,我们看一个接口。

[java] view plain copy 在CODE上查看代码片派生到我的代码片
  1. public interface ContributorView extends MvpView {  
  2.     void onLoadContributorStart();  
  3.     void onLoadContributorComplete(Contributor topContributor);  
  4.     void onChangeContributorName(String name);  
  5. }

这个接口起什么作用呢?还记得我之前说的吗?MVP模式中,view层和presenter层靠的就是接口进行连接,而具体的就是上面的这个了,里面定义的三个方法,*个是开始获取数据,第二个是获取数据成功,第三个是改名。我们的view层(activity)只要实现这个接口就可以了。

下面看activity的代码

[java] view plain copy 在CODE上查看代码片派生到我的代码片
  1. public class MainActivity extends MvpActivity<ContributorView,ContributorPresenter> implements ContributorView {  
  2.     private ProcessDialog dialog;  
  3.     private TextView topContributor;  
  4.     @Override  
  5.     protected void onCreate(Bundle savedInstanceState) {  
  6.         super.onCreate(savedInstanceState);  
  7.         setContentView(R.layout.activity_main);
  8.         topContributor = (TextView)findViewById(R.id.top_contributor);
  9.     }
  10.     @NonNull  
  11.     @Override  
  12.     public ContributorPresenter createPresenter() {  
  13.         return new ContributorPresenter();  
  14.     }
  15.     public void get(View view){  
  16.         getPresenter().get(“square”, “retrofit”);  
  17.     }
  18.     public void change(View view){  
  19.         getPresenter().change();
  20.     }
  21.     @Override  
  22.     public void onLoadContributorStart() {  
  23.         showProgress();
  24.     }
  25.     @Override  
  26.     public void onLoadContributorComplete(Contributor contributor) {  
  27.         topContributor.setText(contributor.toString());
  28.         dismissProgress();
  29.     }
  30.     @Override  
  31.     public void onChangeContributorName(String name) {  
  32.         topContributor.setText(name);
  33.     }
  34.     public void showProgress(){  
  35.         if(dialog == null){  
  36.             dialog = new ProcessDialog(this);  
  37.         }
  38.         dialog.showMessage(“正在加载…”);  
  39.     }
  40.     public void dismissProgress(){  
  41.         if(dialog == null){  
  42.             dialog = new ProcessDialog(this);  
  43.         }
  44.         dialog.dismiss();
  45.     }
  46. }

 

它继承自MvpActivity,实现了刚才的ContributorView接口。继承的那个MvpActivity大家这里不用太关心主要是做了一些初始化和生命周期的封装。我们只要关心这个activity作为view层,到底是怎么工作的。

[java] view plain copy 在CODE上查看代码片派生到我的代码片
  1. public void get(View view){  
  2.     getPresenter().get(“square”, “retrofit”);  
  3. }
  4. public void change(View view){  
  5.     getPresenter().change();
  6. }

get()和change()这两个方法是我们点击按钮以后执行的,可以看到,里面完完全全没有任何和model层逻辑相关的东西,只是简单的委托给了presenter,那我们再看看presenter层做了什么

[java] view plain copy 在CODE上查看代码片派生到我的代码片
  1. public class ContributorPresenter extends MvpBasePresenter<ContributorView> {  
  2.     private Subscriber<Contributor> contributorSub = new Subscriber<Contributor>() {  
  3.         @Override  
  4.         public void onStart() {  
  5.             ContributorView view = getView();
  6.             if(view != null){  
  7.                 view.onLoadContributorStart();
  8.             }
  9.         }
  10.         @Override  
  11.         public void onCompleted() {  
  12.         }
  13.         @Override  
  14.         public void onError(Throwable e) {  
  15.         }
  16.         @Override  
  17.         public void onNext(Contributor topContributor) {  
  18.             ContributorView view = getView();
  19.             if(view != null){  
  20.                 view.onLoadContributorComplete(topContributor);
  21.             }
  22.         }
  23.     };
  24.     public void get(String owner,String repo){  
  25.         GitHubApi.getContributors(owner, repo)
  26.                 .take(1)  
  27.                 .observeOn(AndroidSchedulers.mainThread())
  28.                 .subscribeOn(Schedulers.newThread())
  29.                 .map(new Func1<List<Contributor>, Contributor>() {  
  30.                     @Override  
  31.                     public Contributor call(List<Contributor> contributors) {  
  32.                         return contributors.get(0);  
  33.                     }
  34.                 })
  35.                 .subscribe(contributorSub);
  36.     }
  37.     public void change(){  
  38.         ContributorView view = getView();
  39.         if(view != null){  
  40.             view.onChangeContributorName(“zjutkz”);  
  41.         }
  42.     }
  43. }

其实就是把刚才MVC中activity的那部分和model层相关的逻辑抽取了出来,并且在相应的时机调用ContributorView接口对应的方法,而我们的activity是实现了这个接口的,自然会走到对应的方法中。好了,我们来捋一捋。

首先,和MVC*大的不同,MVP把activity作为了view层,通过代码也可以看到,整个activity没有任何和model层相关的逻辑代码,取而代之的是把代码放到了presenter层中,presenter获取了model层的数据之后,通过接口的形式将view层需要的数据返回给它就OK了。

这样的好处是什么呢?首先,activity的代码逻辑减少了,其次,view层和model层完全解耦,具体来说,如果你需要测试一个http请求是否顺利,你不需要写一个activity,只需要写一个java类,实现对应的接口,presenter获取了数据自然会调用相应的方法,相应的,你也可以自己在presenter中mock数据,分发给view层,用来测试布局是否正确。

%title插图%num

它竟然说data binding的viewmodel层是binding类,其实不止是这篇文章,其他有一些开发者写的关于data binding的文章里都犯了一样的错误。大家如果也有这样的概念,请务必纠正过来!!说完了错误的概念,那data binding中真正的viewmodel是什么呢?我们还是以之前MVC,MVP的那个例子做引导。首先是view层,这没啥好说的,和MVP一样,只不过多了数据绑定。view层就是xml和activity。

[html] view plain copy 在CODE上查看代码片派生到我的代码片
  1. <layout xmlns:android=“http://schemas.android.com/apk/res/android”>  
  2.     <data>  
  3.         <variable name=“contributor” type=”zjutkz.com.mvvm.viewmodel.Contributor”/>  
  4.     </data>  
  5.     <LinearLayout  
  6.         android:layout_width=“match_parent”  
  7.         android:layout_height=“match_parent”  
  8.         android:id=“@+id/container”  
  9.         android:orientation=“vertical”  
  10.         android:fitsSystemWindows=“true”>  
  11.         <Button  
  12.             android:id=“@+id/get”  
  13.             android:text=“get”  
  14.             android:layout_width=“match_parent”  
  15.             android:layout_height=“wrap_content”  
  16.             android:onClick=“get”/>  
  17.         <Button  
  18.             android:id=“@+id/change”  
  19.             android:text=“change”  
  20.             android:layout_width=“match_parent”  
  21.             android:layout_height=“wrap_content”  
  22.             android:onClick=“change”/>  
  23.         <TextView  
  24.             android:id=“@+id/top_contributor”  
  25.             android:layout_width=“match_parent”  
  26.             android:layout_height=“match_parent”  
  27.             android:gravity=“center”  
  28.             android:textSize=“30sp”  
  29.             android:text=“@{contributor.login}”/>  
  30.     </LinearLayout>  
  31. </layout>  

[java] view plain copy 在CODE上查看代码片派生到我的代码片
  1. public class MainActivity extends AppCompatActivity {  
  2.     private Subscriber<Contributor> contributorSub = new Subscriber<Contributor>() {  
  3.         @Override  
  4.         public void onStart() {  
  5.             showProgress();
  6.         }
  7.         @Override  
  8.         public void onCompleted() {  
  9.         }
  10.         @Override  
  11.         public void onError(Throwable e) {  
  12.         }
  13.         @Override  
  14.         public void onNext(Contributor contributor) {  
  15.             binding.setContributor(contributor);
  16.             dismissProgress();
  17.         }
  18.     };
  19.     private ProcessDialog dialog;  
  20.     private MvvmActivityMainBinding binding;  
  21.     @Override  
  22.     protected void onCreate(Bundle savedInstanceState) {  
  23.         super.onCreate(savedInstanceState);  
  24.         binding = DataBindingUtil.setContentView(this, R.layout.mvvm_activity_main);  
  25.     }
  26.     public void get(View view){  
  27.         getContributors(“square”, “retrofit”);  
  28.     }
  29.     public void change(View view){  
  30.         if(binding.getContributor() != null){  
  31.             binding.getContributor().setLogin(“zjutkz”);  
  32.         }
  33.     }
  34.     public void showProgress(){  
  35.         if(dialog == null){  
  36.             dialog = new ProcessDialog(this);  
  37.         }
  38.         dialog.showMessage(“正在加载…”);  
  39.     }
  40.     public void dismissProgress(){  
  41.         if(dialog == null){  
  42.             dialog = new ProcessDialog(this);  
  43.         }
  44.         dialog.dismiss();
  45.     }
  46.     public void getContributors(String owner,String repo){  
  47.         GitHubApi.getContributors(owner, repo)
  48.                 .take(1)  
  49.                 .observeOn(AndroidSchedulers.mainThread())
  50.                 .subscribeOn(Schedulers.newThread())
  51.                 .map(new Func1<List<Contributor>, Contributor>() {  
  52.                     @Override  
  53.                     public Contributor call(List<Contributor> contributors) {  
  54.                         return contributors.get(0);  
  55.                     }
  56.                 })
  57.                 .subscribe(contributorSub);
  58.     }
  59. }

如果你对data binding框架是有了解的,上面的代码你能轻松的看懂。

那model层又是什么呢?当然就是那些和数据相关的类,GithubApi等等。

重点来了,viewmodel层呢?好吧,viewmodel层就是是Contributor类!大家不要惊讶,我慢慢的来说。

[java] view plain copy 在CODE上查看代码片派生到我的代码片
  1. public class Contributor extends BaseObservable{  
  2.     private String login;  
  3.     private int contributions;  
  4.     @Bindable  
  5.     public String getLogin(){  
  6.         return login;  
  7.     }
  8.     @Bindable  
  9.     public int getContributions(){  
  10.         return contributions;  
  11.     }
  12.     public void setLogin(String login){  
  13.         this.login = login;  
  14.         notifyPropertyChanged(BR.login);
  15.     }
  16.     public void setContributions(int contributions){  
  17.         this.contributions = contributions;  
  18.         notifyPropertyChanged(BR.contributions);
  19.     }
  20.     @Override  
  21.     public String toString() {  
  22.         return login + “, ” + contributions;  
  23.     }
  24. }

我们可以看到,Contributor和MVP相比,继承自了BaseObservable,有基础的同学都知道这是为了当Contributor内部的variable改变的时候ui可以同步的作出响应。

我为什么说Contributor是一个viewmodel呢。大家还记得viewmodel的概念吗?view和viewmodel相互绑定在一起,viewmodel的改变会同步到view层,从而view层作出响应。这不就是Contributor和xml中那些组件元素的关系吗?所以,大家不要被binding类迷惑了,data binding框架中的viewmodel是自己定义的那些看似是model类的东西!比如这里的Contributor!

话说到这里,那binding类又是什么呢?其实具体对应到之前MVVM的那张图就很好理解了,我们想一下,binding类的工作是什么?

[java] view plain copy 在CODE上查看代码片派生到我的代码片
  1. binding = DataBindingUtil.setContentView(this,R.layout.mvvm_activity_main);  
  2. binding.setContributor(contributor);

首先,binding要通过DataBindingUtil.setContentView()方法将xml,也就是view层设定。

接着,通过setXXX()方法将viewmodel层注入进去。

由于这两个工作,view层(xml的各个组件)和viewmodel层(contributor)绑定在了一起。

好了,大家知道了吗,binding类,其实就是上图中view和viewmodel中间的那根线啊!!

 

MVVM

MVVM的问题呢,其实和MVC有一点像。data binding框架解决了数据绑定的问题,但是view层还是会过重,大家可以看我上面那个MVVM模式下的activity

[java] view plain copy 在CODE上查看代码片派生到我的代码片
  1. public class MainActivity extends AppCompatActivity {  
  2.     private Subscriber<Contributor> contributorSub = new Subscriber<Contributor>() {  
  3.         @Override  
  4.         public void onStart() {  
  5.             showProgress();
  6.         }
  7.         @Override  
  8.         public void onCompleted() {  
  9.         }
  10.         @Override  
  11.         public void onError(Throwable e) {  
  12.         }
  13.         @Override  
  14.         public void onNext(Contributor contributor) {  
  15.             binding.setContributor(contributor);
  16.             dismissProgress();
  17.         }
  18.     };
  19.     private ProcessDialog dialog;  
  20.     private MvvmActivityMainBinding binding;  
  21.     @Override  
  22.     protected void onCreate(Bundle savedInstanceState) {  
  23.         super.onCreate(savedInstanceState);  
  24.         binding = DataBindingUtil.setContentView(this, R.layout.mvvm_activity_main);  
  25.     }
  26.     public void get(View view){  
  27.         getContributors(“square”, “retrofit”);  
  28.     }
  29.     public void change(View view){  
  30.         if(binding.getContributor() != null){  
  31.             binding.getContributor().setLogin(“zjutkz”);  
  32.         }
  33.     }
  34.     public void showProgress(){  
  35.         if(dialog == null){  
  36.             dialog = new ProcessDialog(this);  
  37.         }
  38.         dialog.showMessage(“正在加载…”);  
  39.     }
  40.     public void dismissProgress(){  
  41.         if(dialog == null){  
  42.             dialog = new ProcessDialog(this);  
  43.         }
  44.         dialog.dismiss();
  45.     }
  46.     public void getContributors(String owner,String repo){  
  47.         GitHubApi.getContributors(owner, repo)
  48.                 .take(1)  
  49.                 .observeOn(AndroidSchedulers.mainThread())
  50.                 .subscribeOn(Schedulers.newThread())
  51.                 .map(new Func1<List<Contributor>, Contributor>() {  
  52.                     @Override  
  53.                     public Contributor call(List<Contributor> contributors) {  
  54.                         return contributors.get(0);  
  55.                     }
  56.                 })
  57.                 .subscribe(contributorSub);
  58.     }
  59. }

大家有没有发现,activity在MVVM中应该是view层的,但是里面却和MVC一样写了对model的处理。有人会说你可以把对model的处理放到viewmodel层中,这样不是更符合MVVM的设计理念吗?这样确实可以,但是progressDialog的show和dismiss呢?你怎么在viewmodel层中控制?这是view层的东西啊,而且在xml中也没有,我相信会有解决的方案,但是我们有没有一种更加便捷的方式呢?

 

首先还是view层。

[html] view plain copy 在CODE上查看代码片派生到我的代码片
  1. <layout xmlns:android=“http://schemas.android.com/apk/res/android”>  
  2.     <data>  
  3.         <variable name=“contributor” type=”zjutkz.com.mvpdatabinding.viewmodel.Contributor”/>  
  4.     </data>  
  5.     <LinearLayout  
  6.         android:layout_width=“match_parent”  
  7.         android:layout_height=“match_parent”  
  8.         android:id=“@+id/container”  
  9.         android:orientation=“vertical”  
  10.         android:fitsSystemWindows=“true”>  
  11.         <Button  
  12.             android:id=“@+id/get”  
  13.             android:text=“get”  
  14.             android:layout_width=“match_parent”  
  15.             android:layout_height=“wrap_content”  
  16.             android:onClick=“get”/>  
  17.         <Button  
  18.             android:id=“@+id/change”  
  19.             android:text=“change”  
  20.             android:layout_width=“match_parent”  
  21.             android:layout_height=“wrap_content”  
  22.             android:onClick=“change”/>  
  23.         <TextView  
  24.             android:id=“@+id/top_contributor”  
  25.             android:layout_width=“match_parent”  
  26.             android:layout_height=“match_parent”  
  27.             android:gravity=“center”  
  28.             android:textSize=“30sp”  
  29.             android:text=“@{contributor.login}”/>  
  30.     </LinearLayout>  
  31. </layout>  
[java] view plain copy 在CODE上查看代码片派生到我的代码片
  1. public class MainActivity extends MvpActivity<ContributorView,ContributorPresenter> implements ContributorView {  
  2.     private ProcessDialog dialog;  
  3.     private ActivityMainBinding binding;  
  4.     @Override  
  5.     protected void onCreate(Bundle savedInstanceState) {  
  6.         super.onCreate(savedInstanceState);  
  7.         binding = DataBindingUtil.setContentView(this, R.layout.activity_main);  
  8.     }
  9.     @NonNull  
  10.     @Override  
  11.     public ContributorPresenter createPresenter() {  
  12.         return new ContributorPresenter();  
  13.     }
  14.     public void get(View view){  
  15.         getPresenter().get(“square”, “retrofit”);  
  16.     }
  17.     public void change(View view){  
  18.         if(binding.getContributor() != null){  
  19.             binding.getContributor().setLogin(“zjutkz”);  
  20.         }
  21.     }
  22.     @Override  
  23.     public void onLoadContributorStart() {  
  24.         showProgress();
  25.     }
  26.     @Override  
  27.     public void onLoadContributorComplete(Contributor contributor) {  
  28.         binding.setContributor(contributor);
  29.         dismissProgress();
  30.     }
  31.     public void showProgress(){  
  32.         if(dialog == null){  
  33.             dialog = new ProcessDialog(this);  
  34.         }
  35.         dialog.showMessage(“正在加载…”);  
  36.     }
  37.     public void dismissProgress(){  
  38.         if(dialog == null){  
  39.             dialog = new ProcessDialog(this);  
  40.         }
  41.         dialog.dismiss();
  42.     }
  43. }

然后是presenter层

[java] view plain copy 在CODE上查看代码片派生到我的代码片
  1. public class ContributorPresenter extends MvpBasePresenter<ContributorView> {  
  2.     private Subscriber<Contributor> contributorSub = new Subscriber<Contributor>() {  
  3.         @Override  
  4.         public void onStart() {  
  5.             ContributorView view = getView();
  6.             if(view != null){  
  7.                 view.onLoadContributorStart();
  8.             }
  9.         }
  10.         @Override  
  11.         public void onCompleted() {  
  12.         }
  13.         @Override  
  14.         public void onError(Throwable e) {  
  15.         }
  16.         @Override  
  17.         public void onNext(Contributor topContributor) {  
  18.             ContributorView view = getView();
  19.             if(view != null){  
  20.                 view.onLoadContributorComplete(topContributor);
  21.             }
  22.         }
  23.     };
  24.     public void get(String owner,String repo){  
  25.         GitHubApi.getContributors(owner, repo)
  26.                 .take(1)  
  27.                 .observeOn(AndroidSchedulers.mainThread())
  28.                 .subscribeOn(Schedulers.newThread())
  29.                 .map(new Func1<List<Contributor>, Contributor>() {  
  30.                     @Override  
  31.                     public Contributor call(List<Contributor> contributors) {  
  32.                         return contributors.get(0);  
  33.                     }
  34.                 })
  35.                 .subscribe(contributorSub);
  36.     }
  37. }

model层就是GithubApi等等。

我们使用了data binding框架去节省了类似findViewById和数据绑定的时间,又使用了presenter去将业务逻辑和view层分离。

当然这也不是固定的,你大可以在viewmodel中实现相应的接口,presenter层的数据直接发送到viewmodel中,在viewmodel里更新,因为view和viewmodel是绑定的,这样view也会相应的作出反应。

说到这里,我还是想重复刚才的那句话,*佳实践都是人想出来的,用这些框架根本的原因也是为了尽量低的耦合性和尽量高的可复用性。