Jack Jiang

我的最新工程MobileIMSDK:http://git.oschina.net/jackjiang/MobileIMSDK
posts - 519, comments - 13, trackbacks - 0, articles - 1

1、引言

在社区中,分享了很多篇基于Netty编写的IM聊天入门文章(比如《跟着源码学IM》系列、《基于Netty,从零开发IM》系列等),在这些文章中分享了各种IM通信算法原理和功能逻辑的实现。但是这样简单的IM聊天系统是比较容易被窃听的,如果想要在里面说点悄悄话是不太安全的。

怎么办呢?学过密码学的朋友可能就想到了一个解决办法,聊天的时候对消息加密,处理的时候再对消息进行解密。是的,道理就是这样。

但密码学本身的理论就很复杂,加上相关的知识和概念又太多太杂,对于IM入门者来说,想要快速理清这些概念并实现合适的加解密方案,是比较头疼的。

本文正好借此机会,以Netty编写的IM聊天加密为例,为入门者理清什么是PKI体系、什么是SSL、什么是OpenSSL、以及各类证书和它们间的关系等,并在文末附上简短的Netty代码实示例,希望能助你通俗易懂地快速理解这些知识和概念!

补充说明:本文为了让文章内容尽可能言简意赅、通俗易懂,尽量不深入探讨各个技术知识和概念,感兴趣的读者可以自行查阅相关资料进一步学习。

学习交流:

本文已同步发布于:http://www.52im.net/thread-4104-1-1.html

2、相关文章

  1. 即时通讯安全篇(一):正确地理解和使用Android端加密算法
  2. 即时通讯安全篇(二):探讨组合加密算法在IM中的应用
  3. 即时通讯安全篇(三):常用加解密算法与通讯安全讲解
  4. 即时通讯安全篇(四):实例分析Android中密钥硬编码的风险
  5. 即时通讯安全篇(五):对称加密技术在Android平台上的应用实践
  6. 即时通讯安全篇(六):非对称加密技术的原理与应用实践
  7. 即时通讯安全篇(十):IM聊天系统安全手段之通信连接层加密技术
  8. 即时通讯安全篇(十一):IM聊天系统安全手段之传输内容端到端加密技术

3、什么是PKI?

我们需要先了解一下公钥和私钥的加密标准体系PKI。

3.1 基本概念

PKI的全称是Public Key Infrastructure,是指支持公钥管理体制的基础设施,提供鉴别、加密、完整性和不可否认性服务。

通俗讲:PKI是集机构、系统(硬件和软件)、人员、程序、策略和协议为一体,利用公钥概念和技术来实现和提供安全服务的、普适性的安全基础设施。

在公钥密码中,发送者用公钥(加密密钥)加密,接收者用私钥(解密密钥)解密。公钥一般是公开的,不再担心窃听,这解决了对称密码中的密钥配送问题。但是接收者依然无法判断收到的公钥是否合法(有可能是中间人假冒的)。

事实上,仅靠公钥密码本身,无法防御中间人攻击。于是,需要(认证机构)对公钥进行签名,从而确认公钥没有被篡改。加了数字签名的公钥称为公钥证书,一般简称证书。

有了证书来认证,可以有效防御中间人攻击,随之带来了一系列非技术性工作。

例如:谁来发证书?如何发证书?不同机构的证书怎么互认?纸质证书作废容易,数字证书如何作废?解决这些问题,需要制定统一的规则,即PKI体系。

PKI体系是通过颁发、管理公钥证书的方式为终端用户提供服务的系统,最核心的元素是证书。

围绕证书构成了PKI体系的要素:

  • 1)使用PKI的用户;
  • 2)颁发证书的机构(Certificate Authority,CA);
  • 3)保存证书的仓库。

总之:PKI是一个总称,既包括定义PKI的基础标准,也包括PKI的应用标准。

3.2 PKI体系现状

事实上PKI已经有两代了。

第一代的PKI标准主要是由美国RSA公司的公钥加密标准PKCS、国际电信联盟的ITU-T X.509、IETF的X.509、WAP和WPKI等标准组成。但是因为第一代PKI标准是基于抽象语法符号ASN.1进行编码的,实现起来比较复杂和困难,所以产生了第二代PKI标准。

第二代PKI标准是由微软、VeriSign和webMethods三家公司在2001年发布的基于XML的密钥管理规范也叫做XKMS。

事实上现在CA中心使用的最普遍的规范还是X.509系列和PKCS系列。

X.509系列主要由X.209、X.500和X.509组成,其中X.509是由国际电信联盟(ITU-T)制定的数字证书标准。在X.500基础上进行了功能增强,X.509是在1988年发布的。

X.509证书由用户公共密钥和用户标识符组成。此外还包括版本号、证书序列号、CA标识符、签名算法标识、签发者名称、证书有效期等信息。

而PKCS是美国RSA公司的公钥加密标准,包括了证书申请、证书更新、证书作废表发布、扩展证书内容以及数字签名、数字信封的格式等方面的一系列相关协议。它定义了一系列从PKCS#1到PKCS#15的标准。

其中最常用的是PKCS#7、PKCS#12和PKCS#10。PKCS#7 是消息请求语法,常用于数字签名与加密,PKCS#12是个人消息交换与打包语法主要用来生成公钥和私钥(题外话:iOS程序员对PKCS#12不陌生,在实现APNs离线消推送时就需要导出.p12证明,正是这个)。PKCS#10是证书请求语法。

4、什么是SSL?

4.1 基本概念

SSL(全称 Secure Socket Layer)安全套接层是网景公司(Netscape)率先采用的网络安全协议。它是在传输通信协议(TCP/IP)上实现的一种安全协议,采用公开密钥技术。

通俗地说:SSL被设计成使用TCP来提供一种可靠的端到端的安全服务,它不是单个协议,而是二层协议。低层是SSL记录层,用于封装不同的上层协议,另一层是被封装的协议,即SSL握手协议,它可以让服务器和客户机在传输应用数据之前,协商加密算法和加密密钥,客户机提出自己能够支持的全部加密算法,服务器选择最适合它的算法。

SSL特点是:它与应用层协议独立无关。上层的应用层协议(例如:HTTP、FTP、Telnet等)能透明的建立于SSL协议之上。SSL协议在应用层协议通信之前就已经完成加密算法、通信密钥的协商以及服务器认证工作。在此之后应用层协议所传送的数据都会被加密,从而保证通信的私密性。

4.2 与TLS的关系

SSL是网景公司(Netscape)设计,但IETF将SSL作了标准化,即RFC2246,并将其称为TLS(Transport Layer Security),其最新版本是RFC5246、版本1.2。

实际上:TLS是IETF在SSL3.0基础上设计的,相当于SSL的后续版本。所以我们通常都是SSL/TLS放一起说。

5、什么是OpenSSL?

5.1 基本概念

 

OpenSSL是一个开放源代码的软件库,应用程序可以使用这个包来进行安全通信,它包括代码、脚本、配置和过程的集合。例如:如果您正在编写一个需要复杂安全加密的软件,那么只有添加一个安全加密库才有意义,这样您就不必自己编写一大堆复杂的加解密函数(而且密码学本身很复杂,要写好它们并不容易)。

其主要库是以 C 语言所写成,实现了基本的加密功能,实现了 SSL 与 TLS 协议。

OpenSSL整个软件包大概可以分成三个主要功能部分:

  • 1)SSL协议库;
  • 2)应用程序;
  • 3)密码算法库。

OpenSSL的目录结构自然也是围绕这三个功能部分进行规划的。

OpenSSL 可以运行在 OpenVMS、 Microsoft Windows 以及绝大多数类 Unix 操作系统上。

5.2 具体来说

密钥和证书管理是PKI的一个重要组成部分,OpenSSL为之提供了丰富的功能,支持多种标准。

OpenSSL实现了ASN.1的证书和密钥相关标准,提供了对证书、公钥、私钥、证书请求以及CRL等数据对象的DER、PEM和BASE64的编解码功能。

OpenSSL提供了产生各种公开密钥对和对称密钥的方法、函数和应用程序,同时提供了对公钥和私钥的DER编解码功能。并实现了私钥的PKCS#12和PKCS#8的编解码功能。

OpenSSL在标准中提供了对私钥的加密保护功能,使得密钥可以安全地进行存储和分发。

在此基础上,OpenSSL实现了对证书的X.509标准编解码、PKCS#12格式的编解码以及PKCS#7的编解码功能。并提供了一种文本数据库,支持证书的管理功能,包括证书密钥产生、请求产生、证书签发、吊销和验证等功能。

5.3 发展历程

OpenSSL 计划在 1998 年开始,其目标是发明一套自由的加密工具,在互联网上使用。

OpenSSL 以 Eric Young 以及 Tim Hudson 两人开发的 SSLeay 为基础,随着两人前往 RSA 公司任职,SSLeay 在 1998 年 12 月停止开发。因此在 1998 年 12 月,社群另外分支出 OpenSSL,继续开发下去。

▲ 上图为 Tim Hudson

OpenSSL 管理委员会当前由 7 人组成有 13 个开发人员具有提交权限(其中许多人也是 OpenSSL 管理委员会的一部分)。只有两名全职员工(研究员),其余的是志愿者。

该项目每年的预算不到 100 万美元,主要依靠捐款。 TLS 1.3 的开发由 Akamai 赞助。

5.4 下载方法

OpenSSL可以从其官网上下载,地址是:https://www.openssl.org/source/,感兴趣的读者可以自行下载安装研究。

6、各类证书

6.1 证书类型

操作过证书的朋友可能会对各种证书类型眼花缭乱,典型的体现就是各种不同的证书扩展名上,一般来说会有DER、CRT、CER、PEM这几种证书的扩展名。

以下是最常见的几种:

  • 1)DER文件:表示证书的内容是用二进制进行编码的;
  • 2)PEM文件:是一个文本文件,其内容是以“ - BEGIN -” 开头的,Base64编码的字符;
  • 3)CRT和CER文件:基本上是等价的,他们都是证书的扩展,也是文本文件,不同的是CRT通常用在liunx和unix系统中,而CER通常用在windows系统中。并且在windows系统中,CER文件会被MS cryptoAPI命令识别,可以直接显示导入和/或查看证书内容的对话框;
  • 4)KEY文件:主要用来保存PKCS#8标准的公钥和私钥。

6.2 常用OpenSSL命令

下面的命令可以用来查看文本证书内容:

openssl x509 -incert.pem -text -noout

openssl x509 -incert.cer -text -noout

openssl x509 -incert.crt -text -noout

下面的命令可以用来查看二进制证书内容:

openssl x509 -incert.der -inform der -text -noout

下面是常见的PEM和DER相互转换。

PEM到DER的转换:

openssl x509 -incert.crt -outform der-out cert.der

DER到PEM的转换:

openssl x509 -incert.crt -inform der -outform pem -out cert.pem

补充说明:上述命令中用到的openssl程序,就是本文中提到的OpenSSL开源库提供的程序。

7、Netty中的聊天加密代码示例

7.1 关于Netty

Netty是一个Java NIO技术的开源异步事件驱动的网络编程框架,用于快速开发可维护的高性能协议服务器和客户端,事实上用Java开发IM系统时,Netty是几乎是首选。

有关Netty的介绍我就不啰嗦了,如果不了解那就详读以下几篇:

史上最强Java NIO入门:担心从入门到放弃的,请读这篇!

Java的BIO和NIO很难懂?用代码实践给你看,再不懂我转行!

新手入门:目前为止最透彻的的Netty高性能原理和框架架构解析

史上最通俗Netty框架入门长文:基本介绍、环境搭建、动手实战

基它有关Netty的重要资料:

Netty-4.1.x 源码 (在线阅读版)

Netty-4.1.x API文档 (在线查阅版)

7.2 启动SSL Server代码示例

事实上这个标题是不对的,Netty中启动的server还是原来那个server,只是对发送的消息进行了加密解密处理。也就是说添加了一个专门进行SSL操作的Handler。

netty中代表ssl处理器的类叫做SslHandler,它是SslContext工程类的一个内部类,所以我们只需要创建好SslContext即可通过调用newHandler方法来返回SslHandler。

让服务器端支持SSL的代码:

ChannelPipeline p = channel.pipeline();

  SslContext sslCtx = SslContextBuilder.forServer(...).build();

  p.addLast("ssl", sslCtx.newHandler(channel.alloc()));

让客户端支持SSL的代码:

ChannelPipeline p = channel.pipeline();

   SslContext sslCtx = SslContextBuilder.forClient().build();

   p.addLast("ssl", sslCtx.newHandler(channel.alloc(), host, port));

netty中SSL的实现有两种方式,默认情况下使用的是OpenSSL,如果OpenSSL不可以,那么将会使用JDK的实现。

要创建SslContext,可以调用SslContextBuilder.forServer或者SslContextBuilder.forClient方法。

这里以server为例,看下创建流程。

SslContextBuilder有多种forServer的方法,这里取最简单的一个进行分析:

publicstaticSslContextBuilder forServer(File keyCertChainFile, File keyFile) {

    returnnewSslContextBuilder(true).keyManager(keyCertChainFile, keyFile);

}

该方法接收两个参数:

  • 1)keyCertChainFile是一个PEM格式的X.509证书文件;
  • 2)keyFile是一个PKCS#8的私钥文件。

熟悉OpenSSL的童鞋应该知道使用openssl命令可以生成私钥文件和对应的自签名证书文件。

具体openssl的操作可以查看我的其他文章,这里就不详细讲解了。

除了手动创建证书文件和私钥文件之外,如果是在开发环境中,大家可能希望有一个非常简单的方法来创建证书和私钥文件,netty为大家提供了SelfSignedCertificate类。

看这个类的名字就是知道它是一个自签名的证书类,并且会自动将证书文件和私钥文件生成在系统的temp文件夹中,所以这个类在生产环境中是不推荐使用的。默认情况下该类会使用OpenJDK's X.509来生成证书的私钥,如果不可以,则使用 Bouncy Castle作为替代。

7.3 启动SSL Client代码示例

同样的在client中支持SSL也需要创建一个handler。

客户端的SslContext创建代码如下:

// 配置 SSL.

finalSslContext sslCtx = SslContextBuilder.forClient().trustManager(InsecureTrustManagerFactory.INSTANCE).build();

上面的代码我们使用了一个InsecureTrustManagerFactory.INSTANCE作为trustManager。

什么是trustManager呢?

当客户端和服务器端进行SSL连接的时候,客户端需要验证服务器端发过来证书的正确性。

通常情况下,这个验证是到CA服务器中进行验证的,不过这样需要一个真实的CA证书环境,所以在测试中,我们使用InsecureTrustManagerFactory,这个类会默认接受所有的证书,忽略所有的证书异常。

当然:CA服务器也不是必须的,客户端校验的目的是查看证书中的公钥和发送方的公钥是不是一致的,那么对于不能联网的环境,或者自签名的环境中,我们只需要在客户端校验证书中的指纹是否一致即可。

netty中提供了一个FingerprintTrustManagerFactory类,可以对证书中的指纹进行校验。

该类中有个fingerprints数组,用来存储安全的授权过的指纹信息。通过对比传入的证书和指纹,如果一致则校验通过。

使用openssl从证书中提取指纹的步骤如下:

openssl x509 -fingerprint -sha256 -inmy_certificate.crt

8、小结一下

上面我们对Netty聊天用到的加密技术和相关概念进行了梳理,我来简单这些概念之间的关系。

这些概念之间的关系,简单来说就是:

  • 1)PKI:是一套加密体系和标准的合集,它是理论方案;
  • 2)SSL:是利用了PKI理论体系,针对Socket网络这个场景设计的一套安全通信标准,属于是PKI的一个具体应用场景;
  • 3)OpenSSL:是PKI体系及SSL标准的算法和代码实现,它包括了具体的开源代码、工具程序等;
  • 4)各种证书:是在SSL或其它基于PKI体系的安全协议标准中需要使用的到一些加密凭证文件等。

而具体到Netty中的聊天加密,那就是应用了上述的PKI体系,基于SSL协议,在OpenSSL等开源库的帮助下实现的安全程序。

9、参考资料

[1] 公钥基础设施(PKI)国际标准进展

[2] 一篇文章让你彻底弄懂SSL/TLS协议

[3] 什么是OpenSSL?它有什么用途

[4] OpenSSL是什么软件

[5] netty系列之对聊天进行加密

[6] 跟着源码学IM

[7] 基于Netty,从零开发IM

[8] TCP/IP详解(全网唯一在线阅读版)

[9] 快速理解TCP协议一篇就够

[10] Netty-4.1.x 源码(在线阅读版)

[11] Netty-4.1.x API文档(在线版)

本文已同步发布于http://www.52im.net/thread-4104-1-1.html

posted @ 2022-12-22 17:34 Jack Jiang 阅读(111) | 评论 (0)编辑 收藏

     摘要: 本文由陶文分享,InfoQ编辑发布,有修订和改动。1、前言本系列的前几篇主要是从各个角度讲解Protobuf的基本概念、技术原理这些内容,但回过头来看,对比JSON这种事实上的数据协议工业标准,Protobuf到底性能到底高多少?本篇将以Protobuf为基准,对比市面上的一些主流的JSON解析库,通过全方位测试来证明给你看看Protobuf到底比JSON快几倍。学习交流:- 移动端IM开发入门文...  阅读全文

posted @ 2022-12-16 12:43 Jack Jiang 阅读(131) | 评论 (0)编辑 收藏

关于MobileIMSDK

MobileIMSDK 是一套专门为移动端开发的开源IM即时通讯框架,超轻量级、高度提炼,一套API优雅支持UDP 、TCP 、WebSocket 三种协议,支持iOS、Android、H5、标准Java平台,服务端基于Netty编写。

工程开源地址是:

关于RainbowChat

► 详细产品介绍:http://www.52im.net/thread-19-1-1.html
► 版本更新记录:http://www.52im.net/thread-1217-1-1.html
► 全部运行截图:Android端iOS端
► 在线体验下载:专业版(TCP协议)专业版(UDP协议)      (关于 iOS 端,请:点此查看

 

RainbowChat是一套基于开源IM聊天框架 MobileIMSDK 的产品级移动端IM系统。RainbowChat源于真实运营的产品,解决了大量的屏幕适配、细节优化、机器兼容问题(可自行下载体验:专业版下载安装)。

* RainbowChat可能是市面上提供im即时通讯聊天源码的,唯一一款同时支持TCP、UDP两种通信协议的IM产品(通信层基于开源IM聊天框架  MobileIMSDK 实现)。

v8.3 版更新内容

此版更新内容更多历史更新日志):

(1)Android端主要更新内容bug修复及优化!】:

  • 1)[bug] 当首页“消息”列表所有的item都是置顶时,取消其中任一个置顶,都会错误地将其排在列表首位而不是列表末尾;
  • 2)[bug] 解决了从首页“消息”列表中遗留的陌生人聊天信息无法重置消息未读数的问题;
  • 3)[bug] 解决了聊天界面中底部面板和输入法软键盘切换时ui发生弹跳的问题;
  • 4)[优化] 重构了APP包名、应用名,防止被某些手机误报成恶意软件。
  • 5)[优化] 重构了搜索功能相关的代码,使之更易理解和维护;
  • 6)[优化] 优化了APP中各种文本输入框UI效果,以及其它UI细节;
  • 7)[优化] 解决了自定义长按菜单在某些机型上item文字会换行的问题;
  • 8)[优化] 大文件发送时,选择的图片、视频文件可以自动以图片消息和短视频消息的形式发送;
  • 9)[优化] 优化了APP处于后台时,收到实时语音/视频请求的通知形式(用高优先级的系统Notification方式提醒用户)。 

(2)服务端主要更新内容:

  • 1)[bug] 解决了uid登陆时的sql注入风险;
  • 2)[优化] 升级MobileIMSDK至v6.2正式版

此版主要功能运行截图更多截图点此查看): 

posted @ 2022-12-07 15:17 Jack Jiang 阅读(110) | 评论 (0)编辑 收藏

     摘要: 本文由腾讯PCG后台开发工程师的SG4YK分享,进行了修订和和少量改动。1、引言近日学习了 Protobuf 的编码实现技术原理,借此机会,正好总结一下并整理成文。接上篇《由浅入深,从根上理解Protobuf的编解码原理》,本篇将从Base64再到Base128编码,带你一起从底层来理解Protobuf的数据编码原理。本文结构总体与 Protobuf 官方文档相似,不少内容也来自官方文档,并在官方...  阅读全文

posted @ 2022-12-02 12:33 Jack Jiang 阅读(151) | 评论 (0)编辑 收藏

     摘要: 本文由码农的荒岛求生陆小风分享,为了提升阅读体验,进行了较多修订和排版。1、引言搞即时通讯IM方面开发的程序员,在谈到通讯层实现时,必然会提到网络编程。那么计算机网络编程中的一个非常基本的问题:到底该怎样组织Client与server之间交互的数据呢?本篇文章我们不讨论IM系统中的那些高端技术话题,我们回归到通讯的本质——也就是数据在网络中交互时的编解码原理,并由浅入深从底...  阅读全文

posted @ 2022-11-24 11:43 Jack Jiang 阅读(149) | 评论 (0)编辑 收藏

本文由vivo技术团队Li Guanyun分享,为了提升阅读体验,行了较多修订和重新排版。

1、引言

Protobuf 作为一种跨平台、语言无关、可扩展的序列化结构数据通讯协议,已广泛应用于网络数据交换的场景中(比如IM通信、分布式RPC调用等)。

随着互联网的发展,分布式系统的异构性会愈发突出,跨语言的需求会愈加明显,同时 gRPC 也大有取代Restful之势,而 Protobuf 作为gRPC 跨语言、高性能的法宝,我们技术人有必要深入理解 Protobuf 原理,为以后的技术更新和选型打下基础。

借此机会,我将个人的Protobuf学习过程以及实践经验,总结成文,与大家一起探讨学习。本篇主要从Protobuf的基础概念开始,包括技术背景、技术原理、使用方法和优缺点。

PS:本篇本跟上篇《Protobuf从入门到精通,一篇就够!》类似,都适合作为Protobuf的入门文章,但本篇力求简洁,尽量不涉及Protobuf的具体技术细节,目的是降低阅读的门槛、提升阅读效果,希望对你有用。

学习交流:

(本文已同步发布于:http://www.52im.net/thread-4081-1-1.html

2、系列文章

本文是系列文章中的第 2 篇,本系列总目录如下:

  • IM通讯协议专题学习(一):Protobuf从入门到精通,一篇就够!
  • IM通讯协议专题学习(二):快速理解Protobuf的背景、原理、使用、优缺点》(* 本文
  • 《IM通讯协议专题学习(三):由浅入深,从通信编解码原理上理解Protobuf》(稍后发布..)
  • 《IM通讯协议专题学习(四):从Base64到Protobuf,详解Protobuf的数据编码原理》(稍后发布..)
  • 《IM通讯协议专题学习(五):Protobuf到底比JSON快几倍?请看全方位实测!》(稍后发布..)
  • 《IM通讯协议专题学习(六):手把手教你如何在Android上从零使用Protobuf》(稍后发布..)
  • 《IM通讯协议专题学习(七):手把手教你如何在NodeJS中从零使用Protobuf》(稍后发布..)
  • 《IM通讯协议专题学习(八):金蝶随手记团队的Protobuf应用实践(原理篇)  》(稍后发布..)
  • 《IM通讯协议专题学习(九):金蝶随手记团队的Protobuf应用实践(实战篇) 》(稍后发布..)

3、什么是Protobuf?

Protobuf(全称是Protocol Buffers)是一种跨平台、语言无关、可扩展的序列化结构数据的方法,可用于网络通信数据交换及存储。

在序列化结构化数据的机制中,Protobuf是灵活、高效、自动化的,相对常见的XML、JSON,描述同样的信息,Protobuf序列化后数据量更小、序列化/反序列化速度更快、更简单。

一旦定义了要处理的数据的数据结构之后,就可以利用Protobuf的代码生成工具生成相关的代码。只需使用 Protobuf 对数据结构进行一次描述,即可利用各种不同语言(proto3支持C++, Java, Python, Go, Ruby, Objective-C, C#)或从各种不同流中对你的结构化数据轻松读写。

PS:类似的介绍,在上篇《Protobuf从入门到精通,一篇就够!》中也有涉及,有兴趣可以一并阅读之。

4、为什么是 Protobuf?

4.1 技术背景

大家可能会觉得 Google 发明 Protobuf 是为了解决序列化速度的,其实真实的原因并不是这样的。

Protobuf最先开始是 Google用来解决索引服务器 request/response 协议的。

在没有Protobuf之前,Google 已经存在了一种 request/response 格式,用于手动处理 request/response 的编解码。

这种sstk式也能支持多版本协议,不过代码不够优雅:

if(protocolVersion=1) {

    doSomething();

} elseif(protocolVersion=2) {

    doOtherThing();

} ...

如果是非常明确的格式化协议,会使新协议变得非常复杂。因为开发人员必须确保请求发起者与处理请求的实际服务器之间的所有服务器都能理解新协议,然后才能切换开关以开始使用新协议。

这也就是每个服务器开发人员都遇到过的低版本兼容、新旧协议兼容相关的问题。

为了解决这些问题,于是Protobuf就诞生了。

4.2 Protobuf 诞生了

Protobuf 最初被寄予以下 2 个期望:

  • 1)更容易引入新的字段,并且不需要检查数据的中间服务器可以简单地解析并传递数据(而无需了解所有字段);
  • 2)数据格式更加具有自我描述性,可以用各种语言来处理(比如C++, Java 等各种语言)。

但这个版本的 Protobuf 仍需要自己手写解析的代码。

随着Protobuf的发展、演进,它具有了更多的特性:

  • 1)自动生成的序列化和反序列化代码(避免了手动解析的需要。官方提供自动生成代码工具,各个语言平台的基本都有);
  • 2)除了用于数据交换之外,Protobuf也被用作某些持久化数据的便捷自描述格式。

Protocol Buffers 命名的由来:

Why the name "Protocol Buffers"?

The name originates from the early days of the format, before we had the protocol buffer compiler to generate classes for us. At the time, there was a class called ProtocolBuffer which actually acted as a buffer for an individual method. Users would add tag/value pairs to this buffer individually by calling methods like AddValue(tag, value). The raw bytes were stored in a buffer which could then be written out once the message had been constructed.

Since that time, the "buffers" part of the name has lost its meaning, but it is still the name we use. Today, people usually use the term "protocol message" to refer to a message in an abstract sense, "protocol buffer" to refer to a serialized copy of a message, and "protocol message object" to refer to an in-memory object representing the parsed message.

4.3 Protobuf 在谷歌业务中的地位

Protobuf 现在是 Google 用于数据交换和存储的通用语言。

谷歌代码树中定义了 48162 种不同的消息类型,包括 12183 个 .proto 文件。它们既用于 RPC 系统,也用于在各种存储系统中持久存储数据。

Protobuf 诞生之初是为了解决服务器端新旧协议(高低版本)兼容性问题,名字也很体贴——“协议缓冲区”,只不过后期慢慢发展成用于传输数据。

5、Protobuf 协议的工作原理

如下图所示:可以看到,对于序列化协议来说,使用方只需要关注业务对象本身,即 idl 定义,序列化和反序列化的代码只需要通过工具生成即可。

6、Protobuf 协议的消息定义

Protobuf 的消息是在idl文件(.proto)中描述的。

下面是本次样例中使用到的消息描述符 customer.proto

syntax = "proto3";

 

package domain;

 

option java_package = "com.Protobuf.generated.domain";

option java_outer_classname = "CustomerProtos";

 

message Customers {

    repeated Customer customer = 1;

}

 

message Customer {

    int32 id= 1;

    string firstName = 2;

    string lastName = 3;

 

    enum EmailType {

        PRIVATE = 0;

        PROFESSIONAL = 1;

    }

 

    message EmailAddress {

        string email = 1;

        EmailType type= 2;

    }

 

    repeated EmailAddress email = 5;

}

上面的消息比较简单,Customers包含多个Customer(Customer包含一个id字段、一个firstName字段、一个lastName字段以及一个email的集合)。

除了上述定义外,文件顶部还有三行可帮助代码生成器的申明:

  • 1)syntax = "proto3":用于idl语法版本,目前有两个版本proto2和proto3,两个版本语法不兼容,如果不指定,默认语法是proto2(由于proto3比proto2支持的语言更多,语法更简洁,本文使用的是proto3);
  • 2)package domain:此配置用于嵌套生成的类/对象;
  • 3)option java_package:生成器还使用此配置来嵌套生成的源(此处的区别在于这仅适用于Java,在使用Java创建代码和使用JavaScript创建代码时,使用了两种配置来使生成器的行为有所不同。也就是说,Java类是在包com.Protobuf.generated.domain下创建的,而JavaScript对象是在包domain下创建的)。

Protobuf 提供了更多选项和数据类型,本文不做详细介绍,感兴趣可以参考官方文档

7、Protobuf 的代码生成

首先安装 Protobuf 编译器 protoc(点这里有详细的安装教程)。

安装完成后,可以使用以下命令生成 Java 源代码:

1protoc --java_out=./src/main/java./src/main/idl/customer.proto

上述命令的意图是:从项目的根路径执行该命令,并添加了两个参数 java_out(即定义 ./src/main/java/ 为Java代码的输出目录;而 ./src/main/idl/customer.proto 是.proto文件所在目录)。

生成的代码非常复杂,但幸运的是它的用法却非常简单:

CustomerProtos.Customer.EmailAddress email = CustomerProtos.Customer.EmailAddress.newBuilder()

        .setType(CustomerProtos.Customer.EmailType.PROFESSIONAL)

        .setEmail("crichardson@email.com").build();

 

CustomerProtos.Customer customer = CustomerProtos.Customer.newBuilder()

        .setId(1)

        .setFirstName("Lee")

        .setLastName("Richardson")

        .addEmail(email)

        .build();

// 序列化

byte[] binaryInfo = customer.toByteArray();

System.out.println(bytes_String16(binaryInfo));

System.out.println(customer.toByteArray().length);

// 反序列化

CustomerProtos.Customer anotherCustomer = CustomerProtos.Customer.parseFrom(binaryInfo);

System.out.println(anotherCustomer.toString());

8、Protobuf 的性能数据

我们简单地以上述Customers为模型,分别构造、选取小对象、普通对象、大对象进行性能对比。

序列化耗时以及序列化后数据大小对比:

反序列化耗时:

更多性能数据可以参考官方的测试Benchmark

9、Protobuf 的优点

9.1效率高

从序列化后的数据体积角度,与XML、JSON这类文本协议相比,Protobuf通过 T-(L)-V(TAG-LENGTH-VALUE)方式编码,不需要", {, }, :等分隔符来结构化信息。同时在编码层面使用varint压缩。

所以描述同样的信息,Protobuf序列化后的体积要小很多,在网络中传输消耗的网络流量更少,进而对于网络资源紧张、性能要求非常高的场景。比如在移动网络下的IM即时通讯应用中,Protobuf协议就是非常不错的选择(PS:这也是我为什么着手分享Protobuf系列文章的原因啦)。

我们来简单做个对比。

要描述如下JSON数据:

1{"id":1,"firstName":"Chris","lastName":"Richardson","email":[{"type":"PROFESSIONAL","email":"crichardson@email.com"}]}

使用JSON序列化后的数据大小为118byte:

7b226964223a312c2266697273744e616d65223a224368726973222c226c6173744e616d65223a2252696368617264736f6e222c22656d61696c223a5b7b2274797065223a2250524f46455353494f4e414c222c22656d61696c223a226372696368617264736f6e40656d61696c2e636f6d227d5d7d

而使用Protobuf序列化后的数据大小为48byte:

0801120543687269731a0a52696368617264736f6e2a190a156372696368617264736f6e40656d61696c2e636f6d1001

从序列化/反序列化速度角度,与XML、JSON相比,Protobuf序列化/反序列化的速度更快,比XML要快20-100倍。

9.2支持跨平台、多语言

Protobuf是平台无关的,无论是Android、iOS、PC,还是C#与Java,都可以利用Protobuf进行无障碍通讯。

proto3支持C++、Java、Python、Go、Ruby、Objective-C、C#(详见《Protobuf从入门到精通,一篇就够》)。

9.3扩展性、兼容性好

Protobuf具有向后兼容的特性:更新数据结构以后,老版本依旧可以兼容,这也是Protobuf诞生之初被寄予解决的问题,因为编译器对不识别的新增字段会跳过不处理。

9.4使用简单

Protobuf 提供了一套编译工具,可以自动生成序列化、反序列化的样板代码,这样开发者只要关注业务数据idl,简化了编码解码工作以及多语言交互的复杂度。

10、Protobuf 的缺点

Protobuf的优点很突出,但缺点也很明显。

Protobuf的缺点主要是:

  • 1)不具备自描述能力:跟XML、JSON相比,这两者是自描述的,而ProtoBuf则不是;
  • 2)数据可读性非常差:ProtoBuf是二进制协议,如果没有idl文件,就无法理解二进制数据流,对调试非常不友好。

不过:Charles已经支持Protobuf协议,导入数据的描述文件即可,详情可参考 Charles Protocol Buffers

然而:由于没有idl文件无法解析二进制数据流,ProtoBuf在一定程度上可以保护数据,提升核心数据被破解的门槛,降低核心数据被盗爬的风险(也算是缺点变优点的典型范例)。

11、参考资料

[1] Protobuf官方网站

[2] Protobuf从入门到精通,一篇就够!

[3] 如何选择即时通讯应用的数据传输格式

[4] 强列建议将Protobuf作为你的即时通讯应用数据传输格式

[5] APP与后台通信数据格式的演进:从文本协议到二进制协议

[6] 面试必考,史上最通俗大小端字节序详解

[7] 移动端IM开发需要面对的技术问题(含通信协议选择)

[8] 简述移动端IM开发的那些坑:架构设计、通信协议和客户端

[9] 理论联系实际:一套典型的IM通信协议设计详解

[10] 58到家实时消息系统的协议设计等技术实践分享

(本文已同步发布于:http://www.52im.net/thread-4081-1-1.html

posted @ 2022-11-17 10:52 Jack Jiang 阅读(100) | 评论 (0)编辑 收藏

为了更好地分类阅读52im.net 总计1000多篇精编文章,我将在每周三推送新的一期技术文集,本次是第5 期。

* 评语:本系列文章尽量使用最浅显易懂的文字、图片来组织内容,力求通信技术零基础的人群也能看懂。但个人建议,至少稍微了解过网络通信方面的知识后再看,会更有收获。特别推荐即时通讯开发者来阅读,因为针对移动弱网的问题,确实可以找到很多有价值的答案。


[- 1 -] IM开发者的零基础通信技术入门(一):通信交换技术的百年发展史(上)

[链接]http://www.52im.net/thread-2354-1-1.html

[摘要] 本文(上下两篇)将带你了解当今通信交换技术最初的模样以及发展过程。学习技术更要了解技术的前世今生,技术本无聊,故事很有趣。


[- 2 -]IM开发者的零基础通信技术入门(二):通信交换技术的百年发展史(下)

[链接]http://www.52im.net/thread-2356-1-1.html

[摘要] 接上篇,本篇里我们需要暂停一下,回过头来看看我们国家的交换机发展情况。


[- 3 -] IM开发者的零基础通信技术入门(三):国人通信方式的百年变迁

[链接] http://www.52im.net/thread-2360-1-1.html

[摘要] 本文通过大量珍贵历史图片,从中国第一条电报线路,到如今触手可及的5G网络,回顾过去、展望未来,一起来看国人通信方式的百年历史变迁。


[- 4 -]IM开发者的零基础通信技术入门(四):手机的演进,史上最全移动终端发展史

[链接] http://www.52im.net/thread-2369-1-1.html

[摘要] 本文将通过大量历史图片,讲述手机这种移动终端的演化过程,为您呈现如今已深度融入人类生活的智能手机本来的样子。了解过去,才能更好地展望未来。


[- 5 -] IM开发者的零基础通信技术入门(五):1G到5G,30年移动通信技术演进史

[链接] http://www.52im.net/thread-2373-1-1.html

[摘要] 今天的5G,3.5GHz+大规模MIMO+波束赋形,还有固定无线应用,不禁让人看到了当年3G时代WiMax的影子,但WiMax为何输给了LTE,难道命运也喜欢对技术开玩笑吗?一部跨越三十年惊心动魄的移动通信史,为你揭晓答案。


[- 6 -] IM开发者的零基础通信技术入门(六):移动终端的接头人——“基站”技术

[链接] http://www.52im.net/thread-2375-1-1.html

[摘要]自上个世纪70年代末移动通信网络诞生以来,移动通信基站已经陪伴人类40年了,为人类社会带来了空前的变革,但你知道它的故事吗?


[- 7 -] IM开发者的零基础通信技术入门(七):移动终端的千里马——“电磁波”

[链接] http://www.52im.net/thread-2382-1-1.html

[摘要] 本文将回归到无线通信的技术之魂——“电磁波”,尽量用通俗易懂的文字讲述这个稍显枯燥的通信技术基础知识。


[- 8 -] IM开发者的零基础通信技术入门(八):零基础,史上最强“天线”原理扫盲

[链接] http://www.52im.net/thread-2385-1-1.html

[摘要] 实际生活中,无线通信中的天线都长什么样?有哪些用途?更重要的是,天线的技术原理是怎样的?本文将通过大量的图片,为你讲述这些内容。本文力求通俗易懂,面向零基础读者,希望继续给即时通讯网的开发者带来更多通信技术方面的收获。


[- 9 -] IM开发者的零基础通信技术入门(九):无线通信网络的中枢——“核心网”

[链接]http://www.52im.net/thread-2391-1-1.html

[摘要] 对于通信专业的人来说,几乎每个人都认为核心网难(不只是难,而且是非常难),很难有人能通俗易懂地讲明白它是什么东西。所以本文想借此机会,为零基础的IM开发者或其他移动端应用层程序员们,讲清楚这个话题。


[- 10 -] IM开发者的零基础通信技术入门(十):零基础,史上最强5G技术扫盲

[链接] http://www.52im.net/thread-2394-1-1.html

[摘要] 作为IM开发者,或者移动端开发者来说,提前了解5G技术显然是很有必要的。那么什么是5G技术?技术原理是怎么样的?5G技术将带来哪些技术革新?本文将以零基础的应用程序开发者为阅读对象,帮你找到这些问题的答案。


[- 11 -] IM开发者的零基础通信技术入门(十一):为什么WiFi信号差?一文即懂!

[链接] http://www.52im.net/thread-2402-1-1.html

[摘要] 为什么WiFi信号会受影响?什么情况下该使用何种方式组网?如何改善WiFi信号差的问题?等等,本文将通俗易懂地为你找到这些问题的答案。


[- 12 -] IM开发者的零基础通信技术入门(十二):上网卡顿?网络掉线?一文即懂!

[链接]http://www.52im.net/thread-2406-1-1.html

[摘要] 本文将详细介绍生活中遇到的常见网络问题,及可能的解决方法,虽说是一篇技术文章,但内容将一如既往地通俗易懂,简单实用。


[- 13 -] IM开发者的零基础通信技术入门(十三):为什么手机信号差?一文即懂!

[链接] http://www.52im.net/thread-2415-1-1.html

[摘要] 关于手机信号的问题真的不是大家想象得那么简单。本文正好收集整理了这一块的通信技术知识,一如既往的力求通俗易懂,希望对你有用。


[- 14 -] IM开发者的零基础通信技术入门(十四):高铁上无线上网有多难?一文即懂!

[链接] http://www.52im.net/thread-2419-1-1.html

[摘要] 为什么在高铁上手机信号会这么差?这个无线通信难题真的无法解决吗?今天,作为通信老司机的笔者,就详细和大家聊聊这个问题。


[- 15 -] IM开发者的零基础通信技术入门(十五):理解定位技术,一篇就够

[链接]http://www.52im.net/thread-2428-1-1.html

[摘要] 定位技术到底是怎么实现的?技术原理怎样?有哪些局限性?貌似我们平时也没有做更多了解,既然这样,那就跟着本文来一窥究竟吧。


👉52im社区本周新文:《IM通讯协议专题学习(一):Protobuf从入门到精通,一篇就够! http://www.52im.net/thread-4080-1-1.html》,欢迎阅读!👈

我是Jack Jiang,我为自已带盐!https://github.com/JackJiang2011/MobileIMSDK/

posted @ 2022-11-11 11:33 Jack Jiang 阅读(225) | 评论 (0)编辑 收藏

     摘要: 本文由IBM开发者社区分享,有较多修订和改动。1、引言在当今移动网络时代,手机流量和电量是宝贵的资源,对于移动端最常见的即时通讯IM应用,由于实时通信基于Socket长连接,它对于流量和电量的需求较一般应用来说更高(详见《移动端IM实践:WhatsApp、Line、微信的心跳策略分析》)。在IM应用中,优化数据流量消耗过多的基本方法就是使用高度压缩的通讯协议,而数据压缩后流量减小带来的自然结果也就...  阅读全文

posted @ 2022-11-10 11:49 Jack Jiang 阅读(125) | 评论 (0)编辑 收藏

关于MobileIMSDK

MobileIMSDK 是一套专门为移动端开发的开源IM即时通讯框架,超轻量级、高度提炼,一套API优雅支持UDP 、TCP 、WebSocket 三种协议,支持iOS、Android、H5、标准Java平台,服务端基于Netty编写。

工程开源地址是:

关于RainbowChat

► 详细产品介绍:http://www.52im.net/thread-19-1-1.html
► iOS端更新记录:http://www.52im.net/thread-2735-1-1.html
► 全部运行截图:iOS端全部运行截图 (另:Android端运行截图 点此查看
► 在线体验下载:App Store安装地址 (另:Android端下载体验 点此查看

 

RainbowChat是一套基于开源IM聊天框架 MobileIMSDK 的产品级移动端IM系统。RainbowChat源于真实运营的产品,解决了大量的屏幕适配、细节优化、机器兼容问题(可自行下载体验:专业版下载安装)。

RainbowChat可能是市面上提供im即时通讯聊天源码的,唯一一款同时支持TCP、UDP两种通信协议的IM产品(通信层基于开源IM聊天框架 MobileIMSDK 实现)。

v6.1 版更新内容

此版更新内容更多历史更新日志):

  • 1)[bug] 在聊天信息界面中查找消息时,点击查看指定消息,在聊天界面中不能自动滚动到这条消息;
  • 2)[bug] 点击首页“消息”列表中遗留的陌生人聊天信息时,无法重置消息未读数的问题;
  • 3)[bug] 在聊天界面中进入别的界面再回来时,底部面板没有自动关闭/收起;
  • 4)[优化] 优化了标题栏弹出菜单的圆角效果(使之更符合最新iOS美感设计);
  • 5)[优化] 优化了APP中各种文本输入框UI效果,以及其它UI细节;
  • 6)[优化] 优化了短视频录制界面在iOS16“灵动岛”手机上的ui适配。

此版主要功能运行截图更多截图点此查看):

 

posted @ 2022-11-05 17:42 Jack Jiang 阅读(93) | 评论 (0)编辑 收藏

1、引言

在《IM消息ID技术专题》系列文章的前几篇中,我们已经深切体会到消息ID在分布式IM聊天系统中的重要性以及技术实现难度,各种消息ID生成算法及实现虽然各有优势,但受制于具体的应用场景,也并不能一招吃遍天下,所以真正在IM系统中该如何落地消息ID算法和实现逻辑,还是要因地致宜,根据自已系统的设计逻辑和产品定义取其精华,综合应用之。

本文将基于网易严选的订单ID使用现状,分享我们是如何结合业内常用的分布式ID解决方案,从而在此基础之上进行ID特性丰富,并不断提升系统可用性和稳定性保障。同时,也对ID生成算法的落地实践过程中遇到坑进行了深入剖析。

本篇中的订单ID虽然不同于IM系统中的消息ID,但其技术实践仍然相通,希望能给你的IM系统消息ID技术选型也来更多的启发。

学习交流:

本文已同步发布于:http://www.52im.net/thread-4069-1-1.html

2、关于作者

西狂:服务端研发工程师, 早期参与严选采购、严选财务、严选合伙人以及报警平台等系统后端建设,目前主要致力于严选交易域技术演进以及业务研发工作。

3、系列文章

本文是系列文章中的第7篇,本系列总目录如下:

4、为什么需要分布式ID?

4.1 业务背景

如上图所示,对于网易严选的主站、分销和tob都会生成各自的订单ID,在同步订单数据到订单中心的时候,订单中心会生成一个订单中心内部的一个订单号,只是推送给到下游仓配时使用的订单ID略有不同。

4.2 带来的问题

 因为订单ID使用的混乱,导致了一系列问题的产生,例如: 沟通壁垒 、管控困难以及代码腐化等等。

4.3 技术目标

 我们希望通过分布式ID来帮助生成订单ID,在业务规则上必须全局唯一、安全性高,在性能上要高可用、低延迟。

5、我们的分布式ID架构原理

5.1 技术选型

下表是业内常见的分布式ID解决方案:

综合考虑是否支持水平扩展以及能够显示指定ID长度,最终选择的是Leaf的Segment模式(详见《深度解密美团的分布式ID生成算法》)。

5.2 架构简介

Leaf采用了预分发的方式来生成ID(如下图所示),在DB之上搭载若干个Server,每个Server在启动的时候,都会去DB中拿固定长度的ID列表,存放于内存中,因为ID是基于内存分发的,所以可以做到很高效。

在数据持久化方面,每次去DB拿固定长度的ID列表,只是把最大的ID持久化。

整体架构实现比较简单,主要是为了尽快解决业务层DB压力的问题,但是在生产环境中也暴露出一些问题。

比如:

  • 1)TP999数据波动大,当号段使用完之后还是会hang在更新数据库的I/O上,tp999数据会出现偶尔的尖刺;
  • 2)当更新DB号段的时候,如果DB宕机或者发生主从切换,会导致一段时间的服务不可用。

5.3 可用性优化

为了解决上面提到这个两个问题,引入双Buffer机制和异步更新策略,当一个Buffer消耗到某个临界点时,就会异步的触发任务,把下一个号段加载到内存中。

保证无论何时DB出现问题,都能有一个Buffer的号段可以正常对外提供服务,只要DB在一个Buffer的下发的周期内恢复,就不会影响整个Leaf的可用性。

5.4 步长动态调整

号段长度在固定不变的前提下,流量的突增和锐减都会使得正常流量下维持原有号段正常工作的时间缩短和提升。

可以尝试使用以下关系表达式来描述:

Q * T = L

(Q:服务qps  L:号段长度  T:号段更新周期)

但是Leaf的本质是希望T固定,如果Q和L可以正相关,T就可以趋于一个定值。

所以在Leaf每次更新号段的时候,会根据上一次号段更新的周期T和号段长度step,来决定下一次号段长度nextStep。

如下所示:

T < 15min,nextStep = step * 2

15min < T < 30min,nextStep = step

T > 30min,nextStep = step / 2

(初始指定step <= nextStep <= 最大值(自定义:100W))

6、我们做了什么改进?

6.1 特性丰富

 

通过结合严选的实际业务场景,进行了特性化支持,例如支持批量ID获取、大促提前扩容以及提前跳段处理。

6.2 可用性保障

1)针对DB:

 DB(MySql)采用主从模式(读写分离、降低主库压力),一主两从的配置方式,Master和Slave之间采用的是半同步复制(数据一致性要求,后期可考虑使用MySql Group Replication)。同时还添加了双1配置,保证不丢数据。

2)引入SDK:

通过引入SDK可以降低各个业务方的接入成本、降低Leaf服务端压力以及在Leaf服务不可用时,客户端起到短暂降级的效果。

SDK的实现原理和Leaf类似,在项目启动之初会加载业务关心参数配置信息,在应用构建本地缓存,同样采用了双Buffer存储模式。

6.3 稳定性保障

1)运维方面:

主要分为3个方面:

  • 1)日志监控:可以帮助发现预期之外的异常情况;
  • 2)流量监控:流有助于号段长度的评估范围,预防号段被快速消费的极端场景;
  • 3)线上巡检:可以时刻对服务进行存活校验。

2)SLA高可用方面:

除了运维之外还做了SLA的接入,通常用SLA来衡量系统的稳定性,除此之外我们还按照接口维度设定了SLO目标规则,目前的指标项比较单一只有请求延迟和错误率这两项。

 


 

7、我们遇到的坑

7.1 问题发现

如下图所示,我们发现每次服务启动上线接口的rt(响应时间)都要比平时高的多,但是过了一段时间之后却又恢复成正常水平。

7.2 问题探究

在分析之前,我们可以先简单的回顾下java虚拟机是如何运行Java字节码的。

虚拟机视角下Java字节码如何被虚拟机运行:

Java虚拟机将class文件加载到虚拟机中,然后将字节码翻译成机器码给底层硬件执行,而这里的翻译有两种形式,解释执行和编译执行。前者的优势在于无需等待编译,后者的优势在于实际运行速度更快。HotSpot默认采用混合模式,它会先解释执行字节码,然后将其中反复执行的热点代码,以方法为单位进行即时编译,JVM是依据方法的调用次数以及循环回边的执行次数来触发JIT编译的。

在Java7之前我们可以根据程序的特性选择对应的即时编译器。Java7开始引入分层编译机制(-XX:+TieredCompilation):综合了C1的启动性能优势和C2的峰值性能优势。

分层编译将JVM的执行状态分为了5个层次:

  • L0:解释执行(也会profiling);
  • L1:执行不带profiling的C1代码;
  • L2:执行仅带方法调用次数和循环回边执行次数profiling的C1代码;
  • L3:执行带所有profiling的C1代码;
  • L4:执行C2代码。

对于C1编译的三个层次,按执行效率从高至低:L1 > L2 > L3, 这是因为profiling越多,额外的性能开销越大。通常情况下,C2代码的执行效率比C1代码高出30%以上。(这里需要注意的是Java8默认开启了分层编译)

这张图列出了常见的分层编译的编译路径:

  • 1)通常情况下,热点方法会被第三层的C1编译器编译,再被C2编译器编译(0-> 3-> 4);
  • 2)如果方法的字节数目比较少并且第三层的profilling没有可收集的数据,jvm会判定该方法对于C1和C2的执行效率相同,在经过3层的C1编译过后,直接回到1层的C1(0-> 3-> 1);
  • 3)在C1忙碌的情况下,JVM在解释执行过程中对程序进行profiling,而后直接由4层的C2编译(0-> 4);
  • 4)在C2忙碌的情况下,方法会被2层的C1编译,然后再被3层的C1编译,以减少方法在3层的执行时间(0-> 2-> 3-> 4)。

上图是项目启动时的分层编译日志以及整个过程接口响应RT。

从图中可以看到先是执行了C1编译,再执行C2编译(日志文件中的3和4分别打标L3和L4),满足 0->3->4 编译顺序。

发现从C1编译到C2编译耗时过程比较长,这符合我们一开始提出的疑问,为什么项目启动需要经过一段时间接口RT才能趋于稳定。

7.3 解决方案

为了能在项目启动之初,快速达到接口RT峰值,因此只要尽最大程度缩短解释执行这个中间过程即可。

相应的解决方案:

  • 方案 1:关闭分层编译,降低编译阈值;
  • 方案 2:Mock接口数据, 快速触发JIT编译以及C2编译;
  • 方案 3:Java9 AOT提前编译。

针对方案3:Java9中支持新特性AOT提前编译,相比较于JIT即时编译而言,AOT在运行前就已经编译好了,避免 JIT 编译器的运行时性能消耗,同时避免解释程序的早期性能开销,可以极大提高java代码性能。

8、落地使用概况

Leaf已经在线上环境投入使用,各个业务方(包括主站、渠道、tob)也相应接入进行统一整改,自此严选订单ID生成得到统一收拢。

在整个严选的落地情况,按照业务维度,目前累计接入3个业务,分别是订单ID、订单快照ID、订单商品快照ID,都经受住了双十一和双十二考验。

9、参考资料

[1] 微信的海量IM聊天消息序列号生成实践(算法原理篇)

[2] 解密融云IM产品的聊天消息ID生成策略

[3] 深度解密美团的分布式ID生成算法

[4] 深度解密滴滴的高性能ID生成器(Tinyid)

本文已同步发布于:http://www.52im.net/thread-4069-1-1.html

posted @ 2022-11-03 11:45 Jack Jiang 阅读(126) | 评论 (0)编辑 收藏

仅列出标题
共52页: First 上一页 17 18 19 20 21 22 23 24 25 下一页 Last 
Jack Jiang的 Mail: jb2011@163.com, 联系QQ: 413980957, 微信: hellojackjiang