从https://mp.weixin.qq.com/s/OIHqmgK4V7Y26uYoFjsCyA 复制
Golang 实现SOLID 设计原则
本章节按照设计模式中的核心设计原则介绍在Go语言中的实现。
单一职责原则
类的设计尽量做到只有一个原因引起变化。 在交易的场景中,我们需要做一些交易存储、验证,我们可以声明交易的结构体,这个结构体是为了存储每笔交易。但是验证的功能我们可以拆开,这样代码更具有维护性、测试的编写也更简单方便。
type Trade struct {
TradeID int
Symbol string
Quantity float64
Price float64
}
type TradeRepository struct {
db *sql.DB
}
func (tr *TradeRepository) Save(trade *Trade) error {
_, err := tr.db.Exec("INSERT INTO trades (trade_id, symbol, quantity, price) VALUES (?, ?, ?, ?)", trade.TradeID, trade.Symbol, trade.Quantity, trade.Price)
if err != nil {
return err
}
return nil
}
type TradeValidator struct {}
func (tv *TradeValidator) Validate(trade *Trade) error {
if trade.Quantity <= 0 {
return errors.New("Trade quantity must be greater than zero")
}
if trade.Price <= 0 {
return errors.New("Trade price must be greater than zero")
}
return nil
}
开闭原则
对扩展开放,对修改关闭。实现常见的方法是,通过接口或者多态继承。 当我们的系统要增加期权交易的功能时,我们可以扩展接口实现,声明TradeProcessor,而不是在声明一个统一的处理器中,在里面写各种的兼容逻辑。
K8s相关
ovs controller
Istio
可以参考的链接
https://developer.aliyun.com/article/759790
https://zhuanlan.zhihu.com/p/499341027
ovs and iptables
https://www.cnblogs.com/jmilkfan-fanguiju/p/11825035.html
http://www.openvswitch.org/support/dist-docs/ovs-vsctl.8.txt
macVlan
https://icloudnative.io/posts/netwnetwork-virtualization-macvlan/
https://www.cnblogs.com/bakari/p/10893589.html
svc 的不同类型 NodePort等
Go
defer
nil
单例模式
struct 是否可以比较
new和make的区别
内存对齐
版权声明:本文为CSDN博主「我想要身体健康」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上> 原文出处链接及本声明。 原文链接:https://blog.csdn.net/m0_57236802/article/details/131249491
如果你想强制 git pull 来覆盖本地的更改,你需要注意这个过程会删除所有你在本地做的更改,并将你的本地分支同步到远程分支。如果你想要这样做,可以使用以下的命令:
git fetch --all
git reset --hard origin/<branch_name>
在上面的 <branch_name>
中填写你想要同步的远程分支的名字。比如,如果你想要同步的分支是 master,你可以运行:
git fetch --all
git reset --hard origin/master
第一条命令 git fetch --all
会从远程仓库获取所有分支的最新更改,但是并不会修改你的本地仓库。
第二条命令 git reset --hard origin/<branch_name>
会将你的本地分支重置到远程分支的状态,这会删除所有的本地更改。
再次提醒,这个过程会丢失所有未提交的本地更改,所以在使用这个命令之前一定要确认你是否真的需要这样做。
非原创
好文:https://yizhi.ren/2019/06/03/goscheduler
参考:https://blog.csdn.net/xmcy001122/article/details/119392934
Go 语言的协程 goroutine
Go 为了提供更容易使用的并发方法,使用了 goroutine 和 channel。goroutine 来自协程的概念,让一组可复用的函数运行在一组线程之上,即使有协程阻塞,该线程的其他协程也可以被 runtime 调度,转移到其他可运行的线程上。最关键的是,程序员看不到这些底层的细节,这就降低了编程的难度,提供了更容易的并发。
Go 中,协程被称为 goroutine,它非常轻量,一个 goroutine 只占几 KB,并且这几 KB 就足够 goroutine 运行完,这就能在有限的内存空间内支持大量 goroutine,支持了更多的并发。虽然一个 goroutine 的栈只占几 KB,但实际是可伸缩的,如果需要更多内容,runtime 会自动为 goroutine 分配。
Goroutine 特点:
- 占用内存更小(2KB左右,系统线程需要1-8MB)
- 调度更灵活(runtime 调度)
调度器
被废弃的 goroutine 调度器 - GM模型
Go 目前使用的调度器是 2012 年重新设计的,因为之前的调度器性能存在问题,所以使用 4 年就被废弃了,那么我们先来分析一下被废弃的调度器是如何运作的?
Go的调度程序是Go运行时的一个更重要的方面。运行时会跟踪每个Goroutine,并将安排它们在线程池中运行。goroutines与线程分离(解耦不强绑定),但运行于线程之上。如何有效地将goroutine调度到线程上对于go程序的高性能至关重要。
Goroutines的背后逻辑是:它们能够同时运行,与线程类似,但相比之下非常轻量。因此,程序运行时,Goroutines的个数应该是远大于线程的个数的。
同时多线程在程序中是很有必要的,因为当goroutine调用了一个阻塞的系统调用,比如sleep,那么运行这个goroutine的线程就会被阻塞,那么这时运行时至少应该再创建一个线程来运行别的没有阻塞的goroutine。线程这里可以创建不止一个,可以按需不断地创建,而活跃的线程(处于非阻塞状态的线程)的最大个数存储在变量GOMAXPROCS中。
简要说明
go运行时使用3个结构来跟踪所有成员来支持调度器的工作。
G的结构:
一个G代表一个goroutine,包含当前栈,当前状态和函数体。
struct G
{
byte∗ stackguard; // stack guard information
byte∗ stackbase; // base of stack
byte∗ stack0; // current stack pointer
byte∗ entry; // initial function
void∗ param; // passed parameter on wakeup
int16 status; // status
int32 goid; // unique id
M∗ lockedm; // used for locking M’s and G’s
...
}
M:
如下命令向所有的SCSI主机发送一个扫描请求,可以帮助系统重新检测新添加的硬盘。
[root@compute1 ~]# echo "- - -" > /sys/class/scsi_host/host0/scan
[root@compute1 ~]# echo "- - -" > /sys/class/scsi_host/host1/scan
[root@compute1 ~]# echo "- - -" > /sys/class/scsi_host/host2/scan
现象
系统文件损坏后进入紧急修复模式,无法进行维护工作
welcome to emergency mode!after logging in ,type “journalctl -xb” to view system logs,“systemctl reboot” to reboot ,“systemctl default” to try again to boot into default mode。
give root password for maintenance
查看运行日志,发现是/home目录挂载问题
journalctl -xb
# Failed to mount /home
修复
执行如下命令,确认/home
的映射关系
[root@controller ~]# cat /etc/fstab |grep home
/dev/mapper/centos-home /home xfs defaults 0 0
方法一
xfs_repair /dev/mapper/centos-home
如果修复失败,请加-L参数
xfs_repair -L /dev/mapper/centos-home
如果修复完成,重启系统即可
方法二
如果方法一失效,则可以试试该方法
- 检查设备是否已经挂载。可以运行以下命令来查看设备的挂载状态:
df -h
2. 如果设备已挂载,请尝试卸载该设备。可以使用以下命令卸载设备:
umount /dev/mapper/centos-home
3. 创建一个目标目录,用于将备份数据存储到该目录中。可以使用以下命令创建目标目录:
go server以相当优雅的姿势退出(待继续完善)
package main
import (
"context"
"fmt"
"log"
"net/http"
"os"
"os/signal"
"syscall"
"time"
)
type H struct{}
func (H) ServeHTTP(w http.ResponseWriter, r *http.Request) {
time.Sleep(5 * time.Second)
fmt.Fprintf(w, "Hello, World!") // 向客户端发送响应
}
func initServer() *http.Server {
s := &http.Server{
Addr: ":8080",
Handler: H{},
}
return s
}
type Backend struct {
Ctx context.Context
Srv *http.Server
CancelFunc func()
}
var onlyOneSignalHandler = make(chan struct{})
var shutdownSignals = []os.Signal{os.Interrupt, syscall.SIGTERM}
var shutdownHandler chan os.Signal
// SetupSignalContext is same as SetupSignalHandler, but a context.Context is returned.
// Only one of SetupSignalContext and SetupSignalHandler should be called, and only can
// be called once.
func (b *Backend) SetupSignalContext() context.Context {
close(onlyOneSignalHandler) // panics when called twice
shutdownHandler = make(chan os.Signal, 2)
signal.Notify(shutdownHandler, shutdownSignals...)
go func() {
<-shutdownHandler
b.Clean()
<-shutdownHandler
os.Exit(1) // second signal. Exit directly.
}()
return b.Ctx
}
// SetupSignalHandler registered for SIGTERM and SIGINT. A stop channel is returned
// which is closed on one of these signals. If a second signal is caught, the program
// is terminated with exit code 1.
// Only one of SetupSignalContext and SetupSignalHandler should be called, and only can
// be called once.
func (b *Backend) SetupSignalHandler() <-chan struct{} {
return b.SetupSignalContext().Done()
}
func (b *Backend) RunAndServe(stopCh <-chan struct{}) {
b.Srv.ListenAndServe()
<-stopCh
if err := b.Srv.Shutdown(b.Ctx); err != nil {
log.Fatal("Server Shutdown:", err)
}
log.Printf("initServer exiting")
}
func (b *Backend) Clean() {
fmt.Println("do some clean")
time.Sleep(time.Second * 10)
b.CancelFunc()
fmt.Println("clean over")
b.Ctx.Done()
}
func main() {
ctx, cancel := context.WithCancel(context.Background())
simpleBackend := Backend{
Ctx: ctx,
Srv: initServer(),
CancelFunc: cancel,
}
simpleBackend.RunAndServe(simpleBackend.SetupSignalHandler())
}
David Hooker 提出7个关注软件工程整体实践的原则
- 存在价值
在确定系统需求前,在关注系统功能前,在决定硬件平台或者开发过程之前,问问自己:这确实能为系统增加真正的价值吗? 如果答案是不,那就坚决不做。
- 保持简洁
所有的设计都应该尽可能简洁,但不是过于简化。
- 保持愿景
有着清晰的目的,明确的思想
- 关注使用者
需求说明时:想到用户怎么用 设计中:想到怎么实现 编码时:想到怎么维护
- 面向未来
永远不要把自己的设计局限于一隅,经常问问“如果出现……应该怎么应对”,构建可以解决通用问题的系统
-
提前计划复用
-
认真思考
行动之前清晰定位、完整思考
cilium组件
〉 搬运自:https://blog.csdn.net/lianhunqianr1/article/details/124977297
eBPF特性
eBPF程序都是事件驱动的,他们会在内核或者应用程序经过某个确定的Hook点的时候运行,这些Hook点都是提前定义的,包括系统调用、函数进入/退出、内核tracepoints、网络事件等。
如果针对某个特定需求的Hook点不存在,可以通过kprobe或者uprobe来在内核或者用户程序的几乎所有地方挂载eBPF程序。
Verification
每个eBPF程序加载到内核都要经过Verification,用来保证eBPF程序的安全性,主要有:
-
要保证 加载 eBPF 程序的进程有必要的特权级,除非节点开启了 unpriviledged 特性,只有特权级的程序才能够加载 eBPF 程序
-
内核提供了一个配置项 /proc/sys/kernel/unprivileged_bpf_disabled 来禁止非特权用户使用 bpf(2) 系统调用,可以通过 sysctl 命令修改
-
比较特殊的一点是,这个配置项特意设计为一次性开关(one-time kill switch), 这意味着一旦将它设为 1,就没有办法再改为 0 了,除非重启内核
-
一旦设置为 1 之后,只有初始命名空间中有 CAP_SYS_ADMIN 特权的进程才可以调用 bpf(2) 系统调用 。Cilium 启动后也会将这个配置项设为 1:
$ echo 1 > /proc/sys/kernel/unprivileged_bpf_disabled
-
-
要保证 eBPF 程序不会崩溃或者使得系统出故障
-
要保证 eBPF 程序不能陷入死循环,能够 runs to completion
-
要保证 eBPF 程序必须满足系统要求的大小,过大的 eBPF 程序不允许被加载进内核
-
要保证 eBPF 程序的复杂度有限,Verifier 将会评估 eBPF 程序所有可能的执行路径,必须能够在有限时间内完成 eBPF 程序复杂度分析
JIT Compilation
Just-In-Time(JIT)编译用来将通用的eBPF字节码翻译成与机器相关的指令集,从而极大加速BPF程序的执行:
- 与解释器相比,他们可以降低每个指令的开销。
- 减少生成的可执行镜像的大小,因此对CPU的指令缓存更友好
- 特别的,对于CISC指令集(例如x86),JIT做了很多优化,目的是为给定的指令产生可能的最短操作码,以降低程序翻译过程所需要的空间。
64位的 x86_64、arm64、ppc64、s390x、mips64、sparc64 和 32 位的 arm 、x86_32 架构都内置了 in-kernel eBPF JIT 编译器,它们的功能都是一样的,可以用如下方式打开: