-Поиск по дневнику

Поиск сообщений в rss_rss_hh_new

 -Подписка по e-mail

 

 -Статистика

Статистика LiveInternet.ru: показано количество хитов и посетителей
Создан: 17.03.2011
Записей:
Комментариев:
Написано: 51

Habrahabr/New








Добавить любой RSS - источник (включая журнал LiveJournal) в свою ленту друзей вы можете на странице синдикации.

Исходная информация - http://habrahabr.ru/rss/new/.
Данный дневник сформирован из открытого RSS-источника по адресу http://feeds.feedburner.com/xtmb/hh-new-full, и дополняется в соответствии с дополнением данного источника. Он может не соответствовать содержимому оригинальной страницы. Трансляция создана автоматически по запросу читателей этой RSS ленты.
По всем вопросам о работе данного сервиса обращаться со страницы контактной информации.

[Обновить трансляцию]

[Из песочницы] Язык программирования SPL — пример решения задачи

Воскресенье, 11 Июня 2017 г. 14:32 + в цитатник
В этой статье я расскажу о том, как можно в языке программирования SPL решить классическую задачу: получить список наиболее часто встречающихся в тексте слов. В качестве образца текста возьмем произведение Шекспира Гамлет.

Далее я сразу приведу пример программы и результат ее работы, а потом подробно разберем все шаг за шагом.

Текст программы:

text = #.readtext("hamlet.txt")
words = #.split(text, " ", ".", ",", ";", "'", "!", "?", "-", "(", ")", "[", "]", #.crlf, #.quot)
> i, 1..#.size(words)
  >> words[i] = ""
  key = #.lower(words[i])
  dict[key] += 1
  total += 1
<
#.sortval(dict)
#.reverse(dict)
#.output(total, " слов; ", #.size(dict), " уникальных слов")
> i, 1..10
  key = dict[i]
  #.output(i, " : ", key, " = ", dict[key])
<

Результат работы программы:

32885 слов; 4634 уникальных слов
1 : the = 1091
2 : and = 969
3 : to = 767
4 : of = 675
5 : i = 633
6 : a = 571
7 : you = 558
8 : my = 520
9 : in = 451
10 : it = 421

Теперь разберем подробнее как это работает.

Первая строка:

text = #.readtext("hamlet.txt")

считывает текст файла «hamlet.txt» в переменную «text».

Затем в строке:

words = #.split(text, " ", ".", ",", ";", "'", "!", "?", "-", "(", ")", "[", "]", #.crlf, #.quot)

функция "#.split" делит текст «text» на отдельные слова с помощью указанных разделителей и сохраняет результат в массив «words». В списке разделителей также присутствуют системные константы "#.crlf" и "#.quot", которые обозначают символы CRLF (перевод строки) и кавычки ".

Далее идет цикл, который начинается командой ">". В первой строке цикла:

> i, 1..#.size(words)

указано, что циклить он будет по переменной «i», которая меняется от 1 до количества слов в массиве «words», которое возвращает функция "#.size".

В следующей строке:

>> words[i] = ""

стоит команда перехода на начало цикла ">>" при условии, что очередное слово «words[i]» — не пустое. Это для того, чтобы не учитывать пустые слова, которые получились при делении текста.

Затем в текстовой переменной «key» получаем очередное слово в нижнем регистре благодаря функции "#.lower":

key = #.lower(words[i])

и следующая строка:

dict[key] += 1

выполняет основную работу — в запись из словаря «dict» по ключу «key» прибавляется 1, таким образом подсчитывая количество каждого слова.

В строке:

total += 1

подсчитывается общее количество слов, которые были учтены, и результат хранится в переменной «total».

Следующая строка:

<

это конец цикла.

Теперь сортируем словарь «dict» по значению:

#.sortval(dict)

Сортировка производится в порядке возрастания, поэтому в следующей строке:

#.reverse(dict)

словарь реверсируется в обратный порядок, по убыванию.

В принципе, работа сделана, нужно напечатать результат. Следующая строка выдает немного статистики:

#.output(total, " слов; ", #.size(dict), " уникальных слов")

где размер словаря «dict», возвращаемый функцией "#.size", дает нам количество уникальных слов.

Следующий цикл:

> i, 1..10

выводит 10 наиболее часто употребляемых слов.

Эта строка:

key = dict[i]

получает очередной ключ словаря, который и есть слово,

а следующая строка:

#.output(i, " : ", key, " = ", dict[key])

печатает и это слово, и сколько раз оно встречается в тексте. Таким образом, обращение к словарю по числовому индексу возвращает нам ключ записи с этим порядковым индексом, а обращение к словарю по текстовому ключу возвращает нам значение записи, которым является число — сколько раз это слово встретилось в тексте.

Последняя команда:

<

закрывает цикл.

Как видно из этого примера, SPL полностью автоматически определяет тип всех объектов — числовых и текстовых переменных, массивов, а также других объектов. При работе со словарем добавление новых записей производится автоматически.

Спасибо за внимание и успехов в программировании!
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/330678/


Метки:  

[Из песочницы] Клиент-сервер шаг — за — шагом, от однопоточного до многопоточного

Воскресенье, 11 Июня 2017 г. 12:15 + в цитатник

Цель публикации показать начинающим Java программистам все этапы создания многопоточного сервера. Для полного понимания данной темы основная информация содержится в комментариях моего кода и в выводимых в консоли сообщениях для лучшего понимания что именно происходит и в какой именно последовательности.


В начале будет рассмотрено создание элементарного клиент-сервера, для усвоения базовых знаний, на основе которых будет строиться многопоточная архитектура.


Понятия.


— Потоки: для того чтобы не перепутать что именно подразумевается под потоком я буду использовать существующий в профессиональной литературе синоним — нить, чтобы не путать Stream и Thread, всё таки более профессионально выражаться — нить, говоря про Thread.


— Сокеты(Sockets): данное понятие тоже не однозначно, поскольку в какой-то момент сервер выполняет — клиентские действия, а клиент — серверные. Поэтому я разделил понятие серверного сокета — (ServerSocket) и сокета (Socket) через который практически осуществляется общение, его будем называть сокет общения, чтобы было понятно о чём речь.


 Кроме того сокетов общения создаётся по одному на каждом из обменивающихся данными приложении, поэтому сокет приложения которое имеет у себя объект - ServerSocket и первоначально открывает порт в ожидании подключения будем называть сокет общения на стороне сервера, а сокет который создаёт подключающееся к порту по известному адресу второе приложение будем называть сокетом общения на стороне клиента.

Оглавление:


1) Однопоточный элементарный сервер.
2) Клиент.
3) Многопоточный сервер – сам по себе этот сервер не участвует в общении напрямую, а лишь является фабрикой однонитевых делигатов(делигированных для ведения диалога с клиентами серверов) для общения с вновь подключившимися клиентами, которые закрываются после окончания общения с клиентом.
4) Имитация множественного обращения клиентов к серверу.


Итак начнём с изучения структуры однопоточного сервер, который может принять только одного клиента для диалога. Код приводимый ниже необходимо запускать в своей IDE в этом идея всей статьи. Предлагаю все детали уяснить из подробно задокументированного кода ниже:


  • 1) Однопоточный элементарный сервер.

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

public class TestAsServer {

/**
 * 
 * @param args
 * @throws InterruptedException
 */
    public static void main(String[] args) throws InterruptedException {
//  стартуем сервер на порту 3345

        try (ServerSocket server= new ServerSocket(3345);){
// становимся в ожидание подключения к сокету под именем - "client" на серверной стороне                                
                Socket client = server.accept();

// после хэндшейкинга сервер ассоциирует подключающегося клиента с этим сокетом-соединением             
                System.out.print("Connection accepted.");

// инициируем каналы для  общения в сокете, для сервера     

                // канал чтения из сокета
                DataInputStream in = new DataInputStream(client.getInputStream());
                System.out.println("DataInputStream created");

                // канал записи в сокет
                DataOutputStream out = new DataOutputStream(client.getOutputStream());
                System.out.println("DataOutputStream  created");

// начинаем диалог с подключенным клиентом в цикле, пока сокет не закрыт                
                while(!client.isClosed()){

                System.out.println("Server reading from channel");

// сервер ждёт в канале чтения (inputstream) получения данных клиента               
                String entry = in.readUTF();

// после получения данных считывает их              
                System.out.println("READ from client message - "+entry);

// и выводит в консоль              
                System.out.println("Server try writing to channel");

// инициализация проверки условия продолжения работы с клиентом по этому сокету по кодовому слову       - quit  
                if(entry.equalsIgnoreCase("quit")){
                    System.out.println("Client initialize connections suicide ...");
                    out.writeUTF("Server reply - "+entry + " - OK");                
                    Thread.sleep(3000);
                    break;
                }

// если условие окончания работы не верно - продолжаем работу - отправляем эхо-ответ  обратно клиенту               
                out.writeUTF("Server reply - "+entry + " - OK");                
                System.out.println("Server Wrote message to client.");

// освобождаем буфер сетевых сообщений (по умолчанию сообщение не сразу отправляется в сеть, а сначала накапливается в специальном буфере сообщений, размер которого определяется конкретными настройками в системе, а метод  - flush() отправляет сообщение не дожидаясь наполнения буфера согласно настройкам системы             
                out.flush();    

                }

// если условие выхода - верно выключаем соединения             
                System.out.println("Client disconnected");
                System.out.println("Closing connections & channels.");

                // закрываем сначала каналы сокета !
                in.close();
                out.close();

                // потом закрываем сам сокет общения на стороне сервера!
                client.close();

                // потом закрываем сокет сервера который создаёт сокеты общения
                // хотя при многопоточном применении его закрывать не нужно
                // для возможности поставить этот серверный сокет обратно в ожидание нового подключения
                server.close();
                System.out.println("Closing connections & channels - DONE.");
            } catch (IOException e) {
                e.printStackTrace();
        }
    }
}

  • 2) Клиент.

Сервер запущен и находится в блокирующем ожидании server.accept(); обращения к нему с запросом на подключение. Теперь можно подключаться клиенту, напишем код клиента и запустим его. Клиент работает когда пользователь вводит что-либо в его консоли (внимание! в данном случае сервер и клиент запускаются на одном компьютере с локальным адресом — localhost, поэтому при вводе строк, которые должен отправлять клиент не забудьте убедиться, что вы переключились в рабочую консоль клиента!). После ввода строки в консоль клиента и нажатия enter строка проверяется не ввёл ли клиент кодовое слово для окончания общения дальше отправляется серверу, где он читает её и то же проверяет на наличие кодового слова выхода. Оба и клиент и сервер получив кодовое слово закрывают ресурсы после предварительных приготовлений и завершают свою работу. Посмотрим как это выглядит в коде:


import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
import java.net.UnknownHostException;

public class TestASClient {

    /**
     * 
     * @param args
     * @throws InterruptedException
     */
    public static void main(String[] args) throws InterruptedException {

// запускаем подключение сокета по известным координатам и нициализируем приём сообщений с консоли клиента      
        try(Socket socket = new Socket("localhost", 3345);  
                BufferedReader br =new BufferedReader(new InputStreamReader(System.in));
                DataOutputStream oos = new DataOutputStream(socket.getOutputStream());
                DataInputStream ois = new DataInputStream(socket.getInputStream()); )
        {

            System.out.println("Client connected to socket.");
            System.out.println();
            System.out.println("Client writing channel = oos & reading channel = ois initialized.");            

// проверяем живой ли канал и работаем если тру         
                while(!socket.isOutputShutdown()){

// ждём консоли клиента на предмет появления в ней данных                   
                    if(br.ready()){

// данные появились - работаем                      
            System.out.println("Client start writing in channel...");
            Thread.sleep(1000);
            String clientCommand = br.readLine();

// пишем данные с консоли в канал сокета для сервера            
            oos.writeUTF(clientCommand);
            oos.flush();
            System.out.println("Clien sent message " + clientCommand + " to server.");
            Thread.sleep(1000);
// ждём чтобы сервер успел прочесть сообщение из сокета и ответить      

// проверяем условие выхода из соединения           
            if(clientCommand.equalsIgnoreCase("quit")){

// если условие выхода достигнуто разъединяемся             
                System.out.println("Client kill connections");
                Thread.sleep(2000);

// смотрим что нам ответил сервер на последок перед закрытием ресурсов          
                if(ois.available()!=0)      {   
                    System.out.println("reading...");
                    String in = ois.readUTF();
                    System.out.println(in);
                            }

// после предварительных приготовлений выходим из цикла записи чтения               
                break;              
            }

// если условие разъединения не достигнуто продолжаем работу            
            System.out.println("Client sent message & start waiting for data from server...");          
            Thread.sleep(2000);

// проверяем, что нам ответит сервер на сообщение(за предоставленное ему время в паузе он должен был успеть ответить)           
            if(ois.available()!=0)      {   

// если успел забираем ответ из канала сервера в сокете и сохраняем её в ois переменную,  печатаем на свою клиентскую консоль                       
            System.out.println("reading...");
            String in = ois.readUTF();
            System.out.println(in);
                    }           
                }
            }
// на выходе из цикла общения закрываем свои ресурсы
            System.out.println("Closing connections & channels on clentSide - DONE.");

        } catch (UnknownHostException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

  • 3) Многопоточный сервер

А что если к серверу хочет подключиться ещё один клиент!? Ведь описанный выше сервер либо находится в ожидании подключения одного клиента, либо общается с ним до завершения соединения, что делать остальным клиентам? Для такого случая нужно создать фабрику которая будет создавать описанных выше серверов при подключении к сокету новых клиентов и не дожидаясь пока делигированный подсервер закончит диалог с клиентом откроет accept() в ожидании следующего клиента. Но чтобы на серверной машине хватило ресурсов для общения со множеством клиентов нужно ограничить количество возможных подключений. Фабрика будет выдавать немного модифицированный вариант предыдущего сервера(модификация будет касаться того что класс сервера для фабрики будет имплементировать интерфейс — Runnable? для возможности его использования в пуле нитей — ExecuteServices). Давайте создадим такую серверную фабрику и ознакомимся с подробным описанием её работы в коде:


  • Фабрика:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @author mercenery
 *
 */
public class MultiThreadServer {

    static ExecutorService executeIt = Executors.newFixedThreadPool(2);

    /**
     * @param args
     */
    public static void main(String[] args) {

        // стартуем сервер на порту 3345 и инициализируем переменную для обработки консольных команд с самого сервера
        try (ServerSocket server = new ServerSocket(3345);
                BufferedReader br = new BufferedReader(new InputStreamReader(System.in))) {
            System.out.println("Server socket created, command console reader for listen to server commands");

            // стартуем цикл при условии что серверный сокет не закрыт
            while (!server.isClosed()) {

                // проверяем поступившие комманды из консоли сервера если такие
                // были
                if (br.ready()) {
                    System.out.println("Main Server found any messages in channel, let's look at them.");

                    // если команда - quit то инициализируем закрытие сервера и
                    // выход из цикла раздачии нитей монопоточных серверов
                    String serverCommand = br.readLine();
                    if (serverCommand.equalsIgnoreCase("quit")) {
                        System.out.println("Main Server initiate exiting...");
                        server.close();
                        break;
                    }
                }

                // если комманд от сервера нет то становимся в ожидание
                // подключения к сокету общения под именем - "clientDialog" на
                // серверной стороне
                Socket client = server.accept();

                // после получения запроса на подключение сервер создаёт сокет
                // для общения с клиентом и отправляет его в отдельную нить
                // в Runnable(при необходимости можно создать Callable)
                // монопоточную нить = сервер - MonoThreadClientHandler и тот
                // продолжает общение от лица сервера
                executeIt.execute(new MonoThreadClientHandler(client));
                System.out.print("Connection accepted.");
            }

            // закрытие пула нитей после завершения работы всех нитей
            executeIt.shutdown();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

  • Модифицированный Runnable сервер для запуска из предыдущего кода:

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;

public class MonoThreadClientHandler implements Runnable {

    private static Socket clientDialog;

    public MonoThreadClientHandler(Socket client) {
        MonoThreadClientHandler.clientDialog = client;
    }

    @Override
    public void run() {

        try {
            // инициируем каналы общения в сокете, для сервера

            // канал чтения из сокета
            DataInputStream in = new DataInputStream(clientDialog.getInputStream());
            System.out.println("DataInputStream created");

            // канал записи в сокет
            DataOutputStream out = new DataOutputStream(clientDialog.getOutputStream());
            System.out.println("DataOutputStream  created");

            ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
            // основная рабочая часть //
            //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

            // начинаем диалог с подключенным клиентом в цикле, пока сокет не
            // закрыт клиентом
            while (!clientDialog.isClosed()) {
                System.out.println("Server reading from channel");

                // серверная нить ждёт в канале чтения (inputstream) получения
                // данных клиента после получения данных считывает их
                String entry = in.readUTF();

                // и выводит в консоль
                System.out.println("READ from clientDialog message - " + entry);

                // инициализация проверки условия продолжения работы с клиентом
                // по этому сокету по кодовому слову - quit в любом регистре
                if (entry.equalsIgnoreCase("quit")) {

                    // если кодовое слово получено то инициализируется закрытие
                    // серверной нити
                    System.out.println("Client initialize connections suicide ...");
                    out.writeUTF("Server reply - " + entry + " - OK");
                    Thread.sleep(3000);
                    break;
                }

                // если условие окончания работы не верно - продолжаем работу -
                // отправляем эхо обратно клиенту

                System.out.println("Server try writing to channel");
                out.writeUTF("Server reply - " + entry + " - OK");
                System.out.println("Server Wrote message to clientDialog.");

                // освобождаем буфер сетевых сообщений
                out.flush();

                // возвращаемся в началло для считывания нового сообщения
            }

            ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
            // основная рабочая часть //
            //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

            // если условие выхода - верно выключаем соединения
            System.out.println("Client disconnected");
            System.out.println("Closing connections & channels.");

            // закрываем сначала каналы сокета !
            in.close();
            out.close();

            // потом закрываем сокет общения с клиентом в нити моносервера
            clientDialog.close();

            System.out.println("Closing connections & channels - DONE.");
        } catch (IOException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

Для имитации множественного обращения клиентов к серверу, создадим и запустим (после запуска серверной части) фабрику Runnable клиентов которые будут подключаться серверу и писать сообщения в цикле:


  • 4) Имитация множественного обращения клиентов к серверу.

import java.io.IOException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Main {

    // private static ServerSocket server;

    public static void main(String[] args) throws IOException, InterruptedException {

        // запустим пул нитей в которых колличество возможных нитей ограничено -
        // 10-ю.
        ExecutorService exec = Executors.newFixedThreadPool(10);
        int j = 0;

        // стартуем цикл в котором с паузой в 10 милисекунд стартуем Runnable
        // клиентов,
        // которые пишут какое-то количество сообщений
        while (j < 10) {
            j++;
            exec.execute(new TestRunnableClientTester());
            Thread.sleep(10);
        }

        // закрываем фабрику
        exec.shutdown();
    }
}

Как видно из предыдущего кода фабрика запускает — TestRunnableClientTester() клиентов, напишем для них код и после этого запустим саму фабрику, чтобы ей было кого исполнять в своём пуле:


import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;

public class TestRunnableClientTester implements Runnable {

    static Socket socket;

    public TestRunnableClientTester() {
        try {

            // создаём сокет общения на стороне клиента в конструкторе объекта
            socket = new Socket("localhost", 3345);
            System.out.println("Client connected to socket");
            Thread.sleep(2000);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void run() {

        try (

                // создаём объект для записи строк в созданный скокет, для
                // чтения строк из сокета
                // в try-with-resources стиле
                DataOutputStream oos = new DataOutputStream(socket.getOutputStream());
                DataInputStream ois = new DataInputStream(socket.getInputStream())) {
            System.out.println("Client oos & ois initialized");

            int i = 0;
            // создаём рабочий цикл
            while (i < 5) {

                // пишем сообщение автогенерируемое циклом клиента в канал
                // сокета для сервера
                oos.writeUTF("clientCommand " + i);

                // проталкиваем сообщение из буфера сетевых сообщений в канал
                oos.flush();

                // ждём чтобы сервер успел прочесть сообщение из сокета и
                // ответить
                Thread.sleep(10);
                System.out.println("Client wrote & start waiting for data from server...");

                // забираем ответ из канала сервера в сокете
                // клиента и сохраняемеё в ois переменную, печатаем на
                // консоль
                System.out.println("reading...");
                String in = ois.readUTF();
                System.out.println(in);
                i++;
                Thread.sleep(5000);

            }
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }
}

Запускайте, вносите изменения в код, только так на самом деле можно понять работу этой структуры.


P.S. В последнем примере клиенты и серверы работают в бесконечном цикле, так что после окончания тестирования кода не забудьте завершить их работу.


Спасибо за внимание.

Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/330676/


Метки:  

[recovery mode] Представляем 3CX Phone System V15.5

Воскресенье, 11 Июня 2017 г. 01:05 + в цитатник
7 июня мы представили новую версию 3CX Phone System v15.5. В 3CX 15.5 появился веб-клиент, построенный по технологии Angular, который позволяет управлять клиентами 3CX и поддерживаемыми IP телефонами из единого, расширяемого интерфейса. Новый клиент работает и любой открытом браузере и не требует установки вручную.

3CX v15.5 подчинена концепции “Deploy Anywhere”. Это значит, что она одинаково быстро и просто устанавливается на:
  • Стандартных серверах Windows и Debian Linux. Система может быть установлена как ISO образ (Linux) и как серверное приложение (Windows и Linux).
  • Мини ПК на платформе Intel, например, Intel NUC, Zotac, Shuttle и Gigabyte. Эти ПК доступны у локальных поставщиков, всегда есть на складе, и покрываются локальной гарантией. Благодаря массовому производству, они стоят значительно дешевле специализированных платформ для АТС. Установка также делается из ISO образа Debian.
  • VPS серверах Debian, предоставляемых такими гигантами, как Google, Amazon, OVH, 1&1. Однако, вы всегда можете выбрать VPS хостинг нужной мощности и в вашем регионе.
  • Автоматический сервис развертывания АТС 3CX из “файла ответов” с помощью сервиса PBX Express. За несколько минут вы получаете готовую к работе систему объединенных коммуникаций.

Q switchboard2

Интегрированные веб-конференции


Новый веб-клиент включает интерфейс моментального создания видеоконференций. Для этого не нужны никакие плагины и вообще какая-либо настройка. Достаточно нажать одну кнопку и пригласить участников по e-mail или просто “сбросив“ ссылку на подключение. Приглашенные участники подключаются через стандартный браузер, в котором формируется интерфейс клиента видеоконференций. 3CX – единственная система на рынке, предлагающая интегрированные бесплатные видеоконференции до 100 одновременных участников!

Визуальное управление вызовами на телефоне и смартфоне


Веб-клиент для управления IP телефонами теперь использует стандарт uaCSTA. Он позволяет надежно управлять связанным телефоном, независимо от физического расположения устройства, пользователя и АТС. Не важно, где расположена АТС – локально или в облаке – любые действия в новом веб-клиенте будут гарантированно отработаны на вашем телефоне, без веб-запросов и открытия дополнительных портов на сетевом экране. Если клиент 3CX установлен на смартфоне, веб-клиент без проблем сможет им управлять, даже если вы находитесь за пределами офиса.

Chrome расширение Click2Call


Обновленное расширение Chrome Click2Call позволяет оператору звонить одним кликом с любой веб-страницы, из большинства веб-CRM и Office365.

Другие возможности 15.5


  • Интегрированный отельный модуль. Теперь он бесплатно включен в редакцию 3CX Pro и позволяет обмениваться служебной информацией с системами управления гостиничным хозяйством (PMS системами) по протоколу Fidelio и Mitel (и совместимыми). Подробнее о возможностях модуля можно узнать из документации.
  • Поддержка Google Firebase PUSH. Новый стандарт доставки PUSH уведомлений, используемый в новых версиях Android, который пришел на замену Google Cloud Messaging. Новая инсталляция 3CX будет использовать предустановленный Firebase аккаунт, однако рекомендуется использовать собственный аккаунт, созданный по этой инструкции.
  • Соответствие стандарту PCI (передача параметров кредитных карт). Если во время записи разговора клиент сообщает информацию о кредитной карте, запись разговора приостанавливается. При этом сохраняется единый файл записи этого разговора.
  • Среда разработки голосовых приложений Call Flow Designer. Теперь она поставляется бесплатно, однако работа созданных приложений поддерживается только в редакции 3CX Pro. Подробнее о разработке приложений для 3CX, включая скрипты исходящего обзвона, можно узнать здесь.

Видеоролики о некоторых функциях веб-клиента



Загрузки и документация


Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/330674/


[Из песочницы] Делаем бота для дракончика в Google Chrome

Воскресенье, 11 Июня 2017 г. 00:12 + в цитатник
Здравствуйте, не так давно загорелся желанием побить рекорд в мини-игре браузера Google Chrome. Если кто не знает, то при отсутствии доступа в интернет и при использовании выше упомянотого браузера, появляется мини-игра для запуска которой необходимо просто нажать клавишу пробел или в случае с мобильными телефонами, тапнуть по экрану.

image

Давайте приступим к его созданию. Принцип действия бота таков, мы парсим цвет пикселя на определенном расстоянии от персонажа в цикле и проверяем, если цвет пикселя равен цвету кактуса, то прыгаем иначе ни чего не делаем.

image

Начнем с того, что найдем нужный пиксель. В моем случае этот пиксель находится на координатах «775x250». Нужный пиксель я беру на 1 выше самого высокого бугорка на дороге и на расстоянии от персонажа выведенного методом проб и ошибок. Также следует заметить, что у меня разрешение экрана 1920х1080 21.5" и если у вас другое, то скорее всего будет работать криво.
Создадим консольное приложение в VS.

Для начала объявим переменные для координат пикселя.

        public static int x = 775;
        public static int y = 250;

Затем подключим DLL для работы.

        [DllImport("user32.dll")]
        public static extern IntPtr GetDC(IntPtr hwnd);

        [DllImport("user32.dll")]
        public static extern int ReleaseDC(IntPtr hwnd, IntPtr hDC);

        [DllImport("gdi32.dll")]
        public static extern uint GetPixel(IntPtr hDC, int x, int y);

Далее все еще проще, добавляем код для самого обработчика

IntPtr hDC = GetDC(IntPtr.Zero);
            while (true)
            {
                uint pixel = GetPixel(hDC, x, y);
                if (pixel == 5460819)
                {
                    SendKeys.SendWait("{UP}");
                }
            }

Код взят в бесконечный цикл.

if (pixel == 5460819)

Данная строчка означает, что когда цвет пикселя равен цвету кактуса в кодировке Decimal, то происходит нажтие стрелки вверх.

SendKeys.SendWait("{UP}");

Так же нужно подключить 2 ссылки.

using System.Diagnostics;
using System.Windows.Forms;

На данный момент дракончик с легкостью преодолевает 500 игровых метров, а дальше его подстерегает неожиданность ввиде птиц и ночи. Для решения этих проблем я напишу следующую статью. Удачи!

Полный код
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Diagnostics;
using System.Windows.Forms;

namespace DragonChrome
{
    class Program
    {
        #region VAR
        public static int x = 775;
        public static int y = 250;

        #endregion
        #region DLL
        [DllImport("user32.dll")]
        public static extern IntPtr GetDC(IntPtr hwnd);

        [DllImport("user32.dll")]
        public static extern int ReleaseDC(IntPtr hwnd, IntPtr hDC);

        [DllImport("gdi32.dll")]
        public static extern uint GetPixel(IntPtr hDC, int x, int y);

        #endregion
        static void Main(string[] args)
        {
            IntPtr hDC = GetDC(IntPtr.Zero);
            while (true)
            {
                uint pixel = GetPixel(hDC, Convert.ToInt32(x), y);
                if (pixel == 5460819)
                {
                    SendKeys.SendWait("{UP}");
                }
            }
        }
    }
}

Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/330672/


[Из песочницы] Настройка Reverse Proxy Apache (Debian 8) с автоматической выдачей Let's Encrypt

Суббота, 10 Июня 2017 г. 22:45 + в цитатник
Так как за частую сайтов в организации много, а IP адресов мало, нужно иметь решение с Reverse Proxy. Для моих целей раньше всегда выступал Microsoft TMG, но у него есть свои недостатки, как и плюсы. Один из основных минусов, это то что на TMG нужно подгружать сертификаты публикуемого ресурса, что с Let's Encrypt довольно неудобно, ввиду обновления сертификатов каждые 90 дней.

Решение было найдено: поднять Reverse Proxy на Apache и сделать так, чтобы работала автовыдача сертификатов Let's Encrypt. А после чего спокойно публиковать его на Firewall, при этом порты буду перенаправляться с http на https.

За основу берем что у нас стоит чистый Debian GNU/Linux 8 (jessie). Подробнее под катом.

Ну что-ж, поехали.

aptitude install -y build-essential
aptitude install -y libapache2-mod-proxy-html libxml2-dev
aptitude install -y apache2

После чего активируем следующие модули:

a2enmod proxy
a2enmod proxy_http
a2enmod proxy_ajp
a2enmod rewrite
a2enmod deflate
a2enmod headers
a2enmod proxy_balancer
a2enmod proxy_html
a2enmod proxy_ftp
a2enmod proxy_connect
a2enmod ssl

и рестартуем Apache:

service apache2 restart

Тут нас поджидает первая неудача, Apach'у для правильной работы не хватает модуля mod_xml2enc, НО! в Jessie этот модуль не работает, нам последовательно нужно внести следующие команды:

aptitude install apache2-prefork-dev libxml2 libxml2-dev apache2-dev
mkdir ~/modbuild/ && cd ~/modbuild/
wget http://apache.webthing.com/svn/apache/filters/mod_xml2enc.c
wget http://apache.webthing.com/svn/apache/filters/mod_xml2enc.h
apxs2 -aic -I/usr/include/libxml2 ./mod_xml2enc.c
cd ~
rm -rfd ~/modbuild/
service apache2 restart

После чего, все у нас хорошо, модуль стоит. Едем дальше )

Так как мы хотим опубликовать HTTPS сайт, до того момента пока мы не установим Let's Encrypt, нам нужно сделать самоподписанный сертификат для нашего сайта, вводим комманду:

mkdir /etc/apache2/ssl
cd /etc/apache2/ssl
openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout server.key -out server.crt

Нам нужно создать файл конфигурации и назвать его понятным именем:

touch /etc/apache2/sites-available/sambi4.conf

И задаем файлу примерно такое содержание:


ServerName sambi4.ru
Redirect permanent / https://sambi4.ru/ #отвечает за перенаправление на https



SSLEngine On
SSLProxyEngine On
ProxyRequests Off
ProxyPreserveHost On
ProxyVia full

SSLCertificateFile /etc/apache2/ssl/server.crt #указываем путь к нашему самоподписанному сертификату
SSLCertificateKeyFile /etc/apache2/ssl/server.key #указываем путь к нашему самоподписанному ключу сертификата

ProxyHTMLInterp On
ProxyHTMLExtended On


Order deny,allow
Allow from all


ProxyPass / https://192.168.199.78/ #IP адрес публикуемого ресурса.
ProxyPassReverse / https://192.168.199.78/ #IP адрес публикуемого ресурса.
ServerName sambi4.ru
ServerAdmin sambi4@sambi4.ru #считается хорошим тоном указывать email админа
DocumentRoot "/var/www/html" #эта строка нужна для того чтобы апач запустился, без нее он не сможет опубликовать ваш ресурс.

После завершения создания, не забываем включить наш сайт:

a2ensite /etc/apache2/sites-available/sambi4.conf

перезапускаем Apache:

service apache2 restart

После всех проделанных процедур, мы имеем настроеный Reverse Proxy на Apache2, теперь можно приступить к настройке Let's Encrypt:

Из всех бесплатных сертификатов, жив остался только Let's Encrypt, но его особенность в том, что сертификат выдается сроком на 3 месяца.

Нам нужно поставить сертификат, и сделать автоматическую выдачу при завершении срока сертификации.

echo 'deb http://ftp.debian.org/debian jessie-backports main' | tee /etc/apt/sources.list.d/backports.list

после:

aptitude update

ну а теперь ставим сам Let's Encrypt:

aptitude install -y python-certbot-apache -t jessie-backports

Дожидаемся процесса установки, и пробуем выпустить сертификат:

certbot --apache

И вот тут нас поджидает неудача:
ERROR:letsencrypt_apache.configurator:No vhost exists with servername or alias of: sambi4.ru. No vhost was selected. Please specify servernames in the Apache config

Связано это с тем, что в репозитариях до сих пор старая версия (на момент написания 0.10.2), в которой наблюдаются ошибки. А именно ошибки в python-скриптах. Решение как обычно просто:
Качаем свежую версию certbot:

git clone https://github.com/certbot/certbot.git

После чего, идем по пути:

 cd /usr/lib/python2.7/dist-packages

Удаляем папки (а лучше делаем бэкап):

acme
certbot
certbot_apache
И копируем файлы из нового релиза:

cp /root/certbot/certbot /usr/lib/python2.7/dist-packages/
cp /root/certbot/acme/acme/ /usr/lib/python2.7/dist-packages/
cp /root/certbot/certbot-apache/certbot_apache/ /usr/lib/python2.7/dist-packages/

Теперь можно со спокойной душой запускать процесс выпуска сертификата:

certbot --apache

Отвечаем на вопросы и все!

Поздравляю, сертификат мы выпустили, теперь нужно добавить скрипт автопродления сертификата, т.к. Let's Encrypt выдает сертификаты сроком только на 90 дней (мы об этом помним).

Все просто. Нам в cron нужно добавить строчку:

30 2 * * 1 /usr/bin/certbot renew >> /var/log/le-renew.log

Т.е. набираем:

crontab -e

И добавляем нашу строку (обязательно перейти на следующую срочку, иначе не сохранится)

И все, повторить бесконечное множество раз с Вашими другими ресурсами.

Удачи, админы!
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/330670/


Метки:  

[Перевод] Советы по Postgres для Rails разработчиков

Суббота, 10 Июня 2017 г. 21:46 + в цитатник

В апреле на RailsConf в Фениксе мы обсудили огромное количество советов по использованию Postgres с Rails, и подумали, что будет полезно их записать и поделиться с более широкой аудиторией. Здесь вы найдете некоторые из них, касающиеся отладки и улучшения производительности базы данных вашего Rails приложения.


Управление долгими запросами с помощью таймаутов


Долгие запросы могут оказывать все виды негативных воздействий на вашу базу данных. Независимо от того, работают ли запросы часами или всего несколько секунд, они могут держать блокировки, переполнять WAL, или просто потреблять огромное количество системных ресурсов. C Postgres легче добиться большей стабильности с помощью установки таймаута на запросы. Удобно, что можно установить значение по умолчанию, например, 5 секунд, как показано в примере ниже, и тогда любой запрос, длящийся дольше 5 секунд будет убит:


production:
    url: 
    variables:
        statement_timeout: 5000

Если вам требуется, чтобы запрос выполнялся дольше в пределах сессии, можно задать таймаут, действительный только для текущего соединения:


class MyAnalyticsJob < ActiveJob::Base
    queue_as :analytics
    def perform
        ActiveRecord::Base.connection.execute “SET statement_timeout = 600000” # 10 минут
        # ...
    ensure
        ActiveRecord::Base.connection.execute “SET statement_timeout = 5000” # 5 секунд
    end
end

Поиск “плохих” запросов


Rails многое абстрагирует при взаимодействии с вашей базой данных. Это может быть как хорошо, так и плохо. Postgres сам позволяет отслеживать долгие запросы, но по мере роста вашего Rails приложения, этого может оказаться недостаточно. Чтобы узнать происхождение запроса, есть чрезвычайно удобный гем marginalia, который будет логировать, откуда именно пришел ваш запрос. Теперь, когда вы видите неправильный запрос, слишком медленный, или который просто можно убрать, вы точно знаете где в коде это поправить:


Account Load (0.3ms) 
SELECT `accounts`.* FROM `accounts`
WHERE `accounts`.`queenbee_id` = 1234567890
LIMIT 1
/*application:BCX,controller:project_imports,action:show*/

Внимание на комментарий — прим.пер.


Высокоуровневый обзор ситуации с запросами к базе


Часто требуется общая картина того, что происходит в вашей базе данных. pg_stat_statements — это расширение Postgres, которое часто предустановленно в облачных средах, таких как Citus Cloud. Оно позволяет видеть, какие запросы выполнялись с момента последнего сброса статистики и как они себя вели.


Например, чтобы увидеть 10 наиболее долго выполняющихся запросов и их среднее время, выполните следующее:


SELECT query, total_time / calls AS avg_time
FROM pg_stat_statements
ORDER BY total_time DESC
LIMIT 10;

Если включить «track_io_timing» в сборщике статистики Postgres, то можно понять что является узким местом — процессор или ввод-вывод. Можно узнать больше о pg_stat_statements здесь (или в другой статье okmeter.io на хабре — прим.пер.).


Использование продвинутых фич


По умолчанию Rails использует файл «schema.rb» для хранения копии схемы базы данных, обычно используемой для инициализации базы данных перед запуском тестов. К сожалению, многие расширенные функции Postgres, такие как функциональные и частичные индексы, а также составные первичные ключи, не могут быть представлены в этом DSL.


Вместо этого имеет смысл перейти на генерируемый и используемый Rails файл «db/structure.sql», который можно сделать следующим образом:


# Use SQL instead of Active Record's schema dumper when creating the database.
# This is necessary if your schema can't be completely dumped by the schema dumper,
# like if you have constraints or database-specific column types
config.active_record.schema_format = :sql

Внутри используется формат Postgres «pg_dump», который иногда может быть чересчур подробным, но зато гарантирует получение полностью восстановленной структуры базы данных. Если вы столкнетесь с проблемой чрезмерно длинных diff-ов, можете взглянуть на activerecord-clean-db-structure.


Следите за тем, чтобы сложные транзакции не блокировали друг друга


Rails любит помещать всё в транзакции, особенно при использовании хуков «before_save» и многоуровневых связей между моделями. Существует одно важное предостережение, которое следует учитывать при транзакциях, которые могут создавать проблемы при масштабировании. Например, в такой транзакции:


BEGIN;
UPDATE organizations SET updated_at = ‘2017-04-27 11:31:03 -0700’ WHERE id = 123;
SELECT * FROM products WHERE store_id = 456;
--- еще всякие statement’ы тут
COMMIT; 

Первый оператор UPDATE будет удерживать блокировку на строку "organizations" с id "123", с самого начала и до COMMIT’а транзакции.


Представьте другой запрос к той же самой "organization", пришедший, например, от другого пользователя, и выполняющий аналогичную транзакцию. Как правило, чтобы выполниться, этому другому запросу придется ждать комита первой транзакции, что увеличит время ответа. Чтобы исправить это, можно переупорядочить транзакцию, чтобы UPDATE выполнялся ближе к концу, а также рассмотреть возможность сгруппировать обновления полей временных меток вне транзакции после выполнения основной работы.


Чтобы обнаруживать подобные проблемы зарание, вы можете установить «log_lock_waits = on» в PostgreSQL.


Контролируйте подключения к базе данных


Rails по умолчанию поддерживает пул подключений к базе данных. Когда приходит новый запрос, он берет одно соединение из пула и передает его в приложение. По мере масштабирования вашего Rails приложения это может привести к сотням открытых подключений к базе данных, хотя на самом деле только часть из них выполняет работу. Ключом к этому является использование менеджера соединений для уменьшения активных подключений к базе данных, например, такого как pgBouncer. Менеджер соединений будет открывать соединения, когда транзакции активны, и не пропускать простаивающие соединения, которые не выполняют никакой работы.


От переводчика — а еще вы можете использовать наш мониторинг, который отследит количество соединений к Постгресу и к pgBouncer и много всего другого и поможет предупредить и разрешить проблемные ситуации.

Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/330628/


Метки:  

Самодельный эмулятор дисковода для Amiga

Суббота, 10 Июня 2017 г. 21:13 + в цитатник
У многих сохранились дома компьютеры Amiga. Но вот дискеты к ним сохранились не у всех. Эту проблему можно решить, собрав эмулятор дисковода. О том, как сделать самому такой эмулятор дисковода для Amiga я и расскажу в этой статье.

Я долго думал, где разместить эту статью — здесь или на geektimes. С одной стороны, она касается разработки для микроконтроллеров, а с другой речь пойдёт о разработке устройства для очень старого компьютера, тема о которых находится на geektimes. Однако, на geektimes описываются сами устройства и программирование для них на их API, а здесь всё-таки, устройство собрано на почти современной элементной базе.

У некоторых дома сохранились компьютеры Amiga, кому-то такой компьютер могли просто отдать (как мне), а кто-то специально себе его купил. И иногда бывает так, что компьютер есть, но на нём нет винчестера (например, у меня на Amiga 500 его нет), а запустить что-то на нём хочется. Возиться с дискетами (которые можно записать на PC только с помощью специальных устройств) не вариант. Вот и выбирают люди эмуляторы дисководов. Сейчас их предлагается довольно много, но вот несколько лет назад таких устройств ещё почти не было. Насколько мне известно, первым эмулятор дисковода для Amiga сделал tnt23. Можно было, конечно, купить у него эмулятор, но хотелось всё-таки попробовать свои силы в создании собственного, тем более, что в попытке запустить этот компьютер я на тот момент уже создал простое устройство для записи дискет, и в целом формат дискеты проблемы не представлял. Описываемая статья была когда-то написана для журнала Dogma, но не попала в выпуск этого журнала потому, что про неё просто забыли. Однако, я знаю, здесь есть амижники и, думаю, им будет интересно прочесть, как можно сделать свой собственный эмулятор дисковода. Быть может, эта информация подтолкнёт кого-то к созданию своего устройства, гораздо более совершенного. Итак, начнём.

Описанный ниже эмулятор дисковода достаточно несложен сборке и не содержит дефицитных деталей. Проблема может возникнуть лишь с поиском 30 контактных модулей SIMM ёмкостью 1 МБ. Однако, такие модули были широко распространены и, скорее всего, их можно найти на радиорынках. На Юноне в СПб они точно были.

Список деталей для сборки:


Марка применяемых конденсаторов, диодов, резисторов, дросселя, кнопок и держателя SD-карты значения не имеет. Диоды требуются обязательно с падением напряжения на переходе не менее 0.5 В. Дисплей может быть с другими буквенными индексами.

Эмулятор подключается в разъём внешнего дисковода и позволяет воспроизводить выбранный образ дискеты. Образы дискет хранятся в корневом каталоге на SD-карте с файловой системой FAT16. Файлы создаются специальной программой-конвертером файлов ADF и представляют собой побайтные данные стандартной 80 дорожечной дискеты, закодированные с помощью MFM кодирования. Никаких заголовков такие файлы не содержат. Ввиду упрощения схемотехники эмулятор не имеет аппаратной возможности переводить линии дисковода в Z-состояние в зависимости от того выбран ли дисковод A или B. Это значит, что все дисководы Amiga требуется аппаратно отключить во время работы с эмулятором. Однако, возможность распознавать адрес дисковода теоретически предусмотрена программно – для этого требуется чтобы микроконтроллер опознавал адрес и переводил выходы в Z-состояние, если адрес не соответствует нужному. Линии для чтения адреса дисковода на схеме предусмотрены, но программно такая возможность не реализована.

Схема эмулятора (в архиве она есть в полном качестве).


Внешний вид эмулятора со стороны дисплея.

Схемотехнически эмулятор состоит из двух частей. Каждая из частей основана на микроконтроллере Atmega16. Микроконтроллер первой части занимается выводом информации на дисплей, обработкой кнопок, чтением информации с SD-карты с использованием аппаратного SPI, формированием части линий дисковода. Микроконтроллер второй части занимается регенерацией динамической памяти и формирует остальные сигналы линий дисковода. Обе части обмениваются информацией по шине SPI. Для контроллера D2 шина SPI для связи с D3 программная, а в D3 задействован аппаратный SPI.

Рассмотрим подробнее назначение элементов. Микросхема D1 обеспечивает питание SD-карты напряжением 3.3 В. Диоды VD1-VD6 понижают напряжение линий SPI c 5 до 3.3 В. Резисторы R1-R6 и R8-R9 притягивают линии к земле. RC-цепочки R7-C3 и R11-C5 формируют сигнал начального сброса микроконтроллеров. Элементы Z1, C1 и C2 обеспечивают генерацию 16 МГц на первом микроконтроллере (D2). Второй микроконтроллер (D3) работает в режиме внешней синхронизации от тактового генератора первого микроконтроллера. Резистор R10 управляет контрастностью изображения на дисплее. Элементы L1 и C4 обеспечивают фильтрацию напряжения источника питания.

Аппаратная часть эмулятора дисковода собирается на плате размерами 220x125 мм. Печать односторонняя, выполняется любым доступным методом, например, ЛУТ. На противоположной печати стороне платы необходимо поставить ряд перемычек проводом (отображены синими дорожками в прилагающемся файле печатной платы в формате Sprint Layout 4.0).

Программная часть эмулятора состоит из двух программ для микроконтроллеров. Программа для микроконтроллера D2 находится в папке MK1, а для микроконтроллера D3, соответственно, в папке MK2. После прошивки микроконтроллеров соответствующим HEX-файлом любым доступным способом (в том числе, для прошивки можно использовать схему “пять проводков в LPT-порт компьютера” совместно с программой программатора Uniprof), необходимо выставить FUSE-биты конфигурации контроллеров. С помощью этих битов необходимо в обоих контроллерах отключить JTAG, переключить D2 на тактирование от внешнего кварцевого резонатора 16 МГц, включить для D2 увеличенную амплитуду сигнала с осциллятора на выходе (бит CKOPT), а D3 переключить на тактирование от внешнего источника. Если ваш программатор не имеет собственного тактового генератора, контроллеры после этой операции будут недоступны для программирования. Чтобы они снова стали программируемыми потребуется подключить к XTAL1 контроллеров любой внешний генератор с частотой несколько сотен килогерц. Обязательно перед модификацией FUSE-битов считайте их с контроллера. Обратите внимание, что разные программаторы по-разному трактуют включенный и выключенный бит.

Эмулятор не требует наладки после сборки. Однако, возможно, не все модули SIMM 30 PIN будут корректно работать с ним. Для этой цели в меню эмулятора есть пункт “ТЕСТИРОВАНИЕ ПАМЯТИ”.

Для облегчения понимания работы программного обеспечения эмулятора необходимо знать следующее. На стандартной дискете Amiga хранит записанные данные по 80 дорожек с двух сторон. Каждая дорожка состоит из 11 секторов по 512 байт. Следовательно, полный объём данных дискеты 901120 байт. Именно такой объём имеют ADF-файлы образов дискет. Такой файл не содержит заголовков, а данные внутри просто перечисляются следующим образом: (дорожка 0, сторона 0), (дорожка 0, сторона 1), (дорожка 1, сторона 0), (дорожка 1, сторона 1)… (дорожка 79, сторона 0), (дорожка 79, сторона 1). Каждая дорожка — это запись 11 секторов по 512 байт начиная с 0 сектора. Для эмуляции дискеты такой файл в чистом виде не подходит. Дело в том, что ADF файл хранит только полезные данные безо всякой служебной информации и без модуляции. Рассмотрим, как осуществляется модуляция сигнала в компьютере Amiga.

Любой дисковод представляет из себя не более, чем аналог магнитофона. Он никак не кодирует записанные данные. Вы выбираете дорожку и модулируете ток (а, следовательно, и напряженность магнитного поля) через головку дисковода для записи информации. При воспроизведении в местах, где напряженность магнитного поля изменялась, дисковод считывает короткий импульс. Amiga использует при записи на дискеты модифицированную частотную модуляцию, так называемую MFM (Modified Frequency Modulation). При таком способе модуляции изменение тока через катушку зависит не только от значения записываемого бита, но и от значения предшествующих бит, как показано в таблице ниже. В графе “кодируется” R означает смену намагниченности (подачу импульса дисководу), а N — намагниченность не изменяется. В дальнейшем для записи MFM файла в бинарном формате примем R равным 1, а N равным 0. В MFM файл записываются уже на один бит данных два бита смены намагниченности. Таким образом, размер данных MFM файла по сравнению с ADF файлом будет удвоенный. Но просто преобразовать ADF в MFM нельзя. Amiga использует свой собственный формат записи на дискеты, который потребуется учесть.



Условимся номером трека называть номер дорожки на одной из сторон диска (т.е. номер трека изменяется от 0 до 79), а номером дорожки ((2*номер трека)+номер стороны диска). Номер дорожки изменяется от 0 до159, номер стороны диска изменяется от 0 до 1, номер сектора изменяется от 0 до 11.

В формате Amiga каждая дорожка содержит:
  1. Начальный зазор (128 байт MFM кода 0x00).
  2. Данные 11 секторов по 544 байта на сектор.
  3. Конечный зазор (704 байта MFM кода 0x00)

Каждый сектор содержит:
  1. Маркер начала сектора.
  2. Идентификатор сектора (4 байта: 0xFF, номер дорожки, номер сектора, смещение сектора. Смещение сектора вычисляется как 11 минус номер сектора на дорожке.)
  3. Метка сектора (16 байт 0x00).
  4. Контрольная сумма заголовка (4 байта).
  5. Контрольная сумма данных (4 байта).
  6. Данные сектора (512 байт).


Идентификатор и данные секторов закодированы перестановкой битов. Для идентификатора берутся 4 байта и переставляются биты. Сначала идут нечётные биты (нумерация битов, как обычно, с 0) – это составит 2 байта. Затем идут чётные биты – так же 2 байта. Таблица иллюстрирует вышесказанное. Верхняя строка – стандартный порядок битов (номера в ячейках), нижняя – результат перестановки.



Данные секторов группируются по 2 байта, и перестановка происходит внутри этой пары аналогичным образом:


Контрольная сумма представляет собой операцию исключающее ИЛИ (XOR) всех байт данных сектора или заголовка (с учётом перестановки битов!), сгруппированных по 2 байта. Сама контрольная сумма не подвергается перестановке битов. Хотя поле для контрольной суммы 4 байтное, сама контрольная сумма получится двухбайтная.

Помимо 544 байт данных, закодированных с помощью MFM кодирования, каждый сектор содержит специальный маркер начала данных. Маркер представляет собой комбинацию нулей и единиц, которую невозможно получить MFM-кодированием. Такой комбинацией является комбинация 01000100100010010100010010001001 перед записью которой записываются 4 байта с комбинацией 10101010. Первый байт комбинации 10101010 зависит от предыдущего выданного с помощью MFM кодирования бита. Если последний выданный бит сектора был 1, то этот байт будет равен 01000100, а если там был 0, то 10101010.

Учитывая всё вышеизложенное, дорожка дискеты в формате MFM выглядит так:
  1. Начальный зазор: 128 байт 00000000.
  2. Маркер начала данных сектора:

    10101010 (этот байт зависит от последнего записанного бита и может быть 01000100)
    10101010
    10101010
    10101010

    01000100
    10001001
    01000100
    10001001
  3. Данные сектора без маркера: 1080 байт на сектор.
  4. Повторить с шага 2 для всех 11 секторов.
  5. Конечный зазор: 704 байт 00000000.


Итого, каждая дорожка в MFM-кодировании занимает (1080+8)*11+128+704=12800 байт кода. Если теперь этот код выдать на линию данных с дисковода учитывая временную развёртку дорожки, то Amiga сможет прочесть данные этой дорожки.

Проблема состоит в том, что длительность импульсов данных составляет от 0.15 до 0.8 мкс. В данном эмуляторе выбрана длительность импульсов 0.5 мкс. Это означает, что на частоте работы контроллера 16 МГц у нас будет 32 такта на бит MFM кода. За эти 32 такта микроконтроллер D3 должен производить регенерацию динамической памяти, считывание байта MFM-кода, выдачу бита смены намагниченности на линии дисковода. Чтобы уложиться в 32 такта, эта часть программы написана на ассемблере и строго выровнена по тактам. Операции чтения байта и регенерации памяти разнесены во времени. Это значит, что пока выдаётся побитно байт MFM кодирования, на каждый бит имеется 32 такта и для каждого выдаваемого бита строго определено, какие именно операции мы в эти 32 такта выполняем. Например, при выдаче бита 0 мы можем установить строку динамической памяти (сигнал RAS). При выдаче бита 1 мы можем установить столбец динамической памяти (сигнал CAS). При выдаче бита 2 мы считываем следующий выдаваемый байт данных. И так далее. Таким образом выдаётся весь образ дискеты.

Собственно, вот и получился простейший эмулятор дисковода.

Видео работы эмулятора:





Архив с печатными платами, прошивками, схемами и программами.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/330668/


Метки:  

Интернет на магнитах 5 — Маяки и сообщения (личные, публичные и обновления)

Суббота, 10 Июня 2017 г. 20:39 + в цитатник

Я вспомнил что не рассказал важную часть для обеспечения возможности общения и обновления контента в P2P сетях.


Не все P2P сети имеют возможность отправки и приёма личных сообщений. Также не всегда сообщение можно оставить в оффлайн. Мы исправим этот недостаток используя три возможности P2P сетей: поиск файла, просмотр шары(списка опубликованных файлов) или комментарии к файлу.


Как это работает


Шаблон маяка создаётся однократно и используется для создания маяков для связи с автором.


Общий алгоритм получения


  1. Публикуется шаблон маяка.
  2. Формируется маяк.
  3. Поиск этого маяка и файла с хешем маяка в имени.
  4. Загрузка найденных файлов или просмотр шары источников маяка.

Общий алгоритм отправки такой


  1. Пишем сообщение.
  2. Шифруем открытым ключом адресата.
  3. Формируем маяк по шаблону адресата.
  4. Получаем хеш от маяка и вставляем в имя файла с сообщением.
  5. Публикуем маяк и файл с сообщением в p2p сетях.

Наше сообщение и маяк свободно могут копировать другие участники сети. Так как оно зашифровано они не смогут его прочитать но помогут его держать онлайн пока его не получит адресат.


Открытое сообщение от автора


  1. Публикуем шаблон маяка.
  2. Пишем сообщение и подписываем.
  3. Генерируем маяк.
  4. В имя файла сообщения вписываем хеш маяка.
  5. Публикуем маяк и файл с сообщением.

Шаблон маяка


Это файл из которого формируется маяк.


Шаблон маяка формирует тот кто желает получать сообщения (адресат) и публикует у себя в шаре либо комментарием с магнитом к другому маяку. Шаблон должен включать в себя


  1. Публичный ключ для шифрования сообщений предназначенных адресату
  2. Шаблон маяка — инструкции для формирования маяка
  3. Подпись — файл подписывается.

Инструкции шаблона должны позволить генерировать файлы с одинаковыми хешами(маяки) каждый час либо другой выбранный период времени.


Пример шаблона маяка


Файл: lighthouse_template_f43b4866b4f47e8e27bfafa56f4c4f2384a3135d.asc


-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256

- -----BEGIN PGP PUBLIC KEY BLOCK-----
Version: GnuPG v2

mQENBFk6ZDMBCADE/yoNEOosw16n3P2ZSjH+quYlSbZePBudHnAlNZ/e4KSxEnXx
B/44vKJX98y0X6TjCk0TVrAfJQhOrwg9/X6IMkeii1Nzt+NhrygojuVmEJS0kHvi
QoJLrotBXdYWbUtfVE8zGS/qVmtFi6ns/ouV1yqLnRSA+dVfLNa/7k5glo+7TEDt
g/CkSQ5CKJowPnBYnCxF4aQQQi8ydDIMXIFVyuApPRHLNsqIeA4CFlpb2Xb3ksEN
/tse4zjBRgNS3nc6SLYfv6KKb8O6oHxWzz+mXlrvL4G1dszUE7Yg4FND9t9P+bMQ
d+7OqDgO00/eEBooTcOptuHXTLxftu3z9S2VABEBAAG0Iml2YW4zODYgPG1hZ25l
dEBpdmFuMzg2LmdpdGh1Yi5pbz6JAT8EEwEIACkFAlk6ZDMCGyMFCQEOQt0HCwkI
BwMCAQYVCAIJCgsEFgIDAQIeAQIXgAAKCRBvTE8jhKMTXUA7B/93TSAcSQW5Nvgw
CpgsJHTRbUsWT2heovl1CeME39H3iKxlNVMl2N5l5kFNUryLMo63ZXp5tm9C7UKx
o2X6+3TJg/Td+WxC2vhXyLeSWc6KkV98a7Sg6kgdBBDXA1x4TfLNT8dijYBqNNgU
OVJ3gOyOCXa7Is9gPYobtwFvTTzsKqfz3JkAZf54Y7EulLYo+esty2nDqIhyC+7Q
a5izjxhGME1gAWjPT4Yr/QytmL1D1WD8tmmVrvFtBwUo3iyZhf2TCVkkJ41mOAq+
Vr2tfZPnxUxSaLMU4cm0rUoZ79NK3FX70xJjy4IgQtj/NDuQwv1KgqSClxCKkpI/
BJSvjxzguQENBFk6ZDMBCAD6DTPWTkG7Zqqhz/5oaM4zFG9ncTtnVXiR0rRPzuyn
aXz1/Kn3odEEG4kyEQbHS/lLfnLsqAkSMjUHiBsC6R0h1bTqSDnHEhPqpuLm526N
6j+o5/oIC8ANlGH5fFGDMNXy5MrRuFGMv/tHDuSW56rb6Kh9jPoU+1HCAGpv4zd9
iXB/nOYHlLl3NZ4+883wd0BgIKslSpvOU8sTjqcEwcIFFHr8SBUM6x1nKSq1Ad+C
bEQPBA7EKdIeV/N5CZ9s6fnvUvc1bNMOO8YdJ4wCWfDSGWu7ZdPyj+Yh+9/R7EIW
bEAHX5v/IcAu1u0U53goehGvEu9K4lNjibYRu8hDU02fABEBAAGJASUEGAEIAA8F
Alk6ZDMCGwwFCQEOQt0ACgkQb0xPI4SjE11j5Af9EBukKcDSQfSFBgsRRxLzfk3l
B4Qa7Kb2JKPdf8p+rlC8n7b7nS1WnDsh/ADnRO1+njP4oHo4eV3P09ao9PNagM5k
YldAG2oAOj9RhVSMI4Jt+/Tf7NZixLPjMCakrGA47wQwC9bp+AmV/ST8I6/r2t3W
4uXDLUKzvZxpBUVqEG9x0olnQiBgMicyUlW1WmyMKiorVET6rSnCv3yv33lMRQRG
5txSw/z0jXhhK6ENn0mHMGeiBqGITTwjQM7uQTkw2GmNJfHpg+jThnJQran+cGDZ
tvKsoBc4BFnM4eTxZfeyYPquG2QWZ59kcqm4JRyJfHYKT+CIPQXmEmD4uyp5/Q==
=QxOi
- -----END PGP PUBLIC KEY BLOCK-----

yyyy - год полностью по UTC±0:00
mm - месяц по времени UTC±0:00 (дополняется нулём если меньше 10)
dd - день по времени UTC±0:00 (дополняется нулём если меньше 10)
hh - час в 24 часовом формате по времени UTC±0:00 (дополняется нулём если меньше 10)

Шаблон имени файла маяка (важно для сети BitTorrent):
lighthouse_yyyy-mm-ddThhZ_f43b4866b4f47e8e27bfafa56f4c4f2384a3135d.asc

Для формирования маяка добавьте в конце файла дату и время в формате:
yyyy-mm-ddThhZ
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v2

iQEcBAEBCAAGBQJZOr9iAAoJEG9MTyOEoxNd8RIIALQUa9BuMgh+uET4wse7sBDG
19RvKbUq+9HCOaxL+Qfz+C5VO1XVIxC/Vi2iP1RVwBjkKtTANXdxcf9jR/S2ZHpL
0ohDsJ8O8EPf0sTEr5nvGE27KobEhvAb9TbxrOLuZlWtqWh09CvgyEZPVnnMa7v1
WL6xVln2BjZOkndn60ToqPcVUYQpwIEfHBz7S8WYdRFumN8sUCfJYaXGFElSLKiA
2Y+fTayhPIKcwBTzRuIU6fQWWCUA7egZ3JsWWiBP68parRcsDDOia3UiddFRr0jW
lmGrsOJiZ2Vq2xfXNqY+BfcSdT8AXnt9yUYCdaH2w2oI3uEXlw8eF8JyxO60w8k=
=FJOw
-----END PGP SIGNATURE-----

Маяк


Маяк формируется из шаблона маяка по инструкциям указанным в нём. Сначала идёт сам шаблон в неизменном виде а далее идут данные которые сформированы по инструкциям из шаблона.


Задача маяка в том чтоб один и тот же маяк(с одинаковым хешем) могли сформировать и отправитель и получатель и по хешу найти друг друга или посредника который хранит сообщение.


Пример маяка


Маяк на 09 июня 2017 года 18:33 Москвы будет выглядеть так.


Файл: lighthouse_2017-06-09T15Z_f43b4866b4f47e8e27bfafa56f4c4f2384a3135d.asc


-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256

- -----BEGIN PGP PUBLIC KEY BLOCK-----
Version: GnuPG v2

mQENBFk6ZDMBCADE/yoNEOosw16n3P2ZSjH+quYlSbZePBudHnAlNZ/e4KSxEnXx
B/44vKJX98y0X6TjCk0TVrAfJQhOrwg9/X6IMkeii1Nzt+NhrygojuVmEJS0kHvi
QoJLrotBXdYWbUtfVE8zGS/qVmtFi6ns/ouV1yqLnRSA+dVfLNa/7k5glo+7TEDt
g/CkSQ5CKJowPnBYnCxF4aQQQi8ydDIMXIFVyuApPRHLNsqIeA4CFlpb2Xb3ksEN
/tse4zjBRgNS3nc6SLYfv6KKb8O6oHxWzz+mXlrvL4G1dszUE7Yg4FND9t9P+bMQ
d+7OqDgO00/eEBooTcOptuHXTLxftu3z9S2VABEBAAG0Iml2YW4zODYgPG1hZ25l
dEBpdmFuMzg2LmdpdGh1Yi5pbz6JAT8EEwEIACkFAlk6ZDMCGyMFCQEOQt0HCwkI
BwMCAQYVCAIJCgsEFgIDAQIeAQIXgAAKCRBvTE8jhKMTXUA7B/93TSAcSQW5Nvgw
CpgsJHTRbUsWT2heovl1CeME39H3iKxlNVMl2N5l5kFNUryLMo63ZXp5tm9C7UKx
o2X6+3TJg/Td+WxC2vhXyLeSWc6KkV98a7Sg6kgdBBDXA1x4TfLNT8dijYBqNNgU
OVJ3gOyOCXa7Is9gPYobtwFvTTzsKqfz3JkAZf54Y7EulLYo+esty2nDqIhyC+7Q
a5izjxhGME1gAWjPT4Yr/QytmL1D1WD8tmmVrvFtBwUo3iyZhf2TCVkkJ41mOAq+
Vr2tfZPnxUxSaLMU4cm0rUoZ79NK3FX70xJjy4IgQtj/NDuQwv1KgqSClxCKkpI/
BJSvjxzguQENBFk6ZDMBCAD6DTPWTkG7Zqqhz/5oaM4zFG9ncTtnVXiR0rRPzuyn
aXz1/Kn3odEEG4kyEQbHS/lLfnLsqAkSMjUHiBsC6R0h1bTqSDnHEhPqpuLm526N
6j+o5/oIC8ANlGH5fFGDMNXy5MrRuFGMv/tHDuSW56rb6Kh9jPoU+1HCAGpv4zd9
iXB/nOYHlLl3NZ4+883wd0BgIKslSpvOU8sTjqcEwcIFFHr8SBUM6x1nKSq1Ad+C
bEQPBA7EKdIeV/N5CZ9s6fnvUvc1bNMOO8YdJ4wCWfDSGWu7ZdPyj+Yh+9/R7EIW
bEAHX5v/IcAu1u0U53goehGvEu9K4lNjibYRu8hDU02fABEBAAGJASUEGAEIAA8F
Alk6ZDMCGwwFCQEOQt0ACgkQb0xPI4SjE11j5Af9EBukKcDSQfSFBgsRRxLzfk3l
B4Qa7Kb2JKPdf8p+rlC8n7b7nS1WnDsh/ADnRO1+njP4oHo4eV3P09ao9PNagM5k
YldAG2oAOj9RhVSMI4Jt+/Tf7NZixLPjMCakrGA47wQwC9bp+AmV/ST8I6/r2t3W
4uXDLUKzvZxpBUVqEG9x0olnQiBgMicyUlW1WmyMKiorVET6rSnCv3yv33lMRQRG
5txSw/z0jXhhK6ENn0mHMGeiBqGITTwjQM7uQTkw2GmNJfHpg+jThnJQran+cGDZ
tvKsoBc4BFnM4eTxZfeyYPquG2QWZ59kcqm4JRyJfHYKT+CIPQXmEmD4uyp5/Q==
=QxOi
- -----END PGP PUBLIC KEY BLOCK-----

yyyy - год полностью по UTC±0:00
mm - месяц по времени UTC±0:00 (дополняется нулём если меньше 10)
dd - день по времени UTC±0:00 (дополняется нулём если меньше 10)
hh - час в 24 часовом формате по времени UTC±0:00 (дополняется нулём если меньше 10)

Шаблон имени файла маяка (важно для сети BitTorrent):
lighthouse_yyyy-mm-ddThhZ_f43b4866b4f47e8e27bfafa56f4c4f2384a3135d.asc

Для формирования маяка добавьте в конце файла дату и время в формате:
yyyy-mm-ddThhZ
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v2

iQEcBAEBCAAGBQJZOr9iAAoJEG9MTyOEoxNd8RIIALQUa9BuMgh+uET4wse7sBDG
19RvKbUq+9HCOaxL+Qfz+C5VO1XVIxC/Vi2iP1RVwBjkKtTANXdxcf9jR/S2ZHpL
0ohDsJ8O8EPf0sTEr5nvGE27KobEhvAb9TbxrOLuZlWtqWh09CvgyEZPVnnMa7v1
WL6xVln2BjZOkndn60ToqPcVUYQpwIEfHBz7S8WYdRFumN8sUCfJYaXGFElSLKiA
2Y+fTayhPIKcwBTzRuIU6fQWWCUA7egZ3JsWWiBP68parRcsDDOia3UiddFRr0jW
lmGrsOJiZ2Vq2xfXNqY+BfcSdT8AXnt9yUYCdaH2w2oI3uEXlw8eF8JyxO60w8k=
=FJOw
-----END PGP SIGNATURE-----
2017-06-09T15Z

Хеши этого маяка (urn:[название]:[хеш или мультихеш или CIDv1])


urn:sha1:mthkta2fsilqxpoox6vessac37674gl3
urn:tree:tiger:abrmyjcyckt7lccz5jjqebazmt3tkuordfetana
urn:btih:i7mif67cu6r2jkxw23qwyanlx7tqqtoy
urn:ed2k:7f9112c53d5ae16377d54c6bdc34d4f7
urn:sha256:ad300aeb8b4ab6db19b640883d19d0900841c25689ed97e0ec0490cd23a7bd71
urn:ipfs:zb2rhiJLDNbWfS1z6yKcTGzQcazmumkcttkXc4FFq3hfeGn8Y

Соответственно каждый час создаётся новый маяк.


В каждой P2P сети используется соответствующий ей хеш.


Поиск фалов по хешу маяка позволяет передать открытое сообщение в имени файла маяка. (Gnutella2, DirectConnect, Edonkey2000, IPFS)


Поиск используя хеш как часть имени файла позволит передать файл. Это может быть зашифрованное публичным ключём из маяка приватное сообщение. (Gnutella2, DirectConnect, Edonkey2000, IPFS)


Комментарии к файлу маяка также могут использоваться. В них может быть сообщение либо магнит на файл с сообщением приватным. (Gnutella2)


IPFS


Для сети IPFS необходимо найти источники маяка и получить связанные с источниками файлы/каталоги по публичным ключам источников. Далее в списке найти сам маяк или файл/каталог с хешем маяка в имени.


Получаем список пиров с маяком:


ipfs dht findprovs [мультихеш маяка]

Результатом будут мультихеши публичных ключей пиров с маяком


Получаем мультихеш шары:


ipfs name resolve [мультихеш публичного ключа]

Результат /ipfs/ путь с мультихешом связанного файла или каталога


Ищем маяк и файл с хешом маяка в имени:


ipfs refs -r --format=" " [мультихеш шары]| grep "[мультихеш маяка]" 

В результате получаем сообщения:


[мультихеш маяка] [Сообщение в имени файла маяка]
[мультихеш файла] [Имя файла которое содержит в себе мультихеш маяка]

Во втором случае сообщением является содержимое файла. Его можно загрузить командой:


ipfs get [мультихеш файла]

Edonkey2000, DirectConnect, Gnutella(1,2)


Эти сети позволяют поиск по хешу и по имени файла. Так что достаточно хеш использовать как имя либо искать сам маяк по хешу и смотреть шару источников.


Gnutella2


В этой сети возможно комментирование источниками файла. В таком случае достаточно искать маяк по хешу и смотреть комментарии к результатам. Или можно также искать используя хеш как часть имени файла.


BitTorrent


Пока не придумал. Возможность комментирования раздачи убрали из uTorrent.


Есть идея по генерации большого маяка и путём затирания частей формировать штрихкод из которого можно получить хеш нового сообщения.


Второй вариант создание множества маяков и тогда присутствие на раздаче будет 1 а отсутствие 0. Часть маяков будут кодировать хеш а другая часть контрольную сумму.


Заключение


Как то так.


Ссылки


CID (Content IDentifier)
multihash
Commands | IPFS Docs


gpg Man


Интернет на магнитах 1 — Магнит
Интернет на магнитах 2 — Гипертекст
Интернет на магнитах 3 — P2P Сайт и Форум
Интернет на магнитах 4 — Делим магнит на части

Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/330666/


Метки:  

[Из песочницы] Дело было вечером. автомасштабируемый веб-сервис с балансировкой нагрузки на примере Bitrix в Google Cloud Platform

Суббота, 10 Июня 2017 г. 20:35 + в цитатник
Сразу стоит оговориться, что данная статья написана скорее не для того, чтобы показать возможность работы данного продукта на Google Cloud Platform (GCP), он и без этого будет на ней работать. Bitrix был взят для опытов просто как популярная платформа. Он и сам умеет строить пулы, ноды и прочее в своем “веб окружении”, правда со своими грабельками. И именно поэтому были взяты даже машины на Debian для тестов, а не любимый всеми CentOS.

На самом деле материал применим ко многим веб-проектам. Точнее это простенький гайд по построению отказоустойчивых и распределенных приложений на базе виртуальных машин Google Compute Engine, баз Google Cloud SQL и балансировщика нагрузки Google.

Уважаемые знатоки и профессионалы Bitrix, вариантов реализации данного решения очень много, тут приведен лишь один. Можно рассматривать и виртуальные машины, и контейнеры, и Google App Engine в виде облачной платформы. Плюсом ко всему будет возможность подключения хранилища Google Storage, которая была включена в движок уже достаточно давно. Вообще, буду рад обсудить возможность совместного пилота Bitrix на GCP, возможно именно ваш опыт будет описан в следующий раз, как применимый именно к Bitrix.

Приступим


Что такое Bitrix, мы объяснять долго не будем. Это профессиональная система управления веб-проектами и огромное количество корпоративных сайтов, интернет-магазинов, порталов и сообществ работают именно на нем.

Несмотря на объем текста с картинками, у меня вся процедура развертывания заняла около двух часов с учетом выловленных грабелек.

Начнем с создания базы данных


Меню -> Cloud SQL -> Создать экземпляр

Выбираем MySQL, «Дальше», «Создать базу второго поколения».

Указываем имя базы, местоположение, зону, размер машины, тип диска, объем хранилища, расписание резервного копирования, пароль root, время перерыва на тех. Обслуживание, добавляем сеть. Я для теста выбрал машину 1vCPU, 1,7Gb RAM и 200Гб обычного HDD хранилища, потом можем изменить. При создании базы, обращаем внимание на зону, в ней-же расположится виртуальная машина.

Разрешенную сеть для тестов можно поставить 0.0.0.0/0 (все) и подключаться напрямую. Но мы поступим правильно и сделаем по-человечески, не будем тут ничего указывать, безопасность важнее.

image

Далее нам потребуется настроить сервисный аккаунт. Сразу оговорюсь для чего он нужен. Если Вы подключаетесь к базе данных прямо по ее IP-адресу, то этот шаг можно пропустить. Аккаунт нам потребуется для безопасного подключения через прокси. Иначе, машина создастся с записью по умолчанию, которая не имеет доступа к другим службам.

Итак, открываем меню, переходим в IAM и администрирование -> Сервисные аккаунты -> Создать сервисный аккаунт. Название и ИД указываем на свой вкус, выбираем роль Cloud SQL -> Клиент Cloud SQL. OK, закончили.

image

С базой разобрались.

Создаем виртуальную машину


Жмем на «бутерброд» (Меню) -> Compute Engine -> Экземпляры ВМ -> Создать экземпляр.

Возьмем образ Debian 8 или любой другой, на вкус. Берем для старта простую машину: 1 ядро, 3,5Гб RAM.

Изменяем сервисный аккаунт машины на тот, который мы создали на предыдущем шаге (для связи с нашим Cloud SQL. Ставим галки «разрешить HTTP» и «разрешить HTTPS» в зависимости от планируемого протокола.

image

Далее нам нужно слегка автоматизировать нашу машину, точнее запускать на ней SQL-прокси. Для этого раскрываем параметры «Настройка параметров управления, диска, сети и SSH-ключей» и в разделе Автоматизация -> Сценарий запуска пишем:

sudo wget https://dl.google.com/cloudsql/cloud_sql_proxy.linux.amd64
sudo mv cloud_sql_proxy.linux.amd64 cloud_sql_proxy
sudo chmod +x cloud_sql_proxy
sudo ./cloud_sql_proxy -instances==tcp:3306 &

— берем из свойств нашей базы данных, это «Название соединения с экземпляром». Статья по подключению через прокси

Почему так? На самом деле для запуска нужна только последняя строчка, все остальное достаточно выполнить один раз при настройке машины. Это пример, чтобы не разбивать на всю статью по кусочкам.

Далее важный момент. Идем на вкладку «Диски» и снимаем галку «Удалить загрузочный диск при удалении экземпляра». Да, мы будем удалять нашу машину после ее настройки.

image

С созданием машины закончили, нажимаем «создать» и ждем.

По завершении создания, подключаемся по SSH к нашей виртуалке и настраиваем ее. Про настройку самой машины под Bitrix я писать много не буду, материалов полно и на Хабре и в Интернете. Есть даже готовый скрипт “Веб окружение”, но я его не использовал по религиозным убеждениям. Мне было проще все запустить руками, чем разбираться в чужом бутерброде. Опишу только то, что делал я для запуска. Причем без какой-либо оптимизации и тюнинга, иначе меня могут завалить комментариями гуру оптимизации PHP, Zend, NGINX и прочего, без обид.

Для начала обновим:

sudo apt-get update
sudo apt-get upgrade

MySQL-клиент:

sudo apt-get install mysql-client

Apache:

sudo apt-get install apache2

PHP:

sudo apt-get install php5 libapache2-mod-php5 php5-mysql

Рестартуем Apache:

sudo systemctl restart apache2

Далее нам для «пряморукого» подключения к базе данных потребуется Cloud SQL прокси. Скрипт его запуска мы писали при создании машины, он срабатывает при каждом ее запуске. Проверим работает-ли:

mysql -u root -p --host 127.0.0.1

Вводим пароль рута от базы, наслаждаемся строчкой:

mysql>

Если с первого раза не сработает, то проверяем стартовый скрипт машины и перезагружаем ее.

Разворачиваем Bitrix


Обычным wget загружаем прямо с сайта:

Wget https://www.1c-bitrix.ru/download/start_encode_php5.tar.gz

Ссылка указана для примера на стартовую версию. Далее распаковываем файлы в каталог веб-сервера, я оставил по-умолчанию.

С машиной вроде закаончили, переходим к списку виртуальных машин на Google Cloud и открываем нашу машину по HTTP (или HTTPS, если настроили):

image

В процессе установки указываем MySQL сервер 127.0.0.1, логин и пароль root от нашей базы Cloud SQL и далее идем по нужному нам пути в мастере до окончания установки. Когда все готово, можем проверить производительность конфигурации. Что у меня получилось:

image

Я, конечно, обратил внимание на производительность MySQL и по началу был удивлен. Но, как выяснилось позднее, это вполне адекватные данные, база в реальности выдает нормальную производительность, просто она у нас все-таки облачная.

Все работает, перейдем к следующему этапу.

Создадим масштабируемую группу машин и поставим ее за балансировщик нагрузки


Я сделаю это в одном регионе, в Европе. Но для понимания сервис все-таки возможно разнести и по регионам (например, если у нас есть партнеры в Азии). Принцип работы похож, только создавать нужно будет две группы в двух регионах и реплику базы там-же.

Для начала нужно создать образ диска. Удаляем нашу виртуальную машину. Да-да, именно удаляем. Тут важно вспомнить, сняли ли мы при ее создании галку «Удалить загрузочный диск при удалении экземпляра». Если забыли, то клонируем (при открытии машины кнопка сверху) и снимаем ее в параметрах. После удаления машины, нам будет доступен ее диск для создания образа.

Идем в раздел Compute Engine -> Образы, нажимаем “создать образ”. При создании ничего хитрого нет, указываем наш освободившийся диск и все готово.

image

Далее создаем шаблон наших будущим машин с Bitrix.

Идем в Compute Engine -> Шаблоны экземпляров. Создаем шаблон по аналогии с нашей виртуалкой, но уже из нашего образа.

Указываем:

  • Имя
  • Размер (нужный нам размер одной ВМ, далее они будут «размножаться»)
  • Образ (в разделе «Пользовательские образы» указываем созданный ранее)
  • Сервисный аккаунт (созданный нами ранее с правами «Клиент Cloud SQL»)
  • Разрешаем трафик по настроенным протоколам HTTP/HTTPS
  • Раскладываем «Настройка параметров управления, диска и SSH-ключей» и пишем сценарий запуска:

sudo ./cloud_sql_proxy -instances==tcp:3306 &

Повторюсь, этот сценарий включался при создании машины, теперь достаточно только последней строчки. Шаблон настроен. Помним про то, что берем с свойствах нашей PaaS базы данных.

Создадим группу экземпляров:

Открываем Compute Engine -> Группы экземпляров -> Создать группу экземпляров.

Указываем:

  • Имя,
  • Зону (я указывал зону расположения базы),
  • Шаблон (который мы только что создали),
  • Включаем автомасштабирование,
  • Основа для автомасштабирования — балансировщик
  • Минимальное и максимальное количество экземпляров указываем как нам нужно
  • Можем создать проверку состояния. Укажем путь HTTP и, если машина не ответит, то будет прибита и запустится другая.

Создали.

Балансировщик нагрузки


Жмем на «бутерброд» слева сверху, Сеть -> Балансировка нагрузки -> Создать балансировщик нагрузки.

Балансировка нагрузки HTTP/HTTPS -> Начать настройку.

Указываем:

  • Назание, как нам удобно
  • Конфигурация серверной ВМ -> Серверные службы и сегменты -> (раскладываем) Серверные службы -> Создать Серверную службу. Указываем группу экземпляров, которую создавали. Включаем нужный нам режим балансировки (я включал по частоте запросов), указываем значения показателей, Готово. Тут же указываем проверку состояния машин, которую мы создавали, или создаем новую. Можем включить CDN.
  • Конфигурация интерфейсной ВМ. Это то, то будет смотреть во внешний мир из нашего решения. Указываем название, протокол HTTP/HTTPS, тип адреса (статика конечно же), подгружаем сертификат (если нужен).

Готово, создать. Работать начинает практически сразу, можно не ждать.

После создания балансировщика, открываем вкладку «интерфейсные ВМ» и видим его внешний адрес. По нему наш Bitrix и будет работать в Google Cloud Platform. Переходим по адресу, наслаждаемся. Этот адрес можем писать в DNS.

image

Напоследок пара тестов производительности и масштабирования


Сначала, что мы имеем:

  • Cloud SQL база — 1vCPU, 1,7Gb RAM
  • Группа виртуальных машин с масштабированием от одной до семи, каждая 1vCPU, 3,5Gb RAM (можете подобрать оптимальную для вашего проекта, я взял стандартные машины)
  • Балансировщик нагрузки с одним внешним IP-адресом

Запуск дополнительных экземпляров машин я настроил сначала при 100 одновременных соединениях, чтобы показать как работает. Для боевого решения можете использовать собственные параметры. Тестирование проводилось в пробном периоде GCP с ограничением на количество ядер для всех машин всего в восемь штук, потому восьмое ядро заняла машина с которой непосредственно запускался тест.

Сначала получился такой график теста:

image

Стало понятно, что машины не успевают запускаться. Потому их минимальное количество было увеличено до двух, количество соединений на машину уменьшено до 50, а предельная нагрузка снижена до 80%. В итоге получилось:

image

В итоге все взлетело, автоматическое масштабирование заработало как надо.

Конечно много чего можно еще добавить к нашему решению. Например, тюнинг сервера под сам Битрикс, копирование и репликацию базы, отработку отказа и пр. Это тема уже для других материалов. Данная статья написана скорее не для того, чтобы показать возможность работы Bitrix и других веб- и мобильных проектов на Google Cloud. Это простенький гайд по построению отказоустойчивых и распределенных приложений на базе виртуальных машин с применением балансировщика нагрузки Google.

Чтобы организовать распределенное приложение, нам понадобится просто реплицировать базу в другой регион, создать еще одну группу виртуальных машин в другом регионе и включить машины в наш глобальный балансировщик. Google сам будет выбирать оптимальных маршрут и направлять пользователей к приложению в ближайшую локацию.

Чуть позже будет опубликована еще одна статья, как запустить Bitrix на Google App Engine. Думаю, что это будет уже интереснее.

В последнюю очередь коснемся стоимости сервисов:

  • В минимальной конфигурации — 2 VM веб-сервера (1 vCore, 3,5 Gb RAM), 1 VM MySQL (1 vCore, 1,7 Gb RAM), load Balancer) ~100$/мес
  • В максимальной конфигурации (7 VM веб-сервера, 1 VM MySQL, load Balancer) ~230$/мес

Цены естественно приблизительные, для конкретного боевого решения может потребоваться мощная БД, распределение по регионам и прочее. Тут все рассчитано для расположения в европейском ЦОДе без излишеств.

Кроме скромного прайса за впечатляющие ресурсы, радует еще и то, что Google Cloud Platform стала наконец то доступна и в России для безналичной оплаты организациям.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/330664/


[Из песочницы] Подходы к версионированию изменений БД

Суббота, 10 Июня 2017 г. 20:02 + в цитатник

Намного лучше дисциплинарные ограничения убирать инструментарным расширением
Автор статьи


Введение


При разработке информационной системы, то есть программы, нацеленной на хранение, работу с данными, обработку, анализ и визуализацию какой-то базы данных, одним из краеугольных камней стоит задача разработки БД. Когда я только начинал задаваться этим вопросом, казалось – что ни сделай, все равно будет криво.


На протяжении 5 лет разработки нескольких корпоративных ИС, я ставил и пытался решать вопросы, как тот или иной аспект разработки БД сделать удобным. Искал инструменты, помогающие что-то делать с БД, методологии. На удивление в этой области мало наработок. И в каждом подходе сразу видно – вот это нельзя, вот тут будет неудобно, тут слишком много дисциплинарных правил (см эпиграф)… В этой статье я попытался собрать те походы, которые считаю наиболее эффективными, и один, в добавление к собранным, представлю как венец моих исканий, который считаю наиболее «бронебойным».


Эволюция проблемы


Приведу пример, как может задача разработки БД стать проблемой и эволюционировать. Если интересна сразу глобальная постановка задачи со всеми фичами, этот параграф можно пропустить.


  1. Есть один инстанс БД, он же prod, на нем и пользователи работают, и программисты смотрят и меняют данные и схему. Вроде удобно, все очень просто, ничего лишнего, бритва Оккама и все такое.
  2. Хм, а программисты ведь программируют сразу на prod’е – а если сделают что-то не то? Залочат данные, или удалят что-то важное? Да или просто нагрузят сервер в поисках нужной оптимизации. Любое решение такой уже возникшей ситуации для пользователей будет плохо. Нет, надо бы отделить БД для разработки dev от prod’а. Возникает вопрос – как переносить изменения с dev’а на prod?
  3. Хм, а как бы нам следить за тем, когда и что изменилось в БД? За кодом приложения-то мы следим, а код хранимых процедур и всего остального в БД? Будем скриптовать некие скрипты с alter’ами? А как версионировать create? Он ведь повторно ненакатываемый, в отличие от alter’а.
  4. Хм, все программисты на одном dev-сервере, он же test для пользователей. То есть тестирующие пользователи могут наткнуться на то, что программист еще не доделал, чего не должно быть видно пользователю. Или просто сломано. Надо бы разделить dev с test’ом.
    (на самом деле на эту проблему во многих ИС можно не обращать большого внимания)
  5. А ведь такая же ситуация с программистами – если два программиста меняют БД, то могут быть какие-нибудь артефакты. Один что-то меняет, а приложение к этому еще не готово – и у всех проект не запускается. Надо бы каждому программисту по своему инстансу БД. Чтобы у каждого своя песочница была. А когда БД очень большая?
  6. Опа, а как же теперь все изменения переносить на prod? Если использовать сравнение схем, то БД одного программиста перетрет изменения другого программиста. И как изменения одного программиста переносить в инстанс другого? Переносить бекап с prod’а каждому программисту? А потом его изменения как на этот бекап накатывать? Нет, ерунда, как-то тут какие-то скрипты должны играть роль.
  7. Постойте, а как быть с теми данными, которые должны быть одинаковыми на всех инстансах (справочники и персистентные справочники)? Они будут переноситься с бекапами? Или все же скриптовать insert’ы или merge’ы и скрипты версионировать? А как их запускать – всегда все сразу, или все же только те, которые поменялись? А как порядок регламентировать?
  8. А похоже, бывает, что есть такие данные и процедуры, которые должны быть разными на разных инстансах! Настройки, репликации, связи с другими сервисами. При шардинге (sharding). Как быть с ними? Перенос бекапа и последующий скрипт изменения, свой для каждого инстанса? Так-то можно, но не все различия этому поддаются – например, memory optimized tables не могут быть преобразованы в обычные таблицы, не считая того, что поднимать бекап с ними может быть просто невозможно, когда не хватит памяти у test-сервера или инстанса программиста.
    К слову, с этим я столкнулся, когда мне нужны было сделать memory optimized tables на 20 ГБ, а еще когда на prod’е была целая система синхронизации с другой системой, чего не должно было быть на других инстансах.
  9. Ммм, а как бы сделать так, чтобы легко можно было поднимать сразу целый новый инстанс БД? Что, если все настройки и БД, и SQL Server’а скриптовать и версионировать?
    Это удобно, когда у каждого программиста свой SQL Server, а не когда сервер один с кучей баз. Или шардинг. Или новый тестовый контур для новой большой бизнес-фичи.

Какие-то из пунктов этой эволюции могут показаться излишними и вообще придумыванием себе проблем, но при их решении удобным инструментом разработка БД станет почти такой же простой и безболезненной, как и разработка обычного кода приложения.


Собрав в одну кучу все возможные проблемы, связанные с разработкой БД, которые увидел при разработке информационных систем, я пришел к глобальной формулировке постановки задачи в моем видении.


Полная универсальная постановка задачи


Задачи и проблемы, которые могут вставать при разработке БД:


  1. Нужно версионировать:
    A. Схему БД. То есть нужно иметь возможность в каком-либо виде сохранять историю изменений схемы БД.
    Это самая основная цель, на которую навешиваются все остальные возможности. Версионирование схемы БД – краеугольный камень разработки БД. Тут может быть два пути. Можно поток изменений всех объектов БД сохранять одним глобальным потоком, и можно как-то версионировать каждый конкретный объект. Я считаю (см п6), что второй путь в итоге намного практичнее.
    B. Некоторые данные: справочники динамические, справочники персистентные (persistent, статические).
    Цель важная, если от этих данных зависит код приложения (тогда они будут персистентными). То есть, если какая-то таблица соответствует одному enum в коде. Или же, иначе, цель второстепенная, но очень полезная для контроля добавления/изменения статичных и не очень данных, а также для автоматизации deploy'я изменений БД и релиза разных контуров (п2) – если их более одного.
    C. Скрипты преобразования данных (миграционные скрипты). Они могут быть связаны либо с изменением схемы БД, либо с бизнес-задачей. Для таких изменений важен порядок, и иногда требуется повторное исполнение.
    Не во всех ИС такие скрипты будут существовать. Но, если они существуют, то эта цель обязательна.
    D. Настройки БД.
    Примеры важных настроек БД, которые стоит версионировать – если должен быть включен service broker (он необходим для sql dependency), change tracking (он может использоваться для более сложных кешей), опция read_committed_snapshot, должен быть нужный user БД для приложения, настройка оповещений об ошибках, настройка поддержки memory optimized tables. Польза от сохранения и версионирования настроек БД в том, что можно легко сделать новую БД, работающую полностью так же, как основная. И что в удобном виде можно провести code review, согласовать изменения. А также просто для того, чтобы была история – что, когда и кем было сделано.
    E. Еще некоторые объекты SQL Server’а – джобы, некоторые настройки сервера, оповещения, логины, связанные серверы (для интеграции с другими системами).
    Это может быть нужно, когда тестовые контуры должны полностью повторить prod, вместе с обработкой в джобах. Что кажется совершенно обычной ситуацией.
    Кроме того, если все настройки, джобы и прочее заскриптованы, то можно легко поднять новый контур. Запустил один скрипт – и все новое SQL-окружение полностью рабочее.
  2. Нужна поддержка многих контуров: один prod (а может быть и не один! – например, при шардинге, или когда продукт коробочный и у каждого клиента своя БД), много тестовых – не только два тестовых контура (prod-test, dev-test), но и по контуру на каждого программиста, или по контуру на большую фичу.
    A. Поддержка многих контуров означает, что должна быть возможность изменения одного контура перенести в любой другой контур, не испортив его и не затерев те изменения, которые у него уже тоже, возможно, есть.
    То есть, если у каждого программиста своя БД-песочница, надо изменения одного программиста переносить не только на prod, но и на все остальные инстансы. Даже если там другой программист тоже что-то сделал.
    B. Бывает, что схема в разных контурах может немного отличаться – например, в тестовом окружении нужны дополнительные объекты (вьюшки, хранимки), а может и наоборот, из какого-то тестового контура надо исключить кусок БД, интеграцию с другой системой etc. Также в случае, когда в одном инстансе есть memory optimized tables, а в другом они не поместятся в память и эти таблицы должны быть обычными.
    C. В разных контурах версионируемые данные могут быть разные – например, настройки запуска приложения, доступа к другим системам. Также тестовые данные для тестовых контуров.
    Можно провести аналогию со случаем, когда у разных программистов, или на разных publish-хостах приложения должны быть разные файлы app.config – что достигается с помощью фичи Visual Studio app config transformation. Аналогично что-то в БД может отличаться в разных инстансах.
    D. То же для настроек БД и сервера. Например, на prod/prod-test нужны все джобы, а на локальных БД у программистов – не все. Поддержка memory optimized tables – на prod’е нужна, а на других контурах может быть и нет.
    E При восстановлении бекапа с prod’а на другой инстанс нужно иметь возможность привести восстановленную БД в соответствие с нужным контуром.
    Иногда это может быть невозможно (опять же – если есть memory optimized tables), но, если это возможно, то это очень удобная фича, когда нужно все данные одного инстанса БД перенести на другой, что наиболее просто сделать переносом бекапа.
  3. Задачи, связанные с версией БД:
    A. Когда программист делает изменения в БД, не все из которых должны быть в prod’е, нужно оставить ненужные – если программист их сделал для своих тестовых целей – и перенести на prod (и другие инстансы) только нужные.
    Программисту удобно оставить в своей песочнице все, что он хочет, хотя переносить на другие инстансы необходимо ровно то, что нужно. То есть если он сделал для себя какую-то удобную и нужную только ему вьюшку, процедуру, или даже таблицу – это не должно попасть на prod.
    Этот пункт почти эквивалентен 2bc, но тут имеется в виду, что могут быть объекты БД, которые можно не версионировать, а просто вручную создать в своей песочнице.
    B. Накат к какой-то конкретной версии БД (большей, чем текущая на выбранном контуре).
    Это бывает нужно, если разработка БД идет отдельно от разработки приложения. Если же изменения БД идут вместе с изменением приложения, то различных будущих версий БД просто не должно быть. То есть должен быть подход «не готово/портит/не поддерживается приложением – не делай коммит, по крайней мере в общую ветку репозитария». А если же изменения в БД есть, то они должны быть обратно совместимы. Например, удаление/переименование столбца должно сопровождаться соответствующим изменением приложения, а добавление – не обязательно. В статье 3 (Эволюционный дизайн баз данных) такие изменения автор называет деструктивными – я согласен с этим термином.
    C. Откат к старой версии, по аналогии с простым git checkout при обычном программировании приложения – невозможен в общем виде. Потому что, при наличии данных в таблицах, преобразовать их к старой (то есть произвольной) схеме невозможно.
  4. Иногда может быть полезно создать чистую БД с данной схемой и данными справочниками (или без справочников) – для тестов, например.
    A. При автоматизации тестирования (continuous integration) можно для прогона тестов создавать свежую чистую БД, или с наполнением случайными данными (бесплатных инструментов для этого не знаю, платные есть у redgate, devart) или поднабором рабочих данных (в этом может помочь Jailer).
    На практике реальную пользу от этого можно извлечь, только если этот процесс будет прост и быстр как щелчок пальцами – иначе это делать никто не будет. Ведь можно использовать обычную тестовую БД.
  5. Если какое-то изменение уже было накатано – в штатном режиме предусмотреть, чтобы оно не было накатано повторно, или делать изменения идемпотентными.
    На практике правило «делать все изменения идемпотентными» слишком дисциплинарно сложное. Намного лучше дисциплинарные ограничения убирать инструментарным расширением.
  6. По каждому объекту БД нужно иметь возможность увидеть историю его изменений.
    A. Для контроля нужно иметь возможность увидеть, какие изменения будут накатаны, в конкретных SQL-скриптах («накопительные скрипты»).
    B. Очень желательна возможность code review. Причем явный оператор alter предпочтительнее сравнения операторов create table (на основе которых делается diff и впоследствии накатывается), поскольку лучше контролировать действия с БД, а не декларации. А для процедур и подобных объектов надо иметь возможность видеть diff тела.


  7. На используемые инструменты нужны лицензии. При подходе shared db (что может провоцировать проблемы и конфликты изменений – см п2) достаточно одной (когда только один специально выделенный человек производит накат изменений), а при подходе, когда у каждого программиста есть своя БД – каждому программисту по лицензии.

В моей практике я руководствовался пунктами:


  • 1 и 2 – от корки до корки;
  • 3a;
  • 3b не использовал, потому что всегда в моих проектах БД с приложением были целостной одной системой и разрабатывались полностью совместно;
  • 4 – пробовал использовать, но тесты в целом у меня не очень прижились, потому что требуют дополнительного ресурса, ну или перестройки парадигмы для TDD;
  • 5 – обязательно;
  • 6 – важный, хотя довольно редко;
  • лицензии не требовались, потому что инструмент для выбранного подхода бесплатен.

Так что я считаю обязательными все пункты, кроме 3b и 4.


Подходы


Я пришел к выводу, что целесообразно выделить подходы:


  1. Сравнение схем целевой БД и БД-источника.
  2. Сравнение заскриптованной схемы (и данных) с целевой БД.
  3. На основе последовательных (инкрементальных) ручных SQL-скриптов.
  4. На основе ручных независимых SQL-скриптов, структура которых повторяет схему БД.

В этом списке подходы отсортированы по увеличению полезности.


Похожие статьи


  1. https://habrahabr.ru/post/121265/ — описывает кратко подходы 2, 3. Еще есть подход с последовательностью идемпотентных изменений, но я его откидываю ввиду слишком высокой сложности поддержки идемпотентности скриптов, когда их количество велико. Не считая того, что просто запустить 1000 скриптов, даже если они ничего не будут в итоге изменять, тоже занимает время (и размер лог-файла наката). Тут должен быть подход «если изменение уже накатано, не надо его накат повторять» (п5).
  2. https://habrahabr.ru/post/258005/ – комбинация подходов 3 и 1 — на основе redgate SQL Source Control и redgate SQL Compare. (статья плохо описывает работу с БД, в основном она о любви к Atlassian) – как я понял, сначала, при коммите, они накатывают скрипты на DB QA, потом сравнением схемы оно переносится на prod.
  3. https://habrahabr.ru/post/312970/ — хорошая длинная статья, подход очень похож на предыдущую статью. Используют CI, чтобы на каждый коммит накатывались изменения на БД QA, и выкатывался артефакт-накопительный скрипт изменений БД для наката на prod. Смысл этого артефакта не очень понятен, если сами скрипты в коммите. Квинтэссенция в картинке.

В целом к идеи автоматизации наката скриптов по коммиту я бы отнесся крайне настороженно – иногда бывает, что коммиты делаются неготовыми или неполными. Дисциплинарное правило «коммить в мастер только готовый код» на практике работает очень плохо. Лучше его избегать улучшением инструментария – для этого существует класс инструментов continuous integration (например, TeamCity от JetBrains или совсем бесплатный Jenkins). Я за то, чтобы накат скриптов на БД происходил исключительно осознанно человеком-программистом и только в нужные моменты времени – которые никак не должны быть связаны с коммитом.


1 Сравнение схем целевой БД и БД-источника


Инструмент


Redgate SQL Compare. Еще есть http://compalex.net/, но он работает только с php. Есть и другие инструменты сравнения схем БД.


Методология


Кроме БД prod – она целевая БД – делается БД dev – она БД-источник.


Каким-либо образом делаются изменения в БД-источнике. Причем эта БД-источник получается не тестовая в общепринятом смысле, потому что с ней нельзя делать все, что угодно – подразумевается, что все изменения (по крайней мере, изменения схемы БД) должны перенестись на целевую БД. Далее эти изменения могут скриптоваться, но эти скрипты впоследствии никак не используются – потому что, если их использовать и накатывать каким-либо образом, то вся суть подхода исчезает, сравнение схем становится бессмысленным. Эти скрипты могут лишь играть роль истории изменений. Но которая может отличаться от реальности, поскольку можно что-то визуально в Management Studio (или другом GUI для БД) поменять и забыть это заскриптовать. Или заскриптовать неправильно. Потом, в момент деплоя на целевую БД, делается (с помощью инструмента) diff-скрипт, который накатывается, приводя целевую БД в состояние равной схемы с источником.


Плюсы


  • Для тех, кто боится писать sql. Изменения в БД-источнике можно делать визуальными средствами (с другой стороны, я не считаю это хорошей практикой).
  • Скрипты необходимо писать только для преобразования данных (опять же, я считаю, что скрипты для программиста должны быть отправной точкой, и уходить от них не стоит).

Минусы


  • Невозможно все, что связано с несколькими контурами (п2).
    Если я делаю изменения на своей выделенной тестовой БД-песочнице – я должен перенести эти изменения на БД-источник для prod’а. Если это делать по такой же схеме, то это дает дыру – перенос тех случайных или личных изменений, которые я внес в свою БД (я ведь могу делать в своей песочнице все-все-все, правда?). Скриптуются ведь только нужные изменения, но переносятся все. Кроме того, то я затру изменения, которые переносит другой программист.
  • Если во втором контуре есть хоть какие-то отличия, нужно для него делать еще одну БД-источник. То есть, если в тестовом контуре есть какие-либо настройки или данные, отличающиеся от prod’а, то либо для него нужна вторая БД-источник, либо как-то еще решать эту проблему.
  • Отсутствие контроля над скриптами переноса изменений – будут накатаны скрипты, которые сгенерит инструмент. Иногда это может быть плохо, надо учитывать специфику. Например, некоторые изменения требуют пересоздания таблицы. Что делать нужно либо крайне осторожно, либо вообще не делать (если таблица часто используемая, очень большая).
    Если же брать этот diff-скрипт, его смотреть и править, то тогда снова вся суть сравнения схем исчезает – можно ведь сразу писать скрипты.
  • Заскриптованная история изменений объектов может отличаться от реальной. Потому что инструмент сравнивает именно схему баз, а не скрипты – можно (случайно, конечно) что-то поменять и не занести это в скрипты. Или занести в скрипты не совсем правильно.
  • Все равно нужны дополнительные усилия и инструменты для поддержки:
    — миграционных скриптов (преобразования данных),
    — версионирования справочников и персистентных данных.
  • Инструмент не перетягивает настройки БД и настройки SQL Server'а.
  • Если кто-то другой накатил что-то на БД-источник, при deploy’е оно тоже перетянется.
  • Зависимость целевой БД от другой БД – в том смысле, что prod должен идти в комплекте со своим источником.
  • Бесплатных хороших инструментов нет, а redgate SQL Compare дорогой. Причем нужна лицензия для инструмента на всех хостах, с которых будет осуществляться миграция на любую целевую БД.

Подход не решает задачи


  • 1b — подразумевается, что для этого надо использовать SQL Data Compare; 1c; 1d; 1e.
  • Для 2 – дополнительные и весьма весомые усилия. Проще отказаться от нескольких контуров, чем их поддерживать.
  • 3a – инструмент перетягивает изменения схемы либо всех объектов, либо выбранных. То есть нужно при deploy’е просмотреть список всех объектов и отщелкнуть ненужные. Принципиально это решает задачу, но… Так делать никто не будет. Снова правило «намного лучше дисциплинарные ограничения убирать инструментарным расширением». Не хватает что-то типа файла .gitignore.
  • 6 – нужны дополнительные усилия, история изменений может расходиться с реальной.

2 Cравнение заскриптованной схемы (и данных) с целевой БД


Инструмент


Я не знаю, какой инструмент мог бы сравнить две схемы БД не по самим базам, а по скриптам. Поэтому этот подход теоретический. Но, если найти или сделать такой инструмент, кажется, подход стал бы неплохим. Особенно, если этот инструмент мог бы сравнивать и данные.


Методология


Роль БД-источника тут играл бы каталог с скриптами, полностью создающими БД – и схему, и версионируемые данные (справочники, персистентные справочники). То есть программист вносит изменения в эти скрипты, запускает инструмент, который сравнивает весь каталог с целевой БД и делает diff-скрипт, который либо сохраняется для code review, либо сразу накатывается.
Так как инструмента нет, то можно лишь фантазировать о том, как этот инструмент мог бы сравнивать данные и настройки БД и SQL Server’а.


Плюсы


  • Отлично решена задача истории изменений каждого объекта БД (п6).
  • Отлично решена задача песочницы (п3a), как и в оставшихся подходах, основанных на скриптах. Не нужны изменения – не вноси в скрипты.

Минусы


  • Аналогично подходу 1, невозможно все, что связано с несколькими контурами (п2).
  • Аналогично подходу 1, нет контроля над накатываемыми скриптами.
  • Та же проблема с миграционными скриптами.

Подход не решает задачи


  • 1c.
  • 1d, 1e – неизвестно, какой инструмент.
  • Для 2 – дополнительные и весьма весомые усилия. Проще отказаться от нескольких контуров, чем их поддерживать.

3 На основе последовательных (инкрементальных) ручных SQL-скриптов


Инструмент


flyway db. Возможно, есть альтернативы (https://github.com/lecaillon/Evolve – я не готов рассказать об этом инструменте, но, похоже, делает что-то похожее).


Методология


Методология подхода наиболее проста. Зачаровывающе проста. По мере необходимости пишутся sql-скрипты изменений – произвольные, как на изменение схемы, так и на изменение данных. Не имеет значения, какие скрипты. Файлики нумеруются, складываются в папочку. В нужное время запускается инструмент, который в порядке нумерации накатывает новые, то есть еще не исполненные файлы скриптов на выбранную БД. Накатанные запоминает в специальной табличке, то есть повторно скрипт не исполнится.


Так работает компания Qiwi. Или работала, когда я там участвовал в разработке платежной системы. Но там без инструментов, инструмент заменяют дисциплинарные правила. Есть несколько QA-сотрудников, которые следят за специальным репозитарием git и накатывают новые скрипты, сначала на тестовую БД – смотрят, не сломалось ли чего, потом, если все хорошо, на prod.


Плюсы


  • Крайне, необыкновенно просто. Пишешь скрипт, кладешь в папочку, и все. Думаю, благодаря этому этот подход самый распространенный.
  • Полный (абсолютно полный) контроль на накатываемыми изменениями. Никакой генерации скриптов.
  • В том числе можно в скриптах хранить и данные (п1b), и миграционные скрипты (п1c), и настройки и БД, и SQL Server’а (п1de).
  • Можно накатывать до какой-то конкретной версии (п3b).
  • Решена проблема многих контуров, но без отличий в них (п2a).

Минусы


  • Нельзя выделить историю изменения одного объекта, alter’ы на один объект разбросаны по многим скриптам, многим файликам.
  • При параллельной работе двух программистов, они могут создать скрипты с одинаковыми номерами.
  • Раз уж подход скриптоцентрированный, то не хватает фич:
    — Некоторые скрипты хотелось бы накатывать при их изменении. То есть добавил строчку в скрипт версионируемого справочника, и он исполнился, и строчка в таблице появилась. В таком виде можно хранить историю изменения данных (см подход 4).
    — Некоторые скрипты хотелось бы накатывать при каждом deploy’е – это, например, какая-нибудь очистка, занесение персистентных справочников, которые должны версионироваться (поэтому их нельзя заносить вручную в БД).
  • В последовательности произвольных скриптов крайне сложно разбираться. Создание таблиц, их alter’ы, добавление строчек в справочники, миграции – разбросаны практически хаотично в одной последовательности. Хотелось бы иметь алфавитную сортировку файликов, различные папочки. Словом, хочется в скриптах видеть структуру БД. Можно, конечно, что-то придумать – сделать кучу папочек, сделать огромный bat, запускающий инструмент на эти папочки в нужном порядке… Да, это начало следующего подхода, 4го.

Подход не решает задачи


  • 2bcde,
  • 6b.

4 На основе ручных независимых SQL-скриптов, структура которых повторяет схему БД


Инструмент


liquibase. Возможно, есть альтернативы (redgate SQL Source Control, https://www.quora.com/What-are-the-alternatives-to-LiquiBase – но я не вполне хорошо знаю, как они работают).


Методология


Идея


Для создания и изменения схемы на каждый объект БД создаем по файлику, в котором будет скрипт, отвечающий за этот объект – таким образом при версионировании файлов получаем историю изменений на каждый объект. Эти файлики кладем в папочки, повторяющие структуру БД. Так как последовательность исполнения скриптов важна, вводим управляющие файлы, содержащие последовательность наката скриптов, а инструмент делает написание этих управляющих файлов достаточно простым и решает, какое изменение накатывать нужно, а какое нет – если оно уже было накатано ранее или отфильтровано. Кроме того, если нужно различие в чем-то в различных инстансах БД, вводим значения переменных, которые инструмент использует, модифицируя нужным образом скрипты для каждого инстанса. Кроме того, можно ввести фильтры на скрипты, и, в зависимости от контекста («только изменение схемы», «только импорт справочников», «создать/обновить только такой-то кусок схемы») отфильтровать скрипты.


Для изменения таблицы нужно в файлик с ее create’ом дописать скриптик (changeset) с оператором alter или create index или каким-то другим. Или можно изменить существующий соответствующий changeset, если возможно сделать его повторнонакатываемым.


Для изменения процедуры/функции/триггера/вью надо поменять код в файлике, соответствующем этому объекту. Чтобы этот скрипт был повторнонакатываемым, нужно в первом changeset’е сделать создание этого объекта с пустым телом, а во втором – оператор alter с нужным телом (жаль, у SQL Server’а нет оператора create or alter). Тогда первый changeset будет исполняться только один раз, а второй – при изменении.


Ну а для непосредственно deploy’я делаем bat-файл(-ы), запускающие инструмент с нужным контекстом и настройками. Таким образом нужный deploy будет запускаться посредством запуска соответствующего bat’ника.


Можно настроить, чтобы логи запусков (какие changeset’ы исполнялись и сколько времени) сохранялись. Добавив их в .gitignore.


Файлы


Делаем следующую структуру папок:


<Имя БД>


  • CLR_Objects
    Тут будет по 1 sql-файлику на каждую CLR сборку. В этом файлике – сначала drop function, потом drop assembly, потом create assembly, потом create function. Сами сборки придется хранить в скрипте в виде base64, но можно сделать простенький инструмент, автоматизирующий это преобразование:
    Clipboard.SetText("0x" + string.Join(“”, File.ReadAllBytes(path).Select(b => b.ToString("x2"))) )
  • Tables
    Тут будет на каждую таблицу по директории, в которой раздельно файлики создания таблицы и скрипта merge для версионируемого справочника.
    — <имя таблицы1>
    — <имя таблицы2>
    — …
  • Types
    Тут будет по 1 sql-файлику на каждый тип.
  • Functions
    Тут будет по 1 sql-файлику на каждую функцию.
  • Views
    Тут будет по 1 sql-файлику на каждую вьюху.
  • Stored_Procedures
    Тут будет по 1 sql-файлику на каждую хранимую процедуру.
  • Triggers
    Тут будет по 1 sql-файлику на каждый триггер.
  • Migration_Scripts
    Тут будет последовательность скриптов изменения данных.

core
Тут будет сам инструмент и его настроечные файлы.


Как видно, идея этих папок в повторении схемы БД и соответствии каждого объекта своему одному файлу.


В головной папке <Имя БД> будут файлы:


  1. Скрипт создания БД (create database, alter database — настройки, пользователи, роли)
    create_db.sql
  2. Файл(-ы) со скриптами, изменяющими настройки SQL Server’а и саму БД (sp_configure, sp_add_job и ее собратья, sysmail_add_account_sp и ее собратья, alter database etc). Хотя скрипты джобов можно хранить для удобства в различных файликах.
    config_db.sql
  3. Файл с последовательностью наката таблиц. Команда инструмента includeAll исполняет скрипты в алфавитном порядке, что некорректно создаст таблицы ввиду наличия ссылок в них, поэтому нужен файл, регулирующий последовательность наката таблиц.
    tables.xml
  4. Файл с последовательностью наката вьюх. Вьюхи могут зависеть друг от друга, поэтому важен их порядок наката.
    views.xml
  5. Файл с последовательностью наката скриптов изменения данных. Для миграций данных тоже важен порядок.
    migrations.xml
  6. Файл с командами инструменту как создавать схему БД. В нем будут ссылки на файлы 1 и 2 с созданием и настройкой БД и сервера, а потом на папки CLR_Objects, Tables (точнее, файлик tables.xml), Types, Functions, Views (точнее, файлик views.xml), Stored_Procedures, Triggers – именно в этой последовательности. Так же в этом файле можно задать значения переменным, от которых будут зависеть скрипты – если в различных инстансах БД есть отличия.
    master_Scheme.xml
  7. Файл со всеми командами инструменту. В нем сначала идет ссылка на предыдущий файл создания схемы, потом на дополнительные действия – преобразование/миграция/очистка данных (ссылка на папку Migration_Scripts, а точнее, файлик migrations.xml)
    master.xml

Заметим, что для создания/изменения хранимых процедур, триггеров, функций и типов последовательность не важна. Поэтому достаточно команды includeAll инструмента, накатывающего их в алфавитном порядке.


Как использовать


Для каждого варианта использования нужно создать bat-файл, запускающий инструмент с соответствующим контекстом – например, deploy_local_scheme.bat, deploy_local_full.bat, deploy_prod_scheme.bat, deploy_prod_full.bat, deploy_.bat etc. В одном проекте у меня таких файликов было аж 18 – там была целая система миграции данных, и нужно было регулировать, когда какую миграцию исполнять.


Кроме контекста, bat-файл в себе должен содержать connection string и имя команды инструмента.
Возможные команды:


  • update. Основная команда исполнения всех нужных changeset’ов.
  • status. Покажет, какие changeset’ы будут накатаны.
  • udpateSQL. Покажет полный sql-скрипт, собирающий в себе все накатываемые changeset’ы.
  • changelogSync. Заносит новые changeset’ы как исполненные, не накатывая их. Нужно использовать, когда изменение накатано вручную. Должна использоваться максимально редко.

Я еще для удобства просмотра логов исполнения сделал вывод их в текстовый файлик:
> %outputfilename% 2>&1


Changeset’ы могут быть помечены атрибутами:


  • runOnChange – при =true указывает инструменту накатить changeset при его изменении (если написать =false, то после изменения changeset'а будет ошибка вида «накатанный changeset был изменен»);
  • runAlways — указывает накатывать changeset при каждом deploy’е.

В случае, когда нужно изменить схему так, что сломаются какие-то скрипты изменения данных, то есть миграционные скрипты (например, если нужно изменить название таблицы, колонки, удаление чего-то), то нужно написать соответствующий alter или sp_rename в файлик, соответствующий данной таблице, и соответствующим образом изменить нужные скрипты. Далее, для одноразовых скриптов из них нужно сделать так, чтобы инструмент не выдавал ошибку, что накатанный changeset изменился. Это достигается двумя путями – либо команда changelogSync, либо вручную изменить соответствующую строку в таблице инструмента, обновив там md5-сумму – значение ее подскажет сам инструмент.


Плюсы


  • Полный (абсолютно полный) контроль на накатываемыми изменениями. Так же как в подходе 3.
  • Аналогично подходу 2, очень удобно проводить code review, смотреть историю изменений всех объектов.
  • Различия в контурах (п2bcde) поддерживаются с помощью переменных. То есть можно сделать по bat-файлу на контур, в котором будут задаваться значения переменных, а в скрипте это значение использовать, или changese’ы можно помечать label’ами, которые можно отфильтровать или нет в зависимости от контура.
  • Даже чистую БД можно создать (п4).

Минусы


  • Сложный в первоначальной настройке. Но это только сложность вхождения. При использовании такой сложности нет – нужно лишь добавить скрипт-changeset в нужный файлик, и все.
  • В длительной разработке, когда одну таблицу нужно в течение времени менять много раз, в некоторых случаях будут множиться changeset’ы с оператором alter table. Но в большинстве случаях этого не будет — если всегда писать многоразовые changeset’ы. Это минус относительно подхода 2 (при котором можно править операторы create), а вот для подхода 3 это усовершенствование – там вообще все изменения будут линейно друг за другом складываться.
  • Инструмент очень плохо поддерживается и развивается. Его пишет один человек как хобби. Но сегодняшней функциональности хватает. Кроме того, инструмент несложен, можно похожий сделать самостоятельно за месяц.

Подход не решает задачи


Только необязательный в моем видении п3b. Победа.

Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/330662/


Метки:  

[Перевод] Внимание! Хакеры начали использовать уязвимость «SambaCry» для взлома Linux-систем

Суббота, 10 Июня 2017 г. 18:44 + в цитатник


Помните SambaCry?

Две недели назад мы сообщали об обнаружении в сетевом программном обеспечении Samba (иная реализация сетевого протокола SMB) критической уязвимости 7-летней давности. Она обеспечивает возможность удалённого выполнение кода и позволяет злоумышленнику взять под контроль уязвимые Linux- и Unix-машины.

Чтобы узнать больше об уязвимости SambaCry (CVE-2017-7494), вы можете прочитать нашу предыдущую статью.

В то время было обнаружено, что в Интернете существует около 485 000 компьютеров с поддержкой Samba и открытым портом 445. Исследователи предсказывали, что атаки на основе уязвимости SambaCry могут распространяться так же как WannaCry ransomware.

Предсказание оказалось довольно точным. Компьютер-приманка, созданный командой исследователей из «Лаборатории Касперского», подцепил вирус, который использует уязвимость SambaCry для заражения компьютеров Linux — загрузки инструкций и криптомайнера.

Специалист по безопасности Омри Бен Бассат независимо от «Лаборатории Касперского» также обнаружил этот вирус и назвал его «EternalMiner».

По мнению исследователей, неизвестная группа хакеров начала захват Linux-компьютеров в состав ботнета всего через неделю после того, как уязвимость Samba была публично раскрыта. Попав на компьютер жертвы, вирус устанавливает модернизированную версию «CPUminer» — программного обеспечения для криптомайнинга цифровой валюты «Monero».

С использованием уязвимости SambaCry злоумышленники выполняют две служебные нагрузки в уязвимых системах:

INAebsGB.so — обратная оболочка, обеспечивающая злоумышленникам удаленный доступ.
CblRWuoCc.so — бэкдор, который включает в себя утилиты для запуска CPUminer.

По мнению исследователей из «Лаборатории Касперского», через обратную оболочку, оставшуюся в системе, злоумышленники могут изменить конфигурацию уже запущенного майнера или заразить компьютер жертвы другими типами вредоносных программ.

Майнинг криптовалют может быть дорогостоящей инвестицией, поскольку для этого требуется огромная вычислительная мощность. Такое вредоносное ПО упрощает этот процесс для киберпреступников, позволяя им использовать вычислительные ресурсы заражённых систем для получения прибыли.

Возможно, вы помните статью об Adylkuzz, вирусе-майнере, который использовал уязвимость SMB в Windows-системах по крайней мере за две недели до начала атаки WanaCry.

Вредоносная программа Adylkuzz также добывала Monero, используя огромное количество вычислительных ресурсов взломанных Windows-машин.


Счёт хакеров по состоянию на 08.06.2017

Организаторы ботнета для майнинга на базе SambaCry уже заработали 98 XMR, стоимость которых сегодня составляет 5 380 долларов. Эта цифра постоянно растет с увеличением числа заражённых Linux-систем.

«В первый день они получили около 1 XMR (около 55 долларов по обменному курсу на 10.06.2017), но за последнюю неделю доход вырос до 5 XMR в день», — говорят исследователи.


Журнал транзакций со всеми доходами злоумышленников

Разработчики Samba уже исправили проблему в новых версиях Samba 4.6.4 / 4.5.10 / 4.4.14 и настоятельно призывают тех, кто использует уязвимую версию Samba, установить патч как можно скорее.

P.S. Другие интересные статьи из нашего блога:
-> Как принять закон или обработка данных в распределённых системах понятным языком
-> ТОП 100 англоязычных сайтов об IT
-> Балансировка нагрузки в Облаках
-> Лучшие игрушки для будущих технарей времён нашего детства (СССР и США)
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/330660/


[Перевод] Интеграция React и DataTables — не так тяжело, как рекламируют

Суббота, 10 Июня 2017 г. 17:30 + в цитатник

Несколько месяцев назад я искал React-компонент для отображения таблицы данных в одном из наших веб-приложений в Undertone. В предыдущем проекте, который не был основан на высокоуровневой библиотеке, такой как React, мы использовали jQuery-плагин DataTables, и мы были очень довольны той гибкостью, которую он предлагает. Теперь я искал нечто похожее, которое можно легко интегрировать как React-компонент в наше новое приложение.


Проведя довольно обширные исследования, я узнал две вещи. Во-первых, это то, что не рекомендуется использовать DataTables вместе с React, потому что обе библиотеки управляют DOM. Во-вторых, не было библиотеки, которая обеспечивала гибкость, необходимую для нашего веб-приложения (с момента моего исследования). Крайний срок для сдачи проекта приближался, и мне нужно было что-то выбрать. Я решил пойти по непопулярному пути и интегрировать DataTables в свой проект. Результат получился намного лучше, чем я ожидал, и весь процесс интеграции на самом деле был довольно плавным. В следующих разделах я опишу схему проекта, в котором работает интеграция React + DataTables.


Предварительная установка


Мы будем использовать Create-React-App (CRA) для строительных лесов нашего проекта. Если вы не знакомы с CRA, это в основном инструмент для создания приложений React без конфигурации. Первое, что нам нужно сделать, это установить CRA (если вы его еще не установили) и инициализировать новый проект с помощью команды create-react-app (см. документацию по CRA для получения подробных сведений о том, как инициализировать и запустить проект).


Мы завершаем нашу установку, добавляя модули jQuery и Datatables в качестве dev-dependencies.


$ npm i --save-dev datatables.net jquery 

После завершения установки модулей мы удаляем ненужные файлы, автоматически генерируемые CRA из репозитория. Окончательный результат проекта, который мы создаем здесь в этой статье, можно найти в репозитории react-datatables на GitHub.


Простой UI


Мы реализуем очень простой интерфейс. У пользователей есть область ввода, где они могут вводить имя и псевдоним в соответствующие текстовые поля. Когда нажимается кнопка «Добавить», данные, введенные в текстовые поля, добавляются в таблицу данных. Имя является уникальным «ключом» для каждой записи — если пользователь набирает имя, которое уже существует, новая пара имен-ников не создается, и только псевдоним существующей пары обновляется в таблице.




Настройка DataTables


У нас уже есть исходные файлы с пустым контентом, созданным CRA. Мы создаем новый файл, содержащий наш компонент Table — этот компонент отвечает за визуализацию и управление таблицей. Сначала мы импортируем как jQuery, так и DataTables и связываем их так, чтобы DataTables имел доступ к функциям jQuerey. Это можно сделать с помощью следующих двух строк:


const $ = require('jquery');
$.DataTable = require('datatables.net');

Мы определяем два столбца, которые извлекают соответствующие значения из пары имя-ник, которую мы собираемся предоставить таблице:


const columns = [
  {
    title: 'Name',
    width: 120,
    data: 'name'
  },
  {
    title: 'Nickname',
    width: 180,
    data: 'nickname'
  },
];

Наконец, само определение компонента:


class Table extends Component {
  componentDidMount() {
    $(this.refs.main).DataTable({
      dom: '<"data-table-wrapper"t>',
      data: this.props.names,
      columns,
      ordering: false,
    })
  }

  componentWillUnmount() {
    $('.data-table-wrapper')
      .find('table')
      .DataTable()
      .destroy(true)
  }

  shouldComponentUpdate(nextProps) {
    if (nextProps.names.length !== this.props.names.length) {
      reloadTableData(nextProps.names)
    } else {
      updateTable(nextProps.names)
    }
    return false
  }

  render() {
    return (
      
) } }

Несколько замечаний. В функции render мы создаем один HTML-элемент таблицы. Это требование DataTables, поскольку для заполнения DOM нужен элемент таблицы. React никогда не узнает, что внутри элемента таблицы будет больше DOM. Мы гарантируем, что никаких повторных попыток со стороны React не произойдет, всегда возвращая false из метода shouldComponentUpdate. Сама инициализация таблицы должна произойти только один раз, когда наш компонент монтируется, потому что мы хотим оставить все внутренние манипуляции DOM в DataTables. Наконец, нам нужно уничтожить таблицу, когда компонент должен быть размонтирован. Это делается в методе componentWillUnmount с соответствующим вызовом API DataTables.


В настоящее время наш компонент Table берет свои данные через props.names, но мы не реализовали способ добавления или обновления имен. Мы сделаем это в следующем разделе.


Обновление таблицы


Существует два способа изменения массива пар имя-ник. Во-первых, добавив на него новую пару имя-ник. Во-вторых, заменив существующую пару имя-ник новой парой с таким же именем и другим ником. Это делается в другом компоненте, внешнем по отношению к компоненту Table. (Я не буду вдаваться в подробности о том, как распространяются эти обновления — более подробную информацию см. в репозитории проекта на GitHub.) Здесь мы рассмотрим два типа обновлений с помощью двух разных методов.


1) Когда добавляется новая пара имя-ник, мы перезагружаем всю таблицу:


function reloadTableData(names) {
  const table = $('.data-table-wrapper').find('table').DataTable()
  table.clear()
  table.rows.add(names)
  table.draw()
}

Мы используем стандартный селектор jQuery, чтобы найти экземпляр таблицы, используя класс, который мы предоставили в componentDidMount (data-table-wrapper). Затем мы удаляем все предыдущие данные и загружаем новые данные (для краткости я не добавлял новую пару имя-ник — это также работает, если вы удаляете одну пару или несколько пар).


2) Когда пара обновляется, мы хотим найти эту пару и изменить конкретную часть данных, которые были изменены (поле ника в нашем примере):


function updateTable(names) {
  const table = $('.data-table-wrapper').find('table').DataTable()
  let dataChanged = false
  table.rows().every(function() {
    const oldNameData = this.data()
    const newNameData = names.find(nameData => {
      return nameData.name === oldNameData.name
    })
    if (oldNameData.nickname !== newNameData.nickname) {
      dataChanged = true
      this.data(newNameData)
    }
    return true
  })

  if (dataChanged) {
    table.draw()
  }
}

Мы должны не забыть перерисовывать таблицу с помощью API-метода draw, если какие-либо данные были изменены или изменения не будут видны пользователю.


Остается только различать два случая и вызывать соответствующий метод, когда компонент таблицы отображается в React. Мы делаем это в коде shouldComponentUpdate, чтобы проверить тип изменения и выполнить соответствующий метод.


  shouldComponentUpdate(nextProps) {
    if (nextProps.names.length !== this.props.names.length) {
      reloadTableData(nextProps.names)
    } else {
      updateTable(nextProps.names)
    }
    return false
  }

Теперь, каждый раз, когда количество пар в файлах props.names изменяется, мы повторно отображаем всю таблицу или обновляем соответствующие элементы. Обратите внимание, что в реальном приложении содержимое всего массива может измениться, а количество элементов может оставаться неизменным — случай, который не может произойти в нашем примере.


Вывод


Это определенно возможно и даже довольно просто интегрировать React и DataTables, если вы ограждаете React от манипуляций DataTables в DOM. Вариант использования, который мы реализовали в этой статье, довольно прост, но он обеспечивает все строительные блоки, необходимые для интеграции работы в реальном проекте.


PS


Пожалуйста, поделитесь опытом — какие используете React-компоненты для реализации функционала таблиц с сортировкой и пагинацией для манипуляции большими объемами данных?

Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/330656/


Метки:  

[Из песочницы] Диалектика нейронного машинного перевода

Суббота, 10 Июня 2017 г. 17:08 + в цитатник
или Перерастает ли количество в качество

Статья по мотивам выступления на конференции РИФ+КИБ 2017.

Neural Machine Translation: почему только сейчас?


Про нейронные сети говорят уже давно, и, казалось бы, что одна из классических задач искусственного интеллекта – машинный перевод – просто напрашивается на то, чтобы решаться на базе этой технологии.

Тем не менее, вот динамика популярности в поиске запросов про нейронные сети вообще и про нейронный машинный перевод в частности:

image

Прекрасно видно, что на радарах вплоть до недавнего времени нет ничего про нейронный машинный перевод – и вот в конце 2016 года свои новые технологии и системы машинного перевода, построенные на базе нейронных сетей, продемонстрировали сразу несколько компаний, среди которых Google, Microsoft и SYSTRAN. Они появились почти одновременно, с разницей в несколько недель или даже дней. Почему так?

Для того, чтобы ответить на этот вопрос, необходимо понять, что такое машинный перевод на базе нейронных сетей и в чем его ключевое отличие от классических статистических систем или аналитических систем, которые используются сегодня для машинного перевода.

В основе нейронного переводчика механизм двунаправленных рекуррентных нейронных сетей (Bidirectional Recurrent Neural Networks), построенный на матричных вычислениях, который позволяет строить существенно более сложные вероятностные модели, чем статистические машинные переводчики.

image

Как и статистический перевод, нейронный перевод требует для обучения параллельные корпуса, позволяющие сравнить автоматический перевод с эталонным «человеческим», только в процессе обучения оперирует не отдельными фразами и словосочетаниями, а целыми предложениями. Основная проблема в том, что для тренировки такой системы требуется существенно больше вычислительных мощностей.

Для ускорения процесса разработчики используют GPU от NVIDIA, а Google также и Tensor Processing Unit (TPU) – чипы собственной разработки, адаптированные специально для технологий машинного обучения. Графические чипы изначально оптимизированы под алгоритмы матричных вычислений, и поэтому выигрыш в производительности составляет 7-15 раз в сравнении с CPU.

Даже при всем этом тренировка одной нейронной модели требует от 1 до 3 недель, тогда как статистическая модель примерно того же размера настраивается за 1-3 дня, и с увеличением размера эта разница увеличивается.

Однако не только технологические проблемы были тормозом для развития нейронных сетей в контексте задачи машинного перевода. В конце концов, обучать языковые модели можно было и раньше, пусть и медленнее, но принципиальных препятствий не было.

Свою роль сыграла в том числе и мода на нейронные сети. Разработки внутри себя вели многие, но заявлять об этом не спешили, опасаясь, возможно, что не получат того прироста качества, которое общество ожидает от словосочетания Neural Networks. Этим можно объяснить тот факт, что сразу несколько нейронных переводчиков были анонсированы один за другим.

Качество перевода: чей BLEU score толще?


Попробуем понять, соответствует ли рост качества перевода накопленным ожиданиям и тому росту затрат, которые сопровождают разработку и поддержку нейронных сетей для перевода.
Google в своем исследования демонстрирует, что нейронный машинный перевод дает Relative Improvement от 58% до 87%, в зависимости от языковой пары, по сравнению с классическим статистическим подходом (или Phrase Based Machine Translation, PBMT, как его еще называют).

image

SYSTRAN проводит исследование, в котором качество перевода оценивается путем выбора из нескольких представленных вариантов, сделанных различными системами, а также «человеческого» перевода. И заявляет, что его нейронный перевод предпочитают в 46% случаев переводу, сделанному человеком.

image

Качество перевода: есть ли прорыв?


Несмотря на то, что Google заявляет об улучшении на 60% и даже выше, в этом показателе есть небольшой подвох. Представители компании говорят о «Relative Improvement», то есть насколько им удалось с нейронным подходом приблизится к качеству Human Translation по отношению к тому, что было в классическом статистическом переводчике.

image

Эксперты отрасли, анализирующие результаты, представленные Google в статье «Google's Neural Machine Translation System: Bridging the Gap between Human and Machine Translation», достаточно скептически относятся к представленным результатам и говорят, что фактически BLEU score удалось улучшить только на 10%, а существенный прогресс заметен именно на достаточно простых тестах из Wikipedia, которые, скорее всего, были использованы и в процессе обучения сети.

Внутри PROMT мы регулярно проводим сравнение перевода на различных текстах наших систем с конкурентами, и поэтому под рукой всегда есть примеры, на которых мы можем проверить, действительно ли нейронный перевод так превосходит предыдущее поколение, как заявляют производители.

Исходный текст (EN): Worrying never did anyone any good.
Перевод Google PBMT: Не беспокоясь не делал никому ничего хорошего.
Перевод Google NMT: Беспокойство никогда никому не помогало.

Кстати, перевод той же фразы на Translate.Ru: «Волнение никогда не приносило никому пользы», можно заметить, что он был и остался таким же и без использования нейронных сетей.

Microsoft Translator в этом вопросе тоже не отстает. В отличие от коллег из Google они даже сделали сайт, на котором можно сделать перевод и сравнить два результата: нейронный и донейронный, чтобы убедиться, что утверждения о росте в качестве не голословны.

image

На этом примере мы видим, что прогресс есть, и он действительно заметный. На первый взгляд, похоже, что заявление разработчиков о том, что машинный перевод практически догнал «человеческий» — правда. Но так ли это на самом деле, и что это значит с точки зрения практического применения технологии для бизнеса?

В общем случае перевод с применением нейронных сетей превосходит перевод статистический, и у этой технологии есть огромный потенциал для развития. Но если внимательно подойти к вопросу, то мы сможем убедиться, что прогресс не во всем, и не для всех задач можно применять нейронные сети без оглядки на саму задачу.

Машинный перевод: в чем задачи


От автоматического переводчика всю историю его существования – а это уже более 60 лет! – ждали некой магии, представляя его как машинку из фантастических фильмов, которая мгновенно переводит любую речь в инопланетный свист и обратно.

На самом деле, задачи бывают разного уровня, один из которых подразумевает «универсальный» или, если можно так выразится, «бытовой» перевод для повседневных задач и облегчения понимания. С задачами этого уровня прекрасно справляются онлайн-сервисы по переводу и множество мобильных продуктов.

К таким задачам можно отнести:

• быстрый перевод слов и коротких текстов для различных целей;
• автоматический перевод в процессе общения на форумах, в социальных сетях, мессенджерах;
• автоматический перевод при чтении новостей, статей Wikipedia;
• переводчик в путешествиях (mobile).

Все те примеры роста качества перевода с использованием нейронных сетей, которые мы рассматривали выше, как раз и относятся к этим задачам.

Однако с целями и задачами бизнеса в отношении машинного перевода все обстоит несколько иначе. Вот, например, некоторые требования, которые предъявляются к корпоративным системам машинного перевода:

• перевод деловой переписки с клиентами, партнерами, инвесторами, иностранными сотрудниками;
• локализация сайтов, интернет-магазинов, описаний продуктов, инструкций;
• перевод пользовательского контента (отзывы, форумы, блоги);
• возможность интеграции перевода в бизнес-процессы и программные продукты и сервисы;
• точность перевода с соблюдением терминологии, конфиденциальность и безопасность.

Попробуем понять на примерах, решаемы ли любые задачи бизнеса по переводу с помощью нейронных сетей и как именно.

Кейс: Amadeus


Amadeus — одна из крупнейших в мире глобальных систем дистрибуции авиабилетов. С одной стороны к ней подключены авиаперевозчики, с другой – агентства, которые должны получать всю информацию об изменениях в режиме реального времени и доносить до своих клиентов.

Задача — локализация условий применения тарифов (Fare Rules), формирующихся в системе бронирования автоматически из разных источников. Эти правила формируются всегда на английском языке. Ручной перевод здесь практически невозможен, ввиду того, что информации много и она часто меняется. Агент по продаже авиабилета хотел бы читать Fare Rules на русском языке, чтобы оперативно и квалифицированно консультировать своих клиентов.

Требуется понятный перевод, передающий смысл тарифных правил, с учетом типичных терминов и аббревиатур. И требуется, чтобы автоматический перевод был интегрирован непосредственно в систему бронирования Amadeus.

-> Подробно задача и реализация проекта расписаны в документе.

Попробуем сравнить перевод, сделанный через PROMT Cloud API, интегрированный в Amadeus Fare Rules Translator, и «нейронный» перевод от Google.

Оригинал: ROUND TRIP INSTANT PURCHASE FARES

PROMT (Аналитический подход): ТАРИФЫ МГНОВЕННОЙ ПОКУПКИ РЕЙСА ТУДА И ОБРАТНО

GNMT: КРУГЛЫЕ ПОКУПКИ

Очевидно, что тут нейронный переводчик не справляется, и чуть дальше станет понятно, почему.

Кейс: TripAdvisor


TripAdvisor один из крупнейших в мире туристических сервисов, который не нуждается в представлении. По данным статьи, опубликованной The Telegraph, ежедневно на сайте появляется 165,600 новых отзывов о различных туристических объектах на разных языках.

Задача перевод отзывов туристов с английского на русский язык с качеством перевода, достаточным для того, чтобы понять смысл этого отзыва. Основная сложность: типичные особенности user generated content (тексты с ошибками, опечатками, пропусками слов).

Также частью задачи была автоматическая оценка качества перевода перед публикацией на сайте TripAdvisor. Так как ручная оценка всего переводимого контента невозможна, решение по машинному переводу должно предоставить автоматический механизм оценки качества переведенных текстов — confidence score, чтобы дать возможность TripAdvisor публиковать переведенные отзывы только высокого качества.

-> Подробнее почитать о проекте можно на сайте компании.

Для решения была использована технология PROMT DeepHybrid, позволяющая получить более качественный и понятный конечному читателю перевод в том числе и за счет статистического постредактирования результатов перевода.

Посмотрим на примеры:

Оригинал: We ate there last night on a whim and it was a lovely meal. The service was attentive without being over bearing.

PROMT (Гибридный перевод): Мы ели там в последний вечер случайно, и это была прекрасная еда. Персонал был внимательным, но не властным.

GNMT: Мы ели там прошлой ночью по прихоти, и это была прекрасная еда. Обслуживание было внимательным, не будучи более подшипников.

Здесь все не так удручающе с точки зрения качества, как в предыдущем примере. И вообще, по своим параметрам эта задача потенциально может быть решена с применением нейронных сетей, и это может еще повысить качество перевода.

Проблемы использования NMP для бизнеса


Как уже говорилось ранее, «универсальный» переводчик не всегда дает приемлемое качество и не может поддерживать специфическую терминологию. Чтобы интегрировать в свои процессы и применять нейронные сети для перевода, нужно выполнить основные требования:

• Наличие достаточных объемов параллельных текстов для того, чтобы иметь возможность обучать нейронную сеть. Часто у заказчика их просто мало или вообще текстов по данной тематике не существует в природе. Они могут быть засекречены или находится в состоянии не очень пригодном для автоматической обработки.

Для создания модели нужна база, где содержится минимум 100 млн. токенов (словоупотреблений), а чтобы получить перевод более-менее приемлемого качества – 500 млн. токенов. Далеко не каждая компания обладает таким объемом материалов.

• Наличие механизма или алгоритмов автоматической оценки качества получаемого результата.

• Достаточные вычислительные мощности.
«Универсальный» нейронный переводчик чаще всего не подходит по качеству, а чтобы развернуть свою частную нейронную сеть, способную обеспечить приемлемое качество и скорость работы, требуется «маленькое облако».

• Непонятно, что делать с конфиденциальностью.
Не каждый заказчик готов отдавать свой контент для перевода в облако по соображениям безопасности, а NMP – это история в первую очередь облачная.

Выводы


• В общем случае нейронный автоматический перевод дает результат более высокого качества, чем «чисто» статистический подход;
• Автоматический перевод через нейронную сеть – лучше подходит для решения задачи «универсального перевода»;
• Ни один из подходов к МП сам по себе не является идеальным универсальным инструментом для решения любой задачи перевода;
• Для решения задач по переводу в бизнесе только специализированные решения могут гарантировать соответствие всем требованиям.

Мы приходим к абсолютно очевидному и логичному решению, что для своих задач по переводу нужно использовать тот переводчик, который максимально для этого подходит. Не важно, есть внутри нейронная сеть или нет. Понимание самой задачи – важнее.

Ссылки по теме
https://research.google.com/pubs/pub45610.html
https://arxiv.org/abs/1609.08144
https://research.googleblog.com/2016/09/a-neural-network-for-machine.html
https://slator.com/technology/nearly-indistinguishable-from-human-translation-google-claims-breakthrough/
https://slator.com/technology/hyperbolic-experts-weigh-in-on-google-neural-translate/
https://translator.microsoft.com/neural/
http://blog.systransoft.com/how-does-neural-machine-translation-work/
http://kv-emptypages.blogspot.ru/2016/09/a-deep-dive-into-systrans-neural.html
https://devblogs.nvidia.com/parallelforall/introduction-neural-machine-translation-with-gpus/
https://kv-emptypages.blogspot.ru/2010/03/need-for-automated-quality-measurement.html
http://kv-emptypages.blogspot.ru/2017/04/the-problem-with-bleu-and-neural.html
https://slator.com/technology/alibaba-launches-language-services-unit/
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/330654/


Метки:  

Как найти работу и переехать в Дубай

Суббота, 10 Июня 2017 г. 15:13 + в цитатник
Во время нашего приезда в Дубай, мы встретились c тренером и видеоблогером Иваном Будько, где он поведал нам о своей историю переезда в Дубай и поделился советами о том как найти найти работу в ОАЭ.
Если Вам интересна возможностость переезда в Дубай то, это видео для Вас:



В этом интервью мы затронули следующие вопросы:
— зачем переезжать в Дубай
— как найти работу в Дубае
— как переехать жить в Дубай
— как создать бизнес в Дубае
— нужно ли знать арабский и/или английский для работы в Дубае
— в чем секрет привлекательного резюме
— с какими трудностями можно столкнуться при иммиграции в Дубай
— как живут русские в Дубае
— потенциал и будущее Дубая
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/330652/


Опыт перехода с Waterfall на методологию RUP для реализации больших ИТ проектов

Суббота, 10 Июня 2017 г. 14:57 + в цитатник

Как возникла необходимость отойти от классической Каскадной Модели жизненного цикла разработки


В 2009 году мне предложили выбрать и реализовать один из «гиблых» проектов. Приставку «Гиблый» каждый получил за то, что раньше за них уже пробовали браться, но ничего не вышло.

В больших проектах причина неудачи чаще лежит не в профессиональном уровне команды, а в готовности Заказчика дойти до конца и получить отдачу от вложенных собственных усилий. Ситуация должна сложится так, что цели проекта должны совпасть с кратко и среднесрочными целями Заказчика.

Большой пример: только оказавшись под санкциями и цене нефти в 50 долларов за баррель (против 110 ранее) руководство страны перешло от рассуждений и неспешных телодвижений к активным действиям по развитию высокотехнологичной экономики.

Так и один из моих Заказчиков созрел и я взялся сделать для него проект по разработке нового функционального модуля Корпоративной Информационной Системы (ERP-системы), который должен был добавить 400 новых пользователей системе и обеспечить проверку 40 000 ипотечных кредитов в год.

Спустя 2 месяца у нас уже была первая версия Технического Задания, которая состояла из 200 страниц основного ТЗ и 200 страниц приложений к нему с описаниями различных форм и бизнес алгоритмов. Надо сказать, что до этого я видел основное ТЗ на всю корпоративную ИС и на момент внедрения оно состояло всего из 600 страниц. Поэтому ещё в ходе разработки ТЗ на модуль всё чаще возникала мысль, что с таким объемом требований велика вероятность, что мы не взлетим, а если взлетим, то очень не быстро.

После того как ребята из нашего выделенного центра разработки в EPAM озвучили трудоемкость реализации в 6500 часов и длительностью 1,5 года разработки стало ясно что точно не взлетим.

Бизнес-процесс и процедуры, которые нужно было автоматизировать, были живыми и постоянно адаптировались под изменения внутри компании и вовне, как под рыночные, так и под требования Центрального Банка России.

Через полтора года те «штаны», которые мы сошьем для них будут им либо малы либо не подойдут вообще, а это выброшенные на ветер деньги, время и ресурсы разработки. Достаточно один раз попробовать через это пройти, чтобы понять что это точно не вариант и карьерное самоубийство в рамках компании. После этого ничего серьезного уже не доверят.

Отсюда последовал вывод, что Waterfall годится для небольших проектов и тех, где автоматизируемые процессы не подвержены постоянным изменениям. Нужно было что-то другое, предполагающее раннюю доставку и при этом, чтобы это было понятно всем кто вовлечен в процесс разработки. В идеале Waterfall, но не Waterfall. Им оказался Rational Unified Process.

Два слова о контексте



Компания разрабатывает и развивает собственную корпоративную ИС взяв за основу платформу Pivotal CRM. Причина самостоятельной разработки КИС и дополнительных функциональных модулей к ней это отсутствие на отечественном рынке каких бы то ни было альтернатив и дорогая стоимость приобретения западных аналогов в 2000-е годы.

Помимо сотрудников компании в КИС работают сотрудники более 200 компаний Партнеров по всей стране (B2B). Как упоминалось выше, разработка находится на аутсорсинге в компании EPAM Systems. На стороне компании оставлены бизнес-аналитика и управление проектами.

Что не так с методологией Waterfall (особенности и недостатки каскадной модели)



Эта методология Мать для всех последующих и является универсальной. По этой методологии были разработаны первые программы.

Основы, которые держат эту Методологию и заложены в ней:
  1. От начала и до конца спроектированный конечный результат.
    (Архитектурный Проект Результата)

  2. Инструкция по сборке конечного результата.
    (План реализации проекта)

  3. Последовательная реализация проекта.
    Анализ, проектирование, сборка, тестирование и передача в эксплуатацию

  4. Сопротивление изменениям в Проекте Результата и Инструкции по сборке в процессе реализации проекта.
    Изменения в проекте

  5. Результат проекта доставляется единой поставкой в конце проекта.
    Динамика сборки результата проекта


Для того, чтобы реализовать длительный проект по Waterfall со сроками более 1-2 лет нужно сделать предположения о той бизнес-среде, потребностях клиентов и компании, которые будут на момент завершения проекта (допущения связанные с бизнес-целями Проекта). Также нужно предположить и заложить ориентировочную стоимость ресурсов на каждый год жизни проекта (допущения связанные с планом реализации Проекта).

Чем больше содержание проекта, трудоемкость и сроки реализации, тем больше допущений закладывается в проект, которые могут оказаться как верными так и не верными. Самые критичные — это допущения связанные с бизнес-целями Проекта, если они оказываются не верными то все вложенные в проект ресурсы, деньги и время обесцениваются.

Попытки внести существенные изменения в Проект Конечного Результата по ходу реализации требуют цикла перепроектирования Конечного Результата и Инструкции по сборке. Существенные изменения часто не позволяют продолжать уже начатые по первоначальному плану работы и приводят к переделке того, что уже было сделано.

По этой причине команда проекта и стейкхолдеры сопротивляются существенным изменениям в проекте, только если обстоятельства и сыгравшие/обнаружившиеся риски не ставят весь проект под сомнение в необходимости его продолжать. Когда изменения всё-таки вносятся они чаще всего приводят к дополнительным затратам и удлинению сроков. Возросшая длительность опять порождает новые требования и новые изменения. Это словно замкнутый круг из которого невозможно выйти победителем. Отсюда происходит постоянное вылетание из первоначальных сроков и бюджета.

Поэтому эта Методология подходит проектам строительства зданий и сооружений, типа атомной электростанции, но не годится для разработки Продуктов под быстро меняющиеся среды, в которых они будут применяться. У которых срок жизни Продукта в изначальном виде без внесения изменений меньше 5 лет.

Идея итеративной разработки призвана устранить связанные с допущениями риски, дать возможность быстро получить обратную связь, сократить масштаб потерь от неверных бизнес допущений и внести изменения в Проект Конечного Результата на основе обратной связи полученной по окончанию очередной итерации.

Преимущества Rational Unified Process



Методология RUP была целенаправленно разработана для разработки больших программных систем.

Новация №1, заложенная в её основу, это «Бизнес-моделирование» и связанные с ним Сценарии Использования (Use Case).

Сперва разрабатывается бизнес-сценарий использования, где будущая система представляет собою «чёрный ящик», который удовлетворяет все потребности Пользователя/Бизнеса. На его основе разрабатываются системные сценарии использования, описывающие функции системы, которые будут поддерживать выполнение бизнес-сценария.

Главная задача при разработке корпоративной ИС обеспечить непрерывность поддерживаемого бизнес-процесса. Когда ткань бизнес-процесса рвется, то встают все следующие за ним этапы в производственной цепочке, а порой и весь бизнес объемом в десятки миллиардов рублей.
пример остановки торгов Московской биржей
На фондовом, валютном и срочном рынках Московской биржи нештатная ситуация … Последний раз биржа приостанавливала торги 1 сентября, но это коснулось только секции «Основной рынок». Этот сбой стал четвертым за последние четыре месяца.Lenta.ru
Поэтому в основе разработки корпоративной ИС лежит проектирование нового бизнес-процесса на основе её использования, чтобы заменить один процесс на другой и сотни людей с определенного дня начали работать по другому чем привыкли до этого. В этом главное и основное отличие разработки корпоративной ИС от других видов программного обеспечения.

Новация №2: На основе выделенных системных сценариев использования системы принимаются архитектурные решения и выделяются компоненты, которые будут поддерживать бизнес-сценарии и решается будут ли они использоваться совместно для поддержки нескольких бизнес-сценариев или останутся заточенными под конкретный сценарий. (Объектно-ориентированное проектирование и программирование)

Наличие отдельных компонент позволяет их переиспользовать и заменять одни на другие. Чем больше компоненты используются совместно тем выше вероятность сломать соседний бизнес-сценарий при внесении изменений в компоненту. Также наличие общих компонент ограничивает скорость и масштаб последующих работ по развитию системы. Если одна команда занимается тем, что меняет какую-то общую компоненту, то другая не может начать работы по её изменению до тех пор пока первая не закончит работы и не отладит свой бизнес-сценарий использования.

Новация №3: Наличие выделенных сценариев использования системы позволяет разделить их на первичные и вторичные. Вторичные основываются на результатах выполнения первичных сценариев, которые по большому счёту самодостаточны. Отсюда появляется возможность выделить итерации и разложить реализацию всех сценариев по ним.

В Waterfall от того нет итераций, так как порой невероятно сложно разделить функциональные требования на самодостаточные блоки и реализовывать их итерациями. Я не говорю, что этого нельзя сделать, можно, но чаще всего результатом этого будет не работающий прототип ИТ-решения, а какая-то часть будущей программы. От этого бизнесу не становится легче, работающий прототип он так и получит через год, наличие итераций здесь помогает, но не сильно.

Вот так выглядит концепт RUP в упрощенном виде:

Каждый бизнес-сценарий разворачивается в системные сценарии использования. Каждый системный сценарий — в требования к интерфейсам, алгоритмам, данным и не функциональные (производительность, время доступности, скорость ответа...). На основе выделенных требований определяется будущая архитектура системы и её функциональные модули (сервисные подсистемы). Подсистемы разбиваются на компоненты. Компоненты содержат исполняемый код.

Основная идея RUP выдать уже в первых итерациях прототип будущей системы с порцией готовых к использованию / применению бизнес-сценариев. Так как доставка части Результата проекта (инкремента) осуществляется в конце каждой итерации, это позволяет начать получать выгоду от использования промежуточных версий Результата и возвращать вложения в проект ещё в ходе его реализации.

Как мы применили RUP



Шаг №1 — Я договорился с руководителем об использовании отличного от принятого в компании шаблона технического задания. В основу нового ТЗ легли бизнес и системные сценарии использования. Это решение было принято раньше чем решение о работе по RUP и по ряду причин о которых можно прочесть в моей статье «Каким должно быть ТЗ на корпоративную ИС?» (ссылка в конце этой статьи в разделе «Что ещё почитать»). 

Шаг №2 — Спроектировали целевой бизнес-процесс и подготовили первую версию ТЗ. После того как получили оценки трудоемкости и длительности реализации приняли решение, что попробуем использовать RUP для этого проекта.  Разбили целевой бизнес-процесс на 5 бизнес-процедур / 5 этапов реализации проекта. Первая версия ТЗ стала концепцией / дорожной картой по продвижению к целевому процессу. 

Шаг №3 — Определились со стратегий автоматизации: двигаться от входной или выходной точки всего бизнес-процесса. 

Первая стратегия «от входной точки» создаёт напряжение и заставляет автоматизировать все остальные этапы в цепочке. В последующие этапы бизнес-процесса перестают поступать первичные данные, которые необходимы им для работы.

Вторая стратегия «от выходной точки» не имеет этого напряжения, но и не имеет тех данных и истории, которые создаются и накапливаются на предыдущих этапах. Нужен регулярный перенос необходимой информации в КИС.

Пошли по первой стратегии. Проблему обеспечения последующих этапов необходимой информацией решили выгрузкой данных в Excel. Получили неожиданный эффект в виде растущей мотивации бизнес-пользователей полностью перейти из Excel в КИС вместе с соответствующей поддержкой и готовностью выделять время на работу с бизнес-аналитиком по проработке требований. 

Шаг №4 — Подготовили ТЗ на первую часть модуля системы (вторую итерацию). Пока велась разработка первой части, готовили ТЗ на вторую часть (третью итерацию). 

Первой итерацией была подготовка Концептуального Технического Задания где прописали полностью бизнес-процесс с использованием КИС, а также разработали первую версию архитектуры будущего модуля. 

Шаг №5 — После выхода первой части модуля и старта его опытной эксплуатации, начали готовить дополнение к ТЗ на первую часть и ТЗ на третью часть (четвертую итерацию). В дополнение вошли те требования, что были упущены в начале и те что оказались неудачными в плане использования. 

Результат — На всё про всё у нас действительно ушло 1,5 года.  Работающий прототип (первая часть модуля системы) был получен спустя 6 месяцев с даты старта проекта. Остальные приходили уже через каждые 2 месяца.



С точки зрения архитектуры компоненты нового функционального модуля были заточены исключительно под него и не использовали компоненты основной ИС. Это позволило:
  1. Стабилизировать работу системы целиком при возникновении ошибок, порождаемых регулярно вносимыми изменениями в модуль, так и модуль от ошибок изменений вносимых в общий функционал ИС. 
  2. Вести работы по развитию существующего функционала КИС параллельно разработке нового модуля. 

Хоть RUP напрямую ассоциируется и связан с нотациями UML, для описания бизнес и системных сценариев использования не обязательно использовать именно их, подойдут и другие нотации. Главное, чтобы используемые нотации были понятны и Бизнес-Пользователям, которые будут согласовывать-утверждать постановку, и Системным Аналитикам и Разработчикам которые будут дальше работать с Техническим Заданием переводя его в проектную-техническую документацию. Мы для описания бизнес-сценария использовали eEPC и ARIS, а также требования нотаций IDEF0/3 для задания рамок  системным сценариям: вход процесса, выход процесса, исполнитель.

Статья «Каким должно быть ТЗ на корпоративную ИС?» даст представление о том, как выглядит бизнес-сценарий и системный сценарий использования. В оригинале руководства по RUP не рекомендуется делать наброски пользовательских интерфейсов при составлении системных сценариев, но мы посчитали их необходимыми так как облегчают понимание и бизнес и системного сценариев засчёт визуализации их элементов. Это было практически первым прототипом системы хотя и не кликабельным. 

После этого проекта написанное ТЗ стало новым шаблоном (стандартом). Также проект повлиял на весь технологический-производственный процесс разработки ПО в компании сделав его ритмичным.

Немного теории для понимания технической стороны методологии RUP и как её применять



Каждая итерация в RUP это классический Waterfall, содержащий все 5 этапов работ по сбору требований и их анализу, проектированию, разработке, тестированию и доставке. Но RUP также вводит такое понятие как фазы для проекта: Сбор требований и Анализ, Проектирование, Построение, Внедрение.



Теперь об этом подробнее.

Первая итерация, Фаза Анализ
Посвящена сбору требований будущих пользователей к системе. Пользователи рассказывают каждый свой кусочек работы (сценарий использования системы пользователем), а Аналитик из отдельных операций и процедур пытается собрать целый рабочий процесс. Цель этой итерации выйти на бизнес-сценарий использования системы / будущий бизнес-процесс на основе ИС. Их может быть больше чем один, поэтому не стоит замыкаться на том чтобы был обязательно один.

Компания-Заказчик — это Банк. Он решает, что хочет начать предоставлять своим клиентам услугу по удаленной выдаче наличных при помощи банкоматов, установленных в офисах клиентов и на станциях метро.

Для оказания данной услуги, нужно разработать и доработать имеющееся у банка программное обеспечение. В ходе сбора требований и их анализа было выделено три крупных процедуры в бизнес-сценарии услуги:
  1. Загрузка наличных в банкомат Сотрудниками банка.
  2. Выдача наличных Клиенту по его запросу со счёта в банке.
  3. Учет количества загруженных и выданных купюр.

В этой итерации ещё нет работ связанных с проектированием архитектуры будущей системы, разработки программы, тестирования и доставки. RUP не ставит условий, что они обязательно нужны, но по своему усмотрению вы можете их выполнить.

Вторая итерация, Фаза Анализ
После того как бизнес сценарии определены, собранные требования раскладываются по шагам бизнес-сценария и системным сценариям. На этом этапе также выявляются те шаги бизнес-сценариев, что были не выявлены в первой итерации.

В ходе второй итерации были выявлены две дополнительные процедуры к основному бизнес-сценарию:
  1. Мониторинг и нотификация, направляемая банкоматом в банк, об окончании в нём наличных.
  2. Мониторинг и нотификация о работоспособности банкомата или наличия на нем проблем/ошибок в работе.
Для обеспечения доступности услуги выдачи наличных в режиме 24/7.

Результатом этой итерации становятся:
  1. На 90% законченные бизнес-сценарии.
  2. Намеченные, но не детализированные системные сценарии использования по каждому бизнес-сценарию.
  3. Собранные требования структурированы по бизнес и системным сценариям.

Третья итерация, Фаза Проектирование
Выявленным бизнес сценариям выставляются приоритеты реализации и самые важные из них становятся предметом проработки на данной итерации. В отобранных бизнес-сценариях выделяются первичные шаги-этапы, которые являются базой для всех последующих и без которых реализация остальных не имеет смысла. Для этих этапов детализируются сценарии использования системы.

Законченный результат передается Системным Аналитикам и Архитектору для моделирования того, что будет происходит внутри системы. Выделяются классы, кооперации между ними, последовательности взаимодействия. Формируется базовое представление об архитектуре будущей системы, выделяются подсистемы, компоненты системы и их распределение по узлам ИТ-решения.

Уже в этой итерации могут быть начаты работы по разработке и результатом итерации станет готовый прототип, поддерживающий выполнение первичных шагов-этапов ключевого бизнес-сценария. Его уже можно показывать пользователям для сбора обратной связи и уточнения требований.

Этот прототип не передается в опытную или промышленную эксплуатацию, так как заложенная в основу архитектура является первым пробным шаром и в последующих итерациях будет скорректирована.


Четвертая итерация, Фаза Проектирование
Стартует параллельно третьей итерации, когда в ней закончены работы по анализу и начаты работы по проектированию.


На этой итерации происходит проработка первичных шагов-этапов остальных бизнес-сценариев. Причина по которой прорабатываются они, а не последующие шаги для ключевых бизнес-сценариев, это определение будущей архитектуры всей программы.

Каждый бизнес-сценарий может потребовать для своей реализации каких-то специфических компонент будущей системы и по этой причине архитектурные решения для каждого бизнес-сценария должны быть согласованы между собой.

Результатом этой итерации станут:
  1. Архитектурные решения для каждого бизнес-сценария.
  2. Согласованный проект общей архитектуры системы 1.0
  3. Вторая версия прототипа с готовыми первыми шагами вторичных бизнес-сценариев в дополнение к первым шагам ключевого бизнес-сценария.

Пятая итерация, Фаза Построения
Стартует параллельно четвертой итерации, когда в ней закончены работы по анализу и начаты работы по проектированию. Предполагается, что к моменту старта итерации уже получена обратная связь по результатам тестирования пользователями прототипа, изготовленного во время третьей итерации. Здесь происходит проработка остальных шагов-этапов ключевого бизнес-сценария и связанных с ними системных сценариев использования.

Предполагается, что к моменту начала этапа работ «Проектирование», уже будет выполнена реализация (разработка) четвертой итерации и начато тестирование второй версии прототипа.
Происходит уточнение согласованного проекта общей архитектуры системы после которого внесение каких-то концептуальных изменений в него практически сводится к нулю, но всё ещё возможно, так как остаются не реализованными полностью вторичные бизнес-сценарии.

Результатом итерации становятся:
  1. Полностью реализованный ключевой бизнес сценарий.
  2. Согласованный проект общей архитектуры системы 2.0
  3. Третья версия системы, реализующая ключевой бизнес-сценарий и первые шаги для вторичных бизнес-сценариев.

Здесь уже возможна передача системы для опытной эксплуатации бизнес-пользователям так как система уже перестаёт быть прототипом и в большей степени похожа на ту, что будет передана бизнес-пользователям как её финальный вариант.

Шестая итерация, Фаза Построения
Стартует параллельно пятой итерации, когда в ней закончены работы по анализу и начаты работы по проектированию.

Прописываются остальные шаги-этапы вторичных бизнес-сценариев и связанных с ними системных сценариев использования. Предполагается что к началу данной итерации выполнено тестирование второй версии прототипа, изготовленной вовремя четвертой итерации и получена обратная связь от пользователей по работе с первичными шагами-этапами вторичных бизнес-сценариев.

Результатом итерации становятся:
  1. Полностью реализованные вторичные бизнес сценарии.
  2. Финальный проект общей архитектуры системы 3.0
  3. Четвертая версия системы, реализующая все бизнес-сценарии.

Начинается полноценная опытная эксплуатация системы бизнес-пользователями и примерка её под промышленное использование.

Седьмая итерация, Фаза Внедрения
Здесь происходит внесение каких-то критических изменений в реализацию бизнес-сценариев, выявленных в ходе опытной эксплуатации и не позволяющих полноценно использовать систему в промышленной эксплуатации.

Восьмая итерация, Фаза Внедрения
На этой итерации проводятся работы по повышению удобства использования системы пользователями и оптимизации изготовленных решений. Пользователей в первую очередь интересует скорость и пропускная способность их участков. Происходит передача системы на поддержку. Проводятся формальные процедуры по завершению и закрытию проекта.

Подитог
Методология RUP не ставит условий по количеству итераций и их распределению по фазам. Концепт фаз и итераций введен для снижения рисков, потребности в ресурсах, задания ритма реализации проекта и предоставления возможности внести требуемые изменения при разработке большой информационной системы.
График зависимости срока проекта от времени начала выявления проблем в ИТ-продукте


Результат каждой итерации уточняет план следующей и даёт больше понимания о том понадобятся ли нам дополнительные ресурсы для следующей итерации, нужны ли будут дополнительные итерации для завершения фазы. Начать работать над проектом может минимальная команда из нескольких человек и её расширение будет обосновываться планом следующей итерации.

Результаты завершения фазы уточняют план и экономику проекта. Здесь принимаются решения продолжать ли проект, нужно ли сузить или расширить содержание проекта (объем бизнес-требований), чтобы остаться в бизнес рамках: нужного срока доставки результата проекта, его стоимости (бюджета) и доходности (рентабельности вложений).

Заключение



Понимаю, что не просто поменять принятые в компании устои как реализовывать ИТ-проекты, особенно если в компании работает Проектный Офис с методологией написанной на основе PMBoK и ГОСТ-ов. Если прочитав статью понимаете что ближайшая перспектива это всё-таки Waterfall, то рекомендую попробовать реализовать следующий минимум:

  1. Прописать автоматизируемый бизнес-процесс на основе использования ИС. Это больше чем на 50% готовая пользовательская документация и приемочные тесты.

  2. Заложить в проект время на то, что как минимум 1 раз в течении года придётся выполнить полный комплекс работ по перепроектированию, подготовке и согласованию изменений в проектную документацию.

  3. Помножить трудоемкость работ по разработке на 1,5. Так как треть сделанной работы придется выкинуть и появятся новые требования. Длительность этапа разработки тоже стоит помножить на 1,5.

  4. Разделить бюджет проекта на две части: создание и развитие.
    На развитие заложить примерно 30% от всего бюджета проекта на создание. Это нужно затем, что когда закончите проект возникнет промежуточное состояние — проект реализован, но не принят на сопровождение.
    Принятие на сопровождение, как правило, сопровождается процедурами согласования и выделения бюджета и ресурсов на сопровождение. При этом после окончания проекта начинается самая горячая фаза, когда пользователи будут требовать реализации критических доработок, которые не вошли в содержание (рамки) проекта, но их точно нужно делать и из чего-то финансировать до тех пор пока система не будет принята на сопровождение и переведена на бюджет поддержки.
    Это позволит мягко договорится с Заказчиками об окончании фазы реализации проекта, выполненной согласно ТЗ.

На мой взгляд:
  • Waterfall – это первая передача (первая скорость с релизом раз в год),
  • RUP – вторая (релиз раз в квартал),
  • Scrum – третья (релиз раз в две недели / месяц),
  • Kanban вместе с автоматической сборкой обновления (Continuous Integration), автоматическим тестированием (Acceptance Test-Driven Development), автоматической доставкой и развертыванием (Continuous Deployment) – четвертая (релиз раз в день).


Что ещё почитать по теме для освоения RUP



  1. Статья «Каким должно быть ТЗ на корпоративную ИС?»

    В статье делается упор на то, что после разработки и создания КИС, начнётся этап её развития. На этом этапе потребуется разработка как новых Технических Заданий на новые функциональные модули, так и внесение изменений в ранее разработанные ТЗ на существующие модули системы в связи с внесением изменений в сценарии их использования.

    Обратите внимание на комментарии под статьёй. Они шире раскрывают материал и самой статьи про ТЗ и этой статьи про RUP.

  2. Книга «Унифицированный процесс разработки программного обеспечения»

    Это настольная книга в которой есть всё что нужно, чтобы понять и научиться делать проекты по RUP.

    Первая и основная сложность — это научиться проектировать не существующие бизнес-процессы / бизнес-сценарии. Чтобы подступится к ним я рекомендую научиться описывать существующие, после чего проектирование нового взамен старого уже не будет таким сложным.

    Авторы книги предлагают для моделирования бизнес-процессов использовать UML, но всё таки книга заточена на проектирование информационной системы и в меньшей степени на проектирование бизнес-процессов. Поэтому рекомендую освоить нотации IDEFx на основе Structured Analysis and Design Technique (книга на русском). Понятие процесса которое заложено в этой методологии является основой для стандартов ISO и PMI. И уже после этого расширять свои знания и умения по использованию других нотаций.

  3. Изучения языка моделирования напрямую связано с необходимостью освоения какого-либо инструмента. RUP предполагает что вы будете использовать линейку продуктов Rational от IBM. Я рекомендую воспользоваться бесплатной демо-версией Business Studio. В этом видео с 12 минуты объясняется как строить модели с помощью данного инструмента.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/329572/


[Перевод] Ruby on Rails соглашение. Часть 4

Суббота, 10 Июня 2017 г. 14:43 + в цитатник


Цените интегрированные системы


Ruby on Rails можно использовать для разных целей, но его конек — это монолитные интегрированные системы. Такие системы нацелены на решение всей задачи совокупно. Через Rails проходит все, начиная от генерации JavaScript для мгновенного обновления страниц, и заканчивая миграцией базы данных от одной версии к другой, когда проект уже в эксплуатации.

Как уже было сказано раньше, в такой постановке задача весьма широка, но она вполне по силам одному человеку. Rails специально спроектирован так, чтобы один разносторонний специалист или небольшая группа могли создать полноценную систему. В противном случае специалисты забиваются в свои узкоспециализированные ниши, и чтобы реализовать серьезный проект, из этих специалистов приходится собирать целую команду.

Именно упор на расширение возможностей отдельного разработчика подталкивает нас к интегрированным системам. Интегрированная система позволяет убрать ненужные абстракции, уменьшить дублирование между слоями (например, использовать одни и те же шаблоны на сервере и на клиенте), а самое главное — до последнего не допустить перехода к распределенной архитектуре.

Основная сложность распределенных систем связана с появлением новых границ, которые затрудняют передачу данных между элементами. Вызвать в одном объекте метод другого объекта намного проще, чем из одного микросервиса удаленно вызвать процедуру в другом микросервисе. В распределенных системах неожиданно появляется множество осложнений, например, планирование обновлений зависимостей, недоступность сервисов и задержки.

Увы, иногда без распределенной архитектуры не обойтись. Если вашему веб-приложению нужен API-интерфейс, к которому пользователи будут обращаться по HTTP, то ничего не поделаешь — придется решать почти все упомянутые проблемы. Остается порадоваться, что работа со входящими запросами куда проще, чем с исходящими, ведь если ваш интерфейс какое-то время не будет работать, то проблемы будут не у вас, а у потребителя этого интерфейса. По крайней мере в таком случае неудобства, которые вам как разработчику придется претерпеть, не столь велики.

Плохо, когда система преждевременно дробится на сервисы или, что еще хуже, на микросервисы. Бытует мнение, что при разработке «Современного Интернет-Приложения» неизбежно придется строить одну и ту же систему много раз: один раз на стороне сервера, один раз на стороне клиента в виде JavaScript MVC, по одному разу для каждой мобильной платформы, и так далее. Но это требование ничем не обусловлено, так делать совершенно необязательно!

Можно запросто использовать одни и те же элементы системы для разных задач и способов доступа. Завести единый набор контроллеров и представлений и для десктопных браузеров, и для нативных мобильных приложений. Сосредоточить как можно больше функций в монолитной интегрированной системе.

При этом практически не страдают ни скорость, ни удобство использования, ни другие характеристики, ради оптимизации которых разработчики бросаются проектировать распределенное приложение.

Все наши силы направлены на то, чтобы получить все преимущества отлично подогнанного распределенного приложения в простой, удобной и понятной единой интегрированной системе.

Прогресс превыше стабильности


Когда системы существуют более десяти лет, такие как Rails, их естественной тенденцией является закостенение. Имеется миллион причин, по которым каждое изменение может быть проблемой для кого либо где-нибудь, для того кто зависит от прошлого поведения системы. И эти причины в частных случаях, совершенно справедливы для отдельных людей.

Но, если мы будем слишком внимательно прислушиваться к голосам консерватизма, мы никогда не увидим другой стороны медали. Мы должны иногда брать на себя смелость и изменять условия, по каким все должно развиваться и расти. Именно это развитие сохранит Rails: и его выживание, и процветание в течение десятилетия (десятилетий?) в будущем.

Это все легко понять в теории, но сложно принять на практике. Особенно, когда ваше приложение ломается от нарушения обратной совместимости изменением в мажорной версии Rails. Именно в такие моменты нам нельзя забывать, что прогресс стоит превыше стабильности, чтобы получить заряд энергии для отладки сбойного кода, выяснения проблемы, и наконец движения дальше в жизненном цикле проекта.

Это не некая вседозволенность причинять кому-то ненужную или чрезмерную боль. Большой Переход от Rails с версии 2.x до версии 3 все еще будоражит раны тех, кто был причастен к этому переходу. Это было тяжело. Серьезный переполох, который многих оставил на версии 2.x на долгое время, причем некоторых из них уже было оттуда не вернуть. Но, по великой схеме вещей, это все равно стоило того.

Это тяжелые решения, которые мы должны продолжать делать. Сделают ли Rails лучше в течении будущих пяти лет изменения, которые мы делаем сегодня? Станет ли Rails лучше для адаптации стека новых задач, например очередей сообщений или WebSockets, в ближайшие годы? Если да, то давайте закатаем рукава и займемся работой.

Эта работа — не просто то, что должно произойти исключительно в Rails, но и в более крупном сообществе самого языка Ruby. Rails должен находиться на переднем крае разработки, помогая продвижению Ruby, заставляя его составляющие быстрее использовать более поздние версии.

До сих пор мы очень хорошо справлялись с этим. С того момента, как я начал, мы прошли через Ruby версий 1.6, 1.8, 1.9, 2.0, 2.1, 2.2 и теперь на 2.3. Множество серьезных изменений произошло на этом пути, но Rails непосредственно присутствовал и участвовал в них, чтобы иметь под капотом Ruby, и помочь каждому быстрее осуществлять процесс разработки. Эта отчасти привилегия, а отчасти и обязательство Rails, является основой популяризации Ruby.

Это также верно для вспомогательных инструментов рабочего процесса. Bundler когда-то был спорной идеей, но по настоянию Rails о том, что это будет краеугольным камнем совместного будущего, сегодня это само собой разумеющийся инструмент де-факто. Это так же касается таких инструментов и технологий как asset pipeline (файлопровод) и Spring, предварительный загрузчик приложений Rails. Все три инструмента прошли стадию укоренения, или же продолжали переживать боль роста, но очевидность их ценности в долгосрочной перспективе помогла нам преодалеть это.

Прогресс, в конечном счете, в основном связан с людьми и их готовностью продвигать изменения. Вот почему в таких группах, как Rails Core или Rails Committers, не сидят без дела. Обе группы предназначены для тех, кто активно работает над достижением прогресса для фреймворка. Для некоторых, их вклад в такой прогресс может измеряться всего несколькими годами, и мы будем навсегда благодарны за их труд, а для других это может продолжаться десятилетиями.

Соответственно, по этому очень важно, чтобы мы продолжали приветствовать и поощрять новых членов сообщества. Нам нужна свежая кровь и свежие идеи, чтобы добиться лучшего прогресса.

Возведите большую палатку


Содержа в себе такое большое количество спорных идей, Rails может быстро стать островком идеологических отшельников, если мы все время будем проявлять полное уважение ко всем принципам сразу.

Именно по этому мы так не делаем!

Нам нужны разногласия. Нам нужны различия. Нам нужно разнообразие мыслей и людей. Именно в этом горниле идей мы получим самое лучшее, чтобы делиться этим со всеми. Множество людей вносят свою лепту так или иначе, пишут ли они код или дискутируют.

Итак, хотя этот документ многое описывает в идеалистическом ключе, повседневная реальность гораздо тоньше (и интереснее). Rails способен поддерживать такое большое сообщество в одной палатке именно потому, что в его культуре очень мало неких «тестов на единственно верный подход», если они вообще есть.

По мере непрекращающегося развития RSpec, DSL для тестирования, я часто выражал серьезное недовольство, и это отлично иллюстрирует сказанное выше. Я могу разглагольствовать до посинения, так как не считаю Rspec серьезной перспективой, а он несмотря на это и процветал, и процветает как технология. И это гораздо важнее, нежели выбор какой-то одной, «единственно верной» технологии!

То же самое можно сказать о режиме Rails в качестве HTTP API. В то время, как я лично предан и сосредотачиваюсь на интегрированной системе, включающей в себя виды (представления), несомненно Rails может многое предложить и разработчикам, желающим разделить свое приложение на клиентскую и серверную часть (бекенд и фронтенд). Мы должны принять это, поскольку такие решения могут развиваться в Rails как второстепенные, и я уверен, что это возможно.

Однако «иметь большую палатку» — не значит пытаться угодить всем и каждому. Это просто означает, что вы приветствуете всех людей на вечеринке и позволяете им «приносить свои напитки». Мы не должны терять ни одного из наших приверженцев или наших ценностей, предлагая другим присоединиться к нам, и мы вполне можем научиться смешивать новые вклады («напитки») пришедших с теми, что уже существуют.

Это не просто. Это требует, чтобы сообщество и работа в нем были дружественными и приветливыми. Особенно, если ваша цель состоит не только в привлечении людей, похожих на уже существующих членов сообщества. Понижение порога входа в сообщество — это работа, которую мы всегда должны воспринимать всерьез.

Вы не можете знать, когда новый разработчик, только начавший исправлять орфографическую ошибку в документации, закончит реализацию следующей большой фичи в Rails. И сделает ли он это вообще. Но у вас есть шанс узнать, улыбнетесь ли вы, и скажете ли вы спасибо за некий небольшой вклад, который и позволяет мотивации пребывать с каждым из нас.

Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/330650/


Метки:  

Решение задач линейного программирования с использованием Python

Суббота, 10 Июня 2017 г. 14:08 + в цитатник

Зачем решать экстремальные задачи


На практике очень часто возникают задачи, для решения которых используются методы оптимизации. В обычной жизни при множественном выборе, например, подарков к новому годы мы интуитивно решаем задачу минимальных затрат при заданном качестве покупок.

К сожалению, не всегда можно положиться на интуицию. Допустим Вы сотрудник коммерческой фирмы и отвечаете за рекламу. Затраты на рекламу в месяц не должны превышать 10 000 денежных единиц (д.е). Минута радиорекламы стоит 5 д.е., а телерекламы 90 д.е. Фирма намерена использовать радиорекламу в два раза чаще чем телерекламу. Практика показывает, что 1 минута телерекламы обеспечивает объём продаж в 30 раз больший чем 1 минута радиорекламы.


Перед Вами стоит задача определить такое распределение средств между двумя упомянутыми видами рекламы при котором объём продаж фирмы будет максимальным. Вы сначала выберите переменные, а именно месячный объём в минутах на телерекламу — x1, а на радиорекламу --x2. Теперь не трудно составить следующую систему:

30x1+x2 –увеличение продаж от рекламы;
90x1+5x2 <=10 000 – ограничение средств;
x2=3x1 – соотношение времён радио и теле рекламы.

Теперь на время забудем о рекламе и постараемся обобщить изложенное. Таких задач, как приваленная, может быть много, но они имеют следующие общие признаки. Обязательным есть наличие линейно зависящей от переменных функции цели, в нашем случае это 30x1+x2, которая при найденных значениях входящих переменных должна иметь единственное максимальное значение. При этом условие не отрицательности входящих переменных выполняется автоматически. Далее следуют опять-таки линейные равенства и неравенства в количестве, зависящем от наличия условий. Вот мы и сформулировали одну группу задач линейного программирования.

Другую большую группу задач линейного программирования, рассмотрим на примере так называемой транспортной задачи. Допустим Вы сотрудник коммерческой фирмы, которая оказывает транспортные услуги. Есть поставщики товара со складами в разных трёх городах, причём объёмы однородной продукции на этих складах соответственно равны a1, a2, a3. Есть и потребители в других трёх городах которым нужно привести товар от поставщиков в объёмах b1, b2, b3 соответственно. Известны также стоимости доставки с1:с9 товаров от поставщиков к потребителям, согласно таблице.



Если обозначить через x1…xn количество перевозимого груза, тогда функцией цели будет общая стоимость перевозки:

F(x)=c1*x1+c2*x2+c3*x3+c4*x4+c5*x5+c6*x6+c7*x7+c8*x8+c9*x9.

Условия, которые записываться. в виде неравенств:

x1+x2+x3<=20 – больше чем есть у поставщика не возьмёшь
x4+x5+x6<=45
x7+x8+x9<=30

Условия, которые записываться. в виде равенств:

x1+x4+x7=b1– сколько надо столько и привезём
x2+x5+x8=b2
x3+x6+x9=b3

Тут дополнительно нужны условия не отрицательности переменных x поскольку они по смыслу не отрицательны и ищется минимум F(x). Эти неравенства не приводим.

Теперь Вы знаете как строить функции цели и условия для основных задач линейного программирования. Но когда Вы прочитаете в специальной литературе про геометрический, симплекс, искусственного базиса методы решения указанных задач Вы бросили и рекламу и логистику. Но ведь можно найти простое и понятное решение на Python.

Выбор библиотек Python для решения типовых задач линейного программирования


Для линейного программирования в Python мне известны три специализированные библиотеки. Рассмотрим решение обеих приведенных задач на каждой из библиотек. Кроме интерфейса и результатов оптимизации будем оценивать и быстродействие. Поскольку нам нужно только качественное отличие в быстродействии воспользуемся для этого самым простым листингом усредняя результаты каждого запуска программ.

Оптимизация с библиотекой pulp [1].

Листинг программы для решения задачи «О рекламе»
from pulp import *
import time
start = time.time()
x1 = pulp.LpVariable("x1", lowBound=0)
x2 = pulp.LpVariable("x2", lowBound=0)
problem = pulp.LpProblem('0',pulp.LpMaximize)
problem += 30*x1 +x2, "Функция цели"
problem += 90*x1+ 5*x2 <= 10000, "1"
problem +=x2 ==3*x1, "2"
problem.solve()
print ("Результат:")
for variable in problem.variables():
    print (variable.name, "=", variable.varValue)
print ("Прибыль:")
print (value(problem.objective))
stop = time.time()
print ("Время :")
print(stop - start)


В лис тенге программы уже знакомые нам соотношения для максимальной прибыли от рекламы 30*x1+x2, условия ограничения затрат, помеченные для сравнения «1». Мы не забыли и об отношении времён использования радио и теле рекламы, помеченные в лис тенге как «2». Назначение других операторов очевидны, Подробности можно прочесть в [1].

Результаты решения задачи оптимизации с использованием pulp.

Результат:
x1 = 95.238095
x2 = 285.71429
Прибыль:
3142.85714
Время:
0.10001182556152344

В итоге мы получили значения времён рекламы, при которых ожидаемая прибыль от её использования будет максимальна.

Оптимизация с библиотекой cvxopt [2].

Листинг программы для решения задачи «О рекламе».
from cvxopt.modeling import variable, op
import time
start = time.time()
x = variable(2, 'x')
z=-(30*x[0] +1*x[1])#Функция цели
mass1 = (90*x[0] + 5*x[1]  <= 10000) #"1"
mass2 = (3*x[0] -x[1] == 0) # "2"
x_non_negative = (x >= 0) #"3"    
problem =op(z,[mass1,mass2,x_non_negative])
problem.solve(solver='glpk')  
problem.status
print ("Прибыль:")
print(abs(problem.objective.value()[0]))
print ("Результат:")
print(x.value)
stop = time.time()
print ("Время :")
print(stop - start)


По структуре программа аналогична предыдущей, но имеются два существенных отличия. Во-первых, библиотека cvxopt настроена на поиск минимума функции цели, а не на максимум. Поэтому целевая функция взята с отрицательным знаком минус -(30*x[0] +1*x[1]). Полученное вследствие этого отрицательное её значение выведено по абсолютной величине. Во-вторых, введено ограничение на не отрицательность переменных- non_negative. Повлияло ли это на результат мы сейчас у видим.

Результаты решения задачи оптимизации с использованием cvxopt.

Прибыль:
3142.857142857143
Результат:
[ 9.52e+01]
[ 2.86e+02]
Время:
0.041656494140625

Никаких существенных изменений в сравнении с библиотекой pulp не произошло за исключением время работы программы.

Оптимизация с библиотекой scipy. optimize [3].

Листинг программы для решения задачи «О рекламе».
from scipy.optimize import linprog
import time
start = time.time()
c = [-30,-1] #Функция цели
A_ub = [[90,5]]  #'1'   
b_ub = [10000]#'1'   
A_eq = [[3,-1]] #'2'   
b_eq = [0] #'2'   
print (linprog(c, A_ub, b_ub, A_eq, b_eq))
stop = time.time()
print ("Время :")
print(stop - start)


Достаточно беглого взгляда на листинг, чтобы понять, что мы имеем дело с принципиально иным подходом к вводу данных. Хотя приведенные в листингах цифры помогают прояснить принцип организации данных, путём сравнения, всё же приведу пояснения. Список c = [-30,-1] содержит коэффициенты функции цели с обратным знаком, поскольку linprog () ищет минимум. Матрица A_ub содержит коэффициенты при переменных для условий в виде неравенств. Для нашей задачи это 90x1+5x2 <=10000. Значения в правой части неравнства-1000, помещается в список b_ub. Матрица A_eq содержит коэффициенты при переменных для условий в виде равенств. Для нашей задачи 3x1-x2=0, причём ноль в правой части, помещается в список b_eq.

Результаты решения задачи оптимизации с использованием scipy. optimize.

Fun: -3142.8571428571431
message: 'Optimization terminated successfully.'
nit: 2
slack: array([ 0.])
status: 0
success: True
x: array ([ 95.23809524, 285.71428571])

Время:
0.03020191192626953

Здесь более подробно выведены результаты расчётов, но сами результаты те же что и в предыдущих библиотеках.

По результатам решения задачи «О рекламе» можно сделать промежуточный вывод, о том что использование библиотеки scipy. optimize обеспечивает большее быстродействие и рациональную форму исходных данных. Однако без результатов решения транспортной задачи окончательный вывод делать рано.

Привожу решение транспортной задачи, но уже без подробных пояснений, поскольку основные этапы решения уже подробно описаны.

Оптимизация с библиотекой pulp.

Листинг программы для решения транспортной задачи.
from pulp import *
import time
start = time.time()
x1 = pulp.LpVariable("x1", lowBound=0)
x2 = pulp.LpVariable("x2", lowBound=0)
x3 = pulp.LpVariable("x3", lowBound=0)
x4 = pulp.LpVariable("x4", lowBound=0)
x5 = pulp.LpVariable("x5", lowBound=0)
x6 = pulp.LpVariable("x6", lowBound=0)
x7 = pulp.LpVariable("x7", lowBound=0)
x8 = pulp.LpVariable("x8", lowBound=0)
x9 = pulp.LpVariable("x9", lowBound=0)
problem = pulp.LpProblem('0',pulp.LpMaximize)
problem += -7*x1 - 3*x2 - 6* x3 - 4*x4 - 8*x5 -2* x6-1*x7- 5*x8-9* x9, "Функция цели"
problem +=x1 + x2 +x3<= 74,"1" 
problem +=x4 + x5 +x6 <= 40, "2"
problem +=x7 + x8+ x9 <= 36, "3"
problem +=x1+ x4+ x7 == 20, "4"
problem +=x2+x5+ x8 == 45, "5"
problem +=x3 + x6+x9 == 30, "6"                     
problem.solve()
print ("Результат:")
for variable in problem.variables():
    print (variable.name, "=", variable.varValue)
print ("Стоимость доставки:")
print (abs(value(problem.objective)))
stop = time.time()
print ("Время :")
print(stop - start)


Решение транспортной задачи требует минимизации затрат по доставке, поэтому функция цели вводиться со знаком минус, а выводиться по абсолютной величине.

Результаты решения транспортной задачи с использованием pulp.

Результат:
x1 = 0.0
x2 = 45.0
x3 = 0.0
x4 = 0.0
x5 = 0.0
x6 = 30.0
x7 = 20.0
x8 = 0.0
x9 = 0.0
Стоимость доставки:
215.0
Время:
0.19006609916687012

Оптимизация с библиотекой cvxopt.

Листинг программы для решения транспортной задачи.
from cvxopt.modeling import variable, op
import time
start = time.time()
x = variable(9, 'x')
z=(7*x[0] + 3*x[1] +6* x[2] +4*x[3] + 8*x[4] +2* x[5]+x[6] + 5*x[7] +9* x[8])
mass1 = (x[0] + x[1] +x[2] <= 74)
mass2 = (x[3] + x[4] +x[5] <= 40)
mass3 = (x[6] + x[7] + x[8] <= 36)
mass4 = (x[0] + x[3] + x[6] == 20)
mass5 = (x[1] +x[4] + x[7] == 45)
mass6 = (x[2] + x[5] + x[8] == 30)
x_non_negative = (x >= 0)    
problem =op(z,[mass1,mass2,mass3,mass4 ,mass5,mass6, x_non_negative])
problem.solve(solver='glpk')  
problem.status
print("Результат:")
print(x.value)
print("Стоимость доставки:")
print(problem.objective.value()[0])
stop = time.time()
print ("Время :")
print(stop - start)


Результаты решения транспортной задачи с использованием cvxopt.

Результат:
[ 0.00e+00]
[ 4.50e+01]
[ 0.00e+00]
[ 0.00e+00]
[ 0.00e+00]
[ 3.00e+01]
[ 2.00e+01]
[ 0.00e+00]
[ 0.00e+00]
Стоимость доставки:
215.0
Время :
0.03001546859741211

Оптимизация с библиотекой scipy. optimize.

Листинг программы для решения транспортной задачи.
from scipy.optimize import linprog	
import time
start = time.time()
c = [7, 3, 6,4,8,2,1,5,9]
A_ub = [[1,1,1,0,0,0,0,0,0],
               [0,0,0,1,1,1,0,0,0],
               [0,0,0,0,0,0,1,1,1]] 
b_ub = [74,40,36] 
A_eq = [[1,0,0,1,0,0,1,0,0],
               [0,1,0,0,1,0,0,1,0],
               [0,0,1,0,0,1,0,0,1]] 
b_eq = [20,45,30] 
print(linprog(c, A_ub, b_ub, A_eq, b_eq))
stop = time.time()
print ("Время :")
print(stop - start)


Результаты решения транспортной задачи с использованием scipy optimize.

fun: 215.0
message: 'Optimization terminated successfully.'
nit: 9
slack: array([ 29., 10., 16.])
status: 0
success: True
x: array([ 0., 45., 0., 0., 0., 30., 20., 0., 0.])
Время:
0.009982585906982422

Анализ решения двух типовых задач линейного программирования с помощью трёх библиотек аналогичного назначения не вызывает сомнения в выборе библиотеки scipy. optimize, как лидера по компактности ввода данных и быстродействию.

Что нового для использования библиотеки scipy. optimize при решении задач линейного программирования


Получение из исходной матрицы, списка для функции цели, а также заполнение матриц неравенств A_ub и равенств A_eq программно, позволит упростить работу по вводу данных и увеличить размерность исходной матрицы. Это позволить решать реальные задачи оптимизации. Рассмотрим, как это можно сделать на простом демонстрационном примере, не претендующем на идеальность кода.

Заголовок спойлера
import numpy as np
from scipy.optimize import linprog
b_ub = [74,40,36] 
b_eq = [20,45,30] 
A=np.array([[7, 3,6],[4,8,2],[1,5,9]])
m, n = A.shape
c=list(np.reshape(A,n*m))# Преобразование матрицы A в список c.
A_ub= np.zeros([m,m*n])
for i in np.arange(0,m,1):# Заполнение матрицы условий –неравенств.
         for j in np.arange(0,n*m,1):
                  if i*n<=j<=n+i*n-1:
                        A_ub  [i,j]=1
A_eq= np.zeros([m,m*n])
for i in np.arange(0,m,1):# Заполнение матрицы условий –равенств.
         k=0
         for j in np.arange(0,n*m,1):
                  if j==k*n+i:
                           A_eq [i,j]=1
                           k=k+1
print(linprog(c, A_ub, b_ub, A_eq, b_eq))


Теперь вводиться только сама матрица A и списки правых частей b_ub неравенств и b_ub – равенств.

Результат рaботы программы предсказуем.
fun: 215.0
message: 'Optimization terminated successfully.'
nit: 9
slack: array([ 29., 10., 16.])
status: 0
success: True
x: array([ 0., 45., 0., 0., 0., 30., 20., 0., 0.])

Вывод частный


При решении задач линейного программирования целесообразно использовать библиотеку scipy.optimize по методике, приведенной в статье, а матрицы условий заполнять програмно. При этом Вам не понадобятся специальные знания о методах решения оптимизационных задач.

Вывод общий


В последнее время появились разные библиотеки Python решающие аналогичные задачи. Решение о применении той или иной библиотеки часто носит субъективный характер. Поэтому целесообразно проводить их сравнительный анализ для области решаемых Вами задач.

Ссылки


  1. pythonhosted.org/PuLP
  2. cvxopt.org/userguide/modeling.html
  3. docs.scipy.org/doc/scipy/reference/tutorial/optimize.html
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/330648/


Метки:  

[Из песочницы] Поворот экрана во время выполнения долговременной операции

Суббота, 10 Июня 2017 г. 13:34 + в цитатник

Введение


При разработке практически любого приложения с пользовательским интерфейсом, программист рано или поздно встречается с ситуацией когда нужно выполнить долговременную операцию. Во время долговременной операции обычно пользователю показывают окно «Пожалуйста, подождите...» или что то в этом роде.

Платформа Android, да и наверное многие другие платформы не позволяют выполнять долговременные операции в UI потоке. Выполняя долговременную операцию в UI потоке вы просто напросто повесите программу.

Android предлагает для решения такого рода задач AsyncTask. AsyncTask позволяет выполнять долговременную операцию и взаимодействовать с UI потоком.

Проблема


Казалось бы ничего сложного, создаем AsyncTask передаем созданному AsyncTask указатель на текущую Activity и все готово, фоновый процесс работает, обновляет UI, все счастливы.

Все прекрасно работает до тех пор, пока не сменится ориентация экрана (Книжная -> Альбомная, Албомная -> Книжная) или приложение не будет отправлено в фон. Обычно при таком подходе после смены ориентации экрана происходит краш приложения.

Почему происходит краш приложения


Потому, что при смене ориентации эрана Android пересоздает Activity, в итоге Activity на который вы передавали ссылку AsyncTask-у уже уничтожен и ваш AsyncTask пытается взаимодействовать с уничтоженным объектом.

Решения


Все решают проблему по разному, не буду описывать все предлагаемые методы решения, их вы можете найти погуглив или в различных блогах или на StackOverflow.

Каждый из подходов требует реализации определенных твиков, дополнительных тестов, отладки, написания дополнительного кода или ограничения возможностей пользовательского интерфейса.
Столкнувшись с этой проблемой меня сильно раздражало, что для такой тривиальной задачи нет готового коробочного решения (во всяком случае я не нашел).

Можно я попробую


Не судите, пожалуйста, строго и не сочтите за самопиар, я просто хочу написать как я решаю данную проблему и поделиться решением с другими.

Я написал некоторое подобие фреймворка (я считаю, что фреймворк это слишком громко для моего решения, но ввиду особенностей использования все же подобие фреймворка) и разместил его на Github.

Исходные коды открыты, лицензия Apache 2.0, подчеркну еще раз, что я ничего не продаю, не навязываю и не попрашайничаю. Фреймворк называется Asmyk.

Как работает


При активации Activity (событие onResume), Activity отмечается в контексте приложения. Далее фоновая задача адресует UI задачи Activity из контекста.

Да ничего сложного, однако при реализации данной схемы начинающему программисту может быть немного сложновато и придется потратить пару дней на реализацию качественного решения.

Как внедрить


Скачиваем AAR файл и подключаем к проекту.

Если вы не расширяете класс Application своей реализацией, укажите в файле AndroidManifest.xml в теге application атрибут:

android:name="net.mabramyan.asmyk.core.AsmykApplicationContext"

Пример:



...

Если вы расширяете класс Application своей реализацией, необходимо наследовать свою реализацию от класса AsmykApplicationContext.

Activity с которыми вы будете работать из фона должны наследоваться от AsmykCompatActivitiy.
Примечание: Можете наследовать хоть все свои Activity от AsmykCompatActivitiy.

Пример


UI «Пожалуйста, подождите...»


Создаете Activity наследуемую от AsmykPleaseWaitActivity или от AsmykBasicPleaseWaitActivity.

Что нужно реализовать для AsmykPleaseWaitActivity


Метод описывает действия которые должна выполнить Activity при обновлении статуса операции. Объект progressObj будет передан из AsmykPleaseWaitTask:

void onProgress(final Object progressObj)

Метод описывает действия которые должна выполнить Activity при провале операции. Объект errorObj будет передан из AsmykPleaseWaitTask.

void onFail(final Object errorObj) 

Метод описывает действия которые должна выполнить Activity при успешном выполнении операции. Объект successObj будет передан из AsmykPleaseWaitTask.

void onSuccess(final Object successObj)

Что нужно реализовать для AsmykBasicPleaseWaitActivity


Метод описывает действия которые должна выполнить Activity при успешном выполнении операции. Объект successObj будет передан из AsmykPleaseWaitTask:

void onSuccess(final Object successObj)

Методы onFail и onProgress уже реализованы. В качестве аргументов принимают String с описанием ошибки или с описанием нового состояния фоновой задачи соответственно.

Фоновая задача


Далее создаем фоновую задачу реализуя класс AsmykPleaseWaitTask. Вам придется реализовать всего один метод описывающий вашу фоновую задачу:

void doInBackground(final AsmykApplicationContext ctx)

В процессе выполнения задачи вы можете вызывать метод void fireProgress(AsmykApplicationContext ctx, final Object progressObj) — данный метод впоследствии вызовет onProgress у вашей AsmykPleaseWaitActivity. По завершении задачи вызовите метод fireSuccess или fireFailed в зависимости от результата выполнения операции.

Запуск


Пример вызова фоновой задачи:


pleaseWaitTask.start((AsmykApplicationContext) MainActivity.this.getApplicationContext());
Intent intent = new Intent(MainActivity.this, PleaseWaitActivity.class);
startActivity(intent);

Внимание: AsmykPleaseWaitTask реально начнет выполнение задачи только после того, как будет показана Activity наследуемая от AsmykPleaseWaitActivity.

Это сделано для того, что бы фоновая задача не завершилась раньше чем будет показана Activity.

Пример простенького приложения вы можете посмотреть тут.

У меня все! Спасибо за внимание.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/330646/


Метки:  

Красно-черные деревья: коротко и ясно

Суббота, 10 Июня 2017 г. 13:08 + в цитатник
История из жизни. Девушка предложила своему парню-программисту пройти психологический тест:
Девушка: Нарисуй дерево.
Программист: (рисует бинарное дерево)
Девушка: Нет, другое.
Программист: Я и красно-черное дерево могу нарисовать.

Итак, сегодня хочу немного рассказать о красно-черных деревьях. Рассказ будет кратким, без рассмотрения алгоритмов балансировки при вставке/удалении элементов в красно-черных деревьях.


Красно-черные деревья относятся к сбалансированным бинарным деревьям поиска.

Как бинарное дерево, красно-черное обладает свойствами:


1) Оба поддерева являются бинарными деревьями поиска.

2) Для каждого узла с ключом $ k $ выполняется критерий упорядочения:

ключи всех левых потомков <= $ k $ < ключи всех правых потомков


(в других определениях дубликаты должны располагаться с правой стороны либо вообще отсутствовать).
Это неравенство должно быть истинным для всех потомков узла, а не только его дочерних узлов.

Свойства красно-черных деревьев:


1) Каждый узел окрашен либо в красный, либо в черный цвет (в структуре данных узла появляется дополнительное поле – бит цвета).

2) Корень окрашен в черный цвет.

3) Листья(так называемые NULL-узлы) окрашены в черный цвет.

4) Каждый красный узел должен иметь два черных дочерних узла. Нужно отметить, что у черного узла могут быть черные дочерние узлы. Красные узлы в качестве дочерних могут иметь только черные.

5) Пути от узла к его листьям должны содержать одинаковое количество черных узлов(это черная высота).

Ну и почему такое дерево является сбалансированным?


Действительно, красно-черные деревья не гарантируют строгой сбалансированности (разница высот двух поддеревьев любого узла не должна превышать 1), как в АВЛ-деревьях. Но соблюдение свойств красно-черного дерева позволяет обеспечить выполнение операций вставки, удаления и выборки за время $O(log N)$. И сейчас посмотрим, действительно ли это так.

Пусть у нас есть красно-черное дерево. Черная высота равна $bh$(black height).

Если путь от корневого узла до листового содержит минимальное количество красных узлов (т.е. ноль), значит этот путь равен $bh$.

Если же путь содержит максимальное количество красных узлов ($bh$ в соответствии со свойством $4$), то этот путь будет равен $2bh$.

То есть, пути из корня к листьям могут различаться не более, чем вдвое ($h<=2log(N + 1)$, где h — высота поддерева), этого достаточно, чтобы время выполнения операций в таком дереве было $O(log N)$

Как производится вставка?


Вставка в красно-черное дерево начинается со вставки элемента, как в обычном бинарном дереве поиска. Только здесь элементы вставляются в позиции NULL-листьев. Вставленный узел всегда окрашивается в красный цвет. Далее идет процедура проверки сохранения свойств красно-черного дерева $1-5$.

Свойство 1 не нарушается, поскольку новому узлу сразу присваивается красный цвет.

Свойство 2 нарушается только в том случае, если у нас было пустое дерево и первый вставленный узел (он же корень) окрашен в красный цвет. Здесь достаточно просто перекрасить корень в черный цвет.

Свойство 3 также не нарушается, поскольку при добавлении узла он получает черные листовые NULL-узлы.

В основном встречаются 2 других нарушения:

1) Красный узел имеет красный дочерний узел (нарушено свойство $4$).

2) Пути в дереве содержат разное количество черных узлов (нарушено свойство $5$).

Подробнее о балансировке красно-черного дерева при разных случаях (их пять, если включить нарушение свойства $2$) можно почитать на wiki.

Это вообще где-то используется?


Да! Когда в институте на третьем курсе нам читали «Алгоритмы и структуры данных», я и не могла представить, что красно-черные деревья где-то используются. Помню, как мы не любили тему сбалансированных деревьев. Ох уж эти родственные связи в красно-черных деревьях («дядя», «дедушка», «чёрный брат и крестный красный отец»), прям Санта-Барбара какая-то. Правые и левые, малые и большие повороты АВЛ-деревьев – сплошные американские горки. Вы тоже не любите красно-черные деревья? Значит, просто не умеете их готовить. А кто-то просто взял и приготовил. Так, например, ассоциативные массивы в большинстве библиотек реализованы именно через красно-черные деревья.

Это все, что я хотела рассказать.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/330644/


Метки:  

Всегда ли надежно шифрование или восстановление данных с внешнего жесткого диска Prestigio Data Safe II

Суббота, 10 Июня 2017 г. 12:42 + в цитатник
Владелец небольшого предприятия вечером в понедельник решил заняться финансовым анализом текущих дел. Придя вечером домой, он расположился в домашнем кабинете, подключил к ноутбуку внешний накопитель Prestigio Data Safe II 500ГБ и погрузился в цифры. Внезапно его мысли прервал донесшийся шум, а следом в помещение с пронзительным мяуканьем влетел домашний любимец, который, игнорируя все препятствия, запрыгнул на стол, пулей промчался по нему, совершил прыжок на шторы и взобрался на карниз, где в итоге замер и лишь подозрительно косился на окружающих, шипя при любой попытке приближения.

Когда кульминация событий сошла на нет и стали ясны причины подобного поведения домашнего животного, хозяин обнаружил, что одним только перепуганным котом дело не ограничилось. На столе и вокруг него царил настоящий хаос: разлетевшиеся по комнате документы и безжизненно болтающийся на USB кабеле винчестер.


рис. 1

После устранения последствий забега несчастного животного было уделено пристальное внимание винчестеру. По спине владельца диска пробежал легкий холодок, когда он так и не дождался от накопителя запуска утилиты с предложением ввести пароль для доступа к скрытому от посторонних глаз разделу. После нескольких бесплодных попыток был срочно вызван системный администратор.

Администратор, убедившись в тщетности попыток работать с накопителем через USB, разобрал бокс, извлек него HDD Toshiba MK5059GSXP и подключил к SATA порту компьютера. Загрузив Windows, в оснастке управления дисками обнаружил, что с точки зрения ОС накопитель не содержит ни одного раздела. Была осуществлена попытка запуска программы автоматического восстановления, но сканирование, едва начавшись, подвесило компьютер, и из накопителя послышались щелчки. На этом этапе было принято решение, что пора остановиться.

С такой предысторией накопитель поступил в нашу лабораторию восстановления данных. Исходя из того, что накопитель был после удара и при попытке сканирования начал издавать стучащие звуки, то первой операцией будет его очистка от пыли и проведение обследования в условиях ламинарного бокса. Тщательному обследованию подлежат подвески и слайдеры БМГ, рециркуляционный воздушный фильтр, а также верхняя поверхность в пакете дисков.


рис. 2

Осмотр рециркуляционного воздушного фильтра и БМГ не выявил деформаций и наличия металлических частиц, на основании чего делаем вывод о допустимости старта накопителя с оригинальным БМГ. Осмотр верхней поверхности под микроскопом позволяет выявить наличие небольшой радиальной царапины у внешнего края.

Собрав накопитель, подключаем его к порту PC3000 и подаем питание. Слышен характерный шум воздуха при вращении вала и звук прохождения калибровочного теста. Далее возникает некоторая задержка, после которой накопитель наконец-то выдает по регистрам готовность принимать команды.


рис. 3

Выполняем резервирование микропрограммы, в процессе обнаруживаем нечитабельность G-list. Так как этот дефект лист необходим для работы системы трансляции, то накопитель не позволяет прочитать ни одного сектора из пользовательской зоны в обычном режиме.

Попытка записи на неиспользуемый трек служебной зоны приводит к тому, что жесткий диск начинает издавать стучащие звуки, что явно говорит о проблемах с записью. Так как возможности переписать нечитаемый модуль у нас нет, то не будем пытаться записать чистый модуль G-list.

Для накопителей Toshiba, у которых не работают или некорректно работают записывающие головки, либо имеются серьезные повреждения в служебной зоне, которые не позволяют корректно функционировать, можно использовать особенности технологического режима. После включения технологического режима, накопитель меняет логику работы: не осуществляется запись в SMART логи, отключено оффлайн сканирование, а также система трансляции работает без учета записей в P-list и G-list.

Перед вычитыванием в технологическом режиме необходимо проанализировать P-list жесткого диска, чтобы учесть все исключенные области из PBA диапазона и при вычитывании получить данные пользователя, которые записывались в рамках LBA диапазона (в котором учтены исключения из P-list), без сдвигов.

Следующим шагом будет построение карты зонного распределения, чтобы понимать каким образом сформировано логическое пространство. Для чтения построим карту из мини зон и отсортируем в порядке убывания, так как при обследовании повреждения обнаруживались у внешнего края пластины.


рис. 4

В данном случае без особых затруднений было прочитано до 15 998 ххх секторов. Потом обнаружились затруднения в чтении.


рис. 5

Попытка перестроить цепочки в порядке возрастания с дальнейшим чтением показало наличие дефектов от самого начала логического диапазона.


рис. 6

Исключаем чтение по головке №0 и пытаемся читать далее, следом обнаруживаются проблемы с 249 xxx сектора, также исключаем чтение по головке №2. Выполняем чтение зон головками №1 и №3 на интервале от 0 до 15 998 975 сектора.

Для дальнейшего вычитывания проблемных зон будем использовать PIO режим для более точного контроля состояния накопителя. Выполнив некоторое число попыток чтения с разным таймаутом ожидания готовности и разным размером прыжков при обнаружении нестабильностей, переходим к этапу многопроходного чтения дефектов. В результате всех попыток чтения непрочитанными остались менее 3000 секторов из всего множества 976 773 168 секторов.

Выполняем заполнение непрочитанных секторов на копии паттерном 0xDE 0xAD. Проводим анализ регулярных выражений для различных популярных типов файлов и обнаруживаем отсутствие пользовательских данных на диске, но в тоже время можно сказать, что диск заполнен некими данными более чем на 95% (согласно того, какое количество секторов заполнено ненулевыми значениями).

На этом этапе отложим посекторную копию и проведем анализ особенностей USB-SATA моста, используемого в боксе.


рис. 7

Данный USB-SATA адаптер основан на MCU J-Micron 20339. Возьмем накопитель, все сектора которого заполнены 0x00, присоединим к данному адаптеру и подключим к ПК.


рис. 8

ОС обнаружила 2 накопителя по 20Мб. При анализе выяснилось, что первый раздел доступен только для чтения. На втором запись доступна.


рис. 9

Исходя из названий устройств в паспортных данных, можно предположить, что роль первого предназначается для хранения приложения, конфигурирующего размер публичного и скрытого раздела, а также открывающего доступ к скрытому разделу. Второй раздел предназначен для хранения «публичных» данных и третий появится, только при вводе пароля в фирменной утилите.

При открытии в дисковом редакторе в каждом секторе любого из устройств видим заполнение неким паттерном.


рис. 10

Отключим диск от данного адаптера и проанализируем, располагает ли контроллер некие метаданные на самом накопителе, так как диск изначально был полностью заполнен нулями. В секторе 0x18FBF (102 335) и секторе 0x18FCF (102 351) обнаруживается заполнение неким хаотичным содержимым.

Начиная с сектора 102 352 выполним запись номера сектора в первые 4 байта на протяжении 40 960 секторов. Вновь подключив диск к адаптеру проанализируем заполнение первого двадцати мегабайтного раздела. Увидим, что на протяжении всего раздела остался прежний паттерн, как на рис. 10, но с изменениями в первых байтах. Выполнив XOR операции между и первоначальным паттерном и текущими значениями, получим сектора, содержимым которых будут нули с номером сектора в первых 4 байтах.

На основании этого, мы можем утверждать, что данный контроллер шифрует данные посредством XOR операции, размер ключа 512 байт. Недостаток данного «шифрования» в том, что в секторах, заполненных нулями, но прочитанных через данный USB-SATA адаптер, будут находиться все 512 байт ключа в оригинальном виде. Также обратим внимание, что в накопителях, где в секторах 102 335 и 102 351 содержится некорректное содержимое с точки зрения микропрограммы JM20339, то произойдет формирование новых ключей случайным образом.

Вернемся к копии проблемного накопителя и проанализируем карту прочитанного. Удостоверимся, что ключевые сектора 102 335 и 102 351 прочитаны и дополнительные методы анализа для нахождения ключа не потребуются. Перенесем содержимое ключевых секторов на чистый накопитель и подсмотрим, какой XOR паттерн сформирует микропрограмма контроллера JM20339.

Создадим резервную копию накопителя, на который была записана посекторная копия пациента, и выполним XOR операцию над каждым сектором, начиная с сектора 102352 с полученным ключом.

Начав анализ регулярных выражений, обратим внимание, что обнаруживается множество признаков наличия популярных типов файлов (jpg, doc, xls и т.п.). Это обстоятельство подтверждает корректность расшифровывания данных.

Для удобства анализа в 0 секторе напишем классическую таблицу разделов, в которой опишем 3 раздела, имеющихся на данном накопителе. В нашу первоначальную задачу подставим накопитель с расшифрованными данными.

Так как в задании клиентом было поставлено восстановление данных из скрытого раздела, то проведем оценку целостности файловой системы на нем посредством анализа расположения метаданных и проверим, нет ли непрочитанных участков. Анализ выявляет, что 16 секторов в MFT прочитать не удалось, что говорит о потере не более 8 файловых записей. Дальнейшее сопоставление файлов с картой прочитанного позволяет выявить еще 73 файла, пострадавших в результате контакта слайдеров с поверхностями пластин.

99, 9% восстановленных файлов – это конечный результат, который удовлетворил клиента.
В качестве заключения можно еще раз напомнить всем о необходимости резервного копирования и также отметить, что по сию пору на рынке существует немало решений, где опция шифрования данных — лишь маркетинговая уловка, которая не в состоянии надежно защитить пользовательские данные.

Предыдущая публикация: Восстановление данных с внешнего жесткого диска Seagate FreeAgent Go
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/330642/


Метки:  

Поиск сообщений в rss_rss_hh_new
Страницы: 1437 ... 1001 1000 [999] 998 997 ..
.. 1 Календарь