万字详解!终于有人把微服务架构讲清楚了。

本文详细探讨了微服务架构中的服务拆分、服务发布与引用的方法(如RESTfulAPI、XMLIDL、gRPC等)、服务注册与发现机制(如注册中心的作用和实现)、以及远程通信中的网络I/O处理、传输协议和序列化选择,强调了微服务稳定性和服务治理的重要性。
摘要由CSDN通过智能技术生成

微服务化的前提是服务拆分到足够”微“,足够单一职责,当然拆分程度与服务边界都需要结合业务自行把握。

广义的服务拆分即包含了应用拆分,也包含了数据拆分。应用拆分后需要引入微服务框架来进行服务通信与服务治理,这也就是传统定义上的微服务。

数据拆分后同样需要引入一系列手段来进行保障,由于不是与微服务强相关的话题,在此只做简单阐述:

  • 分布式 ID

  • 新表优化

  • 数据迁移与数据同步

  • SQL 调用方案改造

  • 切库方案

  • 数据一致性

具体的微服务请求背后

在我们对微服务架构有了整体的认识,并且具备了服务化的前提后,一个完整的微服务请求需要涉及到哪些内容呢?

这其中包括了微服务框架所具备的三个基本功能:

  • 服务的发布与****引用

  • 服务的注册与发现

  • 服务的远程通信

服务的发布与引用

首先我们面临的第一个问题是,如何发布服务和引用服务。具体一点就是,这个服务的接口名是啥,有哪些参数,返回值是什么类型等等,通常也就是接口描述信息。

常见的发布和引用的方式包括:

  • RESTful API/声明式 Restful API

  • XML

  • IDL

一般来讲,不管使用哪种方式,服务端定义接口与实现接口都是必要的,例如:

@exa(id = “xxx”)

public interface testApi {

@PostMapping(value = “/soatest/{id}”)

String getResponse(@PathVariable(value = “id”) final Integer index, @RequestParam(value = “str”) final String Data);

}

具体实现如下:

public class testApiImpl implements testApi{

@Override

String getResponse(final Integer index, final String Data){

return “ok”;

}

}

声明式 Restful API:这种常使用 HTTP 或者 HTTPS 协议调用服务,相对来说,性能稍差。

首先服务端如上定义接口并实现接口,随后服务提供者可以使用类似 restEasy 这样的框架通过 Servlet 的方式发布服务,而服务消费者直接引用定义的接口调用。

除此之外还有一种类似 Feign 的方式,即服务端的发布依赖于 SpringMVC Controller,框架只基于客户端模板化 HTTP 请求调用。

这种情况下需接口定义与服务端 Controller 协商一致,这样客户端直接引用接口发起调用即可。

XML:使用私有 RPC 协议的都会选择 XML 配置的方式来描述接口,比较高效,例如 Dubbo、Motan 等。

同样服务端如上定义接口并实现接口,服务端通过 server.xml 将文件接口暴露出去。服务消费者则通过 client.xml 引用需要调用的接口。

但这种方式对业务代码入侵较高,XML 配置有变更时候,服务消费者和服务提供者都需要更新。

IDL:是接口描述语言,常用于跨语言之间的调用,最常用的 IDL 包括 Thrift 协议以及 gRpc 协议。

例如 gRpc 协议使用 Protobuf 来定义接口,写好一个 proto 文件后,利用语言对应的 protoc 插件生成对应 server 端与 client 端的代码,便可直接使用。

但是如果参数字段非常多,proto 文件会显得非常大难以维护。并且如果字段经常需要变更,例如删除字段,PB 就无法做到向前兼容。

一些 Tips:不管哪种方式,在接口变更的时候都需要通知服务消费者。消费者对api的强依赖性是很难避免的,接口变更引起的各种调用失败也十分常见。

所以如果有变更,尽量使用新增接口的方式,或者给每个接口定义好版本号吧。在使用上,大多数人的选择是对外 Restful,对内 XML,跨语言 IDL。

一些问题:在实际的服务发布与引用的落地上,还会存在很多问题,大多和配置信息相关。

例如一个简单的接口调用超时时间配置,这个配置应该配在服务级别还是接口级别?是放在服务提供者这边还是服务消费者这边?

在实践中,大多数服务消费者会忽略这些配置,所以服务提供者自身提供默认的配置模板是有必要的,相当于一个预定义的过程。

每个服务消费者在继承服务提供者预定义好的配置后,还需要能够进行自定义的配置覆盖。

但是,比方说一个服务有 100 个接口,每个接口都有自身的超时配置,而这个服务又有 100 个消费者,当服务节点发生变更的时候,就会发生 100*100 次注册中心的消息通知,这是比较可怕的,就有可能引起网络风暴。

服务的注册与发现

假设你已经发布了服务,并在一台机器上部署了服务,那么消费者该怎样找到你的服务的地址呢?

也许有人会说是 DNS,但 DNS 有许多缺陷:

  • 维护麻烦,更新延迟

  • 无法在客户端做负载均衡

  • 不能做到端口级别的服务发现

其实在分布式系统中,有个很重要的角色,叫注册中心,便是用于解决该问题。

我终于搞懂了微服务,太不容易了...

使用注册中心寻址并调用的过程如下:

  • 服务启动时,向注册中心注册自身,并定期发送心跳汇报存活状态。

  • 客户端调用服务时,向注册中心订阅服务,并将节点列表缓存至本地,再与服务端建立连接(当然这儿可以 lazy load)。发起调用时,在本地缓存节点列表中,基于负载均衡算法选取一台服务端发起调用。

  • 当服务端节点发生变更,注册中心能感知到后通知到客户端。

注册中心的实现主要需要考虑以下这些问题:

  • 自身一致性与可用性

  • 注册方式

  • 存储结构

  • 服务健康监测

  • 状态变更通知

①一致性与可用性

一个老旧的命题,即分布式系统中的 CAP(一致性、可用性、分区容错性)。

我们知道同时满足 CAP 是不可能的,那么便需要有取舍。常见的注册中心大致分为 CP 注册中心以及 AP 注册中心。

CP 注册中心:比较典型的就是 Zookeeper、etcd 以及 Consul 了,牺牲可用性来保证了一致性,通过 Zab 协议或者 Raft 协议来保证一致性。

AP 注册中心:牺牲一致性来保证可用性,感觉只能列出 Eureka 了。Eureka 每个服务器单独保存节点列表,可能会出现不一致的情况。

从理论上来说,仅用于注册中心,AP 型是远比 CP 型合适的。可用性的需求远远高于一致性,一致性只要保证最终一致即可,而不一致的时候还可以使用各种容错策略进行弥补。

保障高可用性其实还有很多办法,例如集群部署或者多 IDC 部署等。Consul 就是多 IDC 部署保障可用性的典型例子,它使用了 wan gossip 来保持跨机房状态同步。

②注册方式

有两种与注册中心交互的方式,一种是通过应用内集成 SDK,另一种则是通过其他方式在应用外间接与注册中心交互。

应用内:这应该就是最常见的方式了,客户端与服务端都集成相关sdk与注册中心进行交互。

例如选择 Zookeeper 作为注册中心,那么就可以使用 Curator SDK 进行服务的注册与发现。

应用外:Consul 提供了应用外注册的解决方案,Consul Agent 或者第三方 Registrator 可以监听服务状态,从而负责服务提供者的注册或销毁。

而 Consul Template 则可以做到定时从注册中心拉取节点列表,并刷新 LB 配置(例如通过 Nginx 的 upstream),这样就相当于完成了服务消费者端的负载均衡。

③存储结构

注册中心存储相关信息一般采取目录化的层次结构,一般分为服务-接口-节点信息。

同时注册中心一般还会进行分组,分组的概念很广,可以是根据机房划分也可以根据环境划分。

节点信息主要会包括节点的地址(ip 和端口号),还有一些节点的其他信息,比如请求失败的重试次数、超时时间的设置等等。

当然很多时候,其实可能会把接口这一层给去掉,因为考虑到接口数量很多的情况下,过多的节点会造成很多问题,比如之前说的网络风暴。

④服务健康监测

服务存活状态监测也是注册中心的一个必要功能。在 Zookeeper 中,每个客户端都会与服务端保持一个长连接,并生成一个 Session。

在 Session 过期周期内,通过客户端定时向服务端发送心跳包来检测链路是否正常,服务端则重置下次 Session 的过期时间。

如果 Session 过期周期内都没有检测到客户端的心跳包,那么就会认为它已经不可用了,将其从节点列表中移除。

⑤状态变更通知

在注册中心具备服务健康检测能力后,还需要将状态变更通知到客户端。在 Zookeeper 中,可以通过监听器 Watcher 的 Process 方法来获取服务变更。

服务的远程通信

在上面,服务消费者已经正确引用了服务,并发现了该服务的地址,那么如何向这个地址发起请求呢?

要解决服务间的远程通信问题,我们需要考虑一些问题:

  • 网络 I/O 的处理

  • 传输协议

  • 序列化方式

①网络 I/O 的处理

简单来说,就是客户端是怎么处理请求?服务端又是怎么处理请求的?

先从客户端来说,我们创建连接的时机可以是从注册中心获取到节点信息的时候,但更多时候,我们会选择在第一次请求发起调用的时候去创建连接。此外,我们往往会为该节点维护一个连接池,进行连接复用。

如果是异步的情况下,我们还需要为每一个请求编号,并维护一个请求池,从而在响应返回时找到对应的请求。当然这并不是必须的,很多框架会帮我们干好这些事情,比如 rxNetty。

从服务端来说,处理请求的方式就可以追溯到 Unix 的 5 种 IO 模型了。我们可以直接使用 Netty、MINA 等网络框架来处理服务端请求,或者如果你有十分的兴趣,可以自己实现一个通信框架。

②传输协议

最常见的当然是直接使用 HTTP 协议,使用双方无需关注和了解协议内容,方便直接,但自然性能上会有所折损。

还有就是目前比较火热的 HTTP2 协议,拥有二进制数据、头部压缩、多路复用等许多优良特性。

但从自身的实践上看,HTTP2 要走到生产仍有一段距离,一个最简单的例子,升级到 HTTP2 后所有的 header names 都变成小写,同时不是 case-insenstive 了,这时候就会有兼容性问题。

当然如果追求更高效与可控的传输,可以定制私有协议并基于 TCP 进行传输。私有协议的定制需要通信双方都了解其特性,设计上还需要注意预留好扩展字段,以及处理好粘包分包等问题。

③序列化方式

在网络传输的前后,往往都需要在发送端进行编码,在服务端进行解码,这样主要是为了在网络传输时候减少数据传输量。

常用的序列化方式包括文本类的,例如 XML/JSON,还有二进制类型的,例如 Protobuf/Thrift 等。

在选择序列化的考虑上:

一是性能,Protobuf 的压缩大小和压缩速度都会比 JSON 快很多,性能也更好。

二是兼容性上,相对来说,JSON 的前后兼容性会强一些,可以用于接口经常变化的场景。

在此还是需要强调,使用每一种序列化都需要了解过其特性,并在接口变更的时候拿捏好边界。

例如 jackson 的 FAIL_ON_UNKNOW_PROPERTIES 属性、kryo 的 CompatibleFieldSerializer、jdk 序列化会严格比较 serialVersionUID 等等。

微服务的稳定性

当一个单体应用改造成多个微服务之后,在请求调用过程中往往会出现更多的问题,通信过程中的每一个环节都可能出现问题。

而在出现问题之后,如果不加处理,还会出现链式反应导致服务雪崩。服务治理功能就是用来处理此类问题的。

我们将从微服务的三个角色:注册中心、服务消费者以及服务提供者一一说起。

注册中心如何保障稳定性

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

总结

对于面试还是要好好准备的,尤其是有些问题还是很容易挖坑的,例如你为什么离开现在的公司(你当然不应该抱怨现在的公司有哪些不好的地方,更多的应该表明自己想要寻找更好的发展机会,自己的一些现实因素,比如对于我而言是现在应聘的公司离自己的家更近,又或者是自己工作到达了迷茫期,想跳出迷茫期等等)

image

Java面试精选题、架构实战文档

整理不易,觉得有帮助的朋友可以帮忙点赞分享支持一下小编~

你的支持,我的动力;祝各位前程似锦,offer不断!
《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》点击传送门即可获取!
mg.cn/images/e5c14a7895254671a72faed303032d36.jpg" alt=“img” style=“zoom: 33%;” />

总结

对于面试还是要好好准备的,尤其是有些问题还是很容易挖坑的,例如你为什么离开现在的公司(你当然不应该抱怨现在的公司有哪些不好的地方,更多的应该表明自己想要寻找更好的发展机会,自己的一些现实因素,比如对于我而言是现在应聘的公司离自己的家更近,又或者是自己工作到达了迷茫期,想跳出迷茫期等等)

[外链图片转存中…(img-ba0t0mtP-1712268015412)]

Java面试精选题、架构实战文档

整理不易,觉得有帮助的朋友可以帮忙点赞分享支持一下小编~

你的支持,我的动力;祝各位前程似锦,offer不断!
《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》点击传送门即可获取!

  • 18
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值