keep running

  • Home

  • Tags

  • Archives

effective go learning 2

Posted on 2018-11-14 Edited on 2020-05-17

从Two-dimensional slices开始,使用中文版的effctive_go学习
https://www.kancloud.cn/kancloud/effective/72207

Data:

二维切片:

  • Go的数组和切片都是一维的。要创建等价的二维数组或者切片,需要定义一个数组的数组或者切片的切片。

Maps:

  • Map是一种方便,强大的内建数据结构,其将一个类型的值(key)与另一个类型的值(element或value) 关联一起。
  • key可以为任何 定义了等于操作符 的类型,例如整数,浮点和复数,字符串,指针,接口(只要其动态类型支持等于操作),结构体和数组。
  • 切片不能 作为map的key,因为它们没有定义等于操作。和切片类似,map持有对底层数据结构的引用。如果将map传递给函数,其对map的内容做了改变,则这些改变对于调用者是可见的。
1
2
3
4
5
6
7
attended := map[string]bool{
"Ann": true,
"Joe": true,
...}

if attended[person] { // will be false if person is not in the map
fmt.Println(person, "was at the meeting")}
  • 如果只测试是否在map中存在,而不关心实际的值,你可以将通常使用变量的地方换成空白标识符(_)
1
_, present := timeZone[tz]
  • 要删除一个map项,使用delete内建函数,其参数为map和要删除的key。即使key已经不在map中,这样做也是安全的。
1
delete(timeZone, "PDT")  // Now on Standard Time
  • map不太好判断是否存在某个key,如果key不存在返回的对应类型的零值,如果已有key的value恰好为零值会导致误判

打印输出

  • Go中的格式化打印使用了与C中printf家族类似的风格,不过更加丰富和通用。这些函数位于fmt程序包中,并具有大写的名字:fmt.Printf,fmt.Fprintf,fmt.Sprintf等等。字符串函数(Sprintf等)返回一个字符串,而不是填充到提供的缓冲里。
  • 你不需要提供一个格式串。对每个Printf,Fprintf和Sprintf,都有另外一对相应的函数,例如Print和Println。这些函数不接受格式串,而是为每个参数生成一个缺省的格式。Println版本还会在参数之间插入一个空格,并添加一个换行,而Print版本只有当两边的操作数都不是字符串的时候才增加一个空格。在这个例子中,每一行都会产生相同的输出。
  • 格式化打印函数fmt.Fprint等,接受的第一个参数为任何一个实现了io.Writer接口的对象;变量os.Stdout和os.Stderr是常见的实例。
  • 如果只是想要缺省的转换,像十进制整数,你可以使用 通用格式%v(代表“value”);这正是Print和Println所产生的结果。而且,这个格式可以打印任意的的值,甚至是数组,切片,结构体和map。
  • 当打印一个结构体时,带修饰的格式 %+v会将结构体的域使用它们的名字进行注解,对于任意的值,格式%#v会按照完整的Go语法打印出该值。
  • 还可以通过 %q 来实现带引号的字符串格式,用于类型为 string或[]byte 的值。格式 %#q 将尽可能的使用反引号。(格式%q还用于整数和符文,产生一个带单引号的符文常量。)
  • %x 用于字符串,字节数组和字节切片,以及整数,生成一个 长的十六进制字符串,并且如果在格式中 有一个空格(% x),其将会在 字节中插入空格。
  • 不要在Sprintf里面调用接收者的String方法,否则会造成无穷递归,如下。只有%s匹配才会调用MyString的String方法
1
2
3
4
5
6
type MyString string

func (m MyString) String() string {
return fmt.Sprintf("MyString=%s", m) // Error: will recur forever.
// return fmt.Sprintf("MyString=%s", string(m)) // OK: note conversion.
}
  • 另一种打印技术,是将一个打印程序的参数直接传递给另一个这样的程序。Printf的签名使用了类型…interface{}作为最后一个参数,来指定在格式之后可以出现任意数目的(任意类型的)参数。

append内建函数:

  • 其中T为任意给定类型的占位符。你在Go中是无法写出一个类型T由调用者来确定的函数。这就是为什么append是内建的:它需要编译器的支持。append所做的事情是将元素添加到切片的结尾,并返回结果。
1
2
3
4
5
func append(slice []T, elements ...T) []T

x := []int{1,2,3}
x = append(x, 4, 5, 6)
fmt.Println(x)
  • 如果想要在append中把一个slice添加到另一个slice要怎么做?在调用点使用 “…”,
1
2
3
4
x := []int{1,2,3}
y := []int{4,5,6}
x = append(x, y...)
fmt.Println(x)
  • 可以看出 “…” 的作用是,把一个slice转为对应的type,作为一个参数列表进行传递
Read more »

go系列-中间件

Posted on 2018-09-29 Edited on 2020-05-17

go中间件

最近看代码看到go中间件的代码,遂搜相关代码以及类似的框架进行学习

什么是中间件

  • 了解中间件前需要了解 ServeMux、DefaultServeMux、http.Handler、http.HandlerFunc、mux.HandleFunc、ServeHTTP 等相关知识和它们之间的关系[推荐]
    https://www.alexedwards.net/blog/a-recap-of-request-handling

  • context能做什么
    https://blog.questionable.services/article/map-string-interface/

gorilla系列 & Negroni

  • Go实战–Golang中http中间件(goji/httpauth、urfave/negroni、gorilla/handlers、justinas/alice)
    https://blog.csdn.net/wangshubo1989/article/details/79227443

  • Go实战–Gorilla web toolkit使用之gorilla/handlers
    https://blog.csdn.net/wangshubo1989/article/details/78970282

  • Go实战–Gorilla web toolkit使用之gorilla/context
    https://blog.csdn.net/wangshubo1989/article/details/78910935

  • gorilla/mux https://github.com/gorilla/mux (需要着重看一下 文中的Graceful Shutdown和 https://github.com/gorilla/mux#graceful-shutdown)

  • Negroni https://github.com/urfave/negroni

[代码学习](https://github.com/salmon7/go-learning/tree/master/middle

justinas/alice

// TO-DO

gitlab ci && docker-compose(1)-基础知识

Posted on 2018-09-27 Edited on 2020-05-17

工作中需要使用到gitlab ci和docker-compose,而docker是这两者的前提。网上docker学习资料有很多,但是有一大部分是过时的,官网也有详细的文档,但是快速阅读起来比较慢,毕竟不是母语。之前学习的时候走了很多弯路,现把最近搜集到比较好的资料分享出来,希望对大家有帮助

docker学习资料

Docker — 从入门到实践(一个不错的docker入门教程,极力推荐):

  • https://github.com/yeasy/docker_practice
  • https://docker_practice.gitee.io/

Docker 问答录(100 问):https://blog.lab99.org/post/docker-2016-07-14-faq.html

dockerfile中:https://docs.docker.com/engine/reference/builder/

dockerfile的最佳实践:https://docs.docker.com/develop/develop-images/dockerfile_best-practices/

docker之Dockerfile实践(以nginx为例,一步一步构建镜像):http://www.cnblogs.com/jsonhc/p/7767669.html

docker-compose学习资料

docker-compose中的environment:https://docs.docker.com/compose/compose-file/#environment

docker-compose中的变量:https://docs.docker.com/compose/compose-file/#variable-substitution

docker-comppose中的变量的优先顺序:https://docs.docker.com/compose/environment-variables/

Declare default environment variables in file:https://docs.docker.com/compose/env-file/

ENTRYPOINT的shell模式的副作用:https://docs.docker.com/engine/reference/builder/#entrypoint

vishnubob/wait-for-it https://github.com/vishnubob/wait-for-it

dockfile最佳实践:https://docs.docker.com/develop/develop-images/dockerfile_best-practices/

gitlab-ci学习资料

这部分学习大部分都是官网,不做过多的分享,只留一个默认变量的链接

ci的默认变量:https://docs.gitlab.com/ce/ci/variables/README.html

gitlab ci && docker-compose(6)-容器启动先后顺序

Posted on 2018-09-26 Edited on 2020-05-17

docker-compose容器启动先后顺序问题

App应用程序容器需要连接一个mysql容器,使用docker-compose启动容器组,应该怎么做?在docker-compose中,容器A依赖容器B,B容器会先启动,然后再启动A容器,但是B容器不一定初始化完毕对外服务。

先来看一段mysql官方对于这种问题的两段说明

No connections until MySQL init completes

  • If there is no database initialized when the container starts, then a default database will be created. While this is the expected behavior, this means that it will not accept incoming connections until such initialization completes. This may cause issues when using automation tools, such as docker-compose, which start several containers simultaneously.
  • If the application you’re trying to connect to MySQL does not handle MySQL downtime or waiting for MySQL to start gracefully, then a putting a connect-retry loop before the service starts might be necessary. For an example of such an implementation in the official images, see WordPress or Bonita.

所以一般有两种方法解决类似的问题

  • 程序层面改进,程序连接mysql部分需要有重连机制
  • 连接容器改进,在shell命令中判断mysql容器是否启动,如果未启动设定时间等待,如果启动了再启动应用程序

这里只说明第二种方法

使用wait-for-it.sh

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# docker-compose.yml
version: '3'

services:
app:
build:
context: .
dockerfile: DockerfileAPP
image: app:latest
ports:
- "127.0.0.1:8080:8080"
depends_on:
- mysql_db
command: /go/src/app/wait-for-it.sh mysql_db:3306 -s -t 30 -- /go/src/app/app-release start

mysql_db:
#build:
# context: .
# dockerfile: DockerfileMySQL
#image: mysql_db:latest
image: "mysql:5.7.22"
environment:
- MYSQL_ROOT_PASSWORD=test
expose:
- "3306"
volume:
- ./init_sql_script/:/docker-entrypoint-initdb.d/

注意这行代码

command: /go/src/app/wait-for-it.sh mysql_db:3306 -s -t 30 – /go/src/app/app-release start

-s 表示如果没有检测到host为mysql_db的3306端口,则不执行后面的命令;-t 30表示超时时间为30秒,更多配置见参考。wait-for-it.sh可以使用多次,比如需要等待msyql和redis,可以这么写command: /go/src/app/wait-for-it.sh mysql_db:3306 -s -t 30 -- command: /go/src/app/wait-for-it.sh redis:6379 -s -t 30 -- /go/src/app/app-release start

参考:

mysql官方docker说明:https://hub.docker.com/_/mysql/

Control startup order in Compose:https://docs.docker.com/compose/startup-order/

vishnubob/wait-for-it:https://github.com/vishnubob/wait-for-it

Docker-compose check if mysql connection is ready:https://stackoverflow.com/questions/42567475/docker-compose-check-if-mysql-connection-is-ready

gitlab ci && docker-compose(5)-容器启动环境变量传递

Posted on 2018-09-25 Edited on 2020-05-17

docker run和docker-compose启动容器环境变量传递

dockrfile中的ENV和CMD的关系(不考虑ENTRYPOINT)

  • docker run使用默认命令 && dockerfile中 ENV VERSION=100, CMD [“echo”,”$VERSION”]:输出空

  • docker run使用默认命令 && dockrfile中 ENV VERSION=100,CMD [“sh”,”-c”,”echo”,”$VERSION”]:输出空

  • docker run使用默认命令 && dockrfile中 ENV VERSION=100,CMD [“sh”,”-c”,”echo $VERSION”]:输出100

  • docker run使用默认命令 && dockrfile中 ENV VERSION=100,CMD echo $VERSION:输出100

小结:docker run使用默认命令中要读取容器内部的环境变量的话,一定要使用后两种方式。并且需要记住的是,使用默认命令的情况下主机的环境变量不会影响container的变量,比如在root的shell下执行export VERSION=101,对以上四个结果都不会有影响

docke run使用指定命令执行与shell环境变零的关系

docke run使用指定命令,docker run image_name echo $VERSION:则输出本地shell的VERSION变量,这个VERSION变量跟container一点关系都没有,完全取决于当前shell的环境变量。需要注意的是,由于docker run时一般是在root权限下,所以执行export VERSION=xxx 时,请先执行su -,避免因为使用sudo改变了实际的shell导致不能输出。

Read more »

gitlab ci && docker-compose(4)-mysql的添加权限

Posted on 2018-09-20 Edited on 2020-05-17

目前网上添加msyql用户和权限时,很多都是使用INSERT, UPDATE, DELETE 直接操作权限表,并且总结得参差不齐。根据官方网站应该使用 CREATE USER 语句创建用户,使用 GRANT 语句添加权限。

  • 在mysql:5.7.22中需要配合 /docker-entrypoint-initdb.d/ 目录初始化数据,这时可以在该目录的sql中可以添加权限的配置。
1
2
3
4
5
6
7
8
9
10
11
12
13
 -- 不应该在这里直接删除'root'@'%',否则会影响原本root应有的权限。可以通过该命令查看该命令的影响,SHOW GRANTS FOR 'root'@'%';
-- 如果不删用户root,会出现该权限 GRANT ALL PRIVILEGES ON *.* to 'root'@'%' WITH GRANT OPTION
-- 如果删了用户root,会出现该权限 GRANT USAGE ON *.* to 'root'@'%'
-- drop user 'root'@'%';

-- 密码在启动mysql容器或者在docker-compose.yml文件中时就应该指定,如
-- docker run -e MYSQL_ROOT_PASSWORD=test mysql:5.7.22
-- 或
-- environment:
-- - MYSQL_ROOT_PASSWORD=test
-- CREATE USER 'root'@'%' IDENTIFIED BY 'test';

GRANT ALL PRIVILEGES ON YOU_DATABASE.* TO 'root'@'%';
  • 当然建库建表前应该用if判断,不判断也行,因为本来就是新实例。
1
2
3
4
5
6
7
8
DROP DATABASE IF EXISTS YOUR_DATABASE;
CREATE DATABASE YOUR_DATABASE;

USE YOUR_DATABASE;

CREATE TABLE `your_table` (

) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='必要的注释';
  • 查看权限
1
SHOW GRANTS FOR 'root'@'%';

docker-compose up命令

运行docker-compose up时,如果以前未创建相应的镜像,则默认会创建镜像并且根据该镜像启动container;如果以前创建过镜像,则判断当前是否有对应的container,如果有则直接启动,如果没有则创建对应的container;

  • –build Build images before starting containers.

    • –build,加了这个选项后,每次运行 docker-compose up 都会构建镜像。构建镜像有另外一个专门的命令docker-compose build,可以使用–build-arg key=val 传入编译时参数,如下则为 –build-arg X=3 –build-arg Y=4。传入到docker-compose后,再传到dockerfile的 ARG 声明的同名变量中,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# docker-compose.yml
version: '3'

services:
app:
build:
context: .
dockerfile: docker/dockerfile
args:
X: ${X:-1} #如果X不传,则X为1
Y: ${Y:-2} #如果Y不传,则Y为2

# dockerfile
FROM some-image
# 包含编译时默认值
ARG X=1
ARG Y=gitlabci

# 编译时不包含默认值
#ARG X
#ARG Y

# 设置容器内部环境变量
ENV X=$X
ENV Y=$Y
  • –force-recreate Recreate containers even if their configuration and image haven’t changed.
    • –force-recreate,加了这个选项后,docker-compose会重新创建container,即使与它对应的镜像没有变化
  • -V, –renew-anon-volumes Recreate anonymous volumes instead of retrievingdata from the previous containers.
    • 1.22.0版本有这个选项,某些低版本的不存在该选项
    • 仅在启动的容器已经被创建的情况下有意义
    • 从字面上来看,使用该选项运行docker-compose up 启动容器时,将会不使用复用上一个container的volume,而是重新创建对应的volume。如果不使用该选项,而是只加了–force-recreate也将仅仅会重新创建对应的容器,而不会重新mount根据对应目录而创建的volume。这个可以通过docker inpsect containter_name命令验证,两次运行docker-compose –build –force-recreate所创建的容器对应的volume的name依然相同。
    • 这个选项的存在的意义是,如果加了改选项,第一次启动容器的时候,mount对应的目录的文件有误,想stop掉当前的container,并且在对应的目录添加了对应的文件后,第二次启动容器时能够重新mount对应的文件夹,使容器读取正确对应的文件;如果不加改选项,则使用上一个container的volume,不能读取正确的文件
    • 当然,也可以不使用该命令,只要不是重启container的场景即可,什么意思呢,可以先 docker-compose rm -v contaner_name,下次docker-compose up的时候就是创建新容器了,当然也会重新挂载对应的volume。目前低版本的docker-comopose就是这么做的。
    • 之前在mysql:5.7.22 版本的docker容器在使用/docker-entrypoint-initdb.d/ 目录初始化数据时,遇到类似的问题,踩了很多坑。
  • 总结,docker-compose up 与 docker start 命令更相似,因为它们都会复用之前的container(如果存在),而docker run是总会创建新的container。这要点需要牢记,才能避免踩坑。

参考:

Mysql官网 https://dev.mysql.com/doc/refman/5.7/en/adding-users.html

gitlab ci && docker-compose(3)-mysql的初始化

Posted on 2018-09-18 Edited on 2020-05-17

mysql 容器初始化:

  • 根据官方文档可以使用docker启动时bind主机包含sql、sh文件的目录到容器/docker-entrypoint-initdb.d/目录,在使用mysql镜像启动容器时会自动读取该文件夹下的内容对数据库初始化。可以使用以下两种方式在命令行启动,在低版本中可能只支持-v选项,在docker 17.03.0-ce版本(不含)以上支持–mount选项。
1
2
3
4
5
--mount选项
sudo docker run -it --name=mysql_db --mount type=bind,src=/home/zhang/Workspace/go/src/app/init_sql_script/,dst=/docker-entrypoint-initdb.d/ -e MYSQL_ROOT_PASSWORD=test -d mysql:5.7.22

-v选项
sudo docker run -it --name=mysql_db -v /home/zhang/Workspace/go/src/app/init_sql_script/:/docker-entrypoint-initdb.d/ -e MYSQL_ROOT_PASSWORD=test -d mysql:5.7.22
  • 另外一种方法使用dockerfile的方式。直接在dockerfile中复制相应的sql,sh文件到/docker-entrypoint-initdb.d/目录下,再根据dockerfile build出对应的镜像,docker run的时候也会直接初始化对应的数据。
1
2
3
4
# DockerfileMySQL
FROM mysql:5.7.22

COPY ./init_sql_script/ /docker-entrypoint-initdb.d/
1
2
sudo docker build -t app_mysql_db:init-data . 
sudo docker run --name=app_mysql_db app_mysql_db:init-data

docker-compose启动容器组:

Read more »

gitlab ci && docker-compose(2)-dockerfile的使用

Posted on 2018-09-16 Edited on 2020-05-17

dockerfile

COPY VS. ADD

  • ADD支持从本地tar文件复制解压,tar文件内的根目录应该包含dockerfile,并且context也限定为tar内部,通常配合docker build 使用,如 docker build - < archive.tar.gz
  • ADD支持url获取,COPY不支持。当使用 docker build - < somefile 传入dockerfile时,没有context可用,只能使用ADD从一个URL获取context
  • ADD的最佳用途是将本地tar文件自动提取到image中,如 ADD rootfs.tar.xz / 。
  • 它们都是based context,不能复制context之外的东西
  • 区别:https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#add-or-copy

CMD VS. ENTRYPOINT

CMD:

  • The CMD instruction should be used to run the software contained by your image, along with any arguments. Indeed, this form of the instruction is recommended for any service-based image. like:
    • CMD [“executable”, “param1”, “param2”…]
    • CMD [“apache2”,”-DFOREGROUND”]
  • CMD命令还用在交互式的shell中,如bash,python,perl。例如以下几个用法。 使用这种方式的好处是你能够执行如 docker run -it python 就能直接进入到一个有用的shell中。
    • CMD [“perl”, “-de0”], CMD [“python”], or CMD [“php”, “-a”]
  • CMD命令很少以 CMD [“param”, “param”] 的出现,这种用法是为了配合 ENTRYPOINT 使用的,所以不要轻易使用这种方式,除非你和你的目标用户清楚ENTRYPOINT是如何工作的

ENTRYPOINT:

  • ENTRYPOINT的最佳实践是,设置一个主要命令,使得运行image就像运行一个命令一样
  • 假如有一个image定义了s3cmd命令,ENRTRYPOINT和CMD如下
    • ENTRYPOINT [“s3cmd”]
    • CMD [“–help”]
    • 那么如果直接运行 docker run s3cmd 的话,将会显示 s3cmd 的帮助提示
    • 或者提供一个参数再执行命令,docker run s3cmd ls s3://mybucket,将会覆盖CMD的–help参数
  • 当然ENTRYPOINT也可以是一个sh脚本,可以自定义解析docker run 的时候传入的命令参数
  • 配置容器启动后执行的命令,并且不可被 docker run 提供的参数覆盖
Read more »

effective go learning

Posted on 2018-09-16 Edited on 2020-05-17

Formmating:

  • gofmt:针对文件进行格式化
  • go fmt:针对包进行格式化

Indentation

We use tabs for indentation(使用tab来缩进) and gofmt emits them by default. Use spaces only if you must.(仅在必要时使用空格)

Line length

Go has no line length limit. Don’t worry about overflowing a punched card. If a line feels too long, wrap it and indent with an extra tab.

Parentheses

Go needs fewer parentheses(更少的括号) than C and Java: control structures (if, for, switch) do not have parentheses in their syntax. Also, the operator precedence hierarchy is shorter and clearer

控制结构(if,for,switch)的语法中没有括号,使用严格的空格来提升直观感。

Commentary:

  • 提供C模式的 / /的块注释,和C++模式的行注释,行注释更加普遍,而块模式在包注释以及大块注释的时候比较常用
  • godoc会抽取注释成文档
  • 在顶级声明之前出现的注释(没有中间换行符)将与声明一起提取,以作为项目的解释性文本。
  • 每个包都应该有一个包注释,在package子句之前有一个块注释。对于多个文件的package,只需要在任意一个文件中声名包注释即可。包注释应该介绍包,并提供与整个包相关的信息。它将首先出现在godoc页面上。
  • 程序中的每个导出(大写)名称都应具有doc注释。并且最好以被声明的函数、字段或者其他作为开头。这样子用godoc时,容易搜索到对应的文档
Read more »

android获取ip(webtrc-app)

Posted on 2018-09-16 Edited on 2020-05-17

Show your ip by webrtc

代码: 链接

实际演示效果: demo链接

参考: https://github.com/diafygi/webrtc-ips

Show your ip in app

使用java对应的接口查询目前的ip,包括wifi的ip和移动数据网络的ip,不一定每次能够查到所有的ip,与系统是否开启wifi、是否开启移动网络等相关。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public static List<String> getIPAddress() {

ArrayList<String> iplist = new ArrayList<String>();
try {
//Enumeration<NetworkInterface> en=NetworkInterface.getNetworkInterfaces();
for (Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements(); ) {
NetworkInterface intf = en.nextElement();
for (Enumeration<InetAddress> enumIpAddr = intf.getInetAddresses(); enumIpAddr.hasMoreElements(); ) {
InetAddress inetAddress = enumIpAddr.nextElement();
if (!inetAddress.isLoopbackAddress() && inetAddress instanceof Inet4Address) {
Log.d(MainActivity.class.getName(), "ip is: " + inetAddress.getHostAddress());
iplist.add(inetAddress.getHostAddress());
}
}
}
} catch (SocketException e) {
e.printStackTrace();
}

return iplist;
}

参考: https://www.cnblogs.com/anni-qianqian/p/8084656.html

12

Long

20 posts
15 tags
GitHub E-Mail
© 2018 – 2020 Long
Powered by Hexo v3.9.0
|
Theme – NexT.Gemini v7.2.0