Socket.D v2.5.11

监听器应用示例

</> markdown
内置监听器
SimpleListener简单监听器Listener 的空实现
PipelineListener管道监听器Listener 的链式组织实现
EventListener事件监听器,根据消息事件路由(message::event)相当于消息的路由器
PathListener路径监听器,根据连接地址路由(session::path)相当于路径(频道)的路由器

1、SimpleListener(简单监听器)

就是一个空实现,用时只需要重写需要的方法。下面的都是链式写法,有些小伙伴可能不喜欢。

2、 EventListener(事件监听器,根据消息事件路由)

它相当于是事件(event)的路由器。可以直接在上面配置事件监听,也可以通过它做分发处理。

public class Demo {
    public static void main(String[] args) throws Throwable {
        //::启动服务端
        SocketD.createServer("sd:tcp")
                .config(c -> c.port(8602))
                .listen(new EventListener().doOnMessage((s,m)->{
                    System.out.println(m);
                    s.send("/demo", new StringEntity("Me too!"));
                }).doOn("/order", (s,m)->{ //根据消息事件路由
                    System.out.println(m); //在 onMessage 时已打印一次,这算第二次打印
                }).doOn("/user", (s,m)->{ //根据消息事件路由
                    System.out.println(m); //在 onMessage 时已打印一次,这算第二次打印
                }))
                .start();

        Thread.sleep(1000); //等会儿,确保服务端启动完成

        //::打开客户端会话
        ClientSession clientSession = SocketD.createClient("sd:tcp://127.0.0.1:8602/?u=a&p=2")
                .listen(new EventListener().doOnMessage((s, m) -> {
                    System.out.println(m);
                }).doOn("/demo", (s, m) -> { //根据消息事件路由
                    System.out.println(m);
                }))
                .open();
        
        clientSession.send("/order", new StringEntity("Hi"));
        clientSession.send("/user", new StringEntity("Hi"));
    }
}

3、 PipelineListener(管道监听器)

public class Demo {
    public static void main(String[] args) throws Throwable {
        //::启动服务端
        SocketD.createServer("sd:udp")
                .config(c -> c.port(8602))
                .listen(new PipelineListener().next(new EventListener().doOnMessage((s, m) -> {
                    //这里可以做拦截
                    System.out.println("拦截打印::" + m);
                })).next(new EventListener().doOnMessage((s, m) -> {
                    //这里可以做业务处理
                    System.out.println(m);
                })))
                .start();

        Thread.sleep(1000); //等会儿,确保服务端启动完成

        //::打开客户端会话
        ClientSession clientSession = SocketD.createClient("sd:udp://127.0.0.1:8602/hello?u=a&p=2")
                .open();

        clientSession.send("/demo", new StringEntity("Hi"));
    }
}

4、 PathListener(路径监听器,根据连接地址路由)

它相当于是连接地址(path)的路由器,可以起到频道隔离的效果。不过,在 Broker 集群模式下可能用处不大。

public class Demo04_Router {
    public static void main(String[] args) throws Throwable {
        //::启动服务端
        SocketD.createServer("sd:tcp")
            .config(c -> c.port(8602))
            .listen(new PathListener()
                    .doOf("/", new EventListener().doOnMessage((s, m) -> {
                        //用户频道
                        System.out.println("user::" + m);
                    }))
                    .doOf("/admin", new EventListener()
                            .doOnOpen(s -> {
                                //管理员频道
                                if ("admin".equals(s.param("u")) == false) {
                                    s.close();
                                }
                            }).doOnMessage((s, m) -> {
                                System.out.println("admin::" + m);
                            })
                    )
            )
            .start();

        Thread.sleep(1000); //等会儿,确保服务端启动完成

        //::打开客户端会话
        //用户频道(链接地址的 path ,算为频道)
        ClientSession clientSession1 = SocketD.createClient("sd:tcp://127.0.0.1:8602/?u=a&p=2").open();
        clientSession1.send("/demo", new StringEntity("Hi"));

        //管理员频道(链接地址的 path ,算为频道)
        ClientSession clientSession2 = SocketD.createClient("sd:tcp://127.0.0.1:8602/admin?u=a&p=2").open();
        clientSession2.send("/demo", new StringEntity("Hi"));
    }
}

5、 定制应用

这个定制是一个真实的案例:Solon 项目通过 Socket.D 监听器,将消息转换成 Context 并交给 Handler 接口处理,从而实现 Mvc 效果。这也是 Solon Rpc 的实现方案之一。

使用 Socket.D 开发 Rpc 框架,是非常容易的事情。以下可为参考:

public class ToHandlerListener extends SimpleListener {
    private static final Logger log = LoggerFactory.getLogger(ToHandlerListener.class);

    @Override
    public void onMessage(Session session, Message message) throws IOException {
        if (Utils.isEmpty(message.event())) {
            log.warn("This message is missing route, sid={}", message.sid());
            return;
        }

        try {
            SocketdContext ctx = new SocketdContext(session, message);

            Solon.app().tryHandle(ctx);

            if (ctx.getHandled() || ctx.status() != 404) {
                ctx.commit();
            }
        } catch (Throwable e) {
            //context 初始化时,可能会出错
            //
            log.warn(e.getMessage(), e);
        }
    }
}