Viper读取自定义远程配置

实现viper.remoteConfigFactory接口 type remoteConfigProvider struct{} //这个接口是在第一次读取远程配置时调用,viper.ReadRemoteConfig() func (rc remoteConfigProvider) Get(rp viper.RemoteProvider) (io.Reader, error) { log.Println("Getting config from remote", rp) r := bytes.NewReader([]byte("{\"a\":1}")) time.Sleep(time.Second) return r, nil } //这个接口是在客户端调用viper.WatchRemoteConfig(), 只监听一次变化 func (rc remoteConfigProvider) Watch(rp viper.RemoteProvider) (io.Reader, error) { //这里写你获取配置的方法 log.Println("Watching config from remote", rp) s := fmt.Sprintf(`{"app":{"name":"test","version":%d}}`, time.Now().Unix()) r := bytes.NewReader([]byte(s)) time.Sleep(time.Second) return r, nil } //这个接口是在客户端调用viper.GetViper().WatchRemoteConfigOnChannel()时,监听多次变化 func (rc remoteConfigProvider) WatchChannel(rp viper.RemoteProvider) (<-chan *viper.RemoteResponse, chan bool) { log.Println("Watching Channel config from remote", rp) ch := make(chan *viper.RemoteResponse) go func() { //defer close(ch) viper里面使用死循环来处理,如果关闭管道会返回nil, 而viper没有处理,会报painc,所以不用关闭,viper是想让你在整个生命周期都监听 //这里写你获取配置的方法 for i := 0; ; i++ { ch <- &viper.RemoteResponse{ Value: []byte(fmt.Sprintf(`{"app":{"name":"test","version":%d}}`, time.Now().Unix())), } time.Sleep(time.Second) } }() return ch, nil } 设置viper必要参数 func init() { viper.RemoteConfig = remoteConfigProvider{} //把我们写的provider设置好,否则监听不会生效 viper.SupportedRemoteProviders = []string{"app"} //这里名称需要设置好,和下面添加的名称需要一致 } 使用viper viper.SetConfigType("json") //需要和返回的数据格式一致,不然无法解析 viper.AddRemoteProvider("app", ":8080", "/aabb/cc") //app就是上面的SupportedRemoteProviders,viper内部会校验是否包含,后面两个参数根据自己的需求来填写 viper.ReadRemoteConfig() //加载远程配置 // viper.WatchRemoteConfig() //一次性监听配置,阻塞 viper.GetViper().WatchRemoteConfigOnChannel() //一直监听,非阻塞

<span title='2023-09-15 15:56:44 +0800 +0800'>九月 15, 2023</span>

在docker中go编译应注意的问题

1.使用golang:tag 这种镜像编译后在alpine中会报错(not found) 原因是:go默认会使用glibc,而alpine中没有glibc,所以会报错 解决办法有三种: 连接RUN mkdir /lib64 && ln -s /lib/libc.musl-x86_64.so.1 /lib64/ld-linux-x86_64.so.2,建立一个glic链接 使用golang:tag-alpine镜像编译,编译系统和运行系统都用alpine 编译时禁用CGO CGO_ENABLED=0 go build编译出来的文件不依赖动态库

<span title='2023-09-07 11:17:42 +0800 +0800'>九月 7, 2023</span>

Ubuntu当开发过程中主力机遇到的问题

使用deb包安装软件时想要拆卸 在使用dpkg -r拆卸软件需要知道包名,有的时候不好查,可以使用用Ubuntu Software打开deb包,点删除 安装virtualbox,启动虚拟机报 Kernel driver not installed (rc=-1908) 可以使用sudo apt install --reinstall linux-headers-$(uname -r) virtualbox-dkms dkms 安装完成后重启 使用clash for window时,无法正常代理 在ubuntu设置中,点击网络,设置网络代理配置好即可 使用拼音输入法 安装fcitx5 sudo apt install fcitx5 \ fcitx5-chinese-addons \ fcitx5-frontend-gtk4 fcitx5-frontend-gtk3 fcitx5-frontend-gtk2 \ fcitx5-frontend-qt5 安装词库 在https://github.com/felixonmars/fcitx5-pinyin-zhwiki/releases下载.dict结尾的词库文件,放入 ~/.local/share/fcitx5/pinyin/dictionaries/,没有创建即可。 设置默认输入法im-config命令 配置环境变量/etc/profile export XMODIFIERS=@im=fcitx export GTK_IM_MODULE=fcitx export QT_IM_MODULE=fcitx 在home目录.pam_environment添加配置 GTK_IM_MODULE DEFAULT=fcitx QT_IM_MODULE DEFAULT=fcitx XMODIFIERS DEFAULT=@im=fcitx SDL_IM_MODULE DEFAULT=fcitx 非root用户无法使用80端口 通过执行sudo sysctl -w net.ipv4.ip_unprivileged_port_start=80命令,你在更改了系统配置文件,指定了非特权用户可以使用的TCP/UDP端口号的起始值为80。这意味着普通用户可以绑定和使用80端口,而无需特权身份(如root或管理员权限)。 不过这样重启后就无法使用80端口了,所以需要配置一个重启后也生效的配置文件,打开/etc/sysctl.conf文件,在文件后面追加 net.ipv4.ip_unprivileged_port_start = 80,这样重启后也可以生效。

<span title='2023-08-14 15:08:46 +0800 +0800'>八月 14, 2023</span>

内存泄漏

因一次线上内存泄漏问题的总结 go 内存泄漏大概有一下几种情况 长期存活的对象:如果某些对象的生命周期较长,但在使用完之后没有被正确释放,这些对象将一直保留在内存中,导致内存泄漏。 协程泄漏:在 Go 中,每个协程(goroutine)都是轻量级的并发执行单元。如果你启动了太多的协程并且没有适时关闭或终止它们,那么这些协程就会一直存在并占用内存,导致内存泄漏。 循环引用:如果存在对象之间的循环引用关系,即两个或多个对象相互引用,而没有其他的引用指向它们,这将阻止垃圾回收器回收这些对象,导致内存泄漏。 意外的全局变量引用:如果你在函数中意外保留了对全局变量的引用,即使函数执行完毕,这个函数所涉及的内存也无法释放,导致内存泄漏。 未关闭的文件或网络连接:如果在读写文件、网络通信或数据库连接等操作后,忘记关闭这些资源,将会导致系统资源泄漏,最终影响内存的使用。 不正确的缓存管理:当使用缓存来存储数据时,如果没有合适地管理缓存的大小和生命周期,可能会导致过多的对象一直存在于缓存中而无法被释放,从而引发内存泄漏。 未正确使用释放资源的函数:在使用第三方库或 API 时,如果没有按照正确的方式使用和释放资源,比如数据库连接或操作系统句柄等,将导致资源泄漏,可能会间接导致内存泄漏。 这次出问题的是未关闭网络连接(集群内其他服务)没有关闭http Response.Body,导致这两个程序内存一直增长,一个服务内存泄漏导致另一个服务goroutine无法释放,到达k8s内存上限被kill掉 排查方法 通过代码pprof抓取相应数据 导入net/http/pprof自动注册内置好的处理方法,开启默认http服务 我们主要看第二种,第一种可以自己看net/http/pprof里面的写法或者参考文档 用浏览器查看http[s]://xxx/debug/pprof页面,一共有如下几个指标 allocs: 内存过往统计,可以看运行过程中哪些过程使用内存最多 block: 看有哪些被阻塞了,比如各种io cmdline: 运行程序时的名称和参数 比如`./app a b c` goroutine: goroutine信息 heap: 在用堆内存信息 mutex: 锁信息 profile: CPU信息 threadcreate: 系统线程创建信息 trace: 追踪信息,比如GC, 各个goroutine调度等 其中allocs/block/goroutine/heap/mutex/profile 都可以使用go tool pprof -http=":8080" http[s]://host:port/debug/pprof/xxxxx(其中xxxxx用前面几种指标代替) 查看详细信息 trace需要先点击trace下载,然后使用go tool trace tracefile来查看

<span title='2023-08-03 09:57:26 +0800 +0800'>八月 3, 2023</span>

51单片机注意事项

下面的51均指的是STC89C52RC芯片,别的51是否也是这样有待验证 io相关注意事项 51在读取io时需要设置io口为高电平,然后再读取 8051(包括 STC89C52RC)的 I/O 口是开漏(open-drain)输出而非推挽(push-pull)输出。 开漏输出只能主动驱动低电平(0),不能主动驱动高电平(1)。要实现高电平,开漏输出需要通过外部的拉升电阻(通常连接到电源 VCC)实现。这也就是为什么在使用开漏输出的 GPIO 引脚时,你需要首先将它设置为高电平,然后才可以正确读取输入的低电平。 相比之下,推挽输出可以主动驱动低电平和高电平。这就使得在使用推挽输出时,不需要首先设定为高电平来正确读取低电平。 这就是为什么你在使用 STC89C52RC 或者其他 8051 系列的微控制器时,必须首先将引脚设为高电平,只有这样,你才能正确地读取被设为低电平的输入。因此,理解开漏和推挽输出的区别对于理解这个问题是非常重要的。每种类型的输出方式都有其优缺点,需要根据具体应用来选择。 内置EEPROM 在写入前需要先擦除 原因是内部的EEPROM是用Flash来做的,由于Flash的物理性质,只能从1变0,不能从0变1,所以先要擦除为0xFF, 然后在写入相应的数据,注意擦除是按扇区来擦除的 Flash存储器使用了浮动栅层的原理来存储数据,在晶体管中存储了电荷量,代表了0或1的状态。当需要将存储单元从1改为0时,可以通过施加适当的电压,将电荷从浮动栅层中移除,使得晶体管的导通特性被改变为阻断状态,表示0。 然而,将存储单元从0改为1是不可行的,因为在Flash存储器中,写入操作是通过电子隧穿来实现的。当试图将存储单元从0改为1时,需要将电子引入浮动栅层。但是,由于浮动栅层与控制栅层之间的绝缘层,电子无法通过正常的电子隧穿过程引入到浮动栅层中,因此无法实现将0写成1的操作。 为了将存储单元从0改为1,需要执行擦除操作。擦除会将整个存储单元中的电荷移除,将其重置为初始状态,通常是全1。然后再通过编程操作将所需的位写入为1。 由于擦除操作会擦除整个存储单元的数据,而不是单个位或字节,因此对Flash存储器进行写入操作时,通常需要将整个存储块或页擦除,然后再编程所需的位。这种擦除和编程的特性使得Flash存储器在数据写入方面有一定的限制和特殊性。 LCD1602异常 是否是每个阶段的延迟不够 调高延迟试一下是否正常 多写一些字符 如果出现屏幕位置不同,可能是算法问题 乱码(比如全屏写'1’,但是显示的是其他内容) 可能是虚焊 判断方法: 写一个程序,使io口高低电平切换,如果使用示波器可以间隔短点间隔时间, 如果使用万用表让间隔长一点比如5s切换一次电平 使用示波器或者万用表分别测量每个io对应的LCD接口,看是否有电平切换, 可以判断接口是否异常 再看芯片对应异常的io引脚如果输出是正常,则说明虚焊,如果引脚输出异常,则可能是芯片坏了

<span title='2023-07-27 16:14:07 +0800 +0800'>七月 27, 2023</span>

三极管集电极电流产生原理

三极管作为一种重要的半导体器件,其集电结反向偏置时表现出的导电性违背了PN结的传统认知,这一现象的原因一直令人感到好奇。在本文中,笔者将通俗解释三极管反向偏置下集电结仍可导电的科学机理。 我们知道,PN结反向偏置时,少数载流子形成的漏电流非常微弱。但三极管的情况则完全不同,反向偏置的集电结也会产生明显的集电电流Ic。这主要归因于三极管独特的内部结构。 需要强调的是,这种反向偏置下的电流并非由反向击穿产生,而是有别于PN结的全新机理。三极管通过基极控制发射极附近的电子浓度。当基极电流注入电子时,发射极区域会聚集大量电子。这些电子可以跨越集电结的反向势垒,形成显著的反向漏电流。随着基极电流增大,发射极电子浓度提高,反向漏电流Ic也随之增加。 可以看出,三极管反向偏置下集电结的导电性,是由基极注入产生的大量电子引起的。这种机理完全不同于PN结中仅依赖少数载流子的反向漏电流。正是三极管的独特结构及工作原理导致了反向偏置下也存在明显Ic的现象。 值得注意的是,PN结在正向偏置时,电流主要由多数载流子形成;而反向偏置时,则由少数载流子形成微弱的电流。三极管反向偏置下Ic的产生,则利用了基极注入的大量电子,从电流形成机理上破除了PN结的限制。 通过解析三极管反向偏置导通的科学机理,我们加深了对其工作原理的理解。这也展示了半导体器件的奥秘比单纯PN结更为复杂。希望本文能够满足读者对这个有趣现象的好奇心,并成为理解半导体物理的一个窗口。 注意: 在严格意义上说PN并不能单向导电,我们所说的单向导电只是因为反向电流非常微弱 PN结在正向偏置时,电流主要由多数载流子形成;而反向偏置时,则由少数载流子形成微弱的电流

<span title='2023-07-19 16:43:28 +0800 +0800'>七月 19, 2023</span>

复位电路电容选择

${T = R C}$ 变换为 ${C = \frac T R }$ 其中T为时间 R为电阻 C为电容 以1ms复位时间来算, 如果电阻选10kΩ那么 $C = \frac {0.001s} {10000Ω} = 100nF$

<span title='2023-06-08 11:06:21 +0800 +0800'>六月 8, 2023</span>

外部中断和定时器

都说学习硬件是学习计算机组成原理的快速途径,我也入坑了,这里记录一些学习中初学者可能感觉难受的难点 一个MCU就好比一个很复杂的函数,他需要两种初始化,一种就是硬件初始化(也就是最小系统+外设电路), 一种是软件初始化(设置寄存器来使用某些功能) 中断 其实也没什么难的,难的是和GPIO那种简单方式的对比,都是固定套路 中断套路 编写好中断处理函数 设置触发方式(低电平触发或者下降沿触发) 开启某一中断 开启总中断 定时器 套路 设置定时器模式 设置定时器初值 开启定时器 (到目前定时器已经可以使用了) 定时器中断开启 (如果还想定时中断的话,需要剩下3,4两步) 总中断开启 中断优先级可以进行调整

<span title='2023-06-06 11:09:27 +0800 +0800'>六月 6, 2023</span>

数据库中排序的一些想法

数据库中很多时候要有排序字段作为排序依据,有时候还会对排序进行调整,常见的我们会加一个xx_sort字段,从1开始的一个排序,那样如果我们把最后一个元素排到第一时会导致每个记录中排序都要进行+1操作,需要操作很多记录;那有没有一种方法可以只修改被调整的那一条记录或者减少更改记录的次数. 今天想到一个方法,在给排序字段赋值的时候,不是按照+1的方式递增,而是以一个间隔递增比如 id order 1 1 2 1001 3 2001 4 3001 在每次调整顺序时,比如把4调整到1和2之间, 那么就在(1,1001)区间范围内随机找到一个值(或者取中间值),赋给4这种情况下只需要调整一次就可以了. 那么如果上面的结果变成这样了, 3想要移动到1和4之间怎么办 id order 1 1 4 2 2 1001 3 2001 和上面一样现在(1,2)区间随机(或者取中间值),发现不能找到一个值,那么就需要一个策略,把后面的一部分数据进行分散. 分散策略比如:按顺序找到后面一条间距大于多少的(比如1000)数据,然后把这几条数据按总间距平均分配一下,或者更简单粗暴的,如果遇到无法插入的值,就把后面的数据重新安排一下,都按初始间隔重新赋值 只要合理的设置间隔,就能达到在大多数情况下都能只修改一条记录 注意: 设置间隔可能需要考虑最多能容纳多少元素,还有字段类型所能容纳的最大值

<span title='2023-06-02 10:18:35 +0800 +0800'>六月 2, 2023</span>

service中的几种port的区别

port : service暴露的端口,可以在集群内进行访问 nodePort : 可以在集群外访问service的端口 targetPort : pod内的container暴露的端口 一张图来理解

<span title='2023-05-22 15:00:31 +0800 +0800'>五月 22, 2023</span>