mirror of
https://github.com/zhwei820/learn.lianglianglee.com.git
synced 2025-09-17 08:46:40 +08:00
703 lines
32 KiB
HTML
703 lines
32 KiB
HTML
<!DOCTYPE html>
|
||
|
||
<!-- saved from url=(0046)https://kaiiiz.github.io/hexo-theme-book-demo/ -->
|
||
|
||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||
|
||
<head>
|
||
|
||
<head>
|
||
|
||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||
|
||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1.0, user-scalable=no">
|
||
|
||
<link rel="icon" href="/static/favicon.png">
|
||
|
||
<title>43 服务自省设计方案:新版本新方案.md.html</title>
|
||
|
||
<!-- Spectre.css framework -->
|
||
|
||
<link rel="stylesheet" href="/static/index.css">
|
||
|
||
<!-- theme css & js -->
|
||
|
||
<meta name="generator" content="Hexo 4.2.0">
|
||
|
||
</head>
|
||
<body>
|
||
<div class="book-container">
|
||
|
||
<div class="book-sidebar">
|
||
|
||
<div class="book-brand">
|
||
|
||
<a href="/">
|
||
|
||
<img src="/static/favicon.png">
|
||
|
||
<span>技术文章摘抄</span>
|
||
|
||
</a>
|
||
|
||
</div>
|
||
|
||
<div class="book-menu uncollapsible">
|
||
|
||
<ul class="uncollapsible">
|
||
|
||
<li><a href="/" class="current-tab">首页</a></li>
|
||
|
||
</ul>
|
||
<ul class="uncollapsible">
|
||
|
||
<li><a href="../">上一级</a></li>
|
||
|
||
</ul>
|
||
<ul class="uncollapsible">
|
||
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/Dubbo源码解读与实战-完/00 开篇词 深入掌握 Dubbo 原理与实现,提升你的职场竞争力.md.html">00 开篇词 深入掌握 Dubbo 原理与实现,提升你的职场竞争力.md.html</a>
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/Dubbo源码解读与实战-完/01 Dubbo 源码环境搭建:千里之行,始于足下.md.html">01 Dubbo 源码环境搭建:千里之行,始于足下.md.html</a>
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/Dubbo源码解读与实战-完/02 Dubbo 的配置总线:抓住 URL,就理解了半个 Dubbo.md.html">02 Dubbo 的配置总线:抓住 URL,就理解了半个 Dubbo.md.html</a>
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/Dubbo源码解读与实战-完/03 Dubbo SPI 精析,接口实现两极反转(上).md.html">03 Dubbo SPI 精析,接口实现两极反转(上).md.html</a>
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/Dubbo源码解读与实战-完/04 Dubbo SPI 精析,接口实现两极反转(下).md.html">04 Dubbo SPI 精析,接口实现两极反转(下).md.html</a>
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/Dubbo源码解读与实战-完/05 海量定时任务,一个时间轮搞定.md.html">05 海量定时任务,一个时间轮搞定.md.html</a>
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/Dubbo源码解读与实战-完/06 ZooKeeper 与 Curator,求你别用 ZkClient 了(上).md.html">06 ZooKeeper 与 Curator,求你别用 ZkClient 了(上).md.html</a>
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/Dubbo源码解读与实战-完/07 ZooKeeper 与 Curator,求你别用 ZkClient 了(下).md.html">07 ZooKeeper 与 Curator,求你别用 ZkClient 了(下).md.html</a>
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/Dubbo源码解读与实战-完/08 代理模式与常见实现.md.html">08 代理模式与常见实现.md.html</a>
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/Dubbo源码解读与实战-完/09 Netty 入门,用它做网络编程都说好(上).md.html">09 Netty 入门,用它做网络编程都说好(上).md.html</a>
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/Dubbo源码解读与实战-完/10 Netty 入门,用它做网络编程都说好(下).md.html">10 Netty 入门,用它做网络编程都说好(下).md.html</a>
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/Dubbo源码解读与实战-完/11 简易版 RPC 框架实现(上).md.html">11 简易版 RPC 框架实现(上).md.html</a>
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/Dubbo源码解读与实战-完/12 简易版 RPC 框架实现(下).md.html">12 简易版 RPC 框架实现(下).md.html</a>
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/Dubbo源码解读与实战-完/13 本地缓存:降低 ZooKeeper 压力的一个常用手段.md.html">13 本地缓存:降低 ZooKeeper 压力的一个常用手段.md.html</a>
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/Dubbo源码解读与实战-完/14 重试机制是网络操作的基本保证.md.html">14 重试机制是网络操作的基本保证.md.html</a>
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/Dubbo源码解读与实战-完/15 ZooKeeper 注册中心实现,官方推荐注册中心实践.md.html">15 ZooKeeper 注册中心实现,官方推荐注册中心实践.md.html</a>
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/Dubbo源码解读与实战-完/16 Dubbo Serialize 层:多种序列化算法,总有一款适合你.md.html">16 Dubbo Serialize 层:多种序列化算法,总有一款适合你.md.html</a>
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/Dubbo源码解读与实战-完/17 Dubbo Remoting 层核心接口分析:这居然是一套兼容所有 NIO 框架的设计?.md.html">17 Dubbo Remoting 层核心接口分析:这居然是一套兼容所有 NIO 框架的设计?.md.html</a>
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/Dubbo源码解读与实战-完/18 Buffer 缓冲区:我们不生产数据,我们只是数据的搬运工.md.html">18 Buffer 缓冲区:我们不生产数据,我们只是数据的搬运工.md.html</a>
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/Dubbo源码解读与实战-完/19 Transporter 层核心实现:编解码与线程模型一文打尽(上).md.html">19 Transporter 层核心实现:编解码与线程模型一文打尽(上).md.html</a>
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/Dubbo源码解读与实战-完/20 Transporter 层核心实现:编解码与线程模型一文打尽(下).md.html">20 Transporter 层核心实现:编解码与线程模型一文打尽(下).md.html</a>
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/Dubbo源码解读与实战-完/21 Exchange 层剖析:彻底搞懂 Request-Response 模型(上).md.html">21 Exchange 层剖析:彻底搞懂 Request-Response 模型(上).md.html</a>
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/Dubbo源码解读与实战-完/22 Exchange 层剖析:彻底搞懂 Request-Response 模型(下).md.html">22 Exchange 层剖析:彻底搞懂 Request-Response 模型(下).md.html</a>
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/Dubbo源码解读与实战-完/23 核心接口介绍,RPC 层骨架梳理.md.html">23 核心接口介绍,RPC 层骨架梳理.md.html</a>
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/Dubbo源码解读与实战-完/24 从 Protocol 起手,看服务暴露和服务引用的全流程(上).md.html">24 从 Protocol 起手,看服务暴露和服务引用的全流程(上).md.html</a>
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/Dubbo源码解读与实战-完/25 从 Protocol 起手,看服务暴露和服务引用的全流程(下).md.html">25 从 Protocol 起手,看服务暴露和服务引用的全流程(下).md.html</a>
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/Dubbo源码解读与实战-完/26 加餐:直击 Dubbo “心脏”,带你一起探秘 Invoker(上).md.html">26 加餐:直击 Dubbo “心脏”,带你一起探秘 Invoker(上).md.html</a>
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/Dubbo源码解读与实战-完/27 加餐:直击 Dubbo “心脏”,带你一起探秘 Invoker(下).md.html">27 加餐:直击 Dubbo “心脏”,带你一起探秘 Invoker(下).md.html</a>
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/Dubbo源码解读与实战-完/28 复杂问题简单化,代理帮你隐藏了多少底层细节?.md.html">28 复杂问题简单化,代理帮你隐藏了多少底层细节?.md.html</a>
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/Dubbo源码解读与实战-完/29 加餐:HTTP 协议 + JSON-RPC,Dubbo 跨语言就是如此简单.md.html">29 加餐:HTTP 协议 + JSON-RPC,Dubbo 跨语言就是如此简单.md.html</a>
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/Dubbo源码解读与实战-完/30 Filter 接口,扩展 Dubbo 框架的常用手段指北.md.html">30 Filter 接口,扩展 Dubbo 框架的常用手段指北.md.html</a>
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/Dubbo源码解读与实战-完/31 加餐:深潜 Directory 实现,探秘服务目录玄机.md.html">31 加餐:深潜 Directory 实现,探秘服务目录玄机.md.html</a>
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/Dubbo源码解读与实战-完/32 路由机制:请求到底怎么走,它说了算(上).md.html">32 路由机制:请求到底怎么走,它说了算(上).md.html</a>
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/Dubbo源码解读与实战-完/33 路由机制:请求到底怎么走,它说了算(下).md.html">33 路由机制:请求到底怎么走,它说了算(下).md.html</a>
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/Dubbo源码解读与实战-完/34 加餐:初探 Dubbo 动态配置的那些事儿.md.html">34 加餐:初探 Dubbo 动态配置的那些事儿.md.html</a>
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/Dubbo源码解读与实战-完/35 负载均衡:公平公正物尽其用的负载均衡策略,这里都有(上).md.html">35 负载均衡:公平公正物尽其用的负载均衡策略,这里都有(上).md.html</a>
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/Dubbo源码解读与实战-完/36 负载均衡:公平公正物尽其用的负载均衡策略,这里都有(下).md.html">36 负载均衡:公平公正物尽其用的负载均衡策略,这里都有(下).md.html</a>
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/Dubbo源码解读与实战-完/37 集群容错:一个好汉三个帮(上).md.html">37 集群容错:一个好汉三个帮(上).md.html</a>
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/Dubbo源码解读与实战-完/38 集群容错:一个好汉三个帮(下).md.html">38 集群容错:一个好汉三个帮(下).md.html</a>
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/Dubbo源码解读与实战-完/39 加餐:多个返回值不用怕,Merger 合并器来帮忙.md.html">39 加餐:多个返回值不用怕,Merger 合并器来帮忙.md.html</a>
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/Dubbo源码解读与实战-完/40 加餐:模拟远程调用,Mock 机制帮你搞定.md.html">40 加餐:模拟远程调用,Mock 机制帮你搞定.md.html</a>
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/Dubbo源码解读与实战-完/41 加餐:一键通关服务发布全流程.md.html">41 加餐:一键通关服务发布全流程.md.html</a>
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/Dubbo源码解读与实战-完/42 加餐:服务引用流程全解析.md.html">42 加餐:服务引用流程全解析.md.html</a>
|
||
</li>
|
||
|
||
<li>
|
||
<a class="current-tab" href="/专栏/Dubbo源码解读与实战-完/43 服务自省设计方案:新版本新方案.md.html">43 服务自省设计方案:新版本新方案.md.html</a>
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/Dubbo源码解读与实战-完/44 元数据方案深度剖析,如何避免注册中心数据量膨胀?.md.html">44 元数据方案深度剖析,如何避免注册中心数据量膨胀?.md.html</a>
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/Dubbo源码解读与实战-完/45 加餐:深入服务自省方案中的服务发布订阅(上).md.html">45 加餐:深入服务自省方案中的服务发布订阅(上).md.html</a>
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/Dubbo源码解读与实战-完/46 加餐:深入服务自省方案中的服务发布订阅(下).md.html">46 加餐:深入服务自省方案中的服务发布订阅(下).md.html</a>
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/Dubbo源码解读与实战-完/47 配置中心设计与实现:集中化配置 and 本地化配置,我都要(上).md.html">47 配置中心设计与实现:集中化配置 and 本地化配置,我都要(上).md.html</a>
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/Dubbo源码解读与实战-完/48 配置中心设计与实现:集中化配置 and 本地化配置,我都要(下).md.html">48 配置中心设计与实现:集中化配置 and 本地化配置,我都要(下).md.html</a>
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/Dubbo源码解读与实战-完/49 结束语 认真学习,缩小差距.md.html">49 结束语 认真学习,缩小差距.md.html</a>
|
||
</li>
|
||
|
||
</ul>
|
||
</div>
|
||
|
||
</div>
|
||
<div class="sidebar-toggle" onclick="sidebar_toggle()" onmouseover="add_inner()" onmouseleave="remove_inner()">
|
||
|
||
<div class="sidebar-toggle-inner"></div>
|
||
|
||
</div>
|
||
<script>
|
||
|
||
function add_inner() {
|
||
|
||
let inner = document.querySelector('.sidebar-toggle-inner')
|
||
|
||
inner.classList.add('show')
|
||
|
||
}
|
||
function remove_inner() {
|
||
|
||
let inner = document.querySelector('.sidebar-toggle-inner')
|
||
|
||
inner.classList.remove('show')
|
||
|
||
}
|
||
function sidebar_toggle() {
|
||
|
||
let sidebar_toggle = document.querySelector('.sidebar-toggle')
|
||
|
||
let sidebar = document.querySelector('.book-sidebar')
|
||
|
||
let content = document.querySelector('.off-canvas-content')
|
||
|
||
if (sidebar_toggle.classList.contains('extend')) { // show
|
||
|
||
sidebar_toggle.classList.remove('extend')
|
||
|
||
sidebar.classList.remove('hide')
|
||
|
||
content.classList.remove('extend')
|
||
|
||
} else { // hide
|
||
|
||
sidebar_toggle.classList.add('extend')
|
||
|
||
sidebar.classList.add('hide')
|
||
|
||
content.classList.add('extend')
|
||
|
||
}
|
||
|
||
}
|
||
|
||
|
||
function open_sidebar() {
|
||
|
||
let sidebar = document.querySelector('.book-sidebar')
|
||
|
||
let overlay = document.querySelector('.off-canvas-overlay')
|
||
|
||
sidebar.classList.add('show')
|
||
|
||
overlay.classList.add('show')
|
||
|
||
}
|
||
|
||
function hide_canvas() {
|
||
|
||
let sidebar = document.querySelector('.book-sidebar')
|
||
|
||
let overlay = document.querySelector('.off-canvas-overlay')
|
||
|
||
sidebar.classList.remove('show')
|
||
|
||
overlay.classList.remove('show')
|
||
|
||
}
|
||
</script>
|
||
<div class="off-canvas-content">
|
||
|
||
<div class="columns">
|
||
|
||
<div class="column col-12 col-lg-12">
|
||
|
||
<div class="book-navbar">
|
||
|
||
<!-- For Responsive Layout -->
|
||
|
||
<header class="navbar">
|
||
|
||
<section class="navbar-section">
|
||
|
||
<a onclick="open_sidebar()">
|
||
|
||
<i class="icon icon-menu"></i>
|
||
|
||
</a>
|
||
|
||
</section>
|
||
|
||
</header>
|
||
|
||
</div>
|
||
|
||
<div class="book-content" style="max-width: 960px; margin: 0 auto;
|
||
|
||
overflow-x: auto;
|
||
|
||
overflow-y: hidden;">
|
||
|
||
<div class="book-post">
|
||
|
||
<p id="tip" align="center"></p>
|
||
|
||
<div><h1>43 服务自省设计方案:新版本新方案</h1>
|
||
|
||
<p>随着微服务架构的不断发展和普及,RPC 框架成为微服务架构中不可或缺的重要角色,Dubbo 作为 Java 生态中一款成熟的 RPC 框架也在随着技术的更新换代不断发展壮大。当然,传统的 Dubbo 架构也面临着新思想、新生态和新技术带来的挑战。</p>
|
||
|
||
<p>在微服务架构中,服务是基本单位,而 Dubbo 架构中服务的基本单位是 Java 接口,这种架构上的差别就会带来一系列挑战。<strong>从 2.7.5 版本开始,Dubbo 引入了服务自省架构,来应对微服务架构带来的挑战</strong>。具体都有哪些挑战呢?下面我们就来详细说明一下。</p>
|
||
|
||
<h3>注册中心面临的挑战</h3>
|
||
|
||
<p>在开始介绍注册中心面临的挑战之前,我们先来回顾一下前面课时介绍过的 Dubbo 传统架构以及这个架构中最核心的组件:</p>
|
||
|
||
<p><img src="assets/Cip5yF_hcH2AVI2PAAB7_C-aKWA247.png" alt="Drawing 0.png" /></p>
|
||
|
||
<p>Dubbo 核心架构图</p>
|
||
|
||
<p>结合上面这张架构图,我们可以一起回顾一下这些核心组件的功能。</p>
|
||
|
||
<ul>
|
||
|
||
<li><strong>Registry:注册中心。</strong> 负责服务地址的注册与查找,服务的 Provider 和 Consumer 只在启动时与注册中心交互。注册中心通过长连接感知 Provider 的存在,在 Provider 出现宕机的时候,注册中心会立即推送相关事件通知 Consumer。</li>
|
||
|
||
<li><strong>Provider:服务提供者。</strong> 在它启动的时候,会向 Registry 进行注册操作,将自己服务的地址和相关配置信息封装成 URL 添加到 ZooKeeper 中。</li>
|
||
|
||
<li><strong>Consumer:服务消费者。</strong> 在它启动的时候,会向 Registry 进行订阅操作。订阅操作会从 ZooKeeper 中获取 Provider 注册的 URL,并在 ZooKeeper 中添加相应的监听器。获取到 Provider URL 之后,Consumer 会根据 URL 中相应的参数选择 LoadBalance、Router、Cluster 实现,创建相应的 Invoker 对象,然后封装服务接口的代理对象,返回给上层业务。上层业务调用该代理对象的方法,就会执行远程调用。</li>
|
||
|
||
<li><strong>Monitor:监控中心。</strong> 用于统计服务的调用次数和调用时间。Provider 和 Consumer 在运行过程中,会在内存中统计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心。监控中心在上面的架构图中并不是必要角色,监控中心宕机不会影响 Provider、Consumer 以及 Registry 的功能,只会丢失监控数据而已。</li>
|
||
|
||
</ul>
|
||
|
||
<p>通过前面对整个 Dubbo 实现体系的介绍,我们知道<strong>URL 是贯穿整个 Dubbo 注册与发现的核心</strong>。Provider URL 注册到 ZooKeeper 上的大致格式如下:</p>
|
||
|
||
<pre><code>dubbo://192.168.0.100:20880/org.apache.dubbo.demo.DemoService?anyhost=true&application=demo-provider&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&group=groupA&interface=org.apache.dubbo.demo.DemoService&metadata-type=remote&methods=sayHello,sayHelloAsync&pid=59975&release=&side=provider&timestamp=1601390276192
|
||
|
||
</code></pre>
|
||
|
||
<p>其中包括 Provider 的 IP、Port、服务接口的完整名称、Dubbo 协议的版本号、分组信息、进程 ID 等。</p>
|
||
|
||
<p>我们常用的注册中心,比如,ZooKeeper、Nacos 或 etcd 等,都是中心化的基础设施。注册中心基本都是以内存作为核心存储,其内存使用量与服务接口的数量以及 Provider 节点的个数是成正比的,一个 Dubbo Provider 节点可以注册多个服务接口。随着业务发展,服务接口的数量会越来越多,为了支撑整个系统的流量增长,部署的 Dubbo Provider 节点和 Dubbo Consumer 节点也会不断增加,这就<strong>导致注册中心的内存压力越来越大</strong>。</p>
|
||
|
||
<p>在生产环境中为了避免单点故障,在搭建注册中心的时候,都会使用高可用方案。这些高可用方案的本质就是底层的一致性协议,例如,ZooKeeper 使用的是 Zab 协议,etcd 使用的是 Raft 协议。当注册数据频繁发生变化的时候,注册中心集群的内部节点<strong>用于同步数据的网络开销也会增大</strong>。</p>
|
||
|
||
<p>从注册中心的外部看,Dubbo Provider 和 Dubbo Consumer 都可以算作注册中心的客户端,都会与注册中心集群之间维护长连接,这也会造成一部分<strong>网络开销和资源消耗</strong>。</p>
|
||
|
||
<p>在使用类似 ZooKeeper 的注册中心实现方案时,注册中心会主动将注册数据的变化推送到客户端。假设一个 Dubbo Consumer 订阅了 N 个服务接口,每个服务接口由 M 个 Provider 节点组成的集群提供服务,在 Provider 节点进行机器迁移的时候,就会涉及 M * N 个 URL 的更新,这些变更事件都会通知到每个 Dubbo Consumer 节点,这就造成了<strong>注册中心在处理通知方面的压力</strong>。</p>
|
||
|
||
<p>总之,在超大规模的微服务落地实践中,从内存、网络开销、通知等多个角度看,注册中心以及整个 Dubbo 传统架构都受到了不少的挑战和压力。</p>
|
||
|
||
<h3>Dubbo 的改进方案</h3>
|
||
|
||
<p>Dubbo 从 2.7.0 版本开始增加了<strong>简化 URL</strong>的特性,从 URL 中抽出的数据会被存放至元数据中心。但是这次优化只是缩短了 URL 的长度,从内存使用量以及降低通知频繁度的角度降低了注册中心的压力,并没有减少注册中心 URL 的数量,所以注册中心所承受的压力还是比较明显的。</p>
|
||
|
||
<p>Dubbo 2.7.5 版本引入了<strong>服务自省架构</strong>,进一步降低了注册中心的压力。在此次优化中,Dubbo 修改成应用为粒度的服务注册与发现模型,最大化地减少了 Dubbo 服务元信息注册数量,其核心流程如下图所示:</p>
|
||
|
||
<p><img src="assets/CgqCHl_hcJqACV_gAAEpu4IHuz4068.png" alt="Lark20201222-120323.png" /></p>
|
||
|
||
<p>服务自省架构图</p>
|
||
|
||
<p>上图展示了引入服务自省之后的 Dubbo 服务注册与发现的核心流程,Dubbo 会按照顺序执行这些操作(当其中一个操作失败时,后续操作不会执行)。</p>
|
||
|
||
<p>我们首先来看 Provider 侧的执行流程:</p>
|
||
|
||
<p>1.发布所有业务接口中定义的服务接口,具体过程与[第 41 课时]中介绍的发布流程相同;</p>
|
||
|
||
<p>2.发布 MetadataService 接口,该接口的发布由 Dubbo 框架自主完成;</p>
|
||
|
||
<p>3.将 Service Instance 注册到注册中心;</p>
|
||
|
||
<p>4.建立所有的 Service ID 与 Service Name 的映射,并同步到配置中心。</p>
|
||
|
||
<p>接下来,我们再来看Consumer 侧的执行流程:</p>
|
||
|
||
<p>5.注册当前 Consumer 的 Service Instance,Dubbo 允许 Consumer 不进行服务注册,所以这一步操作是可选的;</p>
|
||
|
||
<p>6.从配置中心获取 Service ID 与 Service Name 的映射关系;</p>
|
||
|
||
<p>7.根据 Service ID 从注册中心获取 Service Instance 集合;</p>
|
||
|
||
<p>8.随机选择一个 Service Instance,从中获取 MetadataService 的元数据,这里会发起 MetadataService 的调用,获取该 Service Instance 所暴露的业务接口的 URL 列表,从该 URL 列表中可以过滤出当前订阅的 Service 的 URL;</p>
|
||
|
||
<p>9.根据步骤 8 中获取的业务接口 URL 发起远程调用。</p>
|
||
|
||
<p>至于上图中涉及的一些新概念,为方便你理解,这里我们对它们的具体实现进行一个简单的介绍。</p>
|
||
|
||
<ul>
|
||
|
||
<li>Service Name:服务名称,例如,在一个电商系统中,有用户服务、商品服务、库存服务等。</li>
|
||
|
||
<li>Service Instance:服务实例,表示单个 Dubbo 应用进程,<strong>多个 Service Instance 构成一个服务集群,拥有相同的 Service Name</strong>。</li>
|
||
|
||
<li>Service ID:唯一标识一个 Dubbo 服务,由 <code>${protocol}:${interface}:${version}:${group}</code> 四部分构成。</li>
|
||
|
||
</ul>
|
||
|
||
<p>在有的场景中,我们会在线上部署两组不同配置的服务节点,来验证某些配置是否生效。例如,共有 100 个服务节点,平均分成 A、B 两组,A 组服务节点超时时间(即 timeout)设置为 3000 ms,B 组的超时时间(即 timeout)设置为 2000 ms,这样的话该服务就有了两组不同的元数据。</p>
|
||
|
||
<p>按照前面介绍的优化方案,在订阅服务的时候,会得到 100 个 ServiceInstance,因为每个 ServiceInstance 发布的服务元数据都有可能不一样,所以我们需要调用每个 ServiceInstance 的 MetadataService 服务获取元数据。</p>
|
||
|
||
<p>为了减少 MetadataService 服务的调用次数,Dubbo 提出了<strong>服务修订版本的优化方案</strong>,其核心思想是:将每个 ServiceInstance 发布的服务 URL 计算一个 hash 值(也就是 revision 值),并随 ServiceInstance 一起发布到注册中心;在 Consumer 端进行订阅的时候,对于 revision 值相同的 ServiceInstance,不再调用 MetadataService 服务,直接共用一份 URL 即可。下图展示了 Dubbo 服务修订的核心逻辑:</p>
|
||
|
||
<p><img src="assets/Cip5yF_hcMyALC7UAAEPa7NIifA395.png" alt="Lark20201222-120318.png" /></p>
|
||
|
||
<p>引入 Dubbo 服务修订的 Consumer 端交互图</p>
|
||
|
||
<p>通过该流程图,我们可以看到 Dubbo Consumer 端实现服务修订的流程如下。</p>
|
||
|
||
<ol>
|
||
|
||
<li>Consumer 端通过服务发现 API 从注册中心获取 Provider 端的 ServiceInstance 列表。</li>
|
||
|
||
<li>注册中心返回 100 台服务实例,其中 revision 为 1 的 ServiceInstance 编号是 0~49,revision 为 2 的 ServiceInstance 编号是 50~99。</li>
|
||
|
||
<li>Consumer 端在这 100 台服务实例中随机选择一台,例如,选择到编号为 68 的 ServiceInstance。</li>
|
||
|
||
<li>Consumer 端调用 ServiceInstance 68 暴露的 MetadataService 服务,获得其发布的 Dubbo 服务 URL 列表,并在本地内存中建立 revision 为 2 的服务 URL 列表缓存。</li>
|
||
|
||
<li>Consumer 端再从剩余的 99 台服务实例中随机选择一台,例如,选中了 ServiceInstance 30,发现其 revision 值为 1,且本地缓存中没有 revision 为 1 的服务 URL 列表缓存。此时,Consumer 会如步骤 4 一样发起 MetadataService 调用,从 ServiceInstance 30 获取服务 URL 列表,并更新缓存。</li>
|
||
|
||
</ol>
|
||
|
||
<p>由于此时的本地缓存已经覆盖了当前场景中全部的 revision 值,后续再次随机选择的 ServiceInstance 的 revision 不是 1 就是 2,都会落到本地缓存中,不会再次发起 MetadataService 服务调用。后续其他 ServiceInstance 的处理都会复用本地缓存的这两个 URL 列表,并根据 ServiceInstance 替换相应的参数(例如,host、port 等),这样即可得到 ServiceInstance 发布的完整的服务 URL 列表。</p>
|
||
|
||
<p>一般情况下,revision 的数量不会很多,那么 Consumer 端发起的 MetadataService 服务调用次数也是有限的,不会随着 ServiceInstance 的扩容而增长。<strong>这样就避免了同一服务的不同版本导致的元数据膨胀</strong>。</p>
|
||
|
||
<h3>总结</h3>
|
||
|
||
<p>在本课时,我们重点介绍了 Dubbo 的服务自省架构的相关内容。</p>
|
||
|
||
<p>首先,我们一起复习了 Dubbo 的传统架构以及传统架构中基础组建的核心功能和交互流程。然后分析了 Dubbo 传统架构在超大规模微服务落地实践中面临的各项挑战和压力。最后,我们重点讲解了 Dubbo 2.7.5 版本之后引入的服务自省方案,服务自省方案可以很好地应对 Dubbo 面临的诸多挑战,并缓解基于 Dubbo 实现的、超大规模的微服务系统压力。在此基础上,我们还特别介绍了 Dubbo 服务修订方案是如何避免元数据膨胀的具体原理。</p>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div>
|
||
|
||
<div style="float: left">
|
||
|
||
<a href="/专栏/Dubbo源码解读与实战-完/42 加餐:服务引用流程全解析.md.html">上一页</a>
|
||
|
||
</div>
|
||
|
||
<div style="float: right">
|
||
|
||
<a href="/专栏/Dubbo源码解读与实战-完/44 元数据方案深度剖析,如何避免注册中心数据量膨胀?.md.html">下一页</a>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
<a class="off-canvas-overlay" onclick="hide_canvas()"></a>
|
||
|
||
</div>
|
||
|
||
<script defer src="https://static.cloudflareinsights.com/beacon.min.js/v652eace1692a40cfa3763df669d7439c1639079717194" integrity="sha512-Gi7xpJR8tSkrpF7aordPZQlW2DLtzUlZcumS8dMQjwDHEnw9I7ZLyiOj/6tZStRBGtGgN6ceN6cMH8z7etPGlw==" data-cf-beacon='{"rayId":"70996f7b8ff13d60","version":"2021.12.0","r":1,"token":"1f5d475227ce4f0089a7cff1ab17c0f5","si":100}' crossorigin="anonymous"></script>
|
||
|
||
</body>
|
||
|
||
<!-- Global site tag (gtag.js) - Google Analytics -->
|
||
|
||
<script async src="https://www.googletagmanager.com/gtag/js?id=G-NPSEEVD756"></script>
|
||
|
||
<script>
|
||
|
||
window.dataLayer = window.dataLayer || [];
|
||
function gtag() {
|
||
|
||
dataLayer.push(arguments);
|
||
|
||
}
|
||
gtag('js', new Date());
|
||
|
||
gtag('config', 'G-NPSEEVD756');
|
||
|
||
var path = window.location.pathname
|
||
|
||
var cookie = getCookie("lastPath");
|
||
|
||
console.log(path)
|
||
|
||
if (path.replace("/", "") === "") {
|
||
|
||
if (cookie.replace("/", "") !== "") {
|
||
|
||
console.log(cookie)
|
||
|
||
document.getElementById("tip").innerHTML = "<a href='" + cookie + "'>跳转到上次进度</a>"
|
||
|
||
}
|
||
|
||
} else {
|
||
|
||
setCookie("lastPath", path)
|
||
|
||
}
|
||
function setCookie(cname, cvalue) {
|
||
|
||
var d = new Date();
|
||
|
||
d.setTime(d.getTime() + (180 * 24 * 60 * 60 * 1000));
|
||
|
||
var expires = "expires=" + d.toGMTString();
|
||
|
||
document.cookie = cname + "=" + cvalue + "; " + expires + ";path = /";
|
||
|
||
}
|
||
function getCookie(cname) {
|
||
|
||
var name = cname + "=";
|
||
|
||
var ca = document.cookie.split(';');
|
||
|
||
for (var i = 0; i < ca.length; i++) {
|
||
|
||
var c = ca[i].trim();
|
||
|
||
if (c.indexOf(name) === 0) return c.substring(name.length, c.length);
|
||
|
||
}
|
||
|
||
return "";
|
||
|
||
}
|
||
</script>
|
||
</html>
|
||
|