邢栋博客
邢栋博客,Action博客,记录工作和生活中的点点滴滴
nginx负载均衡 - 根据url做一致性hash
nginx负载均衡 - 根据url做一致性hash
目标:按照指定的参数(如分类/商品编号)做一致性hash,从而保证相同数据到一台机器上
先说下nginx里$request_uri和$uri的区别
$request_uri
This variable is equal to the *original* request URI as received from the client including the args. It cannot be modified. Look at $uri for the post-rewrite/altered URI. Does not include host name. Example: "/foo/bar.php?arg=baz"
这个变量等于从客户端发送来的原生请求URI,包括参数。它不可以进行修改。$uri变量反映的是重写后/改变的URI。不包括主机名。例如:"/foo/bar.php?arg=baz"
$uri
This variable is the current request URI, without any arguments (see $args for those). This variable will reflect any modifications done so far by internal redirects or the index module. Note this may be different from $request_uri, as $request_uri is what was originally sent by the browser before any such modifications. Does not include the protocol or host name. Example: /foo/bar.html
这个变量指当前的请求URI,不包括任何参数(见$args)。这个变量反映任何内部重定向或index模块所做的修改。注意,这和$request_uri不同,因$request_uri是浏览器发起的不做任何修改的原生URI。不包括协议及主机名。例如:"/foo/bar.html"
$document_uri
The same as $uri.
同$uri.
实现步骤
第一步:在nginx中做简单配置,先实现轮训,域名可以根据自己的来
server {
listen 80;
server_name default www.test.com;
location / {
proxy_pass http://www_posts;
index index.html index.htm;
}
}
upstream www_posts {
server 127.0.0.1:7001;
server 127.0.0.1:7002;
server 127.0.0.1:7003;
}
server {
listen 7001;
server_name default www.test.com;
location / {
root /data/www/post1/;
index index.html index.htm;
}
}
server {
listen 7002;
server_name default www.test.com;
location / {
root /data/www/post2/;
index index.html index.htm;
}
}
server {
listen 7003;
server_name default www.test.com;
location / {
root /data/www/post3/;
index index.html index.htm;
}
}
完成后 重启nginx
第二步创建程序文件
mkdir /data/www/post1; cd /data/www/post1;
vim 1.html //写入 1-1
vim 2.html //写入 1-2
vim 3.html //写入 1-3
mkdir /data/www/post2; cd /data/www/post2;
vim 1.html //写入 2-1
vim 2.html //写入 2-2
vim 3.html //写入 2-3
mkdir /data/www/post3; cd /data/www/post3;
vim 1.html //写入 3-1
vim 2.html //写入 4-2
vim 3.html //写入 3-3
通过浏览器访问 www.test.com/1.html,不断刷新,可分别出现1-1,2-1,3-1
第三步 自定义hash key
在server-location中加入
if ( $uri ~* "^\/(\d+).html$" ){ set $defurlkey $1; }
修改upstream,结果如下
upstream www_posts {
hash $defurlkey;
server 127.0.0.1:7001;
server 127.0.0.1:7002;
server 127.0.0.1:7003;
}
可通过分别访问,测试结果
www.test.com/1.html
www.test.com/2.html
www.test.com/3.html
目标:按照指定的参数(如分类/商品编号)做一致性hash,从而保证相同数据到一台机器上
先说下nginx里$request_uri和$uri的区别
$request_uri
This variable is equal to the *original* request URI as received from the client including the args. It cannot be modified. Look at $uri for the post-rewrite/altered URI. Does not include host name. Example: "/foo/bar.php?arg=baz"
这个变量等于从客户端发送来的原生请求URI,包括参数。它不可以进行修改。$uri变量反映的是重写后/改变的URI。不包括主机名。例如:"/foo/bar.php?arg=baz"
$uri
This variable is the current request URI, without any arguments (see $args for those). This variable will reflect any modifications done so far by internal redirects or the index module. Note this may be different from $request_uri, as $request_uri is what was originally sent by the browser before any such modifications. Does not include the protocol or host name. Example: /foo/bar.html
这个变量指当前的请求URI,不包括任何参数(见$args)。这个变量反映任何内部重定向或index模块所做的修改。注意,这和$request_uri不同,因$request_uri是浏览器发起的不做任何修改的原生URI。不包括协议及主机名。例如:"/foo/bar.html"
$document_uri
The same as $uri.
同$uri.
实现步骤
第一步:在nginx中做简单配置,先实现轮训,域名可以根据自己的来
server {
listen 80;
server_name default www.test.com;
location / {
proxy_pass http://www_posts;
index index.html index.htm;
}
}
upstream www_posts {
server 127.0.0.1:7001;
server 127.0.0.1:7002;
server 127.0.0.1:7003;
}
server {
listen 7001;
server_name default www.test.com;
location / {
root /data/www/post1/;
index index.html index.htm;
}
}
server {
listen 7002;
server_name default www.test.com;
location / {
root /data/www/post2/;
index index.html index.htm;
}
}
server {
listen 7003;
server_name default www.test.com;
location / {
root /data/www/post3/;
index index.html index.htm;
}
}
完成后 重启nginx
第二步创建程序文件
mkdir /data/www/post1; cd /data/www/post1;
vim 1.html //写入 1-1
vim 2.html //写入 1-2
vim 3.html //写入 1-3
mkdir /data/www/post2; cd /data/www/post2;
vim 1.html //写入 2-1
vim 2.html //写入 2-2
vim 3.html //写入 2-3
mkdir /data/www/post3; cd /data/www/post3;
vim 1.html //写入 3-1
vim 2.html //写入 4-2
vim 3.html //写入 3-3
通过浏览器访问 www.test.com/1.html,不断刷新,可分别出现1-1,2-1,3-1
第三步 自定义hash key
在server-location中加入
if ( $uri ~* "^\/(\d+).html$" ){ set $defurlkey $1; }
修改upstream,结果如下
upstream www_posts {
hash $defurlkey;
server 127.0.0.1:7001;
server 127.0.0.1:7002;
server 127.0.0.1:7003;
}
可通过分别访问,测试结果
www.test.com/1.html
www.test.com/2.html
www.test.com/3.html
大小端模式
标签:
大小端
不同机器内部对变量的字节存储顺序不同,有的采用大端模式(big-endian),有的采用小端模式(little-endian)。
大端模式是指高位字节数据存放在低地址处,低位字节数据放在高地址处,也称为高尾端
小端模式是指低位字节数据存放在低地址处,高位字节数据放在高地址处,也称为低尾端
在网络上传输数据时,由于数据传输的两端可能对应不同的硬件平台,采用的存储字节顺序也可能不一致,因此 TCP/IP 协议规定了在网络上必须采用网络字节顺序(也就是大端模式)。
通过对大小端的存储原理分析可发现,对于 char 型数据,由于其只占一个字节,所以不存在这个问题,这也是一般情况下把数据缓冲区定义成 char 类型 的原因之一。对于 IP 地址、端口号等非 char 型数据,必须在数据发送到网络上之前将其转换成大端模式,在接收到数据之后再将其转换成符合接收端主机的存储模式。
举一个例子,比如 int n = 0x12345678 //16进制
1)大端模式:
内存地址: 低地址 -----------------> 高地址
n的字节序:0x12 | 0x34 | 0x56 | 0x78
2)小端模式:
内存地址: 低地址 ------------------> 高地址
n的字节序:0x78 | 0x56 | 0x34 | 0x12
大端模式是指高位字节数据存放在低地址处,低位字节数据放在高地址处,也称为高尾端
小端模式是指低位字节数据存放在低地址处,高位字节数据放在高地址处,也称为低尾端
在网络上传输数据时,由于数据传输的两端可能对应不同的硬件平台,采用的存储字节顺序也可能不一致,因此 TCP/IP 协议规定了在网络上必须采用网络字节顺序(也就是大端模式)。
通过对大小端的存储原理分析可发现,对于 char 型数据,由于其只占一个字节,所以不存在这个问题,这也是一般情况下把数据缓冲区定义成 char 类型 的原因之一。对于 IP 地址、端口号等非 char 型数据,必须在数据发送到网络上之前将其转换成大端模式,在接收到数据之后再将其转换成符合接收端主机的存储模式。
举一个例子,比如 int n = 0x12345678 //16进制
1)大端模式:
内存地址: 低地址 -----------------> 高地址
n的字节序:0x12 | 0x34 | 0x56 | 0x78
2)小端模式:
内存地址: 低地址 ------------------> 高地址
n的字节序:0x78 | 0x56 | 0x34 | 0x12
vim常用操作笔记
标签:
vim
普通模式下
w 跳转到下个符号或者单词 b 跳转到上个符号或者单词,对应的大写的WB,跳转的尺度会更大些,比如 I'm 小写会默认是三个,大写会默认为一个
x 删除当前字母
dw 删除下个符号或者单词 db 删除上个符号或者单词
dt+字母 df+字母 删除到某个字母
复制
ctrl+v,然后用鼠标选择要复制的,然后 y,然后p进行粘贴
替换
:s/java/php 替换当前行第一个java为php
:s/java/php/g 替换当前行所有的java为php
:1,$s/java/php 替换第一行开始到最后一行的第一个java为php
:1,$s/java/php/g 替换第一行开始到最后一行的所有 java为php
:%s/java/php 替换每一行的第一个java为php
:%s/java/php/g 替换每一行的所有 java为php
vim = vi + IMproved
多级撤销
语法加亮和自动补全
支持多种插件
通过网络协议(HTTP/SSH)编辑文件
多文件编辑
Vim可以编辑压缩格式文件(GZIP、ZIP等)
光标移动
h 左移
l 右移
j 下移
k 上移
^/0 移动到行首/包含缩进空格
$ 移动到行尾
单词和字符传移动
w/W 正向移动到下一个单词开头
b/B 反向移动
e/E 正向移动下一个单词结尾
ge 反向移动词尾
跳转
ctrl+f/F 下一页
ctrl+b/B 上一页
ctrl+d/u 向上或者向上翻半页
gg 跳转到文件首行
<line_number>gg/G 跳转到指定行
G 跳转到文件最后一行
g+ctrl+g/G 查看文件信息/{g}更加详细
缩进 >右 <左
>> 或者 :> 右缩进
m,n> 或者 :m>(n-m+1) m到n行缩进 :3,5> 第3行到5行进行右缩进
m>n 等价于命令 :m,m+n-1> m行开始共n行缩进一次 :3>3 第3行起包括第3行后3行进行右缩进
删除复制和粘贴
寄存器
类型 含义 表达方式 举例 特点
无名寄存器 默认寄存器 "" "" p=p 会被最后一条覆盖
数字寄存器 "+{0-9}缓存最近10次操作 "0 "{1-9} "0p "1p 0用于复制专用1-9 用于最近9次行删除或者修改记录
有名寄存器 26英文字母命名有名寄存器 "[a-z]/[A-Z] "ayw "A会通过^J追加到" a寄存器中
黑洞寄存器 有去无回 "_ "_dw 只想删除而不想覆盖 无名寄存器
按键操作
d = delete = cut 剪贴
y = yank 约等于 copy 类似于复制
p/P = put 约等于 paste 粘贴到光标后/光标前
u=undo 撤销之前操作
ctrl+r = redo 重做/恢复之前操作
yw 复制当前光标单词
y2w 复制正向两个单词
p/P = put 约等于 paste 粘贴到光标后/光标前
yy类似于dd 复制当前光标整行
组合删除
x/X 删除光标下/前单个字符
dw 删除一个单词,光标必须在词首
d{hjkl} 删除到上下左右一个操作前的字符
d$=D 删除光标到行尾的字符
d^ 删除光标到行首的字符
dd 删除当前整行
{n}dd 向下删除n行,包括当前行
5dw 删除正向单词五次
3w 正向移动单词三次
D3w 正向删除3w动作
2d3w 正向删除3w动作两次
插入
shift + i/a 插入模式并移动到行首/尾
a/i 光标后/前插入
[n]O/o 行前/后插入n次
[n]+i 插入模式重复n次 // 按键 3i,输入hello,world! ,然后ESC ,会再多出四个此字符串
转换
~ 单词字符大小写转换
g~w 单词大小写转换
g~$ 或者 g~~ 整行大小写转换 ,前者需要光标在行首
gU/uw 单词转换成大/小写
查找
F/f{char} 反向/正向查找单个字符
t{char} 查找字符前一个字符
dt{char}/df{space} 删除光标到查找字符间字符
/{char} 命令行查找 ,n向下查找 N向上查找
替换
s/going/rolling/g 当前行所有going替换成rolling
%s/going/rolling/g %匹配所有范围
shift+r/R 替换模式
r 单个字符替换
cw 修改单个单词
c$/C 类似于d操作 修改光标之后行内容
缓冲区与多文件编辑
缓冲区
缓冲区列表
:files
:buffers
:ls
缓冲区列表指示状态
标记 含义
a 激活缓冲区,缓冲区被加载且显示
h 隐藏缓冲区,缓冲区被加载但不显示
% 当前缓冲区
# 交换缓冲区
= 只读缓冲区
+ 已经更改缓冲区
- 不可改缓冲区,'modfiable'选项不置位
vim *.php
缓冲区列表操作
:bp 上一个缓冲区
:bn 下一个缓冲区
:bf 到第一个缓冲区
:bl 到最后一个缓冲区
:buffer Number/File_name 指定缓冲区
:ball 编辑所有缓冲区
Ctrl+^/b# 切换到前一个buffer(交换buffer)
:qall! 退出全部缓冲区
:wall! 写入全部缓冲区
:badd 添加一个缓冲区,但不设置为当前缓冲区
:bd 删除缓冲区
:n,m/%bd 删除n到m编号/所有的缓冲区文件
:bufdo{CMD} bufdo set number
:E /:bd 打开文件目录列表/返回到最初的buffer
多窗口操作(分屏)与标签分组
分屏操作
指令 含义
vim -O/on 水平/垂直分屏 n:数字,分屏的数量,o:open
ctrl+w+c/q 关闭当前窗口/最后一个窗口时退
ctrl+w s 上下分割当前打开的文件
:sp filename 上下分隔,并同时打开一个新的文件
ctrl+w v 左右分隔当前打开的文件
:vsp filename 左右分隔,并打开一个新的文件
移动分屏和尺寸移动
指令 含义
ctrl+w K 向上移动
ctrl+w J 向下移动
ctrl+w L 向右移动
ctrl+w H 向左移动
ctrl+w + 增加高度
ctrl+w - 减少高度
ctrl+w = 让所有屏的高度一致
宏操作案例一
文件内容显示如下
1
2
。
。
100
需要以下操作
进入普通模式
在第一行写入1
按qa + yyp + (ctrl+a)
按q退出
按98@a,则会出现1-100的数字
宏保存
viminfo /vimrc file
保存历史和非空寄存器
vim启动时读取
容易被覆盖
可视化模式
三种子模式
v 激活面向字符的可视模式
V/shift+v 激活面向行的可视模式
ctrl+v 激活面向列块的可视模式
gv 重选上次的高亮选区
o 切换选取
Linux之awk命令
标签:
awk
1.awk行处理方式与格式
awk一次处理一行内容
awk对每行可以切片处理
awk '{print $1}' //输出首个单词 uname -a | awk '{print $1}'
命令行格式
awk [options] 'command' file(s)
脚本格式
awk -f awk-script-file file(s)
command1:pattern {awk操作命令}
操作命令:内置函数: print() printf() getline()
控制指令: if(){...}else{} while(){...}
awk 内置变量 (1)
$0 表示整个当前行
$1 每行的第一个字段
$2 每行的第二个字段
awk内置参数 :分隔符
options : -F field-separator(默认是空格)
例子:
awk -F ':' '{print $1}' /etc/passwd //打印用户名
awk 内置变量 (2)
NR:每行的记录号
NF:字段数量变量
FILENAME:正在处理的文件名
例子:
awk -F ':' '{print NR,NF,FILENAME}' /etc/passwd
2.awk内嵌参数应用
awk内置参数应用
1)
awk -F ':' '{if($3>100)print $1,$3}' /etc/passwd
2)
sed -n '/FastCGI/P' error.log | awk '{print $1,$2}'
等于
cat error.log |awk '/FastCGI/{print $1,$2}'
逻辑判断式
awk逻辑
~,!~ : 匹配正则表达式
==,!=,<,> : 判断逻辑表达式
例子:
awk -F ':' '$1~/s.*/{print $1}' /etc/passwd //匹配 s开头的用户名
使用awk扩展格式
awk [options] 'command' file(s)
command2扩展
BEGIN{print "start"}pattern{commands}END{print "end"}
3.awk内嵌程序应用
awk处理过程
案例(-)
统计当前文件夹下的文件/文件夹占用的大小
ls -l | awk 'BEGIN{size=0}{size+=$5}END{print "size is "size/2014/2014"M"}'
案例(二)
统计显示/etc/passwd的账户总人数
awk -F ':' 'BEGIN{count=0}$1!~/^$/{count++}END{print count}' /etc/passwd //去掉空行
统计uid>100的用户名
awk -F ':' 'BEGIN{count=0}{if($3>100)name[count++]=$1}END{for(i=0;i<count;i++)print i,name[i]}' /etc/passwd
案例(三)
统计netstat -anp 状态下LISTENING和CONNECTED的连接数量
netstat -anp | awk '/CONNECTED|LISTENING/{if($4=="ACC"){sum[$7]++}else{sum[$6]++}}END{for(i in sum)print i,sum[i]}'
php之stream_get_line()和fget()读取文件
php之stream_get_line函数和fget函数读取文件
1.txt内容如下
1111111111111111111112111111111111111111111211111111111111233333333333333
stream_get_line示例
<?php
$file_path = './1.txt';
$fp = fopen($file_path, 'r') or die("open file failure!");
$line = 0;
if ($fp) {
while ($info = stream_get_line($fp, 8192, "2")) { //以2为分隔符
$line++;
}
fclose($fp);
}
echo $line; //结果是4
fgets示例
<?php
$file_path = './1.txt';
$fp = fopen($file_path, 'r') or die("open file failure!");
$line = 0;
if ($fp) {
while ($info = fgets($fp, 8192)) {
$line++;
}
fclose($fp);
}
echo $line; //结果是1
总结:
stream_get_line()函数与fgets()几乎相同,只是它允许除标准、标准和\r\n之外的行尾分隔符,并且不返回分隔符本身。
1.txt内容如下
1111111111111111111112111111111111111111111211111111111111233333333333333
stream_get_line示例
<?php
$file_path = './1.txt';
$fp = fopen($file_path, 'r') or die("open file failure!");
$line = 0;
if ($fp) {
while ($info = stream_get_line($fp, 8192, "2")) { //以2为分隔符
$line++;
}
fclose($fp);
}
echo $line; //结果是4
fgets示例
<?php
$file_path = './1.txt';
$fp = fopen($file_path, 'r') or die("open file failure!");
$line = 0;
if ($fp) {
while ($info = fgets($fp, 8192)) {
$line++;
}
fclose($fp);
}
echo $line; //结果是1
总结:
stream_get_line()函数与fgets()几乎相同,只是它允许除标准、标准和\r\n之外的行尾分隔符,并且不返回分隔符本身。
关于栈内存和堆内存
数据结构中的栈和堆:
栈:是一种连续储存的数据结构,具有先进先出的性质。通常的操作有入栈(压栈)、出栈和栈顶元素,就要将之前的所有元素出栈才能完成。类比现实中的箱子一样。
堆:是一种非连续的树形储存结构,每个节点有一个值,整棵树是经过排序的。特点是根节点的值最小(或最大),且根节点的两个子树也是一个堆。常用来实现优先队列,存取随意。
内存中的栈区和堆区:
一般说的内存,指的是计算机的随机储存器(RAM),程序都在这里面运行。
栈内存:由程序自动向操作系统申请分配以及回收,速度快,使用方便,但程序员无法控制。若分配失败,则提示栈溢出错误。注意,const局部变量也存储在栈区内,栈区向地址减小的方向增长。
堆内存:程序员向操作系统申请一块内存,当系统收到程序的申请时,会遍历一个记录空闲内存地址的链表,寻找第一个空间大于所申请空间的堆栈点,然后将该结点从空间结点链表中删除,并将该节点的空间分配给程序。分配的速度较慢,地址不连续,容易碎片化。此外,由程序员申请,同时也必须由程序员负责销毁,否则会导致内存泄漏。
关于堆和栈区别的比喻:
堆和栈的区别可以引用一位前辈的比喻来看出:
使用栈就象我们去饭馆里吃饭,只管点菜(发出申请)、付钱、和吃(使用),吃饱了就走,不必理会切菜、洗菜等准备工作和洗碗、刷锅等扫尾工作,他的好处是快捷,但是自由度小。
使用堆就象是自己动手做喜欢吃的菜肴,比较麻烦,但是比较符合自己的口味,而且自由度大
栈:是一种连续储存的数据结构,具有先进先出的性质。通常的操作有入栈(压栈)、出栈和栈顶元素,就要将之前的所有元素出栈才能完成。类比现实中的箱子一样。
堆:是一种非连续的树形储存结构,每个节点有一个值,整棵树是经过排序的。特点是根节点的值最小(或最大),且根节点的两个子树也是一个堆。常用来实现优先队列,存取随意。
内存中的栈区和堆区:
一般说的内存,指的是计算机的随机储存器(RAM),程序都在这里面运行。
栈内存:由程序自动向操作系统申请分配以及回收,速度快,使用方便,但程序员无法控制。若分配失败,则提示栈溢出错误。注意,const局部变量也存储在栈区内,栈区向地址减小的方向增长。
堆内存:程序员向操作系统申请一块内存,当系统收到程序的申请时,会遍历一个记录空闲内存地址的链表,寻找第一个空间大于所申请空间的堆栈点,然后将该结点从空间结点链表中删除,并将该节点的空间分配给程序。分配的速度较慢,地址不连续,容易碎片化。此外,由程序员申请,同时也必须由程序员负责销毁,否则会导致内存泄漏。
关于堆和栈区别的比喻:
堆和栈的区别可以引用一位前辈的比喻来看出:
使用栈就象我们去饭馆里吃饭,只管点菜(发出申请)、付钱、和吃(使用),吃饱了就走,不必理会切菜、洗菜等准备工作和洗碗、刷锅等扫尾工作,他的好处是快捷,但是自由度小。
使用堆就象是自己动手做喜欢吃的菜肴,比较麻烦,但是比较符合自己的口味,而且自由度大
golang声明通道struct{}
sign := make(chan struct{}, 3)
sign <- struct{}{}
<-sign
声明通道sign的时候以chan struct{}作为其类型的。其中的类型字面量struct{}有些类似于空接口类型interface{},它代表了既不包含任何字段也不拥有任何方法的空结构体类型。
struct{}类型值的表示方法只有一个,即:struct{}{}。并且,它占用的内存空间是0字节。确切的说,这个值在整个go程序中永远都只会存一份。虽然我们无数次的使用这个值的字面量,但是用到的却都是同一个值。
当我们仅仅把通道当做传递某种简单信号的介质的时候,用struct{}作为其元素类型是再好不过的了。
sign <- struct{}{}
<-sign
声明通道sign的时候以chan struct{}作为其类型的。其中的类型字面量struct{}有些类似于空接口类型interface{},它代表了既不包含任何字段也不拥有任何方法的空结构体类型。
struct{}类型值的表示方法只有一个,即:struct{}{}。并且,它占用的内存空间是0字节。确切的说,这个值在整个go程序中永远都只会存一份。虽然我们无数次的使用这个值的字面量,但是用到的却都是同一个值。
当我们仅仅把通道当做传递某种简单信号的介质的时候,用struct{}作为其元素类型是再好不过的了。
go语句及其执行规则(goroutine)
go语言不但有着独特的并发编程模型,以及用户级线程goroutine,还拥有强大的用于调度goroutine、对接系统线程的调度器。
这个调度器是go语言运行时系统的重要组成部分,它主要负责统筹调配go并发编程模型中的三个元素,即:G(gotoutine的缩写),P(process的缩写)和M(machine的缩写)。
其中M指代的是系统级线程。而P指的是一种可以承诺若干个G,而且能够使这些G适时地与M进行对接,并得到真正运行的中介。
这个调度器是go语言运行时系统的重要组成部分,它主要负责统筹调配go并发编程模型中的三个元素,即:G(gotoutine的缩写),P(process的缩写)和M(machine的缩写)。
其中M指代的是系统级线程。而P指的是一种可以承诺若干个G,而且能够使这些G适时地与M进行对接,并得到真正运行的中介。
//demo1: package main import ( "fmt" //"time" ) func main() { num := 10 sign := make(chan struct{}, num) for i := 0; i < num; i++ { go func() { fmt.Println(i) sign <- struct{}{} }() } // 办法1。 //time.Sleep(time.Millisecond * 500) // 办法2。 for j := 0; j < num; j++ { <-sign } }
golang让多个goroutine按照既定的顺序运行
//demo1: package main import ( "fmt" "sync/atomic" "time" ) func main() { var count uint32 trigger := func(i uint32, fn func()) { for { if n := atomic.LoadUint32(&count); n == i { fn() atomic.AddUint32(&count, 1) //count++ break } time.Sleep(time.Nanosecond) } } for i := uint32(0); i < 10; i++ { go func(i uint32) { fn := func() { fmt.Println(i) } trigger(i, fn) }(i) } trigger(10, func() {}) }