<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>负载均衡归档 - 枫阿雨&#039;s blog</title>
	<atom:link href="https://www.crazyfay.com/tag/%E8%B4%9F%E8%BD%BD%E5%9D%87%E8%A1%A1/feed/" rel="self" type="application/rss+xml" />
	<link>https://www.crazyfay.com/tag/负载均衡/</link>
	<description>CrazyFay</description>
	<lastBuildDate>Tue, 04 Apr 2023 03:00:34 +0000</lastBuildDate>
	<language>zh-CN</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.5.2</generator>

<image>
	<url>https://www.crazyfay.com/wp-content/uploads/2023/04/cropped-DockerGopher-32x32.png</url>
	<title>负载均衡归档 - 枫阿雨&#039;s blog</title>
	<link>https://www.crazyfay.com/tag/负载均衡/</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>Golang实现四种负载均衡算法</title>
		<link>https://www.crazyfay.com/2022/08/10/golang%e5%ae%9e%e7%8e%b0%e5%9b%9b%e7%a7%8d%e8%b4%9f%e8%bd%bd%e5%9d%87%e8%a1%a1%e7%ae%97%e6%b3%95/</link>
					<comments>https://www.crazyfay.com/2022/08/10/golang%e5%ae%9e%e7%8e%b0%e5%9b%9b%e7%a7%8d%e8%b4%9f%e8%bd%bd%e5%9d%87%e8%a1%a1%e7%ae%97%e6%b3%95/#respond</comments>
		
		<dc:creator><![CDATA[crazyfay]]></dc:creator>
		<pubDate>Wed, 10 Aug 2022 02:56:32 +0000</pubDate>
				<category><![CDATA[代码实战]]></category>
		<category><![CDATA[Golang]]></category>
		<category><![CDATA[负载均衡]]></category>
		<guid isPermaLink="false">http://net.crazyfay.xyz/?p=158</guid>

					<description><![CDATA[<p>负载均衡算法实现 在这篇文章中，我将实现最常见的四种负载均衡算法，即随机负载、轮询负载、加权负载和一致性has [&#8230;]</p>
<p><a rel="nofollow" href="https://www.crazyfay.com/2022/08/10/golang%e5%ae%9e%e7%8e%b0%e5%9b%9b%e7%a7%8d%e8%b4%9f%e8%bd%bd%e5%9d%87%e8%a1%a1%e7%ae%97%e6%b3%95/">Golang实现四种负载均衡算法</a>最先出现在<a rel="nofollow" href="https://www.crazyfay.com">枫阿雨&#039;s blog</a>。</p>
]]></description>
										<content:encoded><![CDATA[<h1>负载均衡算法实现</h1>
<p>在这篇文章中，我将实现最常见的四种负载均衡算法，即随机负载、轮询负载、加权负载和一致性hash负载</p>
<h2>随机负载</h2>
<p>随机挑选目标服务器ip</p>
<pre><code class="language-go">type RandomBalance struct {
   curIndex int
   rss      []string
}

// 添加新的服务ip
func (r *RandomBalance) Add(params ...string) error {
   if len(params) == 0 {
      return errors.New(&quot;param len 1 at least&quot;)
   }
   addr := params[0]
   r.rss = append(r.rss, addr)
   return nil
}

// 采用rand.Intn随机取一个服务ip
func (r *RandomBalance) Next() string {
   if len(r.rss) == 0 {
      return &quot;&quot;
   }
   r.curIndex = rand.Intn(len(r.rss))
   return r.rss[r.curIndex]
}

func (r *RandomBalance) Get(key string) (string, error) {
   return r.Next(), nil
}</code></pre>
<h2>轮询负载</h2>
<p>ABC三台服务器,A B C A B C依次轮询</p>
<pre><code class="language-go">type RoundRobinBalance struct {
   curIndex int
   rss      []string
}

func (r *RoundRobinBalance) Add(params ...string) error {
   if len(params) == 0 {
      return errors.New(&quot;param len 1 at least&quot;)
   }
   addr := params[0]
   r.rss = append(r.rss, addr)
   return nil
}

func (r *RoundRobinBalance) Next() string {
   if len(r.rss) == 0 {
      return &quot;&quot;
   }
   lens := len(r.rss)
   if r.curIndex &gt;= lens {
      r.curIndex = 0
   }
    // 保存一个服务ip的游标
   curAddr := r.rss[r.curIndex]
   r.curIndex = (r.curIndex + 1) % lens
   return curAddr
}

func (r *RoundRobinBalance) Get(key string) (string, error) {
   return r.Next(), nil
}</code></pre>
<h2>加权负载</h2>
<p>给目标设置访问权重,按照权重轮询</p>
<p><strong>nginx的加权负载均衡策略</strong></p>
<p>计算策略：</p>
<ol>
<li>currentWeight += effectiveWeight</li>
<li>选出最大的currentWeight节点作为选中节点</li>
<li>currentWeight -= totalWeight
<ul>
<li>其中effectiveWeight的值不变，只有当服务异常的时候减少</li>
</ul>
</li>
</ol>
<p><img decoding="async" src="https://img-1307890592.cos.ap-chengdu.myqcloud.com/typroa/image-20221123201123507.png" alt="image-20221123201123507" /></p>
<pre><code class="language-go">type WeightRoundRobinBalance struct {
   curIndex int
   rss      []*WeightNode
   rsw      []int
}

type WeightNode struct {
   addr            string
   weight          int //权重值
   currentWeight   int //节点当前权重
   effectiveWeight int //有效权重
}

func (r *WeightRoundRobinBalance) Add(params ...string) error {
   if len(params) != 2 {
      return errors.New(&quot;param len need 2&quot;)
   }
   parInt, err := strconv.ParseInt(params[1], 10, 64)
   if err != nil {
      return err
   }
   node := &amp;WeightNode{addr: params[0], weight: int(parInt)}
   node.effectiveWeight = node.weight
   r.rss = append(r.rss, node)
   return nil
}

// Next 参考了 nginx 的加权负载均衡的策略
func (r *WeightRoundRobinBalance) Next() string {
   total := 0
   var best *WeightNode
   for i := 0; i &lt; len(r.rss); i++ {
      w := r.rss[i]
      //step 1 统计所有有效权重之和
      total += w.effectiveWeight

      //step 2 变更节点临时权重为的节点临时权重+节点有效权重
      w.currentWeight += w.effectiveWeight

      //step 3 有效权重默认与权重相同，通讯异常时-1, 通讯成功+1，直到恢复到weight大小
      if w.effectiveWeight &lt; w.weight {
         w.effectiveWeight++
      }
      //step 4 选择最大临时权重点节点
      if best == nil || w.currentWeight &gt; best.currentWeight {
         best = w
      }
   }
   if best == nil {
      return &quot;&quot;
   }
   //step 5 变更临时权重为 临时权重-有效权重之和
   best.currentWeight -= total
   return best.addr
}

func (r *WeightRoundRobinBalance) Get(key string) (string, error) {
   return r.Next(), nil
}</code></pre>
<h2>一致性hash负载</h2>
<p>请求固定URL访问指定IP</p>
<pre><code class="language-go">type Hash func(data []byte) uint32

type UInt32Slice []uint32

func (s UInt32Slice) Len() int {
   return len(s)
}

func (s UInt32Slice) Less(i, j int) bool {
   return s[i] &lt; s[j]
}

func (s UInt32Slice) Swap(i, j int) {
   s[i], s[j] = s[j], s[i]
}

type ConsistentHashBanlance struct {
   mux      sync.RWMutex
   hash     Hash
   replicas int               // 复制因子
   keys     UInt32Slice       // 已排序的节点hash切片
   hashMap  map[uint32]string // 节点哈希和Key的map,键是hash值，值是节点key
}

func NewConsistentHashBanlance(replicas int, fn Hash) *ConsistentHashBanlance {
   m := &amp;ConsistentHashBanlance{
      replicas: replicas,
      hash:     fn,
      hashMap:  make(map[uint32]string),
   }
   if m.hash == nil {
      //最多32位,保证是一个2^32-1环
      m.hash = crc32.ChecksumIEEE
   }
   return m
}

// 验证是否为空
func (c *ConsistentHashBanlance) IsEmpty() bool {
   return len(c.keys) == 0
}

// Add 方法用来添加缓存节点，参数为节点key，比如使用IP
func (c *ConsistentHashBanlance) Add(params ...string) error {
   if len(params) == 0 {
      return errors.New(&quot;param len 1 at least&quot;)
   }
   addr := params[0]
   c.mux.Lock()
   defer c.mux.Unlock()
   // 结合复制因子计算所有虚拟节点的hash值，并存入m.keys中，同时在m.hashMap中保存哈希值和key的映射
   for i := 0; i &lt; c.replicas; i++ {
      hash := c.hash([]byte(strconv.Itoa(i) + addr))
      c.keys = append(c.keys, hash)
      c.hashMap[hash] = addr
   }
   // 对所有虚拟节点的哈希值进行排序，方便之后进行二分查找
   sort.Sort(c.keys)
   return nil
}

// Get 方法根据给定的对象获取最靠近它的那个节点
func (c *ConsistentHashBanlance) Get(key string) (string, error) {
   if c.IsEmpty() {
      return &quot;&quot;, errors.New(&quot;node is empty&quot;)
   }
   hash := c.hash([]byte(key))

   // 通过二分查找获取最优节点，第一个&quot;服务器hash&quot;值大于&quot;数据hash&quot;值的就是最优&quot;服务器节点&quot;
   idx := sort.Search(len(c.keys), func(i int) bool { return c.keys[i] &gt;= hash })

   // 如果查找结果 大于 服务器节点哈希数组的最大索引，表示此时该对象哈希值位于最后一个节点之后，那么放入第一个节点中
   if idx == len(c.keys) {
      idx = 0
   }
   c.mux.RLock()
   defer c.mux.RUnlock()
   return c.hashMap[c.keys[idx]], nil
}

func (c *ConsistentHashBanlance) SetConf(conf LoadBalanceConf) {
   c.conf = conf
}</code></pre>
<p><a rel="nofollow" href="https://www.crazyfay.com/2022/08/10/golang%e5%ae%9e%e7%8e%b0%e5%9b%9b%e7%a7%8d%e8%b4%9f%e8%bd%bd%e5%9d%87%e8%a1%a1%e7%ae%97%e6%b3%95/">Golang实现四种负载均衡算法</a>最先出现在<a rel="nofollow" href="https://www.crazyfay.com">枫阿雨&#039;s blog</a>。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.crazyfay.com/2022/08/10/golang%e5%ae%9e%e7%8e%b0%e5%9b%9b%e7%a7%8d%e8%b4%9f%e8%bd%bd%e5%9d%87%e8%a1%a1%e7%ae%97%e6%b3%95/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
