玩转KCP(1)-快速开始

版权声明 本站原创文章 由 萌叔 发表 转载请注明 萌叔 | http://vearne.cc 1. 前言 KCP协议是一种快速可靠传输ARQ(Automatic Repeat-reQuest)协议,能以比TCP浪费10%-20%的带宽的代价,换取平均延迟降低 30%-40%,且最大延迟降低三倍的传输效果。它跟QUIC协议一样也是基于UDP协议的实现。KCP从TCP协议中借鉴了大量的思路,是理解TCP/IP协议栈的非常好的资料。 补充说明下,无论是QUIC还是KCP只有在弱网络条件下,相比TCP才有优势。 KCP协议在网络分层模型的位置 +-----------------+ | SESSION | +-----------------+ | KCP(ARQ) | +-----------------+ | FEC(OPTIONAL) | +-----------------+ | CRYPTO(OPTIONAL)| +-----------------+ | UDP(PACKET) | +-----------------+ | IP | +-----------------+ | LINK | +-----------------+ | PHY | +-----------------+ KCP的设计者有意识的把KCP依赖的网络通讯给解耦了 纯算法实现,并不负责底层协议(如UDP)的收发,需要使用者自己定义下层数据包的发送方式,以 callback的方式提供给 KCP。 如果读者阅读 skywind3000/kcp 源码,可能会发现如下代码 test.cpp // 创建模拟网络:丢包率10%,Rtt 60ms~125ms vnet = new LatencySimulator(10, 60, 125); 测试和验证是不需要再真实网络上进行。 skywind3000/kcp 只是设计了最初的协议, 包括 非延迟ACK 快速重传(TCP协议也有) 非退让流控(拥塞控制,和TCP实现类同) xtaci/kcp-go 在此基础上,又增加了 加密机制 FEC(Forward Error Correction)前向纠错 PS: skywind3000和xtaci其实是大学同学 ...

May 10, 2020 · 3 min

密码管理工具(命令行)

版权声明 本站原创文章 由 萌叔 发表 转载请注明 萌叔 | http://vearne.cc 萌叔最近做一个类似1password的命令行密码管理工具passwordbox。传送门: vearne/passwordbox 支持使用对象存储进行多端同步(可选),目前支持阿里云OSS,青云QingStor 内部实现细节 首先将每个记录项加密存储在SQLite的数据文件中,然后再对整个数据文件进行二次加密。记录在内存中也是以密文的形式存在,安全系数比较高。 快速开始 编译 make build 安装 make install 启动 pwbox --data=/Users/vearne –data 设置加密数据文件的存储路径 建议你为passwordbox设置一个别名 alias pwbox='pwbox --data=/Users/vearne' 程序启动以后,按照导引的要求创建数据库,所有的记录项都存储在数据库中 ─$ ./pwbox --data /tmp/ ---- login database ---- ? Please type database's name: test fullpath /tmp/6879630a7d56210d2cd2491cb99d781194689fed71d7890a8dabbcb3a678cb73 ? Database is not exist. Do you like to create database now? Yes ---- create database ---- ? Please type database's name: test ? Please type password: ***** ? Please type hint[optional]: test ---- login database ---- ? Please type database's name: test fullpath /tmp/6879630a7d56210d2cd2491cb99d781194689fed71d7890a8dabbcb3a678cb73 ? Please type your password: ***** Hint for database test is test 登录数据库成功之后,可以执行如下的命令 ...

April 22, 2020 · 3 min

动态修改容器中的配置文件

版权声明 本站原创文章 由 萌叔 发表 转载请注明 萌叔 | http://vearne.cc 1. 引言 前段时间接手了一个前端项目。这个前端项目采用docker方式部署。有3套不同的环境,开发联调、测试、生产分别指向不同的后端API服务。 项目目录结构如下 ├── DockerfileDev ├── DockerfileProd ├── DockerfileTest ├── Makefile ├── README.md ├── config │ ├── api.dev.conf │ ├── api.prod.conf │ ├── api.test.conf │ └── nginx.conf ├── package.json ├── src ├── tsconfig.json └── yarn.lock config目录中是nginx相关的配置文件, 仅仅因为后端API服务地址不同,Dockerfile就有3个,对于开发联调、测试和生产环境的docker镜像也需要分别build,显然是非常低效的。 2. 改造 2.1 想法 最好的办法是在容器启动时,使用环境变量传入,动态修改nginx配置文件。 2.2 envsubst 查阅资料,发现一个shell命令 envsubst substitutes environment variables in shell format strings 它可以替换shell字符串中的环境变量 用法大致如下 root@i-bf77szgn:~# export API_HOST=vearne.com root@i-bf77szgn:~# echo "hello ${API_HOST}" | envsubst hello vearne.com 2.3 精简代码 在这种情况下,web.dev.conf、web.test.conf、web.prod.conf合并成一个文件default.template,通过环境变量传入后端API服务地址 ...

March 21, 2020 · 1 min

AI预测模型工程化性能调优

版权声明 本站原创文章 由 萌叔 发表 转载请注明 萌叔 | http://vearne.cc 1. 前言 最近经历了一次AI预测模型工程化性能调优,这里分享出来,希望对大家有所启发。 模型是keras训练的神经网络预测模型,输入一段文本,输出文本的分类结果。为了对外提供服务,已经将模型用 gunicorn + flask对模型进行封装,提供http接口。 环境 OS:ubuntu 18.04 CPU:32核 内存: 256GB 显卡: Tesla P100 前期的压测结果 wrk -t4 -c100 -d30s --script=post.lua --latency http://localhost:8080/api/predict 类型 CPU GPU QPS 2.4 12.41 2. 观察性能指标 QPS过低不能满足业务方的要求,所以萌叔尝试对封装好的服务进行调优。 仔细观察了各项系统指标之后(cpu、load、磁盘IO、内存使用率、虚拟内存页换入换出) 萌叔发现了一个疑点。 2.1 keras 使用CPU 在压测的过程中,CPU的使用率只能达到70%左右 2.2 keras 使用GPU 在压测的过程中,GPU的使用率只能达到30%左右 神经网络模型对文本分类的预测过程,实际就是将输入文本转换成数值,再带入神经网络,进行的2次计算的过程。这是一个计算密集型的场景,性能瓶颈应该是CPU/GPU; 如果CPU、GPU没有打满,则说明性能仍有提升的空间。 3. 调优 3.1 初始协程模式 web使用Flask开发,启动脚本如下: gunicorn -k gevent -t 300 -b 0.0.0.0:8080 run:app gunicorn有几种工作模式 sync (进程模式) eventlet gevent (协程模式) tornado gthread gevent是一种协程模式,它的特点是在请求的处理过程中有IO调用时(或者当前的协程主动让出CPU),会自动触发切换上下文切换,启动另一个协程,提高CPU的利用率。它特别适用于IO密集型的场景,尤其是对于处理过程中有对数据库的调用,第三方服务调用的情况。 ...

March 17, 2020 · 2 min

玩转consul(4)-ACL机制要点

版权声明 本站原创文章 由 萌叔 发表 转载请注明 萌叔 | http://vearne.cc 1. 前言 本文基于Consul v1.6.2 这篇文章并不是consul ACL的完全配置手册,感兴趣的读者请阅读参考资料1 2. Token存在的意义 Token其实用于权限控制中,表征使用者的身份 Consul的ACL中重要的3个实体 Token最终与一组Policy关联, 可以理解为RBAC(基于角色的访问控制)中的权限。 3. 能够细粒度控制的权限有哪些? 4. 如何使用Token 详见参考资料2 curl \ --header "X-Consul-Token: <consul token>" \ http://127.0.0.1:8500/v1/agent/members 其实也可以附加在Query中,但官方不推荐 5. 配置文件示例 5.1 server模式 { "acl": { "enabled": true, "default_policy": "deny", "enable_token_persistence": true }, "datacenter": "dc1", "data_dir": "/data/consul", "disable_update_check": true, "server": true, "ui": true, "bind_addr": "192.168.100.3", "client_addr": "0.0.0.0", "node_name": "s1", "bootstrap_expect": 2, "retry_join": ["192.168.100.3", "192.168.100.4", "192.168.100.5"] } 5.2 client 模式 client上设置default token之后, 在client使用CLI或者执行API请求,都可以自动附加token ...

January 13, 2020 · 1 min

玩转高性能日志库ZAP(5)-异步写日志

版权声明 本站原创文章 由 萌叔 发表 转载请注明 萌叔 | http://vearne.cc 1. 前言 在复杂的业务逻辑中,一个请求就有可能带来10 ~ 30条日志的写入。打日志造成的开销很大,是无法被忽略的一环。怎样才能提高日志的写入速度? 2. 实验 首先来看几个实验 完整程序见 vearne/golab/zaplog 中的log.go log2.go log3.go 2.1 打印到文件 打印到指定文件中,并做归档 package zaplog import ( "go.uber.org/zap" "go.uber.org/zap/zapcore" "gopkg.in/natefinch/lumberjack.v2" ) func InitLogger() *zap.Logger { // 动态调整日志级别 alevel := zap.NewAtomicLevel() hook := lumberjack.Logger{ Filename: "/tmp/tt1.log", MaxSize: 1024, // megabytes MaxBackups: 3, MaxAge: 7, //days Compress: true, // disabled by default } w := zapcore.AddSync(&hook) alevel.SetLevel(zap.InfoLevel) encoderConfig := zap.NewProductionEncoderConfig() encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder core := zapcore.NewCore( zapcore.NewConsoleEncoder(encoderConfig), w, alevel, ) return zap.New(core) } 2.2 打印到终端 这应该是官方推荐的方式,特别是对于使用docker容器的场景,应用开发者的配置非常简单,无需指定日志文件路径,由容器管理服务采集容器的标准输出并归档。 ...

January 2, 2020 · 3 min

一个简单的陌生人聊天系统

版权声明 本站原创文章 由 萌叔 发表 转载请注明 萌叔 | http://vearne.cc 前言 因为一直对IM的实现非常感兴趣,前段时间也自己手撸了一个。 萌叔觉得,这个版本实现比较简单,但能够从部分反映出IM的概貌,可能会其他的朋友有所帮助, 因此决定分享出来。 1. 功能说明 这个聊天系统被设计为陌生人自动匹配进行聊天,因此不需要注册。分为2个模块,在线体验地址 chat.vearne.cc 备注:如果匹配不上,可以多开几个窗口,自己和自己聊 1.1 客户端 vearne/chat-ui 客户端采用Vue开发 依赖组件 MatheusrdSantos/vue-quick-chat 1.2 服务端 vearne/chat 采用golang开发 重要框架 olahol/melody – websocket框架 grpc/grpc-go – grpc框架 架构图 客户与接入层的通讯采用websocket + 自定义的文本协议实现。 自定义的协议其实都是携带着各种命令的JSON字符串,很容易看懂 比如 创建一个新的账号(由客户端发出) request { "cmd": "CRT_ACCOUNT", "nickName": "XXXX" } response { "cmd": "CRT_ACCOUNT", "nickName": "XXXX", "accountId": 1111 } 又比如 匹配一个聊天对象 request { "cmd": "MATCH", "accountId": 1111 } response { "cmd": "MATCH", "partnerId": 1111, "partnerName": "xxxx", "sessionId": 10000 "code": 0 } 具体请看github相关页面 ...

December 19, 2019 · 1 min

istio学习笔记(1)-配置Gateway

版权声明 本站原创文章 由 萌叔 发表 转载请注明 萌叔 | http://vearne.cc 1. 前言 萌叔试图通过Gateway把服务暴露在服务网格外部,下面是笔者的一些总结。 2. 体系结构 很重要 gateway-controller <-> ingress-controller (实际的pod) istio-ingressgateway <-> nginx-ingress-controller (一种实现) gateway <-> ingress (配置) gateway-controller的一个实现是 istio-ingressgateway ingress-controller的一个实现是 nginx-ingress-controller nginx-ingress-controller相当于openresty, 配置ingress以后会生成对应nginx的配置文件。同样配置gateway之后, 会生成envoy对应的配置文件。 3. 配置 3.1 istio-ingressgateway 当安装了istio以后,服务中会有一个istio-ingressgateway 默认情况下, istio-ingressgateway对应的容器并没有暴露在服务网格之外。需要修改配置。 修改istio-ingressgateway的 Deployment 也可以直接修改helm的 deployment.yaml "dnsPolicy": "ClusterFirstWithHostNet" # 修改(确保能够正确访问pilot) "hostNetwork": true # 添加 (将pod以host网络模式暴露在服务网格外部) “ClusterFirstWithHostNet“: For Pods running with hostNetwork, you should explicitly set its DNS policy “ClusterFirstWithHostNet”. 3.2 gateway 以对kiali的配置为例 1) 创建namespace kubectl create namespace myistio 2)配置gateway 文件 kiali-gateway.yaml ...

December 5, 2019 · 2 min

聊聊短地址服务的实现

版权声明 本站原创文章 由 萌叔 发表 转载请注明 萌叔 | http://vearne.cc 1. 前言 一个长URL地址,形如 “https://www.google.com.hk/search?q=%E7%9F%AD%E5%9C%B0%E5%9D%80%E6%9C%8D%E5%8A%A1&oq=%E7%9F%AD%E5%9C%B0%E5%9D%80%E6%9C%8D%E5%8A%A1&aqs=chrome..69i57j69i61.4824j1j7&sourceid=chrome&ie=UTF-8" , 由于不可避免的带有PATH、各种参数和追踪标识,这导致URL往往很长。当你要把这个URL发送给其它人时,可能会变得很不方便。比如短信/微博有字数限制。或者对方将链接粘贴到浏览器时,容易漏掉部分数据。 因此短链接服务就显得非常重要。(短链接还可以使的生成的二维码色块更大,提高识别速度) 国内常见的短地址服务有新浪 百度 那么他们是怎么实现的呢? 建议先阅读参考资料1 2. 实现&原理 2.1 实现 萌叔受到参考资料1的启发也实现了一版。 vearne/tinyurl 代码及使用步骤见README 你也可以 在线尝试 2.2 生成短地址 1) 将长地址与一个整数建立映射(一对多) 这里整数使用int64,保存映射关系。笔者为了简单使用是MySQL数据库,如果为了更好的并发存储,还可以NoSQL数据库或者数据分分库分表。 "https://github.com/vearne/tinyurl" -> 10363 这里主键id就是整数值 长地址存储在url字段中 +-------+---------------------------------+---------------------+ | id | url | created_at | +-------+---------------------------------+---------------------+ | 10000 | http://vearne.cc/archives/39217 | 2019-11-28 14:02:56 | +-------+---------------------------------+---------------------+ 提示 这里不能用哈希的原因是,哈希后的值如果太短则容易出现碰撞,如果太长则压缩的效率太低,没有意义。 2)base62编码 整数如果直接按字符串传输,长度还是太长。可以对整数进行base62编码 之所以是62,其实是 26个小写英文字母[a-z], 26个大写英文字母[A-Z], 以及阿拉伯数字[0-9]。有意的避开特殊符号和不可见字符。 3) 拼接上域名 就得形如, 短链接地址: http://st.vearne.cc/2h9 2.3 访问短地址 访问短地址得到长地址的过程与短地址的生成过程刚好相反 1)base62解码 base62解码得到整数 ...

November 29, 2019 · 1 min

聊聊GeoHash

版权声明 本站原创文章 由 萌叔 发表 转载请注明 萌叔 | http://vearne.cc 本文基于 Redis 3.2.0 前言 鲁班门前弄大斧,笔者今天要聊一下geohash。 redis/elasticsearch/mongoDB 中地理位置的搜索,比如某个位置,附近1公里内的POI点,类似这样的功能都是基于geohash算法实现的。 首先看看维基百科的定义 Geohash is a public domain geocode system invented in 2008 by Gustavo Niemeyer[1], which encodes a geographic location into a short string of letters and digits. It is a hierarchical spatial data structure which subdivides space into buckets of grid shape, which is one of the many applications of what is known as a Z-order curve, and generally space-filling curves. ...

November 12, 2019 · 2 min