有赞云如何支持多语言

一、背景

有赞是Saas公司,向商家提供了全方位的软件服务,支撑商家进行采购、店铺、商品、营销、订单、物流等等管理服务。

在这个软件服务里,能够满足大部分的商家,为商家保驾护航。

Saas形成是追求共性的过程,Saas生态化是求同存异的过程,所以当我们能够满足大部分客户需求时,我们得考虑大客户的个性化需求场景。

1.1 客户分析

上面讲到我们要求同存异,我们要满足个性化需求,这里简单讲解一下大客户的价值,下面就不区分优势和劣势了,都放一起:

  • 中小客户
    • 企业规模小,付费能力相对弱一些;
    • 企业周期短,无法很好的保证续费;
    • 大部分停留使用产品基本能力,说明软件可替代性强,难形成粘性;
    • 但是数量庞大;
    • 获客成本相对低一些;
  • 大客户
    • 企业规模大,付费能力强;
    • 发展稳定,企业周期长,能够保证续费;
    • 有利于形成标杆案例,用于推广;
    • 只要能实现需求,对资金要求和价格不敏感;
    • 定制需求多,一旦定制,替代成本高,粘性好;

简单罗列了一下,当然其他点还有很多,对比会发现,Saas会满足大部分客户的需求,尤其是中小客户,而且中小客户量可能会达到90%以上,但是中小客户续费能力弱会导致销售成本高,同时无法形成标杆,很难有行业影响力;大客户付费能力很强,只要能够满足需求,可能不太会对相对高的价格说不,但是基本上每个大客户都有定制需求,而且一旦达成合作,可以稳定的续费。

1.2 有赞云是干嘛的

前面简单的分析了一下中小客户和大客户,由于本篇的重点在于多语言,所以就不过多的讲解,实际上一些中小客户也有定制需求,希望自己的店铺标新立异,希望能够用低成本并且快速满足自己的需求,当成长为大客户时再有更多的需求。

Saas传统情况下是怎么做定制的呢?

//核心业务流程
    ............
    ...........
  if (店铺Id.equeals(0023423)) {
      //do custom logic
  } else if (店铺Id.equeals(0056673)) {
      //do custom logic
  } else {
      //do stardand logic
  }
    ............
    ...........

作为一个技术人员,可能会对上述代码很熟悉,客户是上帝,当客户提各种各样的需求时,如果不满足可能会导致客户远走他乡,如果去满足,那么我们的核心业务代码耦合性会很高,而且代码扩展性很差,定制代码越来越多,越来越约束业务的发展,导致系统出现瓶颈;当然上面的代码有点直白,一般情况下会运用规则引擎、会独立一个定制应用来处理恶心的业务,但是问题依旧;更难受的一点是客户要定制页面,难道在node层做业务判断吗?看到这里相信大家都会说不,这个系统将没法扩展,业务没办法大步向前走。

这个时候有赞云出现了,所以有赞云的初衷是解决客户的个性定制需求,让各种各样的客户都能够和有赞合作,有赞提供的软件可以应用于各行各业,覆盖所有场景。

image-20190612113217592

上图相对抽象,但是更好理解。从图中的箭头可以看出,有赞云就是核心业务的扩展,我们的核心业务系统将交易流程输出到有赞云,开发者或者商家可以自己定制交易流程,如卖电影票的商家可以在下单流程中加一个选座的流程;同时在流程中的各个业务关键点输出扩展能力,让开发者可以去实现这个扩展能力,如价格计算,原来价格计算是核心系统做的事情,现在开发者可以自己写价格计算逻辑,这个价格计算的结果可能就是商品的实际成交价格;前端页面组件化,开发者可以定制自己的组件,修改原有组件的行为。当然还有很多很多更加复杂的定制。

所以有赞云是一个平台,提供了定制能力,并且把定制能力市场化,开发者开发各种各样的工具型App,实现了各种奇思妙想的功能,然后发布到有赞云的应用市场;商家浏览应用市场,发现某个或者几个应用的功能和自己的需求很匹配,然后购买应用,快速又低成本的完成了自己的店铺的定制。除此之外,我们还提供行业解决方案定制,如果你足够有能力,你可以定制整个行业的方案,比如有赞除了微商城还有有赞教育,你可以定制一套有赞教育,这是不可思议的;大客户往往希望能够私有定制,因为大客户的定制往往很特殊,不能推广,所以大客户找开发者可以开发自用型App,最大程度的完成自己的需求。

1.2.1 工具型应用

image-20190612120545635

这里有两个角色两种资源,开发者和商家,店铺和应用。

开发者开发完App上架应用市场,商家购买应用,购买后会产生商家店铺和应用的授权关系,此时店铺可以使用应用的功能,比如某应用实现了国际地址,那么该店铺的买家在下单过程中选择地址时可以选择全球地址了。

1.2.2 自用型应用

自用型顾名思义是自己使用的,此时商家可以自己找开发者为自己开发一个应用来实现自己店铺的定制,该应用的所有权也属于商家。

1.2.3 行业解决方案

这是一整套方案,前面提到,你可以开发一个行业的软件,比如打车、外卖等等,和工具型应用一样需要上架应用市场,然后商家购买。

1.2.4 有赞云的影响

将会全面助力商家成功,大大提升商家成功发展,给商家更多可能性;

给开发者提供平台,拓展开发者的收入,提升开发能力,拓展影响力,给开发者带来巨大的市场。

二、什么是多语言

2.1 应用

什么是多语言,多语言应用在什么地方,离不开一个载体,那就是应用。

应用是开发者开发定制功能的实体,它包含代码、控制台、组件(mysql、redis等等)、权限、业务配置等等。

同时这个应用是部署在有赞云提供的容器里。

那么为什么要部署在有赞云提供的容器里:

  1. 如果部署在私有机房和外部的云,很难保证公网的稳定和延时;
  2. 我们需要保证应用的质量,防止违规违法现象出现;
  3. 部署在有赞云的容器里,和有赞核心应用更近,可以通过RPC的方式进行调用;
  4. 这种方式可以监控整个链路,出问题容易排查;
  5. 可靠性、稳定性、安全性有保障,这是对商家的保障。

2.2 多语言

我们希望通过这种模式形成一种生态,支持各种语言进行开发的开发者,所以我们也上线了开发者社区,方便开发者学习、交流、更好地与有赞云沟通、与商家沟通。

多语言,此语言非彼语言,不是国家化支持多个国家语言的意思哈。

多语言是指我们提供一些规范、协议、框架、基础设施等等来支持开发者进行如Java、Php、Python、NodeJs等等语言的应用开发,并且我们的调用。

image-20190612161354429

如上图所示,多语言包含很多模块,会着重挑几块讲一下,事实上每一块内容都可以写好几篇文章。

三、如何实现多语言

如何实现多语言,这是一个好问题,也是一个难题,至少做有赞云是如此,这是一个全新的模式,虽然多语言调用有多种解决方案,但是对于有赞云这种模式来说没有前路可参考。

一开始可能还不知道如何上手,思维导图是个很好的工具,能够帮助梳理多语言该做哪些事情,如上图所示,当然下面细分的内容还有很多。

3.1 远程调用

我们先来看远程调用,毕竟这个是主链路,没有这个能力,我们的扩展能力就无法实现。

相信大部分Java开发同学都在用dubbo或者spring cloud体系,但是这些目前基本能够解决Java应用之间的RPC调用,作为一个公司来说,我们可以去限定大家统一用某些技术栈,统一模型。但是有赞云的用户是开发者,在有赞云里部署的应用是各个语言开发的。

3.1.1 技术选型

首先我们需要保证远程调用时低延时的,支持多种开发语言,开发者不用感知服务的注册、发现;需要用监控和链路追踪能力;有赞云需要在开发者不感知或者不影响开发者实际开发体验的情况下去添加功能、完善链路并持续升级。

Service Mesh的理念非常符合我们的场景,接下来看看我们的多语言实践。

3.1.2 内部RPC

相信大部分公司的RPC框架使用了Dubbo,在Dubbo的基础上进行二次开发来满足自己公司的场景,有赞也是如此。

img

这张图相信大家都很熟悉,有consumer、provider、registry、monitor,但是这里有几个问题:

  1. Consumer和Provider需要Java应用;
  2. Consumer和Provider的行为耦合在业务应用里;
  3. 架构上对跨机房、跨网络的调用没做支持。

有赞公司内部对Dubbo进行了二次开发来支持前面提到的几个问题,对于Java应用来说,基本上和图中的架构类似。所以可以理解为有赞原本Java应用之间通过Dubbo来做RPC(这是现状),然后现在要实现跨网络并且调用多种语言的应用(目标)。

当然有赞内部本身就演进过对特定语言的调用方案。

3.1.3 部署架构

image-20190613161959467

如图,有赞内部核心域和有赞开发者定制域属于两个独立的网络,两个网络之间不能进行相互访问。

上图中的Side Car是完全由有赞自主研发的组件,取名叫Tether,他完成了服务的请求转发,与有赞监控、日志对接,实现了对服务化调用的监控和报警,并将服务发现、负载均衡、后端服务的优雅下线等等,全部都下沉到Tether层处理,所以可以理解为Tether就是Service Mesh中的Side Car。

3.1.3 Mesh实践

从上图中会发现,我们在核心域调用App的过程中没有采用Dubbo直连的方式进行RPC行为,而是通过了Side car进行转发。

service-mesh

Service Mesh架构图,如果将前面的部署架构图两边的网络域换成两个服务器,会发现和Service Mesh图类似。

Service Mesh的理念是将服务的注册、发现、服务治理、服务调用等等能力作为基础设施,让应用不用去感知业务逻辑之外的内容,这也是云原生的一部分。

因为如果让应用去做这些事情,很多事情变得不可控,比如服务协议版本不一致、出现Bug时不能统一升级。而现在有赞云的这种微服务化的架构,仅需静默升级Sidecar即可,无需任何业务开发者的参与和协同,极大的提升了整体架构的灵活性和功能迭代可控性。

前面提到,目前有赞核心域使用的Dubbo RPC协议与Java语言特性耦合严重,不适合于跨语言调用的场景。基于此,有赞云定制域中,我们设计了基于HTTP1.1和HTTP/2协议的可拓展的RPC协议,用于跨语言调用的场景。其中HTTP/2协议由于其标准化、异步性、高性能、语义清晰等特性。

3.1.4 调用流程

讲了这么多,给大家一个比较清晰的调用流程图来理解一下。

image-20190613171247767

举个例子,比如开发者实现了一个价格计算扩展点,当我们的核心业务逻辑进行到计算价格时:

  1. 价格中心通过Dubbo调用Bep(扩展点网关),这样做的好处时内部应用本身使用Dubbo框架,无须改造,就按正常Dubbo服务处理就行;
  2. Bep直连Side car,当Bep收到请求后进行协议转换,然后调用Side Car;
    • 如果对方应用是Java应用,那么还是按照原有的Dubbo协议进行调用,前文提到过,有赞内部已经做了一些Dubbo支持开发,这套方案早就存在,所以这是最低成本,对于Java开发者来说也是非常熟悉Dubbo,这种方式对于有赞和开发者来说比较合适。
    • 如果对方应用不是Java应用,Bep会将Dubbo协议转换为Http协议调用Side Car。
  3. Side Car通过网络规则调用到有赞云开发者定制域机房的Side Car,这里有个问题,有赞云定制域机房的Side Car如何知道该调用那个App呢?
    • 首先定制域里是一个K8S的集群,当App发布的时候,通过K8S提供的能力进行服务注册;
    • 有赞云引入了Istio的Pilot,并进行了一定的改造来进行容器中应用的服务发现;
    • 还有一点就是前文提到,App和店铺之间有授权关系才能调用,所以当核心域发起Dubbo请求时系统知道是哪个店铺的操作,知道了店铺就知道是哪个App。

所以我们提供的多语言方案并不是一刀切,而是因地制宜,根据实际情况去做RPC的多语言方案,针对Java应用,我们使用有赞已有的技术能力放到有赞云中来实施;对于其他类型的应用,我们提供通用的Http1.1/2.0来拓展RPC协议,其中HTTP/2协议由于其标准化、异步性、高性能、语义清晰等特性能够满足我们的场景。

3.1.5 RPC监控

image-20190614103316149

传统Dubbo监控一般会在应用依赖的Jar包里去做数据上报,也就是应用来上报监控数据,在做多语言时这样会有一些问题:

  1. 每种语言框架都得去做上报,当上报协议发生变化时就得去更新各个框架,并且推动应用去升级;
  2. 应用本身做数据上报,如果这个逻辑出现问题,可能导致业务出错,应用异常;
  3. 框架层会占用太多应用资源;
  4. 多种语言得适配;
  5. 监控会随着业务的发展,底层系统的升级,经常性的变更。

针对这几个问题,我们将Tracing、Metrics、Logging能力下层到Side Car,屏蔽多语言,通过这种方式,我们可以让开发者无感知的去升级底层能力,升级监控;应用层只需要关注服务实现;当一种新的语言加入时,我们的监控收集不需要做任何改造。

3.2 应用框架

讲了远程调用,那么应用如何去实现服务呢?我们希望给开发者提供极致的开发体验,而不是让开发者去感知太多协议层的内容,如果不管这些我们完全可以在有赞云文档中说明我们的协议细节,然后让开发者去理解协议去实现协议,但是这样体验太差了,可能开发了好几天还在调试协议,而没有进入能够真正产生价值的业务代码,这是完全没必要浪费的时间。

这就需要有个应用框架来屏蔽这个麻烦。

3.2.1 扩展点调用

image-20190613175535243

不管是Java语言还是Php或者其他语言,有赞云都会提供应用框架,但是在设计之初这是一个艰难的抉择,提供应用框架意味着我们的成本会很高,开发成本和维护成本,但是我们开发的协议我们自己最清楚,出现问题有赞云的开发同学也最了解,从长期来看对于有赞云和开发者来说都是非常有益了,尤其是对于开发者,大大降低了开发者的开发成本以及和有赞云技术支持的沟通成本。

从上图可以看到,当Side Car调用应用时,框架层会解析协议,将协议还原,通过必要的一些校验,转换为当前语言可读的对象或者实体,通过规则路由调用到具体的扩展点实现。

一目了然,开发者就关注扩展点实现就行,在扩展点实现上,针对各个语言设计了各个语言的方式来进行声明和实现,比如Java需要采用注解、Php采用Facade等等。

3.2.2 日志

对于所有语言来说,日志是开发和运行过程中最需要的组件,日志是一个相对稳定的组件,经过这么多年的发展,已经很明确的能够定义日志内容包含哪些信息,同时日志也是应用主动调用去打印的过程,所以我们将日志上报能力封装在了框架里,因为他离业务更近。

image-20190613181031519

天网,有赞云应用的日志平台,所有开发者的应用日志都会打印上传到天网进行计算和存储;

所以针对日志,天网提供了统一的日志协议和端口,应用只需要按照规范封装日志格式,就可以将日志打印到天网,应用的日志并不是存在在服务器本地文件上;

3.2.2.1 Java应用

  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;

  ...........................
    ..............................
        private static final Logger logger = LoggerFactory.getLogger(Xxxxx.class);
  ............................
    ...............................
            logger.info("test log");

在应用框架层,已经封装好了日志协议和上报天网的逻辑,开发者只需要像平常使用Slf4j一样打印日志就行。

那么在实现上我们使用了Slf4j的规范,通过SPI实现了天网的日志管道。

3.2.2.2 Php应用

image-20190613211736716

Php框架使用Monolog,在Monolog里增加了日志处理器SkynetProcessor来封装日志协议,通过SocketHandler来打印日志到Rsyslog上。

3.3 其他

回到一开始多语言的思维导图,要支持多语言,其实要做很多事情。

我们在做这些事情的原则就是尽量让开发者少感知,保证整体系统的健壮性和扩展能力。

在做这些事情时最麻烦的可能就是升级,当我们发布一个新功能或者升级了协议后,推动所有应用去升级是一个漫长的周期,此时我们的底层可能得做无数的兼容逻辑,这样会导致系统无法往前发展,同时开发者也会很反感,频繁的去手动指定版本并去发布(此时没有修改一行业务代码、而且可能还得改造代码)。

关注其他的一些模块,本期暂时就先略过了。

四、规划与展望

规划的目标是希望提升开发者的体验,助力商家成功。

所以会开放更多扩展能力,真正做到全流程每个细节点都可以定制。

在开发体验上,需要实现WebIDE,方便开发调试,甚至让开发者不用感知项目,Faas也可能是一条很好的路。

在多语言上,尽量得做到统一底层,而不是新增一种语言给我们带来浩大的工作量。

同时乐见各路开发者的奇思妙想,开发出各种非常有意思、有价值的应用。

欢迎关注我们的公众号