前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >为什么SOA如此重要

为什么SOA如此重要

作者头像
tyrchen
发布2018-03-28 14:53:52
9070
发布2018-03-28 14:53:52
举报
文章被收录于专栏:程序人生程序人生

SOA是Service Oriented Architecture的缩写,希望你知道其大概意思。在上一篇文章「测量」中,我引用了Bezos的一段将Amazon的软件架构完全转换SOA的备忘录,它是如此关键,某种程度上可能改变了Amazon的命运(想想AWS)。

SOA是一种思想,而不是wikipedia上列举的一些「已经没落的」技术的合集(XML/HTTP/SOAP/WSDL/UDDI)。十年前SOAP/WSDL/UDDI大行其道,现在已经基本无人问津(如果你趟过在线旅游的浑水,那么可能知道booking.com依旧提供基于SOAP封装的API),可见技术会没落,但思想不会。

SOA的精髓是严格的松散耦合,大家按照一个契约(service interface)来进行交流。正如Bezos所述那样,"There will be no other form of interprocess communication allowed: no direct linking, no direct reads of another team's data store, no shared-memory model, no back-doors whatsoever. The only communication allowed is via service interface calls over the network"。

不允许shared memory,不允许back door,不允许直接访问其它服务的数据这都好理解,因为它破坏了封装性,造成了一种内部依赖。当服务的内部状态发生改变,则这改变带来的影响会散播到所有依赖该服务的地方 —— 做软件的人都知道,这是最头疼的事情,各种莫名的bug往往在这种情况下产生。

但为什么不允许把服务封装成一个library,提供标准的API,让调用者将其link到自己的服务中调用呢?只要API不发生变化,无论library怎么折腾,都不会影响依赖于该library的地方啊?

考虑一下这样一个功能:给定一张图片的路径,获取里面的exif信息。这个功能使用python实现再简单不过了:

代码语言:javascript
复制
import exifread
import urllib
def get_exif(path):
    if path.startswith('http'):
        path, _ = urllib.urlretrieve(path)
    f = open(path, 'rb')
    return exifread.process_file(f, details=False)

当然,这个实现并不完善,而且对http不友好(整个文件被download下来,写入磁盘然后再被读出),但基本可用。

如果我们把它作为一个package给其它人用,似乎也没有什么大问题。姑且这么做吧。

后来随着系统的扩张,某个新功能X选择用golang实现。不巧的是,X也需要从图片里读取exif信息这个功能。golang无法直接调用python代码,如果要坚持使用static linking的方式,摆在面前有两个选择:

1) 用golang把相同的服务重新实现一遍

2) 用c重写获取exif信息这个功能的核心代码,然后分别创建python和golang的binding。

1)自然不好,违反了DRY (Don't Repeat Yourself) principle。2)虽然理论上可行,但是在给自己挖坑 —— 本来只需要维护几行代码的,现在变成了三种语言的三套代码。

这是static linking的诸多潜在缺陷的一个。

如果我们把获取exif信息这个功能做成一个服务,该怎么做?以下是一个方案(我刻意没有将其作为一个http service):

代码语言:javascript
复制
import exifread
import urllib
import zmq

ENDPOINT = "tcp://*:5555"

def main_loop():
    ctx = zmq.Context()
    sock = ctx.socket(zmq.REP)
    sock.bind(ENDPOINT)
    print("Listen to %s" % ENDPOINT)

    while True:
        cmd, path = sock.recv_string().split()[:2]
        print("Got request: %s %s" % (cmd, path))
        if cmd == 'EXIF':
            if path.startswith('http'):
                path, _ = urllib.urlretrieve(path)
            f = open(path, 'rb')
            tags = exifread.process_file(f, details=False)
            tags = dict(map(lambda (k,v): (k, str(v)), tags.iteritems()))
            print("Sending response: %s" % tags)
            sock.send_json(tags)

if __name__ == '__main__':
    main_loop()

exif服务和该服务的调用者之间的约定是:

1) 调用者向5555端口发送 CMD PATH(如:EXIF http://domain.com/img.jpg)

2) exif服务会返回json格式的exif信息

如果用golang访问该服务,则非常简单:

代码语言:javascript
复制
package main

import (
    zmq "github.com/pebbe/zmq4"
    "log"
)

func main() {
    sock, err := zmq.NewSocket(zmq.REQ)
    if err != nil {
        log.Printf("err create: %v", err);
        return
    }
    defer sock.Close()
    err = sock.Connect("tcp://127.0.0.1:5555")
    if err != nil {
        log.Printf("err connect: %v", err);
        return
    }

    _, err = sock.Send("EXIF https://farm8.staticflickr.com/7163/6602016347_24803ae230_o.jpg", zmq.DONTWAIT)
    if err != nil {
        log.Printf("err send: %v", err);
        return
    }

    msg, err := sock.Recv(zmq.Flag(0))
    if err != nil {
        log.Printf("err recv: %v", err);
        return
    }

    log.Printf("exif: %v\n", msg);
}

这样做除了把服务和其调用者使用的语言解耦外,还有很多其它好处:

1) 服务本身非常专一,不会混入乱七八糟的逻辑,因而更容易定位问题。

2) 服务本身很好测量,可以随时检查其performance和latency。测量是优化的前提,一旦有好的测量手段,那么优化只是一个时间问题,总能想到办法(甚至可以换一种语言重写)。

3) 很容易scale out。

4) 可以轻松添加ACL,为调用者设置优先级和阈值。

有一天我们发现这个服务不但可以内部使用,还可以公开给第三方获得收入,只需要再添加一个新的服务调用者,然后把获取的数据通过http service发布即可。

多说几句「把服务和调用者使用的语言解耦」的重要性。

从软件工程的角度来说,这有助于项目的快速高质完成。我们知道,每种语言(及其类库)都有其优缺点,在需要glue language的场景下使用c而不是python,在需要高性能高并发的场景下使用ruby而不是golang,都只能是事倍功半。

从软件工程师的角度来说,他们可以不必深深陷入已有系统的泥沼中,完整了解整个系统的来龙去脉,代码的曲折历史就能开始做他们该做的活。理想情况下,做一个新的功能,有80%和老代码解耦,只有20%通过接口耦合。如果对于一个已经在同行业里浸淫过的人来说,一个系统需要花至少三个月的时间培训和学习才能开始上手,那么这一定是系统架构出了问题。

有人认为SOA无法适用于对性能要求很高的场景,这是一个误区。任何系统都有一个现实的性能指标,而非毫无目标的越高越好。如果是那样,撇开操作系统提供的服务,所有代码直接用汇编构造理论上能榨干硬件最大的能力。但除非极少数项目,没有人会那么做。有了可参照的性能指标,服务需要做的就是达成这个性能指标。硬件的选择,开发语言的选择,并发模型的选择,各种工具的选择,直至消息传递方案的选择,都可以帮助达成约定的性能指标。使用SOA反倒促成了这种多样的选择,同时其松散的结构让大刀阔斧地调优成为可能。如果系统是紧密耦合的,即便有测量的手段感知到影响性能的关键路径,但由于「牵一发而动全身」,反倒不好处理。

最后,SOA的思想其实对软件工程师的职业生涯大有裨益。你不必被「锁定」在某个公司使用的某只语言,某个平台,或者某种框架上,而是可以依行业的「最佳实践」,进行「自由裁量」。某个公司总有倒掉的那一天,某种技术总有会没落的那一刻,不变的只有变化和思想。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2014-09-11,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 程序人生 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档