Nitro's

Apr 18, 2020 - Comments - tech

唯一ID生成算法

前言 唯一ID在互联网大厂的业务线中广泛使用,比如订单号、数据库主键、IM消息序号、交易流水号等等,这些ID的共同特点是唯一标记某一个业务记录或者消息、部分ID自增、全局有序或者局部有序。 UUID作为是一种全局唯一的方案是可供使用的选择之一,但在实际操作中大家一般使用的很少,几个原因: 16个字节128位占用太多的存储空间不适合用来作为主键; UUID的版本1基于MAC地址和时间戳,可保证全局唯一,但容易被硬件追踪; 后续版本使用伪随机数、SHA1又没有前向兼容,用在特定业务的一定范围内使用没有问题,但作为分布式系统的唯一ID不适合。 所以就有了各个互联网大厂分享的各自业务中实现唯一ID的不同算法和系统部署方案,唯一ID算法主要核心聚焦在以下几点: ID位数 ID算法原理 全局唯一 全局(局部)趋势有序 时钟回拨问题 外部系统依赖 技术实现汇总 从大厂分享的技术实现汇总如下: | 厂牌 | 技术方案 | ID位数 | 全局唯一 | 有序 | 时钟回拨 | 外部系统依赖 | | ———|———–|——–|——— |———-|———-|————-| | Twitter | SnowFlake | 64 | 全局唯一 | 局部有序 | block wait | | None | | MongoDB | ObjectId,与SnowFlake类似 | 96 | 全局唯一 | 局部有序 | \ | None | | 百度 | UidGenerator,与SnowFlake类似 | 64 | 全局唯一 | 局部有序 | 兼容 | None || | 微信 | 与SnowFlake类似 | 64 | 全局唯一 | 场景有序 | \ | None | | 美团 | Leaf-segment | 64 | 全局唯一 | 全局有序 | \ | MySQL | | 美团 | Leaf-snowerFlake ,与SnowFlake类似 | 64 | 全局唯一 | 局部有序 | fixed or interrupt |Zookeeper | | 融云 | 自定义 | 80 | 全局唯一 | 场景有序 | \ | None |

Apr 11, 2020 - Comments - life

逝去的哥

新冠疫情依然在国外肆虐,国内的防控形势还在紧绷着没有一丝的放松,这次疫情就像一把无情之刀突然的刺向人群,悄无声息地蔓延,等待着大流行。年前有敢于自己身边的人竟然还能感染甲流,不免心有余悸,奥司他韦的药品名称依然常记于脑海。之前在老家,人们常说老人难熬冬,意思就是对于老人来说冬天是最难熬过去的,尤其是上了年纪又有一些基础病,一场感冒可能就把自己送走了。这次新冠对老人的考验就更大了,本身免疫力低,又是肺部感染,稍不注意就要器官插管带ICU监护,能活过来大难不死,撑不过去也就是新的十年就说声再见,化作一捧尘土回归了自然。 昨天在微博刷到一篇关于纽约哈特岛埋葬无人认领的尸体棺材的新闻,联想前几天看他们科普尸体焚烧进化史的视频,一声感慨又想到了因病去世的东哥,最后应该也是在病痛的折磨中离去的吧。 夜晚嫂子的一声电话,东哥还是没能挺过这一关,走了,从确诊癌症到死亡也就是半年的时间,从一个胖实的中年汉子到最后的骨瘦如柴,看着都能想象到的痛苦与挣扎,无奈这样也没能驱走病魔,最终还是离我们而去。 如果说老人的离去是自然的凋零,但一个中年人的逝去就好像自然的背叛,我似不惑,何罪之有?但生命的一次过活,身在自己,乐在自己。虽然人类学会了群居,学会了社会维系,构建了组织、国家、政体,但你总免不了自然的回归,如若走了,但请好好珍惜当下,珍惜身边的人,走了的,终归是逝去的歌,下面的曲目留给还能唱,还能听的亲人。 东哥,虽然和我爸之间有很深的瓜葛,但是对我来说从没二心,从我上小学一丁点的时候灰头土脸的跟着你进城玩,到后来周末经常坐在你开的那辆轻卡上兜风,到一块去拜年侄女被你抱着天真无邪的笑声,再到我们婚礼上你忙前忙后的操持,一幕幕的场景就像在脑中无限循环的电影剧本,每一帧都是完整的你,每一帧却又抹上了灰色的印记,曾经来过一遭,你我相逢三十年,就此别过。 回忆就想哭,缅怀的纪念。虽然东哥已经走了快半年,就像6年前回忆奶奶一样,当记忆铁盒打开的瞬间,不是回忆笔下,而是泪如雨下。 放心走吧,哥。 父母、孩子、亲人一切都好。

Feb 19, 2020 - Comments - translate tech golang

Golang依赖包管理知多少(4)-v2与后续版本

前言 成功的项目随着逐步成熟以及新功能的加入,过去的特性和设计方案可能不能满足需求。开发者可能想整合一下他们所学的知识比如移除废弃函数、重命名参数或者分割复杂的包为多个可维护模块。这些变更都需要下游用户付出努力来迁移他们的代码来适配新的API,所以他们不会没有认真权衡过收益和成本而做出变更的。 对于一些还在实验中的项目,比如主版本为v0的项目,用户会预期到偶尔的重大变更。对于已经发布为稳定版本的项目,比如主版本为v1或者更高版本的项目,重大的变更需要在一个大版本上进行。这篇文章主要介绍了大版本策略、如果创建并发布一个新的大版本以及如何维护一个module的多个主版本。 主版本与module path 主版本策略 发布v2及后续版本 主版本与module path modules模式规范了一个重要的原则,导入兼容性原则: 如果一个老版本的包和一个新版本的包使用相同的导入path,那么新版本的包必须向后兼容老版本的包。 一个新的大版本的包不会向后兼容之前的版本。这就意味着一个新的大版本module必须有一个不同于历史版本的module path。从v2版本开始,大版本号必须紧跟在module path后面(声明在go.mod文件的module语句后面)。比如,当一名开发者开发了github.com/googleapis/gax-go module的v2时,他需要使用新的module path-github.com/googleapis/gax-go/v2,如果用户想使用这个module的v2版本就必须要改变他们的包导入以及module依赖到github.com/googleapis/gax-go/v2。 大版本后缀在module path上是module模式不同于其他依赖管理工具的地方。后缀方式可以解决菱形依赖问题。在module模式之前,gopkg.in允许软件维护者遵循我们现在的这套导入兼容性规则,如果你的module依赖一个包gopkg.in/yaml.v1,另外的一个module依赖gopkg.in/yaml.v2,这时是不会有冲突的,因为这两个yaml 包使用了不同的module path,和module模式类似 gopkg.in也适用了后缀方式。由于gopkg.in使用了module模式相同的后缀版本技术,所以go命令兼容gopkg.in中.v2作为版本后缀,但这仅针对gopkg.in,托管在其他域名下的module还是需要使用/v2这种后缀方式。 主版本策略 在开发v2以及后续版本时推荐的方式是创建一个对应版本号的文件目录。 github.com/googleapis/gax-go @ master branch /go.mod → module github.com/googleapis/gax-go /v2/go.mod → module github.com/googleapis/gax-go/v2 这样做并不是为了module模式,主要为了兼容一些工具:在仓库中的文件路径正好是GOPATH模式中go get中所需要的路径。这种方式也方便把所有的大版本按照不同的目录来进行开发。 其他的策略也有按照不同的大版本放在不同的分支,但是如果v2+版本的源代码放在仓库的默认分支(通常是master),像GOPATH下的go这类的工具又不识别版本可能就不能区分不同的大版本。 由于不同的大版本放在不同的子目录这一策略的兼容性好,本文的例子将会遵循这个策略。如果他们让用户维持GOPATH模式,那我们建议module的开发者遵循这一策略。 发布v2及后续版本 本文使用github.com/googleapis/gax-go作为例子: $ pwd /tmp/gax-go $ ls CODE_OF_CONDUCT.md call_option.go internal CONTRIBUTING.md gax.go invoke.go LICENSE go.mod tools.go README.md go.sum RELEASING.md header.go $ cat go.mod module github.com/googleapis/gax-go go 1.

Feb 18, 2020 - Comments - translate tech golang

Golang依赖包管理知多少(3)-发布一个module

前言 这篇文章将讨论如何编写并发布可被其他module依赖引用的一个module。 注意:这篇主要涉及开发配置直至v1版本,如果你对v2版本迭代感兴趣,可以参考v2版本与版本迭代。 本文默认使用Git作为例子, 但是Mercurial、Bazaar等其他版本管理工具也是支持的。 项目配置 版本与module v0-初始版本、非稳定版本 v1-第一个稳定版本 项目配置 本文需要你有一个已经存在的项目作为例子,所以我们以认识Go modules文章结尾的文件为例子: $ cat go.mod module example.com/hello go 1.12 require rsc.io/quote/v3 v3.1.0 $ cat go.sum golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c h1:qgOY6WgZOaTkIIMiVjBQcw93ERBE4m30iBm00nkL0i8= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= rsc.io/quote/v3 v3.1.0 h1:9JKUTTIUgS6kzR9mK1YuGKv6Nl+DijDNIc0ghT58FaY= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0 h1:7uVkIFmeBqHfdjD+gZwtXXI+RODJ2Wc4O7MPEh/QiW4= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= $ cat hello.go package hello import "rsc.io/quote/v3" func Hello() string { return quote.HelloV3() } func Proverb() string { return quote.Concurrency() } $ cat hello_test.go package hello import ( "testing" ) func TestHello(t *testing.

Feb 16, 2020 - Comments - translate tech golang

Golang依赖包管理知多少

前言 本文内容结合自己的理解,对Golang官方博客针对Golang新版本modules包管理系统的文章翻译。 使用Go modules Go从1.11和1.12已经初步支持modules,新版Go包依赖管理系统通过显性的版本依赖使得更加易于维护。这篇文章主要介绍了在开始使用Golang modules时的一些基本操作。 一个module是许多Go Pakcage的集合,在它的根目录有一个go.mod文件。go.mod文件中定义这个module的module路径,也定义了需要导入的module路径、外部依赖,这些都是modules成功构建所必须的。每一个外部依赖都有一个module路径和版本号组成。 从Go 1.11开始,如果当前目录或者它的父级目录中有go.mod文件并且此目录不在$GOPATH/src下面,go命令就会开启对modules的支持(如果在$GOPATH/src目录下,为了保持兼容即便存在go.mod文件go命令还会使用原有的GOPATH模式)。从Go 1.13开始module模式就会默认启用了。 下面就开始介绍在使用module模式开发Go代码过程中的几个步骤: 创建一个新的module 增加一个依赖包 升级依赖包的小版本 增加一个大版本的依赖包 升级一个依赖包到大版本 移除未使用的依赖包 创建一个新的module 创建一个不在\$GOPATH/src的空目录,cd到这个目录,然后创建一个golang源文件hello.go: package hello func Hello() string { return "Hello,World." } 再写一个测试用例: package hello import "testing" func TestHello(t *testing.T) { want: "Hello,World." if got := Hello(); got != want { t.Errorf("Hello() = %q, want %q", got, want) } } 到这里时,目录下已经包含一个package了,但是它还不是一个module因为它缺少 go.mod 文件。下面在这个目录下运行 go test 执行单元测试,我们会看到: $ go test PASS ok _/home/gopher/hello 0.

Feb 16, 2020 - Comments - translate tech golang

Golang依赖包管理知多少(2)-迁移工程到modules模式

前言 在golang工程中可以使用多种依赖管理策略,像dep、glide这种Vendoring工具非常的流行,但是它们又各自为政并不能很好的兼容。很多项目把工程文件放在GOPATH目录下存储为一个Git仓库,其他人通过go get命令依赖存放在GOPATH中最新版本的代码。 Go modules系统在Go1.11时发布,它通过go命令提供了一个官方的依赖管理解决方案。这篇文章主要讲述了迁移原有工程到modules的一些工具和技术。 注意:如果你的工程已经标记为v2.0.0或者更高版本,在增加go.mod文件时需要更新工程中的module path。我们将会在后面的文章中解释如何做才能在v2或者更高版本时不让你的用户糟心。 原工程中使用了依赖管理工具 使用一个依赖管理器 不使用依赖管理器 在modules模式下的测试用例 发布一个版本 导入和规范module path 原工程中使用了依赖管理工具 为了转换一个使用依赖管理工具的工程,需要执行以下命令: $ git clone https://github.com/my/project [...] $ cd project $ cat Godeps/Godeps.json { "ImportPath": "github.com/my/project", "GoVersion": "go1.12", "GodepVersion": "v80", "Deps": [ { "ImportPath": "rsc.io/binaryregexp", "Comment": "v0.2.0-1-g545cabd", "Rev": "545cabda89ca36b48b8e681a30d9d769a30b3074" }, { "ImportPath": "rsc.io/binaryregexp/syntax", "Comment": "v0.2.0-1-g545cabd", "Rev": "545cabda89ca36b48b8e681a30d9d769a30b3074" } ] } $ go mod init github.com/my/project go: creating new go.mod: module github.com/my/project go: copying requirements from Godeps/Godeps.

Aug 17, 2019 - Comments - dev tech golang

Goim单机配置启动简单记录

Goim v2.0.0为例 运行环境:MacOS 10.14 依赖组件、框架 Java Kafka依赖 Kafka 消息队列 Zookeeper Kafka依赖 Redis 在线状态、服务状态缓存 Discovery 服务注册、发现框架 配置 Redis配置 host=localhost port=6379 ZooKeeper配置 host=localhost port=2081 Kafka配置 host=localhost port=9092 job job -conf=goim/cmd/job/job-example.toml -region=sh -zone=sh001 -deploy.env=dev comet comet -conf=goim/cmd/comet/comet-example.toml -region=sh -zone=sh001 -deploy.env=dev -weight=10 -addrs=127.0.0.1 logic logic -conf=goim/cmd/logic/logic-example.toml -region=sh -zone=sh001 -deploy.env=dev -weight=10 启动 Zookeeper 支持Kafka的运行 Kafka 消息队列启动,业务Push消息到这里 Redis 用户在线状态、服务状态的缓存服务启动 Discovery 启动服务注册、发现框架 logic启动,接收业务Push消息,推送到消息队列 comet启动,接受用户注册、连接注册、消息下发 job启动,读取消息队列,发送到comet 测试 通过源代码中的example,测试连通性是否正确,通过RPC的Push接口发送消息测试。

May 19, 2019 - Comments - tech

Retrofit、Volley、HttpClient比较

Retrofit作为现在大家用的最多HTTP请求框架,非常流行,Github上star 3w+。它为什么能超越Volley、HTTPClient能够一枝独秀呢?那先看看其他两个HTTP栈的实现。 HTTPClient Java界老牌HTTP栈实现,由Apache维护,它支持HTTP1.0/1.1、极其丰富的API来支持各种子协议,代码庞大臃肿也变成了它的缺点,虽然它的创建一开始是为了弥补Java SDK中java.net包HTTP请求的简单实现,Android在6.0以后彻底弃用了它,转而推荐使用HTTPURLConnection。 Volley 作为Google官方开源的一款HTTP栈,它支持切换HTTPURLConnection、HTTPClient底层协议栈的切换,同时提供了请求线程池、缓存的支持,刚一推出也是收获许多赞赏,但是它就像G家好多产品一样,几年一次的维护和有些bug都需要使用方去维护解决,都是开发者逐渐抛弃的原因。虽然从Volley开始,这些HTTP栈的实现都在逐渐向上层实现,因为不管是Android、iOS大部分App的网络交互都是Restful-API的实现,所以能在提供基础HTTP服务的基础上,提供请求内容的封装、响应内容的解析就越来越迫切,Volley在这方便做出了第一步,响应内容的解析以及支持自定义解析这些功能。但是Volley的致命问题在于它的响应内容处理上,必须将全部数据读取出来交给上层数据,这对内存的要求可能很高,缺乏一定的灵活性,所以只适合小数据量的HTTP请求。 Retrofit 来自Android界开源大厂Square.Inc ,底层HTTP栈来自于OKHTTP框架,它支持HTTP 1.1/2,支持TLS,HTTP2作为socket连接复用的最新技术,OKHTTP的支持可以说是一大重点,因为作为移动端App,相比浏览器中的Web开发,App端点的可控性可以让我们快速支持这种技术来提高加载速度,同时OKHTTP提供了请求拦截器方便我们在请求发起前的对参数进行操纵。 Retrofit建立在OKHTTP的基石之上,抽象出了Converter、CallAdapter这两个重要的概念。 Coverter作为HTTP请求、响应内容的类型服务类为上层不同的技术实现提供支持,现在支持GSON、Guava、JSON、XML、Protobuf,同样你可以根据自己的需求进行定制。 CallAdapter则为不同的上层框架提供了容器支持,让你能够在Retrofit的基础上同步、异步的分发请求与响应,现在支持Java8、RxJava、RxJava2的标准实现。 除此之外,动态代理、注解编程的使用,让开发者从复杂的请求参数、URL拼接中解放出来,更多的去关心业务逻辑的实现。 这一系列的优势让Retrofit逐渐得到开发者的青睐。 番外: 在Java11 OpenJDK的文档中看到另外一个HTTPClient的实现,支持HTTP2、支持上层RxJava编程,但这对于Android平台来说JDK 11还是有点远。 参考资料: OKHTTP JDK Httpclient

May 18, 2019 - Comments - tech

Java动态代理类的生成和调用

Java的动态代理在开发中都有大量的使用,如Spring框架,Android的HTTP开发框架Retrofit,本文主要关注动态代理编写使用背后的代理类是如何的生成以及与代理对象的调用是如何实现的,以Retrofit这个框架中动态代理的使用为例子进行分析。 在Retrofit中,某类的API接口大多定义在一个Service接口类中,某一个API接口则被声明为Service接口类的一个方法,通过方法的注解、参数来描述具体API参数等信息。在调用某个API接口时,Retrofit实例调用create()创建Service接口类的实例对象来完成调用,具体源代码如下: public <T> T create(final Class<T> service) { // 必须是接口,并且不允许是继承来的接口 Utils.validateServiceInterface(service); //是否预加载 if (validateEagerly) { eagerlyValidateMethods(service); } //动态代理的核心代码 return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service }, new InvocationHandler() { private final Platform platform = Platform.get(); @Override public Object invoke(Object proxy, Method method, @Nullable Object[] args) throws Throwable { // If the method is a method from Object then defer to normal invocation. if (method.getDeclaringClass() == Object.class) { return method.invoke(this, args); } if (platform.