V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
DavidGao
V2EX  ›  程序员

像架构师一样来思考微服务接口设计

  •  
  •   DavidGao · 2017-08-04 13:54:31 +08:00 · 5777 次点击
    这是一个创建于 2674 天前的主题,其中的信息可能已经有所发展或是发生改变。

    如何做接口的设计

    从我个人角度来说,可以从以下几个特性进行分析:

    1. 大规模系统和小规模系统
    2. 面向内部系统接口和面向外部系统的接口
    3. 大数据传输的接口和小数据传输的接口
    4. 长链接的接口和短链接的接口

    很多时候我们优先考虑的是系统有多大,扩张性要有多好,对内还是对外以及我们有多大的能力。很多时候这个东西并没有一个定论,更多是基于业务和团队人员组成而决定的。

    接口的实施

    一定要做的事情

    不管接口是对内的还是对外的,我们都要做以下几件事情:

    1. 接口功能定义是否明确,是否有功能重复的地方
    2. 接口的升级机制,是否能兼容以前的数据
    3. 接口的数据量是多少,是否需要使用传输压缩机制
    4. 接口的熔断点在何处,何时该降级或停止服务
    5. 接口的安全机制是怎么样的,如何将非法调用隔离开来

    这些事情是我们在开始设计和实现接口的时候,必须要先想到的。但是不要认为我们想到了这些东西,我们就可以高枕无忧,然后事情就会像我们期待的那样发展下去。很多时候,接口都会变成像阿米巴原虫一样,不是圆的而是不规则的多边形。

    对内的接口

    对内的接口简单说就是 SOA,但是 SOA 也有很多种做法,例如常见的 dubbo 框架。在 dubbo 框架下,我们所做的事情完全可以说是在 dubbo 框架下进行业务开发,并定义 interface 然后暴露出去,我们此时貌似没有进行接口设计,但是实际上我们是完全按照 dubbo 的规范完成了接口的定义,没错就是那个 interface。看起来对内部的接口完全非常明确了,没什么可讲的,但是其中还是有很多东西可讲的,我先讲讲我们常见的。

    对服务发现的方案选择:

    1. 使用主动推送的方式,注册中心每次发生变化都会推送最新的列表给服务的使用者
    2. 使用被动拉取的方式,注册中心每次变化都保存好,然后使用者每次调用服务者的时候,先到注册中心查询一次

    好了,让我分别来说说这两个方案

    使用主动推送

    可以让使用者很快的更新服务者信息,使用者调用服务者的时候只需要在本地的一个 hash 表中查询一下即可,并且注册中心挂掉了之后,也不影响使用者调用服务者,看起来不错吧。那么让我来说说这方案的弊端,首先要实现 watch - notify 机制,大概有人会说不是有 Zookeeper 吗?自带该机制和数据冗余机制,那么我想说的是,当业务量起来的时候,Zookeeper 的 watch 机制真的能顶住吗?接着是,服务者的负载均衡并不好处理。那么有没有解决方法,这个可以参看 dubbo 中的注册中心是如何玩耍的。

    使用被动拉取

    这个好像很直观,但是每次都查询注册中心,这性能,注册中心能处理的了吗?大家不妨想一下 DNS 服务器,其实该方案完全可以使用简单的内部 DNS 实现。那么该方案的好处不言而喻,负载均衡好处理,并且非常简单。但是问题呢,性能和稳定性是要深入考虑的事情。

    传输协议

    剩下的就是需要考虑的传输协议了,为什么要考虑传输协议?原因很简单:

    1. 接口平均传输的数据量和自己的内网带宽的平衡
    2. 是否要跨语言协作
    3. 是否侵入业务了

    为何考虑带宽

    虽然注册中心第一步解决了我们的快速扩张的问题,但是呢,内网带宽毕竟是有限的。随着服务数量增多和调用量的增加,有时候我们会发现,同一个服务我们明明增加了 N 台部署响应时间却下降了很多,按照公式应该响应时间不变的呀?这个时候,我们可能猛然看到监控上我们的内网带宽已经跑满了。

    为何考虑跨语言

    难道一个公司不就是一种后端语言?其实不然,我曾见面试过一个公司,内部的业务之复杂,语言使用之繁多。很多时候,我们需要站在一个公司发展的角度上考虑这个问题,而不是一个纯技术的细节上考虑这个问题。

    为什么要考虑是否侵入业务

    不侵入业务,就是尽可能的封装底层的实现,让业务线更少的去考虑底层发生什么了。很多人说,这对业务线的人不公平,阻碍了他们的技术发展。其实不然,让业务线的同仁们更多,更深入的思考业务发展是非常重要的事情,我个人认为研发分两类,一类是玩算法和底层的,另一类就是深入业务的,他们都有自己的长处和短处。其实减少业务的侵入是为了更快的实现产品功能,让产品上线,让公司的业务快速迭代起来,这样对任何人都是有好处的。

    接口升级

    这个与其说是升级,不如说是怎么做不同版本的数据共存和 A/B 测试。一般在很多成行的 SOA 系统中,已经很完善了,我没必要在这里面多废话。但是还是要多说一句,数据多版本不易,且升且小心。

    对外接口

    对外接口,大家很快就会想到 Restful。随着现在创业的兴起,应当说是智能手机和 Web2.0 的兴起(更应该说的本质是,网络带宽变好,手机流量降价)。但是对外接口并不限于 Restful,还有大家不愿意谈的纯 Socket 接口。对外接口可讲的东西非常多,不过思路上基本上和对内接口没太大的差别,所以我这里就主要讲下为什么选择纯 Socket 的接口。

    我们不愿意面对的长链接,很多研发,甚至公司级别,都不愿意去尝试这个技术。原因嘛,请看下面:

    1. 调试复杂,研发成本高
    2. 国内网络环境复杂,加重了第一条
    3. 国内用户对流量敏感,长链接心跳控制不好,容易被认为是偷流量
    4. 协议设计比较复杂,对研发的要求上升了很多

    但是长链接真的就那么难嘛,其实不然。更多时候,是产品层面用不上,一般只有 IM 类型的应用或者实时对战类的游戏才会选择长链接。当然偶尔我们也想提供一些高互动的交互,如果只是在应用内短暂使用,完全可以选择 websocket (不过面对中国强大的高铁和运营商基础建设的规划 TT )。

    接口的保护

    安全保护

    当我们面对很多外部接口的时候,我们需要考虑数据的安全性。为什么要考虑安全性:

    1. 包含用户数据
    2. 包含交易数据
    3. 以及甚至你不想让用户自己知道的数据

    保护接口的方式最基本的是 SSL/TLS,然后呢:

    1. 对称加密的方式
    2. 非对称加密的方式
    3. 动态秘钥

    先说下我们为什么要在 SSL/TLS 下面再次进行加密呢?大家可能听说过以色列一个网络安全公司的事情了,换句话说一旦根证书被释放出去了,分分钟可以做 SSL/TLS 的 man in middle 的攻击。同时有些稍微高级的用户,会针对你的接口进行刷接口的行为。

    对称加密

    简单且易用。但是问题也明显,一旦秘钥泄漏或者被用户强猜出来了,那么影响还是很大的。

    非对称加密的方式

    实现略复杂,同样也面临第一种方式的问题。但是可以有一个专门的秘钥管理人员,生成公钥和秘钥对后,将公钥交付给客户端,将秘钥交付给服务器端,大大减少了泄漏的可能性。同时用户即便猜出了客户端的公钥,也无法解密别的用户提交的数据,而只能伪造请求。

    动态秘钥

    机器在运行的时候,定期自动和秘钥管控中心进行秘钥交换,每台机器在交互的时候使用的秘钥都不同。虽然可以带来一定的安全性,但是会给秘钥管理中心带来巨大的压力,同时调试也比较麻烦。这种方式个人认为适合使用在,传统小交易量的行业中,例如说银行的 ATM 机。

    熔断保护

    内部接口需要吗?

    我们可以假定一个场景,服务者 A 有 10 个服务器,但是由于使用者 B 的算法错误,总是先选择服务者 A 的某台服务器,那么我们可以想象到服务者 A 的某台服务器压力非常大,然后逐步的就失去了响应,接着就会被认为被离线,接着使用者 B 又同样的方式打掉了第二台服务器。带来的影响就是,轻者响应速度很慢,严重的就是整个系统雪崩性的逐个崩溃停止服务。

    一般怎么做

    不管对内部还是对外部,我们都可以选择使用漏桶和令牌桶等算法来保护接口。对外部,我们还可以通过使用时间戳加整个 URL 整体签名技术来防止重放攻击和进行限流保护。

    转载自TTalkIM

    12 条回复    2017-08-07 10:17:09 +08:00
    m1911star
        1
    m1911star  
       2017-08-04 16:44:48 +08:00   ❤️ 1
    前排支持学长
    ihuotui
        2
    ihuotui  
       2017-08-04 20:02:56 +08:00 via iPhone   ❤️ 1
    微服务接口重要是低耦合,高内聚,单一原则。也不单单是架构师来考虑的,要和业务结合。
    DavidGao
        3
    DavidGao  
    OP
       2017-08-04 21:29:31 +08:00
    @m1911star 感谢支持
    DavidGao
        4
    DavidGao  
    OP
       2017-08-04 21:33:42 +08:00   ❤️ 1
    @ihuotui 你说的很对,很多时候不单单是要架构师单方面考虑。但是这个话题比较宽泛,我在讨论这个问题的时候,提出了一个讨论范围,主要还是针对通用的接口设计范畴,而各个业务有其本身业务特性,很有可能已经超出我上面所提到的所有范畴。同时我对这种通用范畴的接口设计,大的观念还是减少对业务的侵入。
    ihuotui
        5
    ihuotui  
       2017-08-04 21:35:17 +08:00 via iPhone
    @DavidGao 其他文章质量也高,经过思考的文章
    DavidGao
        6
    DavidGao  
    OP
       2017-08-04 21:43:02 +08:00
    @ihuotui 很多是自己的工作经验和一些研究,希望你能一直喜欢。
    zjsxwc
        7
    zjsxwc  
       2017-08-04 21:52:46 +08:00   ❤️ 1
    学习了
    YzSama
        8
    YzSama  
       2017-08-05 07:41:34 +08:00 via iPhone   ❤️ 1
    @DavidGao 我公司同事喜欢,使用一个对外接口,通过 method 参数来指定调用那个服务(他说好像是用反射)。虽然,总觉得不好,但也没错。。 按他说法:安全 维护简单 性能也没问题?
    DavidGao
        9
    DavidGao  
    OP
       2017-08-05 08:21:35 +08:00
    @zjsxwc 感谢支持,互相学习。
    DavidGao
        10
    DavidGao  
    OP
       2017-08-05 08:33:36 +08:00   ❤️ 1
    @YzSama 你同事这么做也没有错,在 Restful 出现之前,就存在了很多对外接口设计方案,例如说 SOAP。你提到的设计,我的第一反应就是类 SOAP 设计,并且 SOAP 是一个非常强大的企业级设计,虽不轻量级,但是很完整。

    回到你这个具体案例上,单一 URI 并不会对性能造成影响,因为可以实用 nginx 随机负载到业务前端机上,后面在放置业务集群。安全性,可以在 Nginx 和业务前端机上进行统一处理和验证,所以参数定义明确且规范,安全措施有在做,就不是问题了。

    你感觉不太好是很正常的一个情况,因为现在随着 Restful 的推进,很少再能看到 SOAP 或类 SOAP 设计了。我曾经也使用 SOAP 和类 SOAP 设计,接触 Restul 后感觉这类设计确实有点怪怪的感觉。
    leeg810312
        11
    leeg810312  
       2017-08-05 20:04:31 +08:00 via Android   ❤️ 1
    我觉得现在很多时候只考虑 restful 风格的 API 不考虑 rpc 风格的 API 是非常有问题的。我认为读写资源用 restful 设计比较好,而操控资源用 rpc 风格比较好,我要的不是资源本身,而是操控资源的结果以及一些状态,现在云服务商提供的接口 2 种风格设计都有
    DavidGao
        12
    DavidGao  
    OP
       2017-08-07 10:17:09 +08:00
    @leeg810312 非常赞同你的说法,根据业务不同尽可能的针对性设计比较好。因为操控资源的 RPC 直接使用 Restful 很多时候让人感到迷惑,但是选择这样做的很大原因是,为了接口统一,开发统一。如果在条件容许的条件的情况下,尽可能明确和分开是很好的选择。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2894 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 28ms · UTC 13:30 · PVG 21:30 · LAX 05:30 · JFK 08:30
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.