0

we have a message campaign where we send over 100k messages (SMS) a day. So we are a client of SMSC server. We have no influence on SMSC server code. Before some time, we had around 80-90 message per second, now frequency dropped to 15 messages per second, according to tcpdumps.

I have few information regarding this, so I will try to explain best as I can.

So we are using Spring Boot 2.7 and open source jsmpp (3.0.0) library for sending SMS messages (PDU commands) to SMSC.

While reading about protocol (page 40), I noticed that there is a way to send messages asynchronously by providing a seqence_number. The code example is here. But I am not sure if that is going to help...

The code:

@Component
public class ClientConfig {


  @Autowired
  private MessageReceiverListener msgListener;
  @Autowired 
  private SessionStateListener sessionListener;
  private SMPPSession session;
  public String charset = "ISO-10646-UCS-2";   
  public long idleReceiveTimeout = 65000;
  public long checkBindingTimeout = 12000;
  public long timeout = 7000;
  public int enquireLinkTimeout = 15000;
  public String hostIp = "someIpAddress";
  public int port = 5000;
  public String final systemId = "someSystemId";
  public String final password = "password";
  public BindType bindType = BindType.BIND_TRX; //transceiver
  public String systemType = null;
  public String addressRange = null;
  public TypeOfNumber addrTon = TypeOfNumber.UNKNOWN;
  public NumberingPlanIndicator addrNpi = NumberingPlanIndicator.UNKNOWN;

  protected synchronized void tryToConnectToSmsc() throws Exception {
   
    try {
        // Connect to host
        BindParameter bp = new BindParameter(bindType, systemId, password, systemType, addrTon, addrNpi, addressRange);
        
        session = new SMPPSession();
        session.setEnquireLinkTimer(enquireLinkTimer);
        session.connectAndBind(host, port, bp, timeout);
        session.setMessageReceiverListener(msgListener);
        session.addSessionStateListener(sessionListener);
    }
    // Main connection failed.
    catch (Exception e) {
        //log and re-attempt connection logic here
    }
}

}

The listeners:

@Component
public class MySessionListenerImpl implements SessionStateListener {

@Override
public void onStateChange(SessionState newState, SessionState oldState, Session source) {
//TODO
 }
}

@Service
public class SmsListenerImpl implements MessageReceiverListener {
@Override
public void onAcceptDeliverSm(DeliverSm deliverSm) throws ProcessRequestException {
//TODO
}

    @Override
    public void onAcceptAlertNotification(AlertNotification alertNotification) {}
    
    @Override
    public DataSmResult onAcceptDataSm(DataSm dataSm, Session session) throws ProcessRequestException {
        return null;
    }

}

Message sending service:

@Service
public class MessageSendingServiceImpl extends ClientConfig implements MessageSendingService{
private final ESMClass esmClass = new ESMClass();
private final  byte protocolId = (byte) 0;
private final  byte priorityFlag = (byte) 1;
private final TimeFormatter formatter = new AbsoluteTimeFormatter();
private final byte defaultMsgId = (byte) 0;
 public SmsAdapterServiceImpl() {
  super();
 }

    @PostConstruct
    public synchronized void init() throws Exception {
        super.tryToConnectToSmsc();
    }
    
    @Override
    public String send(DomainObject obj){ //DomainObject -> contains fields: id, to, from, text, delivery, validity;
       String serviceType = null;
    
       //source
       TypeOfNumber sourceTON = TypeOfNumber.NATIONAL; //there is some logic here which determines if it is INTERNATIOANL, ALPHANUMERIC etc...
       NumberPlaningIndicator sourceNpi = NumberPlaningIndicator.ISDN; //constant...
       String sourcePhone = obj.getFrom();
      
       //destination
       TypeOfNumber destinationTON = TypeOfNumber.NATIONAL; //there is some logic here which determines if it is INTERNATIOANL, ALPHANUMERIC etc...
       NumberPlaningIndicator destinationNpi = NumberPlaningIndicator.ISDN; //constant...
       String destinationPhone = obj.getTo();
    
       String scheduledDeliveryTime = null;
       if (obj.getDelivery() != null) scheduledDeliveryTime = formatter.format(obj.getDelivery());
    
       String validityPeriodTime = null;
       if (obj.getValidity() != null) validityPeriodTime = formatter.format(obj.getValidity());
    
       Map<Short, OptionalParameter> optionalParameters = new HashMap<>();
       String text = obj.getText();
       if ( text.length() > 89 ) { //set text as payload instead of message text
           OctetString os = new OctetString(OptionalParameter.Tag.MESSAGE_PAYLOAD.code(), text, "ISO-10646-UCS-2"); //"ISO-10646-UCS-2" - encoding
           optionalParameters.put(os.tag, os);
           text = "";
       }
    
       String msgId = 
       session.submitShortMessage( serviceType ,
                                   sourceTON ,
                                   sourceNpi ,
                                   sourcePhone ,
                                   destinationTON ,
                                   destinationNpi ,
                                   destinationPhone ,
                                   esmClass ,
                                   protocolId ,
                                   priorityFlag ,
                                   scheduledDeliveryTime ,
                                   validityPeriodTime ,
                                   new RegisteredDelivery() ,
                                   ReplaceIfPresentFlag.DEFAULT.value() ,
                                   new GeneralDataCoding(Alphabet.ALPHA_UCS2) ,
                                   defaultMsgId ,
                                   text.getBytes("ISO-10646-UCS-2") ,
                                   optionalParameters.values().toArray(new OptionalParameter[0]));
                                  
       return msgId;                       
    }

}

Client code which invokes the service (it is actually a scheduler job):

    @Autowired private MessageSendingService messageSendingService;
    
    @Scheduled(cron)
    public void execute() {
          List<DomainObject> messages = repository.findMessages(pageable, config.getBatch()); //up to several thousand
            start(messages);
            
           ThreadPoolExecutor executorService = (ThreadPoolExecutor)                       Executors.newFixedThreadPool(getSchedulerConfiguration().getPoolSize(), new   NamedThreadFactory("Factory"));
            List<DomainObject> domainObjects = Collections.synchronizedList(messages);
            List<List<DomainObject>> domainObjectsPartitioned = partition(domainObjects.size(), config.getPoolSize()); //pool size is 4 

for (List<DomainObject> list: domainObjectsPartitioned ) {
         executorService.execute(new Runnable() {
                               @Override
                               public void run() {
                               try {
                                  start(list);
                                } catch (Exception e) {
                                  e.printStackTrace();
                                }
                               });
            }
         executorService.shutdown();

 }
}
private void start(List<DomainObject> list){
  for (DomainObject> obj : list) {
    String mid = messageSendingService.send(obj);
    //do smtg with id...
 }
}
Wrapper
  • 794
  • 10
  • 25

0 Answers0