皮皮网

【linux实例源码下载】【rot公式源码】【看源码吗】playground源码

2024-11-23 12:58:52 来源:督查源码java

1.ios开发常识
2.如何通过玩TensorFlow Playground来理解神经网络
3.swift 教程 swift介绍
4.Context 使用场景&&源码解读
5.深入研究ast-grep的源码模式: 一文解析Tree-Sitter核心概念
6.Vue3源码解析(computed-计算属性)

playground源码

ios开发常识

       1.IOS开发要学习哪些方面的知识

        来自网友的答案,请参考

        第一步:编程入门课

        时间预计:4个星期

        推荐看公开课,Udacity也行,网易公开课也行,自己找一个面对对象语言(一般是JAVA, C++, Python)的课。我是在网易公开课看的斯坦福的CSA,学的JAVA。

       å¦‚果你纯粹学iOS开发,不推荐看哈佛CS,CS是给CS系的学生介绍整个计算机世界的框架,讲的内容比较多,进度比较快,对iOS开发其实有点累赘了。(臣妾有点跟不上啊!!)

        计划安排是一天一课,看课程要求的书(至少看完一本)及大部分作业。这一阶段重点不是语法,而是以下3个目标。

        目标:

        1. 让自己对编程这件事感到适应。

        写hello world。

        怎么写function, 怎么调用function。

        全局变量,局部变量这类基本知识点。

        都是基本的东西。看看书,写多两个程序就欧啦。

        2. 掌握编程语言的基本要素。

        编程语言4个要素:

        a. 基本的数据类型:整数,实数,character, string, boolean

        b. 基本的运算符号:+-*/><;++--那啥的

        c. 怎样输入输出

        d. 怎样控制程序:sequence,selection,loop

        3. 了解编程范式

        面对过程编程。

        面向对象编程。

        第二步:上手iOS!

        时间预计:2星期

        强烈推荐CSP,老头子讲的超级好!我的很多东西(对象思维啥的)是在这里跟着做练习的时候才真正明白的(好啦,也可能是上一堂课练习做得少的原因)。如果等到9月应该itunes U上会开始教iOS 7了。网易公开课的是年iOS 5版的,前堂课,也行。(iTunes U上有完整的课)

        CSP说有prerequisite,一开始被吓到,事实证明还是可以学下去的。头两节课一头雾水,没关系,把itunes U上的课件下载下来,把所有代码打出来,然后一个个元素对应之前学的语言匹配,再不懂先放着,继续学后边的,过几天打多点代码就懂了。

        感觉学5、6堂课,一个星期左右就可以开始进入下一阶段自己做东西了。之后用啥学啥,每堂课都有主题的。速度慢点的同学们,这阶段跟我一样准备两个星期吧!

        第三步:开发app!

        时间预计:2星期(本人。1个半月,实在不好意思说出口)

        这个时间就可长可短啦,还包括美工,交互啥的。坚持要用啥学啥的原则,其实就是知道iOS SDK都有什么组件,每个组件有什么function而已。stackoverflow, Github, apple sample code多上,搜索引擎多用。如果有个师傅,这个阶段真的是进步神速。

        好的!不出意外,你的第一个app就这么新鲜出炉了!从今天开始,成为一个冷艳逼格高尚的iOS开发者吧!

        2.学习ios编程所要的知识基础大概是什么

        C语言,可以这么说所有的开发中要么就用到C语言,要么就要用到C语言的语法结构,反正怎么弄都会和C沾边,IOS也不例外,IOS的开发语言是objective-c,而objective-c又是C的超级,所以学习IOS需要C的基础,同时在编写IOS程序时也支持C/C++混编,所以如果从事IOS开发,必须先熟悉objective-c这门开发语言,实际上就是C语言了。

        当然在学习IOS的时候,也并不是必须有基础,目前还达不到没有基础不能学的地步,其实在中国培训行业来说,无论是什么专业,无论是多么高端的行业,只要是属于培训行业,就一定有零基础授课班,就算你没基础学校也会为你免费补习基础。

        3.想问下做ios平台的软件开发,需要那些基础知识

        iOS是运行于iPhone、iPodtouch以及iPad设备的操作系统,它管理设备硬件并为手机本地应用程序的实现提供基础技术。根据设备不同,操作系统具有不同的系统应用程序,例如Phone、Mail 以及 Safari,这些应用程序可以为用户提供标准系统服务。

        iOS 开发工程师主要从事手机操作系统研发及手机应用软件开发(包括手机游戏等各类功能的软件开发)。在参与调查的开发技术人员中,%的称,他们正在 iOS 平台开发人才,其中 %的表示, iOS 平台开发经验要比任何其他平台开发经验更受青睐。

        由于国内 iOS 开发起步相对较晚,人才培养机制更是远远跟不上市场发展速度。有限的iOS 开发人才成了国内企业必争的资源。甚至有的企业不得不考虑通过收购来填补人才空缺。一名 iOS 开发新手要比普通软件开发新手高出约 -%的薪资,符合条件或有项目经验的开发工程师更是有价无市。 IOS开发方面的知识,大概有一下几点。

        1、环境准备。这就是开发前所需的环境,相关软件的安装及使用详解。这里包括有IOS开发前准备,Xcode集成开发环境的安装使用与项目建立流程,Xcode6中的Playground的特点和使用方法,IOS开发常用操作及技巧,IOS程序打包与发布

        2、Swift编程语言。包括有Swift语言基础,Swift语言与Objective-C语言混合编程,Swif语言面向对象详解,Swift、语言调试技巧

        3、IOS用户界面。包括有IOS项目介绍,IOS用户界面开发基本刘晨恶搞,IOS裂变控件TableView的开发与使用,使用StoryBoard做IOS UI界面跳转。

        4、界面优化。包括有IOS绘图API绘制线条/文字/几何图形,Xcode常用项目模版,屏幕适配,IOS自定义控制实例,IOS的动画效果及实现方法,IOS中的CALayeer的使用。

        4.IOS开发要学什么

        Objective-C面向对象基础:继承,多态、动态类型和动态绑定;

        Objective-C访问系统中的文件,Objective-C的XML解析和生成,对象序列化;

        Objective-C线程和子任务,Objective-C下的Socket和HTTP,内存管理,资源释放和回放

        UI应用程序开发基础,Interface Builder工具,窗口视图,UI基础概念,基本交互 ,Cocoa事件传播与相应基础,常用控件,TableView等较为复杂的控件,文件包和资源,图形和颜色,Cocoa自定义 绘制图像基础,Objective-C 的动画效果开发, Objective-C的多国语言支持,在应用中嵌入浏览器以及使用WebService等等。

        IOS开发还要学习一些其他的知识,具体的可以问问“4G梦工场”的老师,因为“4G梦工场”里有IOS培训课程,里面的老师将会给你更专业的解答。

        5.ios游戏开发入门应该准备些什么理论知识

        这个多看多准备肯定是没坏处的 作为一名初出茅庐的iOS开发人员,如何着手编写iPhone 和iPad游戏呢?您可以借助《iOS游戏开发入门经典》来奠定良好基础。

        无论您是否拥有iOS编程经验,都可以借助本书来了解开始创建有趣iOS游戏所需的技术。不过更专业的理论知识你可以到千锋教育网站上看看 资料很全的 也很方便学习 现在iOS培训机构有很多,可是与学员签订《就业培训协议》,在协议里明确写出薪水保障的企业,最低薪水:专科5K起、本科6K起、硕士8K起,千锋是业内唯一一家,口碑很好。

        6.一个资深的iOS开发者需要掌握哪些技能

        iOS开发能力

        掌握(最好是精通)OC语言和runtime各种细节(读过相关的clang源码和runtime源码为佳)。精通基本的framework(Foundation,UIKit等,平时干活用得最多的)。掌握一些第三方的轮子,这个主要看你的工作经验和习惯,如果自己有搞过不错的轮子最好(这点可以看出你的经验,眼界和解决问题的能力。)

        基础知识

        学校里的那些经典课程(数据结构,算法,三大浪漫基础,各分支研究领域的基础课程),这些反正都是老生常谈了。。。不好好打基础只能书到用时方恨少了。即使做不到精通,至少应该了解各个基础知识的关系和入口点(知识体系结构),就像你写oo时建立的类层次结构。

        学习能力

        能根据自己的工作目标快速的调研、掌握、改进和山寨各种技术,并且能把各种相关知识点融合起来,产生新技术,推动技术发展。

        搜索技能,其实很多人不知道自己要找什么和如何去找,到哪去找

        提问技巧,能清晰的表达自己的意图,好让别人知道怎么帮你,请搜索“提问的艺术”

        广义的开发能力

        不仅限于iOS,要在技术上有所建树,还是需要开阔自己的眼界,从不同领域汲取营养,开阔眼界,所谓的全栈和跨界正是如此。

        搞技术的人要有一颗“好奇心”。

        工程能力

        各种工具(VSC,CI,Issue Tracking,Testing,Profiling)使用,订制改进,开发。这些算加分项,合理使用可以有效提高开发效率。

        需求能力,可理解,制定和改进各种技术需求,业务需求。

        文档能力,各种文档和绘图工具,善于表达和总结,最好是多多分享,提高“知名度”和“影响力”

        管理能力

        管理自己,可以按自己得既定目标有条不紊的前进,遇到突发事件能按优先级调整并实施新计划。

        管理团队,首先是三观端正,有一定的亲和力和影响力,能有效的和同事就问题进行商讨,有一定的说服能力,可以代领身边同事一起往正确的方向前进。

        另外附上一张学习图,希望能够帮助你:

如何通过玩TensorFlow Playground来理解神经网络

       单个神经元的数学机制很简单,它所能做的源码只是将数据点分成两类。而众多神经元组成的源码深度神经网络能提取更多特征,解决更复杂更抽象的源码问题。(之前相关报道:业界 | 想揭开深度学习隐藏层的源码神秘面纱?试试Tensor Flow的神经网络游乐场)源码:htt如何通过玩TensorFlow Playground来理解神经网络

swift 教程 swift介绍

       1、要使用swift须拥有一台苹果电脑。源码linux实例源码下载因为集成开发环境XCode只能运行在OS X系统上。源码电脑系统必须在OS .9.3及以上,源码电脑必须安装Xcode集成开发环境。源码

       2、源码下载完成后,源码双击下载的源码 dmg 文件安装,安装完成后我们将 Xcode 图标踢移动到应用文件夹。源码Xcode 安装完成后,源码就可以开始编写 Swift 代码了。源码接下来我们在应用文件夹打开 Xcode,打开后在屏幕顶部选择 File => New => Playground。接着 为 playground 设置一个名字并选择 iOS 平台。

       3、如果你想创建 OS x 程序,需要导入 Cocoa 包 import Cocoa以上程序载入后,会在Playground 窗口右侧显示程序执行结果。

       4、选择一个Single View Application,并点击next,创建一个简单示例app应用。

       5、接着我们输入项目名称(ProductName),公司名称(Organization Name),公司标识前缀名(Organization identifier) 还要选择开发语言(Language),选择设备(Devices)。其中Language有两个选项:Objective-c和swift,因为我们是学习swift当然选择swift项了。 点击Next下一步。

       6、选择存放的目录,如果要使用Git源代码管理,将勾上Source Control的create git repository on My Mac. 点击create创建项目。

       7、项目创建后,默认生成了一个示例文件,可以看到swift将oc中的h和m文件合并成了一个文件(即swift后缀名文件). Main.storyboard相当于xib文件,有比xib更多的功能。

       8、打开main.storyboard,默认看到一个简单的空白的应用界面,大小为平板界面大小。 如果开发都只需要开发兼容iphone手机的app,那么可以把Use Auto Layout的勾去掉(默认为勾上)。弹出了一个对话框,让我们选择界面尺寸,iPhone 或 iPad。我们选择iPhone的尺寸。

       9、界面添加点内容,在右下方找到Text控件,将它拖入storyboard上,并双击写入文本Hello World!

       、运行一下模拟器(command+R 快捷键或在菜单栏中选择 Product => Run)。

       至此,第一个Swift项目就完成了。

       、swift介绍

       《Swift》是一种支持多编程范式和编译式的开源编程语言,苹果于年WWDC(苹果开发者大会)发布,用于开发 iOS,OS X 和 watchOS 应用程序。 Swift 结合了 C 和 Objective-C 的优点并且不受 C 兼容性的限制。 Swift 在 Mac OS 和 iOS 平台可以和 Object-C 使用相同的运行环境。 年6月8日,苹果于WWDC 上宣布,Swift将开放源代码,包括编译器和标准库。

       Swift 是一种全新的编程语言,结合了 C 和 Objective-C 的优点,并且不受C兼容性的限制。Swift 采用的安全编程模式添加了很多新特性,这使得编程更简单,更灵活,也更有趣。Swift 在 Foundation 和 Cocoa的基础上构建框架,并且采用了很多 Objective-C 的rot公式源码命名参数以及动态对象模型,并且支持过程式编程和面向对象编程。Swift 将现代编程语言的精华和苹果工程师文化的智慧结合了起来,既能够开发简单的小程序,也能够构建出一套完整的操作系统。

Context 使用场景&&源码解读

       本文深入探讨了Go语言中的Context机制及其在协程生命周期控制、参数传递和超时管理等场景的应用。

       Context是Go语言中用于管理和控制协程生命周期、传递全局参数的重要工具。它为开发者提供了灵活的机制,以实现协程的取消、超时控制和公共参数的传递,从而提高了程序的健壮性和可维护性。

       下面,本文将分几个部分详细介绍Context的使用场景和源码解读,以帮助读者更好地理解和应用这一机制。

       Context的使用场景

       1. **协程生命周期控制**:通过Context,可以实现对协程的取消操作,即在必要时停止协程的执行,避免资源的浪费和死锁现象。

       2. **超时控制**:在执行耗时操作时,Context可以设置超时机制,一旦超时,将自动停止执行并返回错误,避免阻塞系统。

       3. **请求跟踪与参数传递**:在多层调用或服务链路中,Context可用于传递请求ID、用户信息等全局参数,方便跟踪请求状态和上下文信息。

Context源码解读

       1. **Context接口**:定义了Context的主要方法,如Deadline、Done、Err和Value,用于获取截止时间、取消状态、错误和值等信息。

       2. **canceler接口**:定义了可取消Context的方法,如cancel方法,用于向后代Context传递取消信号。

       3. **cancelCtx结构体**:作为实现Context的核心,包含父Context、只读的无缓冲通道Done、错误信息err和直属后代Context的map children。

Context调用链路与Demo

       本文详细展示了Context的调用链路,包括Background、TODO、WithValue、WithCancel、WithDeadline和WithTimeout等方法的使用场景和效果。通过这些方法,开发者可以根据具体需求构建灵活的Context树,实现协程的精细控制和参数传递。

       Context Demo

       本文提供了一个Go Playground直接运行的Demo代码,展示了如何在实际应用中使用Context进行HTTP请求超时控制和主动取消操作。通过引入Context,开发者可以在发送HTTP请求时设置超时时间,一旦请求超时,程序将收到错误响应并中断,从而避免了长时间等待或系统阻塞的问题。

       总之,Context机制在Go语言中扮演了关键角色,为开发者提供了高效、灵活的协程管理和控制手段,有助于构建更健壮、高效的并发程序。

深入研究ast-grep的模式: 一文解析Tree-Sitter核心概念

       如果你对代码重构工具有兴趣,你可能听说过 ast-grep,它是一个基于 Tree-sitter 的工具,可以进行结构搜索和替换。 ast-grep 允许你编写代码模式,根据代码的结构,而不仅仅是文本来查找和修改代码。但它是如何在引擎盖下工作的呢?在这篇文章中,我将为你深入介绍ast-grep的模式。它还能帮助你理解Tree-sitter的看源码吗核心概念。

       模式是一种写和读描述语法树的表达式的方便方法。 它类似于代码,但有一些特殊的语法和语义,允许你根据语法树的结构、类型或内容来匹配部分。

       ast-grep的模式易学难精。它要求你了解目标语言的Tree-sitter语法和含义,以及ast-grep的规则和惯例。

       在本文中,我们将帮助你掌握所有基于Tree-sitter的工具所共有的核心概念。我们还将向你展示如何编写ast-grep模式,让它的全部威力都为你所用。

       什么是Tree-sitter? ast-grep使用 Tree-sitter作为它的底层解析框架,这是因为它的流行度、高性能和健壮性。

       Tree-sitter是一个生成解析器的工具,并提供一个增量解析库。

       解析器是一个将源代码文件作为输入并产生一个描述代码组织的树状结构的程序。(该树状结构不是抽象语法树,我们将在后面看到)。

       为各种编程语言编写优秀的解析器是一项艰巨的任务,尤其是对于像ast-grep这样的单一项目。幸运的是,Tree-sitter是一个被社区广为传唱的好工具。许多主流语言,如C、Java、JavaScript、Python、Rust等,都被Tree-sitter支持。使用Tree-sitter作为ast-grep的底层解析库,可以让这个工具支持任何具有良好维护的语法的语言。

       Tree-sitter的另一个好处是它的增量性质。增量解析器是指当源代码文件被编辑时能够有效地更新语法树的解析器,而不需要重新解析整个文件。在 ast-grep的交互式编辑中,它可以非常快速地运行在每一个代码变化上。

       最后,Tree-sitter还可以优雅地处理语法错误,它可以在同一个文件中解析多种语言。这使得模式代码的解析更加稳健,更容易编写。在未来,我们还可以像Vue那样支持多语言的源代码。

       当你使用 ast-grep 来搜索源代码中的模式时,你需要了解文本匹配和结构匹配之间的区别。

       源代码输入是文本,是遵循某些语法规则的字符序列。你可以使用常见的搜索工具,如 silver-searcher或 ripgrep来搜索源代码中的文本模式。

       然而,ast-grep并不直接与文本匹配模式。相反,它将文本解析成一个代表代码语法的树状结构。这使得ast-grep能够根据代码的语义来匹配模式,而不仅仅是其表面现象。这就是所谓的 结构搜索,它搜索的是具有特定结构的代码,而不仅仅是特定的文本。

       因此,你写的模式也必须是有效的语法,可以与代码树进行比较。

       ast-grep中的文本搜索 虽然pattern在结构上匹配代码,但你可以使用ref=" ast-grep.github.io/guid...">原子规则regex通过指定正则表达式来匹配节点的文本。这样,就可以在ast-grep中结合文本和结构匹配。

       我们可以用两种类型的树结构来表示代码的语法和语义: AST和CST。

       AST代表 抽象语法树,它是代码的简化表示,省略了一些细节,如标点符号和空白处。CST代表 具体语法树(Concrete Syntax Tree),它是运营 tv 源码对代码的一种更保真的表示,包括所有的细节。

       Tree sitter是一个库,可以将代码解析为许多编程语言的CST。因此,ast-grep与它的名字相反,是根据CST模式而不是AST来搜索和重写代码的。

       让我们通过一个例子来看看为什么CST更有意义。考虑一下1 + 1这个JavaScript片段。它的AST表示法 看起来像这样:

       精明的读者应该注意到重要的运算符+并没有在AST中被编码。与此同时,代码的CST则忠实地表达了所有的关键信息。

       你可能会想,使用CST是否会让不重要的空白影响你的搜索结果。幸运的是,ast-grep使用了一种智能匹配算法,可以在适当的时候跳过CST中的琐碎节点,帮你节省了很多麻烦。

       如果不关心标点符号和空白,我们就可以将CST转换为AST。Tree-sitter有两种类型的节点:命名节点和匿名节点(未命名节点)。

       更重要的命名节点在语法规则中被定义为常规名称,如binary_expression或identifier。不太重要的匿名节点是用字面字符串定义的,如", "或 "+"。

       命名节点对于理解代码的结构和意义更为重要,而匿名节点则不那么重要,有时会被 ast-grep 的匹配算法跳过。

       下面的例子改编自 Tree-sitter的官方指南,显示了语法定义的不同。

       实际上,命名的节点有一个叫做kind的属性,表示它们的名字。你可以使用ast-grep的ref=" ast-grep.github.io/guid...">原子规则kind来找到具体的AST节点。下面的例子的 Playground链接。

       更进一步,ast-grep的元变量默认只匹配命名的节点。return $A 只匹配下面的第一条语句。 Playground链接。

       我们可以使用两个米元符号(非笔误)$$VAR在模式匹配的结果中包括匿名节点。return $$A将匹配上面的两个语句。 Playground链接。

       有时,仅仅使用kind并不足以找到我们想要的节点。一个节点可能有几个具有相同kind的子节点,但在代码中的作用不同。例如,在JavaScript中,一个对象可能有多个键和值,但它们都可以是字符串。

       为了区分它们,我们可以使用field来指定一个节点和它的父节点之间的关系。在ast-grep中,field可以用在两个 关系规则中:has和inside。

       has和inside接受一个特殊的配置项,叫做field。field的值是父-子关系的字段名。例如,JavaScript对象中的键值对有两个孩子:一个字段是key,另一个字段value。我们可以用 这个规则来匹配string的key节点。

       field可以帮助我们缩小搜索范围,使模式更加精确。

       我们也可以用has重写上面的规则,搜索带有字符串key的键值对。 Playground链接。

       kind和field的关键区别: kind是节点本身的属性。只有命名节点才有。 field是父子关系的属性。匿名节点也可以有。

       一个节点同时拥有kind和field可能会让新用户感到困惑。kind属于节点本身,在ast-grep的Playground上用蓝色文本表示。子节点只有相对于它的父节点才有field,反之亦然。源码分析学习字段在Playground中用深**文本表示。由于field是节点关系的一个属性,匿名节点也可以有field。例如,binary_expression的1 + 1中的+的field就是operator。

       ast-grep比Tre-sitter走得更远。它有关于节点的 "重要性 "的概念。

       即使是“重要性”这个概念也是不够的。 大多数Tree-Sitter语言没有在命名节点AST中编码所有的关键语义。即使我们定义了范围更广的显著节点,也不足以表示代码的意义。我们必须保留一些琐碎节点来进行精确匹配。

       Tree-sitter一般不会用命名节点来编码所有的语义。例如,class A { get method() { } } 和 class A { method() { } } 在Tree-sitter的AST中是等同的。关键标记get没有命名,也没有field。它是一个琐碎节点!

       如果你不关心这个方法是getter方法、静态方法还是实例方法,你可以使用class $A { method() { } }来 一次性匹配所有三种方法。如果你需要区分getter方法和普通方法,你可以 拼出完整的方法修饰符。

       感谢你阅读到这里! 这篇文章中有许多概念。让我们用一段话来总结一下。

       ast-grep使用Tree-sitter将文本源代码解析成一个详细的树状结构,称为CST。我们可以从CST中得到AST,只保留命名节点,这些节点有kind。为了搜索语法树中的节点,可以同时使用节点kind和节点field,后者是一个子节点相对于其父节点的特殊标记。一个有kind或者有field的节点就是一个重要的节点。

Vue3源码解析(computed-计算属性)

       作者:秦志英

       Vue3计算属性源码解析

       在理解了Vue3响应式系统后,我们继续深入剖析其核心组件——计算属性的实现机制。Vue3中的计算属性通过computed函数提供API,让我们通过源码来揭示其内部运作。

       在ComputedRefImpl类中,有两个关键私有属性:_value用于缓存计算结果,_dirty用于标记是否需要重新计算。当属性值改变时,会触发trigger函数,遍历并执行依赖的effect函数。如果effect配置了scheduler,那么计算属性的getter并不会立即执行,而是设置_dirty为false,并通知依赖的副作用函数。

       构造函数中,我们会包装getter函数为effect,并将其添加到依赖集合中。同时,lazy和scheduler参数控制了计算属性在何时调度。让我们通过一个示例来看计算属性的完整流程:当点击按钮改变testData时,计算属性的更新流程如图所示。

       总结:计算属性特性

       计算属性的主要特性包括:其值依赖于其他属性的更新,但只有在必要时才会重新计算,且通过lazy和scheduler配置实现灵活调度。如果你对Electron感兴趣,不妨关注我们的开源项目Electron Playground,了解更多技术知识。

       我们是好未来·晓黑板前端技术团队,持续分享最新技术动态。关注我们:知乎、掘金、Segmentfault、CSDN、简书、开源中国、博客园。

PolarDB-X 源码解读(七):私有协议连接的一生(CN篇)

       通过前文的介绍,大家基本了解了一条SQL在polardbx-sql中的解析和执行流程。由于polardbx-sql是无状态的计算节点,真正数据需要从存储节点传输到计算节点,这部分工作由私有协议完成。本文将详细介绍从发送请求到存储节点,接收返回数据的完整流程,重点在于私有协议连接的生命周期和关键代码解析。

       概述

       为了提高数据节点本地计算能力,同时减少网络数据传输量,计算节点会尽可能下推计算内容。一个逻辑表可能需要多个物理分片,因此计算节点与存储节点的请求会话数量会随着分片数增加而增加。传统MySQL协议+连接池架构已不能满足PolarDB-X的需求,因此私有协议在这一需求场景下应运而生。

       如图所示,私有协议采用连接与会话分离的RPC协议设计理念,支持多个会话在同一个TCP通道中并行运行,具备流控机制、全双工响应式工作模式和高吞吐、可扩展等特性。

       更多关于私有协议解决上述问题的设计详情,可以参考《PolarDB-X私有协议设计》一文。本文主要从代码层面详细描述私有协议的工作流程。

       我们将从计算节点和存储节点两个角度完整解析私有协议连接的生命周期。篇幅限制,本文仅关注计算节点上私有协议的处理,存储节点部分将在后续文章中详细说明。

       计算节点

       计算节点作为私有协议的客户端,负责发送下推请求,并接收返回的数据。

       网络层框架

       PolarDB-X私有协议网络层采用定制化Reactor框架实现,基于Java的NIO,改进自polardbx-sql中的Reactor框架。网络层初始化时,设置CPU核心数的2倍(上限为)作为NIOProcessor,每个Reactor使用独立的堆外内存池作为收发包缓冲,总缓冲内存大小限制为堆内存大小的%。

       NIO接收的包直接调用注册的处理函数,发送数据仅写入send buf,网络写入由单独线程完成。线程优先写入TCP send buf,当无法写入时,注册OP_WRITE事件等待可写后再写入剩余内容。

       数据包的编码和解码在NIOClient中实现。为实现最佳性能,解包流程直接在堆外内存上进行,使用protobuf对流直接解析,将结果放入堆内。堆外内存被切分为KB chunk,每个Reactor独占一个chunk,连续解析和复用,最大化接收、解析效率。对于特大包,额外构造堆内大buffer接收和解析,回退标志在定时任务中重置,连续s无超大包时释放堆内内存,恢复高性能堆外KB buffer接收。

       请求发送集成在NIOClient中,writer优先尝试写入发送缓冲队列尾部的buffer,不足时新申请buffer填充并追加到队尾。buffer来自预分配的堆外缓冲池,超过chunk大小时分配堆内buf进行序列化。

       同时,NIOClient负责TCP连接的建立和断开资源释放,作为独立的底层网络资源管理实现。

       连接及会话

       网络层之后,我们聚焦连接与会话分离的具体实现。通过剥离连接及收发包的具体实现,连接和会话的管理变得更加清晰简洁。

       首先,一个TCP连接的逻辑抽象结构在XClient中实现,为避免误解,取名为client与JDBC中的Connection区别。该类管理TCP连接和并行运行的会话,负责TCP完整生命周期的管理、认证鉴权,并维护公共信息。其中,workingSessionMap记录了连接上并行运行的所有会话映射关系,可快速通过会话ID找到对应的会话抽象结构XSession。

       XSession提供了所有会话相关的请求函数和信息存储,包括执行计划请求、SQL查询请求、SQL更新请求、TSO请求、会话变量处理、数据包处理及异步唤醒等。

       连接池及全局单例管理器

       为了提高性能,TCP连接和会话的复用必不可少。由于连接和会话的解绑,连接池不仅缓存了到计算节点的TCP连接,也缓存了到计算节点的会话。

       XClientPool管理到一个存储节点的连接池,通过IP,端口,用户名三元组唯一确定目标存储节点,同时存储该节点的全部TCP连接(XClient)和建立的会话(XSession)。

       XClientPool实现存储节点会话获取,对应JDBC接口中的getConnection,同时实现连接和会话生命周期管理、连接探活、会话预分配等功能。实现单个存储节点连接池后,XConnectionManager维护目标存储节点三元组到实例连接池的映射,管理定时任务线程池,实现定时探活、会话&连接最长生命控制以及连接池预热等功能。

       JDBC兼容层

       新的SQL协议层对上层使用者要求较高,为了提高开发效率,私有协议提供兼容JDBC的使用方法,实现从JDBC平滑切换至私有协议,并支持协议热切换。

       JDBC兼容层代码目录在compatible目录下,Connection继承在XConnection文件中。提供包括DataSource、Connection、Statement、PreparedStatement、ResultSet、ResultSetMetaData在内的大部分常用接口函数实现,不支持的函数会明确抛出异常避免误用。

       整体关系

       至此,私有协议计算节点端的大部分结构已说明完成。给出一个整体的关系图。

       私有协议连接的一生(CN视角)

       了解了私有协议各层实现后,我们以发到存储节点的请求为例,完整梳理执行流程。绕开计算节点复杂流程,直接运行代码示例(注:需将com.alibaba.polardbx.rpc.XConfig#GALAXY_X_PROTOCOL设置为true)。

       直接运行playground看到预期的select 1的结果。接下来,我们深入跟踪说明。

       数据源初始化

       要使用私有协议,需要初始化对应存储节点的XDataSource。构造过程中,XDataSource会到XConnectionManager注册新的实例连接池,已存在的连接池引用计数加一。

       获取Connection

       当需要执行查询时,首先获取会话。无论是显式开启事务还是使用auto commit事务,会话都是执行请求的最小上下文。通过XDataSource的getConnection方法获取到对应存储节点的会话。XDataSource根据存储的IP,端口,用户名三元组查找到XConnectionManager中的连接池,在最高并发检查后,会话获取逻辑在XClientPool实现。首先尝试在空闲会话池中拿会话,通过重置检查和初始化后返回给调用者。大部分场景下,ConcurrentLinkedQueue提供较好的并发性能。

       在代码场景下,数据源刚新建,后台定时任务未运行,流程进入连接创建流程。会有一把大锁锁住连接池,在TCP连接未达上限且没有超时的情况下,快速新建一个XClient占坑。若超限,则进入busy waiting循环。真正的TCP connect(waitChannel)在锁外被调用,首先client以阻塞模式带超时方式connect,然后切换为非阻塞模式,round robin策略注册到NIOProcesser上,返回时,TCP连接已建立。

       为了兼顾安全和性能,连接鉴权在TCP建连后只用做一次,会话创建不需要鉴权。鉴权在initClient中完成,发送SESS_AUTHENTICATE_START_VALUE包,后续校验由回调完成。认证采用标准的MySQL认证流程,server端返回challenge值,库名、用户名和加盐hash后的密码返回给MySQL即可完成认证。

       至此,到存储节点的TCP连接已建立,创建会话是一个异步流程。在创建新XClient时,XConnection已new好,通过下断点跟进去可看到newXSession流程,分配session id,设置状态为init,将XSession绑定到XConnection上。

       最后,XConnection经过初始化(重置auto commit状态)、重置默认DB、默认字符集(lazy操作)和统计信息记录,返回给用户使用。

       发送查询请求

       拿到初始化好的兼容JDBC的Connection,为了简化流程,直接调用XConnection中的execQuery。XConnection的execQuery包装了XSession的execQuery,执行前执行了设置流式模式。

       首先记录调用信息进行统计,进入关键的initForRequest流程。XSession初始化流程lazy,仅分配session id,设置状态为Init,真正创建session时发送SESS_NEW给server,绑定新session和session id。如果session已复用,则状态为Ready。

       执行字符集更改的lazy操作,session可能在其他请求中切换字符集,根据目标字符集和当前字符集对比,决定是否发送额外的字符集更改请求。

       经过一系列变量设置、lazy DB设置和protobuf包构造,请求发送到存储节点执行。发送后,同步生成XResult负责结果解析,同时XResult按照请求顺序依次拉链表,确保结果与请求一一对应。

       请求流水线结构如下图所示,处理完成前序请求后,才能解析后续结果。

       接收结果集

       请求已发送到存储节点执行,拿到XResult,通过XResult收集查询结果集。XResult与发送请求一一对应,存储节点处理也是在会话上排队进行,不会影响流水线上其他请求的返回,保证流水线正常工作。

       首先,查看结果集处理的状态机,主要状态包括获取元数据、获取数据行、获取额外信息等,顺序固定,根据请求类型,部分环节可能被省略。报错处理贯穿整个状态机,任何报错信息都会导致状态机进入错误处理环节。

       对于非流式数据读取,请求结束时主动调用finishBlockMode将所有数据读出并缓存到rows中。对于流式执行的情况,结果集状态机消费数据包队列由XResult的next函数推动,内部函数internalFetchOneObject递归调用前序XResult,消费前序请求结果,从数据包队列中消费并推动状态机流转。

       对于查询,首先收到RESULTSET_COLUMN_META_DATA包,表示返回数据列定义,一个包表示一列。元数据包后,收到包含数据行的RESULTSET_ROW包,一个包对应一行。数据行传输完成后,server端发送RESULTSET_FETCH_DONE标示数据发送完成。请求结束前,NOTICE包用于告知客户端rows affected等信息。最后,SQL_STMT_EXECUTE_OK包标示请求结束。

       至此,完整请求处理完成,控制台应显示查询结果。

       总结

       本文详细描述了私有协议连接流程中的关键点和关键数据结构,相信通过本文描述,大家掌握了私有协议连接流程的基本点,在调试和修改使用中能够更加得心应手。虽然本文篇幅较长,但实际使用中涉及更多高级特性的使用,如多请求流水线、流控、执行计划传输、chunk结果集传输等。通过本文,我们对私有协议连接流程有了深入理解,为在实际场景中应用提供坚实基础。

谈一谈 ABI, C++ ABI, Rust ABI 的稳定性 (下)

       本文撰写于年9月2日。

       在上一篇文章中,我们讨论了C++的ABI稳定性。实际上,Rust的ABI稳定性也有着类似的含义。我们所说的ABI稳定性,指的是同一套代码的二进制在不同编译器下能够相互兼容。

       目前,Rust还没有提供稳定ABI的保证。在ABI不稳定的情况下,库通常无法保证ABI的兼容性。这也是为什么Rust经常以源代码的形式分发,所有部分都需要从源代码重新编译的原因之一(ABI不稳定,直接分发binary可能会遇到兼容性问题)。

       尽管没有给出保证,但这并不意味着每个版本都会造成破坏。

       对于数据布局,Rust提供的保证并不多。其中一个原因是Rust允许编译器重排类型中的变量位置以寻求更小的结构大小。

       在C这样的语言中,因为成员b要求A以位对齐,所以a、c之后都需要填充位的padding。

       而Rust允许编译器调换a、b、c在内存中的位置,这样可以得到更紧凑的结构。具体介绍可以参考相关文档。

       除此之外,还有一个典型的原因是Rust允许大小为0的类型,以及niche optimization。比如Option大于占用的大小和一个NonNull类型是相同的。利用NonNull中的Null值代表了不存在的情况。

       给大家展示一个常见类型布局图:

       为了与C互操作,对于Rust来说,引入了一个#[repr(C)]的标志。这个标志告诉Rust编译器,用C的布局方式来布局这个类型。对于有这些标记的类型,它们的ABI是稳定的,并且与C ABI规定的一致。

       Rust对于layout能给出的保证都在这个页面里面。其他的真的就没有办法保证了。

       Rust中允许很多同名函数,翻译成binary的时候也需要进行名字修饰。当前的修饰方式是编译器实现细节,没有给出过任何保证,部分采用了和C++ Itanium ABI相似的策略,部分采用了哈希后缀。

       Rust RFC 试图给出一个稳定的命名修饰方案,目前未默认打开。

       目前不应对Rust的命名修饰做出任何假设。

       Rust中动态派发的方法是trait。方法依照trait组织在了一起。

       对于这样的情况,Rust编译器会为impl Hello的类型生成一个虚表。对于object safe的trait,可以把一个trait想象成一个虚表。虚表的布局目前还没有保证,但不妨碍我们看看现在的实现。

       第0个指针放Destructor,后面跟着字节放size和align。再后面放具体的函数指针。

       可以看到Rust为foo生成的LLVM IR(target x-)。

       函数入参a.0和a.1分别就是"对象的地址"和"虚表的地址"。可以看到虚表指针a.1用了readonly来修饰。

       _ZNplayground3foohaee3acfccE就是被修饰过后的playground::foo的名字。

       这3行是

       后面3行也是类似

       对于一个稳定的ABI而言,虚表的布局也需要稳定。

       可以想象,破坏ABI兼容性有多么容易。当我们改变一下trait里面两个方法的顺序时,就会改变虚表的布局。之前编译的binary就不能用了,而从源代码编译则完全不受影响。

       Rust中有panic和unwind机制。对于Rust ABI稳定来说,它们也是需要被稳定的一部分。目前人们在panic与外部语言交互的方式上进行努力。

       默认是extern "Rust",也可以用extern "C"来标记某些函数需要按照C语言调用约定来。

       Rust没有稳定的ABI,稳定ABI的目标主要通过C部分来实现。有一个库abi_stable就试图做这件事情,它把Rust类型变成ABI稳定的Rust类型,再跨二进制传递。

       原则上来说,不应该跨二进制传递任何repr(Rust)的类型,因为没有办法保证其ABI兼容性。而绝大多数Rust库的作者也没有/也不会把ABI兼容性放在首要考虑的目标。

       有趣的是,因为C++的ABI已经稳定了,所以我们可以在外部模拟(猜测)C++对象的布局。这个cxx库就做了这个。

       将Rust ABI稳定化的新实验已经开始了。crABI就在试图稳定化Rust ABI,但距离真正稳定还有很长一段路要走,不过至少有人开始了努力。