分享
  1. 首页
  2. 文章

golang 重构博客统计服务

haifeiWu · · 3496 次点击 · · 开始浏览
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

欢迎关注楼主与他的小伙伴们的小站,每周分享一些技术文章,让我们在技术上一起成长------> 戳这里,欢迎光临小站 -_-

作为一个后端开发,在docker,etcd,k8s等新技术不断涌现的今天,其背后的功臣golang在语言排行榜上持续走高,因此楼主也就开了这次使用golang自己开发的基础功能的二次装逼之旅。

源于Spring Boot

感兴趣的小伙伴可以看看楼主的上一篇,基于Spring Boot实现的功能,请移步使用Spring Boot实现博客统计服务

实现redis存储逻辑

选择redis而没选择数据库的原因是redis提供了丰富的数据结构与数据持久化策略,另外redis是基于内存的,相对于数据库来说,快了不止一个数量级。而统计阅读次数的场景对接口处理的速度还是有一定的要求的,因此楼主选择了redis作为阅读次数统计的db。
下面就是redis操作的基础代码,比较简单楼主贴一下代码,不做进一步的阐述。

  • redigo依赖下载
go get github.com/gomodule/redigo/redis
  • redis操作的工具类
func initRedisPool() {
 // 建立连接池
 RedisClient = &redis.Pool{
 // 从配置文件获取maxidle以及maxactive,取不到则用后面的默认值
 MaxIdle: 1,
 MaxActive: 10,
 IdleTimeout: 180 * time.Second,
 Dial: func() (redis.Conn, error) {
 c, err := redis.Dial("tcp", RedisAddress)
 if err != nil {
 return nil, err
 }
 // 选择db
 c.Do("SELECT", RedisDb)
 return c, nil
 },
 }
}
/**
 * 设置redis的对应key的value
 */
func redisSet(key string, value string) {
 c, err := RedisClient.Dial()
 if err != nil {
 fmt.Println("Connect to redis error", err)
 return
 }
 _, err = c.Do("SET", key, value)
 if err != nil {
 fmt.Println("redis set failed:", err)
 }
}
/**
 * 获取redis的对应key的value
 */
func redisGet(key string) (value string) {
 c, err := RedisClient.Dial()
 if err != nil {
 fmt.Println("Connect to redis error", err)
 return
 }
 val, err := redis.String(c.Do("GET", key))
 if err != nil {
 fmt.Println("redis get failed:", err)
 return ""
 } else {
 fmt.Printf("Got value is %v \n", val)
 return val
 }
}
/**
 * redis使得对应的key的值自增
 */
func redisIncr(key string) (value string) {
 c, err := RedisClient.Dial()
 _, err = c.Do("INCR", key)
 if err != nil {
 fmt.Println("incr error", err.Error())
 }
 incr, err := redis.String(c.Do("GET", key))
 if err == nil {
 fmt.Println("redis key after incr is : ", incr)
 }
 return incr
}

博客阅读次数统计接口实现

博客阅读次数统计的基本业务逻辑就是,对应每篇博客的blogId作为redis的key,而访问次数就是这个key所对应的value,每访问一次该接口就要将对应的blogId自增一次,并返回对应的value。这里楼主选择的redis的数据结构是redis的Stirng,下面是楼主实现该逻辑的主要代码:


package main
import (
 "encoding/json"
 "fmt"
 "github.com/garyburd/redigo/redis"
 "log"
 "net/http"
 "time"
 "strings"
)
const RedisAddress = "127.0.0.1:6379"
const RedisDb = 0
const AllowRequestUrlH = "*"
const AllowRequestUrlW = "*"
const IllegalCharacters = "?"
const DefaultReadCount = "1"
var (
 // 定义常量
 RedisClient *redis.Pool
)
func main() {
 // 初始化redis连接池
 initRedisPool()
 // 启动web服务监听
 http.HandleFunc("/*-*/*/", blogReadCountIncr) //设置访问的路由
 err := http.ListenAndServe(":9401", nil) //设置监听的端口
 if err != nil {
 log.Fatal("ListenAndServe: ", err)
 }
}
func blogReadCountIncr(responseWriter http.ResponseWriter, request *http.Request) {
 // 解析参数,默认不解析
 request.ParseForm()
 blogId := request.Form.Get("blogId")
 log.Println(">>>>>> method blogReadCountIncr exec , request params is : ",blogId)
 // 判断请求参数是否为空
 if "" == blogId {
 result := ResultCode{
 Code: 200,
 Msg: "success",
 }
 ret, _ := json.Marshal(result)
 fmt.Fprintf(responseWriter, string(ret)) //这个写入到w的是输出到客户端的
 }
 
 readCount := redisGet(blogId)
 if "" == readCount {
 // 不符合规则,直接返回
 flag := strings.Index(blogId, AllowRequestUrlH) != 0 ||strings.Index(blogId, AllowRequestUrlW) != 0||strings.Contains(blogId, IllegalCharacters)
 if !flag {
 result := ResultCode{
 Code: 200,
 Msg: "success",
 }
 ret, _ := json.Marshal(result)
 fmt.Fprintf(responseWriter, string(ret)) //这个写入到w的是输出到客户端的
 }
 redisSet(blogId, DefaultReadCount)
 readCount = DefaultReadCount
 } else {
 readCount = redisIncr(blogId)
 }
 log.Println(">>>>>> readCount is : ",readCount)
 result := ResultCode{
 Code: 200,
 Msg: "success",
 Data: readCount,
 }
 ret, _ := json.Marshal(result)
 fmt.Fprintf(responseWriter, string(ret)) //这个写入到w的是输出到客户端的
}
// 结构体定义返回值
type ResultCode struct {
 Msg string `json:"msg"`
 Code int `json:"code"`
 Data string `json:"data"`
}

实现过程中遇到的坑

出现的问题

使用golang原生的json工具序列化时,出现序列化失败的问题,如下所示的结构体定义,乍一看是没啥问题的,然而使用

ret, _ := json.Marshal(result)

序列化时,出现无法序列化成json串的问题,另外还不报错,这让楼主很是头疼。

type ResultCode struct {
 msg string `json:"msg"`
 code int `json:"code"`
 data string `json:"data"`
}

问题解决

最终楼主通过各种姿势的排查,发现是结构体定义有问题,当定义结构体时首字母必须大写才能序列化成功,这个特点在golang里面很是明显,在函数调用时首字母小写的函数在其他文件里面是调不到的。下面给出正确的结构体定义

type ResultCode struct {
 Msg string `json:"msg"`
 Code int `json:"code"`
 Data string `json:"data"`
}

小结

目前很多大佬都写过关于golang web的教程,如有雷同,请略过不看,本文通过自己的亲身实战以及楼主自己踩到的坑完成的,另外本文是基于go内置的net/http库实现的web服务。

号外

楼主造了一个轮子,LIGHTCONF 是一个基于Netty实现的一个配置管理平台,其核心设计目标是"为业务提供统一的配置管理服务",可以做到开箱即用。感兴趣的给个star支持一下。


有疑问加站长微信联系(非本文作者)

本文来自:博客园

感谢作者:haifeiWu

查看原文:golang 重构博客统计服务

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

关注微信
3496 次点击
被以下专栏收入,发现更多相似内容
暂无回复
添加一条新回复 (您需要 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传

用户登录

没有账号?注册
(追記) (追記ここまで)

今日阅读排行

    加载中
(追記) (追記ここまで)

一周阅读排行

    加载中

关注我

  • 扫码关注领全套学习资料 关注微信公众号
  • 加入 QQ 群:
    • 192706294(已满)
    • 731990104(已满)
    • 798786647(已满)
    • 729884609(已满)
    • 977810755(已满)
    • 815126783(已满)
    • 812540095(已满)
    • 1006366459(已满)
    • 692541889

  • 关注微信公众号
  • 加入微信群:liuxiaoyan-s,备注入群
  • 也欢迎加入知识星球 Go粉丝们(免费)

给该专栏投稿 写篇新文章

每篇文章有总共有 5 次投稿机会

收入到我管理的专栏 新建专栏