从零开始撸,基于gin+gorm+grpc实现项目改造
创始人
2024-06-03 17:21:31
0

拆箱gin框架

  • 参考文档
  • 参考文档
  • 创建项目gin_grpc
  • 目录下写一个入口函数main.go
package mainimport "github.com/gin-gonic/gin"func main() {r := gin.Default()r.GET("/ping", func(c *gin.Context) {c.JSON(200, gin.H{"message": "pong",})})r.Run() // listen and serve on 0.0.0.0:8080
}
  • 开启gomod go mod init gin_grpc
  • 加载依赖 go mod tidy
  • 此时我们查看项目下多了一个go.mod文件
    在这里插入图片描述

目录的配置

  • 有了入口文件,我们再创建一个app目录,用来存放我们的项目代码
  • 在app目录下创建一个router目录存放路由,在app目录下创建一个controller存放控制器层的代码
  • 在app/router目录下创建api.go,用来注册api相关的路由
package routerimport ("gin_grpc/app/controller""github.com/gin-gonic/gin"
)// UserRouter 用户模块的路由
func UserRouter(r *gin.Engine) {r.GET("/index", controller.User.Index)
}// InitRouter 路由注册
func InitRouter(r *gin.Engine) {// 用户相关的路由注册UserRouter(r)
}
  • 在app/controller下创建user.go作为user的控制器
package controllerimport ("github.com/gin-gonic/gin"
)// Controller controller基类
type Controller struct{}// UserController 继承controller的user
type UserController struct {Controller
}// User UserController的变量
var User=UserController{
}func (t *UserController)Index(c *gin.Context){c.JSON(200, gin.H{"message": "pong",})return
}
  • 修改一下入口文件main.go,一个是启动gin服务,一个是注册路由
package main
import ("gin_grpc/app/router""github.com/gin-gonic/gin"
)
func main() {//r:=gin.New()//启动gin服务r := gin.Default()//Default returns an Engine instance with the Logger and Recovery middleware already attached.router.InitRouter(r)//注册路由r.Run("127.0.0.1:8089") // listen and serve on 0.0.0.0:8080
}
  • 至此,一个简易版本的gin部署完毕了。我们继续往下看

配置文件

  • 我们读取mysql,读取redis配置,肯定少不了配置文件

  • 这里,我们使用viper来读取配置文件

  • 相关参考文档

  • 安装vipergo get github.com/spf13/viper

  • 在项目根目录下创建config文件夹,在config下创建base.yaml,因为yaml文件相对来说比json更轻量级

#项目相关配置
app:name: gin_grpcdebug: true
#数据库相关配置
database:driver: mysqlhost: 127.0.0.1port: 3306username: acurd_comdbname: cmspassword: acurd_com
#redis相关配置
redis:host: 127.0.0.1port: 6379
  • 在config目录下创建一个config.go,用来加载配置文件
package configimport ("fmt""github.com/fsnotify/fsnotify""github.com/spf13/viper"
)// InitConfig 初始化配置文件
func InitConfig() {viper.AddConfigPath("./config")viper.AddConfigPath(".")//多路径查找viper.SetConfigName("base")viper.SetConfigType("yaml")if err := viper.ReadInConfig(); err != nil {panic(err)}//监控并重新读取配置文件viper.WatchConfig()viper.OnConfigChange(func(e fsnotify.Event) {// 配置文件发生变更之后会调用的回调函数fmt.Println("Config file changed:", e.Name)})
}
  • 在入口文增加一个init函数,完成对配置文件的初始化
package main
import ("gin_grpc/app/router""gin_grpc/config""github.com/gin-gonic/gin"
)
//初始化配置文件
func init() {config.InitConfig()
}
func main() {//r:=gin.New()//启动gin服务r := gin.Default()//Default returns an Engine instance with the Logger and Recovery middleware already attached.router.InitRouter(r)//注册路由r.Run("127.0.0.1:8089") // listen and serve on 0.0.0.0:8080
}

gorm的使用

  • 在config/config.go文件中写方法连接MySQL,这里使用gorm
  • 在app下创建model目录,在model目录下新建文件model.go
package modelimport ("fmt"_ "github.com/go-sql-driver/mysql""github.com/jinzhu/gorm""github.com/spf13/viper""log"
)// Db 用来承接db变量
var Db *gorm.DB// InitDb 初始化数据库连接
func InitDb()*gorm.DB{var (Username = viper.GetString("database.username")Password = viper.GetString("database.password")Host = viper.GetString("database.host")Port = viper.GetInt("database.port")DbName = viper.GetString("database.dbname"))dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8&parseTime=True&loc=Local", Username, Password, Host, Port, DbName)fmt.Println(Username)db, err := gorm.Open("mysql", dsn)if err != nil {log.Fatal("数据库连接失败,报错信息"+err.Error())}// 设置连接池,空闲连接db.DB().SetMaxIdleConns(50)// 打开链接db.DB().SetMaxOpenConns(100)// 表明禁用后缀加sdb.SingularTable(true)// 启用Logger,显示详细日志db.LogMode(viper.GetBool("app.debug"))return db
}
  • 在model下继续创建文件blog,用于博文的model
package model// Blog 定义博文结构体
type Blog struct {AutoID      uint   `gorm:"column:auto_id;primary_key;AUTO_INCREMENT"` // IDID          string `gorm:"column:id;NOT NULL"`                        // 业务idTitle       string `gorm:"column:title;NOT NULL"`                     // 标题TitleStyle  string `gorm:"column:title_style;NOT NULL"`               // 标题样式Thumb       string `gorm:"column:thumb;NOT NULL"`                     // 缩略图Keywords    string `gorm:"column:keywords;NOT NULL"`                  // 关键词Description string `gorm:"column:description;NOT NULL"`               // 描述Content     string `gorm:"column:content;NOT NULL"`                   // 内容CreateTime  uint   `gorm:"column:create_time;default:0;NOT NULL"`     // 创建时间UpdateTime  uint   `gorm:"column:update_time;default:0;NOT NULL"`     // 更新时间Author      string `gorm:"column:author;NOT NULL"`                    // 作者Source      string `gorm:"column:source;NOT NULL"`                    // 来源Summary     string `gorm:"column:summary;NOT NULL"`                   // 摘要
}// TableName 标记Blog结构体使用的表名
func (m *Blog) TableName() string {return "cms_blog"
}// BlogModel 用来继承model
type BlogModel struct{
}// GetOne 获取一篇文章
func (m *BlogModel)GetOne(i int)Blog{var blog BlogDb.First(&blog,i)return blog
}
  • 增加一个路由,用来获取文章
// BlogRouter  博客相关路由
func BlogRouter(r *gin.Engine) {r.GET("/blog/:id", controller.Blog.GetOne)
}
  • 在入口文件中增加对MySQL的初始化
func main() {//r:=gin.New()//启动gin服务r := gin.Default()//Default returns an Engine instance with the Logger and Recovery middleware already attached.model.Db=model.InitDb()defer model.Db.Close()router.InitRouter(r)//注册路由r.Run("127.0.0.1:8089") // listen and serve on 0.0.0.0:8080
}
  • 访问路由
    在这里插入图片描述

使用grpc完成服务间的调用

grpc的入门的例子,我再之前的文章golang入门微服务
中详细的讲过,这里简单记录一下步骤

定义protobuf文件

//我们定义一个博文的数据结构,
syntax="proto3";
package blog;
option  go_package="/Users/zhangguofu/website/gin_grpc/app/proto/gen/go;blog";//生成的go文件存放目录在哪;包名叫什么
message Blog {uint32 auto_id = 1; // IDstring id = 2; // 业务idstring title = 4; // 标题string title_style = 5; // 标题样式string thumb = 6; // 缩略图string keywords = 7; // 关键词string description = 8; // 描述string content = 9; // 内容uint32 create_time = 14; // 创建时间uint32 update_time = 15; // 更新时间string author = 16; // 作者string source = 17; // 来源string summary = 18; // 摘要
}
// 定义blog服务
service BlogInfo{//定义方法rpc getBlog(Id) returns(Blog);
}
//定义id的消息类型
message Id{int64 value=1;
}
  • 在proto文件目录下执行命令 生成pb文件,其中,blog.pb.go中定义了结构体,字段映射等信息,blog_grpc.pb.go中定义了一些和rpc交互的一些方法,包括获取grpc客户端,和我们在protobuf文件中顶一顶

│ ├── proto
│ │ ├── blog.proto
│ │ └── gen
│ │ └── go
│ │ └── blog
│ │ ├── blog.pb.go
│ │ └── blog_grpc.pb.go

gprc服务端实现

  • 接下来我们改造一下,由原来的请求MySQL 改为请求grpc
func (t *BlogController) RpcGetOne(c *gin.Context) {id := c.Param("id")id_int, _ := strconv.Atoi(id)//监听端口conn, err := grpc.Dial("localhost:9988", grpc.WithInsecure())if err!=nil {log.Fatal(err)}defer conn.Close()//取到客户端连接client:=blog2.NewBlogInfoClient(conn)//组装结构体blogId:=new(blog2.Id)blogId.Value=int64(id_int)info, _ :=client.GetBlog(context.Background(),blogId)fmt.Println(id)c.JSON(200, gin.H{"message": "success","data":    info,})return
}

gprc服务端实现

  • 接下来我们编写服务端的实现
package mainimport ("context""fmt""github.com/blog-server/proto/gen/go/blog""google.golang.org/grpc""log""net""strconv"
)type  Server struct{}
/*** @Description* @Author 老a技术联盟* @Date 2023/2/6 11:48**/
func main() {listen, err := net.Listen("tcp", "localhost:9988")if err != nil {return}//创建grpc服务s:=grpc.NewServer()blog.RegisterBlogInfoServer(s,Server{})if err := s.Serve(listen); err != nil {log.Println("failed to serve...", err)return}
}//继承接口
func (s Server)GetBlog(c context.Context,b *blog.Id) (*blog.Blog, error) {//从mysql中取数据的过程忽略,直接返回结果blog:=new(blog.Blog)blog.Id= strconv.Itoa(int(b.Value))fmt.Printf("接收到的id是:%d",b.Value)blog.Author="grpc返回"return blog,nil
}

重启服务

  • 先启动grpc服务端,在启动客户端
  • 然后请求获取一条博文

在这里插入图片描述

  • 服务端信息打印
  • 在这里插入图片描述
  • 其中 pb文件在项目中往往是作为一个包文件被引用的,这样就不会重复拷贝文件到目录

相关内容

热门资讯

监控摄像头接入GB28181平... 流程简介将监控摄像头的视频在网站和APP中直播,要解决的几个问题是:1&...
Windows10添加群晖磁盘... 在使用群晖NAS时,我们需要通过本地映射的方式把NAS映射成本地的一块磁盘使用。 通过...
protocol buffer... 目录 目录 什么是protocol buffer 1.protobuf 1.1安装  1.2使用...
educoder数据结构与算法...                                                   ...
MySQL下载和安装(Wind... 前言:刚换了一台电脑,里面所有东西都需要重新配置,习惯了所...
MFC文件操作  MFC提供了一个文件操作的基类CFile,这个类提供了一个没有缓存的二进制格式的磁盘...
有效的括号 一、题目 给定一个只包括 '(',')','{','}'...
【Ctfer训练计划】——(三... 作者名:Demo不是emo  主页面链接:主页传送门 创作初心ÿ...
Fluent中创建监测点 1 概述某些仿真问题,需要创建监测点,用于获取空间定点的数据࿰...
带头循环双向链表来咯!!! 前言:继上文,我们了解了结构最简单的一种链表---单链表那么我们今天就来...