jDiameter介绍以及使用
jDiameter是Diameter协议的开源实现(比较不幸的是AGPL 3.0协议),项目地址https://github.com/RestComm/jdiameter。
项目框架
jDiameter由它自己和ha(主要是多个java实例如何进行数据共享)组成,各自又分为api和impl两部分,api定义了接口,而impl则是实现。ha部分不在本文讨论范围。
图1
图1是一个jDiameter框架主要部分的简单示意图,一个Diameter应用由两部分组成,即base部分和application部分(如Sh应用)。在jDiameter中,由Stack负责diameter base协议部分的初始化和启动工作,Application则通过注册监听函数的方式被调用。
在Diameter协议中,各个应用即Peer是对等的,没有主从的区分,也不区分客户端和服务器。jDiameter实现中Peer表示一个对等的远端Diameter应用,PeerFSM负责Peer的状态机,TransportLayer则是负责维持一个本Peer到对等Peer的长连接。注意jDiameter中有server和client代码包,而且server代码是继承自client的,这只是实现上逻辑的划分,不表示可以只用client部分启动jDiameter。Router则表示如何在多个Peer中选择路由。AppSession包含BaseSession,表示一个会话,数据存于SessionData Store中(本地或者ha),注意有的应用如Gx/Gy有状态,而有的如Sh是无状态的。
启动
下面我们来看一个例子如何使用jDiameter实现一个Sh应用:
1 public class testServer extends ShSessionFactoryImpl implements NetworkReqListener { 2 3 private static final Logger logger = Logger.getLogger(testServer.class); 4 public static StackImpl stack; 5 public static ISessionFactory sessionFactory; 6 7 public static void main(String args[]) throws Exception { 8 PropertyConfigurator.configure("log4j.properties"); 9 testServer t = testServer.init("config.xml"); 10 t.start(); 11 while (true) { 12 Thread.sleep(10000); 13 } 14 } 15 16 public testServer(SessionFactory sessionFactory) { 17 super(sessionFactory); 18 } 19 20 public static testServer init(String fileName) { 21 try { 22 XMLConfiguration config = new XMLConfiguration(fileName); 23 stack = new StackImpl(); 24 sessionFactory = (ISessionFactory) stack.init(config); 25 testServer t = new testServer(sessionFactory); 26 Network network = stack.unwrap(Network.class); 27 network.addNetworkReqListener(t, ApplicationId.createByAuthAppId(10415, 16777217)); 28 sessionFactory.registerAppFacory(ServerShSession.class, t); 29 sessionFactory.registerAppFacory(ClientShSession.class, t); 30 return t; 31 } catch (Exception e) { 32 logger.error("Failed:", e); 33 System.exit(-1); 34 } 35 return null; 36 } 37 38 public void start() throws IllegalDiameterStateException, InternalException { 39 stack.start(); 40 }
例1
例1展示了如何初始化并启动一个Sh应用服务器(模拟HSS)。主要步骤为:
- 通过xml配置初始化Stack,得到session factory.
- 初始化app session factory,这里是ShSessionFactoryImpl,testServer继承自它。
- 注册app session listener,这里由于继承关系没有体现,可以使用如下代码:
((ShSessionFactoryImpl) appSessionFactory).setClientShSessionContext(this); ((ShSessionFactoryImpl) appSessionFactory).setClientShSessionListener(this); ((ShSessionFactoryImpl) appSessionFactory).setStateChangeListener(this);
- 注册network request listener,监听app request。
- 注册app session factory到session factory。
- 启动Stack。
配置文件
下面是上例所需的配置文件:
<?xml version="1.0"?> <Configuration xmlns="http://www.jdiameter.org/jdiameter-server"> <LocalPeer> <URI value="aaa://hss1.server.test.com:3868" /> <IPAddresses> <IPAddress value="1.1.1.1" /> </IPAddresses> <Realm value="server.test.com" /> <VendorID value="0" /> <ProductName value="hssServer" /> <FirmwareRevision value="1" /> </LocalPeer> <Parameters> <AcceptUndefinedPeer value="true" /> <DuplicateProtection value="true" /> <DuplicateTimer value="240000" /> <UseUriAsFqdn value="false" /> <QueueSize value="10000" /> <MessageTimeOut value="60000" /> <StopTimeOut value="10000" /> <CeaTimeOut value="10000" /> <IacTimeOut value="30000" /> <DwaTimeOut value="10000" /> <DpaTimeOut value="5000" /> <RecTimeOut value="10000" /> <Concurrent> <Entity name="ThreadGroup" size="64" /> <Entity name="ProcessingMessageTimer" size="16" /> <Entity name="DuplicationMessageTimer" size="16" /> <Entity name="RedirectMessageTimer" size="16" /> <Entity name="PeerOverloadTimer" size="16" /> <Entity name="ConnectionTimer" size="16" /> <Entity name="StatisticTimer" size="16" /> <Entity name="ApplicationSession" size="32"/> </Concurrent> </Parameters> <Network> <Peers> <Peer name="aaa://sh.client.test.com:3868" ip="2.2.2.2" attempt_connect="false" rating="1" /> </Peers> <Realms> <Realm name="client.test.com" peers="sh.client.test.com" local_action="LOCAL" dynamic="false" exp_time="10000"> <ApplicationID> <VendorId value="10415" /> <AuthApplId value="16777217" /> <AcctApplId value="0" /> </ApplicationID> </Realm> </Realms> </Network> <Extensions /> </Configuration>
config.xml
LocalPeer 配置自己的相关信息。
Parameters 配置的是jDiameter相关参数。
Network 配置的是其他Peer的信息。
详细结构可以参考jDiameter源码中的src/main/resources/jdiameter-server.xsd。
实现App回调
接上例我们来实现Sh应用服务器,主要有如下方法需要实现:
图2
下例是其中的一部分处理UserDataRequest:
1 @Override 2 public Answer processRequest(Request request) { 3 int code = request.getCommandCode(); 4 if (code != ProfileUpdateRequest.code && code != UserDataRequest.code && code != SubscribeNotificationsRequest.code) { 5 logger.error("Received Request with code not used by Sh!. Code[" + request.getCommandCode() + "]"); 6 return null; 7 } 8 try { 9 AppSession serverShSession = ((ISessionFactory) sessionFactory).getNewAppSession(request.getSessionId(), appId, ServerShSession.class, (Object) null); 10 ((NetworkReqListener) serverShSession).processRequest(request); 11 } catch (Exception e) { 12 logger.error("Fail to creat serverShSession", e); 13 } 14 return null; 15 } 16 17 @Override 18 public void doUserDataRequestEvent(ServerShSession session, UserDataRequest request) 19 throws InternalException, IllegalDiameterStateException, RouteException, OverloadException { 20 UserDataAnswer answer = new UserDataAnswerImpl((Request) request.getMessage(), 2001); 21 AvpSet set = answer.getMessage().getAvps(); 22 set.removeAvp(Avp.DESTINATION_HOST); 23 set.removeAvp(Avp.DESTINATION_REALM); 24 set.addAvp(Avp.USER_DATA_SH, "testdata", appId.getVendorId(), true, false, true); 25 session.sendUserDataAnswer(answer); 26 session.release(); 27 }
例2
其他回调方法类似。
jDiameter负责连接对等的Peer,接受发送消息并解析,然后通过ApplicationId往上抛到回调函数处理Application业务逻辑。
待续…