pinpoint笔记

1、关于pinpooint

GitHub:naver/pinpoint

Pinpoint是用于用Java / PHP编写的大规模分布式系统的APM(应用程序性能管理)工具。受Dapper的启发,Pinpoint提供了一种解决方案,可通过跟踪跨分布式应用程序的事务来帮助分析系统的整体结构以及其中的组件如何互连。

  • 一目了然地了解您的应用程序拓扑
  • 监控应用程序的实时
  • 获得每笔交易的代码级可见性
  • 安装APM代理而无需更改任何代码
  • 对性能的影响最小(资源使用量增加约3%)

2、安装

2.1、 脚本安装

使用git clone https://github.com/naver/pinpoint.git或用zip文件下载Pinpoint下载该项目并解压缩。

通过运行安装Pinpoint ./mvnw install -Dmaven.test.skip=true

异常:

2.1.1

1
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-enforcer-plugin:3.0.0-M2:enforce (enforce-pinpoint-build-requirements) on project pinpoint: Some Enforcer rules have failed. Look above for specific messages explaining why the rule failed. -> [Help 1]

解决:
https://my.oschina.net/chengxiaoyuan/blog/822994

https://cwiki.apache.org/confluence/display/MAVEN/MojoExecutionException

2.2、使用Docker安装

安装Docker Compose

https://docs.docker.com/compose/install/

1
2
3
4
5
6
$ sudo curl -L "https://github.com/docker/compose/releases/download/1.24.1/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose

$ sudo chmod +x /usr/local/bin/docker-compose

$ docker-compose --version
docker-compose version 1.24.1, build 1110ad01

使用Docker安装

https://github.com/naver/pinpoint-docker

1
2
3
git clone https://github.com/naver/pinpoint-docker.git
cd pinpoint-docker
docker-compose pull && docker-compose up -d

3、Mysql(可选,1.8.1+)

Pinpoint-Mysql是使用“警报”功能所必需的。它的服务器在端口13306上运行,并包含用于注册用户,组和要发送的警报的数据结构。

要发送电子邮件警报,必须使BATCH_ENABLE = true并将其他与邮件相关的环境变量(MAIL_HOST,MAIL_PORT,MAIL_USERNAME,MAIL_PASSWORD,MAIL_PROPERTIES_MAIL_SMTP_FROM等)更改为.env文件中的Pinpoint-Web服务器。

有关更多信息,请参见Pinpoint文档中的“ 设置警报”。

4、Flink配置(可选)

Pinpoint-Flink是使用“应用程序检查器”功能所必需的。

启动所有容器并准备就绪后。在Pinpoint中使用此功能还有另一件事。此功能不是强制性的,但是由于启用它是一项简单的任务,因此请注意它。

在jobon上定位到flink服务器。
可以job从Pinpoint的开放源代码构建,更多指南

Node设置正确时区(以CentOS 7.5.1804 为例)

Node设置正确时区(以CentOS 7.5.1804 为例)

1
2
3
timedatectl list-timezones
timedatectl set-timezone Asia/Shanghai
timedatectl status | grep 'Time zone'


提示:这里相当于为Docker Container和Kubernetes Pod的宿主机设置正确的时区,需要优先配置完成。

Docker Container 与 Node同步时区设置

1
docker run -it -v /etc/localtime:/etc/localtime --rm alpine:3.8 /bin/sh


提示:上述命令注意-v参数的部分。


总结:经测试验证,alpine:3.8、centos:7.5.1804和ubuntu:16.04处理方式相同。

Kubernetes Pod 与 Node同步时区设置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
apiVersion: v1
kind: Pod
metadata:
name: alpine
labels:
app: alpine
spec:
containers:
- image: alpine:3.8
imagePullPolicy: IfNotPresent
name: alpine
command:
- sleep
- "3600"
volumeMounts:
- name: tz-config
mountPath: /etc/localtime
restartPolicy: Always
volumes:
- name: tz-config
hostPath:
path: /etc/localtime


提示:上述YAML注意volumes和volumeMounts部分。


总结:经测试验证,alpine:3.8、centos:7.5.1804和ubuntu:16.04处理方式相同。

Docker Container 和 Kubernetes Pod 通用 与 Node同步时区设置


该方式在需要构建Docker Image的时候,即修改Docker Image的默认时区设置。

Alpine Linux 3.8 Dockerfile Example

1
2
3
4
5
6
FROM alpine:3.8

MAINTAINER wangxin_0611@126.com

RUN apk --no-cache add tzdata && \
ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime

Ubuntu Linux 16.04 Dockerfile Example

1
2
3
4
5
6
7
FROM ubuntu:16.04

MAINTAINER wangxin_0611@126.com

RUN apt-get update && \
apt-get install -y tzdata && \
ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime

CenOS Linux 7.5.1804 Dockerfile Example

1
2
3
4
5
FROM centos:7.5.1804

MAINTAINER wangxin_0611@126.com

RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime


参考资料:


https://blog.csdn.net/qq_34924407/article/details/82057080


https://blog.csdn.net/u013201439/article/details/79436413

#Nginx安装

http://www.nginx.cn/install(官网的不是很友好)

http://nginx.org/en/docs/

1.centos平台编译环境

###安装依赖包:

1
yum -y install gcc zlib zlib-devel pcre-devel openssl openssl-devel

##Nginx安装

1
2
3
4
5
6
7
//创建一个文件夹
cd /usr/local
mkdir nginx
cd nginx
//下载tar包
wget http://nginx.org/download/nginx-1.13.7.tar.gz
tar -xvf nginx-1.13.7.tar.g

####安装nginx

1
2
3
4
5
6
7
8
//进入nginx目录
cd /usr/local/nginx
//执行命令
./configure
//执行make命令
make
//执行make install命令
make install

####配置nginx.conf

1
2
# 打开配置文件
vi /usr/local/nginx/conf/nginx.conf

将端口号改成8089,因为可能apeache占用80端口,apeache端口尽量不要修改,我们选择修改nginx端口。

localhost修改为你服务器ip地址。

###启动nginx

1
/usr/local/nginx/sbin/nginx -s reload

查看nginx进程是否启动:

1
ps -ef | grep nginx
1
2
3
root      2331  1807  0 Nov18 ?        00:00:00 runsv nginx
root 2351 2331 0 Nov18 ? 00:00:00 svlogd -tt /var/log/gitlab/nginx
root 2352 2331 0 Nov18 ? 00:00:00 nginx: master process /opt/gitlab/embedded/sbin/nginx -p /var/opt/gitlab/nginx

##访问服务器ip查看

##常用命令

##异常

####1、

1
2
[root@JD local]# /usr/local/nginx/sbin/nginx -c /usr/local/nginx/nginx.conf  
nginx: [emerg] open() "/usr/local/nginx/nginx.conf" failed (2: No such file or directory)

####解决办法
使用nginx -c的参数指定nginx.conf文件的位置

1
/usr/local/nginx/sbin/nginx -c /usr/local/nginx/conf/nginx.conf
1
2
3
4
5
6
7
8
[root@JD nginx]# cd logs/
[root@JD logs]# ll
total 8
-rw-r--r-- 1 root root 0 Nov 25 15:29 access.log
-rw-r--r-- 1 root root 464 Nov 25 15:28 error.log
-rw-r--r-- 1 root root 6 Nov 25 15:29 nginx.pid

nginx.pid文件已经有了。

#mysql 数据库引擎

##1 InnoDB

InnoDB的主要优势

##2 MYISAM

##3 其他引擎

MapTools

MapTools

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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
/**
* 地球半径,单位千米
*/
private static final double EARTH_RADIUS = 6378.137;

/** 180° **/
private static final DecimalFormat df = new DecimalFormat("0.000000");

public static boolean checkWithPath(Float myLong, Float myLati, Float[] areaLongitude, Float[] areaLatitude) {
Point2D.Double myPosition = new Point2D.Double(myLong, myLati);
List<Point2D.Double> areaBoundaryList = new ArrayList<>();
for (int i = 0; i < areaLatitude.length; i++) {
Point2D.Double areaBoundary = new Point2D.Double(areaLongitude[i], areaLatitude[i]);
areaBoundaryList.add(areaBoundary);
}
return checkWithPath(myPosition, areaBoundaryList);
}

private static boolean checkWithPath(Point2D.Double point, List<Point2D.Double> polygon) {
GeneralPath p = new GeneralPath();
Point2D.Double first = polygon.get(0);
p.moveTo(first.x, first.y);
for (Point2D.Double d : polygon) {
p.lineTo(d.x, d.y);
}
p.lineTo(first.x, first.y);
p.closePath();
return p.contains(point);
}

private static double rad(double d) {
return d * Math.PI / 180.0;
}

/**
* @param latitude1
* @param longitude1
* @param latitude2
* @param longitude2
* @return
*/
public static double getDistance(double latitude1, double longitude1, double latitude2, double longitude2) {
// 纬度
double Lat1 = rad(latitude1);
double Lat2 = rad(latitude2);
//两点纬度之差
double a = Lat1 - Lat2;
//经度之差
double b = rad(longitude1) - rad(longitude2);
//计算两点距离的公式
double s = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a / 2), 2) + Math.cos(Lat1) * Math.cos(Lat2) * Math.pow(Math.sin(b / 2), 2)));
//弧长乘地球半径(半径为米)
s = s * 6378137.0;
//精确距离的数值
s = Math.round(s * 10000d) / 10000d;
return s;

}

/**
* 通过GPS的list算出距离
*
* @param
* @return
*/
public static Double getTotalDistance(List<DistanceBO> distanceBOS) {

//每两个点之间的set集合
Set<Double> distance = new HashSet<>();
for (int i = 0; i < distanceBOS.size()-1; i++) {
Double singleDistance=getDistance(distanceBOS.get(i).getLatitude(),
distanceBOS.get(i).getLongitude(),
distanceBOS.get(i+1).getLatitude(),
distanceBOS.get(i+1).getLongitude());
distance.add(singleDistance);
}
//算出距离
Double totalDistance = distance.stream().mapToDouble(Double::doubleValue).sum();
return totalDistance;
}


/**
* 判断一个点是否在圆形区域内
*/
public static boolean isInCircle(double lng1, double lat1, double lng2, double lat2, String radius) {
return getDistance(lat1, lng1, lat2, lng2) > Double.parseDouble(radius);
}
/**
* 判断当前位置是否在围栏内
* @param mobilelocationEntity -- 位置信息类 包含经度纬度
* @param enclosureList -- 围栏类 每一个对象是形成多边形围栏的一个点 包含经度纬度
* @return
*/
public static boolean isInPolygon(DistanceBO mobilelocationEntity,List<DistanceBO> enclosureList){
double p_x = mobilelocationEntity.getLongitude();
double p_y = mobilelocationEntity.getLatitude();
Point2D.Double point = new Point2D.Double(p_x, p_y);

List<Point2D.Double> pointList= new ArrayList<Point2D.Double>();

for (DistanceBO distanceBO : enclosureList){
double polygonPoint_x = distanceBO.getLongitude();
double polygonPoint_y = distanceBO.getLatitude();
Point2D.Double polygonPoint = new Point2D.Double(polygonPoint_x,polygonPoint_y);
pointList.add(polygonPoint);
}
return checkWithJdkGeneralPath(point,pointList);
}

/**
* 返回一个点是否在一个多边形区域内
* @param point
* @param polygon
* @return
*/
private static boolean checkWithJdkGeneralPath(Point2D.Double point, List<Point2D.Double> polygon) {
java.awt.geom.GeneralPath p = new java.awt.geom.GeneralPath();

Point2D.Double first = polygon.get(0);
p.moveTo(first.x, first.y);
polygon.remove(0);
for (Point2D.Double d : polygon) {
p.lineTo(d.x, d.y);
}

p.lineTo(first.x, first.y);

p.closePath();

return p.contains(point);

}

/**
* 根据一点的坐标与距离,以及方向,计算另外一点的位置
* @param angle 角度,从正北顺时针方向开始计算
* @param startLong 起始点经度
* @param startLat 起始点纬度
* @param distance 距离,单位 km
* @return
*/
public static DistanceBO getOneSixthCircles(double angle, double startLong, double startLat, double distance){
DistanceBO bo = new DistanceBO();
//将距离转换成经度的计算公式
double δ = distance/EARTH_RADIUS;
// 转换为radian,否则结果会不正确
angle = Math.toRadians(angle);
startLong = Math.toRadians(startLong);
startLat = Math.toRadians(startLat);
double lat = Math.asin(Math.sin(startLat)*Math.cos(δ)+Math.cos(startLat)*Math.sin(δ)*Math.cos(angle));
double lon = startLong + Math.atan2(Math.sin(angle)*Math.sin(δ)*Math.cos(startLat),Math.cos(δ)-Math.sin(startLat)*Math.sin(lat));
// 转为正常的10进制经纬度
lon = Math.toDegrees(lon);
lat = Math.toDegrees(lat);
bo.setLongitude(lon);
bo.setLatitude(lat);
return bo;
}

public static void main(String[] args) {
double angle = 60;
double startLong = 116.442774;
double startLat = 39.977867;
double distance = 1.732;
getOneSixthCircles(angle, startLong, startLat, distance);
System.out.println(MapTool.getDistance(22.75424, 112.76535, 23.014171, 113.10111));
}

logrotate 设计

logrotate 设计


底层的日志分散在三种数据源之上,它们分别是:


1. docker 容器的对应目录,默认在 /var/lib/docker/containers/;


2. kubernetes 的 kubelet 创建的容器对应的目录,默认在 /var/log/containers/,但是它们都是软连接(symbolic link),不会占用磁盘空间,因此可以忽略不计;


3. 各个宿主机操作系统使用systemd管理的进程产生的日志,一般在/var/log/journal/ 或者 /run/log/journal,由 systemd-journald 管理,它本身是一个服务端程序,其客户端就是 journalctl 。





底层的日志汇总在一种数据源之上,它就是日志后端,我们选用 Elasticsearch 。





分散在三种数据源之上的日志由日志代理统一采集,日志代理我们选用Fluentd,最后汇总到日志后端(Elasticsearch)统一存储管理。





可以看出,日志的数据源包括三种分散数据源和一种汇总数据源。





在此基础上,考虑 logrotate 的设计:


1. 分散数据源的第一种,即 docker 容器的对应目录,需要做 rotate ;


实现方案:


第一种,配置 docker 的日志 rotate 规则,即在宿主机操作系统层面解决。具体配置示例如下:

1
2
3
4
5
6
7
{
"log-driver":"json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
}
}


第二种,利用社区提供的针对 docker 的 logrotate 镜像解决,详情见下面的参考资料。


参考资料:


https://blog.csdn.net/qcloudcommunity/article/details/77824015


https://github.com/blacklabelops/logrotate








2. 分散数据源的第三种,即 systemd-journald 管理的日志,需要做 rotate ;


实现方案:配置 systemd-journald 保存的最大日志容量,即在宿主机操作系统层面解决。


参考资料:


https://superuser.com/questions/982620/is-it-safe-to-remove-journal-files-in-centos





3. 汇总数据源,即 Elasticsearch 上的汇总日志,需要做 rotate 。


实现方案:根据要求保留的时间周期,删除不在时间周期内的 Elasticsearch 索引。例如:curl -XDELETE http://10.96.151.162:9200/logstash-2018.07.21



golang给定一个数组(数组包含很多元素),请用5个协程并发读取并打印数据元素。

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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
package main

import (
"fmt"
"sync"
)

func main() {
var wg sync.WaitGroup
list := []int{
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, //11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
}
ch := make(chan int, 0)
flag := make(chan int, 0)
fmt.Println("数组内容:",list)
fmt.Println("输出结果:")
go func() {
for i := 0; i < len(list); i++ {
ch <- list[i]
}
flag <- 1
}()

for i := 0; i < 5; i++ {
wg.Add(1)
go func(i int) {
Loop:
for {
select {
case e, ok := <-ch:
if ok {
fmt.Printf("第%d个协程,数组元素是:%d\n",i,e)
} else {
wg.Done()
break Loop
}
case <-flag:
close(ch)
wg.Done()
break Loop
}
}
}(i)
}
wg.Wait()
}

golang环境下安装 swagger

安装 swagger

1
$ go get -u -v github.com/swaggo/swag/cmd/swag

若 $GOPATH/bin 没有加入$PATH中,你需要执行将其可执行文件移动到$GOBIN下

1
$ mv $GOPATH/bin/swag /usr/local/go/bin

验证是否安装成功

1
2
$ swag -v
swag version v1.1.1

安装 gin-swagger

1
2
3
$ go get -u -v github.com/swaggo/gin-swagger

$ go get -u -v github.com/swaggo/gin-swagger/swaggerFiles

注:三个包都有一定大小,安装需要等一会

初始化

编写API注释

Swagger 中需要将相应的注释或注解编写到方法上,再利用生成器自动生成说明文件

gin-swagger 给出的范例:

1
2
3
4
5
6
7
8
9
// @Summary Add a new pet to the store
// @Description get string by ID
// @Accept json
// @Produce json
// @Param some_id path int true "Some ID"
// @Success 200 {string} string "ok"
// @Failure 400 {object} web.APIError "We need ID!!"
// @Failure 404 {object} web.APIError "Can not find ID"
// @Router /testapi/get-string-by-int/{some_id} [get]

参照 Swagger 的注解规范和范例去编写

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// @title Swagger for yhbaas
// @version 1.0
// @description 提供一站式配置、开发、测试、部署和运维的区块链服务平台.

// @license.name Apache 2.0
// @license.url http://www.apache.org/licenses/LICENSE-2.0.html

// @host localhost:9999/
// @BasePath

func main() {
router = gin.Default()
router.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
router.Run(":9999")
}

在对应的方法体中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// GetPeersCanInit
// @Summary 查询可以初始化的节点
// @Description GetPeersCanInit
// @Accept json
// @Tags GetPeersCanInit
// @Security Bearer
// @Produce json
// @Param chaincodeID path string true "chaincodeID"
// @Param channelId path string true "channelId"
// @Resource ID
// @Success 200 {object} structs.BCNodeInfo
// @Failure 500 {object} httputil.HTTPError
// @Router /network/peersCanInit/{channelId}/{chaincodeID} [get]
func GetPeersCanInit(c *gin.Context) {
chaincodeID := c.Params.ByName("chaincodeID")
channelIdStr := c.Params.ByName("channelId")
//...
c.JSON(http.StatusOK, gin.H{
"bcNodeInfo": bcNodeInfo,
})
}

参考的注解可见 gin-blog

生成

我们进入到yhbaas的项目根目录中(main.go所在同级目录下/yhbaas/backend),执行初始化命令

1
2
3
4
/yhbaas/backend$ swag init
2018/10/15 10:40:10 Generate swagger docs....
2018/10/15 10:40:10 Generate general API Info
2018/10/15 10:40:10 create docs.go at docs/docs.go

完毕后会在项目根目录(main.go所在同级目录下/yhbaas/backend)下生成docs

1
2
3
4
5
docs/
├── docs.go
└── swagger
├── swagger.json
└── swagger.yaml

我们可以检查 docs.go 文件中的 doc 变量,详细记载中我们文件中所编写的注解和说明

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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
// GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
// This file was generated by swaggo/swag at
// 2018-10-12 16:36:46.110316843 +0800 CST m=+13.550947436

package docs

import (
"bytes"

"github.com/alecthomas/template"
"github.com/swaggo/swag"
)

var doc = `{
"swagger": "2.0",
"info": {
"description": "提供一站式配置、开发、测试、部署和运维的区块链服务平台.",
"title": "Swagger for yhbaas",
"termsOfService": "http://swagger.io/terms/",
"contact": {},
"license": {
"name": "Apache 2.0",
"url": "http://www.apache.org/licenses/LICENSE-2.0.html"
},
"version": "1.0"
},
"host": "petstore.swagger.io",
"basePath": "/v1",
"paths": {
"/allChannels/{netId}": {
"get": {
"security": [
{
"Bearer": []
}
],
"description": "GetAllChannels by netId",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"GetOrg"
],
"summary": "GetAllChannels",
"parameters": [
{
"type": "string",
"description": "netId",
"name": "netId",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "object",
"$ref": "#/definitions/structs.ChannelInfo"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"type": "object",
"$ref": "#/definitions/httputil.HTTPError"
}
}
}
}
},

验证

大功告成,访问一下 http://127.0.0.1:9999/swagger/index.html, 查看 API 文档生成是否正确

补充:

API 操作

注解 描述
description 描述,操作行为的详细解释
id ID,用于识别操作的唯一字符串。在所有API操作中必须是唯一的
tags 标签,A list of tags to each API operation that separated by commas
summary 概要,A short summary of what the operation does
accept API可以使用的MIME类型列表。值必须如MIME类型所述
produce API可以生成的MIME类型列表。值必须如MIME类型所述
param 参数, 以空格分隔的参数 param name,param type,data type,is mandatory?,comment attribute(optional)
security 安全性
success 以空格分隔的成功响应。 return code,{param type},data type,comment
failure 以空格分隔的失败响应。 return code,{param type},data type,comment
router 访问路径。 path,[httpMethod]

Mime 类型

1
2
3
4
5
6
7
8
9
10
11
12
json
application/json
xml
text/xml
plain
text/plain
html
text/html
mpfd
multipart/form-data
json-api
application/vnd.api+json

安全

一般API信息

1
2
3
4
5
// @securityDefinitions.basic BasicAuth
// @securitydefinitions.oauth2.application OAuth2Application
// @tokenUrl https://example.com/oauth/token
// @scope.write Grants write access
// @scope.admin Grants read and write access to administrative information

每个API操作

1
// @Security ApiKeyAuth

创建和条件

1
2
// @Security ApiKeyAuth
// @Security OAuth2Application[write, admin]

参数类型

1
2
3
4
5
6
object (struct)
string (string)
integer (int, uint, uint32, uint64)
number (float32)
boolean (bool)
array

数据类型

1
2
3
4
5
string (string)
integer (int, uint, uint32, uint64)
number (float32)
boolean (bool)
user defined struct

属性

1
2
3
4
5
6
// @Param enumstring 查询字符串false“字符串枚举”枚举(A,B,C)
// @Param enumint 查询int int“int enums”枚举(1,2,3)
// @Param 枚举查询编号false“int枚举“枚举(1.1,1.2,1.3)
// @Param 字符串查询字符串假”字符串有效“minlength(5)maxlength(10)
// @Param int查询int假”int valid“mininum(1)maxinum(10)
// @Param 默认查询字符串false“字符串默认”默认(A)

具有数组类型的用户定义结构

1
// @Success 200 {array} model.Account
1
2
3
4
5
6
<-- This is a user defined struct.
package model
type Account struct {
ID int `json:"id" example:"1"`
Name string `json:"name" example:"account name"`
}

使用多个路径参数

1
2
3
4
5
/// ...
// @Param group_id path int true "Group ID"
// @Param account_id path int true "Account ID"
// ...
// @Router /examples/groups/{group_id}/accounts/{account_id} [get]

结构体的例子

1
2
3
4
5
6
7
// 节点信息表
type BCNodeInfo struct {
NodeID int32 `gorm:"AUTO_INCREMENT;primary_key"`
NodeName string `json:"nodeName"` //peer0.org1
OrgID int32 `json:"orgID"`
RunStatus RunStatusType `json:"runStatus"`
}

搭建gitlab&jenkins

搭建gitlab&jenkins

1、 搭建mysql

1
docker run --name mysql -p 3306:3306 -e MYSQL_ROOT_PASSWORD=12345 -d mysql:5.7 --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci

用户名/密码:root/12345

实用docker启动的mysql,远程无法连接,在启动的时候添加远程机器和mysql端口号映射: -p 3306:3306

git config –global user.email “794182189@qq.com

2、 搭建gitlab

centos 7 使用gitlab docker镜像安装gitlab

docker-hub

GitLab Docker images

1
2
3
4
5
6
7
8
9
10
docker run --detach \
--hostname 192.168.0.3 \
--publish 443:443 --publish 80:80 --publish 8022:22 \
--env GITLAB_OMNIBUS_CONFIG="external_url 'http://192.168.0.3/'; gitlab_rails['gitlab_shell_ssh_port'] = 8022;" \
--name gitlab \
--restart always \
--volume /srv/gitlab/config:/etc/gitlab \
--volume /srv/gitlab/logs:/var/log/gitlab \
--volume /srv/gitlab/data:/var/opt/gitlab \
gitlab/gitlab-ce:latest

3、 搭建jenkins

jenkins

docker-jenkins

docker-jenkins-README.md

1
docker run -p 8080:8080 -p 50000:50000 -d --name jenkins jenkins/jenkins:lts

jenkins密码 ( /var/jenkins_home/secrets/initialAdminPassword):admin/ c84649faf5b3470499691a621d8d90d7

管理员账户:root/12345678

jenkinsfile

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
pipeline {
agent any

stages {
stage('build') {
steps {
echo 'Buliding ... '
}
}

stage('Test') {
steps {
echo 'Testing ... '
}
}

stage('Deploy') {
steps {
echo 'Deploying ... '
}
}
}
}