learn.lianglianglee.com/文章/Spring Boot 2.x 结合 k8s 实现分布式微服务架构.md.html
2022-05-11 19:04:14 +08:00

2404 lines
64 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!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>Spring Boot 2.x 结合 k8s 实现分布式微服务架构.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="/文章/AQS 万字图文全面解析.md.html">AQS 万字图文全面解析.md.html</a>
</li>
<li>
<a href="/文章/Docker 镜像构建原理及源码分析.md.html">Docker 镜像构建原理及源码分析.md.html</a>
</li>
<li>
<a href="/文章/ElasticSearch 小白从入门到精通.md.html">ElasticSearch 小白从入门到精通.md.html</a>
</li>
<li>
<a href="/文章/JVM CPU Profiler技术原理及源码深度解析.md.html">JVM CPU Profiler技术原理及源码深度解析.md.html</a>
</li>
<li>
<a href="/文章/JVM 垃圾收集器.md.html">JVM 垃圾收集器.md.html</a>
</li>
<li>
<a href="/文章/JVM 面试的 30 个知识点.md.html">JVM 面试的 30 个知识点.md.html</a>
</li>
<li>
<a href="/文章/Java IO 体系、线程模型大总结.md.html">Java IO 体系、线程模型大总结.md.html</a>
</li>
<li>
<a href="/文章/Java NIO浅析.md.html">Java NIO浅析.md.html</a>
</li>
<li>
<a href="/文章/Java 面试题集锦(网络篇).md.html">Java 面试题集锦(网络篇).md.html</a>
</li>
<li>
<a href="/文章/Java-直接内存 DirectMemory 详解.md.html">Java-直接内存 DirectMemory 详解.md.html</a>
</li>
<li>
<a href="/文章/Java中9种常见的CMS GC问题分析与解决.md.html">Java中9种常见的CMS GC问题分析与解决.md.html</a>
</li>
<li>
<a href="/文章/Java中9种常见的CMS GC问题分析与解决.md.html">Java中9种常见的CMS GC问题分析与解决.md.html</a>
</li>
<li>
<a href="/文章/Java中的SPI.md.html">Java中的SPI.md.html</a>
</li>
<li>
<a href="/文章/Java中的ThreadLocal.md.html">Java中的ThreadLocal.md.html</a>
</li>
<li>
<a href="/文章/Java线程池实现原理及其在美团业务中的实践.md.html">Java线程池实现原理及其在美团业务中的实践.md.html</a>
</li>
<li>
<a href="/文章/Java魔法类Unsafe应用解析.md.html">Java魔法类Unsafe应用解析.md.html</a>
</li>
<li>
<a href="/文章/Kafka 源码阅读笔记.md.html">Kafka 源码阅读笔记.md.html</a>
</li>
<li>
<a href="/文章/Kafka、ActiveMQ、RabbitMQ、RocketMQ 区别以及高可用原理.md.html">Kafka、ActiveMQ、RabbitMQ、RocketMQ 区别以及高可用原理.md.html</a>
</li>
<li>
<a href="/文章/MySQL · 引擎特性 · InnoDB Buffer Pool.md.html">MySQL · 引擎特性 · InnoDB Buffer Pool.md.html</a>
</li>
<li>
<a href="/文章/MySQL · 引擎特性 · InnoDB IO子系统.md.html">MySQL · 引擎特性 · InnoDB IO子系统.md.html</a>
</li>
<li>
<a href="/文章/MySQL · 引擎特性 · InnoDB 事务系统.md.html">MySQL · 引擎特性 · InnoDB 事务系统.md.html</a>
</li>
<li>
<a href="/文章/MySQL · 引擎特性 · InnoDB 同步机制.md.html">MySQL · 引擎特性 · InnoDB 同步机制.md.html</a>
</li>
<li>
<a href="/文章/MySQL · 引擎特性 · InnoDB 数据页解析.md.html">MySQL · 引擎特性 · InnoDB 数据页解析.md.html</a>
</li>
<li>
<a href="/文章/MySQL · 引擎特性 · InnoDB崩溃恢复.md.html">MySQL · 引擎特性 · InnoDB崩溃恢复.md.html</a>
</li>
<li>
<a href="/文章/MySQL · 引擎特性 · 临时表那些事儿.md.html">MySQL · 引擎特性 · 临时表那些事儿.md.html</a>
</li>
<li>
<a href="/文章/MySQL 主从复制 半同步复制.md.html">MySQL 主从复制 半同步复制.md.html</a>
</li>
<li>
<a href="/文章/MySQL 主从复制 基于GTID复制.md.html">MySQL 主从复制 基于GTID复制.md.html</a>
</li>
<li>
<a href="/文章/MySQL 主从复制.md.html">MySQL 主从复制.md.html</a>
</li>
<li>
<a href="/文章/MySQL 事务日志(redo log和undo log).md.html">MySQL 事务日志(redo log和undo log).md.html</a>
</li>
<li>
<a href="/文章/MySQL 亿级别数据迁移实战代码分享.md.html">MySQL 亿级别数据迁移实战代码分享.md.html</a>
</li>
<li>
<a href="/文章/MySQL 从一条数据说起-InnoDB行存储数据结构.md.html">MySQL 从一条数据说起-InnoDB行存储数据结构.md.html</a>
</li>
<li>
<a href="/文章/MySQL 地基基础:事务和锁的面纱.md.html">MySQL 地基基础:事务和锁的面纱.md.html</a>
</li>
<li>
<a href="/文章/MySQL 地基基础:数据字典.md.html">MySQL 地基基础:数据字典.md.html</a>
</li>
<li>
<a href="/文章/MySQL 地基基础:数据库字符集.md.html">MySQL 地基基础:数据库字符集.md.html</a>
</li>
<li>
<a href="/文章/MySQL 性能优化:碎片整理.md.html">MySQL 性能优化:碎片整理.md.html</a>
</li>
<li>
<a href="/文章/MySQL 故障诊断:一个 ALTER TALBE 执行了很久,你慌不慌?.md.html">MySQL 故障诊断:一个 ALTER TALBE 执行了很久,你慌不慌?.md.html</a>
</li>
<li>
<a href="/文章/MySQL 故障诊断:如何在日志中轻松定位大事务.md.html">MySQL 故障诊断:如何在日志中轻松定位大事务.md.html</a>
</li>
<li>
<a href="/文章/MySQL 故障诊断:教你快速定位加锁的 SQL.md.html">MySQL 故障诊断:教你快速定位加锁的 SQL.md.html</a>
</li>
<li>
<a href="/文章/MySQL 日志详解.md.html">MySQL 日志详解.md.html</a>
</li>
<li>
<a href="/文章/MySQL 的半同步是什么?.md.html">MySQL 的半同步是什么?.md.html</a>
</li>
<li>
<a href="/文章/MySQL中的事务和MVCC.md.html">MySQL中的事务和MVCC.md.html</a>
</li>
<li>
<a href="/文章/MySQL事务_事务隔离级别详解.md.html">MySQL事务_事务隔离级别详解.md.html</a>
</li>
<li>
<a href="/文章/MySQL优化优化 select count().md.html">MySQL优化优化 select count().md.html</a>
</li>
<li>
<a href="/文章/MySQL共享锁、排他锁、悲观锁、乐观锁.md.html">MySQL共享锁、排他锁、悲观锁、乐观锁.md.html</a>
</li>
<li>
<a href="/文章/MySQL的MVCC多版本并发控制.md.html">MySQL的MVCC多版本并发控制.md.html</a>
</li>
<li>
<a href="/文章/QingStor 对象存储架构设计及最佳实践.md.html">QingStor 对象存储架构设计及最佳实践.md.html</a>
</li>
<li>
<a href="/文章/RocketMQ 面试题集锦.md.html">RocketMQ 面试题集锦.md.html</a>
</li>
<li>
<a href="/文章/SnowFlake 雪花算法生成分布式 ID.md.html">SnowFlake 雪花算法生成分布式 ID.md.html</a>
</li>
<li>
<a class="current-tab" href="/文章/Spring Boot 2.x 结合 k8s 实现分布式微服务架构.md.html">Spring Boot 2.x 结合 k8s 实现分布式微服务架构.md.html</a>
</li>
<li>
<a href="/文章/Spring Boot 教程:如何开发一个 starter.md.html">Spring Boot 教程:如何开发一个 starter.md.html</a>
</li>
<li>
<a href="/文章/Spring MVC 原理.md.html">Spring MVC 原理.md.html</a>
</li>
<li>
<a href="/文章/Spring MyBatis和Spring整合的奥秘.md.html">Spring MyBatis和Spring整合的奥秘.md.html</a>
</li>
<li>
<a href="/文章/Spring 帮助你更好的理解Spring循环依赖.md.html">Spring 帮助你更好的理解Spring循环依赖.md.html</a>
</li>
<li>
<a href="/文章/Spring 循环依赖及解决方式.md.html">Spring 循环依赖及解决方式.md.html</a>
</li>
<li>
<a href="/文章/Spring中眼花缭乱的BeanDefinition.md.html">Spring中眼花缭乱的BeanDefinition.md.html</a>
</li>
<li>
<a href="/文章/Vert.x 基础入门.md.html">Vert.x 基础入门.md.html</a>
</li>
<li>
<a href="/文章/eBay 的 Elasticsearch 性能调优实践.md.html">eBay 的 Elasticsearch 性能调优实践.md.html</a>
</li>
<li>
<a href="/文章/不可不说的Java“锁”事.md.html">不可不说的Java“锁”事.md.html</a>
</li>
<li>
<a href="/文章/互联网并发限流实战.md.html">互联网并发限流实战.md.html</a>
</li>
<li>
<a href="/文章/从ReentrantLock的实现看AQS的原理及应用.md.html">从ReentrantLock的实现看AQS的原理及应用.md.html</a>
</li>
<li>
<a href="/文章/从SpringCloud开始聊微服务架构.md.html">从SpringCloud开始聊微服务架构.md.html</a>
</li>
<li>
<a href="/文章/全面了解 JDK 线程池实现原理.md.html">全面了解 JDK 线程池实现原理.md.html</a>
</li>
<li>
<a href="/文章/分布式一致性理论与算法.md.html">分布式一致性理论与算法.md.html</a>
</li>
<li>
<a href="/文章/分布式一致性算法 Raft.md.html">分布式一致性算法 Raft.md.html</a>
</li>
<li>
<a href="/文章/分布式唯一 ID 解析.md.html">分布式唯一 ID 解析.md.html</a>
</li>
<li>
<a href="/文章/分布式链路追踪:集群管理设计.md.html">分布式链路追踪:集群管理设计.md.html</a>
</li>
<li>
<a href="/文章/动态代理种类及原理,你知道多少?.md.html">动态代理种类及原理,你知道多少?.md.html</a>
</li>
<li>
<a href="/文章/响应式架构与 RxJava 在有赞零售的实践.md.html">响应式架构与 RxJava 在有赞零售的实践.md.html</a>
</li>
<li>
<a href="/文章/大数据算法——布隆过滤器.md.html">大数据算法——布隆过滤器.md.html</a>
</li>
<li>
<a href="/文章/如何优雅地记录操作日志?.md.html">如何优雅地记录操作日志?.md.html</a>
</li>
<li>
<a href="/文章/如何设计一个亿级消息量的 IM 系统.md.html">如何设计一个亿级消息量的 IM 系统.md.html</a>
</li>
<li>
<a href="/文章/异步网络模型.md.html">异步网络模型.md.html</a>
</li>
<li>
<a href="/文章/当我们在讨论CQRS时我们在讨论些神马.md.html">当我们在讨论CQRS时我们在讨论些神马.md.html</a>
</li>
<li>
<a href="/文章/彻底理解 MySQL 的索引机制.md.html">彻底理解 MySQL 的索引机制.md.html</a>
</li>
<li>
<a href="/文章/最全的 116 道 Redis 面试题解答.md.html">最全的 116 道 Redis 面试题解答.md.html</a>
</li>
<li>
<a href="/文章/有赞权限系统(SAM).md.html">有赞权限系统(SAM).md.html</a>
</li>
<li>
<a href="/文章/有赞零售中台建设方法的探索与实践.md.html">有赞零售中台建设方法的探索与实践.md.html</a>
</li>
<li>
<a href="/文章/服务注册与发现原理剖析Eureka、Zookeeper、Nacos.md.html">服务注册与发现原理剖析Eureka、Zookeeper、Nacos.md.html</a>
</li>
<li>
<a href="/文章/深入浅出Cache.md.html">深入浅出Cache.md.html</a>
</li>
<li>
<a href="/文章/深入理解 MySQL 底层实现.md.html">深入理解 MySQL 底层实现.md.html</a>
</li>
<li>
<a href="/文章/漫画讲解 git rebase VS git merge.md.html">漫画讲解 git rebase VS git merge.md.html</a>
</li>
<li>
<a href="/文章/生成浏览器唯一稳定 ID 的探索.md.html">生成浏览器唯一稳定 ID 的探索.md.html</a>
</li>
<li>
<a href="/文章/缓存 如何保证缓存与数据库的双写一致性?.md.html">缓存 如何保证缓存与数据库的双写一致性?.md.html</a>
</li>
<li>
<a href="/文章/网易严选怎么做全链路监控的?.md.html">网易严选怎么做全链路监控的?.md.html</a>
</li>
<li>
<a href="/文章/美团万亿级 KV 存储架构与实践.md.html">美团万亿级 KV 存储架构与实践.md.html</a>
</li>
<li>
<a href="/文章/美团点评Kubernetes集群管理实践.md.html">美团点评Kubernetes集群管理实践.md.html</a>
</li>
<li>
<a href="/文章/美团百亿规模API网关服务Shepherd的设计与实现.md.html">美团百亿规模API网关服务Shepherd的设计与实现.md.html</a>
</li>
<li>
<a href="/文章/解读《阿里巴巴 Java 开发手册》背后的思考.md.html">解读《阿里巴巴 Java 开发手册》背后的思考.md.html</a>
</li>
<li>
<a href="/文章/认识 MySQL 和 Redis 的数据一致性问题.md.html">认识 MySQL 和 Redis 的数据一致性问题.md.html</a>
</li>
<li>
<a href="/文章/进阶Dockerfile 高阶使用指南及镜像优化.md.html">进阶Dockerfile 高阶使用指南及镜像优化.md.html</a>
</li>
<li>
<a href="/文章/铁总在用的高性能分布式缓存计算框架 Geode.md.html">铁总在用的高性能分布式缓存计算框架 Geode.md.html</a>
</li>
<li>
<a href="/文章/阿里云PolarDB及其共享存储PolarFS技术实现分析.md.html">阿里云PolarDB及其共享存储PolarFS技术实现分析.md.html</a>
</li>
<li>
<a href="/文章/阿里云PolarDB及其共享存储PolarFS技术实现分析.md.html">阿里云PolarDB及其共享存储PolarFS技术实现分析.md.html</a>
</li>
<li>
<a href="/文章/面试最常被问的 Java 后端题.md.html">面试最常被问的 Java 后端题.md.html</a>
</li>
<li>
<a href="/文章/领域驱动设计在互联网业务开发中的实践.md.html">领域驱动设计在互联网业务开发中的实践.md.html</a>
</li>
<li>
<a href="/文章/领域驱动设计的菱形对称架构.md.html">领域驱动设计的菱形对称架构.md.html</a>
</li>
<li>
<a href="/文章/高效构建 Docker 镜像的最佳实践.md.html">高效构建 Docker 镜像的最佳实践.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>Spring Boot 2.x 结合 k8s 实现分布式微服务架构</h1>
<h3>Spring Boot 1.x 与 2.x 的区别</h3>
<p>在《<a href="https://gitbook.cn/gitchat/activity/5e8ada3452383e517ff2b5f8">微服务 Spring Cloud 架构设计</a>》一文中,笔者讲过 Spring Cloud 的架构设计。其实 Spring Boot 在一开始时,运用到的基本就是 Eureka、Config、Zuul、Ribbon、Feign、Hystrix 等。到了 Spring Boot 2.x 的时候,大量的组件开始风云崛起。下面简单列下这两个版本之间的区别如下。</p>
<p>Spring Boot 1.x 中session 的超时时间是这样的:</p>
<pre><code>server.session.timeout=3600
</code></pre>
<p>而在 2.x 中:</p>
<pre><code>server.servlet.session.timeout=PT120M
</code></pre>
<p>截然不同的写法cookie 也是一样的:</p>
<pre><code>server:
servlet:
session:
timeout: PT120M
cookie:
name: ORDER-SERVICE-SESSIONID
</code></pre>
<ul>
<li>应用的 ContextPath 配置属性改动,跟上面的 session 一样,加上了一个 servlet。</li>
<li>Spring Boot 2.x 基于 Spring 5而 Spring Boot 1.x 基于 Spring 4 或较低。</li>
<li>统一错误处理的基类 AbstarctErrorController 的改动。</li>
<li>配置文件的中文可以直接读取,不需要转码。</li>
<li>Acutator 变化很大默认情况不再启用所有监控需要定制化编写监控信息完全需要重写HealthIndicator,EndPoint 同理。</li>
<li>从 Spring Boot 2.x 开始,可以与 K8s 结合来实现服务的配置管理、负载均衡等,这是与 1.x 所不同的。</li>
</ul>
<h3>K8s 的一些资源的介绍</h3>
<p>上面说到 Spring Boot 2.x 可以结合 K8s 来作为微服务的架构设计,那么就先来说下 K8s 的一些组件吧。</p>
<p>ConfigMap看到这个名字可以理解它是用于保存配置信息的键值对可以用来保存单个属性也可以保存配置文件。对于一些非敏感的信息比如应用的配置信息则可以使用 ConfigMap。</p>
<p>创建一个 ConfigMap 有多种方式如下。</p>
<p><strong>1. key-value 字符串创建</strong></p>
<pre><code>kubectl create configmap test-config --from-literal=baseDir=/usr
</code></pre>
<p>上面的命令创建了一个名为 test-config拥有一条 key 为 baseDirvalue 为 &quot;/usr&quot; 的键值对数据。</p>
<p><strong>2. 根据 yml 描述文件创建</strong></p>
<pre><code>apiVersion: v1
kind: ConfigMap
metadata:
name: test-config
data:
baseDir: /usr
</code></pre>
<p>也可以这样,创建一个 yml 文件,选择不同的环境配置不同的信息:</p>
<pre><code>kind: ConfigMap
apiVersion: v1
metadata:
name: cas-server
data:
application.yaml: |-
greeting:
message: Say Hello to the World
---
spring:
profiles: dev
greeting:
message: Say Hello to the Dev
spring:
profiles: test
greeting:
message: Say Hello to the Test
spring:
profiles: prod
greeting:
message: Say Hello to the Prod
</code></pre>
<p>注意点:</p>
<ol>
<li>ConfigMap 必须在 Pod 使用其之前创建。</li>
<li>Pod 只能使用同一个命名空间的 ConfigMap。</li>
</ol>
<p>当然,还有其他更多用途,具体可以参考官网。</p>
<p>Service顾名思义是一个服务什么样的服务呢它是定义了一个服务的多种 pod 的逻辑合集以及一种访问 pod 的策略。</p>
<p>service 的类型有四种:</p>
<ul>
<li>ExternalName创建一个 DNS 别名指向 service name这样可以防止 service name 发生变化,但需要配合 DNS 插件使用。</li>
<li>ClusterIP默认的类型用于为集群内 Pod 访问时,提供的固定访问地址,默认是自动分配地址,可使用 ClusterIP 关键字指定固定 IP。</li>
<li>NodePort基于 ClusterIp用于为集群外部访问 Service 后面 Pod 提供访问接入端口。</li>
<li>LoadBalancer它是基于 NodePort。</li>
</ul>
<h3>如何使用 K8s 来实现服务注册与发现</h3>
<p>从上面讲的 Service我们可以看到一种场景所有的微服务在一个局域网内或者说在一个 K8s 集群下,那么可以通过 Service 用于集群内 Pod 的访问,这就是 Service 默认的一种类型 ClusterIPClusterIP 这种的默认会自动分配地址。</p>
<p>那么问题来了,既然可以通过上面的 ClusterIp 来实现集群内部的服务访问,那么如何注册服务呢?其实 K8s 并没有引入任何的注册中心,使用的就是 K8s 的 kube-dns 组件。然后 K8s 将 Service 的名称当做域名注册到 kube-dns 中,通过 Service 的名称就可以访问其提供的服务。那么问题又来了,如果一个服务的 pod 对应有多个,那么如何实现 LB其实最终通过 kube-proxy实现负载均衡。</p>
<p>说到这,我们来看下 Service 的服务发现与负载均衡的策略Service 负载分发策略有两种:</p>
<ul>
<li>RoundRobin轮询模式即轮询将请求转发到后端的各个 pod 上,其为默认模式。</li>
<li>SessionAffinity基于客户端 IP 地址进行会话保持的模式,类似 IP Hash 的方式,来实现服务的负载均衡。</li>
</ul>
<p>其实K8s 利用其 Service 实现服务的发现,其实说白了,就是通过域名进行层层解析,最后解析到容器内部的 ip 和 port 来找到对应的服务,以完成请求。</p>
<p>下面写一个很简单的例子:</p>
<pre><code>apiVersion: v1
kind: Service
metadata:
name: cas-server-service
namespace: default
spec:
ports:
- name: cas-server01
port: 2000
targetPort: cas-server01
selector:
app: cas-server
</code></pre>
<p>可以看到执行 <code>kubectl apply -f service.yaml</code> 后:</p>
<pre><code><a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="2f5d40405b6f5a4d5a415b5a">[email&#160;protected]</a>:~$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
admin-web-service ClusterIP 10.16.129.24 &lt;none&gt; 2001/TCP 84d
cas-server-service ClusterIP 10.16.230.167 &lt;none&gt; 2000/TCP 67d
cloud-admin-service-service ClusterIP 10.16.25.178 &lt;none&gt; 1001/TCP 190d
</code></pre>
<p>这样,我们可以看到默认的类型是 ClusterIP用于为集群内 Pod 访问时,可以先通过域名来解析到多个服务地址信息,然后再通过 LB 策略来选择其中一个作为请求的对象。</p>
<h3>K8s 如何来处理微服务中常用的配置</h3>
<p>在上面,我们讲过了几种创建 ConfigMap 的方式,其中有一种在 Java 中常常用到:通过创建 yml 文件来实现配置管理。</p>
<p>比如:</p>
<pre><code>kind: ConfigMap
apiVersion: v1
metadata:
name: cas-server
data:
application.yaml: |-
greeting:
message: Say Hello to the World
---
spring:
profiles: dev
greeting:
message: Say Hello to the Dev
spring:
profiles: test
greeting:
message: Say Hello to the Test
spring:
profiles: prod
greeting:
message: Say Hello to the Prod
</code></pre>
<p>上面创建了一个 yml 文件,同时,通过 spring.profiles 指定了开发、测试、生产等每种环境的配置。</p>
<p>具体代码:</p>
<pre><code>apiVersion: apps/v1
kind: Deployment
metadata:
name: cas-server-deployment
labels:
app: cas-server
spec:
replicas: 1
selector:
matchLabels:
app: cas-server
template:
metadata:
labels:
app: cas-server
spec:
nodeSelector:
cas-server: &quot;true&quot;
containers:
- name: cas-server
image: {{ cluster_cfg['cluster']['docker-registry']['prefix'] }}cas-server
imagePullPolicy: Always
ports:
- name: cas-server01
containerPort: 2000
volumeMounts:
- mountPath: /home/cas-server
name: cas-server-path
args: [&quot;sh&quot;, &quot;-c&quot;, &quot;nohup java $JAVA_OPTS -jar -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=128m -Xms1024m -Xmx1024m -Xmn256m -Xss256k -XX:SurvivorRatio=8 -XX:+UseConcMarkSweepGC cas-server.jar --spring.profiles.active=dev&quot;, &quot;&amp;&quot;]
hostAliases:
- ip: &quot;127.0.0.1&quot;
hostnames:
- &quot;gemantic.localhost&quot;
- ip: &quot;0.0.0.0&quot;
hostnames:
- &quot;gemantic.all&quot;
volumes:
- name: cas-server-path
hostPath:
path: /var/pai/cas-server
</code></pre>
<p>这样,当我们启动容器时,通过 <code>--spring.profiles.active=dev</code> 来指定当前容器的活跃环境,即可获取 ConfigMap 中对应的配置。是不是感觉跟 Java 中的 Config 配置多个环境的配置有点类似呢?但是,我们不用那么复杂,这些统统可以交给 K8s 来处理。只需要你启动这一命令即可,是不是很简单?</p>
<h3>Spring Boot 2.x 的新特性</h3>
<p>在第一节中,我们就讲到 1.x 与 2.x 的区别其中最为凸显的是Spring Boot 2.x 结合了 K8s 来实现微服务的架构设计。其实,在 K8s 中,更新 ConfigMap 后pod 是不会自动刷新 configMap 中的变更,如果想要获取 ConfigMap 中最新的信息,需要重启 pod。</p>
<p>但 2.x 提供了自动刷新的功能:</p>
<pre><code>spring:
application:
name: cas-server
cloud:
kubernetes:
config:
sources:
- name: ${spring.application.name}
namespace: default
discovery:
all-namespaces: true
reload:
enabled: true
mode: polling
period: 500
</code></pre>
<p>如上,我们打开了自动更新配置的开关,并且设置了自动更新的方式为主动拉取,时间间隔为 500ms同时还提供了另外一种方式——event 事件通知模式。这样,在 ConfigMap 发生改变时,无需重启 pod 即可获取最新的数据信息。</p>
<p>同时Spring Boot 2.x 结合了 K8s 来实现微服务的服务注册与发现:</p>
<pre><code>&lt;dependency&gt;
&lt;groupId&gt;org.springframework.cloud&lt;/groupId&gt;
&lt;artifactId&gt;spring-cloud-kubernetes-core&lt;/artifactId&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
&lt;groupId&gt;org.springframework.cloud&lt;/groupId&gt;
&lt;artifactId&gt;spring-cloud-kubernetes-discovery&lt;/artifactId&gt;
&lt;/dependency&gt;
</code></pre>
<p>开启服务发现功能:</p>
<pre><code>spring:
cloud:
kubernetes:
discovery:
all-namespaces: true
</code></pre>
<p>开启后,我们在《[微服务 Spring Cloud 架构设计]》一文中讲过,其实最终是向 K8s 的 API Server 发起 http 请求,获取 Service 资源的数据列表。然后根据底层的负载均衡策略来实现服务的发现,最终解析到某个 pod 上。那么为了同一服务的多个 pod 存在,我们需要执行:</p>
<pre><code>kubectl scale --replicas=2 deployment admin-web-deployment
</code></pre>
<p>同时,我们如果通过 HTTP 的 RestTemplate Client 来作服务请求时可以配置一些请求的策略RestTemplate 一般与 Ribbon 结合使用:</p>
<pre><code>client:
http:
request:
connectTimeout: 8000
readTimeout: 3000
backend:
ribbon:
eureka:
enabled: false
client:
enabled: true
ServerListRefreshInterval: 5000
ribbon:
ConnectTimeout: 8000
ReadTimeout: 3000
eager-load:
enabled: true
clients: cas-server-service,admin-web-service
MaxAutoRetries: 1 #对第一次请求的服务的重试次数
MaxAutoRetriesNextServer: 1 #要重试的下一个服务的最大数量(不包括第一个服务)
#ServerListRefreshInterval: 2000
OkToRetryOnAllOperations: true
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RoundRobinRule #com.damon.config.RibbonConfiguration #分布式负载均衡策略
</code></pre>
<p>可以配置一些服务列表,自定义一些负载均衡的策略。</p>
<p>如果你是使用 Feign 来作为 LB其实与 Ribbon 只有一点点不一样,因为 Feign 本身是基于 Ribbon 来实现的,除了加上注解 @EnableFeignClients 后,还要配置:</p>
<pre><code>feign:
client:
config:
default: #provider-service
connectTimeout: 8000 #客户端连接超时时间
readTimeout: 3000 #客户端读超时设置
loggerLevel: full
</code></pre>
<p>其他的可以自定义负载均衡策略,这一点是基于 Ribbon 的,所以是一样的。</p>
<h3>实战 Spring Boot 2.x 结合 K8s 来实现微服务架构设计</h3>
<p>微服务架构中,主要的就是服务消费者、服务的生产者可以互通,可以发生调用,在这基础上,还可以实现负载均衡,即一个服务调用另一个服务时,在该服务存在多个节点的情况下,可以通过一些策略来找到该服务的一个合适的节点访问。下面主要介绍服务的生产者与消费者。</p>
<p>先看生产者,引入常用的依赖:</p>
<pre><code>&lt;parent&gt;
&lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
&lt;artifactId&gt;spring-boot-starter-parent&lt;/artifactId&gt;
&lt;version&gt;2.1.13.RELEASE&lt;/version&gt;
&lt;relativePath/&gt;
&lt;/parent&gt;
&lt;properties&gt;
&lt;project.build.sourceEncoding&gt;UTF-8&lt;/project.build.sourceEncoding&gt;
&lt;project.reporting.outputEncoding&gt;UTF-8&lt;/project.reporting.outputEncoding&gt;
&lt;java.version&gt;1.8&lt;/java.version&gt;
&lt;swagger.version&gt;2.6.1&lt;/swagger.version&gt;
&lt;xstream.version&gt;1.4.7&lt;/xstream.version&gt;
&lt;pageHelper.version&gt;4.1.6&lt;/pageHelper.version&gt;
&lt;fastjson.version&gt;1.2.51&lt;/fastjson.version&gt;
&lt;springcloud.version&gt;Greenwich.SR3&lt;/springcloud.version&gt;
&lt;springcloud.kubernetes.version&gt;1.1.1.RELEASE&lt;/springcloud.kubernetes.version&gt;
&lt;mysql.version&gt;5.1.46&lt;/mysql.version&gt;
&lt;/properties&gt;
&lt;dependencyManagement&gt;
&lt;dependencies&gt;
&lt;dependency&gt;
&lt;groupId&gt;org.springframework.cloud&lt;/groupId&gt;
&lt;artifactId&gt;spring-cloud-dependencies&lt;/artifactId&gt;
&lt;version&gt;${springcloud.version}&lt;/version&gt;
&lt;type&gt;pom&lt;/type&gt;
&lt;scope&gt;import&lt;/scope&gt;
&lt;/dependency&gt;
&lt;/dependencies&gt;
&lt;/dependencyManagement&gt;
&lt;dependencies&gt;
&lt;dependency&gt;
&lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
&lt;artifactId&gt;spring-boot-starter-web&lt;/artifactId&gt;
&lt;exclusions&gt;
&lt;exclusion&gt;
&lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
&lt;artifactId&gt;spring-boot-starter-tomcat&lt;/artifactId&gt;
&lt;/exclusion&gt;
&lt;/exclusions&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
&lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
&lt;artifactId&gt;spring-boot-starter-undertow&lt;/artifactId&gt;
&lt;/dependency&gt;
&lt;!-- 配置加载依赖 --&gt;
&lt;dependency&gt;
&lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
&lt;artifactId&gt;spring-boot-actuator&lt;/artifactId&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
&lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
&lt;artifactId&gt;spring-boot-actuator-autoconfigure&lt;/artifactId&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
&lt;groupId&gt;org.springframework.cloud&lt;/groupId&gt;
&lt;artifactId&gt;spring-cloud-starter-kubernetes-config&lt;/artifactId&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
&lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
&lt;artifactId&gt;spring-boot-starter-test&lt;/artifactId&gt;
&lt;scope&gt;test&lt;/scope&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
&lt;groupId&gt;io.jsonwebtoken&lt;/groupId&gt;
&lt;artifactId&gt;jjwt&lt;/artifactId&gt;
&lt;version&gt;0.9.0&lt;/version&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
&lt;groupId&gt;cn.hutool&lt;/groupId&gt;
&lt;artifactId&gt;hutool-all&lt;/artifactId&gt;
&lt;version&gt;4.6.3&lt;/version&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
&lt;groupId&gt;com.google.guava&lt;/groupId&gt;
&lt;artifactId&gt;guava&lt;/artifactId&gt;
&lt;version&gt;19.0&lt;/version&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
&lt;groupId&gt;org.apache.commons&lt;/groupId&gt;
&lt;artifactId&gt;commons-lang3&lt;/artifactId&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
&lt;groupId&gt;commons-collections&lt;/groupId&gt;
&lt;artifactId&gt;commons-collections&lt;/artifactId&gt;
&lt;version&gt;3.2.2&lt;/version&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
&lt;groupId&gt;io.springfox&lt;/groupId&gt;
&lt;artifactId&gt;springfox-swagger2&lt;/artifactId&gt;
&lt;version&gt;${swagger.version}&lt;/version&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
&lt;groupId&gt;io.springfox&lt;/groupId&gt;
&lt;artifactId&gt;springfox-swagger-ui&lt;/artifactId&gt;
&lt;version&gt;${swagger.version}&lt;/version&gt;
&lt;/dependency&gt;
&lt;!-- 数据库分页依赖 --&gt;
&lt;dependency&gt;
&lt;groupId&gt;com.github.pagehelper&lt;/groupId&gt;
&lt;artifactId&gt;pagehelper&lt;/artifactId&gt;
&lt;version&gt;${pageHelper.version}&lt;/version&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
&lt;groupId&gt;org.mybatis.spring.boot&lt;/groupId&gt;
&lt;artifactId&gt;mybatis-spring-boot-starter&lt;/artifactId&gt;
&lt;version&gt;1.1.1&lt;/version&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
&lt;groupId&gt;mysql&lt;/groupId&gt;
&lt;artifactId&gt;mysql-connector-java&lt;/artifactId&gt;
&lt;version&gt;${mysql.version}&lt;/version&gt;
&lt;/dependency&gt;
&lt;!-- 数据库驱动 --&gt;
&lt;dependency&gt;
&lt;groupId&gt;com.alibaba&lt;/groupId&gt;
&lt;artifactId&gt;druid&lt;/artifactId&gt;
&lt;version&gt;1.1.3&lt;/version&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
&lt;groupId&gt;com.alibaba&lt;/groupId&gt;
&lt;artifactId&gt;fastjson&lt;/artifactId&gt;
&lt;version&gt;${fastjson.version}&lt;/version&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
&lt;groupId&gt;org.jsoup&lt;/groupId&gt;
&lt;artifactId&gt;jsoup&lt;/artifactId&gt;
&lt;version&gt;1.11.3&lt;/version&gt;
&lt;/dependency&gt;
&lt;/dependencies&gt;
</code></pre>
<p>上面我们使用了比较新的版本Spring Boot 2.1.13Cloud 版本是 Greenwich.SR3其次我们配置了 K8s 的 ConfigMap 所用的依赖,加上了数据库的一些配置,具体其他的,实现过程中,大家可以自行添加。</p>
<p>接下来,我们看启动时加载的配置文件,这里加了关于 K8s ConfigMap 所管理的配置所在的信息,以及保证服务被发现,开启了所有的 namespace同时还启动了配置自动刷新的功能注意的是该配置需要在 bootstrap 文件:</p>
<pre><code>spring:
application:
name: cas-server
cloud:
kubernetes:
config:
sources:
- name: ${spring.application.name}
namespace: default
discovery:
all-namespaces: true #发现所有的命令空间的服务
reload:
enabled: true
mode: polling #自动刷新模式为拉取模式,也可以是事件模式 event
period: 500 #拉取模式下的频率
logging: #日志路径设置
path: /data/${spring.application.name}/logs
</code></pre>
<p>剩下的一些配置可以在 application 文件中配置:</p>
<pre><code>spring:
profiles:
active: dev
server:
port: 2000
undertow:
accesslog:
enabled: false
pattern: combined
servlet:
session:
timeout: PT120M #session 超时时间
client:
http:
request:
connectTimeout: 8000
readTimeout: 30000
mybatis: #持久层配置
mapperLocations: classpath:mapper/*.xml
typeAliasesPackage: com.damon.*.model
</code></pre>
<p>接下来看下启动类:</p>
<pre><code>/**
*
* @author Damon
* @date 2020 年 1 月 13 日 下午 8:29:42
*
*/
@Configuration
@EnableAutoConfiguration
@ComponentScan(basePackages = {&quot;com.damon&quot;})
//@SpringBootApplication(scanBasePackages = { &quot;com.damon&quot; })
@EnableConfigurationProperties(EnvConfig.class)
public class CasApp {
public static void main(String[] args) {
SpringApplication.run(CasApp.class, args);
}
}
</code></pre>
<p>这里我们没有直接用注解 @SpringBootApplication因为主要用到的就是几个配置没必要全部加载。</p>
<p>我们看到启动类中有一个引入的 EnvConfig.class</p>
<pre><code>/**
* @author Damon
* @date 2019 年 10 月 25 日 下午 8:54:01
*
*/
@Configuration
@ConfigurationProperties(prefix = &quot;greeting&quot;)
public class EnvConfig {
private String message = &quot;This is a dummy message&quot;;
public String getMessage() {
return this.message;
}
public void setMessage(String message) {
this.message = message;
}
</code></pre>
<p>这就是配置 ConfigMap 中的属性的类。剩下的可以自己定义一个接口类,来实现服务生产者。</p>
<p>最后,我们需要在 K8s 下部署的话,需要准备几个脚本。</p>
<p><strong>1. 创建 ConfigMap</strong></p>
<pre><code>kind: ConfigMap
apiVersion: v1
metadata:
name: cas-server
data:
application.yaml: |-
greeting:
message: Say Hello to the World
---
spring:
profiles: dev
greeting:
message: Say Hello to the Dev
spring:
profiles: test
greeting:
message: Say Hello to the Test
spring:
profiles: prod
greeting:
message: Say Hello to the Prod
</code></pre>
<p>设置了不同环境的配置,注意,这里的 namespace 需要与服务部署的 namespace 一致,这里默认的是 default而且在创建服务之前先得创建这个。</p>
<p><strong>2. 创建服务部署脚本</strong></p>
<pre><code>apiVersion: apps/v1
kind: Deployment
metadata:
name: cas-server-deployment
labels:
app: cas-server
spec:
replicas: 3
selector:
matchLabels:
app: cas-server
template:
metadata:
labels:
app: cas-server
spec:
nodeSelector:
cas-server: &quot;true&quot;
containers:
- name: cas-server
image: cas-server
imagePullPolicy: Always
ports:
- name: cas-server01
containerPort: 2000
volumeMounts:
- mountPath: /home/cas-server
name: cas-server-path
- mountPath: /data/cas-server
name: cas-server-log-path
- mountPath: /etc/kubernetes
name: kube-config-path
args: [&quot;sh&quot;, &quot;-c&quot;, &quot;nohup java $JAVA_OPTS -jar -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=128m -Xms1024m -Xmx1024m -Xmn256m -Xss256k -XX:SurvivorRatio=8 -XX:+UseConcMarkSweepGC cas-server.jar --spring.profiles.active=dev&quot;, &quot;&amp;&quot;]
volumes:
- name: cas-server-path
hostPath:
path: /var/pai/cas-server
- name: cas-server-log-path
hostPath:
path: /data/cas-server
- name: kube-config-path
hostPath:
path: /etc/kubernetes
</code></pre>
<p>注意:这里有个属性 replicas其作用是当前 pod 所启动的副本数,即我们常说的启动的节点个数,当然,你也可以通过前面讲的脚本来执行生成多个 pod 副本。如果这里没有设置多个的话,也可以通过命令来执行:</p>
<pre><code>kubectl scale --replicas=3 deployment cas-server-deployment
</code></pre>
<p>这里,我建议使用 Deployment 类型的来创建 pod因为 Deployment 类型更好的支持弹性伸缩与滚动更新。</p>
<p>同时,我们通过 <code>--spring.profiles.active=dev</code> 来指定当前 pod 的运行环境。</p>
<p><strong>3. 创建一个 Service</strong></p>
<p>最后,如果服务想被发现,需要创建一个 Service</p>
<pre><code>apiVersion: v1
kind: Service
metadata:
name: cas-server-service
namespace: default
spec:
ports:
- name: cas-server01
port: 2000
targetPort: cas-server01
selector:
app: cas-server
</code></pre>
<p>注意,这里的 namespace 需要与服务部署的 namespace 一致,这里默认的是 default。</p>
<p>看看服务的消费者,同样,先看引入常用的依赖:</p>
<pre><code>&lt;parent&gt;
&lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
&lt;artifactId&gt;spring-boot-starter-parent&lt;/artifactId&gt;
&lt;version&gt;2.1.13.RELEASE&lt;/version&gt;
&lt;relativePath/&gt;
&lt;/parent&gt;
&lt;properties&gt;
&lt;project.build.sourceEncoding&gt;UTF-8&lt;/project.build.sourceEncoding&gt;
&lt;project.reporting.outputEncoding&gt;UTF-8&lt;/project.reporting.outputEncoding&gt;
&lt;java.version&gt;1.8&lt;/java.version&gt;
&lt;swagger.version&gt;2.6.1&lt;/swagger.version&gt;
&lt;xstream.version&gt;1.4.7&lt;/xstream.version&gt;
&lt;pageHelper.version&gt;4.1.6&lt;/pageHelper.version&gt;
&lt;fastjson.version&gt;1.2.51&lt;/fastjson.version&gt;
&lt;springcloud.version&gt;Greenwich.SR3&lt;/springcloud.version&gt;
&lt;!-- &lt;springcloud.version&gt;2.1.8.RELEASE&lt;/springcloud.version&gt; --&gt;
&lt;springcloud.kubernetes.version&gt;1.1.1.RELEASE&lt;/springcloud.kubernetes.version&gt;
&lt;mysql.version&gt;5.1.46&lt;/mysql.version&gt;
&lt;/properties&gt;
&lt;dependencyManagement&gt;
&lt;dependencies&gt;
&lt;dependency&gt;
&lt;groupId&gt;org.springframework.cloud&lt;/groupId&gt;
&lt;artifactId&gt;spring-cloud-dependencies&lt;/artifactId&gt;
&lt;version&gt;${springcloud.version}&lt;/version&gt;
&lt;type&gt;pom&lt;/type&gt;
&lt;scope&gt;import&lt;/scope&gt;
&lt;/dependency&gt;
&lt;/dependencies&gt;
&lt;/dependencyManagement&gt;
&lt;dependencies&gt;
&lt;dependency&gt;
&lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
&lt;artifactId&gt;spring-boot-starter-web&lt;/artifactId&gt;
&lt;exclusions&gt;
&lt;exclusion&gt;
&lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
&lt;artifactId&gt;spring-boot-starter-tomcat&lt;/artifactId&gt;
&lt;/exclusion&gt;
&lt;/exclusions&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
&lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
&lt;artifactId&gt;spring-boot-starter-undertow&lt;/artifactId&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
&lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
&lt;artifactId&gt;spring-boot-starter-test&lt;/artifactId&gt;
&lt;scope&gt;test&lt;/scope&gt;
&lt;/dependency&gt;
&lt;!-- 配置加载依赖 --&gt;
&lt;dependency&gt;
&lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
&lt;artifactId&gt;spring-boot-actuator&lt;/artifactId&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
&lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
&lt;artifactId&gt;spring-boot-actuator-autoconfigure&lt;/artifactId&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
&lt;groupId&gt;org.springframework.cloud&lt;/groupId&gt;
&lt;artifactId&gt;spring-cloud-starter-kubernetes-config&lt;/artifactId&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
&lt;groupId&gt;org.springframework.cloud&lt;/groupId&gt;
&lt;artifactId&gt;spring-cloud-commons&lt;/artifactId&gt;
&lt;/dependency&gt;
&lt;!-- 结合 k8s 实现服务发现 --&gt;
&lt;dependency&gt;
&lt;groupId&gt;org.springframework.cloud&lt;/groupId&gt;
&lt;artifactId&gt;spring-cloud-kubernetes-core&lt;/artifactId&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
&lt;groupId&gt;org.springframework.cloud&lt;/groupId&gt;
&lt;artifactId&gt;spring-cloud-kubernetes-discovery&lt;/artifactId&gt;
&lt;/dependency&gt;
&lt;!-- 负载均衡策略 --&gt;
&lt;dependency&gt;
&lt;groupId&gt;org.springframework.cloud&lt;/groupId&gt;
&lt;artifactId&gt;spring-cloud-starter-kubernetes-ribbon&lt;/artifactId&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
&lt;groupId&gt;org.springframework.cloud&lt;/groupId&gt;
&lt;artifactId&gt;spring-cloud-starter-netflix-ribbon&lt;/artifactId&gt;
&lt;/dependency&gt;
&lt;!-- 熔断机制 --&gt;
&lt;dependency&gt;
&lt;groupId&gt;org.springframework.cloud&lt;/groupId&gt;
&lt;artifactId&gt;spring-cloud-starter-netflix-hystrix&lt;/artifactId&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
&lt;groupId&gt;cn.hutool&lt;/groupId&gt;
&lt;artifactId&gt;hutool-all&lt;/artifactId&gt;
&lt;version&gt;4.6.3&lt;/version&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
&lt;groupId&gt;com.alibaba&lt;/groupId&gt;
&lt;artifactId&gt;fastjson&lt;/artifactId&gt;
&lt;version&gt;${fastjson.version}&lt;/version&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
&lt;groupId&gt;org.jsoup&lt;/groupId&gt;
&lt;artifactId&gt;jsoup&lt;/artifactId&gt;
&lt;version&gt;1.11.3&lt;/version&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
&lt;groupId&gt;io.springfox&lt;/groupId&gt;
&lt;artifactId&gt;springfox-swagger2&lt;/artifactId&gt;
&lt;version&gt;${swagger.version}&lt;/version&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
&lt;groupId&gt;io.springfox&lt;/groupId&gt;
&lt;artifactId&gt;springfox-swagger-ui&lt;/artifactId&gt;
&lt;version&gt;${swagger.version}&lt;/version&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
&lt;groupId&gt;org.apache.commons&lt;/groupId&gt;
&lt;artifactId&gt;commons-lang3&lt;/artifactId&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
&lt;groupId&gt;commons-collections&lt;/groupId&gt;
&lt;artifactId&gt;commons-collections&lt;/artifactId&gt;
&lt;version&gt;3.2.2&lt;/version&gt;
&lt;/dependency&gt;
&lt;!-- 数据库分页 --&gt;
&lt;dependency&gt;
&lt;groupId&gt;com.github.pagehelper&lt;/groupId&gt;
&lt;artifactId&gt;pagehelper&lt;/artifactId&gt;
&lt;version&gt;${pageHelper.version}&lt;/version&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
&lt;groupId&gt;org.mybatis.spring.boot&lt;/groupId&gt;
&lt;artifactId&gt;mybatis-spring-boot-starter&lt;/artifactId&gt;
&lt;version&gt;1.1.1&lt;/version&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
&lt;groupId&gt;mysql&lt;/groupId&gt;
&lt;artifactId&gt;mysql-connector-java&lt;/artifactId&gt;
&lt;version&gt;${mysql.version}&lt;/version&gt;
&lt;/dependency&gt;
&lt;!-- 数据库驱动 --&gt;
&lt;dependency&gt;
&lt;groupId&gt;com.alibaba&lt;/groupId&gt;
&lt;artifactId&gt;druid&lt;/artifactId&gt;
&lt;version&gt;1.1.3&lt;/version&gt;
&lt;/dependency&gt;
&lt;/dependencies&gt;
</code></pre>
<p>这里大部分的依赖跟生产者一样,但,需要加入服务发现的依赖,以及所用的负载均衡的策略依赖、服务的熔断机制。</p>
<p>接下来 bootstrap 文件中的配置跟生产者一样,这里不在说了,唯一不同的是 application 文件:</p>
<pre><code>backend:
ribbon:
eureka:
enabled: false
client:
enabled: true
ServerListRefreshInterval: 5000
ribbon:
ConnectTimeout: 3000
ReadTimeout: 1000
eager-load:
enabled: true
clients: cas-server-service,edge-cas-service,admin-web-service #负载均衡发现的服务列表
MaxAutoRetries: 1 #对第一次请求的服务的重试次数
MaxAutoRetriesNextServer: 1 #要重试的下一个服务的最大数量(不包括第一个服务)
OkToRetryOnAllOperations: true
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RoundRobinRule #负载均衡策略
hystrix:
command:
BackendCall:
execution:
isolation:
thread:
timeoutInMilliseconds: 5000 #熔断机制设置的超时时间
threadpool:
BackendCallThread:
coreSize: 5
</code></pre>
<p>引入了负载均衡的机制以及策略(可以自定义策略)。</p>
<p>接下来看启动类:</p>
<pre><code>/**
* @author Damon
* @date 2020 年 1 月 13 日 下午 9:23:06
*
*/
@Configuration
@EnableAutoConfiguration
@ComponentScan(basePackages = {&quot;com.damon&quot;})
@EnableConfigurationProperties(EnvConfig.class)
@EnableDiscoveryClient
public class AdminApp {
public static void main(String[] args) {
SpringApplication.run(AdminApp.class, args);
}
}
</code></pre>
<p>同样的 EnvConfig 类,这里不再展示了。其他的比如:注解 @EnableDiscoveryClient 是为了服务发现。</p>
<p>同样,我们新建接口,假如我们生产者有一个接口是:</p>
<pre><code>http://cas-server-service/api/getUser
</code></pre>
<p>则,我们在调用它时,可以通过 RestTemplate Client 来直接调用,通过 Ribbon 来实现负载均衡:</p>
<pre><code>@LoadBalanced
@Bean
public RestTemplate restTemplate() {
SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
requestFactory.setReadTimeout(env.getProperty(&quot;client.http.request.readTimeout&quot;, Integer.class, 15000));
requestFactory.setConnectTimeout(env.getProperty(&quot;client.http.request.connectTimeout&quot;, Integer.class, 3000));
RestTemplate rt = new RestTemplate(requestFactory);
return rt;
}
</code></pre>
<p>可以看到,这种方式的分布式负载均衡实现起来很简单,直接注入一个初始化 Bean加上一个注解 @LoadBalanced 即可。</p>
<p>在实现类中,我们只要直接调用服务生产者:</p>
<pre><code class="language-java">ResponseEntity&lt;String&gt; forEntity = restTemplate.getForEntity(&quot;http://cas-server/api/getUser&quot;, String.class);
</code></pre>
<p>其中URL 中 必须要加上 <code>&quot;http://&quot;</code>这样即可实现服务的发现以及负载均衡其中LB 的策略,可以采用 Ribbon 的几种方式,也可以自定义一种。</p>
<p>最后,可以在实现类上加一个熔断机制:</p>
<pre><code class="language-java">@HystrixCommand(fallbackMethod = &quot;admin_service_fallBack&quot;)
public Response&lt;Object&gt; getUserInfo(HttpServletRequest req, HttpServletResponse res) {
ResponseEntity&lt;String&gt; forEntity = restTemplate.getForEntity(envConfig.getCas_server_url() + &quot;/api/getUser&quot;, String.class);
logger.info(&quot;test restTemplate.getForEntity(): {}&quot;, forEntity);
if (forEntity.getStatusCodeValue() == 200) {
logger.info(&quot;================================test restTemplate.getForEntity(): {}&quot;, JSON.toJSON(forEntity.getBody()));
logger.info(JSON.toJSONString(forEntity.getBody()));
}
}
</code></pre>
<p>其中发生熔断时,回调方法:</p>
<pre><code class="language-java">private Response&lt;Object&gt; admin_service_fallBack(HttpServletRequest req, HttpServletResponse res) {
String token = StrUtil.subAfter(req.getHeader(&quot;Authorization&quot;), &quot;bearer &quot;, false);
logger.info(&quot;admin_service_fallBack token: {}&quot;, token);
return Response.ok(200, -5, &quot;服务挂啦!&quot;, null);
}
</code></pre>
<p>其返回的对象必须与原函数一致,否则可能会报错。具体的可以参考《<a href="https://mp.weixin.qq.com/s/TcwAONaCexKIeT-63ClGsg">Spring cloud 之熔断机制</a>》。</p>
<p>最后与生产者一样,需要创建 ConfigMap、Service、服务部署脚本下面会开源这些代码这里也就不一一展示了。最后我们会发现当请求 认证中心时,认证中心存在的多个 pod可以被轮训的请求到。这就是基于 Ribbon 的轮训策略来实现分布式的负载均衡,并且基于 Redis 来实现信息共享。</p>
<h3>结束福利</h3>
<p>开源几个微服务的架构设计项目:</p>
<ul>
<li><a href="https://github.com/damon008/spring-cloud-oauth2">https://github.com/damon008/spring-cloud-oauth2</a></li>
<li><a href="https://github.com/damon008/spring-cloud-k8s">https://github.com/damon008/spring-cloud-k8s</a></li>
<li><a href="https://gitee.com/damon_one/spring-cloud-k8s">https://gitee.com/damon_one/spring-cloud-k8s</a></li>
<li><a href="https://gitee.com/damon_one/spring-cloud-oauth2">https://gitee.com/damon_one/spring-cloud-oauth2</a></li>
</ul>
<p>欢迎大家 star多多指教。</p>
</div>
</div>
<div>
<div style="float: left">
<a href="/文章/SnowFlake 雪花算法生成分布式 ID.md.html">上一页</a>
</div>
<div style="float: right">
<a href="/文章/Spring Boot 教程:如何开发一个 starter.md.html">下一页</a>
</div>
</div>
</div>
</div>
</div>
</div>
<a class="off-canvas-overlay" onclick="hide_canvas()"></a>
</div>
<script data-cfasync="false" src="/cdn-cgi/scripts/5c5dd728/cloudflare-static/email-decode.min.js"></script><script defer src="https://static.cloudflareinsights.com/beacon.min.js/v652eace1692a40cfa3763df669d7439c1639079717194" integrity="sha512-Gi7xpJR8tSkrpF7aordPZQlW2DLtzUlZcumS8dMQjwDHEnw9I7ZLyiOj/6tZStRBGtGgN6ceN6cMH8z7etPGlw==" data-cf-beacon='{"rayId":"70998019cf128b66","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>