前言

在大型应用中,应用程序自己的调用不可避免。大多数情况下我们可以通过HTTP REST API进行通,也可以通过gRPC、Thrift、消息队列等其他形式。

比如,当你需要某网站的数据时,可以调用它API来获取,这期间,通常需要先获得URL地址和访问认证权限。

在传统应用中,我们大多数情况下使用静态配置文件来配置API对应的URL地址。在第三方服务的场景下,这很实用。因为三方服务提供商不可能频繁的更换URL地址。

但对基于微服务架构的应用来说,静态配置文件就失效了。

关于微服务

Martin Fowler是这样对微服务进行定义的:“微服务架构设计是将一个单体应用拆分成一组微服务,每个服务运行自己的进程,并通过轻量级协议进行相互通信,比如基于Http的API。”

通过上面的定义我们得知,微服务架构通常有一组服务组成,每个服务之间通过基于Http协议进行通信。假设你的系统中有三个服务:TweetServie、TimelineService和UseService。

当用户发一条消息时,通过基础设施先请求TweetService,将数据存储到数据库,然后调用TimelineService,TimelineService查找到要发送的用户,然后调用UserService进行消息的传播。

在上述实例中TweetService必须知道TimelineService服务的地址,而TimelineService服务必须知道UserService的地址。假设上述过程通过HTTP进行通信,对应的流程图如下:

服务发现

在微服务系统中,上述的每一个服务通常都是有多个实例构成,而且每个服务的实例都会动态的变化。

什么是服务发现?

在分布式应用中,服务发现是一个基础概念。服务通过一定机制让自身可以被其他服务发现,从而其他服务可以通过服务名称来调用它,而不是通过物理位置或服务地址。主要用来解决不同业务之间相互通信的问题。

服务发现的几种方法

目前,通常会采用三种形式的服务发现方式。下面就来看一下具体的实现。

方式一:服务端服务发现

在服务器端服务发现中,每个服务都位于负载均衡器的后面,并且该服务的客户端使用负载均衡器的URI来调用该服务。这是最简单的服务发现机制。

在上述实例中,将TweetService放在负载均衡器后面,TweetService请求对应的负载均衡器即可。负载平衡器负责维护注册表,并存储TweetService不同实例的信息。

服务发现

当请求到达负载均衡器,它会查询注册表获得可以处理该请求的实例列表,并通过路由机制请求到可用的服务实例。

服务实例在启动时将自己注册到注册表,关闭时将自己注销。AWS ELB(Elastic Load Balancer)是一个很好的示例。AWS ELB提供健康检查和自动注册/注销的功能。

这是最简单的服务发现实现。如果你的应用程序使用的是AWS ELB,你可以使用内置的服务通信机制。这种机制也常见于Kubernetes和Marathon中,你可以免费使用它们提供的这些服务。

方式二:客户端服务发现

这种方式中,发现服务实例的责任发生了反转。客户端负责来决定哪些实例可用以及请求的负载均衡。客户端通过调用服务注册表来获取实例列表,而服务注册表是需要单独管理的一个组件。服务注册表的作用类似数据库,客户端可以用来检索可用的服务实例。

当客户端获得服务实例集合之后,通过负载均衡算法获取一个可用的实例,然后发送请求。

服务发现

这是大多数客户端服务发现工具的工作方式:

  • 服务实例启动时需将自己注册到服务注册表中,通常通过HTTP POST或类似的形式发送请求,请求信息包括名称和地址。当服务停止时,服务实例发送注销请求到注册表进行注销操作。
  • 服务注册表收到实例的请求信息,并进行数据存储。服务注册表以key-value的形式存储数据,这样通过服务的名称便可以获得服务的实例信息。
  • 服务实例通常每个30秒发送一次心跳请求到注册表来刷新注册。如果服务注册表在指定时间内未收到任何请求,则会移除该实例。
  • 服务客户端通过服务注册表获取服务实例的详情,客户端通常使用一些进程间通信(ICP)类库来处理对服务实例的请求。
  • 客户端类库从服务注册表获取实例列表并通过负载均衡来请求服务实例。

对于上述方式,Netflix的Eureka和Ribbon框架就是一个很好的示例。Eureka是一个注册表框架,提供基于REST API形式的服务注册和查询功能。Ribbon是一个进程间通信(ICP)类库,内置了负载均衡的功能。

这种方式的优势是客户端可以明智的选择负载均衡策略,同时,摆脱了部署平台和云服务商的束缚。

但同时也有三个缺点:第一,需要对每种编程语言和框架的的客户进行功能实现;第二,需要自己维护服务注册表组件,并且需要具有高可用性;第三,客户端需需要通过本地逻辑来实现服务发现、重试等功能,客户端会变得比较臃肿。

方式三:基于服务网格

这是最新的服务发现机制,服务网格是部署平台(如Kubernetes)上的抽象层,用于构建基于微服务架构的应用程序。它们提供了一种可以处理服务通信的结构,在Kubernetes等部署平台上提供应用程序级别的服务,例如服务发现。

服务网格不仅仅是服务发现,还致力于提供如下功能:

  • 服务间的通信,即服务发现;
  • 高级路由;
  • 自动重试;
  • 断路;
  • 延迟负载均衡;
  • 标准指标和日志记录;
  • 中心控制版;
  • 分布式追踪;
  • 限流;
  • 权限验证;

考虑到客户端发现方法的弊端,人们一直在思考更好的方法,通过一种编程语言以一种独立的方式来处理这些跨领域的问题。

服务网格是一种富中间件的方式,将服务发现和重试逻辑放入网络中间件中。这有助于客户端保持功能与运行时语言的独立性。

服务网格是在诸如Kubernetes之类的调度程序上运行的微服务智能反向代理,通常有三种流行的解决方案:Linkerd、Envoy、Istio。

像Istio这样的网格服务是通过一个代理实现的。因此,除了服务之外,节点/容器上还安装了代理。这也称为边车模式。代理拦截网络请求,并将请求转发到适当的服务。如下所示。

服务发现

如果你使用的是Netflix OSS,则必须使用下图中定义的多个组件。

服务发现

在使用服务网格时,你只需要将代理与服务一起安装。

毫无疑问,服务网格是将是我们构建微服务应用程序的基础。唯一缺点是,需要耦合到Kubernetes等平台,这需要更多的投入。



分布式应用中服务发现(Service Discovery)机制插图5

关注公众号:程序新视界,一个让你软实力、硬技术同步提升的平台

除非注明,否则均为程序新视界原创文章,转载必须以链接形式标明本文链接

本文链接:http://www.choupangxia.com/2021/05/05/service-discovery-2/