技术总结

gorm使用记录

关于Gorm #

gorm文档

遇见问题 #

无法通过结构体的方式更新或查询零值 #

这里零值是说,各个类型的默认值。

关于这一点是在这里中注明了的,也提供了解决方案:

WARNING when update with struct, GORM will only update those fields that with non blank value

For below Update, nothing will be updated as “”, 0, false are blank values of their types

NOTE When query with struct, GORM will only query with those fields has non-zero value, that means if your field’s value is 0, ‘’, false or other zero values, it won’t be used to build query conditions,

...

Golang学习笔记

目录 #

Channel #

一开始是在看channel的源码,结果发现里面含有一些抽象的描述(可能也就是我觉得。。。毕竟没有深入)

Do not change another G’s status while holding this lock (in particular, do not ready a G), as this can deadlock with stack shrinking.

其中G是啥?我看着是很懵逼的,去google了一下,其实是goroutine相关的知识,那就把goroutine理解了先。

2020-04-13 填坑

channel in go

Goroutine #

G: 表示goroutine,存储了goroutine的执行stack信息、goroutine状态以及goroutine的任务函数等;另外G对象是可以重用的。 P: 表示逻辑processor,P的数量决定了系统内最大可并行的G的数量(前提:系统的物理cpu核数>=P的数量);P的最大作用还是其拥有的各种G对象队列、链表、一些cache和状态。 M: 代表着真正的执行计算资源。在绑定有效的p后,进入schedule循环;而schedule循环的机制大致是从各种队列、p的本地队列中获取G,切换到G的执行栈上并执行G的函数,调用goexit做清理工作并回到m,如此反复。M并不保留G状态,这是G可以跨M调度的基础。M必须关联了P才能执行Go代码。

结合下图更方便理解: –源于Tonybai的博客,见参考资料。 Goroutine调度原理图

参考资料 #

aliyun-rds数据备份方案

本文主要是总结下在使用aliyun-rds数据备份方案过程中的心得。

高可用一直都是线上服务维护用户体验的关键之一。为了达到高可用,业界已经有了很多方案。最典型的就是“冗余备份+自动故障转移”。冗余备份是说,当一个节点服务不可用时,有其他服务能够代替其工作。除此之外,如果服务出现了必须人工介入解决的故障,也会影响系统的高可用特性。

本文着重介绍数据的高可用方案

数据库冗余 #

如果是单节点的数据库,还用的着说吗?要保证服务高可用,除了主-从数据库之外,还需要从备份数据库,当然不能保证说一定不会遇到所有的备份数据库,都挂掉的情况…。阿里云提供了RDS-高可用版本RDS-单机版,两者的区别见下图: 基础版和高可用 这就算最基本的冗余了,没有主从复制,没有读写分离。但是能保证主库在换掉的时候,还能使用备库提供服务。如果服务对于数据库性能和可用性有一定要求,那么可以在这个基础上升个级,见下图: 灾备方案

数据故障自动转移 #

已经有了冗余的数据库节点了,那么接下来要做的事情就是怎么感知数据库异常,并实现自动切换到备份实例中? 阿里云灾备方案的文档是这样描述的:

主实例和灾备实例均搭建主备高可用架构,当主实例所在区域发生突发性自然灾害等状况,主节点(Master)和备节点(Slave)均无法连接时,可将异地灾备实例切换为主实例,在应用端修改数据库链接地址后,即可快速恢复应用的业务访问。

对于主节点全部不可用的情况对应用服务是可见的,因此应用服务可以通过指定一些异常判断,在判定主节点不可用的时候,主动切换数据库连接地址来获取数据,提供服务。

// sql-detect.go

package main

import (
    "database/sql"
    "fmt"
    "sync"
    "time"

    _ "github.com/go-sql-driver/mysql"
    _ "github.com/mxk/go-sqlite"
)

var (
    mysqlAvailable bool    = true
    mutex                  = sync.Mutex{}
    db             *sql.DB = nil
)

func MysqlDetection(db *sql.DB, ticker *time.Ticker) {
    for {
        select {
        case <-ticker.C:
            if e := db.Ping(); e != nil {
                fmt.Println("got error", e)

                mutex.Lock()
                mysqlAvailable = false
                mutex.Unlock()

            } else {
                fmt.Println("status ok")
            }
        }
    }
}

func MysqlSwitch() {
    for {
        mutex.Lock()
        if !mysqlAvailable {
            fmt.Println("Switch Sqlite3")
            db, _ = sql.Open("sqlite3", "./foo.db")
        }
        mutex.Unlock()

        time.Sleep(time.Second * 4)
    }
}

func main() {
    c := make(chan bool)

    db, _ = sql.Open("mysql", "yeqiang:yeqiang@/test_yeqiang")
    ticker := time.NewTicker(time.Second * 2)

    go MysqlDetection(db, ticker)
    go MysqlSwitch()

    <-c
}

测试截图:

...

ShortURL系统实现

在知乎上看了一个很有启发的回答,因此实际动手来实现短URL生成系统。贴上链接: 知乎 - 短URL系统是如何设计的。其中提到了,要实现短URL生成系统要解决的问题有:

  • 如何优雅的实现?
  • 怎么基本实现长对短、一对一?
  • 如何实现分布式,高并发,高可用?
  • 储存选用?

基本原理 #

数据库自增ID转换62进制

  1. 使用自增ID不会产生重复的短链接。
  2. 为了解决自增ID超长和不便记忆,对ID进行62进制编码。所谓62进制就是0-9,a-z,A-Z。

简单计算下:

62 ^ 4 = 14,776,336
62 ^ 5 = 916,132,832
62 ^ 6 = 56,800,235,584 // 已经足够使用了

总体结构及处理流程 #

服务结构

长链接处理流程 #

  1. 获取参数,调用shortURL服务
  2. 尝试从缓存中获取,如果命中,则读取短链接(重置过期时间)。跳转第4步
  3. 将长链接存储到Mysql数据库,根据ID进行base62编码,组装Domain+Encoded字符串并更新数据库
  4. 返回生成的短链接

短链接处理流程 #

  1. 解析短链接为ID
  2. 查询ID对应的长链接
  3. 以301方式跳转到长链接

长链接与短链接的对应关系 #

一对多,一个长链接可能对应多个短链接。数据表存储结构如下:

+-----------+--------------+------+-----+---------+----------------+
| Field     | Type         | Null | Key | Default | Extra          |
+-----------+--------------+------+-----+---------+----------------+
| id        | int(64)      | NO   | PRI | NULL    | auto_increment |
| long_url  | varchar(100) | NO   |     | NULL    |                |
| short_url | varchar(40)  | YES  |     | NULL    |                |
+-----------+--------------+------+-----+---------+----------------+

分布式和高并发设计 #

###注:这部分未实现。我的思路如下:

...

Golang服务端技术笔记

总结使用Golang开发服务端时,使用的基础的工具和部署方式。用于思考不足并优化,提升编码效率。 总体上采用MVCS的软件模式,如下图:

MVCS模式

从图中可以看出,MVCS是从MVC进化而来,相比于MVC,增加了Service层。把业务逻辑从Controller层中抽离出来,这样做的好处在于,项目日益庞大之后,将某些功能独立出来。

Golang工具 #

“gvt” 依赖管理工具 “httprouter” 路由及中间件配置 “schema” 解析请求参数到结构体 “beego/validation” 结构体校验工具 “github.com/go-redis/redis” redis操作库 “github.com/go-sql-driver/mysql” Mysql Driver

文件结构 #

--Golang Project
    |-sh            # shell脚本,包括数据库脚本
    |-config        # 配置文件
    |-logs          # 日志文件
    |-vendor        # 项目源码及依赖
    |  |-github.com #
    |  |-mainfest   # gvt 依赖管理文件
    |  |-app
    |     |-utils
    |     |-controllers
    |     |-models
    |     |-route
    |     |-services
    |-Dockerfile    # docker构建镜像配置文件
    |-docker-compose.yml # docker-compose.yml文件
    `-entry.go      # web服务入口文件

部署方式 #

采用docker来部署应用。分别编写Dockerfile和docker-composer.yml文件,实例如下:

...

docker-compose上手

docker compose 用于快速在集群中部署分布式应用。按我的理解也可以用于简化部署单个应用。譬如我要使用dock er启动一个nginx服务,需要做端口映射,挂载数据文件,指定镜像…等等,这种情况下,可以将启动容器的命令整合到docker-compose.yml文件中,可以在多个服务器上运行,瞬间就完成了nginx的安装及配置,再也不用去编译,解决环境依赖了,这种感觉实在是太爽了!!!

安装 #

  1. 使用pip pip install docker-compose
  2. 从官方Github Release下载二进制包文件
  3. 其他方法略去

使用场景 #

在日常工作中,经常会碰到需要多个容器相互配合来完成某项任务的情况。例如要实现一个 Web 项目,除了 Web 服务容器本身,往往还需要再加上后端的数据库服务容器,甚至还包括负载均衡容器等。

实战场景 #

需要部署的项目,只有两个docker容器,一个server,一个db。一般的部署方式是,分别启动两个容器,容器间通过互联的方式通信:

sudo docker run --rm -p 5433:5432 --name postgres -e POSTGRES_PASSWORD=minepwd -e POSTGRES_USER=mineusr -d postgres

sudo docker run --rm -p 9091:9091 --link postgres:postgres --name mineserver -d me/mineserver

这两条命令还是有挺麻烦的,如果记不住,当然可以用shell脚本来运行,可以如果其中某一个服务无法如期运行。。。就很监介了。这时候就可以引入docker-compose了。

编写docker-compose.yml来部署项目 #

version: "2" # 指定docker-compose版本
services:    # 项目依赖的服务
  postgres:  # 服务名字
    image: postgres # 服务需要的docker镜像与docker run命令中的镜像指定方式一致
    volumes:        # 挂载卷,这里的主要目的是,方便同步数据库和数据脚本
      - ./postgres:/var/lib/postgresql/data
      - ./sh:/usr/src/sh
    ports:          # 端口绑定
      - 5433:5432
    container_name: postgres
    environment:    # 设置环境变量
      POSTGRES_PASSWORD: "minepwd"
      POSTGRES_USER: "mineusr"
      POSTGRES_DB: "minedb"
  mineserver:
    image: me/mineserver
    volumes:        # 挂载卷,方便查看输出日志
      - ./logs:/usr/src/mineserver/logs
    ports:
      - 9091:9091
    container_name: mineserver
    links:          # 容器互联
      - postgres:postgres

在编写docker-compose.yml的时候,需要注意的是各个选项的数据类型,不过docker-compose会有提示,也很方便

...

RN历险记

RN

讲述配置ReactNative的心酸历程

程序猿长征第一步 #

根据官方文档来安装RN, 以及巨大无比的Xcode Ver9.0.0

错误一:Build Fail #

可能描述不太一致, 但是原因都差不多, 文件缺失。 菜鸟想必看到这些个报错, 两眼一懵逼, 啥子情况, 怎么和官方的描述不一致, 一个及其简单的RN-Demo, 我就是想跑一下的喂!

我遇到的情况, 分为两种:

其一是安装很慢, 之后失败 其二是安装很快, 然后失败

经过反复的“瞎子”调整, 在多次更换react, react-native版本, 求助Google大叔无果之后。我开始了阅读输出日志的漫漫长路, 终于发现了build失败的元凶, boost/xxx.hpp not found为啥找不到呢, 去文件夹一看, 才发现这些文件真的不存在……

好了知道错误, 就再Google下咯(其实我还去改过这些#includ<boost/config/user.hpp> 0<~>0)这里就直接给出我找的结果吧: http://cdn2.jianshu.io/p/2ef019a7e82a

总的说来, 就是自动下载的的第三方库是残缺的

错误二:CFBundleIdentifier not Found #

第二错误也是困扰了比较多人, 我遇到的只是导致这个情况的其中之一

通过查看输出日志, 并没有发现什么有用的信息, 提示的是Command Fail , balabala… 手动搜索了一下PlistBuddy, 了解了下用法, 然后我手动执行了下命令, 居然可以!!!!什么情况, 那为什么提示错误信息?果断进入到文件夹中查看,果然文件是存在的那么为啥一个可以, 一个不可以呢?到这里, 大致猜到原因了, 没有找到文件

再次以此为点求助Google大叔: http://blog.csdn.net/ohyeahhhh/article/details/54691512

这个坑就是, Xcode编译保存的路径和react-native-cli寻找的路径不一致, 通过修改路径就OK啦, 还有其他原因导致的这个fail 请参阅链接, 先搞清楚react-native run-ios做了啥事.

...

访问量 访客数