1 RPCs
在常规的Netconf/YANG使用情况下,RPCs被用来模块化Netconf服务器向Netconf客户端提供的功能和API。在Controller SAL中,RPCs被用来模块化Providers提供的功能并提供给Consumers使用。
RPCs用来模块化可被 使用北向插件的Consumers(应用程序)调用的功能。RPC可以模块化所有的功能,但通常模块化不能被抽象成配置数据的功能,如:PacketOut,初始化新会话(控制器-设备的会话)等。RPC相关的接口和方法实际的定义和使用是在Netconf模块中。
https://wiki.opendaylight.org/view/OpenDaylight_Controller:MD-SAL:Southbound_Plugin_Development_Guide#RPCs
1.1 使用方法
RPCs使用rpc语句进行模块化:
Rpc foo{ } |
这映射到方法
RPC Input
在RPC中插入input语句定义PRC Input。Input结构使用和通知、配置等相同的语句进行定义。
Rpc foo{ input{ .... } } |
RPC Output
使用RPC output语句定义RPC Output(结果的结构)
Rpc foo{ output{ ... } } |
通知
通知用来模块化来自 向Consumers曝露并被监听的网络设备或南向插件 的事件。Notification用notification语句定义。
notification foo { ... } |
配置数据
Configuration data可以很好的为协议插件和(或)网络设备模块化/提供CRUD访问;或者Configuration data可以作为配置信息(configuration)向Consumers/applications曝露,模块化所有功能。使用带true参数的config语句定义YANG中的Configuration data:
container foo { config true; ... } |
运行时 (只读)数据
Runtime(read-only)Data可以很好的为协议插件状态和(或)网络设备进行模块化/提供读访问,这种类型的数据可以很好的模块化数据统计和所有的状态数据;这些数据都不能被Consumers(应用)修改,但是必要的时候会向外曝露(如:拓扑学习,显式已连接的交换机)。使用带false的配置子语句(substatement)定义YANG模式中的Runtinme data。
container foo { config false; } |
结构元素
RPCs的结构、通知、配置数据、和运行时数据通过使用元素(数据结构节点)进行模块化。这种元素定义了访问/存储这种元素实际的XML结构,DataDom文本和Java APIs。通常这样定义:
l Container l List l Leaf l leaf-list l choice |
Augumentations
Augumentations通过不同模块提供的额外的结构扩展元素和语义进行扩展已存在的模块。Augmentation不能改变原模式中必要(mandatory)节点状态,也不能引入任何新强制的(mandatory)语句。
2.2 RPC的基本调用过程
l Consumers调用
org.opendaylight.controller.sal.core.api.Broker.ConsumerSession.rpc(QName, CompositeNode)
l Broker发现注册的RpcImplementation
l Broker调用RpcImplementation.invokeRpc(QName, CompositeNode)
l RpcImplementation处理数据并返回RpcResult
l Broker将RpcResult返回给Consumers。
如图:
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsICdzFWRoRXdvN1LclHdpZXYyd2LcBzNvwVZ2x2bzNXak9CX90TQNNkRrFlQKBTSvwFbslmZvwFMwQzLcVmepNHdu9mZvwFVywUNMZTY18CX052bm9CX90zdaVnVzIWN012YshmMjZXUYpVd1kmYr50MZV3YyI2cKJDT29GRjBjUIF2LcRHelR3LcJzLctmch1mclRXY39jM5ADN1EzMyEDMzcDM0EDMy8CX0Vmbu4GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.jpg)
对应过程为:
1:Consumer想要与SAL进行通信
2:Consumer进行会话的初始化
3:Consumer调用Broker.ConsumerSession.rpc(QName, CompositeNode)进行注册
4:Broker为发现注册的RpcImplementationConsumer建立会话
5:Broker调用RpcImplementation.invokeRpc(QName, CompositeNode)
6:RpcImplementation处理数据并返回RpcResult
7:Broker将RpcResult返回给Consumers
1.3 core-api中的RPC
在sal-core-api中与rpc相关的主要是以下几个文件:
2.2.1 RpcImplementation.java
这是Providers的RPC实现。为了向其他组件曝露RPC,providers必须为这个接口注册一个具体实现。
这个接口继承了Provider接口中的ProviderFunctionality接口;也就是说,RPC的实现就是对Provider功能的封装,在提供给Consumer(应用)使用。
定义方法invokeRpc(),通过rpc的QName和输入的节点调用相应的RPC:
ListenableFuture<RpcResult<CompositeNode>> invokeRpc(QName rpc, CompositeNode input); } |
同时在RoutedRpcDefaultImplementation接口中也声明了invokeRpc()方法,此方法与上面的方法的不同之处在于后者方法中的参数多一个InstanceIdentifier identifier这是节点实例的标识符(每个节点的实例都有一个QName以及Identifier,Indentifier功能目前未知)。但这两个接口之间不存在继承关系,方法当然不存在重写的关系。同时RoutedRpcDefaultImplementation接口中只声明了一个invokeRpc()方法。
public interface RoutedRpcDefaultImplementation { ListenableFuture<RpcResult<CompositeNode>> invokeRpc(QName rpc, InstanceIdentifier identifier, CompositeNode input); } |
2.2.2 RpcConsumptionRegistry.java
定义了一个RpcConsumerRegistry接口,该接口中只声明了一个rpc()方法,如下:
Future<RpcResult<CompositeNode>> rpc(QName rpc, CompositeNode input) |
该方法在Broker接口中的ConsumerSession接口中也声明了这个方法两个方法完全一样。
2.2.3 RpcRegistrationListener.java
全部代码如下,该接口用于监听,RPC注册事件。该接口继承了java.util.EventListener接口。这是所有事务都必须继承的接口。
import java.util.EventListener; import org.opendaylight.yangtools.yang.common.QName; public interface RpcRegistrationListener extends EventListener { public void onRpcImplementationAdded(QName name); public void onRpcImplementationRemoved(QName name); } |
2.2.4 RpcRoutingContext.java
定义了一个公共类实现两个接口
2.2.4.1 实现接口及方法重写
① 该类实现了Immutable接口,该接口中并未申明方法和变量但是用了@interface注解:
public @interface Immutable {} |
@interface是用来自定义JAVA Annotation的语法,注释中的每一个方法定义了这个注释类型的一个元素,注释中方法的声明中一定不能包含参数,也不能抛出异 常;方法的返回值被限制为简单类型、String、Class、emnus、注释,和这些类型的数组。方法可以有一个缺省值。
② 实现了Serializable接口
通过实现 java.io.Serializable 接口以启用其序列化功能。可序列化类的所有子类型本身都是可序列化的。序列化接口没有方法或字段,仅用于标识可序列化的语义。在反序列化过程中,将使用该类的公用或受保护的无参数构造方法初始化不可序列化类的字段。可序列化的子类必须能够访问无参数的构造方法。可序列化子类的字段将从该流中还原。
③ 重写方法toString()
将context和rpc中的内容转化成RpcRoutingContext [context= context, rpc= rpc ]格式,一般用作测试。
④ 重写hashCode()方法,这个看不懂
public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((context == null) ? 0 : context.hashCode()); result = prime * result + ((rpc == null) ? 0 : rpc.hashCode()); return result; } |
⑤ 重写equals()方法,将两个类进行比较,相同则返回true,不同返回false。
2.2.4.2 变量和方法
① 私有构造函数进行初始化;
② 定义私有final成员变量:serialVersionUID;
③ 定义私有方法Create(),返回值类型为RpcRoutingContext类,调用此函数时根据QName参数新建RpcRoutingContext类的对象 ;
④ 公共方法getContext(),返回值类型为QName获取context的QName,因为需要通过BundleContext注册对外提供的服务,同时也可以通过BundleContext来获得需要引用的服务;
⑤ 公共方法getRpc(),返回值类型QName,获取rpc的QName。
2.2.5 RpcProvisionRegistry.java
为RPC的实例提供注册,通过Broker提供服务,为RPC的实现提供注册。已注册的RPC功能可以被所有其他已向Broker注册的Consumers和Providers通过关联RPC的QName获得。addRpcImplementation(),addRpcRegistrationListener(),addRoutedRpcImplementation()这些方法将各种RPC实现进行注册,并返回对应类型的RpcResult。
2 QNames
常规的XML QName由本地元素名和XML命名空间组成。为了支持版本变化,添加了模块版本。
qname是qualified name 的简写构成:由名字空间(namespace)前缀(prefix)以及冒号(:),还有一个元素名称构。QName是有着特定格式的xml元素,其作用主要是增加了名字空间,比如有同样的元素名称,而名字空间不同的情况。
在YANG模式下,QName用来给定义节点名、类型、程序(procedure)或通知命名。
需要通过BundleContext注册对外提供的服务,同时也可以通过BundleContext来获得需要引用的服务;
QName=(XML命名空间,版本号,本地名):
n XML命名空间(XML namespace):YANG模式下定义的元素、类型、过程和通知的命名方式
n 版本(Revision ):YANG模块中描述元素的版本号
n 本地名:YANG模式中定义节点的标识符
QName.java中对于QName类的部分定义:
//QName的前缀,如QName“a:foo”的前缀就是“a” public String prefix; // QName的本地部分.如QName "a:foo"的本地部分就是“foo” public String localpart; //QName的元素名称,如QName“a:foo”的rawname就是“a:foo” public String rawname; //URL与QName的前缀是绑定的,这种绑定必须用XML命名空间感知处理器来处理 //The URI to which the qname prefix is bound. This binding must be performed by a XML Namespaces aware processor. public String uri; |
疑问:
1、多个QName.class文件?(RpcImplementation.java)
以上内容纯属个人总结,存在很多的不完善和错误,仅作参考和讨论用