JAVA面试题分享四百七十七:物联网短信业务服务网关解决方案

目录

前言

设计

问题一

问题二


前言

有个短信需求需要联通开放发送短信的HTTP调用能力给xxx短信服务器(简称第三方平台),第三方平台可通过调用接口发送短信给相应用户,如果接收到短信的用户有相应回复,需要通知第三方平台,实现双向互通。

但联通网关目前仅支持SGIP协议,接入的第三方平台是HTTP协议,因此需要开发一个中间协议转化服务(简称短信业务服务网关),通过进行协议转换,实现短信发送。

SGIP协议是SMGSP之间、SMGGNS之间、以及SMGSMG之间的接口协议,简称SGIP

通过应用SGIP协议,SP可以接入到SMG,实现SP应用的一点接入、全网服务;SMG可以通过SGIP协议,实现消息在不同SMG之间的路由和转发。同时SMG通过该协议也可以和GNS通信,以实现各SMGGNS之间路由表的同步功能。

设计

总体流程:

JAVA面试题分享四百七十七:物联网短信业务服务网关解决方案_第1张图片

业务流程:

JAVA面试题分享四百七十七:物联网短信业务服务网关解决方案_第2张图片

下发短信流程:

客户端建立socket,先发送一个Bind请求,解析收到的BindResp包,正常连接后可以使用submit命令发送短信,获取解析收到的submitresp包,发送成功后调用unbind命令断开连接,解析收到的UnbindResp,如果命令操作正常就关闭socket

bind的有效期60秒,有效期内可以一直调用submit请求下发短信,如果期间不发送unbind,则bind60秒后自动断开连接。

基于上面流程会存在两个问题:

  • 连接断开时需要重新建立连接

  • 建立连接期间要保证短信不丢失

这里测试的时候发现,bind有效期内,再次发送bind,返回失败,即bind期间不允许再次bind

问题一

定时任务,周期性重新连接,伪代码:

@Scheduled(fixedRate = 50000, initialDelay = 50000)
public void reconnect() throws Exception {
    sgipChannelFuture();
}

public ChannelFuture sgipChannelFuture() {
    if (null == channelFuture || !channelFuture.channel().isActive()) {
        try {
            channelFuture = bootstrap.connect(ip, port).sync();
            System.out.println("远程服务已经连接,可以进行数据交换了...");
        } catch (Exception e) {
            e.printStackTrace();
        }
    } else {
        log.info("开始销毁连接,发送unbind请求");
        channelFuture.channel().writeAndFlush("unbind");
        try {
            channelFuture = bootstrap.connect(ip, port).sync();
            System.out.println("远程服务已经连接,可以进行数据交换了...");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    return channelFuture;
}

这里介绍一种更优雅的做法:

@Bean("sgipChannelFuture")
public ChannelFuture sgipChannelFuture() {
    ChannelFuture channelFuture = null;
    try {
        Bootstrap bootstrap = new Bootstrap();
        channelFuture = bootstrap.connect("127.0.0.1", 8888).sync();
        System.out.println("远程服务已经连接,可以进行数据交换了...");
    } catch (Exception e) {
        e.printStackTrace();
    }
    return channelFuture;
}
@Scheduled(fixedRate = 50000, initialDelay = 50000)
public void reconnect(){
    sgipChannelFuture = (ChannelFuture) applicationContext.getAutowireCapableBeanFactory().getBean("sgipChannelFuture");
    if (null == sgipChannelFuture || !sgipChannelFuture.channel().isActive()) {
        DefaultSingletonBeanRegistry registry = (DefaultSingletonBeanRegistry) applicationContext.getAutowireCapableBeanFactory();
        registry.destroySingleton("sgipChannelFuture");
        registry.registerSingleton("sgipChannelFuture", client.sgipChannelFuture());
    } else {
        log.info("开始销毁连接,发送unbind请求");
        sgipChannelFuture.channel().writeAndFlush("unbind");
        DefaultSingletonBeanRegistry registry = (DefaultSingletonBeanRegistry) applicationContext.getAutowireCapableBeanFactory();
        registry.destroySingleton("sgipChannelFuture");
        registry.registerSingleton("sgipChannelFuture", client.sgipChannelFuture());
    }
}

这里有必要介绍一下DefaultSingletonBeanRegistry,部分源码:

// 单例缓存 beanName -> 单例实例
private final Map singletonObjects = new ConcurrentHashMap<>(256);

// 工厂缓存,又称二级缓存,缓存单例工厂
private final Map> singletonFactories = new HashMap<>(16);

// 三级缓存,缓存提前暴露的单例实例
private final Map earlySingletonObjects = new ConcurrentHashMap<>(16);

// 缓存以及注册单例的单例名称(按注册顺序)
private final Set registeredSingletons = new LinkedHashSet<>(256);

// 正在创建的 bean 的 name
private final Set singletonsCurrentlyInCreation =
        Collections.newSetFromMap(new ConcurrentHashMap<>(16));

// 是否允许 循环引用
private boolean singletonsCurrentlyInDestruction = false;

//...
  • 定义了大量的缓存属性,用于解决循环依赖,提高性能等

  • 其中 singletonObjects 即我们常说的单例池,所有的单例都在创建完成后缓存其中

DefaultSingletonBeanRegistrySpring Framework中的单例Bean管理。

Spring Framework中,单例Bean的管理是非常重要的一部分。Spring容器负责管理单例Bean的生命周期,并确保在应用程序中只有一个实例存在。这一管理的核心是DefaultSingletonBeanRegistry,一个用于注册和解析单例Bean的默认实现。

DefaultSingletonBeanRegistry的主要职责是维护一个单例Bean的注册表,并提供方法来注册、获取和解析单例Bean。它提供了一个通用的接口,使得开发者能够轻松地自定义单例Bean的生命周期行为。

首先,让我们来看看如何使用DefaultSingletonBeanRegistry注册单例Bean。在Spring中,我们可以使用registerSingleton方法将Bean注册到DefaultSingletonBeanRegistry中:

DefaultSingletonBeanRegistry registry = new DefaultSingletonBeanRegistry();  
registry.registerSingleton("myBean", MyBean.class);

通过 destroySingleton 方法来处理单例对象的销毁:

DefaultSingletonBeanRegistry registry = new DefaultSingletonBeanRegistry();  
registry.destroySingleton("myBean");

问题二

从发送unbind到接收到bind结果期间,短信网关接收到的http请求缓存到redis队列中,定时进行补发。

当然也可以使用其他第三方专门的消息队列,如RabblitMQ、RocketMQ

你可能感兴趣的:(JAVA,面试题分享,API网关,内容分享,java,开发语言)