Golang用300行代码实现内网穿透
我们经常会遇到一个问题,如何将本机的服务暴露到公网上,让别人也可以访问。我们知道,在家上网的时候我们有一个 IP 地址,但是这个 IP 地址并不是一个公网的 IP 地址,别人无法通过一个 IP 地址访问到你的服务,所以在例如:微信接口调试、三方对接的时候,你必须将你的服务部署到一个公网的系统中去,这样太累了。
这个时候,内网穿透就出现了,它的作用就是即使你在家的服务,也能被其人访问到。
今天让我们来用一个最简单的案例学习一下如何用 go 来做一个最简单的内网穿透工具。
整体结构首先我们用几张图来说明一下我们是如何实现的,说清楚之后再来用代码实现一下。
当前网络情况
我们可以看到,画实线的是我们当前可以访问的,画虚线的是我们当前无法进行直接访问的。
我们现在有的路是:
用户主动访问公网服务器是可以的
内网主动访问公网服务也是可以的
当前我们要做的是想办法能让用户访问到内网服务,所以如果能做到公网服务访问到内网服务,那么用户就能间接访问到内网服务了。
想是这么想的,但是实际怎么做呢?用户访问不到内网服务,那我公网服务器同样访问不到吧。所以我们就需要利用现有的链路来完成这件事。
基 ...
将版本信息打包到go的二进制中
很多时候对于go打包后的二进制文件,我们是无法知道这个二进制是什么当前什么版本、什么时候打包的,而很多软件的命令行都会有一个 -version 的选项来打印出当前程序的版本号,当然你可以直接在程序里面写死这个版本号,但是还有更加优雅的解决方式。
实现其实很简单,只需要在 build 的时候通过 -X 参数去指定变量值就可以了
1234567891011121314package mainimport "fmt"var ( Tag = "v0.0.0" CommitID = "" Branch = "" DATE = "")func main() { fmt.Println("tag:", Tag, "branch:", Branch, "commitID:", CommitID, "DATE:", DATE)}
123456789101112 ...
回首网络知识之 TCP 协议
之前我们说了 UDP 协议,也说到了 UDP 协议和 TCP 协议的对比,知道了 TCP 协议有那么一些复杂,所以这次就来仔细说说这个复杂的 TCP
前言提到 TCP 你第一想到的应该就是两个字 “靠谱”~ 因为它的所有设计都是围绕着这个任务展开的。
报文格式
很多人一看到这个就头疼了,因为格式好复杂啊,和 UDP 对比起来,怎么多了那么多东西呢?
别急,其实并不是很多,而且只要你想着为什么,你就知道会有什么。
首先是两个端口,这个和 UDP 一样,肯定需要端口,不然的话不知道是发给谁的
然后是序号和确认序号,序号是为了解决乱序的问题,如果没有序号,那么我是不知道那个先那个后,同时我还要告诉对方我收到了这个序号的信息,所以需要一个确认序号,确认的方式是告诉你我要的下一个序号是多少
首部长度也很好理解,告诉你首部有多长,你就知道前面有多长,你才知道数据从哪里开始
中间那几个标识位需要知道的是:
SYN:建立连接时用的,建立连接会标识为 1
ACK:回复你建立连接,建立连接之后标识为 1
RST:重新连接
FIN:结束连接
窗口大小,接收方告诉发送方别发太快了或者太慢了,用于流 ...
Golang 之 WaitGroup 源码解析
如果我们有一个大的任务要做,我们会尝试将这个任务分解,分解完成之后并发交由 goroutine 去做,并且我需要当全部的任务完成之后再进行下面的步骤,在 sync 包下,就有这样一个东西适合上述情况,WaitGroup,今天我们来看看具体它是怎么实现的。
PS:在下面我统一用 wg 来简称 WaitGroup
使用它的使用非常简单,如下:
12345678910111213func main () { wg := sync.WaitGroup {} for i := 0; i < 10; i++ { wg.Add (1) go func (job int) { defer wg.Done () //do something fmt.Printf ("job % d done\n", job) }(i) } wg.Wait () fmt.Println ...
回首网络知识之 UDP 协议
最近开始在恶补网络知识,算是一个复习,在学习了很多上层建筑之后回来看看之前的基础确实又有一个新的认识。一开始准备看完之后做一个整体的总结,但是发现知识点太多所以分了几期来写,每期都会短一点,这期我们先来简单了解一下 UDP
前言传输层中重要的两个协议 UDP 和 TCP ,在平时的时候,你虽然一直用着他们,但是往往感受不到他们的存在,他们两个往往经常会在面试的时候出现,打你一个措手不及。
在学习它之前,你首先要有一个概念,为什么我们需要 UDP 或者 TCP 这样的协议,它到底是在什么时候,什么地方,帮我们完成了什么样的事情?
在网络,我们研究的往往都是如何以一种合理的方式传递数据
而且协议则指定了这样的规定,当双方都遵守这样的规定,才能看懂互相说的话,才能达到合理传递数据的目的。
UDP 怎么样
UDP 格式简单,没有花里胡哨的东西
UDP 不会先探路,而是直接就发上了
UDP 不会管你卡不卡,发给你我就不管了
报文格式
源端口号(16 位,2 字节)
目的端口(16 位,2 字节)
UDP 长度(16 位,2 字节)
UDP 校验和(16 位,2 字节)
...
实现分布式锁,你能想到什么?
所谓分布式锁,即在多个相同服务水平扩展时,对于同一资源,能稳定保证有且只有一个服务获得该资源 — by LinkinStar
其实对于分布式锁,也是属于那种看似简单,实则有很多细节的问题。很多人在被问到这个问题的时候,一上来就会说用redis嘛,setnx嘛,我知道我知道。但仅仅是这样就能搞定了吗?那么当我们在实现一个分布式锁的时候,我们究竟需要考虑些什么呢?
必考点首先作为一个分布式锁,你一定要保证的是什么呢?
不能有两个服务同时获取到一把锁(资源)
不能出现有一个资源一直被锁住(锁一直被持有)
我认为上面两点是必须要保证的,其他的点,比如锁的获取是否高效,锁获取的非阻塞等等是评价一个锁是否好用的点(当然也不是说不重要)
下面我们一个个实现方案来说,来看看究竟有多少细节是我们需要考虑的。
redis实现先从最普遍的实现方案开始说起,redis。利用redis的特性,nx,资源不存在时才能够成功执行 set 操作,同时设置过期时间用于防止死锁
加锁SET resource_key random_value NX PX lock-time
解锁DEL resource_key
...
细说kubernetes - 初识deployment
当我们认识的k8s的时候,我们第一个认识的是pod,那么我觉得第二个认识的应该就是Deployment了。作为k8s中一个非常常见的对象,今天我们来看看它的实现原理和设计思想。
PS: 本文需要你对pod的定义和理解有一定的基础
定义在k8s中,对象常常都是以一个yaml格式的文件来定义的,deployment也不例外。如果你对k8s还不是特别了解,你大可以将一个文件看做是一个对象的所有属性,每个属性都有对应的值,其实也并不复杂。deployment的定义如下:
1234567891011121314151617181920212223242526# 定义版本apiVersion: apps/v1# 定义类型kind: Deployment# 定义名称和标签metadata: name: nginx-deployment labels: app: nginx# 定义规格spec: replicas: 2 selector: matchLabels: app: nginx # 定义模板 template: metadata: labe ...
细说kubernetes - 为什么是pod
k8s作为现在最火的容器编排调度平台,好用我也就不必多说了。当我们初识k8s的时候一个新的概念就到了我们眼前,那就是pod。我们在使用了之后也就渐渐的接受了pod这个东西,但是你有没有想过,为什么是pod?k8s为什么会有这样的设计?今天我们就来细细说说这个pod
架构图首先我们来回忆看看k8s的架构图是什么样子的这个是来源于 https://www.kubernetes.org.cn/ 中文官网的一个架构图
从架构图中我们可以看到,整个k8s的设计架构有以下几个要点:
master、node架构;master就是大脑充当着管理者的角色
master中提供了api-server、controller-manager、scheduler、etcd用于管理node并向外提供服务
node就是实际干活的,提供了kubelet、proxy用于向master汇报情况,并管理pod的网络
而pod是k8s中最小的调度单位,Pod就是最小的,管理,创建,计划的最小单元.
当然其他组件都非常重要,这个我们以后再说,我们今天就来看看主角“pod”
为什么是pod?一开始用的时候我就好奇为什么k8 ...
细说docker - 容器技术
docker对于现在的我们来说,已经是一个非常熟悉的东西了,docker无论是在部署打包,自动化,等方方面面都起着重要的作用,但是你是否有疑问,docker究竟是如何帮我们创建一个个隔离的环境的呢?今天我们就来看看,仔细说说docker
PS: 以下的讨论都限定在linux环境下,在windows和macos下容器技术实现不相同,不在讨论范围内。
大方向为什么先要提到这个词呢?因为所有在操作系统上运行的程序都叫做进程。docker也不例外,从大的方向来讲,docker就是帮你创建了一个进程而已。而不一样的是,docker通过限制了各种环境,就像给这个进程画了一个圈,所以在这个进程本身看来,它自己好像被隔离了一般。
docker容器技术的核心,就是通过约束和修改进程的动态表现,从而为其创造出一个“边界”
限制条件那么我们有了大方向,那么来细细看看,首先的第一个问题就来了,docker是通过什么方法对这个进程进行限制的呢?
Namespace命名空间,没错就是它,是它限制了docker容器的环境。其实这是linux的一个功能而已,只不过没人想到docker会那它来做这个事情。下面看 ...
再看golang垃圾回收
首先要说一些废话:之前我已经有博客写过golang的垃圾回收相关的内容,虽然很简略,但是涵盖了整体的流程,现在为啥又来写一遍呢?一方面有一些政治(你懂得)因素在里面,一方面最近又再研究。那么问题来了,那么多博客已经写过了它,我怎么把它讲出花来呢?我思前想后,于是想出了几个独特的角度来重新诠释一下golang的垃圾回收。
那首先如果再把整个gc过程简单说一遍,可能就没有人愿意听了,但是golang的gc说简单也简单说复杂其实也有很多细节,如何做到有自己的想法呢?于是我就强行举例了几个问题。
问题&角度在研究golang垃圾回收的时候,你有没有想过下面几个问题
golang如果有两个对象循环互相引用,是否会出现永远回收不了的对象?
golang的gc标记方式为什么用bfs而不是dfs?
是否有可能永远不触发gc?
为什么golang的gc不整理、不分代?
个人理解首先说明一下,这些问题都是我自己想的,也没有什么所谓的正确答案,所以下面也是我的个人理解,如果有问题可以在下方留言进行讨论。
问题1
golang如果有两个对象循环互相引用,是否会出现永远回收不了的对象?
为什么 ...
快速上手kubernetes——minikube最小实现
最近在研究k8s,就来写一个关于k8s快速上手,并记录采坑的点。需要的前置知识点:docker、k8s的一些基本概念,下面这个可能对你有帮助。https://juejin.im/post/5d1b2a656fb9a07edc0b7058
什么是k8s我们知道,我们可以将项目制作成docker镜像,然后利用docker去部署我们的项目,这样可以解决很多服务器环境所带来的问题;但是容器多了,容器与容器之间就需要访问,之间就需要网络配置等等,从而就有了docker-compose;但是当我们的服务进行升级,或者服务需要进行调度,扩容等等,这个时候就需要一个大管家来管所有的东西;这个大管家就是 - Kubernetes
初学会遇到的问题因为k8s的东西太多了,所以学习成本现在越来越高,好在k8s已经很多教程。我说一下现在学的时候肯定会遇到的大问题:
国内的问题(国内环境很多镜像拉不到)
本地搭建环境(原来搭建k8s需要一些服务器)
电脑环境的问题(windows和mac都有坑点)
最小实现现在我们就来在本机实现一个最小的k8s的实现,给出一个hello-worldk8s提供了minik ...
快速上手terraform —— 阿里云OSS和ECS的创建
最近在研究terraform,采了一圈坑,记录一下。
什么是terraform?terraform 通过代码配置实现物理机等一些资源的分配。简单说就是,写一个配置文件,启动,就能帮你购买一台云的机器,或者说申请到oss的资源,或者是别的什么。具体功能见官网。
https://www.terraform.io/docs/index.html
名词解释:provider你可以把它看做各个厂商对terraform提供的插件,terraform可以调用这些插件从而实现对资源的操作管理。terraform流程:init -> plan -> apply -> destroy对应为:初始化,计划验证,实际应用,销毁
最小demo我将用一个最小的demo来演示它怎么干活的:通过terraform来创建一个阿里云的oss(以下没有利益相关)只是因为阿里云我有账号而已,其他供应商也有。
我的本地环境:macOS
步骤1 下载相关资源下载对应的客户端:https://www.terraform.io/downloads.html下载解压后得到:terraform的客户端,将它复制到 ...
golang中神奇的sync.Pool
在 golang 中有一个池,它特别神奇,你只要和它有个约定,你要什么它就给什么,你用完了还可以还回去,但是下次拿的时候呢,确不一定是你上次存的那个,这个池就是 sync.Pool
说实话第一次看到这个东西的时候,真的想不到这个东西有啥用啊,为什么要有这个东西呢?等我看完之后,嗯,还有有点用的;等到有一次优化经历的时候,嗯,这个有点意思了。今天我们就来看看这个神奇的 sync.Pool
简单案例首先我们来看看这个 sync.Pool 是如何使用的,其实非常的简单。它一共只有三个方法我们需要知道的:New、Put、Get
1234567891011121314151617181920package mainimport ( "fmt" "sync")var strPool = sync.Pool{ New: func() interface{} { return "test str" },}func main() { ...
图解golang内存分配
我们知道所有程序运行都需要使用内存,而内存的管理和分配又是非常重要的,它决定了你的程序能不能在有限的资源内跑的更快。可以设想一下,如果你自己来设计的一个内存分配的规则,会遇到什么问题呢?如果你有了一大块内存你要怎么去合理的分配和使用呢?今天我们通过几张图来看看golang中的内存分配是怎样的。
前置知识:对golang的GPM模型有所了解,对GC有一定的了解,有助于你理解下面的内容。
想一想我们首先来想一下,如果我们自己来分配内存的时候可能会遇到什么问题。
我想要512G,你能给吗?操作系统的内存不是你想要多少就给你多少的。比如我跟操作系统说我要512G内存,你赶紧给我,不给我我就掐死你,如果你是操作系统,是不是立马就想把我给结束了?
能随便分割吗?如果我拿到一块内存,挺大的,你把它想象成一块地,我今天要用这块地的这个部分,肯定是从中间切一块出来用,然后明天要另一个部分,然后再切出来一部分。如果随便切,今天要一块三角形,明天要一块圆形,那么肯定会留有很多小块的地方,那些地方没有办法被合理的使用,就会浪费。等到想再要一块正方形的地的时候发现没地方可以切了。
不用了我需要放回去吗?如果我 ...
大话图解gin源码
最近在网上搜了一下,对于gin框架用的人还是比较多的,我自己之前也在使用,但是对于源码解析这块,我没有看到自己想看到的那种从框架入手的解析图,所以嘿嘿嘿,我的机会就来了,今天就带来最完整的gin源码图解。希望通过这篇博客你也能自己学会拆轮子。
PS:本文建立在你已经能熟练使用gin的基础之上,如果还没用过可以去官网看一下:https://gin-gonic.com/zh-cn/docs/然后gin是对golang的http包的封装,所以最好对http包也要有了解。
整体分析逻辑先来说明一下我整体拆解的逻辑,对于一个框架,我喜欢从下面几个方面去入手拆解:
启动方式
如何使用
实现与特点针对于gin,我也将从这几个方面去入手,就会得到下面几个问题,带着问题看源码是必备条件。首先启动的时候gin做了些什么?gin封装了什么然后怎么去实现的?gin整体结构是怎么样的,有哪些结构?…
然后使用一个比较小的demo,然后先从方法入手,进源码看。
12345678910111213141516func main() { router := gin.Default() ...
图解goroutine调度
其实这个话题我早就想做了,奈何这个问题确实有点复杂,看了很多文章才有了一点点自己的理解。从golang一开始的使用我就已经开始好奇了,这个goroutine到底是怎么实现的呢?怎么就能搞出一个和线程类似,但是性能又那么好的东西的呢?
模型三个小伙子在看整体结构之前,我先来介绍三个小伙子,golang为了实现goroutine,定义了这样三个小伙子,让他们帮忙去实现。
G表示goroutine,存储了goroutine的执行stack信息、goroutine状态以及goroutine的任务函数等;另外G对象是可以重用的。
MM代表着真正的执行计算资源。在绑定有效的P后,进入调度器循环;而调度器循环的机制大致是从各种队列、P的本地队列中获取G,切换到G的执行栈上并执行G的函数,调用goexit做清理工作并回到M,如此反复。M并不保留G状态,这是G可以跨M调度的基础。
P表示逻辑processor,P的数量决定了系统内最大可并行的G的数量(前提:系统的物理cpu核数>=P的数量);P的最大作用还是其拥有的各种G对象队列、链表、一些cache和状态。
整体结构模型我们先来看 ...
Golang的interface
由于golang中说interface的文章太多了,很多都已经说的很细节了,所以我再说感觉也有点难。于是总结出几个关键问题,供你参考,如果能做到准确无误有理有据的回答,那么interface应该是没有问题了。
问题
interface底层结构有哪两种,分别是什么样子的,里面保存了哪些信息?
其中tab是什么时候生成的?
从别的类型转换成interface,从interface转换成别的类型,这两者的过程是怎么样的?
两个interface之间是否可以比较?
golang底层是如何判断一个类型是否实现了一个interface?
1、底层结构12345678910111213141516171819202122232425262728293031type eface struct { // 16 bytes on a 64bit arch _type *_type data unsafe.Pointer}type iface struct { // 16 bytes on a 64bit arch tab *itab data ...
Golang之channel
go中的一个精髓就是就是channel,那么你有没有想过,它究竟是怎么实现的呢?我之前就怀疑过,是不是就是通过一个数组保存了一下传入的数据,然后在接收方读一读就完事了,那么阻塞又是怎么实现的呢?close的时候需要注意些什么呢?
结构首先我们来看一下channel的结构是怎么样的。
1234567891011121314151617181920type hchan struct { qcount uint // total data in the queue dataqsiz uint // size of the circular queue buf unsafe.Pointer // points to an array of dataqsiz elements elemsize uint16 closed uint32 elemtype *_type // element type sendx uint // send index recvx uint // receive index r ...
你可能不知道的redis
以下是针对redis的知识点整理,用于复习,主要以罗列为主,详细具体讲解可以参考书《Redis设计与实现》,你可以过一遍看看有无知识点遗漏。个人能力有限,如果你还有补充可以再下方评论指出,万分感谢。
基础知识点
数据类型string (字符串)、list (列表)、set (集合)、hash (哈希) 和 zset (有序集合)底层实现包括:SDS动态字符串,双向链表,数组加链表,渐进式hash,跳表
redis是单线程
redis默认有16个数据库,默认先用0,可以使用select命令切换
过期策略惰性删除:当这个key被访问到,但是已经过期,那就删除定期删除:过一定时间,拿出一定的key判断,进行删除,如果超过一定数量,继续拿出一定的key进行判断删除,时间存在上限
内存超限当redis使用超过内存限制会根据策略来执行:noeviction:不能put,但是可以del和get,默认是这个策略volatile-lru:在有过期时间的key中,淘汰最少用的(redis是近似lru算法,会随机取几个淘汰最少用的)volatile-ttl:在有过期时间的key中,淘汰过期时间最 ...
你可能不知道的mysql
以下是针对mysql的知识点整理,用于复习,主要以罗列为主,详细具体讲解可以参考书《高性能mysql》,你可以过一遍看看有无知识点遗漏。
执行sql过程客户端 -> 连接器 -> 分析器 -> 优化器 -> 执行器 -> 存储引擎连接器:连接上数据库,长连接分析器:分析语法(包含解析器和预处理器,解析器生成解析树,预处理器判断字段存在歧义)优化器:选择正确的索引进行优化执行执行器:执行具体sql返回结果
mysql的两个重要日志redo-log(重做日志):固定大小的循环缓存,InnoDB使用,即使重启,只要记录到了redo-log就不会丢失。防止mysql意外。bin-log:归档日志,所有sql都会记录,并且采用追加,满了之后新开,有两种方式,一种是记录sql语句(statement),一种是row,记录出现的事件。如果只记录sql语句会导致主从同步上面存在问题,从库执行相同的sql得到效果不同,所以还有一种混合的方式,mysql会自动判断当前语句是否会造成主从不同步的情况,如果会,那么就使用row记录如果不会就是用sql记录,因为row记录会增加 ...
Golang的垃圾回收
最近垃圾分类的话题热度一下子就上去了,很多人因为垃圾分类的问题很头痛。因为垃圾这个话题,那我就想来说说Golang里面的垃圾,于是就有了这篇博客,golang中的垃圾回收。
现阶段网上针对golang垃圾回收的解析已经很多了,所以我也没有必要仔仔细细的一点点说,还是那个原则,用最直白的话告诉你,垃圾到底是怎么收的。
GC的意义首先本文后续都会使用 GC 代替垃圾回收这几个字。我们知道创建对象会给他分配内存资源,如果这个对象不使用了,而这个内存资源却一直被占用的话,那么我们的电脑很快就会被放满,所以需要将这些垃圾对象进行回收。
什么才是垃圾要回收,那么我们必须知道什么才是垃圾,什么不是垃圾。在我们看来,一个对象以后都不用了,就是垃圾。在程序看来,一个对象没有被引用了,就是垃圾。
GC的流程首先说明一下,下面说的停,都是STW,stop the world,全世界暂停,所有运行的都停下来了。这个流程可以由下面这张图展示:
第一个过程先告诉所有人,停一下,我来记录一下当前状态。
第二个过程告诉所有人,你们继续,该干嘛干嘛,我标记一下要用的对象一开始所有点是白色,首先从根节点出发,标记相连的 ...
不是我吹,你可能连defer都不清楚
在golang中,对于defer,我之前的理解就是和java中的finally代码块一样,没什么难度,但是吧,当我最近看的一些神奇的问题,我就发现原来并非想的那么简单。
先举个栗子123456789101112131415161718192021222324252627282930313233343536373839404142package mainimport "fmt"func main() { fmt.Println(DeferFunc1(1)) fmt.Println(DeferFunc2(1)) fmt.Println(DeferFunc3(1)) DeferFunc4()}func DeferFunc1(i int) (t int) { t = i defer func() { t += 3 }() return t}func DeferFunc2(i int) int { t := i defer fun ...
Golang之context
当我们使用一些golang框架的时候,总能在框架中发现有个叫做context的东西。如果你之前了解过java的spring,那么你肯定也听说过其中有个牛逼的ApplicationContext。Context这个东西好像随时随地都在出现,在golang中也是非常重要的存在。今天我们就来看看这个神奇的Context。
定义
首先我们要知道什么是context?
很多人把它翻译成上下文,其实这个是一个很难描述很定义的东西,对于这种东西,我习惯用功能去定义它。我的定义是:context是用于在多个goroutines之间传递信息的媒介。官方定义:At Google, we developed a context package that makes it easy to pass request-scoped values, cancelation signals, and deadlines across API boundaries to all the goroutines involved in handling a request.
用法同样的我们先来看看它的一些基本用法,大 ...
Golang之reflect
反射 —— 如果你之前学过一些别的语言,比如java可能就会了解,反射是一个传说中很厉害的操作,算是一个高级用法。而同时,很多人也会告诉你,反射是一个危险的操作,那么在golang中,反射又是怎么操作的呢?今天就来说说golang中的反射reflect。
反射的定义首先问问自己,你知道什么是反射吗?如果你有一个清楚的定义,证明你已经对反射非常熟悉了。官方的定义很官方,我就说说我的:反射,反射,从字面理解就是通过镜子(或类似的东西)看到自己。而在编程中,反射指的是在运行的过程中看到自己。在实际的编程过程中我们知道,创建的这个变量或者对象是什么类型或者是什么样子的,同时很容易能对它进行操作。而在运行过程中,程序没有我们的眼睛,它并不知道这个东西是怎么样的,这个时候就需要运用到反射。通过反射我可以知道自己长什么样子。
反射的使用reflect.TypeOf如果你对反射还是有些模糊,那么看下面这个最简单的例子
1234func main() { a := 1.3 fmt.Println("a的类型是", reflect.TypeOf(a)) ...
Golang的strings.go源码解析 - Rabin-Karp了解一下?
strings包是我们经常在处理字符串的时候要用的,这次我们来看看它其中的一些方法具体是如何实现的。我就找到其中常用的几个方法,然后针对其中比较难的部分还有应用到一些特别算法的部分进行分析。
ToUpper先来看个简单的ToUpper,将所有字符转换成大写。这个如果让我们自己实现也没有什么难度,就是遍历每个字符转换成大写就可以。
12345678910111213141516171819202122232425262728// ToUpper returns a copy of the string s with all Unicode letters mapped to their upper case.func ToUpper(s string) string { isASCII, hasLower := true, false for i := 0; i < len(s); i++ { c := s[i] if c >= utf8.RuneSelf { isASCII = false break } has ...
Golang的slice
今天来说个简单的,也不简单的东西,那就是切片。slice对于golang来说那真的是一个非常常用的东西了,很多地方都会用到它,今天就来说说,slice底层是如何实现的,又有哪些坑是需要提前注意的。
slice结构很多第一次接触golang的同学都会认为,数组和切片是差不多的东西,其实不是的,切片是数组的封装。
12345type slice struct { array unsafe.Pointer len int cap int}
上面这个就是slice的结构,顺便说一下:slice的源码位置是:go/src/runtime/slice.go
其中array是一个指针,指向底层的数组
len代表slice的长度
cap代表slice的容量
为什么会有长度和容量这个区分呢,这两个东西是用来干什么的呢?我们往下看。
slice的长度和容量我们先来看一个最简单的案例
1234sli := make([]int, 2)fmt.Printf("len=%d cap=%d\n", len(sli), cap( ...
浅入深出ETCD之【集群部署与golang客户端使用】
之前说了etcd的简介,命令行使用,一些基本原理。这次来说说现实一点的集群部署和golang版本的客户端使用。因为在实际使用过程中,etcd的节点肯定是需要2N+1个进行部署的,所以有必要说明一下集群的部署。
集群部署网上有很多集群部署的教程,有的很复杂,其实对于我们实际使用来说,其实配置并不复杂,下面举例一种最简单的集群配置。(简单到你想不到~)
下载https://github.com/etcd-io/etcd/releases还是在github上面找到需要下载的版本我使用的是etcd-v3.3.13-linux-amd64.tar.gz使用wget下载到linux你喜欢的目录,或者本地下载完成之后上传均可。
部署首先我找了三台机器,对应ip为192.168.4.224192.168.4.225192.168.4.226PS:提醒一下记得开发对应防火墙的端口
然后将下载的文件解压,之后进入解压后的目录,分别使用下面的命令启动。(注意下面的命令对应的是三台不同的机器,你需要修改对应为你自己的ip)
1234567891011121314151617181920212223$ ./ ...
浅入深出ETCD之【raft原理】
这次我们来说说,有关于etcd原理的一些事情。之前我们已经了解到了etcd是一个分布式的k-v存储,那么它究竟是如何保证数据是如何复制到每个节点上面去的呢?又是如何保证在网络分区的情况下能正常工作下去?raft协议到底是什么?带着这些问题我们继续往下看。
raft选举策略我们知道etcd使用raft协议来保证整个分布式的节点网络能正常的运转并且能正确的将数据复制到每个节点上面去。那么什么是raft协议嘞?
首先我们有这样一个背景:raft是想维护整一个网络,其中有一个领导人,这个领导人负责将收到的信息同步给网络中的其他所有节点,从而保证整个网络数据一致。
如果你有一定的英文基础,我建议直接查看下面这个网站,它用动画非常清楚的描述了raft选举的整个过程:http://thesecretlivesofdata.com/raft/
这个其实已经说明的超级棒了,如果你还看不懂,我下面会用最简单的几个要点来进行最简单的说明。
大多数理论首先说明一个理论,叫做大多数理论,很简单,举个栗子:
有10个人,如果你将苹果给其中的6个人(大多数),那么你随机选择5个人,一定有一个人会有苹果。
在 ...
浅入深出ETCD之【简介与命令行使用】
你知道etcd吗?随着k8s的使用广泛之后,etcd被非常多的人所知道,同时又因为它可靠的分布式特性被很多人喜欢。所以,我准备有几篇博文来记录一下,从基本使用到线上部署再到原理分析,做一个系列。那么,今天先来说说它的简介与命令行的使用。
简介ETCD是什么我个人总结为下面用几个要点:
高可用K-V存储,就类似于redis一样的键值对存储。
允许应用实时监听存储中的K-V变化。
能够容忍单点故障,能够应对网络分区。
etcd利用raft在集群中同步K-V信息,raft是强一致的集群日志同步算法。
总结:etcd是一个分布式高可用k-v存储,通过复制达到每个节点存储的信息一致,从而保证高可用。
数据复制这里简单说一下复制的具体流程:
(client为我们的客户端,用来发出存储请求,leader和follower都是etcd的节点)就如图上所看到的,我叫它两段式提交:
客户端请求leader发送存储的数据,然后leader节点要将信息通过日志复制给大多数的follower节点,如上图所示,只需要复制给两个(加上它自己是三个)那么就是大多数节点。
leader当复制完成之后才会本地 ...
Golang指针与unsafe
我们知道在golang中是存在指针这个概念的。对于指针很多人有点忌惮(可能是因为之前学习过C语言),因为它会导致很多异常的问题。但是很多人学习之后发现,golang中的指针很简单,没有C那么复杂。所以今天就详细来说说指针。
指针的使用123a := 1p := &afmt.Println(p)
输出:0xc42001c070
可以看到p就是一个指针,也可以说是a的地址。
1234a := 1var p *intp = &afmt.Println(p)
或者也可以写成这样,因为我知道,在很多人看来,看到*号才是指针(手动滑稽)
123a := 1p := &afmt.Println(*p)
输出:1
然后使用就直接通过*号就能去到对应的值了,就这么简单
指针的限制Golang中指针之所以看起来很简单,是因为指针的功能不多。我们能看到的功能就是指针的指向一个地址而已,然后对于这个地址也只能进行传递,或者通过这个的地址去访问值。
不能像C语言中一样p++,这样移动操作指针,因为其实这样操作确实不安全,很容易访问到奇怪的区域。
不同类型的指针不能相互赋值、转换、比 ...
大话图解golang map源码详解
网上分析golang中map的源码的博客已经非常多了,随便一搜就有,而且也非常详细,所以如果我再来写就有点画蛇添足了(而且我也写不好,手动滑稽)。但是我还是要写,略略略,这篇博客的意义在于能从几张图片,然后用我最通俗的文字,让没看过源码的人最快程度上了解golang中map是怎么样的。
当然,因为简单,所以不完美。有很多地方省略了细节问题,如果你觉得没看够,或者本来就想了解详细情况的话在文末给出了一些非常不错的博客,当然有能力还是自己去阅读源码比较靠谱。
那么下面我将从这几个方面来说明,你先记住有下面几个方向,这样可以有一个大致的思路:
基础结构:golang中的map是什么样子的,是由什么数据结构组成的?
初始化:初始化之后map是怎么样的?
get:如何获取一个元素?
put:如何存放一个元素?
扩容:当存放空间不够的时候扩容是怎么扩的?
基础结构图解这个就是golang中map的结构,其实真的不复杂,我省略了其中一些和结构关系不大的字段,就只剩下这些了。
大话大话来描述一些要点:
最外面是hmap结构体,用buckets存放一些名字叫bmap的桶(数量不定,是2的指数倍 ...
Golang 读写锁RWMutex 互斥锁Mutex 源码详解
Golang中有两种类型的锁,Mutex (互斥锁)和RWMutex(读写锁)对于这两种锁的使用这里就不多说了,本文主要侧重于从源码的角度分析这两种锁的具体实现。
引子问题我一般喜欢带着问题去看源码。那么对于读写锁,你是否有这样的问题,为什么可以有多个读锁?有没有可能出现有协程一直无法获取到写锁的情况?带着你的疑问来往下看看,具体这个锁是如何实现的。
如果你自己想看,我给出阅读的一个思路,可以先看读写锁,因为读写锁的实现依赖于互斥锁,并且读写锁比较简单一些,然后整理思路之后再去想一下实际的应用场景,然后再去看互斥锁。
下面我就会按照这个思路一步步往下走。
基础知识点
知识点1:信号量信号量是 Edsger Dijkstra 发明的数据结构(没错就是那个最短路径算法那个牛人),在解决多种同步问题时很有用。其本质是一个整数,并关联两个操作:
申请acquire(也称为 wait、decrement 或 P 操作)释放release(也称 signal、increment 或 V 操作)
acquire操作将信号量减 1,如果结果值为负则线程阻塞,且直到其他线程进行了信号量累加为正数才 ...
感谢您为本博客的长远发展做出的贡献
感谢您为本博客的长远发展做出的贡献
本博客不会悬挂任何广告,若发现评论中含有任何广告,请勿点击,长远发展源于我的热爱和你的投币~ 😄
本博客当前主要开销
云服务器 (约 300/年)
图床 CDN 存储和流量 (约 24/年)
域名 (约 72/年)
评论资源 (约 80/年)
….
赞助者
赞助时间
赞助者
金额
友链
2019 年 06 月 27 日
好大一瓶芬达
20
-
2022 年 06 月 14 日
李*双
1
-
2023 年 03 月 12 日
黄*博
66
-
2023 年 04 月 28 日
张*恒
72
-
2024 年 05 月 29 日
G*a
10
-
赞助方式
微信
支付宝
赞助时记得备注昵称或者友链 📮
面向问题编程
面向问题编程Redis
IO多路复用和多线程会影响Redis分布式锁吗?
Redis 的 set nx 底层怎么实现的?
Redis 主从复制是如何实现的?
Redis 事件机制是如何实现的?
Redis Sorted Set 实现与应用
Redis List 设计与实现
假如 Redis 里面有 1 亿个 key,其中有 10w 个 key 是以某个固定的已知的前缀开头的,如何将它们全部找出来?
Go
Golang 中 能否将 slice 作为 map 的 key?
MySQL
什么是索引下推?
读书吃饭睡觉
读书吃饭睡觉
程序员真是一个活到老学到老的职业,读书的时候别忘记吃饭睡觉哦~
《笨开发学习操作系统》
操作系统的学习总是让人觉得自己像个“笨蛋”,希望通过编写这个专栏来让自己变“聪明”一点 😂,希望你也可以
《笨开发学习操作系统》0 前言
《笨开发学习操作系统》1 启动
《笨开发学习操作系统》2 进程
《笨开发学习操作系统》3 内存
《笨开发学习操作系统》4 进程间通信
《笨开发学习操作系统》5 文件系统
《笨开发学习操作系统》6 输入输出系统
《笨开发学习操作系统》7 网络
《Go 单元测试指北》
Go 单元测试的小总结
《Go 单元测试指北》0 前言
《Go 单元测试指北》1 数据层
《Go 单元测试指北》2 逻辑层
《Go 单元测试指北》3 接口层
《Go 单元测试指北》4 其他小技
《一起读 kubernetes 源码》(WIP🚧)
学习读源码的方法和技巧,理解为什么要读源码 🤔
《一起读 kubernetes 源码》序
《一起读 kubernetes 源码》kubelet 如何创建 pod?
《一起读 kubernetes 源码》probe 监控 po ...