Данная статья подготовлена Дмитрием Овчаренко, архитектором Департамента прикладных финансовых систем компании «Инфосистемы Джет»
Да будет унификация! Такое решение было принято при проектировании интеграционной архитектуры, связывающей CRM с другими внешними системами посредством шины на Oracle Service Bus. Помимо онлайн-интеграции на основе веб-сервисов, она принимает файлы, поступающие в систему, и вызывает веб-сервисы на стороне CRM, специально разработанные для каждого типа входящих данных.
Файл содержит множество записей, и по каждой требуется выполнить отдельный вызов сервиса на стороне CRM. Обработка файла производится в цикле по записям. На каждый вызов сервиса уходит по 5 секунд – это довольно много, но для выполнения поставленных требований вполне хватало. Процесс обработки вызова веб-сервиса в CRM предварительно проверяет запись на дубль, затем выполняет требуемую бизнес-логику и создает запись в БД.
Но «внезапности» могут возникнуть в непредвиденных моментах «шиномонтажа». На промышленных объемах данных в базе CRM стали появляться дубли. Мы выяснили, что источник может почему-то отправить большой файл повторно (сразу после того, как он будет подхвачен файловым proxy-сервисом и помещен в Stage-папку). Причем отставание между вызовами веб-сервисов, создающих дубли, настолько мало, что в момент второго вызова данные в первом еще не закоммичены, и проверка на стороне CRM не успевает срабатывать.
К черту унификацию! Решили реализовывать отдельный Statefull-сервис на Java, хранящий в памяти имена обрабатываемых в текущий момент файлов. При получении файла proxy-сервис на OSB вызывает Java-сервис, передает ему имя файла и узнает, обрабатывается ли он сейчас. В конце обработки файла (или в случае исключения) proxy-сервис сообщает Java-сервису о том, что он закончил обрабатывать файл с данным именем. Соответственно, это имя из списка текущих надо удалить. Обработка двух файлов с одинаковым именем недопустима по бизнес-требованиям. Рестарт сервера тоже не страшен, поскольку будет сброшен не только Java-процесс, но и обрабатываемый файл, если таковой окажется во время перезагрузки.
В общем случае в Oracle SOA Suite Service Bus 11g нет возможности установить блокировку на процесс с синхронизацией по какому-либо идентификатору – предлагается использовать подобный подход не только для ограничения запуска при повторном получении файла, но в принципе в любых случаях, передавая в Statefull-сервис нужный идентификатор.
При этом важно, чтобы в файловом обработчике на OSB был блок Catch верхнего уровня, для того чтобы удалить имя обрабатываемого файла из списка, даже если произошло исключение.
Код сервиса:
Данный метод очень простой, но, к сожалению, не будет действовать в кластерном варианте развертывания. Также желательно разворачивать его на OSB Managed-сервере WebLogic, чтобы при перезагрузке сбросились одновременно и процессы обработки OSB, и список текущих процессов. Если OSB серверы находятся в кластере, сервис должен быть развернут на отдельном Managed-сервере, и при перезагрузке об этом факте придется помнить.
В заключение. С такой защитой от повторных обработок решение работало больше года, однако ему на смену пришел новый механизм загрузки. Он реализован на Oracle SOA Suite, а обработка записей в нем выполняется параллельно. Но эту тему раскроет уже другая статья.
Мы будем рады вашим конструктивным комментариям.
Да будет унификация! Такое решение было принято при проектировании интеграционной архитектуры, связывающей CRM с другими внешними системами посредством шины на Oracle Service Bus. Помимо онлайн-интеграции на основе веб-сервисов, она принимает файлы, поступающие в систему, и вызывает веб-сервисы на стороне CRM, специально разработанные для каждого типа входящих данных.
Файл содержит множество записей, и по каждой требуется выполнить отдельный вызов сервиса на стороне CRM. Обработка файла производится в цикле по записям. На каждый вызов сервиса уходит по 5 секунд – это довольно много, но для выполнения поставленных требований вполне хватало. Процесс обработки вызова веб-сервиса в CRM предварительно проверяет запись на дубль, затем выполняет требуемую бизнес-логику и создает запись в БД.
Но «внезапности» могут возникнуть в непредвиденных моментах «шиномонтажа». На промышленных объемах данных в базе CRM стали появляться дубли. Мы выяснили, что источник может почему-то отправить большой файл повторно (сразу после того, как он будет подхвачен файловым proxy-сервисом и помещен в Stage-папку). Причем отставание между вызовами веб-сервисов, создающих дубли, настолько мало, что в момент второго вызова данные в первом еще не закоммичены, и проверка на стороне CRM не успевает срабатывать.
К черту унификацию! Решили реализовывать отдельный Statefull-сервис на Java, хранящий в памяти имена обрабатываемых в текущий момент файлов. При получении файла proxy-сервис на OSB вызывает Java-сервис, передает ему имя файла и узнает, обрабатывается ли он сейчас. В конце обработки файла (или в случае исключения) proxy-сервис сообщает Java-сервису о том, что он закончил обрабатывать файл с данным именем. Соответственно, это имя из списка текущих надо удалить. Обработка двух файлов с одинаковым именем недопустима по бизнес-требованиям. Рестарт сервера тоже не страшен, поскольку будет сброшен не только Java-процесс, но и обрабатываемый файл, если таковой окажется во время перезагрузки.
В общем случае в Oracle SOA Suite Service Bus 11g нет возможности установить блокировку на процесс с синхронизацией по какому-либо идентификатору – предлагается использовать подобный подход не только для ограничения запуска при повторном получении файла, но в принципе в любых случаях, передавая в Statefull-сервис нужный идентификатор.
При этом важно, чтобы в файловом обработчике на OSB был блок Catch верхнего уровня, для того чтобы удалить имя обрабатываемого файла из списка, даже если произошло исключение.
Код сервиса:
@WebService(serviceName = "TaskManager")
public class TaskManager {
private static HashMap<String,String> map;
public TaskManager() {
super();
map = new HashMap<String,String>();
}
@WebMethod
@WebResult(name = "Result")
public String addTask(@WebParam(name = "ProcessName") String name){
String result;
try{
result = map.get(name); // Try to get name from map
}catch (Exception e){ // if there is no such name
result = null; // we receive exception
}
if (result == null){ // if there is no such name
map.put(name, "Is running"); // we add name
result = "Task added";
}
else{
result = "Task is running"; // else - inform caller
}
return result;
}
@WebMethod
@WebResult(name = "Result")
public String removeTask(@WebParam(name = "ProcessName") String name){
String result;
try{
result = map.get(name);
}catch (Exception e){
result = null;
}
if (result == null){
result = "There is no task";
}
else{
map.remove(name);
result = "Task removed";
}
return result;
}
}
Данный метод очень простой, но, к сожалению, не будет действовать в кластерном варианте развертывания. Также желательно разворачивать его на OSB Managed-сервере WebLogic, чтобы при перезагрузке сбросились одновременно и процессы обработки OSB, и список текущих процессов. Если OSB серверы находятся в кластере, сервис должен быть развернут на отдельном Managed-сервере, и при перезагрузке об этом факте придется помнить.
В заключение. С такой защитой от повторных обработок решение работало больше года, однако ему на смену пришел новый механизм загрузки. Он реализован на Oracle SOA Suite, а обработка записей в нем выполняется параллельно. Но эту тему раскроет уже другая статья.
Мы будем рады вашим конструктивным комментариям.