专栏首页架构专题这次要是讲不明白Spring Cloud核心组件,那我就白编这故事了

这次要是讲不明白Spring Cloud核心组件,那我就白编这故事了

一、我要聊点技术了:单体应用

我们刚开始的服务,其实并没有那么复杂。我只有一台配置非常低的机器,我的应用,我的代码,我的聪明才智,全部在这一个小小的工程里面。由于我是搞it的,所以我的项目名字就叫jisuanji。有人说我用中文拼音做项目名,太那个。我不听,我就是这么命名。我还把公共模块叫gg,密码字段叫mm,谁管得着呢。

对,看下面的图,就是这么简单。项目能活到用nginx来做负载均衡这一步,就算是小成功了。

这个时候,所有的代码就是一个整体,用户访问什么,我直接给就是。

二、我拆成了两个服务

可能是我和我一样二的人有点多,我的项目访问量越来越大,这也许就叫臭味相投吧。我自己的开发速度,已经追不上头脑里的idea,是时候招个人对服务进行拆分了。

不能拆的太过火,所以刚开始,我把jisuanji拆成了两个服务。其中的服务B,仅仅部署了一个节点,因为它的压力还不是太大。即使这样,我不得不买上3台服务器来部署服务节点,真是肉痛。我这么抠门的人,数据库当然也是共用的。虽然有时候机器压力有点大,但暂时还死不了人。

这个时候我就面临了一个选择问题:服务A要怎么访问服务B呢? 由于我搞过一段时间的webservice,首先就想到了它。但这玩意太重了,我还不如通过Http访问来的舒爽。通过HttpClient,或者OkHttp,我的服务A,现在可以直接模拟Http请求访问服务B了。

当团队里有第二个人,就开始吐槽我的项目了。以下是他罗列的,我的项目的罪状:1、复杂度太高,代码严重耦合;2、技术债务多,拍脑袋需求一箩筐;3、代码不规范,一坨屎;4、技术创新难,一个类几千行…

至于么?从一个服务拆成两个,就这么吐槽我。不过为了以后能拆出成百上千个服务,这口气我暂时忍了,毕竟我这人还是比较虚心的。

三、乱成一锅粥了

等过去半年一看,好家伙,服务给我拆了了几十个。当我的同伴把系统结构图拿给我看,我直接懵逼了。我挑了9个能看的服务,画了张图。

首先进行了业务拆分。比如支付业务,订单业务,用户中心,商品中心等,都组建了独立的团队。每个业务又进行了细分,拆分成不同的服务。

在这之间,进行了下面的改动: 一、有小伙伴写了个通用的HttpClient调用组件,自己的负载均衡策略。 二、有另外一个小伙伴,习惯protobuf,所以选了gRPC。 三、事实证明SOA还是有市场的,这不,就有几个服务的交互引入了webservice。 四、有人想要用RMI,被我及时发现、否决,腹死胎中了。 五、每次建个新服务,都需要更新一下excel,然后将这个excel周知出去。

现在的整个系统,简直是个四不像。什么通信方式都有,什么交互格式都不缺。拿最要命的D服务来说,光通讯模块,就引入了20几个jar包。如果应用扩展到上千个…My god…

更要命的是,这么多服务,每次上线一个模块都胆战心惊,因为它不知道到底会有什么连锁反应。

是时候叫出超级飞侠了。哦不,叫出微服务了。

四、微服务来袭

目前,最火的微服务框架,就是SpringCloud了。虽然netflix公司对某些组件的维护经常爽约,但有些核心组件还是非常经典的。

1、注册中心:Eureka

服务A,怎么找到服务B,有很多种方式。比如你生活在一个小镇上,你问xjjdog是谁,老王可能认识他,但小李可能并不知晓;但小李认识老王,所以通过他最终也能找到xjjdog,只不过麻烦一些。

你可以随便拉小镇上的一个人,来问xjjdog是谁。你还会变戏法一样拿出一个小本本,把你认识的人,都告诉他们。当你脑残式的问了一个遍,到最后所有人都知道xjjdog了。

上面说的就是gossip协议。最终,你们都能够知道彼此,因为都是大嘴巴。比如小郑生了个孩子,过不了多少时间,全镇子的人都把这个孩子记录在本子上了。

用这种方式,服务都能够知道彼此,完成通信。


可惜这并不美好,从小镇的东头跑到西头,需要很长时间。在这个时间里,小郑刚生的孩子可能因为先天疾病夭折了。我们需要一种信息集中度和实效性更高的方式。

这就需要一个中心,那里的信息就是权威。 在SpringCloud体系中,最常用的注册中心就是Eureka。任何服务启动以后,都会把自己注册到Eureka的注册表中;当服务死亡的时候,也会通知Eureka。

这样,当服务A想要找服务B的时候,只需要问一下Eureka Server就可以了,它什么都知道。

为了达到这个目的,还是要有一部分工作量的。且看下图。这个注册动作,是由一个叫做Eureka Client的组件来完成的。服务启动和关闭的时候,会通过这个组件推销自己;而当服务A想要调用服务B的时候,直接问Eureka Server就可以了。服务A拿到结果后,会把结果缓存在本地的注册表里。

你可以认为是一个拷贝。所以Eureka Server死掉后,并不影响服务A找到服务B。

2、负载均衡组件:Ribbon

现在问题来了。服务A拿到服务B的实例列表以后,发现有两台。

10.0.0.12
10.0.0.16

接下来麻烦了,该调哪台机器呢?这就是SpringCloud中组件Ribbon的作用。其实Round Robin是一个通用的计算机术语。它是最常用的负载均衡策略,请求会均匀的分配给后面的每台服务器。

Ribbon工作时,会做下面四件事: 1、优先选择在一个Zone且负载较少的Eureka Server,进行连接。 2、定期从Eureka更新、过滤服务和实例列表。 3、根据负载均衡策略,从注册表中选择一个真正的实例地址。 4、通过RestClient对服务发起调用。

可以看到,Ribbon背后,还是采用的Http协议进行交互。看以下代码,就可以直接实现对远端服务的调用。

@Bean
@LoadBalanced
RestTemplate restTemplate(){
    return new RestTemplate();
}
...

 @Autowired
RestTemplate restTemplate;
public String test() {
    return restTemplate.getForObject("http://test-service/test", String.class);
}

Ribbon的Filter会查找test-service,并替换成相应的实例地址。

策略 Ribbon不仅仅提供了轮询的策略,还有其他的,比如: 1、随机Random 2、根据响应时间加权 3、自定义

拿轮询来说,最终的选择逻辑就在RoundRobinRule类中。

private int incrementAndGetModulo(int modulo) {
        for (;;) {
            int current = nextServerCyclicCounter.get();
            int next = (current + 1) % modulo;
            if (nextServerCyclicCounter.compareAndSet(current, next))
                return next;
        }
}

3、为简化代码而生:Feign

可以看到,Ribbon需要自己构建http请求,模拟http请求然后使用RestTemplate发送给其他服务,步骤相当繁琐。而且返回类型不安全,也表达不出什么语义。

其实,通过Ribbon方式,已经能够完成微服务之间的调用了。但SpringCloud的开发语言是Java,肯定要进行更加高级的封装,才能体现它的逼格。

Feign得益于Java的动态代理机制,最终封装出一套简洁的接口调用方式,将需要调用的其他服务的方法定义成抽象方法即可,不需要自己构建http请求。

首先,Feign会根据@FerignClient注解,通过动态代理,创建一个动态代理类。接下来,你只要通过调用接口的方式,就可以构造上面提到的Ribbon调用参数,这个过程会自动填充。最后,通过构造的Ribbon请求,发起真正的调用,并通过反射组装返回值。

所以,Feign只是一层皮,最终还是要通过Ribbon进行调用。在我看来,把Ribbon和Feign合成一个组件,也是合理的。

它们有一个比较通用的名词,就叫做RPC(远程调用)。

4、异常的保护伞:断路器

下面以一个支付请求为例,说一下不是风平浪静的情况下,服务会有什么反应。

每一个真正的支付请求,都会调用其他四个服务。首先,使用鉴权服务,获取用户的支付权限;然后,风控服务会做一些规则验证;为了更好的推销产品,会调用营销业务,获取一些推荐信息;最后,调用聚合支付服务,进行真正的支付。

其中,营销业务其实是可有可无的。让用户首先把钱花出去,是我们的首要任务。

考虑下面一种场景,营销业务由于系统故障或者负载问题,发生了大面积的不可用或者超时。然后,所有的请求都卡在了获取营销信息的代码上。

如图所示,鉴权和风控都已经通过了。因为一个旁路功能:营销业务,导致真正的支付无法进行。这个时候,如果有人调用支付请求,会发现支付请求也完蛋了。

因为它们最终都卡在了营销这一段小代码上。

所以,对于营销业务这种不是链路上必备的服务提供者,要有一个手段,让它在发生问题的时候,隔离它一段时间。

负责这个功能的组件,就叫做Hystrix。

以我们编程的思维来说,这就是个if条件。

if(服务发生问题){
    return "暂时不要处理";
}

但我们不能这么编码在业务代码里。所以Hystrix对每个服务开了一个线程池,并有比较复杂的规则,来控制这些出问题的服务的行为。比如,在2分钟内,直接返回营销业务的默认结果,而不是一直卡在那里。

这个过程,就叫熔断。就像电源一样,出了问题,先切断保险丝,别把电器给烧了。

5、此网关非彼网关:zuul

API网关是一个反向的路由,它屏蔽了内部的细节,为调用者提供了统一的入口。网关,其实是一堆过滤器的几何,可以实现一系列和业务无关的横切面功能。

熟悉Spring的都知道AOP,路由的一个功能,就是针对于分布式服务的一个AOP。

还是先说下网关的职责吧。简单罗列几个: 1、安全认证。提供统一的认证方式和鉴权功能,避免重复开发。 2、熔断,限流。针对问题服务,进行熔断操作;对流量进行预估,限制访问。 3、日志监控。统一流量入口,进行流量分析和监控。 4、屏蔽内部细节,对外提供一致的接口。 5、实现灰度。使用自定义策略实现分流,达到测试的目的。

网关的位置,大体就如下图。

可以看到,我们平常用的nginx,就可以当作网关。但对于微服务来说,nginx的配置实在是太麻烦了。不是说nginx功能不够强大,而是因为它们不是一个体系的,就存在整合成本(比如kong)。

zuul就不一样了,它和SpringCloud的其他组件,是一家子的。一家子的,当然会特殊照顾。Zuul本身就是一个Servlet,外部请求经过一系列Filter后,会达到真正的服务。上面说的熔断器,就是高度集成的。

6、一张聚合图

有了上面关键组件,事情就明了的多了。我们把它放在一张图中,就是下面的样子。

我们将其简化一下,就可以得到一张更简洁的图。可以看到,只需要3个关键点:

1、服务注册中心,统一管理所有服务的信息,默认组件是Eureka。 2、RPC,网络通信组件,服务A怎么调用服务B。在SpringCloud中,就是Ribbon+Feign。 3、网关,拆分的服务怎么暴露接口,最终见人的样子。默认组件是Zuul。

本文分享自微信公众号 - 小姐姐味道(xjjdog),作者:小姐姐养的狗

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-07-31

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 这一轮,skywalking胜出

    了解xjjdog的都知道,在微服务trace方面,我在两家公司实施了uber的jaeger。但是,jaeger虽然可以搜集调用链信息并查询,但统计图表相对欠缺,...

    xjjdog
  • 传统企业的人才们,先别忙着跳“互联网”!

    谈到互联网,从业者首先会想到2B (toB) 和2C两个概念。B和C两个字母隔得很近,但却有着天囊之别。十年河东十年河西,在2C逐渐没落的今天,很多多金的大佬,...

    xjjdog
  • 有了MinIO,你还会用FastDFS么?

    最近看到ReadHat在搞Ceph的培训,而且是收费的,真的是吓了一跳。难道真要搞这么复杂这么强大的存储方案么?有了MinIO,我知道我永远和Ceph无缘了。

    xjjdog
  • 这次要是讲不明白Spring Cloud核心组件,那我就白编这故事了

    我们刚开始的服务,其实并没有那么复杂。我只有一台配置非常低的机器,我的应用,我的代码,我的聪明才智,全部在这一个小小的工程里面。由于我是搞it的,所以我的项目名...

    macrozheng
  • 这次要是讲不明白Spring Cloud核心组件,那我就白编这故事了

    这几天可真是热啊,泡个海澡是再好不过了。玩的正起劲,突然脚底绊上一股暗流,然后我就一直在水里旋转旋转旋转…终于眼前一黑。

    猿天地
  • 这次要是讲不明白Spring Cloud核心组件,那我就白编这故事了

    邻国相望,鸡犬之声相闻,民至老死不相往来。这个世界被小诸侯给切的七零八落,一锅乱麻。

    田维常
  • 云计算将传统的IT服务转化为在线服务

    云计算将传统的IT服务转化为在线服务,虽然云存储的使用成本较低,但结合安全、性能、维护等多方面问题,真正将云端做大做强需要强稳的根基和雄厚的资金链。所以并不是所...

    静一
  • 深度|2020年中国企业服务研究报告

    ? 来源 :艾瑞咨询  ---- 企业服务 | 研究报告 全文字数:11231字 精读时间:28分钟 核心摘要: 本质是社会分工及复用: 企业服务的本质是社...

    腾讯SaaS加速器
  • 微服务架构

    微服务产生的背景 在网站初期,网站的架构比较简单,通常把所有代码统一打包部署的服务器上 以java项目为例,例如有5个程序员,他们各自开发自己的功能模块,然后...

    dys
  • 什么是微服务?

    在介绍微服务时,首先得先理解什么是微服务,顾名思义,微服务得从两个方面去理解,什么是"微"、什么是"服务", 微 狭义来讲就是体积小、著名的"2 pizza 团...

    芋道源码

扫码关注云+社区

领取腾讯云代金券