任务书:https://blog.youkuaiyun.com/xljiayou_/article/details/104707510
目录结构
本次开发项目名为first_demo,采用mvc三层架构。controller中的server.go用于创建httpserver,配置路由和处理器。
service中的service.go用于处理业务,比如查询操作,先从redis查,没有时再从mysql查。
dao中的mysql_util封装了go连接mysql的crud操作,redis_util封装了go连接redis的crud操作。
github.com目录中包含两个驱动,用于go连接redis和mysql。
开发
在开发过程中,我是从dao层开始写的,到controller层时发现底层写的方法与业务有些偏差,然后又进行了修改,在以后的开发中,最好事先理解好需求,开发时从上层开始写起。
server.go
package main
import (
"encoding/json"
"first_demo/dao/mysql_util"
"first_demo/dao/redis_util"
"fmt"
"net/http"
"strings"
"first_demo/service"
)
type AddHandler struct{}
type DelHandler struct{}
type ModHandler struct{}
type QueryHandler struct{}
// 封装处理成功的信息
type succ struct {
Ret int `json:"ret"`
Data map[string] string `json:"data"`
}
// 封装处理失败的信息
type fail struct {
Ret int `json:"ret"`
Msg string `json:"msg"`
}
func (h *AddHandler) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
fmt.Println(req.Method)
// 如果不是POST请求,返回404
if req.Method != "POST" {
resp.WriteHeader(404)
return
}
fmt.Println("add")
// 对请求的json参数进行解码,存放到map中
decoder := json.NewDecoder(req.Body)
params := map[string] string{}
decoder.Decode(¶ms)
err := service.Add(params["key"], params["value"])
var res []byte
if err != nil {
// 处理失败
ret := -1
msg := "add error."
res, _ = json.Marshal(fail{ret, msg})
}else {
// 处理成功
res, _ = json.Marshal(succ{1, nil})
fmt.Println("1")
}
// 写入响应
resp.Write(res)
}
func (h *DelHandler) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
fmt.Println("del")
if req.Method != "POST" {
resp.WriteHeader(404)
return
}
decoder := json.NewDecoder(req.Body)
params := map[string] []string{}
decoder.Decode(¶ms)
err := service.Del(params["keys"])
var res []byte
if err != nil {
res, _ = json.Marshal(fail{-1, "del error."})
}else {
res, _ = json.Marshal(succ{1, nil})
}
resp.Write(res)
}
func (h *ModHandler) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
fmt.Println("mod")
if req.Method != "POST" {
resp.WriteHeader(404)
return
}
decoder := json.NewDecoder(req.Body)
params := map[string] string{}
decoder.Decode(¶ms)
err := service.Mod(params["key"], params["value"])
res := []byte{}
if err != nil {
res, _ = json.Marshal(fail{-1, "mod error."})
}else {
res, _ = json.Marshal(succ{1, nil})
}
resp.Write(res)
}
func (h *QueryHandler) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
fmt.Println("query")
if req.Method != "POST" {
resp.WriteHeader(404)
return
}
// 将输入的json数据,解码存放到map中
decoder := json.NewDecoder(req.Body)
m := map[string] []string{}
decoder.Decode(&m)
kvs, err := service.Query(m["keys"])
res := []byte{}
if err != nil {
res, _ = json.Marshal(fail{-1, "query error."})
}else {
res, _ = json.Marshal(succ{1, kvs})
}
resp.Write(res)
}
// 将数组字符串转换成切片
func strToSlice(str string) []string {
str = strings.ReplaceAll(str[1:len(str)-1], "\"", "")
return strings.Split(str,",")
}
func main() {
// 建立redis连接和mysql连接
redis_util.StartRedis()
mysql_util.StartMysql()
// 建立路由表
http.Handle("/add", &AddHandler{})
http.Handle("/del", &DelHandler{})
http.Handle("/mod", &ModHandler{})
http.Handle("/query", &QueryHandler{})
// 监听本地端口
http.ListenAndServe(":8000", nil)
}
service.go
package service
import (
"first_demo/dao/mysql_util"
"first_demo/dao/redis_util"
)
func Add(key, value string) error {
return mysql_util.AddByKeyValue(key, value)
}
func Del(keys []string) error {
for _, key := range(keys) {
// 同时在redis 和 mysql中删除
redis_util.DelByKey(key)
err := mysql_util.DelByKey(key)
if err != nil {
return err
}
}
return nil
}
func Mod(key, value string) error {
// 在mysql中更新
err := mysql_util.ModByKeyValue(key, value)
if err != nil {
return err
}
// 在redis中删除
redis_util.DelByKey(key)
return nil
}
func Query(keys []string) (map[string]string, error) {
// 存储查询到的结果
kvs := map[string] string{}
for _, key := range(keys) {
// 对每个key,现在redis中进行查找
value, err := redis_util.QueryByKey(key)
if err == nil {
kvs[key] = value
continue
}
// 如果redis里中没有,在mysql中查找
value, err = mysql_util.QueryByKey(key)
if err != nil {
return nil, err
}
// 如果找到,将数据写入到redis中
kvs[key] = value
err = redis_util.AddKeyValue(key, value)
if err != nil {
return nil, err
}
}
return kvs, nil
}
mysql_util.go
package mysql_util
import (
"database/sql"
"errors"
"fmt"
_ "github.com/go-sql-driver/mysql"
)
var db *sql.DB
func StartMysql() {
var err error
db, err = sql.Open("mysql", "root:Xuelei123@tcp(127.0.0.1:3306)/first_demo?charset=utf8")
if err != nil {
fmt.Println("connect mysql error!")
return
}
}
func QueryByKey(key string) (string, error) {
var v string
stmt, _ := db.Prepare("select `value` from key_value where `key`=?")
row := stmt.QueryRow(key)
err := row.Scan(&v)
return v, err
}
func DelByKey(key string) error {
stmt, _ := db.Prepare("delete from key_value where `key` = ?")
_, err := stmt.Exec(key)
return err
}
func ModByKeyValue(key, value string) error {
result, err := db.Exec("update key_value set `value`=? where `key`=?", value, key)
if err != nil {
return err
}
if i, _ :=result.RowsAffected(); i == 0 {
return errors.New("mod error.")
}
return nil
}
func AddByKeyValue(key, value string) error {
res, err := db.Exec("insert into key_value values(?, ?)", key, value)
if err != nil {
return err
}
if i, _ := res.RowsAffected(); i == 0 {
return errors.New("insert error.")
}
return nil
}
redis_util.go
package redis_util
import (
_ "errors"
"fmt"
"github.com/garyburd/redigo/redis"
)
// 设置存储时间
const LIVETIME string = "300"
var conn redis.Conn
func StartRedis() {
var err error
conn, err = redis.Dial("tcp", "127.0.0.1:6379")
if err != nil {
fmt.Println("connect error.")
return
}
}
func QueryByKey(key string) (string, error) {
value, err := redis.String(conn.Do("Get", key))
return value, err
}
func DelByKey(key string) error {
_, err := conn.Do("DEL", key)
return err
}
func ModByKeyValue(key, value string) error {
_, err := redis.String(conn.Do("SET", key, value, "EX", LIVETIME))
return err
}
func AddKeyValue(key, value string) error {
_, err := conn.Do("SET", key, value, "EX", LIVETIME)
return err
}