812 lines
29 KiB
HTML
812 lines
29 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en-us">
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||
|
||
|
||
<script>
|
||
(function() {
|
||
|
||
|
||
const autoTheme = false;
|
||
if (autoTheme) {
|
||
document.documentElement.setAttribute('data-auto-theme', 'true');
|
||
}
|
||
|
||
const theme = localStorage.getItem('cleanwhite-theme') ||
|
||
(window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light');
|
||
document.documentElement.setAttribute('data-theme', theme);
|
||
})();
|
||
</script>
|
||
|
||
|
||
<meta property="og:site_name" content="David Young">
|
||
<meta property="og:type" content="article">
|
||
|
||
|
||
<meta property="og:image" content="https://davidpaulyoung.com//img/istio-traffic-shifting/crossroads.png">
|
||
<meta property="twitter:image" content="https://davidpaulyoung.com//img/istio-traffic-shifting/crossroads.png" />
|
||
|
||
|
||
|
||
<meta name="title" content="使用Istio实现应用流量转移" />
|
||
<meta property="og:title" content="使用Istio实现应用流量转移" />
|
||
<meta property="twitter:title" content="使用Istio实现应用流量转移" />
|
||
|
||
|
||
|
||
<meta name="description" content="本任务将演示如何将应用流量逐渐从旧版本的服务迁移到新版本。通过Istio,可以使用一系列不同权重的规则(10%,20%,··· 100%)将流量平缓地从旧版本服务迁移到新版本服务。">
|
||
<meta property="og:description" content="本任务将演示如何将应用流量逐渐从旧版本的服务迁移到新版本。通过Istio,可以使用一系列不同权重的规则(10%,20%,··· 100%)将流量平缓地从旧版本服务迁移到新版本服务。" />
|
||
<meta property="twitter:description" content="本任务将演示如何将应用流量逐渐从旧版本的服务迁移到新版本。通过Istio,可以使用一系列不同权重的规则(10%,20%,··· 100%)将流量平缓地从旧版本服务迁移到新版本服务。" />
|
||
|
||
|
||
<meta property="og:url" content="https://davidpaulyoung.com/2017/11/07/istio-traffic-shifting/" />
|
||
|
||
|
||
<meta property="twitter:card" content="summary" />
|
||
|
||
|
||
|
||
<meta name="keyword" content="Von Balthasar, Scripture, Gravel Riding, Ham Radio, Divine Office, Open Source">
|
||
<link rel="shortcut icon" href="/img/favicon.ico">
|
||
|
||
<title>使用Istio实现应用流量转移 | David Young Blog</title>
|
||
|
||
<link rel="canonical" href="/2017/11/07/istio-traffic-shifting/">
|
||
|
||
|
||
|
||
|
||
<link rel="stylesheet" href="/css/bootstrap.min.css">
|
||
|
||
|
||
<link rel="stylesheet" href="/css/hugo-theme-cleanwhite.css">
|
||
|
||
|
||
<link rel="stylesheet" href="/css/theme-variables.css">
|
||
|
||
|
||
<link rel="stylesheet" href="/css/zanshang.min.css">
|
||
|
||
|
||
<link rel="stylesheet" href="/css/font-awesome.all.min.css">
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<script src="/js/jquery.min.js"></script>
|
||
|
||
|
||
<script src="/js/bootstrap.min.js"></script>
|
||
|
||
|
||
<script src="/js/hux-blog.min.js"></script>
|
||
|
||
|
||
<script src="/js/lazysizes.min.js"></script>
|
||
|
||
|
||
|
||
|
||
</head>
|
||
|
||
|
||
|
||
|
||
|
||
<nav class="navbar navbar-default navbar-custom navbar-fixed-top">
|
||
|
||
<div class="container-fluid">
|
||
|
||
<div class="navbar-header page-scroll">
|
||
<button type="button" class="navbar-toggle">
|
||
<span class="sr-only">Toggle navigation</span>
|
||
<span class="icon-bar"></span>
|
||
<span class="icon-bar"></span>
|
||
<span class="icon-bar"></span>
|
||
</button>
|
||
<a class="navbar-brand" href="/">David Young</a>
|
||
</div>
|
||
|
||
|
||
|
||
<div id="huxblog_navbar">
|
||
<div class="navbar-collapse">
|
||
<ul class="nav navbar-nav navbar-right">
|
||
<li>
|
||
<a href="/">All Posts</a>
|
||
</li>
|
||
|
||
|
||
<li>
|
||
<a href="/categories/life/">life</a>
|
||
</li>
|
||
|
||
<li>
|
||
<a href="/categories/tech/">tech</a>
|
||
</li>
|
||
|
||
<li>
|
||
<a href="/categories/tips/">tips</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
<li><a href="/archive//">ARCHIVE</a></li>
|
||
|
||
<li><a href="/notes//">NOTES</a></li>
|
||
|
||
<li><a href="/about//">ABOUT</a></li>
|
||
|
||
<li>
|
||
<a href="/search"><i class="fa fa-search"></i></a>
|
||
</li>
|
||
|
||
<li>
|
||
<a href="#" id="theme-toggle" title="Toggle dark mode" style="opacity: 0;">
|
||
<i class="fa fa-moon"></i>
|
||
<i class="fa fa-sun" style="display: none;"></i>
|
||
</a>
|
||
</li>
|
||
<script>
|
||
(function() {
|
||
|
||
var theme = localStorage.getItem('cleanwhite-theme') ||
|
||
(window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light');
|
||
var toggleBtn = document.getElementById('theme-toggle');
|
||
if (toggleBtn) {
|
||
var moonIcon = toggleBtn.querySelector('.fa-moon');
|
||
var sunIcon = toggleBtn.querySelector('.fa-sun');
|
||
|
||
if (theme === 'dark') {
|
||
if (moonIcon) moonIcon.style.display = 'none';
|
||
if (sunIcon) sunIcon.style.display = 'inline';
|
||
toggleBtn.setAttribute('title', 'Switch to light mode');
|
||
} else {
|
||
if (moonIcon) moonIcon.style.display = 'inline';
|
||
if (sunIcon) sunIcon.style.display = 'none';
|
||
toggleBtn.setAttribute('title', 'Switch to dark mode');
|
||
}
|
||
|
||
|
||
requestAnimationFrame(function() {
|
||
toggleBtn.style.transition = 'opacity 0.2s ease';
|
||
toggleBtn.style.opacity = '1';
|
||
});
|
||
}
|
||
})();
|
||
</script>
|
||
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
</nav>
|
||
<script>
|
||
|
||
|
||
|
||
var $body = document.body;
|
||
var $toggle = document.querySelector('.navbar-toggle');
|
||
var $navbar = document.querySelector('#huxblog_navbar');
|
||
var $collapse = document.querySelector('.navbar-collapse');
|
||
|
||
$toggle.addEventListener('click', handleMagic)
|
||
function handleMagic(e){
|
||
if ($navbar.className.indexOf('in') > 0) {
|
||
|
||
$navbar.className = " ";
|
||
|
||
setTimeout(function(){
|
||
|
||
if($navbar.className.indexOf('in') < 0) {
|
||
$collapse.style.height = "0px"
|
||
}
|
||
},400)
|
||
}else{
|
||
|
||
$collapse.style.height = "auto"
|
||
$navbar.className += " in";
|
||
}
|
||
}
|
||
|
||
|
||
document.addEventListener('DOMContentLoaded', function() {
|
||
var navLinks = document.querySelectorAll('.navbar-collapse a');
|
||
navLinks.forEach(function(link) {
|
||
link.addEventListener('click', function() {
|
||
if ($navbar.className.indexOf('in') > 0) {
|
||
|
||
$navbar.className = " ";
|
||
setTimeout(function(){
|
||
if($navbar.className.indexOf('in') < 0) {
|
||
$collapse.style.height = "0px"
|
||
}
|
||
},400)
|
||
}
|
||
});
|
||
});
|
||
});
|
||
</script>
|
||
|
||
|
||
|
||
|
||
<style type="text/css">
|
||
header.intro-header {
|
||
background-image: url('/img/istio-traffic-shifting/crossroads.png')
|
||
}
|
||
</style>
|
||
|
||
<header class="intro-header" >
|
||
|
||
<div class="container">
|
||
<div class="row">
|
||
<div class="col-lg-8 col-lg-offset-2 col-md-10 col-md-offset-1">
|
||
<div class="post-heading">
|
||
<div class="tags">
|
||
|
||
<a class="tag" href="/tags/istio" title="Istio">
|
||
Istio
|
||
</a>
|
||
|
||
</div>
|
||
<h1>使用Istio实现应用流量转移</h1>
|
||
<h2 class="subheading"> "本文翻译自istio官方文档"</h2>
|
||
<span class="meta">
|
||
|
||
Posted by
|
||
|
||
"赵化冰"
|
||
|
||
on
|
||
Tuesday, November 7, 2017
|
||
|
||
|
||
|
||
|
||
</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</header>
|
||
|
||
|
||
|
||
|
||
<article>
|
||
<div class="container">
|
||
<div class="row">
|
||
|
||
|
||
<div class="
|
||
col-lg-8 col-lg-offset-2
|
||
col-md-10 col-md-offset-1
|
||
post-container">
|
||
|
||
|
||
<p>关于Istio的更多内容请参考<a href="http://istio.doczh.cn/">istio中文文档</a>。</p>
|
||
<p>原文参见<a href="https://istio.io/docs/tasks/traffic-management/traffic-shifting.html">Traffic Shifting</a>。</p>
|
||
<p>本任务将演示如何将应用流量逐渐从旧版本的服务迁移到新版本。通过Istio,可以使用一系列不同权重的规则(10%,20%,··· 100%)将流量平缓地从旧版本服务迁移到新版本服务。</p>
|
||
<p>为简单起见,本任务将采用两步将流量从<code>reviews:v1</code> 迁移到 <code>reviews:v3</code>,权重分别为50%,100%。</p>
|
||
<h1 id="开始之前">开始之前</h1>
|
||
<ul>
|
||
<li>
|
||
<p>参照文档<a href="http://istio.doczh.cn/docs/setup/kubernetes/index.html">安装指南</a>中的步骤安装Istio。</p>
|
||
</li>
|
||
<li>
|
||
<p>部署<a href="http://istio.doczh.cn/docs/guides/bookinfo.html">BookInfo</a> 示例应用程序。</p>
|
||
</li>
|
||
</ul>
|
||
<blockquote>
|
||
<p>请注意:本文档假设示采用kubernetes部署示例应用程序。所有的示例命令行都采用规则yaml文件(例如<code>samples/bookinfo/kube/route-rule-all-v1.yaml</code>)指定的kubernetes版本。如果在不同的环境下运行本任务,请将<code>kube</code>修改为运行环境中相应的目录(例如,对基于Consul的运行环境,目录就是<code>samples/bookinfo/consul/route-rule-all-v1.yaml</code>)。</p>
|
||
</blockquote>
|
||
<h1 id="基于权重的版本路由">基于权重的版本路由</h1>
|
||
<ol>
|
||
<li>
|
||
<p>将所有微服务的缺省版本设置为v1.</p>
|
||
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>istioctl create -f samples/bookinfo/kube/route-rule-all-v1.yaml
|
||
</span></span></code></pre></div></li>
|
||
<li>
|
||
<p>在浏览器中打开http://$GATEWAY_URL/productpage, 确认<code>reviews</code> 服务目前的活动版本是v1。</p>
|
||
<p>可以看到浏览器中出现BooInfo应用的productpage页面。
|
||
注意<code>productpage</code>显示的评价内容不带星级。这是由于<code>reviews:v1</code>不会访问<code>ratings</code>服务。</p>
|
||
<blockquote>
|
||
<p>请注意:如果之前执行过 <a href="http://istio.doczh.cn/docs/tasks/traffic-management/request-routing.html">配置请求路由</a>任务,则需要先注销测试用户“jason”或者删除之前单独为该用户创建的测试规则:</p>
|
||
</blockquote>
|
||
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>istioctl delete routerule reviews-test-v2
|
||
</span></span></code></pre></div></li>
|
||
<li>
|
||
<p>首先,使用下面的命令把50%的流量从<code>reviews:v1</code>转移到<code>reviews:v3</code>:</p>
|
||
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>istioctl replace -f samples/bookinfo/kube/route-rule-reviews-50-v3.yaml
|
||
</span></span></code></pre></div><p>注意这里使用了<code>istioctl replace</code>而不是<code>create</code>。</p>
|
||
</li>
|
||
<li>
|
||
<p>在浏览器中多次刷新<code>productpage</code>页面,大约有50%的几率会看到页面中出现带红星的评价内容。</p>
|
||
<blockquote>
|
||
<p>请注意:在目前的Envoy sidecar实现中,可能需要刷新<code>productpage</code>很多次才能看到流量分发的效果。在看到页面出现变化前,有可能需要刷新15次或者更多。如果修改规则,将90%的流量路由到v3,可以看到更明显的效果。</p>
|
||
</blockquote>
|
||
</li>
|
||
<li>
|
||
<p>当v3版本的<code>reviews</code>服务已经稳定运行后,可以将100%的流量路由到<code>reviews:v3</code>:</p>
|
||
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>istioctl replace -f samples/bookinfo/kube/route-rule-reviews-v3.yaml
|
||
</span></span></code></pre></div><p>此时,以任何用户登录到<code>productpage</code>页面,都可以看到带红星的评价信息。</p>
|
||
</li>
|
||
</ol>
|
||
<h1 id="理解原理">理解原理</h1>
|
||
<p>在这个任务中,我们使用了Istio的带权重路由的特性将流量从老版本的<code>reviews</code>服务逐渐迁移到了新版本服务中。</p>
|
||
<p>注意该方式和使用容器编排平台的部署特性来进行版本迁移是完全不同的。容器编排平台使用了实例scaling来对流量进行管理。而通过Istio,两个版本的<code>reviews</code>服务可以独立地进行scale up和scale down,并不会影响这两个版本服务之间的流量分发。</p>
|
||
<p>想了解更多支持scaling的按版本路由功能,请查看<a href="https://istio.io/blog/canary-deployments-using-istio.html">Canary Deployments using Istio</a>。</p>
|
||
<h1 id="清理">清理</h1>
|
||
<ul>
|
||
<li>
|
||
<p>删除路由规则。</p>
|
||
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>istioctl delete -f samples/bookinfo/kube/route-rule-all-v1.yaml
|
||
</span></span></code></pre></div></li>
|
||
<li>
|
||
<p>如果不打算尝试后面的任务,请参照<a href="http://istio.doczh.cn/docs/guides/bookinfo.html#cleanup">BookInfo cleanup</a> 中的步骤关闭应用程序。</p>
|
||
</li>
|
||
</ul>
|
||
<h1 id="进阶阅读">进阶阅读</h1>
|
||
<ul>
|
||
<li>更多的内容请参见<a href="http://istio.doczh.cn/docs/concepts/traffic-management/rules-configuration.html">请求路由</a>。</li>
|
||
</ul>
|
||
|
||
|
||
|
||
<div class="entry-shang text-center">
|
||
|
||
<p>「真诚赞赏,手留余香」</p>
|
||
|
||
<button class="zs show-zs btn btn-bred">赞赏支持</button>
|
||
</div>
|
||
<div class="zs-modal-bg"></div>
|
||
<div class="zs-modal-box">
|
||
<div class="zs-modal-head">
|
||
<button type="button" class="close">×</button>
|
||
<span class="author"><a href="https://davidpaulyoung.com/"><img src="/img/favicon.png" />David Young</a></span>
|
||
|
||
<p class="tip"><i></i><span>真诚赞赏,手留余香</span></p>
|
||
|
||
|
||
</div>
|
||
<div class="zs-modal-body">
|
||
<div class="zs-modal-btns">
|
||
<button class="btn btn-blink" data-num="2">2元</button>
|
||
<button class="btn btn-blink" data-num="5">5元</button>
|
||
<button class="btn btn-blink" data-num="10">10元</button>
|
||
<button class="btn btn-blink" data-num="50">50元</button>
|
||
<button class="btn btn-blink" data-num="100">100元</button>
|
||
<button class="btn btn-blink" data-num="1">任意金额</button>
|
||
</div>
|
||
<div class="zs-modal-pay">
|
||
<button class="btn btn-bred" id="pay-text">2元</button>
|
||
<p>使用<span id="pay-type">微信</span>扫描二维码完成支付</p>
|
||
<img src="/img/reward/wechat-2.png" id="pay-image"/>
|
||
</div>
|
||
</div>
|
||
<div class="zs-modal-footer">
|
||
<label><input type="radio" name="zs-type" value="wechat" class="zs-type" checked="checked"><span ><span class="zs-wechat"><img src="/img/reward/wechat-btn.png"/></span></label>
|
||
<label><input type="radio" name="zs-type" value="alipay" class="zs-type" class="zs-alipay"><img src="/img/reward/alipay-btn.png"/></span></label>
|
||
</div>
|
||
</div>
|
||
<script type="text/javascript" src="/js/reward.js"></script>
|
||
|
||
|
||
|
||
|
||
<hr>
|
||
<ul class="pager">
|
||
|
||
<li class="previous">
|
||
<a href="/2017/11/04/istio-install_and_example/" data-toggle="tooltip" data-placement="top" title="Istio及Bookinfo示例程序安装试用笔记">←
|
||
Previous Post</a>
|
||
</li>
|
||
|
||
|
||
<li class="next">
|
||
<a href="/2017/11/08/istio-canary-release/" data-toggle="tooltip" data-placement="top" title="采用Istio实现灰度发布(金丝雀发布)">Next
|
||
Post →</a>
|
||
</li>
|
||
|
||
</ul>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<link href="https://xxx.xxx.com/dist/Artalk.css" rel="stylesheet" />
|
||
<script src="https://xxx.xxx.com/dist/Artalk.js"></script>
|
||
|
||
|
||
<div id="Comments"></div>
|
||
|
||
<script>
|
||
Artalk.init({
|
||
el: '#Comments',
|
||
pageKey: 'https:\/\/davidpaulyoung.com\/2017\/11\/07\/istio-traffic-shifting\/',
|
||
pageTitle: '使用Istio实现应用流量转移',
|
||
server: 'https:\/\/xxx.xxx.com',
|
||
site: 'xxx blog',
|
||
})
|
||
</script>
|
||
|
||
|
||
</div>
|
||
|
||
|
||
|
||
<div class="
|
||
col-lg-2 col-lg-offset-0
|
||
visible-lg-block
|
||
sidebar-container
|
||
catalog-container">
|
||
<div class="side-catalog">
|
||
<hr class="hidden-sm hidden-xs">
|
||
<h5>
|
||
<a class="catalog-toggle" href="#">CATALOG</a>
|
||
</h5>
|
||
<ul class="catalog-body"></ul>
|
||
</div>
|
||
</div>
|
||
|
||
|
||
|
||
<div class="
|
||
col-lg-8 col-lg-offset-2
|
||
col-md-10 col-md-offset-1
|
||
sidebar-container">
|
||
|
||
|
||
|
||
<section>
|
||
<hr class="hidden-sm hidden-xs">
|
||
<h5><a href="/tags/">FEATURED TAGS</a></h5>
|
||
<div class="tags">
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/tags/docker" title="docker">
|
||
docker
|
||
</a>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/tags/istio" title="istio">
|
||
istio
|
||
</a>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/tags/kubernetes" title="kubernetes">
|
||
kubernetes
|
||
</a>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/tags/microservice" title="microservice">
|
||
microservice
|
||
</a>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/tags/security" title="security">
|
||
security
|
||
</a>
|
||
|
||
|
||
|
||
<a href="/tags/service-mesh" title="service mesh">
|
||
service mesh
|
||
</a>
|
||
|
||
|
||
|
||
<a href="/tags/tips" title="tips">
|
||
tips
|
||
</a>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
</div>
|
||
</section>
|
||
|
||
|
||
|
||
|
||
<section>
|
||
<hr>
|
||
<h5>FRIENDS</h5>
|
||
<ul class="list-inline">
|
||
|
||
<li><a target="_blank" href="https://zhaozhihan.com">Linda的博客</a></li>
|
||
|
||
</ul>
|
||
</section>
|
||
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</article>
|
||
|
||
|
||
|
||
|
||
<footer>
|
||
<div class="container">
|
||
<div class="row">
|
||
<div class="col-lg-8 col-lg-offset-2 col-md-10 col-md-offset-1">
|
||
<ul class="list-inline text-center">
|
||
|
||
<li>
|
||
<a href="mailto:youremail@gmail.com">
|
||
<span class="fa-stack fa-lg">
|
||
<i class="fas fa-circle fa-stack-2x"></i>
|
||
<i class="fas fa-envelope fa-stack-1x fa-inverse"></i>
|
||
</span>
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li>
|
||
<a target="_blank" href="/your%20wechat%20qr%20code%20image">
|
||
<span class="fa-stack fa-lg">
|
||
<i class="fas fa-circle fa-stack-2x"></i>
|
||
<i class="fab fa-weixin fa-stack-1x fa-inverse"></i>
|
||
</span>
|
||
</a>
|
||
</li>
|
||
|
||
|
||
<li>
|
||
<a target="_blank" href="https://github.com/yourgithub">
|
||
<span class="fa-stack fa-lg">
|
||
<i class="fas fa-circle fa-stack-2x"></i>
|
||
<i class="fab fa-github fa-stack-1x fa-inverse"></i>
|
||
</span>
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
<li>
|
||
<a target="_blank" href="https://www.linkedin.com/in/yourlinkedinid">
|
||
<span class="fa-stack fa-lg">
|
||
<i class="fas fa-circle fa-stack-2x"></i>
|
||
<i class="fab fa-linkedin fa-stack-1x fa-inverse"></i>
|
||
</span>
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
<li>
|
||
<a target="_blank" href="https://stackoverflow.com/users/yourstackoverflowid">
|
||
<span class="fa-stack fa-lg">
|
||
<i class="fas fa-circle fa-stack-2x"></i>
|
||
<i class="fab fa-stack-overflow fa-stack-1x fa-inverse"></i>
|
||
</span>
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li>
|
||
<a href='' rel="alternate" type="application/rss+xml" title="David Young" >
|
||
<span class="fa-stack fa-lg">
|
||
<i class="fas fa-circle fa-stack-2x"></i>
|
||
<i class="fas fa-rss fa-stack-1x fa-inverse"></i>
|
||
</span>
|
||
</a>
|
||
</li>
|
||
|
||
</ul>
|
||
<p class="copyright text-muted">
|
||
Copyright © David Young 2026
|
||
|
||
<br>
|
||
<a href="https://themes.gohugo.io/hugo-theme-cleanwhite">CleanWhite Hugo Theme</a> by <a href="https://zhaohuabing.com">Huabing</a> |
|
||
<iframe
|
||
style="margin-left: 2px; margin-bottom:-5px;"
|
||
frameborder="0" scrolling="0" width="100px" height="20px"
|
||
src="https://ghbtns.com/github-btn.html?user=zhaohuabing&repo=hugo-theme-cleanwhite&type=star&count=true" >
|
||
</iframe>
|
||
|
||
</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</footer>
|
||
|
||
|
||
|
||
|
||
<script>
|
||
function loadAsync(u, c) {
|
||
var d = document, t = 'script',
|
||
o = d.createElement(t),
|
||
s = d.getElementsByTagName(t)[0];
|
||
o.src = u;
|
||
if (c) { o.addEventListener('load', function (e) { c(null, e); }, false); }
|
||
s.parentNode.insertBefore(o, s);
|
||
}
|
||
</script>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<script>
|
||
|
||
if($('#tag_cloud').length !== 0){
|
||
loadAsync("/js/jquery.tagcloud.js",function(){
|
||
$.fn.tagcloud.defaults = {
|
||
|
||
color: {start: '#bbbbee', end: '#0085a1'},
|
||
};
|
||
$('#tag_cloud a').tagcloud();
|
||
})
|
||
}
|
||
</script>
|
||
|
||
|
||
<script>
|
||
(function() {
|
||
function updateTagcloudColors() {
|
||
const isDark = document.documentElement.getAttribute('data-theme') === 'dark';
|
||
const startColor = isDark ? '#808080' : '#bbbbee';
|
||
if($('#tag_cloud').length !== 0 && $.fn.tagcloud) {
|
||
$.fn.tagcloud.defaults = {
|
||
color: {start: startColor, end: '#0085a1'},
|
||
};
|
||
$('#tag_cloud a').tagcloud();
|
||
}
|
||
}
|
||
|
||
|
||
$(document).ready(function() {
|
||
updateTagcloudColors();
|
||
});
|
||
|
||
|
||
const observer = new MutationObserver(function(mutations) {
|
||
mutations.forEach(function(mutation) {
|
||
if (mutation.attributeName === 'data-theme') {
|
||
updateTagcloudColors();
|
||
}
|
||
});
|
||
});
|
||
|
||
observer.observe(document.documentElement, {
|
||
attributes: true,
|
||
attributeFilter: ['data-theme']
|
||
});
|
||
})();
|
||
</script>
|
||
|
||
|
||
<script>
|
||
loadAsync("https://cdn.jsdelivr.net/npm/fastclick@1.0.6/lib/fastclick.min.js", function(){
|
||
var $nav = document.querySelector("nav");
|
||
if($nav) FastClick.attach($nav);
|
||
})
|
||
</script>
|
||
|
||
|
||
<script src="/js/theme-toggle.js"></script>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<script type="text/javascript">
|
||
function generateCatalog(selector) {
|
||
|
||
|
||
|
||
|
||
|
||
_containerSelector = 'div.post-container'
|
||
|
||
|
||
|
||
var P = $(_containerSelector), a, n, t, l, i, c;
|
||
a = P.find('h1,h2,h3,h4,h5,h6');
|
||
|
||
|
||
$(selector).html('')
|
||
|
||
|
||
a.each(function () {
|
||
n = $(this).prop('tagName').toLowerCase();
|
||
i = "#" + $(this).prop('id');
|
||
t = $(this).text();
|
||
c = $('<a href="' + i + '" rel="nofollow" title="' + t + '">' + t + '</a>');
|
||
l = $('<li class="' + n + '_nav"></li>').append(c);
|
||
$(selector).append(l);
|
||
});
|
||
return true;
|
||
}
|
||
|
||
generateCatalog(".catalog-body");
|
||
|
||
|
||
$(".catalog-toggle").click((function (e) {
|
||
e.preventDefault();
|
||
$('.side-catalog').toggleClass("fold")
|
||
}))
|
||
|
||
|
||
|
||
|
||
loadAsync("\/js\/jquery.nav.js", function () {
|
||
$('.catalog-body').onePageNav({
|
||
currentClass: "active",
|
||
changeHash: !1,
|
||
easing: "swing",
|
||
filter: "",
|
||
scrollSpeed: 700,
|
||
scrollOffset: 0,
|
||
scrollThreshold: .2,
|
||
begin: null,
|
||
end: null,
|
||
scrollChange: null,
|
||
padding: 80
|
||
});
|
||
});
|
||
</script>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
</body>
|
||
</html>
|