XMPP eJabberd 的服务发现

DavidAlphaFox · 发布于 2017年11月30日 · 325 次阅读
84794b

什么是服务发现

服务发现在微服务和容器部署的项目中会被经常提到,当我们需要远程的访问REST API或者Thrift API时,我们必须得知道服务的网络地址(IP地址和端口号)。传统的应用程序都是运行在固定的物理机器上,IP地址和端口号都是相对固定的,可以通过配置文件方式来实现不定期更新的IP地址和端口号,最常见的例子就是DNS。

从这里我们可以总结出,服务发现是记录了(大规模)分布式系统中所有服务的信息,人们或者其它服务可以据此找到这些服务。

服务发现应该具备哪些关键特性

服务发现是支撑大规模 SOA 的核心服务,它必须是高可用的,提供注册、目录和查找三大关键特性,仅仅提供服务目录是不够的。服务元数据存储是服务发现的关键,因为复杂的服务提供了多种服务接口和端口,部署环境也比较复杂。一旦服务发现组件存储了大量元数据,它就必须提供强大的查询功能,包括服务健康和其它状态的查询。

eJabberd中的服务发现

eJabberd的作为实现XMPP协议的高性能,高可用服务器,自然而然会显现XMPP的服务器发现协议XEP-0030。从XEP-0030中可以看出,XMPP将服务发现定义为一种目录逐层查找的IQ处理机制。用来发现服务器上更多的IQ功能。在XMPP中,服务发现被简称为disco,是一个被定义在http://jabber.org/protocol/disco 这个XML命名空间内的IQ处理器。

发现实体上的服务

一个实体可以提供多种服务和特性,为了得到这些信息,就需要去发现实体上的服务,所以XMPP做了下面这些设计。

要求

为了完成服务发现,实体必须提供下面这些信息

  1. 实体的身份标识。在disco中,一个实体的身份细分成多个种类(服务器、客户端、网关、目录等等)及其种类中的特殊类型(IM服务器、电话或处理的客户端、MSN网关或AIM网关、用户目录或聊天室目录等等)。这些信息帮助请求实体确定最适合放置实体最服务组或服务“桶”,(例如,可能在GUI中用合适的图标把实体显示出来)。一个实体 可以有多个身份。当提供多个identity元素的时候,每个identity元素的 name 属性应该有相同的值。 1.实体的特性和协议。这个信息帮助请求实体测定对目标实体可以做什么样的动作(注册、搜索、联合等等),实体支持什么样的协议,以及是否有感兴趣的特性类型(例如,为了特性协商的目的)。

查询和响应

disco为了让请求实体,发现目标实体,做了这样的规定,必须向特定实体发送一个IQ,其中query的XML命名空间为http://jabber.org/protocol/disco

<iq type='get'
    from='[email protected]/orchard'
    to='plays.shakespeare.lit'
    id='info1'>
  <query xmlns='http://jabber.org/protocol/disco#info'/>
</iq>

收到该IQ 请求之后,disco必须做出下面三种回应中的一种

查询成功,返回实体锁能提供的服务,以及特性

<iq type='result'
    from='plays.shakespeare.lit'
    to='[email protected]/orchard'
    id='info1'>
  <query xmlns='http://jabber.org/protocol/disco#info'>
    <identity
        category='conference'
        type='text'
        name='Play-Specific Chatrooms'/>
    <identity
        category='directory'
        type='chatroom'
        name='Play-Specific Chatrooms'/>
    <feature var='http://jabber.org/protocol/disco#info'/>
    <feature var='http://jabber.org/protocol/disco#items'/>
    <feature var='http://jabber.org/protocol/muc'/>
    <feature var='jabber:iq:register'/>
    <feature var='jabber:iq:search'/>
    <feature var='jabber:iq:time'/>
    <feature var='jabber:iq:version'/>
  </query>
</iq>

发现异常,实体可能不存在的时候,会返回这些信息

<iq type='error'
    from='plays.shakespeare.lit'
    to='[email protected]/orchard'
    id='info1'>
  <query xmlns='http://jabber.org/protocol/disco#info'/>
  <error type='cancel'>
    <item-not-found xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
  </error>
</iq>

实体不能正常服务

<iq type='error'
    from='plays.shakespeare.lit'
    to='[email protected]/orchard'
    id='info1'>
  <query xmlns='http://jabber.org/protocol/disco#info'/>
  <error code='503' type='cancel'>
    <service-unavailable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
  </error>
</iq>

发现所有实体

有时候,客户端需要知道,服务器都支持哪些扩展,就需要查询下,现在有多少可发现实体在服务器上。

基本要求

请求实体为了发现一个服务器上的所有实体, 必须向目标实体发送类型为get的IQ请求,其中包含一个空的元素,其中query的XML命名空间 为 http://jabber.org/protocol/disco#items

查询和响应

查询非常简单,唯一的区别就是查询对象直接指向host,查询的命名空间变为 http://jabber.org/protocol/disco#items

<iq type='get'
    from='[email protected]/orchard'
    to='shakespeare.lit'
    id='items1'>
  <query xmlns='http://jabber.org/protocol/disco#items'/>
</iq>

服务端收到请求,就必须做出下面的回应

<iq type='result'
    from='shakespeare.lit'
    to='[email protected]/orchard'
    id='items1'>
  <query xmlns='http://jabber.org/protocol/disco#items'>
    <item jid='people.shakespeare.lit'
          name='Directory of Characters'/>
    <item jid='plays.shakespeare.lit'
          name='Play-Specific Chatrooms'/>
    <item jid='mim.shakespeare.lit'
          name='Gateway to Marlowe IM'/>
    <item jid='words.shakespeare.lit'
          name='Shakespearean Lexicon'/>
    <item jid='globe.shakespeare.lit'
          name='Calendar of Performances'/>
    <item jid='headlines.shakespeare.lit'
          name='Latest Shakespearean News'/>
    <item jid='catalog.shakespeare.lit'
          name='Buy Shakespeare Stuff!'/>
    <item jid='en2fr.shakespeare.lit'
          name='French Translation Service'/>
  </query>
</iq>

eJabberd是如何实现的

eJabberd将XEP-0030,作为一个常规的扩展,在mod_disco中进行了实现。

mod_disco模块

mod_disco会在启动的时候建立一系列的命名,公开且键值有序的ETS,用来保存IQ的特性。同时注册host上最基本的三个IQ特性,iq,presence和presence-invisible。并同时在ejabberd_local和ejabberd_sm中注册IQ处理器,为什么这么做请参考 eJabberd 的消息路由,因为在eJabberd_local中主要处理JID中没有user部分的请求,而eJabberd_sm主要负责JID带有User部分的。

每当我们在ejabberd_local上注册IQ处理器的时候,会自动注册到mod_disco上

handle_info({register_iq_handler, Host, XMLNS, Module, Function}, State) ->
    ets:insert(?IQTABLE, {{XMLNS, Host}, Module, Function}),
    catch mod_disco:register_feature(Host, XMLNS),
    {noreply, State};
handle_info({register_iq_handler, Host, XMLNS, Module, Function, Opts}, State) ->
    ets:insert(?IQTABLE, {{XMLNS, Host}, Module, Function, Opts}),
    catch mod_disco:register_feature(Host, XMLNS),
    {noreply, State};

当我们收到info和items查询的时候,会自动去调用mod_disco的process_local_iq_和process_sm_iq_这几个函数。

总结

XMPP的服务发现机制disco,主要是通过目录查询的方式来完成的。并且这种发现机制是针对IQ处理器而设置的,换句话说,在扩展eJabberd的时候仍然需要编写Erlang代码并编译到eJabberd的服务器中。虽然有很多不便利之处,但是却已经让eJabberd服务器(XMPP协议)具有极强的扩展能力。甚至可以使用eJabberd做一个购物APP的后端,这已经超出了一个通讯服务器的能力范围之外,因此拥有了disco协议之后,eJabberd就可以说是一个超级服务器了。后面将会继续介绍如何使用外部服务来扩展eJabberd。

在这里主要介绍了如何用disco发现服务器上的资源,作为对等实体disco是可以发现客户端上的资源,具体请看XEP-0030规范。

暂无回复。
需要 登录/注册 后方可回复, 如果你还没有账号请点击这里 注册