golang重构博客统计服务

代码星冰乐

专注成就未来

首页 归档 关于

golang重构博客统计服务

Jun 25, 2018 | haifeiWu | Go | 阅读
文章目录
  1. 1. 源于Spring Boot
  2. 2. 实现redis存储逻辑
  3. 3. 博客阅读次数统计接口实现
  4. 4. 实现过程中遇到的坑
    1. 4.1. 出现的问题
    2. 4.2. 问题解决
  5. 5. 小结
  6. 6. 号外

作 者:haifeiWu
原文链接:https://www.hchstudio.cn/article/2018/2622/
版权声明:非特殊声明均为本站原创作品,转载时请注明作者和原文链接。


由于版权原因,请阅读原文 --> golang重构博客统计服务

关注我们

作 者:haifeiWu
原文链接:https://www.hchstudio.cn/article/2018/2622/
版权声明:非特殊声明均为本站原创作品,转载时请注明作者和原文链接。

作 者:haifeiWu
原文链接:https://www.hchstudio.cn/article/2018/2622/
版权声明:非特殊声明均为本站原创作品,转载时请注明作者和原文链接。

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

源于Spring Boot

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

实现redis存储逻辑

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

  • redigo依赖下载

    1
    go get github.com/gomodule/redigo/redis
  • redis操作的工具类

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
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,下面是楼主实现该逻辑的主要代码:

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

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工具序列化时,出现序列化失败的问题,如下所示的结构体定义,乍一看是没啥问题的,然而使用

1
ret, _ := json.Marshal(result)

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

1
2
3
4
5
type ResultCode struct {
msg string `json:"msg"`
code int `json:"code"`
data string `json:"data"`
}

问题解决

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

1
2
3
4
5
type ResultCode struct {
Msg string `json:"msg"`
Code int `json:"code"`
Data string `json:"data"`
}

小结

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

号外

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

  • 基于Netty实现的轻量级分布式应用配置中心

关注我们

作 者:haifeiWu
原文链接:https://www.hchstudio.cn/article/2018/2622/
版权声明:非特殊声明均为本站原创作品,转载时请注明作者和原文链接。

分享
Redisgolangweb
阿里离线数据同步工具 DataX 踩坑记录Netty实战之第一个应用
微信关注我们
分类
  • Android8
  • Go4
  • Java59
  • Kafka,Java1
  • Kotlin2
  • Linux1
  • MapReduce1
  • Python2
  • Raft1
  • Redis1
  • ThreadPoolExecutor1
  • go1
  • 工具1
  • 总结8
  • 旅游日记1
标签
Nginx ChanghuiN haifeiWu Android Java 设计模式 hexo Kotlin 算法 MySQL 源码解析 Python Redis golang web Kafka 配置中心 总结 性能优化 旅游日记 Shell Go 问题排查 译文 Docker Spring Boot 工具 学习笔记 WebFlux 性能测试 go 散列表 源码 netty Raft
最近文章
  • Kafka的日志复制机制
  • 从20到21
  • go 并发编程
  • 【译】了解Linux CPU负载-您何时应该担心?
  • Zookeeper 与分布式锁
  • 基于Redis的分布式锁到底安全吗?
  • 【译】Raft 学生指南
  • ThreadPoolExecutor 的简单梳理
  • MapReduce 的简单实现
  • 使用 Map 实现策略模式
福利专区
    免费SSL证书
      阿里云红包
        腾讯云专属福利
        Copyright © 2021 代码星冰乐. Powered by ChanghuiN. 版权所有 晋ICP备15001365号
        特别感谢: 云服务器服务商 、 CDN 服务商