netlify应用部署服务开启history模式

如果你的应用服务启用了history路由模式,服务需要重定向到 index.html

netlify 默认不启用,启用需要在 public 目录新建 _redirects 文件内容:

/* /index.html 200

netlify.toml

[[redirects]]
  from = "/*"
  to = "/index.html"
  status = 200

参考:https://docs.netlify.com/routing/redirects/rewrites-proxies/#history-pushstate-and-single-page-apps

具体可以参考此项目 https://github.com/xjh22222228/tomato-work

2024-06-05 · xiejiahe · HTML
小程序生成二维码

找了下目前小程序没有跨端小程序生成二维码的库, 花了点时间整合了一下

小程序生成二维码支持微信、支付宝 等小程序使用。

具体使用移步到仓库:

https://github.com/xjh22222228/qrcode-mini

2024-05-05 · xiejiahe · JavaScript HTML
Fluence空投5000FLT币Github用户福利

Fluence 给大约1W个Github用户投放,领取条件是 Github 用户在去年给 WEB3 项目提交过贡献即可领取,如果不知道是否有领取资格打开链接 https://claim.fluence.network 输入你的Github 用户名检查是否有资格。

如果检查到你有资格你没有SSH密钥也是不行的,Fluence是通过你本地的SSH 和Github上SSH公钥匹配才能领取,如果不放心领取完把Github上的SSH公钥删掉, 我就因为换了电脑没密钥差点丢失了几万块钱,还好电脑给别人用了拿回密钥。

Fluence 是什么? Fluence 是一个新的项目,目前还没上交易所, FLT币。

5000FLT币价值多少?大约2.5万到3.6万RMB,有波动,上个月前还是1U,现在0.8572, 可以在这个网站查询 https://www.bybit.com/en/trade/spot/FLT/USDT

领取后锁仓2个月,2个月后我在更新文章提现成人民币。

领取过程比较复杂,我把领取的过程图片贴上去了:

  1. 检查是否有资格,并且有Github密钥缺一不可
  2. 创建钱包并关联起来
  3. 克隆仓库并运行里面的脚本生成 base64 提交 proofs
  4. 需要在交易所里购买 0.01个ETH币,因为要手续费大约几十块钱,但是ETH最低购买0.01差不多250左右RMB,建议在币安购买,全球最大的交易所 https://www.binance.com
  5. 购买ETH币后转账到钱包里的ETH,然后支付GAS
  6. 复制合约地址在首页找到 ETH 手动添加代币, 5000 就到账了(锁仓2个月)

参考资料

https://blog.fluence.network/the-future-is-cloudless-fluences-depin-computing-platform-and-flt-token-are-now-live/

https://fluence.network/

2024-04-21 · xiejiahe · WEB3
Github用户免费领取空投111.1币

Starknet 供应计划领取111.1个币,$250左右,可换人民币大概2000左右,不要白不要啊。

具体详情看这里 https://www.starknet.io/en/content/starknet-provisions-program

领取资格

1、在 2023 年 11 月 15 日之前,您对属于全球前 5,000 个存储库(按星数排名)之一的存储库至少进行了 3 次提交。其中至少有一项提交发生在 2018 年或之后。

如果您是开源贡献者这完全不是问题。

检查领取资格

1、打开 https://provisions.starknet.io/#widget

2、按照下图查看是否有资格

如果有资格,注册钱包并绑定即可领取。

兑现成CNY

  1. 先注册 币安,点我注册 , 币安是全球最大的交易所。
  2. 将钱包的 STRK 转移到币安
  3. 将 STRK 兑换成 USDT , 这是一种与美元1比1挂钩的货币
  4. 将现货账户划转到资金账户
  5. 将USDT卖出成CNY,几分钟就能完成交易

如果领到币的还不知道怎么卖出的可以联系我。

2024-02-23 · xiejiahe · WEB3
Chrome 一些实用功能

截图

可以对选中的 DOM 节点进行截图:

或者使用快捷键 shift+command+p 然后输入 screenshot 进行全屏截图。

copy

这是浏览器控制台内置的方法,主要用来剪贴内容。 比如说我们要把 localStorage 里的数据都拷贝出来,如果一个一个key/value 考出来就很费劲。 copy 就可以很好的解决这个问题。

$0

在控制台调试时需要对某个元素进行操作时一般会使用 document.querySelector(element) ,这种方式不仅长,还得知道元素选择器。

$0 则可以获取选中的目标元素。

快捷键 H

切换隐藏选中元素,实际上 Chrome 添加了一个类, visibility: hidden !important

$、$$

这2个方法实际上是 document.querySelector / document.querySelectorAll 简写。

重新发起请求

在一些场景定位BUG时想要对某个请求重新发起,如果不知道这个技巧,一般都是刷新页面。

选中需要重新发起请求栈右键:

生成页面二维码

这个功能我用得也挺多的, 主要是移动端调试直接扫一扫即可,而不需复制链接。

$_

获取最后一次执行结果,减少了赋值操作很方便。

2022-01-12 · xiejiahe · 其他
Go - 指针声明

在大部分时候初始化变量我们会这么做:

var n int
// 或者
n := 0

上面的代码实际上是等价的,没有问题, 但是在切片就不同了,看下面例子:

var sliceN1 []int // 结果为 nil

sliceN2 := []int{} // 结果为 []

这TM是一个坑啊,官方是这么解释的:

When storage is allocated for a variable, either through a declaration or a call of new, or when a new value is created, either through a composite literal or a call of make, and no explicit initialization is provided, the variable or value is given a default value. Each element of such a variable or value is set to the zero value for its type: false for booleans, 0 for numeric types, “” for strings, and nil for pointers, functions, interfaces, slices, channels, and maps. This initialization is done recursively, so for instance each element of an array of structs will have its fields zeroed if no value is specified.

官方文档指出切片在没有初始化的情况下切换的零值是 nil , 第一种是未初始化,第二种是初始化为空,所以这2种声明都是不等价的,也就是 nil != slice。 在开发过程中需要注意。

还有一种区别就是在 json 转换时 nil 会被当成 null, 而另外一种是被当初 [] 空数组。 在WEB开发中完全是坑。

var n []string
r, _ := json.Marshal(n)
fmt.Println(string(r)) // "null"


n := []string{}
r, _ := json.Marshal(n)
fmt.Println(string(r)) // "[]"

如果我们使用第二种初始化,就会被 Goland 认为是一种冗余的写法,会让你转换为第一种 var, 这样就又出刚刚说的问题了。

有人说这是 Goland 的 Bug, Goland 团队认为 nil切片在功能上等同于零长度切片,即使它不指向任何内容

解决办法是使用 make

var n []int = make([]int, 0) // 全局
sliceN := make([]int, 0) // 函数内

我的建议就是在声明切片时都使用 make 来初始化,防止入坑。

2022-01-09 · xiejiahe · Golang
go1.16 - go:embed 指令用法

go1.16 出了 go:embed 指令, 了解后发现很强!!!

作用

go:embed 是一个指令,用来嵌入静态文件到二进制文件当中。

我们在部署时会打包成一个二进制文件,如果应用包含一些配置文件比如 config.ini / index.html config.yaml 等一系列静态文件就比较麻烦了,在以往的做法就是将这些静态文件放到二进制同一个目录里头,通过文件系统进行读取, 或者是写在 .go 文件里, 就很不方便。

现在可以通过 go:embed 指令将这些静态文件打包在二进制文件里,也就是只有一个二进制文件。

规则

在使用 go:embed 指令之前先了解一下有哪些限制。

  • 如果 go:embed 指令跟着单个文件,只支持 string / []byte / embed.FS
  • 如果 go:embed 指令跟着多个文件或文件夹,只支持 embed.FS
  • go:embed 指令下一行必须紧跟着嵌入变量
  • 只支持全局变量
  • 只支持 string / []byte / embed.FS, 其他类型包括别名都不支持
  • 嵌入文件路径不支持绝对路径,不支持路径中包含 . / .., 当前路径使用 *

来个典型的例子

2.txt 文件内容:

hello golang
package main

import (
    _ "embed"
    "fmt"
)

//go:embed 2.txt
var s string
func main() {
    fmt.Println(s)
    // hello golang
}

注意 // 注释后面不能有空格

[]byte

字节切片类型:

package main

import (
    _ "embed"
    "fmt"
)

//go:embed 2.txt
var b []byte
func main() {
    fmt.Println(b)
    // [104 101 108 108 111 32 103 111 108 97 110 103]
}

嵌入文件系统

package main

import (
    "embed"
    "fmt"
)

//go:embed 2.txt
var f embed.FS

func main() {
    s, _ := f.ReadFile("2.txt")
    fmt.Println(string(s))
    // hello golang
}

嵌入多个文件

当嵌入多个文件只能使用 embed.FS 类型,不支持其他类型。

package main

import (
    "embed"
    "fmt"
)

//go:embed 1.txt 2.txt
var f embed.FS
func main() {
    s1, _ := f.ReadFile("1.txt")
    s2, _ := f.ReadFile("2.txt")
    fmt.Println(string(s1))
    fmt.Println(string(s2))
}

或者指令分开多行写也可以

package main

import (
    "embed"
    "fmt"
)

//go:embed 1.txt
//go:embed 2.txt
var f embed.FS
func main() {
    s1, _ := f.ReadFile("1.txt")
    s2, _ := f.ReadFile("2.txt")
    fmt.Println(string(s1))
    fmt.Println(string(s2))
}

FS 是只读的

FS 目前提供了3个API:

  • func (f FS) Open(name string) (fs.File, error)
  • func (f FS) ReadDir(name string) ([]fs.DirEntry, error)
  • func (f FS) ReadFile(name string) ([]byte, error)

这3个都是只读的,没有可写的接口, 也就是不存在竞态问题,可以并发读取。

embed.FS 实现了 1.16 io/fs.FS 新的接口:

package main

import (
    "embed"
    "fmt"
    "io/fs"
)

//go:embed 1.txt
var f embed.FS
func main() {
    content, _ := fs.ReadFile(f, "1.txt")
    fmt.Println(string(content))
}

嵌入文件夹

采用 / 斜杠来访问目录下的文件,windows 也一样。

当嵌入的是文件夹时除了 ._ 开头的文件和文件夹都会被嵌入即使在程序中没有使用。

package main

import (
    "embed"
    "fmt"
    "io/fs"
)

//go:embed test
var f embed.FS
func main() {
    content, _ := fs.ReadFile(f, "test/1.txt")
    fmt.Println(string(content))
    // hello golang
}
2021-02-28 · xiejiahe · Golang
快速获取你的电脑WIFI密码

快速获取你的电脑WIFI密码,并在需要时生成WiFi的QR码,以便手机轻松连接。

效果

安装

Mac:

curl -fsSL https://raw.sevencdn.com/xjh22222228/wifi/main/install.sh | bash

Windows:

Download

./wifi.exe

使用

mac

# 获取WIFI密码
$ wifi

# 打印二维码
$ wifi -q

# 生成二维码图片
$ wifi -i

# 获取 SSID
$ wifi -s

windows 一样

./wifi.exe

./wifi.exe -q
2021-01-31 · xiejiahe · Golang
GOSH - Golang实用程序库

GOSH - Golang实用程序库

推荐一个 Golang 的实用程序库,GOSH

提供了常用的字符串操作、随机生成数据操作,数学操作等, 让开发效率增倍。

文档地址

2021-01-16 · xiejiahe · Golang
go mod 基本操作

如果你用的 go版本 >= 1.13 GOMODULE 默认会启用,低于此版本的话升级吧。

初始化

如果项目需要 go module 就必须初始化一次。

go mod init 模块名

如果是开源项目放在 github 上:

# 注意不要带协议
go mod init github.com/xjh22222228/gosh

生成后会在项目下生成 go.mod 文件, 大概长这样

module github.com/xjh22222228/gosh

go 1.15

下载项目依赖

如果开了 go module 下载依赖会跟以往不太一样

go get ./...

更新依赖

更新项目下所有依赖最新版本

go get -u

也可以指定要更新的包

go get -u github.com/xjh22222228/gosh

纠正依赖

删除用不到的依赖包和下载用到的依赖包。

go mod tidy -v

生成vendor

在项目下生成 vendor 目录,也就是依赖包。

go mod vendor

验证go.mod依赖是否正确

$ go mod verify
all modules verified
2020-12-30 · xiejiahe · Golang
Github加速镜像
2020-11-29 · xiejiahe · git/svn
用Go写的一个检查Python依赖包最新版本命令行工具

用Go写的一个检查Python依赖包最新版本命令行工具

全称 python-check-updates 简称 pcu

为什么用 Go 写

主要原因是 Go 非常适合写工具类,因为不需要安装环境即可运行,加上更快,体积也小。

项目开源戳 https://github.com/xjh22222228/python-check-updates

这个项目只是用来玩的,直接用 pip 更好。

PCU

Find the latest version of your requirements.txt current dependency package.

Installation

Deno ships as a single executable with no dependencies. You can install it using the installers below, or download a release binary from the releases page.

Shell (Mac, Linux):

curl -fsSL https://raw.githubusercontent.com/xjh22222228/python-check-updates/main/install.sh | bash

Usage

Show any new dependencies for the project in the current directory:

$ pcu
Checking /opt/requirements_test.txt
13 / 13 [--------------------------] 100.00% 1 p/s
 pytest-cov                                       →   2.10.1
 pytest-mock                           ==2.0.0    →    3.3.1
 httpbin                               ==0.7.0    →    0.7.0
 pytest-httpbin                           <1.0    →    1.0.0
 MySQL-python                          ==1.2.5    →    1.2.5
 aliyun-python-sdk-cdn                 ==2.3.1    →    3.5.8
 XlsxWriter                            ==0.9.3    →    1.3.7
 aliyun-python-sdk-core                ==2.3.2    →  2.13.29
 wheel                                            →   0.35.1
 service-identity                     ==16.0.0    →   18.1.0
 pytest                       >=2.8.0,<=3.10.1    →    6.1.2
 zope.interface                        ==4.3.2    →    5.2.0

Done in  1 s.

Options

-f, --file                   Specify the file name of the check dependency
 package, default 'requirements.txt'
-v, --version                output the version number
-c, --check                  Check the latest version
-h, --help                   display help for command

Example

$ pcu -f=requirements_test.txt

$ pcu -v
2020-11-29 · xiejiahe · Golang
APP-IOS接入微信登录

基于Flutter。

由于笔者不是专业的APP开发,在这方面较弱,在接入微信登录遇到不少坑,基本都是配置问题,在这里记录下过程。


xcode: 12.0.1 mac: 10.15.5

IOS接入微信登录必须具备以下几点(缺一不可):

  • Universal Links (微信用于拉取APP)
  • 一个域名,必须支持Https
  • 微信第三方开放平台

1、新建一个文件名叫 apple-app-site-association 没有任何后缀, 并写入以下配置

{
    "applinks": {
        "apps": [],
        "details": [
            {
                "appID": "1UPF2ET6AP.com.example.app.scp",
                "paths": [ "/help/*" ]
            }
        ]
    }
}

需要修改 appIDpaths

  • appID由 teamId.Bundle ID组成, teamId 在APPLE开发者中心可以找到
  • paths (help是域名下的目录可以任意修改,后面必须带通配符,因为微信会随机在后面带字符串)

2、将文件放在域名根目录下。

打开你的域名比如 https://www.example.com/apple-app-site-association 如果文件被下载说明这步没问题。

3、使用 safari 浏览器打开 https://www.example.com/help/123 前面修改为你的域名,help 是前面在 paths 指定的, 123是乱写的随机字符串,打开后如果出现类似下面说明正常:

4、打开微信开放平台,找到IOS配置,下面必须全部都要配置

5、打开APPLE开发者中心,找到 Associated Domains 这个选项将它开启。

XCODE配置

1、使用 XCODE 打开 Runner.xcodeproj 文件。

如图配置 Domains, applinks 前缀是固定的,后面就前面配置的 Universal Links

2、配置 LSApplicationQueriesSchemes。

这个干嘛用的呢,主要是添加微信的 scheme 到白名单上去,这一步少不了,如果不添加没有办法知道微信是否已安装(尽管已安装了)。

找到 info.plist 文件, 将下面这一块内容添加到 dict 节点的末位,如下:

<key>LSApplicationQueriesSchemes</key>
<array>
  <string>wechat</string>
  <string>weixin</string>
  <string>weixinULAPI</string>
</array>

如果不知道这个文件在哪里,直接在当前工程下搜索这个文件, 一定有。

总结

接入这微信登录费了不少心思,少一步就要找各种问题进行排除,太蛋疼,还好终于搞定了,祝后面的小老弟们一切顺利哈。

参考

2020-11-16 · xiejiahe · Flutter
关于Docker无法在宿主执行容器命令

很多情况需要在 宿主 运行容器内的命令, 比如需要在宿主运行容器 nginx / redis / 等命令

有2种方法可以执行容器命令:

1、用内联重定向 EOF

docker exec -it c6261194df8e /bin/bash << EOF
touch /home/inner.txt
EOF

2、docker 参数-c 可以执行容器shell

docker exec -it c6261194df8e /bin/bash -c "touch /home/inner.txt"

用上面2种方法会发现虽然执行了,但是并没有在容器内创建文件,不信可以试试。

目前我还没知道什么问题。

执行Shell脚本解决

既然无法在宿主内执行容器内的Shell,那是不是可以执行容器Shell脚本?

写 Shell 脚本得有编辑器,容器默认不带 vim,所以得先安装:

apt-get update && apt-get install vim

Shell 脚本, 这里是Demo,实际参照自己需要执行的Shell。

ci.sh 内容如下:

#!/bin/bash

echo "hello" >> /home/inner.txt

来试试

docker exec -it c6261194df8e /bin/bash -c "/bin/bash /opt/ci.sh"

然后进入容器会发现 /home 目录下确实有 inner.txt 文件,说明这思路是可以的。

对了,执行容器内的外建命令记得用绝对路径执行,例如:

# 必须
/bin/nginx -s reload

# 这样不行的,因为有些外建命令没有加入环境变量
nginx -s reload
2020-11-14 · xiejiahe · 其他
Golang中的标准库Log

在我们编写程序时是离不开日志的,在Go标准库中提供了一个 log 模块。

比较常用的方法有下面这几个:

ln 结尾的只是多了个换行符 \n, f 结尾的是格式化文本。

  • Panicln / Panic / Panicf - 抛出异常并终止程序
  • Println / Print / Printf - 打印信息
  • Fatalln / Fatal / Fatalf - 打印信息并退出程序

很多新手不知道这些方法和 fmt 包中的方法有什么区别。

实际上上面那3个都只是 fmt 包中的语法糖

Panic

抛出异常信息并终止程序。

示例代码:

package main

import (
	"log"
)

func main() {
	log.Panic("error")
}

底层实现是这样子的:

可以看到这其实只是一个语法糖而已

func Panic(v ...interface{}) {
	s := fmt.Sprint(v...)
	std.Output(2, s)
	panic(s)
}

如果说能不能用全局方法 panic 呢? 不行, 因为panic 只接受一个参数:

panic("error")

所以为什么会有 log.Panic 这个方法了吧。

Fatal和Panic区别

FatalPanic 都是用来打印信息然后终止程序,不知道何时用哪个?

  • Panic: 抛出异常终止程序,一般来说在调试时想获得更多错误信息那就使用 Panic
  • Fatal: 这其实只是打印信息后正常退出程序不会提供更多额外信息给你

所以在程序中使用 Fatal 是没什么意义的,所以不推荐使用。

创建日志文件

创建日志文件的好处在于可以持久化,在出现问题的时候可以快速定位。

创建日志文件需要借助 os 模块和 log 模块。

示例代码,以注释说明:

package main

import (
	"log"
	"os"
)

func main() {
  // 打开一个文件
  // 第一个参数是写入日志文件的位置
  // 第二个参数是需要以什么权限进行操作,多个mode用 | 符号分隔代表多个
  // 最后参数是权限的八进制表示法
	file, err := os.OpenFile("./log.txt", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0)

	if err != nil {
		log.Panicln("文件打开错误:", err)
	}

	defer file.Close()

  // 用 New 方法实例化
  // 第一个参数是 io.Writer 接口,只要实现了该接口都支持,文件就是
  // 第二个参数是 前缀信息 一般用于区别日志是属哪个分类
  // 最后一个参数是 标志, 也就是系统额外附加信息 LstdFlags 包含了日志和时间
	ilog := log.New(file, "Error:", log.LstdFlags)
	ilog.Println("输出错误信息")
}

标志有以下(都是以 L 开头):

参数 描述
Ldate 本地时区日期 2020/11/11
Ltime 本地时区时间 01:23:23
Lmicroseconds 微妙 01:23:23.123123
Llongfile 打印完整文件路径和行号
Lshortfile 打印文件名和行号
LUTC 如果设置了日期或时间,请使用UTC而不是本地时区
LstdFlags Ldate和Ltime的结合,日期和时间一起打印
Lmsgprefix 将Flag信息移至消息前面

打印行号

golang打印日志行号有时非常有用,因为默认 fmt 是不打印行号的,给调试带来不便。

有了上面的Flag信息就好实现了, 借助 Llongfile 即可。

package main

import (
	"log"
	"os"
)

func main() {
	ilog := log.New(os.Stdout, "Info:", log.Llongfile)
  ilog.Println("Hello")
  // 输出:Info:/Users/main.go:10: Hello
}

强大的第三方日志模块

目前比较流行的是这两个日志系统,可以去了解下

2020-11-07 · xiejiahe · Golang
Goland Unresolved dependency

每次新建项目都会遇到这个提示,明明安装依赖包了, 能用,就是爆红。

解决办法是在 Goland 设置里将 Go Modules 开启即可。

2020-09-23 · xiejiahe · Golang
《JavaScript 20 年》中文在线版发布
2020-09-23 · xiejiahe · JavaScript
今天 Vue 3.0.0 正式发布!

相关链接

等Vue3很长时间了,一把梭。

Vue3 提供了2种开发模式,Composition API 和 Vue2的导出模式, 以供Vue2.0快速切入。

VueRouter 和 Vuex 没有太大的变化,详细参考文档。

2020-09-19 · xiejiahe · Vue.js
Redis 常用命令集合

在学习Redis时整理的一些命令,方便日后快速查找。

Redis 命令不区分大小写, 即 SET name xjh 等价于 set name xjh

目录

字符串

设置一个值

命令: SET key value

SET name xiejiahe

设置多个值

命令: MSET key value key value ...

# 返回的是一个数组
MSET name xiejiahe age 18

获取一个key值

命令:GET key

GET name

获取多个key值

命令:GET key key...

GET name age

获取字符串长度

命令:STRLEN key

# 返回 3
STRLEN name

追加字符串

命令:APPEND key value,如果key不存在则创建,等价于 SET key value

# xjh + _hi = xjh_hi
APPEND name _hi

列表

列表类似于编程当中的数组。

向头部添加元素

向左(头部)添加一个或多个元素,返回列表长度, 命令: LPUSH key value value...

LPUSH names xjh 1 2 3

向尾部添加元素

向右(尾部)添加一个或多个元素,返回列表长度, 命令: RPUSH key value value...

RPUSH names xjh 1 2 3

提取指定范围元素

提取列表指定范围元素,支持负数,-1最后一个,-2倒数第二个, 命令: LRANGE key index index

# 提取所有元素
LRANGE names 0 -1

向左弹出一个元素

向左弹出一个元素, 返回弹出的元素,如果没有元素返回 null, 命令: LPOP key

LPOP names

向右弹出一个元素

向右弹出一个元素,返回弹出的元素,如果没有元素返回 null, 命令: RPOP key

RPOP names

上限列表

上限列表,只保留前面指定N个元素,其他元素移除掉, 有点类似于JS splice, 命令: LTRIM key start end

LTRIM names 0 2

阻塞操作

LPOP 有点类似,等待 Key 列表元素,如果超时了仍然没有元素可弹出返回nil, 命令: BLPOP key seconds

接收2个参数,第一个是 Key, 第二个是超时单位秒,如果为0表示不超时,永久阻塞直到有新元素。

# 左边
BLPOP names 5
BLPOP name age 5 # 支持多个key

# 右边
BRPOP names 5

获取列表元素个数

获取列表元素个数, 如果不存在返回 0, 命令: LLEN key

LLEN names

删除数组指定元素

删除数组指定元素, 如果是正数从左边开始删N个元素的值,如果是负数则从右边删N个元素的值,如果是0删除所有指定元素, 命令: LREM key index value

LREM names 2 zhangsan

获取指定索引元素值

获取指定索引元素值, 索引从0开始, 命令: LINDEX key index

# xiejiahe
LINDEX names 0

设置指定索引元素值

设置指定索引元素值, 索引从0开始,如果索引不存在则报错, 命令: LSET key index value

LSET names 1 xiejiahe

Hash

Redis 哈希/散列相当于编程语言当中的 Map

设置一个字段

设置一个字段, 如果字段存在则覆盖,命令:HSET key 键 值

HSET user:001 username xiejiahe

设置多个字段

设置多个字段, 如果字段存在则覆盖,命令:HMSET key 键 值 键 值...

HMSET user:001 username xiejiahe age 18

读取字段的值

读取字段的值, 命令:HGET key 键

HGET user:001 username

读取多个字段的值

读取多个字段的值,命令:HMGET key 键 键...

HMGET user:001 username age

获取所有字段键和值

获取所有字段键和值, 返回的是列表 [键, 值, 键, 值], 单数是键,偶数是值。命令:HGETALL key

# name
# 谢家和
# age
# 18
HGETALL user:001

获取字段数量

获取字段数量, 命令:HLEN key

# 10
HLEN user:001

删除字段键

删除字段一个或多个键, 命令:HDEL key 键...

# 删除 name 和 age
HDEL user:001 name age

获取所有字段键名

获取所有字段键名, 返回列表, 命令:HKEYS key

# names
# age
HKEYS user:001

获取所有字段值

获取所有字段值, 返回列表, 命令:HVALS key

# xiejiahe
# 18
HVALS user:001

自增数量

自增是原子的,如果字段不存在则创建为0然后增加指定数值, 命令:HINCRBY key 键 number

# 首次创建 like 字段不存在则为 0 + 2 = 2
HINCRBY user:001 like 2

检查字段是否存在

检查字段是否存在, 存在返回 1, 否则返回 0, 命令:HEXISTS key 键

# 返回 1
HEXISTS user:001 name

字段不存在创建

字段不存在创建, 如果字段存在则跳过, 命令:HSETNX key 键 值

# 跟 HSET 命令一致,区别于如果字段存在了则不创建
HSETNX user:001 name 谢家和

集合

Redis 集合是无序的字符串集合且唯一, 每次调用时随意顺序返回元素。

添加元素

添加一个或多个元素,命令: SADD key value value...

SADD users 1 2 3
SADD users 1 2 3 5 # 因为是唯一的,所以只有5会被添加进去

删除元素

删除一个或多个元素,命令: SREM key value value...

SREM users 1 2 3...

获取集合所有元素

获取集合所有元素, 命令: SMEMBERS key

# 1
# 2
# 3
# 5
SMEMBERS users

检查指定元素是否存在

检查指定元素是否存在, 返回 1 成功,0 失败, 命令: SISMEMBER key value

# 返回 1
SISMEMBER users xiejiahe

获取集合元素个数

获取集合元素个数, 命令: SCARD key

# 返回 10
SCARD names

随机获得集合中的元素

随机获得集合中的元素, 命令: SRANDMEMBER key [number]

# 不指定数量,随机获取一个元素
SRANDMEMBER names

# 指定随机获取 10 个元素
SRANDMEMBER names 10

从集合中弹出一个元素

从集合中弹出一个元素, 因为集合是无序的,所以会随机弹出一个元素, 命令: SPOP key

# 随机弹出一个元素
SPOP names

有序集合

有序集合类型为集合中的每个元素都关联了一个分数(score)。

添加元素

添加1个或多个元素, value 在集合不能重复,否则被覆盖,score 可以正负数也可以浮点数, 命令: ZADD key score value...

# 添加多个元素
ZADD score 99 xjh 88 zhangsan

# 添加一个元素, score 为浮点数
ZADD score 100.1 xjh

根据value修改score

根据 value 修改 score, 命令: ZADD key score value

# 将 xjh 分数修改为 100
ZADD score 100 xjh

根据value获得score值

根据 value 获得元素的 score 值, 命令: ZSCORE key value

# 返回 100
ZSCORE score xjh

通过索引获取元素范围

通过索引获取元素范围, 类似 LRANGE 命令,支持负数, 命令: ZRANGE key start end <WITHSCORES>

# xjh
# zhangsan
ZRANGE score 0 -1

# xjh
# 100
# zhangsan
# 99
ZRANGE score 0 -1 WITHSCORES # 可选参数 WITHSCORES 返回包含 value 和 score

根据分数score范围获取元素

根据分数 score 范围获取元素, 可选标识符 (, 命令: ZRANGEBYSCORE key index index <WITHSCORES>

# xjh
# zhangsan
# lisi
ZRANGEBYSCORE score 60 100 # 返回 60 - 100 元素

# 可以在 数值 前面加 "(" 表示不包含此分数
ZRANGEBYSCORE score 60 (100 # 取60-100,但不包含100
ZRANGEBYSCORE score (60 100 # 取60-100,但不包含60

# 支持正负无穷数, +inf / -inf
ZRANGEBYSCORE score 60 +inf # 取60含60以上的分数

# xjh
# 100
# zhangsan
# 99
ZRANGEBYSCORE score 60 +inf WITHSCORES # 额外参数 WITHSCORES, 同时返回 value 和 score

获取分数范围并限制条数

ZREVRANGEBYSCORE 有点类似 ZRANGE 命令,但是支持 LIMIT 用于限制条数。命令:ZREVRANGEBYSCORE key max min LIMIT index index

注意:结果按照从大到小进行排序。

# xiejiahe
# zhangsan
# wangwu
ZREVRANGEBYSCORE score 100 0 LIMIT 0 3

增加或减少元素分数

如果value存在则在现有基础上增加,否则添加一个新元素并设置相应的数值,命令:ZINCRBY key number value

number 支持负数,所以可以用来减少元素的分数。

ZINCRBY score 98 xiejiahe

# 支持负数
ZINCRBY score -98 xiejiahe

获得集合元素数量

命令:ZCARD key

# 5
ZCARD score

获得集合指定分数范围元素数量

命令:ZCOUNT key min max

# 在 0 - 60 分数中元素数量
ZCOUNT score 0 60

删除元素

删除一个或多个元素,命令:ZREM key value value...

# 删除一个
ZREM score xiejiahe
# 删除多个
ZREM score xiejiahe wangwu

按照排名范围删除元素

ZREMRANGEBYRANK 按照元素分数从小到大的顺序进行删除。 命:ZREMRANGEBYRANK key start stop

# 3 (返回删除元素数量)
ZREMRANGEBYRANK score 0 3

按照分数范围删除元素

ZREMRANGEBYSCORE 按照分数范围删除所有元素,返回删除的元素数量。命令:ZREMRANGEBYSCORE key min max

# 删除0-60的所有元素
ZREMRANGEBYSCORE score 0 60

获取元素的排名

ZRANK 命令按照元素分数从小到大进行排序获得指定元素的排名,从0开始,命令:ZRANK key value

# 返回1, xiejiahe 排在第二
ZRANK score xiejiahe

计数器

Redis 字符串可以当做计数器使用, 内部会自动做类型转换, 常用于点赞、访问量等场景。

自增

自增,如果 key 不存在则默认为 0, 命令:INCR key

INCR number

自增指定数值

自增指定数值, 命令:INCRBY key number

number 支持负数,所以可以替代 DECRBY 命令。

INCRBY number 10

递减

递减,如果 key 不存在则默认为 -1, 命令:DECR key

DECR number

递减指定数值

递减指定数值, 命令:DECRBY key number

DECRBY number 10

自增或递减指定浮点数

自增或递减指定浮点数, 命令:INCRBYFLOAT key number

INCRBYFLOAT number 2.2
INCRBYFLOAT number -2.2

事务

执行事务

2个关键字: MULTI 开始事务, EXEC 执行事务。

注意:Redis不像关系型数据库那样可以回滚 rollback,一旦失败只能交给开发者擦屁股。

事务中的命令是在 EXEC 之后才执行的。

一个简单例子:

# 开始事务
> MULTI

# 中间是执行命令
> INCR num

> GET num

# 执行事务并返回所有结果集合
> EXEC

WATCH

监控一个或多个键,一旦其中有一个键被修改或删除,之后的事务就不会执行,一直持续到 EXEC 命令。

因为事务是在 EXEC 命令之后才执行的,假如在事务内执行原子自增,当其他客户也在执行原子自增就会出现竞态。

可以把 WATCH 理解为锁,锁住键防止其他客户端修改或删除。

# OK, 先设置一个值
> SET name xiejiahe

# OK, 监控 name
> WATCH name

# OK, 这里是能执行的,因为监控的是事务内的执行
> SET name facai

# 开启事务
> MULTI

# 修改 name, 如果其他客户端修改 name 那么这里执行失败,否则成功
SET name wangwu

# 执行事务
> EXEC

WATCH 命令还有一个对应的 UNWATCH 用于取消监控:

# 监控一个键
> WATCH num

# ...其他操作


# 取消监控,为了不影响下一个事务
UNWATCH num

# ...

排序

SORT

SORT 命令可以对 列表类型、集合类型、有序集合类型进行排序。

注意:只能对数字元素进行排序,如果是非数字会出错。

# 创建一个列表
LPUSH score 99 5 3 -1 -55 9 100 88

# 排序
SORT score
# 1)  "-55"
# 2)  "-1"
# 3)  "3"
# 4)  "5"
# 5)  "9"
# 6)  "88"
# 7)  "99"
# 8)  "100"

倒序

默认情况下 SORT 是升序排序的从小到大。大多数情况下用倒序比较多,通过 DESC 参数可以实现倒序:

SORT score DESC

字典排序

有时候并非所有元素都是数字,比如是字母或其他情况。通过 ALPHA 参数实现按照字典顺序排列。

# 设置一个列表为字母元素
LPUSH letter b d z a o p

# 排序
SORT letter ALPHA
# 1)  "a"
# 2)  "b"
# 3)  "d"
# 4)  "o"
# 5)  "p"
# 6)  "z"

限制条数

SORT 命令还支持 LIMIT 参数,和 SQL 语句一样,可以用于分页。命令: SORT key LIMIT offset count

SORT score DESC LIMIT 1 5

发布订阅

发布

发布的命令是 PUBLISH, 完整命令使用: PUBLISH channel message

channel 是信道或频道或者理解为是房间,往某个房间发送数据。

执行后返回返回收到这条消息的订阅者数量,如果没有客户端订阅就返回 0;

发布不是持久化的,当发布后接着客户端订阅当前信道接收不到之前的消息。

# 往 room:0 发送 Hello
PUBLISH room:01 Hello

订阅

有发布肯定有订阅, 订阅命令是 SUBSCRIBE, 完整命令使用:SUBSCRIBE channel ...

当执行 SUBSCRIBE 命令后将进入订阅状态。

# 订阅 room:01
SUBSCRIBE room:01

# 进入订阅状态
Switch to Pub/Sub mode. Close console tab to stop listen for messages.
 1)  "subscribe"    # 订阅成功反馈信息
 2)  "room:01"
 3)  "1"
 1)  "message"      # 收到的消息
 2)  "room:01"
 3)  "hello"

取消订阅

UNSUBSCRIBE 命令可以取消订阅信道, 如果不指定信号将取消所有订阅信道。

注意:如果是在 redis-cli ,无法执行 UNSUBSCRIBE 因为执行订阅后当前实例会被阻塞, 无法执行其他命令。

# 取消指定订阅信道
UNSUBSCRIBE room:01

# 取消全部订阅
UNSUBSCRIBE

检查、类型

检查key是否存在

检查 key 是否存在,存在返回 1,否则 0, 命令:EXISTS key

EXISTS names

获取key类型

获取key类型, 命令:TYPE key

# "string"
TYPE names

过期时间

可以让一个键在某个时间段删除。 通常会用在获取验证码频繁,登录次数过多等场景。

目前Redis过期时间误差值在 0 ~ 1 毫秒之间。

给指定Key设置过期时间

给指定 key 设置过期时间,单位秒(整数), 命令: EXPIRE key seconds

# 方法一:在已存在的key设置过期时间
EXPIRE names 10

# 或者在设置值时指定过期时间
SET name xiejiahe EX 10

检查Key的生存剩余时间

检查Key的生存剩余时间(秒), 命令: TTL key

TTL name

删除过期时间

删除过期时间,使 Key 永久存在, 命令: PERSIST key

PERSIST name

除了 PERSIST, 还可以使用 SET 命令赋值也会删除过期时间

# 设置过期时间为10秒
EXPIRE key 10

# 重新赋值会删除过期时间
SET key 1

删除

删除Key

删除一个或多个任何类型的 key, 命令: DEL key...

DEL name
DEL name age # 支持删除多个

清空所有数据库中的数据

清空所有数据库中的数据, 相当于还原Redis初始状态, 命令: FLUSHALL

# 0-16 数据库数据全部清空
FLUSHALL

清空当前数据库所有数据

# 当前数据库中的所有数据将被清除
FLUSHDB

管理

配置文件设置

配置文件位于 redis 的安装目录下的 redis.conf 文件。 除了通过配置文件设置以外还支持命令进行配置查看。

设置命令 CONFIG SET key value

需要注意不是所有配置都可以用命令设置,有一些配置就必须在 redis.conf 文件中设置,比如 rename-command

# 这里增加一个配置name 为 xjh
CONFIG SET name xjh

查看配置 CONFIG GET key

CONFIG GET name

设置密码

redis 数据库只有密码可以设置,没有用户名一说,redis的设计初衷就是简洁为美。

设计密码很简单,就是通过设置配置文件字段 requirepass, 命令: CONFIG SET requirepass 密码

密码一定要设置得复杂一点,因为很容易被穷举。

CONFIG SET requirepass ddj1ja@9)..

当数据库设置密码后每次连接 redis 都要进行授权:

# AUTH 后面跟着密码
AUTH ddj1ja@9)..

命令重命名

有些命令比较长很难记,这时候可以给命令进行重命名。需要在 redis.conf 文件中进行设置。

# 将 SET 命令重命名为 s
rename-command SET s

禁用命令

有一些大厂就比较规范,会将一些比较耗性能或危险的命令给禁用掉。 需要在 redis.conf 文件中进行设置。

# 禁掉 FLUSHALL 命令
rename-command FLUSHALL ""

其他

获取所有key名字

获取所有key名字, 支持正则, 此命令不建议生产环境使用,因为会遍历所有键,当键较多时会影响性能, 命令: KEYS key

# 正则通配符、正则
KEYS *
KEYS n*

获取当前数据库Key数量

获取当前数据库Key数量, 命令: DBSIZE

DBSIZE

修改键名

RENAME 命令可以将存在的键名修改为新的键名。

SET inc 0

# 修改键名
RENAME inc increment
2020-09-14 · xiejiahe · SQL
Golang 交叉编译与应用部署

交叉编译

在mac平台上编译只能在mac平台运行, 如果想在mac上编译windows上运行就需要交叉编译

编译

通常编译只需要运行 go build main.go 即可,如果需要交叉编译就用到 GOOS /GOARCH 2个环境变量。

Go 1.5+ 版本就已经内置支持了,所以不需要考虑版本问题。

下面编译成 windows 可执行文件 main.exe

GOOS=windows GOARCH=amd64 go build main.go

GOOS 是目标平台, GOARCH 则是目标平台的体系架构(通常是 amd64)。

GOOS和GOARCH参考列表

GOOS GOARCH
darwin 386
darwin amd64
darwin arm
darwin arm64
dragonfly amd64
freebsd 386
freebsd amd64
freebsd arm
linux 386
linux amd64
linux arm
linux arm64
linux ppc64
linux ppc64le
netbsd 386
netbsd amd64
netbsd arm
openbsd 386
openbsd amd64
openbsd arm
plan9 386
plan9 amd64
solaris amd64
windows 386
windows amd64

除了上面通过命令行实现以外,还可以通过修改内置环境变量,但不建议,可以敲 go env 即可找到相关信息。

部署

golang 没有提供类似保守进程的工具,只能借助第三方,如 nohup / Supervisord / upstart / daemontools 等工具来实现。

最简单的方式就是使用 nohup 了, 唯一不好的就是很难监控应用状态。

nohup ./main &

这里推荐使用 node.js pm2

# 这样就启动起来了
pm2 start ./main

Go 亮点之一就是编译快,通常5秒就能编译一个程序出来,糟糕情况下也就1分钟左右。

2020-09-07 · xiejiahe · Golang