什么是SOA服务架构?

SOA

SOA是Service Oriented Architecture的缩写,面向服务架构。Bezos(亚马逊CEO)将Amazon的软件架构完全转换为SOA,它是如此关键,从某种程度上改变了亚马逊 Amazon 的命运。

思想

SOA的精髓是严格的松散耦合,不允许直接访问其它服务的数据,大家按照一个契约或标准(service interface)来进行交流。

百科解释:
面向服务的体系结构(SOA)是一个组件模型,它将应用程序的不同功能单元(称为服务)通过这些服务之间定义良好的接口和契约联系起来。接口是采用中立的方式进行定义的,它应该独立于实现服务的硬件平台、操作系统和编程语言。这使得构建在各种这样的系统中的服务可以以一种统一和通用的方式进行交互。

Paste_Image.png

优点

  1. 松耦合
  2. 易上手
  3. 可复用(DRY原则 Don't Repeat Yourself)
  4. 组件化
  5. 跨平台
  6. ....

松耦合

之前的所有操作集成于某个功能模块,包括数据操作、逻辑处理等等。当项目一旦大起来,可能一次操作要操作多个功能模块,那么这种嵌套关系就变得很复杂,导致相互依赖,那么之前所有的东西修改起来都会影响到其它业务的执行,这样维护起来变得相当困难。
而松耦合恰好就是解决这种依赖关系,通过接口访问数据信息。
不允许直接访问其它服务的数据,因为它破坏了封装性,造成了一种内部依赖。当服务的内部状态发生改变,则这改变带来的影响会散播到所有依赖该服务的地方 —— 做软件的人都知道,这是最头疼的事情,各种莫名的bug往往在这种情况下产生。

组件化

laravel的composer组件库、ruby的gem组件库、jquery组件库等等, 组件之所以为组件,是因为当下载或拉取之后,能够通过很简单的东西实现某一功能。

可复用

SOA架构是面向服务的架构设计,它通过封装一个服务,所有的操作数据功能,都只能用给定的接口使用。当需要扩展一个功能时,如果已存在相应接口,那么只需要调取相应的接口即可。

跨平台、跨语言

Service层可以用任何语言来实现,也可以使用多种语言,实现物尽其用的原则,比如PHP擅长处理逻辑、Ruby语言擅长高并发、java大数据等。但提供的接口统一,不管服务层如何实现,应用层只需要调用接口即可。

易上手

何为易上手?
当进入到一个公司,首先要做的是熟悉代码,少则四五天,多则七八天,因为嵌套太多,要屡清楚层层的关系。而SOA架构呢,服务层跟应用层分离,刚进入公司,你直接可以写东西,接口数据唯一,你只需要了解都有啥接口,然后这些接口传递参数、返回的数据就可以了。

问题

为什么不在每个模块中封装API接口呢,供给其他模块使用?而是要单独抽离服务层出来呢?只要API不发生变化,无论library怎么折腾,都不会影响依赖于该library的地方啊?

Paste_Image.png

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

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选择用go语言实现。不巧的是,X也需要从图片里读取exif信息这个功能。go语言无法直接调用python代码,如果要坚持使用static linking的方式,摆在面前有两个选择:

  1. 用go语言把相同的服务重新实现一遍
  2. 用c重写获取exif信息这个功能的核心代码,然后分别创建python和go语言的对应的方法。
  3. 在python模块中封装API,供其它人调用。

![Uploading Paste_Image_254274.png . . .]

方法一自然不好,违反了DRY (Don't Repeat Yourself) principle。
方法二虽然理论上可行,但是在给自己挖坑 —— 本来只需要维护几行代码的,现在变成了三种语言的三套代码。
方法三是将它作为一种服务,提供对应的接口,实现对应功能,但是当将来发展时,python模块因为版本更迭,要去除。这时,提供的接口也相应会被砍掉,从而导致了管理空难。

最好的方式就是单独抽离一层出来,做成一个服务,所有模块的增加与去除都不影响该项服务的任何功能。
如果我们把获取exif信息这个功能做成一个服务,该怎么做?以下是一个方案

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信息
    如果用go语言访问该服务,则非常简单:
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. 服务本身很好测量,接口并发、负载等。测量是优化的前提,一旦有好的测量手段,那么优化只是一个时间问题,总能想到办法(甚至可以换一种语言重写)。
  3. 有一天我们发现这个服务不但可以内部使用,还可以公开给第三方获得收入,只需要再添加一个新的服务调用者,然后把获取的数据通过http service发布即可。

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

从软件工程的角度来说,这有助于项目的快速高质完成。我们知道,每种语言(及其类库)都有其优缺点,在需要glue language的场景下使用c而不是python,在需要高性能高并发的场景下使用ruby而不是go语言,都只能是事倍功半。
从软件工程师的角度来说,他们可以不必深深陷入已有系统的泥沼中,完整了解整个系统的来龙去脉,代码的曲折历史就能开始做他们该做的活。理想情况下,做一个新的功能,有80%和老代码解耦,只有20%通过接口耦合。如果对于一个已经在同行业里浸淫过的人来说,一个系统需要花至少三个月的时间培训和学习才能开始上手,那么这一定是系统架构出了问题。
有人认为SOA无法适用于对性能要求很高的场景,这是一个误区。任何系统都有一个现实的性能指标,而非毫无目标的越高越好。如果是那样,撇开操作系统提供的服务,所有代码直接用汇编构造理论上能榨干硬件最大的能力。但除非极少数项目,没有人会那么做。有了可参照的性能指标,服务需要做的就是达成这个性能指标。硬件的选择,开发语言的选择,并发模型的选择,各种工具的选择,直至消息传递方案的选择,都可以帮助达成约定的性能指标。使用SOA反倒促成了这种多样的选择,同时其松散的结构让大刀阔斧地调优成为可能。如果系统是紧密耦合的,即便有测量的手段感知到影响性能的关键路径,但由于「牵一发而动全身」,反倒不好处理。
最后,SOA的思想其实对软件工程师的职业生涯大有裨益。你不必被「锁定」在某个公司使用的某只语言,某个平台,或者某种框架上,而是可以依行业的「最佳实践」,进行「自由裁量」。某个公司总有倒掉的那一天,某种技术总有会没落的那一刻,不变的只有变化和思想。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,053评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,527评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,779评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,685评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,699评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,609评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,989评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,654评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,890评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,634评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,716评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,394评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,976评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,950评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,191评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,849评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,458评论 2 342

推荐阅读更多精彩内容