借助JArchitect探究Cassandra CLI内部机制

关系型数据库管理系统(RDBMS)是存储和使用数据最常用的系统,但对于超大量数据,这些数据库的扩展性不是很好。

\\

近年来,由于对关系型数据库替代产品的需求日益增长,NoSQL的概念已经受到广泛的欢迎。NoSQL背后的最大动机是可扩展性。NoSQL数据库解决方案提供了一种存储和使用超大量数据的方法,而且开销更小,工作量更少,性能更好,停机时间更短。

\\

Apache Cassandra是一个基于列的NoSQL数据库。它是Facebook为推动其收件箱搜索功能而开发的,后来成为Apache的开源项目。TwitterDigg、Reddit及其他许多组织都已经开始使用它。

\\

Cassandra本身提供了一个非常基础的交互式命令行接口(CLI)。开发人员可以使用CLI连接到集群中的远程节点,创建或更新模式以及设置和检索记录。

\\

对于Cassandra管理员而言,CLI是一个有用的工具。即使只提供了基础的命令,它也是一个很好的例子,从中可以知道如何实现Cassandra客户端。要开发自定义的Cassandra客户端甚至是扩展CLI工具,必须得了解CLI的内部工作原理。

\\

本文将使用JArchitect工具和CQLinq语言分析CLI的代码库,以探究CLI的架构模型。JArchitect工具用来分析代码结构,并指定设计原则,以获得更好的代码质量。借助JArchitect,软件质量可以用代码度量进行测量,用“图(graph)”和“树图(treemap)”进行可视化,并用标准和自定义的规则来执行。

\\

下面是分析得出的依赖关系图:

\\

4032cbe082628a77e35d5ae932a47f0b.png

\\

Cassandra使用了若干大家熟知的jar包,如antlr、log4j、slf4j、commons-lang,也使用了若干大家不怎么知道的jar包,比如下面这些:

\\
  • Libthrift:它是一个跨多种编程语言和用例的API,其目标是尽可能高效和无缝地实现跨语言通信和数据序列化的可靠性和高性能。\\t
  • Snakeyaml:YAML是一个为人工可读性和与脚本语言交互而设计的数据序列化格式。Cassandra的配置文件用了这一格式。\\t
  • Jackson:一个高性能JSON processor。\\t
  • Snappy:它是一个用C++编写的快速压缩/解压缩程序,最初由Google开发,snappy-java是其Java版本。\\t
  • High-scale-lib:它是一个并发和高扩展性实用程序的集合,其目的是为了直接取代包java.util.*或者java.util.concurrent.*中的集合类,而当许多CPU并发使用集合时性能更好。\

下图的矩阵图是对这些JAR文件之间依赖权重的更为详细的说明。

\\

c5e8c2472f789f5611a18d007d52c73e.png

\\

Cassandra命令行接口

\\

包org.apache.cassandra.cli实现了命令行接口逻辑,入口是CliMain类。

\\

使用下面的CQLinq查询查找main方法调用的方法:

\\
\from m in Methods where m.IsUsedBy \(\"org.apache.cassandra.cli.CliMain.main(String[])\") \select new { m, m.NbBCInstructions } 
\\

d3b39e222c05b8f2d75dd91f99dc7ecd.png

\\

Main方法用了JLine,这是一个处理控制台输入的Java库。通过它,开发人员不需要太费力就可以写出很棒的CLI应用程序。它对命令历史、Tab键自动补全、行编辑、自定义键绑定以及字符屏蔽提供了开箱即用的支持。

\\

以下是main方法用到的两个有趣的方法:

\\
  • connect:该方法用于连接Cassandra数据库服务器。\\t
  • processStatetementInteractive:该方法用于执行来自用户的命令。\

9c4a192221655ef1add2f97cd16f1c78.png

\\

CLI与Cassandra服务器之间的通信

\\

在与Cassandra服务器进行交互之前,客户端必须使用connect方法连接到服务器。

\\

下面的查询查找connect方法直接或间接使用的所有方法:

\\
\from m in Methods\let depth0 = m.DepthOfIsUsedBy(\"org.apache.cassandra.cli.CliMain.connect\(String,int)\")\where depth0 \u0026gt;= 0 orderby depth0\select new { m, depth0 }
\\

ab0cb85913314ec21636cf380722900c.png

\\

CLI使用Thrift库与服务器进行通信,Thrift允许开发人员用一个简单的定义文件定义数据类型和服务接口。将这个文件作为输入,编译器会生成代码,开发人员可以用这些代码轻松地构建RPC客户端和服务器,从而实现跨编程语言的无缝通信。无需编写大量的样板代码来序列化和传输对象以及调用远程代码,开发人员就可以马上开始处理业务。

\\

下面是Thrift服务器实现的一个简单例子:

\\

Thrift服务器实现了org.apache.thrift.server.TServer接口,其构造函数接收一个processor和一个服务器传输规范类的实例作为参数。Processor需要一个handler来处理传入的请求。

\\

让我们在Cassandra服务器中找出所有的这些组成部分。为了做到这一点,我们可以从找出所有继承了TServer类的类开始。

\\
\public class Server {\       public static class SomethingHandler implements Something.Iface {\              public SomethingHandler() {}\              public int ping() {\                    return 1;\                     }\       }\public static void main(String [] args) {\       SomethingHandler handler = new SomethingHandler();\       Something.Processor processor = new Something.Processor(handler);\       TServerTransport serverTransport = new TServerSocket(9090);\       TServer server = new TSimpleServer(processor, serverTransport);\       //下面的语句适用于多线程服务器\       // server = new TThreadPoolServer(processor, serverTransport)\       server.serve();\}\}
\\

Thrift服务器实现了org.apache.thrift.server.TServer接口,其构造函数接收一个processor和一个服务器传输规范类的实例作为参数。Processor需要一个handler来处理传入的请求。

\\

让我们在Cassandra服务器中找出所有的这些组成部分。为了做到这一点,我们可以从找出所有继承了TServer类的类开始。

\\
\from t in Types\let depth0 = t.DepthOfDeriveFrom(\"org.apache.thrift.server.TServer\")\where depth0 \u0026gt;= 0 orderby depth0\select new { t, depth0 }
\\

c7f35b6a7608d020c83772778d5bc223.png

\\

Cassandra定义了如下类:

\\

CustomTThreadPoolServer:它是Apache Thrift类TThreadPoolServer的一个稍作修改的版本。TThreadPoolServer类会使用一个线程池为传入的请求提供服务。

\\

CustomerTHsHaServer:该服务器的目的是避免IO粘在一个CPU上。为了获得更大的吞吐量,它将IO任务分配给多个线程。Selector线程的数目可以与可用的CPU数目相同。

\\

CustomTNonBlockingServer:它使用一个非阻塞的套接字传输方式。

\\

以下是ThriftServer启动时所做的工作:

\\

692f21008fadb5e995866a93e0bc5701.png

\\

该过程会使用工厂类创建一个TServer,并创建一个CassandraServer handler来处理传入的请求,后者实现了Cassandra.Iface接口,其中包含了Cassandra支持的所有命令。下图展示了该接口的部分方法:

\\

bb32139fef9b25c9c99bd2a95c0d36a2.png

\\

正如前面的Thrift服务器示例所示,需要用processor处理传入的请求;所有这些processor都继承了ProcessFunction类。

\\

下面是部分Cassandra processor:

\\

82013e2636d42ced43aeb22887e93a11.png

\\

在找出Cassandra Thrift服务器的组成部分之后,让我们回到客户端,找出main方法调用connect方法时发生了什么。

\\

5a3382f16ae0d89c90812ca9b8a75e81.png

\\

org.apache.thrift.TServiceClient用于客户端和服务器之间的通信,它调用sendBase方法向Thrift服务器发送消息。

\\

在服务器上,login processor接收这一请求,并调用login方法。

\\

91828d232538ca411387805def8763c7.png

\\

下面的依赖关系图展示了login方法调用的部分方法。

\\

321b144d10bd124b06f211033d5129ac.png

\\

下面会通过添加一个新方法MyMethod进入到扩展CLI的步骤。

\\

在查明CLI的内部工作原理之后,我们可以很容易地给它添加新方法,下面是所需的主要步骤:

\\

I- 扩展服务器:

\\
  • Cassandra.Iface接口中添加方法\\t
  • CassandraServer类中添加该方法的实现\\t
  • 新建一个继承ProcessFunction\u0026lt;T\u0026gt;的类Cassandra.Processor.MyMethod\u0026lt;I\u0026gt;。\\t
  • Cassandra.Processor\u0026lt;I\u0026gt;.getProcessMap方法返回的Map中添加上一步中新建的processor的一个实例。\

II- 扩展客户端:

\\
  • 新增一个开关项,并在CliOptions.processArgs方法中对其进行处理。\\t
  • Cassandra.Client类中添加一个方法,并使用TServiceClient.sendBase方法向服务器发送请求。\

结论

\\

学习如何实现Cassandra客户端,命令行接口是一个很好的例子,而且从现实项目中学习要好过只是在网上搜索例子。因此,要开发Cassandra客户端的话,就要毫不犹豫地仔细阅读它的源代码,并享受其中的乐趣。

\\

关于作者

\\

Dane Dennis是JArchitect产品经理。他为CoderGears工作,这是一家为开发人员和架构师开发工具的公司。

\\

查看英文原文:Cassandra CLI Internals Using JArchitect

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值