golang 重构博客统计服务
欢迎关注楼主与他的小伙伴们的小站,每周分享一些技术文章,让我们在技术上一起成长------> 戳这里,欢迎光临小站 -_-
作为一个后端开发,在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支持一下。
golang 重构博客统计服务的更多相关文章
- Node.js 从零开发 web server博客项目[express重构博客项目]
web server博客项目 Node.js 从零开发 web server博客项目[项目介绍] Node.js 从零开发 web server博客项目[接口] Node.js 从零开发 web se ...
- Node.js 从零开发 web server博客项目[koa2重构博客项目]
web server博客项目 Node.js 从零开发 web server博客项目[项目介绍] Node.js 从零开发 web server博客项目[接口] Node.js 从零开发 web se ...
- 浅析-博客Ping服务
简介:PING服务是博客站点向博客目标网站.搜索引擎等发出的博客内容更新通知服务,然后博客目标网站.搜索引擎就会及时的索引.收录以及传播您的博客内容. PING原理 PING 服务是博客站点向博客目标 ...
- 第三代微服务架构:基于 Go 的博客微服务实战案例,支持分布式事务
这是一个可一键部署在 Kubernetes-Istio 集群中的,基于 Golang 的博客微服务 Demo,支持分布式事务. 项目地址:https://github.com/jxlwqq/blog- ...
- I-team 博客全文检索 Elasticsearch 实战
一直觉得博客缺点东西,最近还是发现了,当博客慢慢多起来的时候想要找一篇之前写的博客很是麻烦,于是作为后端开发的楼主觉得自己动手丰衣足食,也就有了这次博客全文检索功能Elasticsearch实战,这里 ...
- 国内博客(blog)搬家工具(服务)大全
如今网络上的博客搬家 服务,博客搬家工具 越来越多,博客联盟 大概收集了下,希望对那些想搬家的博客有所帮助. 一.和讯博客的“搬家公司”提供博客搬家 服务 搬家服务地址:点这里 目标对象:新浪博客 . ...
- Node.js 从零开发 web server博客项目[安全]
web server博客项目 Node.js 从零开发 web server博客项目[项目介绍] Node.js 从零开发 web server博客项目[接口] Node.js 从零开发 web se ...
- 前端react+redux+koa写的博客推荐
React-Node搭建的博客 曾经用的php+mysql+js写的博客,现在看来已经很low了,所以用目前最火的react+koa框架重构一下.先上地址吧:目前线上版本http://www.liuw ...
- 基于 Github Actions 自动部署 Hexo 博客
前言 前不久使用了 Hexo 搭建独立博客,我是部署在我的腾讯云轻量应用服务器上的,每次都需要 hexo deploy 然后打包.上传.解压和刷新 CDN,非常麻烦.我的服务器配置也不高 2C2G 无 ...
随机推荐
- 从零开始搭建包含多个子系统的Vue工程项目
本文以windows为例,介绍支持多个子系统的Vue工程项目的搭建过程,相对于单一系统的工程,多个子系统引入了如下一些问题: 项目目录结构设计 打包结果设计:每个子系统可以独立发布上线 多布局实现:多 ...
- 阻塞IO(blocking IO)
在linux中,默认情况下所有的socket都是blocking,一个典型的读操作流程大概是这样: 当用户进程调用了recvfrom这个系统调用,kernel就开始了IO的第一个阶段:准备数据.对于n ...
- 【linux】查看进程使用的端口和端口使用情况
netstat -a 查看所有服务端口 netstat -tln 查看当前使用的端口 ps命令查看进程的id: ps aux | grep ftp 或者 pidof Name netstat命 ...
- python's fourth day for me 列表
break 可以打断 for 循环不执行 else 语句 s = 'fdddsadwes' for i in s: if i == 's': break #可跳出for循环且不用执行else语句 pr ...
- Spring AOP基于注解的“零配置”方式实现
为了在Spring中启动@AspectJ支持,需要在类加载路径下新增两个AspectJ库:aspectjweaver.jar和aspectjrt.jar.除此之外,Spring AOP还需要依赖一个a ...
- 【294】◀▶ Python 字符串说明
目录: 一.Python访问字符串中的值 二. Python 转义字符 三.Python 字符串运算符 参考:Python 字符串 一.Python访问字符串中的值 Python不支持单字符类型, ...
- alsa-lib及alsa-utils成功移植(原…
准备工作 alsa-lib版本:alsa-lib-1.0.23.tar.bz2 alsa-util版本:alsa-utils-1.0.23.tar.bz2 其他版本的alsa-lib和alsa-uti ...
- Java虚拟机(三):垃圾收集器
一.串行(Serial)收集器 最古老,最稳定 效率高 可能会产生较长的停顿 -XX:+UseSerialGC 新生代.老年代使用串行回收 新生代复制算法 老年代标记-压缩 二.并行收集器 1. Pa ...
- PhoneGap 3.4 开发配置及问题
PhoneGap这个坑爹货,开发确实迅速,又无需学习新知识,但又有N多深不见底坑,最大的坑无疑是性能,滑动时卡顿明显,iPhone5上性能比较好,大部分安卓上就坑爹了,神马动画效果最好少用:其次是不同 ...
- Solidity mapping循环
https://medium.com/@blockchain101/looping-in-solidity-32c621e05c22