分布式存储系统使用kubernetes部署
环境搭建
快速搭建k3s环境
1234567891011121314151617# 关闭防火墙systemctl disable firewalld --now# 下载 k3s 安装脚本:curl -sfL https://get.k3s.io -o install.sh# 运行安装脚本:sudo sh install.sh# 等待安装完成后,通过以下命令检查 k3s 是否已成功安装:sudo systemctl status k3s# 若要将当前用户添加到 Kubernetes 集群的 kubeconfig 文件中,以便能够使用 kubectl 命令行工具连接到集群,运行以下命令:sudo cat /etc/rancher/k3s/k3s.yaml >> ~/.kube/config# 确认是否已成功添加到 kubeconfig 文件中,运行以下命令:kubectl get nodes
将其他节点加入到kubernetes集群中
12345# 查看 Kubernetes 集群主节点的 token cat /var/lib/rancher/k3s/server/node- ...
力扣339场周赛(力扣题解)
T1题目链接
题目描述:给你一个仅由 0 和 1 组成的二进制字符串 s 。
如果子字符串中 所有的 0 都在 1 之前 且其中 0 的数量等于 1 的数量,则认为 s 的这个子字符串是平衡子字符串。请注意,空子字符串也视作平衡子字符串。
返回 s 中最长的平衡子字符串长度。
子字符串是字符串中的一个连续字符序列。
题解思路:暴力遍历字符串数组,直接找连续的0和连续的1(01连续中0字符串和1字符串最小长度*2),并记录最大值即可
代码:123456789101112func findTheLongestBalancedSubstring(s string) int { res := 0 for i := 0; i < len(s);{ x, y := 0, 0 for i < len(s) && s[i] == '0' {x++; i++} for i < len(s) && s[i] == '1' ...
力扣336场周赛(力扣题解)
T1题目链接
题目描述:给你一个下标从 0 开始的字符串数组 words 和两个整数:left 和 right 。
如果字符串以元音字母开头并以元音字母结尾,那么该字符串就是一个 元音字符串 ,其中元音字母是 'a'、'e'、'i'、'o'、'u' 。
返回 words[i] 是元音字符串的数目,其中 i 在闭区间 [left, right] 内。
题解思路:暴力遍历字符串数组,直接查看前后两个字母是否是元音字母。
代码:12345678910111213141516171819202122232425func vowelStrings(words []string, left int, right int) int { res := 0 mp := map[byte]bool{ 'a': true, 'e': true, 'i': true, 'o& ...
146-LRU 缓存(力扣题解)
题目描述请你设计并实现一个满足 LRU (最近最少使用) 缓存 约束的数据结构。
实现 LRUCache 类:
LRUCache(int capacity) 以 正整数 作为容量 capacity 初始化 LRU 缓存
int get(int key) 如果关键字 key 存在于缓存中,则返回关键字的值,否则返回 -1 。
void put(int key, int value) 如果关键字 key 已经存在,则变更其数据值 value ;如果不存在,则向缓存中插入该组 key-value 。如果插入操作导致关键字数量超过 capacity ,则应该 逐出 最久未使用的关键字。
函数 get 和 put 必须以 O(1) 的平均时间复杂度运行。
输入输出12345输入["LRUCache", "put", "put", "get", "put", "get", "put", "get", "get", & ...
6-防止缓存击穿与protobuf通信(Go实现分布式缓存)
防止缓存击穿什么是缓存雪崩、击穿、穿透?缓存雪崩当大量缓存数据在同一时间过期(失效)或者 Redis 故障宕机时,如果此时有大量的用户请求,都无法在 Redis 中处理,于是全部请求都直接访问数据库,从而导致数据库的压力骤增,严重的会造成数据库宕机,从而形成一系列连锁反应,造成整个系统崩溃,这就是缓存雪崩的问题。
缓存击穿如果缓存中的某个热点数据过期了,此时大量的请求访问了该热点数据,就无法从缓存中读取,直接访问数据库,数据库很容易就被高并发的请求冲垮,这就是缓存击穿的问题。
缓存穿透当用户访问的数据,既不在缓存中,也不在数据库中,导致请求在访问缓存时,发现缓存缺失,再去访问数据库时,发现数据库中也没有要访问的数据,没办法构建缓存数据,来服务后续的请求。那么当有大量这样的请求到来时,数据库的压力骤增,这就是缓存穿透的问题。
使用 singleflight 防止缓存击穿
定义请求对象
12345type call struct { wg sync.WaitGroup // 控制线程是否等待 val interface{} // 请求返回结果 er ...
5-多节点间的通信(Go实现分布式缓存)
多节点间的通信远程访问流程
当 客户端 发送一个查询请求达到某个缓存节点时, 该节点会先判断 key 是否在本地, 不在的话, 再通过发送网络请求去访问其他 node 节点。
每个 node 既要处理来自客户端这样的外部请求, 也要处理来自其他远端节点的内部请求。
我们需要在 node 内部, 启动两个 http 服务, 一个处理客户端请求(APIServer), 一个处理节点的请求(CacheServer).
定义一个查询节点的方法
12345type PeerPicker interface { // PickPeer 于根据传入的 key 选择相应节点(选择相应的节点方法) PickPeer(key string) (peer PeerGetter, ok bool)}
通过网络请求帮我们拿到缓存结果
1234// 这个接口为我们提供需要的能力.type PeerGetter interface { Get(group string, key string) ([]byte, error)}
实现 PeerGetter# ...
4-一致性哈希(Go实现分布式缓存)
一致性哈希普通hash算法普通的hash算法在分布式应用中的不足
在分布式的存储系统中,要将数据存储到具体的节点上,如果我们采用普通的hash算法进行路由,将数据映射到具体的节点上,如key%N,key是数据的哈希值,N是服务器节点数。
如果有一个节点加入或退出这个集群,也就意味着几乎缓存值对应的节点都发生了改变。即几乎所有的缓存值都失效了。节点在接收到对应的请求时,均需要重新去数据源获取数据,容易引起 缓存雪崩。
一致性哈希一致性哈希原理哈希算法是对节点的数量进行取模运算,而一致性哈希是对2^32进行取模运算。一致性哈希将整个哈希值空间组成一个虚拟的圆环,也就是哈希环。
计算节点/机器(通常使用节点的名称、编号和 IP 地址)的哈希值,放置在环上。
计算 key 的哈希值,放置在环上,顺时针寻找到的第一个节点,就是应选取的节点/机器。
在一致性哈希算法中,如果某个节点宕机不可用了,那么受影响的数据仅仅是会寻址到此节点和前一节点之间的数据。
一致性哈希数据倾斜在一致性哈希算法中,如果节点太少,容易因为节点分布不均匀造成数据访问的冷热不均,也就是说大多数访问请求都会集中少量几个节点 ...
3-基于HTTP的分布式缓存(Go实现分布式缓存)
基于HTTP的分布式缓存目的:通过main 函数启动 HTTP Server测试API
Cache HTTP 服务端分布式缓存需要实现节点间通信,建立基于 HTTP 的通信机制是比较常见和简单的做法。如果一个节点启动了 HTTP 服务,那么这个节点就可以被其他节点访问。
具体流程
默认通信地址前缀是defaultBasePath = "/_gocache/"。
首先判断url路径中是否包含 basePath。
把groupname/key字符截断为groupname和key。
通过groupname获取Group对象。
使用Group对象方法和key来获取key对应的缓存值。
将缓存值作为http body进行响应。
代码http.go123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354package Cacheimport ( "net/http" "strings")const ...
2-实现单机缓存并发控制(Go实现分布式缓存)
单机缓存并发控制通过互斥锁Mutex实现 LRU 缓存的并发控制封装一个只读数据结构
使用[]byte,支持各种数据类型。
封装一个ByteView结构体,并且在返回时,返回一个拷贝,防止缓存值被外部程序修改。
byteview.go
1234567891011121314151617181920212223242526272829303132package Cache/*缓存值的抽象与封装*/// ByteView 只读数据结构type ByteView struct { b []byte // 存储真实缓存值}// Len 返回其所占的内存大小。func (v ByteView) Len() int { return len(v.b)}// ByteSlice 返回一个拷贝,防止缓存值被外部程序修改func (v ByteView) ByteSlice() []byte { return cloneBytes(v.b)}// String 以字符串形式返回数据func (v ByteView) String() stri ...
1-实现LRU缓存淘汰策略(Go实现分布式缓存)
简介为什么需要分布式缓存
减少数据库压力:缓存数据一般是在内存中,而数据库中的数据一般是在磁盘上,二者的存储速度有着非常大的差距,数据库的操作很耗时,对于一些热点数据,一般都会被暂存在分布式缓存服务器集群中,减轻数据库压力。
优化请求响应时间:如果请求命中缓存,就会直接返回,速度较快,不用再去请求数据库。
支持高可用:如果缓存时单点服务,那这台服务器宕机之后,就不能继续进行缓存服务。如果缓存服务器没有数据分片的能力,那么当某个热点key所在的服务器宕机后,容易出现缓存击穿问题。
项目介绍 这个项目模仿了 groupcache 的实现。
采用最近最少访问算法进行缓存淘汰。
实现了单机缓存和基于HTTP的分布式缓存。
使用Go锁机制防止缓存击穿。
使用一致性哈希选择节点,实现负载均衡。
使用protobuf优化节点之间二进制通信。
通过学习这个项目去了解分布式缓存如何设计,体会go语言的精巧,也感受一下设计之美。
实现LRU缓存淘汰策略淘汰策略一般常见的缓存淘汰策略有:FIFO,LFU 和 LRU。
FIFOFIFO是先进先出,淘汰缓存中最老的记录。
原因:最早添加 ...