我们前面已经实现了API的基础版本,能对参数校验和返回指定数据,这一章,我们将对主机和交换机进行建模,存入数据库。

考虑到数据库安装和使用的简便性,我们采用文档存储结构的MongoDB数据库。

Mongo数据库下载安装,安装后不用设置密码,直接使用即可

下载链接 https://www.filehorse.com/download-mongodb/download/ 或者 https://www.mongodb.com/try/download/community

配置文件

使用数据库之前需要配置数据库地址和端口,所以我们将配置信息存放到配置文件,采用yaml格式存储解析

  • 配置文件内容

这里,我们还将API监听的地址和端口,日志路径也可以都配上

  1. api_server:
  2. env: prod
  3. host: 127.0.0.1
  4. port: 9000
  5. mgo:
  6. uri: mongodb://127.0.0.1:27017/?compressors=disabled&gssapiServiceName=mongodb
  7. database: gin_ips
  8. pool_size: 100
  9. log:
  10. path: log/gin_ips
  11. level: DEBUG
  12. name: gin.log
  13. count: 180
  • Golang Yaml文件解析
  1. // 通用 Config 接口
  2. type Config interface {
  3. InitError(msg string) error
  4. }
  5. // 根据yaml文件初始化通用配置, 无需输出日志
  6. func InitYaml(filename string, config Config) error {
  7. fp, err := os.Open(filename)
  8. if err != nil {
  9. msg := fmt.Sprintf("configure file [ %s ] not found", filename)
  10. return config.InitError(msg)
  11. }
  12. defer func() {
  13. _ = fp.Close()
  14. }()
  15. if err := yaml.NewDecoder(fp).Decode(config); err != nil {
  16. msg := fmt.Sprintf("configure file [ %s ] initialed failed", filename)
  17. return config.InitError(msg)
  18. }
  19. return nil
  20. }

Golang 使用Mongo数据库

golang中有多种优秀的orm库,例如xorm,gorm。由于orm将数据库模型和语言紧密封装,使用起来非常方便,很适合web前端开发。

但与此同时,使用orm也会导致部分性能丢失(深度使用后会发现隐藏的坑也不少),有兴趣的同学可以了解下。

本文主要使用golang官方mongodb库mongo-driver,直接通过sql语句操作数据库(这个过程可以学习下如何explain和优化sql语句)。

  • 模型定义
  1. type Collection struct {
  2. client *mongo.Collection
  3. database string // 数据库
  4. collection string // 集合
  5. }
  • 创建连接池
  1. // 连接池创建
  2. func CreatePool(uri string, size uint64) (pool *mongo.Client, e error) {
  3. defer func() {
  4. if err := recover(); err != nil {
  5. e = errors.New(fmt.Sprintf("%v", err))
  6. }
  7. }()
  8. ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) // 10s超时
  9. defer cancel()
  10. var err error
  11. pool, err = mongo.Connect(ctx, options.Client().ApplyURI(uri).SetMinPoolSize(size)) // 连接池
  12. if err != nil {
  13. return pool, err
  14. }
  15. err = pool.Ping(context.Background(), nil) // 检查连接
  16. if err != nil {
  17. return pool, err
  18. }
  19. return pool, nil
  20. }
  • 销毁连接池
  1. func DestroyPool(client *mongo.Client) error {
  2. err := client.Disconnect(context.Background())
  3. if err != nil {
  4. return err
  5. }
  6. return nil
  7. }
  • 查询操作
  1. // 查找单个文档, sort 等于1表示 返回最旧的,sort 等于-1 表示返回最新的
  2. /*
  3. BSON(二进制编码的JSON) D家族 bson.D
  4. D:一个BSON文档。这种类型应该在顺序重要的情况下使用,比如MongoDB命令。
  5. M:一张无序的map。它和D是一样的,只是它不保持顺序。
  6. A:一个BSON数组。
  7. E:D里面的一个元素。
  8. */
  9. func (m *Collection) FindOne(filter bson.D, sort, projection bson.M) (bson.M, error) {
  10. findOptions := options.FindOne().SetProjection(projection)
  11. if sort != nil {
  12. findOptions = findOptions.SetSort(sort)
  13. }
  14. singleResult := m.client.FindOne(context.Background(), filter, findOptions)
  15. var result bson.M
  16. if err := singleResult.Decode(&result); err != nil {
  17. return result, err
  18. }
  19. return result, nil
  20. }
  21. /*
  22. 查询多个 sort 等于1表示 返回最旧的,sort 等于-1 表示返回最新的
  23. 每次只返回1页 page size 大小的数据
  24. project 不能混合 True 和 False
  25. */
  26. func (m *Collection) FindLimit(filter bson.D, page, pageSize uint64, sort, projection bson.M) ([]bson.M, error) {
  27. var resultArray []bson.M
  28. if page == 0 || pageSize == 0 {
  29. return resultArray, errors.New("page or page size can't be 0")
  30. }
  31. skip := int64((page - 1) * pageSize)
  32. limit := int64(pageSize)
  33. if projection == nil {
  34. projection = bson.M{}
  35. }
  36. findOptions := options.Find().SetProjection(projection).SetSkip(skip).SetLimit(limit)
  37. if sort != nil {
  38. findOptions = findOptions.SetSort(sort)
  39. }
  40. cur, err := m.client.Find(context.Background(), filter, findOptions)
  41. if err != nil {
  42. return resultArray, err
  43. }
  44. defer func() {
  45. _ = cur.Close(context.Background())
  46. }()
  47. for cur.Next(context.Background()) {
  48. var result bson.M
  49. err := cur.Decode(&result)
  50. if err != nil {
  51. return resultArray, err
  52. }
  53. resultArray = append(resultArray, result)
  54. }
  55. //err = cur.All(context.Background(), &resultArray)
  56. if err := cur.Err(); err != nil {
  57. return resultArray, err
  58. }
  59. return resultArray, nil
  60. }
  61. // 返回查找条件的全部文档记录
  62. // project 不能混合 True 和 False
  63. func (m *Collection) FindAll(filter bson.D, sort, projection bson.M) ([]bson.M, error) {
  64. var resultArray []bson.M
  65. if projection == nil {
  66. projection = bson.M{}
  67. }
  68. findOptions := options.Find().SetProjection(projection)
  69. if sort != nil {
  70. findOptions = findOptions.SetSort(sort)
  71. }
  72. cur, err := m.client.Find(context.Background(), filter, findOptions)
  73. if err != nil {
  74. return resultArray, err
  75. }
  76. defer func() {
  77. _ = cur.Close(context.Background())
  78. }()
  79. for cur.Next(context.Background()) {
  80. // fmt.Println(cur.Current)
  81. var result bson.M
  82. err := cur.Decode(&result)
  83. if err != nil {
  84. return resultArray, err
  85. }
  86. resultArray = append(resultArray, result)
  87. }
  88. if err := cur.Err(); err != nil {
  89. return resultArray, err
  90. }
  91. return resultArray, nil
  92. }
  • 新增操作
  1. //插入单个
  2. func (m *Collection) InsertOne(document interface{}) (primitive.ObjectID, error) {
  3. insertResult, err := m.client.InsertOne(context.Background(), document)
  4. var objectId primitive.ObjectID
  5. if err != nil {
  6. return objectId, err
  7. }
  8. objectId = insertResult.InsertedID.(primitive.ObjectID)
  9. return objectId, nil
  10. }
  11. //插入多个文档
  12. func (m *Collection) InsertMany(documents []interface{}) ([]primitive.ObjectID, error) {
  13. var insertDocs []interface{}
  14. for _, doc := range documents {
  15. insertDocs = append(insertDocs, doc)
  16. }
  17. insertResult, err := m.client.InsertMany(context.Background(), insertDocs)
  18. var objectIds []primitive.ObjectID
  19. if err != nil {
  20. return objectIds, err
  21. }
  22. for _, oid := range insertResult.InsertedIDs {
  23. objectIds = append(objectIds, oid.(primitive.ObjectID))
  24. }
  25. return objectIds, nil
  26. }
  • 修改操作
  1. /*
  2. 更新 filter 返回的第一条记录
  3. 如果匹配到了(matchCount >= 1) 并且 objectId.IsZero()= false 则是新插入, objectId.IsZero()=true则是更新(更新没有获取到id)
  4. ObjectID("000000000000000000000000")
  5. document 修改为 interface 表示支持多个字段更新,使用bson.D ,即 []bson.E
  6. */
  7. func (m *Collection) UpdateOne(filter bson.D, document interface{}, insert bool) (int64, primitive.ObjectID, error) {
  8. updateOption := options.Update().SetUpsert(insert)
  9. updateResult, err := m.client.UpdateOne(context.Background(), filter, document, updateOption)
  10. var objectId primitive.ObjectID
  11. if err != nil {
  12. return 0, objectId, err
  13. }
  14. if updateResult.UpsertedID != nil {
  15. objectId = updateResult.UpsertedID.(primitive.ObjectID)
  16. }
  17. // fmt.Println(objectId.IsZero())
  18. return updateResult.MatchedCount, objectId, nil
  19. }
  20. /*
  21. 更新 filter 返回的所有记录,返回的匹配是指本次查询匹配到的所有数量,也就是最后更新后等于新的值的数量
  22. 如果匹配到了(matchCount >= 1) 并且 objectId.IsZero()= false 则是新插入, objectId.IsZero()=true则是更新(更新没有获取到id)
  23. ObjectID("000000000000000000000000")
  24. docAction 修改为 interface 表示支持多个字段更新,使用bson.D ,即 []bson.E
  25. */
  26. func (m *Collection) UpdateMany(filter bson.D, docAction interface{}, insert bool) (int64, primitive.ObjectID, error) {
  27. updateOption := options.Update().SetUpsert(insert)
  28. updateResult, err := m.client.UpdateMany(context.Background(), filter, docAction, updateOption)
  29. var objectId primitive.ObjectID
  30. if err != nil {
  31. return 0, objectId, err
  32. }
  33. if updateResult.UpsertedID != nil {
  34. objectId = updateResult.UpsertedID.(primitive.ObjectID)
  35. }
  36. // fmt.Println(objectId.IsZero())
  37. return updateResult.MatchedCount, objectId, nil
  38. }
  39. /*
  40. 替换 filter 返回的1条记录(最旧的)
  41. 如果匹配到了(matchCount >= 1) 并且 objectId.IsZero()= false 则是新插入, objectId.IsZero()=true则是更新(更新没有获取到id)
  42. ObjectID("000000000000000000000000")
  43. 采用 FindOneAndReplace 在查找不到但正确插入新的数据会有"mongo: no documents in result" 的错误
  44. */
  45. func (m *Collection) Replace(filter bson.D, document interface{}, insert bool) (int64, primitive.ObjectID, error) {
  46. option := options.Replace().SetUpsert(insert)
  47. replaceResult, err := m.client.ReplaceOne(context.Background(), filter, document, option)
  48. var objectId primitive.ObjectID
  49. if err != nil {
  50. return 0, objectId, err
  51. }
  52. if replaceResult.UpsertedID != nil {
  53. objectId = replaceResult.UpsertedID.(primitive.ObjectID)
  54. }
  55. // fmt.Println(objectId.IsZero())
  56. return replaceResult.MatchedCount, objectId, nil
  57. }
  • 删除操作
  1. /*
  2. 查找并删除一个 sort 等于1表示 删除最旧的,sort 等于-1 表示删除最新的
  3. 一般根据 id 查找就会保证删除正确
  4. */
  5. func (m *Collection) DeleteOne(filter bson.D, sort bson.M) (bson.M, error) {
  6. findOptions := options.FindOneAndDelete()
  7. if sort != nil {
  8. findOptions = findOptions.SetSort(sort)
  9. }
  10. singleResult := m.client.FindOneAndDelete(context.Background(), filter, findOptions)
  11. var result bson.M
  12. if err := singleResult.Decode(&result); err != nil {
  13. return result, err
  14. }
  15. return result, nil
  16. }
  17. /*
  18. 根据条件删除全部
  19. */
  20. func (m *Collection) DeleteAll(filter bson.D) (int64, error) {
  21. count, err := m.client.DeleteMany(context.Background(), filter)
  22. if err != nil {
  23. return 0, err
  24. }
  25. return count.DeletedCount, nil
  26. }
  • 创建索引
  1. // 创建索引,重复创建不会报错
  2. func (m *Collection) CreateIndex(index string, unique bool) (string, error) {
  3. indexModel := mongo.IndexModel{Keys: bson.M{index: 1}, Options: options.Index().SetUnique(unique)}
  4. name, err := m.client.Indexes().CreateOne(context.Background(), indexModel)
  5. return name, err
  6. }

数据模型设计和返回

  • 模型设计

根据需求,我们将主机和交换机的各个字段提前定义好,这时候可以考虑各个模型分开存储到多个集合,也可以合并成1个(本文选择合并)

  1. //|ID|主机名|IP|内存大小|磁盘大小|类型|负责人|
  2. type HostModel struct {
  3. Oid configure.Oid `json:"oid"` // 考虑所有实例存放在同一个集合中,需要一个字段来区分
  4. Id string `json:"id"`
  5. Ip string `json:"ip"`
  6. Hostname string `json:"hostname"`
  7. MemSize int64 `json:"mem_size"`
  8. DiskSize int64 `json:"disk_size"`
  9. Class string `json:"class"` // 主机类型
  10. Owner []string `json:"owner"`
  11. }
  12. //|ID|设备名|管理IP|虚IP|带外IP|厂家|负责人|
  13. type SwitchModel struct {
  14. Oid configure.Oid `json:"oid"` // 考虑所有实例存放在同一个集合中,需要一个字段来区分
  15. Id string `json:"id"`
  16. Name string `json:"name"`
  17. Ip string `json:"ip"`
  18. Vip []string `json:"vip"`
  19. ConsoleIp string `json:"console_ip"`
  20. Manufacturers string `json:"manufacturers"` // 厂家
  21. Owner []string `json:"owner"`
  22. }
  • 手工录入数据

随机插入几条测试数据即可

  1. hmArr := []HostModel{
  2. {
  3. Oid: configure.OidHost,
  4. Id: "H001",
  5. Ip: "10.1.162.18",
  6. Hostname: "10-1-162-18",
  7. MemSize: 1024000,
  8. DiskSize: 102400000000,
  9. Class: "物理机",
  10. Owner: []string{"小林"},
  11. },
  12. {
  13. Oid: configure.OidHost,
  14. Id: "H002",
  15. Ip: "10.1.162.19",
  16. Hostname: "10-1-162-19",
  17. MemSize: 1024000,
  18. DiskSize: 102400000000,
  19. Class: "虚拟机",
  20. Owner: []string{"小黄"},
  21. },
  22. }
  • API调用返回

最后我们修改下参数的验证规则,支持返回指定模型和返回所有模型。测试结果如下:

  1. curl "http://127.0.0.1:8080?ip=10.1.162.18"
  2. {"code":0,"message":"","data":{"page":1,"page_size":2,"size":2,"total":2,"list":[{"class":"物理机","disksize":102400000000,"hostname":"10-1-162-18","id":"H001","ip":"10.1.162.18","
  3. memsize":1024000,"oid":"HOST","owner":["小林"]},{"consoleip":"10.3.32.11","id":"S001","ip":"10.2.32.11","manufacturers":"华为","name":"上海集群交换机","oid":"SWITCH","owner":["老马
  4. ","老曹"],"vip":["10.2.20.1","10.2.20.13","10.1.162.18"]}]}}
  5. curl "http://127.0.0.1:8080?ip=10.1.162.18&oid=HOST"
  6. {"code":0,"message":"","data":{"page":1,"page_size":1,"size":1,"total":1,"list":[{"class":"物理机","disksize":102400000000,"hostname":"10-1-162-18","id":"H001","ip":"10.1.162.18","
  7. memsize":1024000,"oid":"HOST","owner":["小林"]}]}}

本文模拟生产环境完成了数据库的设计和数据配置,下一章,我们将配置Gin Log,同时开始使用Gin中间件完善API。

Github 代码

请访问 Gin-IPs 或者搜索 Gin-IPs

【Gin-API系列】配置文件和数据库操作(三)的更多相关文章

  1. Java Web----Java Web的数据库操作(三)

    Java Web的数据库操作 前面介绍了JDBC技术和JDBC API及API的使用示例,下面详细介绍JDBC在Web中的应用. Java Web----Java Web的数据库操作(一) Java ...

  2. ThinkPHP 数据库操作(三) : 查询方法、查询语法、链式操作

    查询方法 条件查询方法 where 方法 可以使用 where 方法进行 AND 条件查询: Db::table('think_user') ->where('name','like','%th ...

  3. Django-website 程序案例系列-4 ORM数据库操作

    数据库表的创建: 使用mysql时注意,在setting.py中的设置: DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql' ...

  4. Java Web----Java Web的数据库操作(二)

    Java Web的数据库操作 三.JDBC操作数据库 上一篇介绍了JDBC API,之后就可以通过API来操作数据库,实现对数据库的CRUD操作了. http://blog.csdn.net/zhai ...

  5. 循序渐进学.Net Core Web Api开发系列【9】:常用的数据库操作

    系列目录 循序渐进学.Net Core Web Api开发系列目录 本系列涉及到的源码下载地址:https://github.com/seabluescn/Blog_WebApi 一.概述 本篇描述一 ...

  6. C#实现多级子目录Zip压缩解压实例 NET4.6下的UTC时间转换 [译]ASP.NET Core Web API 中使用Oracle数据库和Dapper看这篇就够了 asp.Net Core免费开源分布式异常日志收集框架Exceptionless安装配置以及简单使用图文教程 asp.net core异步进行新增操作并且需要判断某些字段是否重复的三种解决方案 .NET Core开发日志

    C#实现多级子目录Zip压缩解压实例 参考 https://blog.csdn.net/lki_suidongdong/article/details/20942977 重点: 实现多级子目录的压缩, ...

  7. phoenix 开发API系列(三)phoenix api 结合数据库

    概述 介绍了 api 的各种写法之后,下面介绍构建 api 时与数据库连接的方式. 注 下面使用的工程的完整代码已经公开在: http://git.oschina.net/wangyubin/phoe ...

  8. ASP.NET实现二维码 ASP.Net上传文件 SQL基础语法 C# 动态创建数据库三(MySQL) Net Core 实现谷歌翻译ApI 免费版 C#发布和调试WebService ajax调用WebService实现数据库操作 C# 实体类转json数据过滤掉字段为null的字段

    ASP.NET实现二维码 using System;using System.Collections.Generic;using System.Drawing;using System.Linq;us ...

  9. 第三百零六节,Django框架,models.py模块,数据库操作——创建表、数据类型、索引、admin后台,补充Django目录说明以及全局配置文件配置

    Django框架,models.py模块,数据库操作——创建表.数据类型.索引.admin后台,补充Django目录说明以及全局配置文件配置 数据库配置 django默认支持sqlite,mysql, ...

随机推荐

  1. java.lang.NoSuchMethodError: org.apache.poi.ss.usermodel.CellStyle.setVerticalAlignment(Lorg/apache/poi/ss/usermodel/VerticalAlignment;)V

    项目里引入了两个不同的 POI 版本 ,可能是版本冲突引起的. 但是奇怪的是 用Eclipse在本地就失败,在公共测试 环境就是OK的,同事用的 edea 编译器也是OK的. Caused by: j ...

  2. Go Pentester - HTTP CLIENTS(5)

    Parsing Document Metadata with Bing Scaping Set up the environment - install goquery package. https: ...

  3. Python Ethical Hacking - Malware Analysis(3)

    Stealing WiFi Password Saved on a Computer #!/usr/bin/env python import smtplib import subprocess im ...

  4. 【Nginx】面试官问我Nginx能不能配置WebSocket?我给他现场演示了一番!!

    写在前面 当今互联网领域,不管是APP还是H5,不管是微信端还是小程序,只要是一款像样点的产品,为了增加用户的交互感和用户粘度,多多少少都会涉及到聊天功能.而对于Web端与H5来说,实现聊天最简单的就 ...

  5. 小书MybatisPlus第6篇-主键生成策略精讲

    本文为mybatis系列文档的第6篇,前5篇请访问下面的网址. 小书MybatisPlus第1篇-整合SpringBoot快速开始增删改查 小书MybatisPlus第2篇-条件构造器的应用及总结 小 ...

  6. css中使用浮动的情况和清除浮动的方法

    1.使用浮动时出现的情况: (1)使块元素在一行显示 (2)使内嵌元素支持宽高 (3)不设置宽高的时候宽度由内容撑开 (4)换行不被解析(故使用行内元素的时候清除间隙的方法可以使用浮动) (5)元素添 ...

  7. 【CVPR2020】Wavelet Integrated CNNs for Noise-Robust Image Classification

    深度学习中的下采样(max-pooing, average-pooling, strided-convolution)通常会有两个不足:破坏了目标的基本结构.放大随机噪声.上采样操作同样容易受到影响. ...

  8. 第四课 OOP封装继承多态解析,接口抽象类选择 2019-04-21

    父类 xx = new 子类(); xx.method(); 1 普通方法由编译时决定(左边) --- 提高效率 2 虚方法(virtual)  由运行时决定-- -多态,灵活 3 抽象方法由运行时决 ...

  9. cli框架 获取 命令行 参数

    package main import ( "fmt" "log" "os" "github.com/urfave/cli&quo ...

  10. JAVA实现BP神经网络算法

    工作中需要预测一个过程的时间,就想到了使用BP神经网络来进行预测. 简介 BP神经网络(Back Propagation Neural Network)是一种基于BP算法的人工神经网络,其使用BP算法 ...