Проведите выходные, изучая основные принципы SpringCloud Ribbon

Архитектура
Проведите выходные, изучая основные принципы SpringCloud Ribbon

предисловие

Вторая статья о распределенной среде после SpringCloud Feign также придерживается принципа большой цели одного компонента SpringCloud за один уик-энд.

Если ты хочешь увидеть друзей Фейна,кликните сюда, основной принцип Feign встречает вас неожиданно

При обычном использовании SpringCloud обычно используется Feign, потому что Feign интегрирует Ribbon внутри.

Но Ribbon — это еще одна точка познания, которую нельзя игнорировать, и она гораздо сложнее, чем Feign. Перечислите темы плана эссе

  1. Как получить экземпляр службы реестра
  2. Как перейти в автономный режим для экземпляра службы, не связанной с работоспособностью
  3. Реализация основного принципа ленты
  4. Пользовательская политика балансировки нагрузки ленты

Статья использует SpringCloud ленты исходного кода hoxton.sr9 версия:2.2.6.RELEASE

Кроме того, в конце статьи я высказал некоторые мысли по поводу процесса просмотра исходного кода, а также неразумного авторского описания процесса в Ribbon.

Советы по концепции

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

Балансировка нагрузки относится к распределению между несколькими исполнительными модулями с помощью стратегий балансировки нагрузки.Существует два распространенных метода балансировки нагрузки.

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

Ribbon

Лента — это компонент балансировки нагрузки с открытым исходным кодом Netflix.Поведение балансировки нагрузки происходит на стороне клиента, поэтому он относится ко второму типу выше.

Вообще говоря, когда SpringCloud создается и используется, Ribbon используется как инструмент балансировки нагрузки клиента. Однако он не будет использоваться отдельно, а будет использоваться в сочетании с RestTemplate и Feign, Feign интегрирует ленту внизу, без дополнительной настройки, из коробки.

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

RestTemplate — это веб-фреймворк, который обеспечивает доступ к сторонним RESTFul Http-интерфейсам в Spring Web.

Подготовка окружающей среды

Регистрационный центр использует ALI NACO для создания двух сервисов, запущен кластер производителя, и потребитель использует Resttemplate + ленту для вызова. Общая структура вызова выглядит следующим образом

Код производителя выглядит следующим образом: зарегистрируйте службу в Nacos и откройте службу Http Get для внешнего мира.

Код потребителя выглядит следующим образом: зарегистрируйте службу в Nacos и инициируйте удаленный вызов балансировки нагрузки через RestTemplate + Ribbon.

RestTemplate по умолчанию не балансирует нагрузку, поэтому вам нужно добавить @LoadBalanced

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

Хотите представить принципы фреймворка в строгом порядке, без предварительного цитирования не введенных терминов,Это почти невозможно, постараюсь объяснить как можно понятнее

Как получить экземпляр службы реестра

Давайте сначала посмотрим, как Ribbon получает работающий экземпляр реестра на стороне клиента.Этот момент меня раньше смущал.

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

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

Объясните место, отмеченное желтым прямоугольником выше:

  • RibbonLoadBalancerClient: отвечает за обработку запросов балансировки нагрузки.
  • ILoadBalancer: в интерфейсе определен ряд методов для реализации балансировки нагрузки, что эквивалентно роли маршрута Класс реализации по умолчанию в ленте — ZoneAwareLoadBalancer.
  • unknown: ZoneAwareLoadBalancer — это балансировщик нагрузки с несколькими зонами, этот unkonwn представляет собой зону по умолчанию.
  • allServerList: представляет экземпляр службы интерфейса, полученный из реестра Nacos, а upServerList представляет работоспособный экземпляр.

Теперь, если вы хотите узнать, как лента получает экземпляр службы, вам нужноgetLoadBalancer()

getLoadBalancer

Сначала объявите, что семантика метода getLoadBalancer() заключается в получении имени из контейнера контекста Ribbon parent-child.ribbon-produce, типILoadBalancer.classВесенняя фасоль

Когда я говорил о Feign ранее, я сказал, что Ribbon создаст родительско-дочерний контекст Spring для каждого поставщика услуг, а здесь получит bean-компоненты из дочернего контекста.

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

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

ZoneAwareLoadBalancer

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

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

ZoneAwareLoadBalancer создается дочерним контейнером при первом вызове службы.

@Bean @ConditionalOnMissingBean  // RibbonClientConfiguration 被加载,从 IOC 容器中获取对应实例填充到 ZoneAwareLoadBalancer
public ILoadBalancer ribbonLoadBalancer(IClientConfig config,
                                        ServerList<Server> serverList, ServerListFilter<Server> serverListFilter,
                                        IRule rule, IPing ping, ServerListUpdater serverListUpdater) {
    ...
    return new ZoneAwareLoadBalancer<>(config, rule, ping, serverList,
            serverListFilter, serverListUpdater);
}

public ZoneAwareLoadBalancer(IClientConfig clientConfig, IRule rule,
                             IPing ping, ServerList<T> serverList, ServerListFilter<T> filter,
                             ServerListUpdater serverListUpdater) {
    // 调用父类构造方法
    super(clientConfig, rule, ping, serverList, filter, serverListUpdater);
}

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

public DynamicServerListLoadBalancer(IClientConfig clientConfig, IRule rule, IPing ping,
                                     ServerList<T> serverList, ServerListFilter<T> filter,
                                     ServerListUpdater serverListUpdater) {
    // 调用父类 BaseLoadBalancer 初始化一些配置,包括 Ping(检查服务是否可用)Rule(负载均衡规则)
    super(clientConfig, rule, ping);  
    // 较重要,获取注册中心服务的接口
    this.serverListImpl = serverList;
    this.filter = filter;
    this.serverListUpdater = serverListUpdater;
    if (filter instanceof AbstractServerListFilter) {
        ((AbstractServerListFilter) filter).setLoadBalancerStats(getLoadBalancerStats());
    }
    // 初始化步骤分了两步走,第一步在上面,这一步就是其余的初始化
    restOfInit(clientConfig);
}

Для начала поговорим о методе инициализации в BaseLoadBalancer.Здесь мы в основном назначаем некоторые важные параметры а так же Ping и Rule.Кроме того таймер реализован по IPing.Что такое Ping и Rule описывается далее.

Метод примерно делает следующее:

  1. Установите ключевые параметры, такие как объект конфигурации клиента, имя и т. д.
  2. Получить максимальную продолжительность каждого интервала и пинг-пинг
  3. Установите конкретное правило балансировки нагрузки IRule, ZoneAvoidanceRule по умолчанию, опрос в соответствии с сервером и зонами зон.
  4. Установите конкретный метод ping, DummyPing по умолчанию, возвращайте True напрямую
  5. В соответствии с конкретной реализацией Ping выполнять запланированные задачи Ping Server

Здесь мы представим, что заполняют IPing и IRule и каковы их реализации?

Зонд службы IPing

Интерфейс iPing отвечает за отправку запроса PING на экземпляр сервера, определяя, отвечает ли сервер, чтобы определить, доступен ли сервер.

Интерфейс имеет только один метод isAlive, который завершает функцию обнаружения и проверки связи, реализуя класс

public interface IPing {
    public boolean isAlive(Server server);
}

Класс реализации IPing выглядит следующим образом:

  • PingUrl: инициировать сетевой вызов, чтобы определить, доступен ли сервер с помощью ping (как правило, вам необходимо указать путь для создания PingUrl, по умолчанию используется IP + порт)
  • PingConstant: возвращает информацию о том, доступна ли служба, и по умолчанию возвращает True, указывая, что она доступна.
  • NoOpPing: Нет операции, возвращает True напрямую, указывая доступность
  • DummyPing: класс по умолчанию, который возвращает True напрямую, реализует метод initWithNiwsConfig.

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

Интерфейс IRULE отвечает за обработку стратегий балансировки нагрузки на основе различных алгоритмов и логики. Существует 7 встроенных стратегий, ZoneaVoidanceRule по умолчанию

  1. BestAvailableRule: выберите сервер с наименьшим объемом запросов в списке услуг.
  2. RandomRule: случайный выбор сервера из списка служб
  3. RetryRule: повторите попытку сервера в соответствии с методом опроса.
  4. ZoneAvoidanceRule: выберите сервер на основе региона зоны сервера и опроса доступности.
  5. ...

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

void restOfInit(IClientConfig clientConfig) {
    boolean primeConnection = this.isEnablePrimingConnections();
    // turn this off to avoid duplicated asynchronous priming done in BaseLoadBalancer.setServerList()
    this.setEnablePrimingConnections(false);
    // 初始化服务列表,并启用定时器,对服务列表作出更新
    enableAndInitLearnNewServersFeature();
    // 更新服务列表,enableAndInitLearnNewServersFeature 中定时器的执行的就是此方法
    updateListOfServers();
    if (primeConnection && this.getPrimeConnections() != null) {
        this.getPrimeConnections()
                .primeConnections(getReachableServers());
    }
    this.setEnablePrimingConnections(primeConnection);
    LOGGER.info("DynamicServerListLoadBalancer for client {} initialized: {}", clientConfig.getClientName(), this.toString());
}

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

public void updateListOfServers() {
    List<T> servers = new ArrayList<T>();
    if (serverListImpl != null) {
        // 获取服务列表数据
        servers = serverListImpl.getUpdatedListOfServers();
        LOGGER.debug("List of Servers for {} obtained from Discovery client: {}",
                getIdentifier(), servers);

        if (filter != null) {
            servers = filter.getFilteredListOfServers(servers);
            LOGGER.debug("Filtered List of Servers for {} obtained from Discovery client: {}",
                    getIdentifier(), servers);
        }
    }
    // 更新所有服务列表
    updateAllServerList(servers);
}

Первый вопрос ходил по кругу и наконец нашел список услуг, как его получить.serverListImpl реализован из ServerList, поскольку мы используем реестр Nacos, конкретная реализация ServerListNacosServerList

public interface ServerList<T extends Server> {
    public List<T> getInitialListOfServers();
    public List<T> getUpdatedListOfServers();
}

В ServerList есть только два метода интерфейса, а именноПолучите инициализированный список сервисов, чтобы получить обновленный список набора услуг, оба вызова в реализации Nacos являются методом реализации, который может быть разработан следующим образом

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

Между лентой и различными реестрами служб эта реализация очень похожа на реализацию между JDBC и различными базами данных.

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

  1. Клиент балансировки нагрузки отправляетРегистрационный центр NACOS получает информацию об регистрации услуг
  2. Согласно различным реализациям IPing, к полученному списку услугОтправлять пинг последовательно, чтобы судить о доступности услуги. Правильно, он серийный, если у вас много экземпляров, то можноПопробуйте переписать логику блока ping
  3. если доступность услугиИзменился или вышел из системы вручную, затем повторно извлеките или обновите список услуг
  4. Когда у клиента балансировки нагрузки есть список этих классов регистрации службы, он, естественно, можетСтратегия балансировки нагрузки IRule

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

Сначала я сделал два«Смелый» эксперимент, в первый раз должен выполнить процесс отключения для проекта Pribeboot производителя. В настоящее время реестр NACOSОперативное восприятие и удаление экземпляра по данному сервису

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

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

  1. Измените политику балансировки нагрузки клиента наСлучайная загрузка RandomRule, можете сами протестировать, нет фиксированных правил загрузки
  2. Зарегистрируйте три экземпляра службы производителя в Nacos, проверьтеУбедитесь, что экземпляры в сервисной группе зарегистрированы нормально
  3. Ключ к операции, соответствующей первому запросу в примере интерфейсов производителя-потребителя, для обеспеченияЛента кэширует соответствующий сервер для клиента
  4. Остановите службу производителя, в это времяНемедленно звоните с Jmeter, группа потоков Jmeter инициирует запрос 100 раз (обязательно инициируйте запрос Jmeter перед обновлением кеша сервера)
  5. В это время вы увидите, что будут происходить случайные сбои, то есть после остановки службы,Худшие 30 секунд производственного обслуживания недоступныЭто время можно настроить вернусь Почему 30 секунд

Регулярное ведение списка услуг

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

  1. Используйте класс реализации PingUrl IPing,Пинговать адрес службы каждые 10 секунд, если статус возврата не 200, то инстанс по умолчанию отключен
  2. Сканирование, встроенное в ленточный клиент,По умолчанию Nacos, экземпляр службы реестра, извлекается каждые 30 секунд., если автономный экземпляр удален из кэша клиента

Этот фрагмент исходного кода больше не публикуется, просто поместите два места исходного кода, просто посмотрите, если вам интересно.

+ DynamicServerListLoadBalancer#enableAndInitLearnNewServersFeature
+ BaseLoadBalancer#setupPingTask

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

Реализация основного принципа ленты

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

  1. Создайте клиент балансировки нагрузки ILoadBalancer и инициализируйте необходимую ленту.Список таймеров и экземпляров службы в реестре
  2. Из ILoadBalancer черезБалансировка нагрузки выбирает сервер в списке исправных служб
  3. Замените имя службы (лента-производство) на IP + порт на сервере., затем создайте HTTP-запрос для вызова и возврата данных

Как упоминалось выше, ILoadBalancer отвечает за маршрутизацию балансировки нагрузки, а класс реализации IRule используется внутри для выполнения вызовов нагрузки.

public interface ILoadBalancer {
    public Server chooseServer(Object key);
  	...
}

В процессе ChooseServer вызывается метод Choose в политике загрузки IRule, и внутри метода получается исправный Сервер.

public Server choose(ILoadBalancer lb, Object key) {
    ... 
    Server server = null;
    while (server == null) {
        ...
        List<Server> upList = lb.getReachableServers();  // 获取服务列表健康实例
        List<Server> allList = lb.getAllServers();  // 获取服务列表全部实例
        int serverCount = allList.size();  // 全部实例数量
        if (serverCount == 0) {  // 全部实例数量为空,返回 null,相当于错误返回
            return null;
        }
        int index = chooseRandomInt(serverCount);  // 考虑到效率问题,使用多线程 ThreadLocalRandom 获取随机数
        server = upList.get(index);  // 获取健康实例
        if (server == null) {
            // 作者认为出现获取 server 为空,证明服务列表正在调整,但是!这只是暂时的,所以当前释放出了 CPU
            Thread.yield();
            continue;
        }
        if (server.isAlive()) {  // 服务为健康,返回
            return (server);
        }
        ...
    }
    return server;
}

Кратко расскажу о процессе выбора случайной стратегии

  1. Получить список всех услуг и медицинских услуг,Определить, равно ли количество всех экземпляров 0, если это так, он вернет null, что эквивалентно ошибке
  2. Получите индекс индекса из списка всех сервисов, затем перейдите кПолучить список работоспособных экземпляров сервера
  3. Если полученный сервер пуст, он отдаст ЦП, а затем снова повторит описанный выше процесс,Эквивалент механизма повторных попыток
  4. Если полученный сервер неисправен, установите сервер пустым, немного отдохните и продолжите описанный выше процесс.

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

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

Оставьте вопрос-мысль:

Почему бы просто не выбрать экземпляр непосредственно из исправных экземпляров?

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

Пользовательская политика балансировки нагрузки ленты

Этот вид пользовательской стратегии относительно удобен в рамках.В соответствии с вышеуказанными вопросами мы настраиваем стратегию

@Slf4j
public class MyRule extends AbstractLoadBalancerRule {
    @Override
    public Server choose(Object key) {
        ILoadBalancer loadBalancer = getLoadBalancer();
        while (true && ) {
            Server server = null;
            // 获取已启动并且可访问的服务列表
            List<Server> reachableServers = loadBalancer.getReachableServers();
            if (CollectionUtils.isEmpty(reachableServers)) return null;
            int idx = ThreadLocalRandom.current().nextInt(reachableServers.size());
            server = reachableServers.get(idx);
            if (server == null || server.isAlive()) {
                log.warn("Ribbon 服务实例异常, 获取为空 || 状态不健康");
                Thread.yield();
                continue;
            }
            return server;
        }
    }

    ... initWithNiwsConfig 不用实现
}

Поговорим о логике загрузки MyRule, которую мы реализовали сами:

  1. IRule получить список услуг, не реализованный для вызывающего абонента, но абстрактный AbstractLoadBalancerRule, поэтому нам просто нужно получить наследование списка сервисов
  2. Это примерно похоже на правило случайной загрузки, за исключением того, что здесь процесс упрощен.Получите экземпляр сервера непосредственно из списка работоспособных экземпляров службы.
  3. Возврат после подтверждения того, что сервер не пуст и узел исправен, если не совпадает, распечатать лог, немного поспать и повторить
  4. Если вы в безопасности,Лучше добавить условие на количество циклов в while, чтобы избежать бесконечного цикла

Затем зарегистрируйте MyRule в контейнере SPring IOC, и он заменит правило загрузки Rule по умолчанию во время инициализации.

Мысли об IP-адресах

При чтении исходного кода Ribbon Ping я обнаружил два места, которые считаю необоснованными.

  1. setPingIntervalНет смысла выполнять поставленную задачу пинга при установке интервала пинга
  2. BaseLoadBalancerПинг - это нуль в конструкторе,setPingInterval вызывается снова, результат просто вернется пустым

Два метода setPingInterval и setPing применяются при инициализации BaseLoadBalancer, что эквивалентно продолжению вышеописанной логики. Сначала объясните логику выполнения, а потом смотрите неразумные места

setupPingTask используется для периодического выполнения задачи проверки связи с Сервером, то есть для определения доступности Сервера.

Лично считаю, что нет необходимости выполнять метод setupPingTask в setPingInterval

Приведенные выше выводы основаны на следующем:

  1. При выполнении setPingInterval в первый раз ping должен быть пустым, тогда он вернет True в canSkipPing, а затем напрямую завершит метод setPingInterval.
  2. Позже я подумал о том, не будет ли он ссылаться в других местах и ​​его нужно принудительно обновлять.Однако гусь ищет ссылку глобально, и он вызывается только при этой инициализации.Конечно, не исключено, что другие зависимые пакеты будут использовать этот метод.
  3. Подводя итог, можно сказать, что метод setPingInterval, выполняющий поставленную задачу Ping, не имеет смысла.

Другое дело, что автору кажется, что вызываемые в коде методы не имеют практического смысла. Как и в предыдущем случае, метод setPingInterval выполняется, когда ping пуст.

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

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

Вывод

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

Лента начинается с инициализации клиента балансировки нагрузки ILoadBalancer и описывает конкретное содержание процесса инициализации, в том числе то, как включить таймер IP-адресации и таймер обновления списка служб.

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

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

Поиск в Wechat [круг интереса к исходному коду], подпишитесь на официальный аккаунт и ответьте 123, чтобы получить учебные материалы, такие как GO, Netty, Seata, SpringCloud Alibaba, спецификации разработки, сборник интервью, структуру данных и так далее!