#753 Golang 观察

2022-01-18

InfoQ 的公众号文章《解读Go语言的2021:稳定为王》中引用 TIOBE Index 和 Google Trends 的数据来分析 Go 语言的发展趋势。
非常有意思,我找他的思路,直接去源头研究了一番。

Go 在 2009 年和 2016 年得了 TIOBE 年度语言。
PS: Go 是 2009 年 11 月发布。TIOBE 也是够捧场了,刚一出来就得了年度语言。

根据 TIOBE 的数据,Go 在 2016 年下半年大火了一波,随后热度消退,从 2018 年到现在一直比较稳定。

然后 Google Trends 的数据(2009/10/01 至今,全球)显示:

  • Go 在 2009 年诞生之后就慢慢攀升
  • 2012 年达到一个阶段
  • 2013 年 10 月又上一个台阶
  • 2014 年 5 月达到巅峰
  • 然后一直持续下跌到 2020 年底
  • 2021 年稍有复苏的迹象

最后再上这张图,看看 Go 在各个地区的火爆程度:

那篇文章,包括我之前在网上看到的其他文章,都在这个地方得出了一个结论:Go 在国内的热度远超国外,甚至有地方说 Go 语言的热度全靠国内开发者支撑。
我认为这是非常不严谨的。

其实 Google Trends 的数据是根据这个地方搜索相关词条的比例来计算的。而 Google 的中国用户已经被过滤过一道,应该是以开发者为主。所以这个热度数据的对比是不能说明什么问题的。而且由于一些上网方式的问题,国内的这部分统计数据应该是非常不可靠的。

#751 Linux 小工具汇总

2022-01-16

常规的系统自带工具就不提了。

  • htop 代替 top
  • jq JSON
  • bat 代替 cat
  • dog DNS 查询
  • tldr 替代 man 文档
  • ack ag 代替 grep
  • fzf
  • fuck
  • mtr 代替 traceroute
  • httppie 代替 wget 和 curl
  • ncdu 代替 du
  • duf 代替 df
  • pydf 代替 df
  • fd 代替 find
  • eva 代替 ls
  • neovim 代替 vim
  • lftp 代替 ftp
  • aria2
  • nnn

参考资料与拓展阅读

#750 一个编码转换问题

2022-01-15

问题

我拿到一串字符串:

"Client: \\344\\275\\240\\345\\245\\275\\357\\274\\214\\344\\270\\226\\347\\225\\214"

根据上下文,我知道这是 UTF-8。

print(b'Client: \344\275\240\345\245\275\357\274\214\344\270\226\347\225\214'.decode())
# 'Client: 你好,世界'

现在的问题是,我怎么把他转换成普通的字符串呢(unicode)?

再举例子

这其实是一个比较普遍的问题,只要有过一段时间 Python 开发,可能就会偶尔碰到这样的问题:

a = "I Love 中国".encode('utf-8')
b = str(a)
# "b'I Love \\xe4\\xb8\\xad\\xe5\\x9b\\xbd'"

由于不同系统或者不同模块之间的衔接有问题(有可能部分组件只支持 ASCII),中间的过程不受我们控制,最后,我们就是拿到了这个 b,怎么给转换成 a?

解决方案

s = "Client: \\344\\275\\240\\345\\245\\275\\357\\274\\214\\344\\270\\226\\347\\225\\214"
result = eval(f'b"{s}".decode("utf-8")')
print(result)
# Client: 你好,世界

但是这有点危险,改成 str.literal_eval 好一些:

s = "Client: \\344\\275\\240\\345\\245\\275\\357\\274\\214\\344\\270\\226\\347\\225\\214"
#  b'Client: \xe4\xbd\xa0\xe5\xa5\xbd\xef\xbc\x8c\xe4\xb8\x96\xe7\x95\x8c'
import ast
print(ast.literal_eval(f'b"{s}"').decode('utf-8'))

延伸

unicode_escape

In [1]: u'nihao 中国'.encode('raw_unicode_escape')
Out[1]: 'nihao \\u4e2d\\u56fd'

In [2]: u'nihao 中国'.encode('unicode_escape')
Out[2]: 'nihao \\u4e2d\\u56fd'

In [3]: re.sub('.', lambda x: r'\u%04X' % ord(x.group()), 'nihao 中国')
Out[3]: '\\u006E\\u0069\\u0068\\u0061\\u006F\\u0020\\u4E2D\\u56FD'

In [4]: ''.join(r'\u{:04X}'.format(ord(chr)) for chr in 'nihao 中国')
Out[4]: '\\u006E\\u0069\\u0068\\u0061\\u006F\\u0020\\u4E2D\\u56FD'

八进制和十六进制

s = "Client: \\344\\275\\240\\345\\245\\275\\357\\274\\214\\344\\270\\226\\347\\225\\214"
#  b'Client: \xe4\xbd\xa0\xe5\xa5\xbd\xef\xbc\x8c\xe4\xb8\x96\xe7\x95\x8c'
0o344 == 0xe4 == 228

#748 Poetry

2022-01-12

我研究了半天之后感觉无法掌握 poetry,决定放弃,还是用我的 Pipenv。

img

Poetry 是一个 Python 虚拟环境管理器。

#746 RedisJSON 体验

2022-01-09

相关阅读:Redis 拓展模块,其中用 RedisJSON 举例编译安装。

简单示例 (GET / SET):

127.0.0.1:6379> json.set abc . 123
OK
127.0.0.1:6379> json.get abc .
"123"
127.0.0.1:6379> json.set xyz . '{"a": 1, "b": true, "c": [{"name": "foo"}, {"name": "bar"}]}'
OK
json.get xyz .c[0]
"{\"name\":\"foo\"}"
127.0.0.1:6379> json.set xyz .a 2
OK
127.0.0.1:6379> json.get xyz .c[0].name
"\"foo\""
127.0.0.1:6379> json.set xyz .c[0].name '"loong"'
OK
127.0.0.1:6379> json.get xyz .a .c[0].name
"{\".c[0].name\":\"loong\",\".a\":2}"

Json 路径语法

现在还没有相关行业标准,RedisJSON 自己实现了一套(v1), 后来参考 JSONPath 语法又重新实现了一遍 (v2)。
至于 v1 和 v2 有什么区分,就不用深究了。

大致知道这样可以就行了:

.store.book[0].title
// 相当于 `V['store']['book'][0]['title']`

拓展命令

  1. JSON.ARRAPPEND arrappend
  2. JSON.ARRINDEX arrindex
  3. JSON.ARRINSERT arrinsert
  4. JSON.ARRLEN arrlen
  5. JSON.ARRPOP arrpop
  6. JSON.ARRTRIM arrtrim
  7. JSON.CLEAR clear
  8. JSON.DEBUG debug
  9. JSON.DEL delete
  10. JSON.GET get / jsonget
  11. JSON.MGET mget / jsonmget
  12. JSON.NUMINCRBY numincrby
  13. JSON.NUMMULTBY nummultby img
  14. JSON.OBJKEYS objkeys
  15. JSON.OBJLEN objlen
  16. JSON.RESP resp
  17. JSON.SET set / jsonset
  18. JSON.STRAPPEND strappend
  19. JSON.STRLEN strlen
  20. JSON.TOGGLE toggle
  21. JSON.TYPE type

Python 库还有以下两个方法:

  1. set_file(name, path, file_name, nx=False, xx=False, decode_keys=False)
    return self.set(name, path, file_content, nx=nx, xx=xx, decode_keys=decode_keys)
    
    1. set_path(json_path, root_folder, nx=False, xx=False, decode_keys=False)
    遍历 root_folder 得到 file_path, file_name (file_path.split('.', 1)[0])
    然后调用 set_file(file_name, json_path, file_path, nx=nx, xx=xx, decode_keys=decode_keys)

使用(Python)

Python 最知名的 Redis 客户端库 redis-py 已经支持 RedisJSON 拓展指令。

import redis
from redis.commands.json import JSON

r = redis.Redis(host='localhost', port=6379, db=0)
jr = JSON(r)

jr.set('foo', '.', {"a": 1, "b": True, "c": [{"name": "foo"}, {"name": "bar"}]})

jr.numincrby('foo', 'a', 1)

jr.toggle('foo', '.b') # only works for booleans

jr.arrappend('foo', '.c', {"name": "air"})

print(jr.get('foo', '.'))
# {'a': 2, 'b': False, 'c': [{'name': 'foo'}, {'name': 'bar'}, {'name': 'air'}]}

print(jr.get('foo', '.c[-1].name'))
# air

就缺搜索了,如果加上 RediSearch,就完美了。
PS: RediSearch 的新版本已经优化了对 JSON 的支持。

参考资料与拓展阅读

#745 Redis 拓展模块

2022-01-09

Redis 官方其实还提供了以下拓展:

PS: 还有更多第三方拓展,参考 https://redis.io/modules

关于授权协议

Redis 是 BSD 协议,Redis 拓展模块是 AGPL 协议。
2018 年 8 月,为了防止云服务厂商的利益侵害,Redis 拓展模块授权协议切换到 Apache v2.0 modified with Commons Clause (官方声明)。其中 Common Clause 就是限制提供商业的 Reids 服务。
2019 年 2 月,Redis 拓展模块授权协议再次切换,改成 Redis Source Available License (RSAL),(官方声明)。因为认为当前协议不够明确,容易让人产生误解,而且对 Redis 公司的权益保障不到位。

这个协议对普通用户(个人或企业)没有限制,只是限制了直接利用 Redis 服务赚钱的云厂商。我觉得这是开发者的合理诉求,没有什么值得质疑的。

模块的安装

img

如果只是要体验一下,不想编译,可以去 https://redis.com/redis-enterprise-software/download-center/modules/ 下载试用。

  1. 下载模块源代码,然后编译,生成动态链接库 xxx.so
  2. 修改 Redis 配置文件 loadmodule xxx.so,然后重启 Redis 服务
    PS: 也可以在启动时使用 --loadmodule 参数动态加载模块。

比如 ReJSON:

wget https://github.com/RedisJSON/RedisJSON/archive/refs/tags/v2.0.6.zip -O redisjson-v2.0.6.zip
unzip redisjson-v2.0.6.zip
cd RedisJSON-2.0.6/

# 安装需要用到官网没有提到的 libclang.so
# From: bindgen-0.59.2/src/lib.rs
sudo apt install libclang-dev

cargo build --release
cargo test --features test
# 需要先安装 Rust (请参考其他资料)
# 我了解到的其他模块大多数是 C 写的,RedisJSON 比较特殊,用 Rust 写的

redis-server --loadmodule target/release/librejson.so

相关文章:RedisJSON 体验