从零开始的人生

从零开始,你今天所做的一切最后的后果还是得由你来承担,此为因果!


  • 首页

  • 分类

  • 归档

  • 标签

linux系统与进程文件打开数设置

发表于 2019-08-08 | 分类于 配置 |

场景是这样的:小伙伴在mongodb主备环境把备机停了一段时间后重新启动,mongodb主机在同步数据的过程中出现”too many open files error”, 在网上查到的说可以通过设置linux系统文件打开数来解决(服务器是Ubuntu),
然后开始一顿操作修改 /etc/security/limits.conf 进行设置

1
2
3
4
5
6
7
8
9
vi /etc/security/limits.conf 添加以下内容
* soft nproc 65533
* hard nproc 65533
* soft nofile 65533
* hard nofile 65533
root soft nproc 65533
root hard nproc 65533
root soft nofile 65533
root hard nofile 65533

重启后 ulimit -a 查看系统文件打开数已修改。但是查看mongodb进程限制仍未改变

1
cat /proc/pid/limits // pid 为mongod的进程id

登陆mongo通过,查看mongo连接数仍未改变。

1
db.runCommand({serverStatus:1}).connections

最后在stackoverflow 找到答案, 修改/etc/systemd/user.conf 以及 /etc/systemd/system.conf
添加以下内容

1
DefaultLimitNOFILE=65535

然后reboot重启后再看已生效

golang在recover里面发生panic

发表于 2018-05-12 | 分类于 programming |

最近在排查代码的时候,发现同事有一段代码在recover里面关闭连接结果导致panic,造成程序异常退出的现象。
用一个小试例记录一下,正常代码在recover里面打印捕获到的信息为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package main
import "fmt"
func main() {
for i := 0; i < 10; i++ {
f(i)
}
fmt.Println("end main")
}
func f(i int) {
defer func() {
if r := recover(); r != nil {
fmt.Println("Recover :", r)
// panic(r)
}
}()
if i == 8 {
panic("panic when i == 8")
}
fmt.Println(i)
}

程序可以正常执行,输出如下:

0
1
2
3
4
5
6
7
Recover : panic when i == 8
9
end main

但是如果在recover里面的panic注释去掉,模拟产生panic,再次运行就会发生错误。

所以切忌不要在recover里面发生panic,如关闭不存在的连接等。

记一次golang经典错误--for循环中的go协程调用

发表于 2018-05-06 | 分类于 programming |

最近在开发过程中遇到问题,追踪了很久后发现是golang的经典问题,在for循环中使用了goroutine,在goroutine中使用了for循环的参数。

问题现象:

在使用rabbitmq进行数据传递时,发送端在一次循环中发送了8000条id不同的数据到rabbitmq的队列中,接收端监听该队列并从rabbitmq中取数据。接收到的数据在程序中处理后写入数据库,结果发现数据中并没有写入8000条数据。最后定位原因为:在接收数据时在for循环中使用go协程,导致同时收到两条数据时,协程都是使用的后一条数据,入库因为是同一条数据,导致主键重复,插入失败,所以数据库中没有8000条数据。错误代码大致如下:

1
2
3
4
5
for d := range msgs {
go func() {
handler(d)
}()
}

用一个简单的程序模拟该错误为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package main
import (
"fmt"
"time"
)
func main() {
for i := 0; i < 10; i++ {
go func() {
fmt.Println(i)
}()
}
time.Sleep(2 * time.Second)
}

输出为:

7
10
10
10
10
10
10
10
10
10

问题解析:

闭包go协程里面引用的是变量i的地址;所有的go协程启动后等待调用,在上面的协程中,部分协程很可能在for循环完成之后才被调用,所以输出结果很多都是10;正常输出最多到9哦

解决方法一

通过参数传递数据到协程

1
2
3
4
5
for i := 0; i < 10; i++ {
go func(data int) {
fmt.Println(data)
}(i)
}

解决方法二

在for循环中加一个临时变量tmp,每次将i的值赋值给tmp,然后将tmp通过参数传进协程。

此方法可以解决不能通过参数传递数据的情况(某些第三方库不能传参数)

1
2
3
4
5
6
for i := 0; i < 10; i++ {
tmp := i
go func(data int) {
fmt.Println(data)
}(tmp)
}

go-micro安装错误处理

发表于 2018-04-18 | 分类于 配置 |

go-micro 安装错误种的错误处理

按照github上micro的教程使用非docker方式安装的错误处理

1
go get -u github.com/micro/micro

会报关于 golang.org/x/crypto/acme/autocert 的错误。

  • 情况1:由于防火墙的原因导致连接网络失败。
  • 情况2:由于之前手动拷贝安装过crypto这个包会报使用一个未知的版本控制系统的错误

解决办法:

1
2
cd $GOPATH/src/golang.org/x/
git clone https://github.com/golang/crypto.git

注意:

1
2
1、不要手动下载然后拷贝过去,会报上述情况2的错误
2、之前手动拷贝过crypto的话,先备份或删除再git clone

我不知道的go里面的json操作

发表于 2018-04-05 | 分类于 programming |

一、可以在tag里面声明把Integer,Float,Boolean类型的值直接序列化时转换为string类型返回

1
2
3
4
{
ID int64 `json:"id,string"`
Name string `json:"name"`
}

这样在返回给前端的时候就不用自己把int64转成然后再返回给前端了,毕竟javascript不支持int64

二、 可以把tag写成”-“, 表示忽略该字段序列化的时候忽略该字段

1
2
3
4
5
{
ID int64 `json:"id,string"`
Name string `json:"name"`
Age int `json:"-"`
}

三、自己实现MarshalJSON 和 UnmarshalJSON 替换掉默认的Marshal和UnMarshal方法,自己定制序列化和反序列化

需要了解更多json的高级操作可以看json的源码注释

/src/encoding/json/encode.go

和这篇文章

https://blog.gopheracademy.com/advent-2016/advanced-encoding-decoding/

终端使用privoxy搭配ss代理上网

发表于 2017-11-18 | 分类于 配置 |

作为一名程序员,翻墙技能是必备的,但是你会发现仅仅使用浏览器翻墙师不够,有时你
还需要在终端下git个项目,但是发现由于墙的原因老是下载不好,这个时候,就需要配置下终端代理翻墙了,
当然你也可以配置各种源或是浏览器下载了在安装(如果不觉得每次都这样很麻烦的话)。
下面介绍下实现终端代理翻墙的方法。

一、使用前必须具备的条件

有ss或ssr可使用于科学上网

能在终端安装使用的代理http(https)请求的工具,本文使用privoxy

二、运行你正在使用的ss(ssr)工具,通过浏览器验证能翻墙上外网

三、安装privoxy

1
sudo apt-get install privoxy

安装完后修改privoxy的配置文件

1
sudo vim /etc/privoxy/config

在750行左右找到listen-address,在下面添加两行,内容如下(注意不要加注释,也就是前面不要加#号)

1
2
listen-address 0.0.0.0:8118
forward-socks5 / localhost:1080 .

运行privoxy

修改用户主目录下的shell文件,可能是.bashrc或者.bash_profile或者.zshrc
添加内容如下

1
2
3
/usr/sbin/privoxy /etc/privoxy/config
export http_proxy=http://127.0.0.1:8118
export https_proxy=https://127.0.0.1:8118

保存退出后,source 你刚刚修改的文件

1
source ~/.zshrc

这个时候使用

1
wget www.google.com

验证下是否能获取到index.html文件,在终端无法翻墙的情况下是获取不到的,
国内网站,比如百度就可以

到此,配置结束。

注意

privoxy代理了终端所有的http(https)请求,有时可能你的一些操作可能不需要代理,
但是老是失败,可以通过错误信息看看是不是因为请求被代理到8118端口了,如果是,你需要
找到privoxy的程序,然后kill掉,再重试下应该就可以了。如果需要重新启用privoxy,再次
source ~/.zshrc或者service restart privoxy就可以了。

mongo索引使用2

发表于 2017-10-15 | 分类于 数据库 |

前面说了mongo的索引创建,查看,删除;今天主要来说下复合索引和索引使用时的注意事项。

复合索引

mongodb支持用户为多个域创建索引,也就是复合索引。
还是以用户登陆记录集合loginrecord为例,假设我们要查询用户登陆的每条记录,
并按登陆时间逆序显示,所以需要为username和logintime创建复合索引。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
> db.loginrecord.createIndex({username:1,logintime:-1})
> db.loginrecord.stats()
{
"ns" : "test.loginrecord",
"count" : 1,
"size" : 72,
"avgObjSize" : 72,
"storageSize" : 4096,
"numExtents" : 1,
"nindexes" : 3,
"lastExtentSize" : 4096,
"paddingFactor" : 1,
"systemFlags" : 0,
"userFlags" : 0,
"totalIndexSize" : 24528,
"indexSizes" : {
"_id_" : 8176,
"username_-1" : 8176,
"username_1_logintime_-1" : 8176
},
"ok" : 1
}

此时我们通过

1
db.loginrecord.find({username:"zhangsan"}).sort({logintime:-1})

进行查询时就会用到{username:1,logintime:-1}的复合索引

使用注意事项

1、find方法和sort方法都会用到索引

例如,当我们需要查看用户的登陆记录,并将记录按用户名按字母序输出

1
db.loginrecord.find({}).sort({username:1})

sort方法就会用到以username为键的索引。
如果我们要查询用户名为zhangsan的登陆记录,并按登陆时间logintime逆序输出

1
db.login.record.find({username:"zhangsan"}).sort({logintime:1})

此时就会用到username和logintime的复合索引(注意:不是username,logintime的单独索引)。

2、复合索引字段的顺序很重要

复合索引创建时字段的先后顺序是很重要的,需要知道的是以{username:1,logintime:-1}和以{logintime:-1,username:1}
创建的复合索引是不同的,{username:1,logintime:-1}是先以username分组然后再在满足条件的分组里以logintime分组;
而{logintime:-1,username:1}则是先按logintime分组然后再在满足条件的分组里以username分组。
为了说明复合索引顺序的问题,我们先为loginrecord集合加一个字段online用来表示用户当前是否在线。

1
2
3
4
5
type LoginRecord struct {
UserName string `bson:"username"`
LoginTime time.Time `bson:"logintime"`
OnLine bool `bson:"online"`
}

现在假设用户登陆后online字段为true,用户登出后改为false,于是在只允许单用户登陆的情况下,loginrecord集合里面
一个用户就只可能有一条online为true的记录。假设用户量很大且登陆频繁的情况下,我们需要查找当前在线的用户,
按用户名字母序输出。这个时候就需要建立online和username两个字段的复合索引。于是我们就要考虑

情况1:建立{username:1,online:1}的复合索引
情况2:建立{online:1,username:1}的复合索引

情况1:建立{username:1,online:1}的复合索引

先以用户名分组,在已online分组,此时由于每个用户都存在多条登陆记录而只有一条在线记录,
此时每个username分组我们都只能找到一条记录。然后又在把每条记录以username排序,实际是先删选出来然后再排序,
索引{username:1,online:1}就没有创建的意义。

情况2:建立{online:1,username:1}的复合索引

先以online分组,在以username分组,此时就能很方便的从online为true的分组里找到所有记录。查询速度是
情况1所不能比的。所以根据自己的业务需要设定复合索引的字段顺序很重要。

注: mongodb的索引的数据是B树,感兴趣的可以自己搜索以便更好理解mongodb的索引使用

3、复合索引还可以支持与索引字段前缀匹配的查询

复合索引除了支持所有索引字段的匹配查询外,还支持与索引字段前缀匹配的查询。怎么理解呢?
例如上面我们建立了{username:1,logintime:-1}的索引,此时不但可以同时匹配{username:”xxx”,logintime:”xxx”}
这种查询,还支持{username:”xxx”}这种查询,但不支持{logintime:”xxx”}这种查询,因为logintime不是
{username:1,logintime:-1}的前缀

1
2
db.loginrecord.find({username:"zhangsan",logintime:{"$gt":ISODate("2017-06-08T01:41:28.944Z")}})
db.loginrecord.find({username:"zhangsan"})

因此,如果你建立了{username:1,logintime:-1},就不必在建立{username:1}或者{username:-1}这种索引,
要知道索引多了是会影响插入和更新操作的。

mongo索引使用1

发表于 2017-10-14 | 分类于 数据库 |

在数据库做查询时,适当的索引可以提高查询的效率,避免全表扫描。
mongo建立索引的时候需要指定索引为升序1或降序-1,对于单个字段建立索引时这个排序规则并不重要,
因为mongodb可以沿任一方向遍历索引。

索引创建

假设我有一个loginrecord集合(下面是golang 定义),有两个字段,string类型的username和时间类型的logintime

1
2
3
4
type LoginRecord struct {
UserName string `bson:"username"`
LoginTime time.Time `bson:"logintime"`
}

记录用户和该用户的登陆记录,现在对用户名建立索引;

1
db.loginrecord.createIndex({username:1})

索引查看

1
db.loginrecord.getIndexes()

此时会看到有两个索引,一个是我们刚刚建立的username,另一个是mongodb默认以_id创建的索引。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
> db.loginrecord.createIndex({username:1})
> db.loginrecord.getIndexes()
[
{
"v" : 1,
"key" : {
"_id" : 1
},
"ns" : "test.loginrecord",
"name" : "_id_"
},
{
"v" : 1,
"key" : {
"username" : 1
},
"ns" : "test.loginrecord",
"name" : "username_1"
}
]

不过我更喜欢用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
> db.loginrecord.stats()
{
"ns" : "test.loginrecord",
"count" : 1,
"size" : 72,
"avgObjSize" : 72,
"storageSize" : 4096,
"numExtents" : 1,
"nindexes" : 2,
"lastExtentSize" : 4096,
"paddingFactor" : 1,
"systemFlags" : 1,
"userFlags" : 0,
"totalIndexSize" : 16352,
"indexSizes" : {
"_id_" : 8176,
"username_1" : 8176
},
"ok" : 1
}

来查看索引,indexSizes里面的就是索引,1表示升序,-1表示降序

索引删除

1
2
> db.loginrecord.dropIndex({username:1})
{ "nIndexesWas" : 2, "ok" : 1 }

索引删除的时候也必须指定升序还是降序,不然会找不到报错

linux_ssr_use

发表于 2017-09-09 | 分类于 配置 |

linux 使用ssr进行科学上网

ss使用

之前一直使用的命令行sslocal对shadowsocks进行连接,使用简单,顺便记录下:

先下载shadowsocks

1
2
sudo apt-get install python-pip
pip install shadowsocks

下载完成后把ss的服务器配置放到 /etc/shadowsocks.json(没有文件就新建一个)里
配置类似,替换对应的字段即可

1
2
3
4
5
6
7
8
9
10
11
12
{
"server": "server.com",
"local_address": "127.0.0.1",
"local_port": 1080,
"timeout": 300,
"workers": 1,
"server_port": 12345,
"password": "mypassword",
"method": "aes-256-cfb",
"obfs": "plain",
"protocol": "origin"
}

连接方法

1
sslocal -c /etc/shadowsocks

ssr 使用

ss使用方式简单,为了跟上大家的步伐,当然还有就是ss无法使用混淆,所以今天更换为ssr

步骤1:从github上下载shadowsocksr

1
2
sudo apt-get install git
git clone https://github.com/shadowsocksr/shadowsocksr.git

步骤2:从github上下载可以在界面上添加配置的客户端 electron-ssr

下载地址 ,下载最新的release版本即可,下载完成后解压,
打开electron-ssr即可看到界面,然后选择步骤1中下载的 shadowsocksr/shadowsocks 所在目录,保存后添加配置,
我一般直接拷贝ssr连接到最后一个ssr选项中。添加完后配置在~/.config/electron-ssr/shadowsocks.json中,
如果配置错误把这个文件删了重新打开electron-ssr添加(我不删除时再次打开界面不会显示)

1
2
3
tar xzvf electron-ssr*
cd electron-ssr*
./electron-ssr

到这里我原以为到shadowsocksr/shadowsocks目录下执行

1
python local.py -c ~/.config/electron-ssr/shadowsocksr.json

就可以使用了,但是执行时出错了,看了下错误是因为配置的加密方式是 chacha20,但是系统默认是不支持的,so

步骤3:从github上下载 libsodium,由于最新的1.0.13版本在我电脑上安装失败,所以我下载了1.0.12的release版本,

下载地址,下载完成后

1
2
3
4
5
6
7
tar xzvf libsodium-1.0.12.tar.gz
cd libsodium*
sudo ./configure
sudo make -j4 && make install
cd /etc/ld.so.conf.d
sudo vim usr_local_lib.conf (添加一句 /usr/local/lib ,保存退出)
sudo ldconfig

到这里安装都算完了,可是当我再次打开 local.py时还是报错,于是我看了下 ~/.config/electron-ssr/shadowsocksr.json的内容,
有很多字段和ss的不一样,于是我就想把~/.config/electron-ssr/shadowsocksr.json服务器配置字段改成ss时的配置试试,于是配置从

1
2
3
4
5
6
7
8
9
10
11
12
{
"host": "server.com",
"localAddr": "127.0.0.1",
"localPort": "1080",
"method": "chacha20",
"obfs": "tls1.2_ticket_auth",
"obfsparam": "",
"password": "password",
"port": "8388",
"protocol": "auth_chain_a",
"remark": ""
}

改为(其他的内容我都删除了,然后主要修改了一些键值,去掉了端口的引号)

1
2
3
4
5
6
7
8
9
10
11
12
{
"server": "server.com",
"local_address": "127.0.0.1",
"local_port": 1080,
"method": "chacha20",
"obfs": "tls1.2_ticket_auth",
"obfsparam": "",
"password": "password",
"server_port": 8388,
"protocol": "auth_chain_a",
"remark": ""
}

再次通过

1
python local.py -c ~/.config/electron-ssr/shadowsocksr.json

打开就可以使用了,真是折腾啊!

总结下:

  • 下载shadowsocksr
  • 下载electron-ssr方便添加配置(可以添加了还是要改,也就只能来解析一些只提供ssr连接,不提供配置的站点的配置)
  • 下载libsodium已支持chacha20的加密方式
  • 修改配置

总算用上了,不过应该还有更简单的方式,如果有那位知道,请一定要告诉我哦。再次谢过!

baorongquan

baorongquan

find myself

9 日志
3 分类
6 标签
© 2019 baorongquan
由 Hexo 强力驱动
主题 - NexT.Muse
访问人数 访问总量 次