Кратко опишите реализацию принципа RPC

Архитектура алгоритм открытый источник балансировки нагрузки

предисловие

Изменения в архитектуре часто связаны с расширением масштабов бизнеса.

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

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

сервисная структура

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

Идеи демонстрационной реализации RPC

Оригинальный автор, Лян Фэй, записал его очень простые идеи реализации RPC здесь.

Базовые классы фреймворка

/*
 * Copyright 2011 Alibaba.com All right reserved. This software is the
 * confidential and proprietary information of Alibaba.com ("Confidential
 * Information"). You shall not disclose such Confidential Information and shall
 * use it only in accordance with the terms of the license agreement you entered
 * into with Alibaba.com.
 */
package com.alibaba.study.rpc.framework;

import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * RpcFramework
 * 
 * @author william.liangf
 */
public class RpcFramework {

    /**
     * 暴露服务
     * 
     * @param service 服务实现
     * @param port 服务端口
     * @throws Exception
     */
    public static void export(final Object service, int port) throws Exception {
        if (service == null)
            throw new IllegalArgumentException("service instance == null");
        if (port <= 0 || port > 65535)
            throw new IllegalArgumentException("Invalid port " + port);
        System.out.println("Export service " + service.getClass().getName() + " on port " + port);
        ServerSocket server = new ServerSocket(port);
        for(;;) {
            try {
                final Socket socket = server.accept();
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            try {
                                ObjectInputStream input = new ObjectInputStream(socket.getInputStream());
                                try {
                                    String methodName = input.readUTF();
                                    Class<?>[] parameterTypes = (Class<?>[])input.readObject();
                                    Object[] arguments = (Object[])input.readObject();
                                    ObjectOutputStream output = new ObjectOutputStream(socket.getOutputStream());
                                    try {
                                        Method method = service.getClass().getMethod(methodName, parameterTypes);
                                        Object result = method.invoke(service, arguments);
                                        output.writeObject(result);
                                    } catch (Throwable t) {
                                        output.writeObject(t);
                                    } finally {
                                        output.close();
                                    }
                                } finally {
                                    input.close();
                                }
                            } finally {
                                socket.close();
                            }
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }).start();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 引用服务
     * 
     * @param <T> 接口泛型
     * @param interfaceClass 接口类型
     * @param host 服务器主机名
     * @param port 服务器端口
     * @return 远程服务
     * @throws Exception
     */
    @SuppressWarnings("unchecked")
    public static <T> T refer(final Class<T> interfaceClass, final String host, final int port) throws Exception {
        if (interfaceClass == null)
            throw new IllegalArgumentException("Interface class == null");
        if (! interfaceClass.isInterface())
            throw new IllegalArgumentException("The " + interfaceClass.getName() + " must be interface class!");
        if (host == null || host.length() == 0)
            throw new IllegalArgumentException("Host == null!");
        if (port <= 0 || port > 65535)
            throw new IllegalArgumentException("Invalid port " + port);
        System.out.println("Get remote service " + interfaceClass.getName() + " from server " + host + ":" + port);
        return (T) Proxy.newProxyInstance(interfaceClass.getClassLoader(), new Class<?>[] {interfaceClass}, new InvocationHandler() {
            public Object invoke(Object proxy, Method method, Object[] arguments) throws Throwable {
                Socket socket = new Socket(host, port);
                try {
                    ObjectOutputStream output = new ObjectOutputStream(socket.getOutputStream());
                    try {
                        output.writeUTF(method.getName());
                        output.writeObject(method.getParameterTypes());
                        output.writeObject(arguments);
                        ObjectInputStream input = new ObjectInputStream(socket.getInputStream());
                        try {
                            Object result = input.readObject();
                            if (result instanceof Throwable) {
                                throw (Throwable) result;
                            }
                            return result;
                        } finally {
                            input.close();
                        }
                    } finally {
                        output.close();
                    }
                } finally {
                    socket.close();
                }
            }
        });
    }

}

определить сервисный интерфейс

/*
 * Copyright 2011 Alibaba.com All right reserved. This software is the
 * confidential and proprietary information of Alibaba.com ("Confidential
 * Information"). You shall not disclose such Confidential Information and shall
 * use it only in accordance with the terms of the license agreement you entered
 * into with Alibaba.com.
 */
package com.alibaba.study.rpc.test;

/**
 * HelloService
 * 
 * @author william.liangf
 */
public interface HelloService {

    String hello(String name);

}

внедрить сервис

/*
 * Copyright 2011 Alibaba.com All right reserved. This software is the
 * confidential and proprietary information of Alibaba.com ("Confidential
 * Information"). You shall not disclose such Confidential Information and shall
 * use it only in accordance with the terms of the license agreement you entered
 * into with Alibaba.com.
 */
package com.alibaba.study.rpc.test;

/**
 * HelloServiceImpl
 * 
 * @author william.liangf
 */
public class HelloServiceImpl implements HelloService {

    public String hello(String name) {
        return "Hello " + name;
    }

}

Выставлять сервис

/*
 * Copyright 2011 Alibaba.com All right reserved. This software is the
 * confidential and proprietary information of Alibaba.com ("Confidential
 * Information"). You shall not disclose such Confidential Information and shall
 * use it only in accordance with the terms of the license agreement you entered
 * into with Alibaba.com.
 */
package com.alibaba.study.rpc.test;

import com.alibaba.study.rpc.framework.RpcFramework;

/**
 * RpcProvider
 * 
 * @author william.liangf
 */
public class RpcProvider {

    public static void main(String[] args) throws Exception {
        HelloService service = new HelloServiceImpl();
        RpcFramework.export(service, 1234);
    }

}

Служба цитирования

/*
 * Copyright 2011 Alibaba.com All right reserved. This software is the
 * confidential and proprietary information of Alibaba.com ("Confidential
 * Information"). You shall not disclose such Confidential Information and shall
 * use it only in accordance with the terms of the license agreement you entered
 * into with Alibaba.com.
 */
package com.alibaba.study.rpc.test;

import com.alibaba.study.rpc.framework.RpcFramework;

/**
 * RpcConsumer
 * 
 * @author william.liangf
 */
public class RpcConsumer {
    
    public static void main(String[] args) throws Exception {
        HelloService service = RpcFramework.refer(HelloService.class, "127.0.0.1", 1234);
        for (int i = 0; i < Integer.MAX_VALUE; i ++) {
            String hello = service.hello("World" + i);
            System.out.println(hello);
            Thread.sleep(1000);
        }
    }
    
}

резюме

Блог Лян Фейды использует нативный API-интерфейс jdk, чтобы показать читателям яркую демонстрацию rpc, которая действительно мощная.

Идея реализации этого простого примера такова:

  • использовать блокировкупоток ввода-вывода сокетаДля осуществления связи между сервером и клиентом, то есть поставщиком услуг и потребителем услуг в приложении rpc. И это сквозное использование номеров портов для прямой связи.
  • Удаленный вызов метода используетДинамический прокси для jdk
  • Параметр сериализации также является самым простым в использовании.objectStream

сервисная структура

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

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

  • Invoker — вызывающий абонент службы

  • Провайдер — поставщик услуги

  • Реестр - это реестр для услуг

  • Монитор - модуль мониторинга службы

Понятно, что Invoker и Provider являются вызывающим и вызываемым сервисом соответственно.

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

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

основной модуль

На следующем рисунке показана блок-схема структуры службы Мы разделяем процесс на три аспекта: регистрация службы, обнаружение и вызов.

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

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

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

В соответствии с приведенной выше блок-схемой мы анализируем в соответствии с конкретным процессом запроса.

Конкретный процесс в качестве вызывающей службы Invoker:

  1. Запрос снизу вверх, поскольку вызывающая сторона может получить только интерфейс API или JAR-пакет интерфейса API, предоставленный поставщиком службы, вызывающая сторона должна пройти через уровень прокси, чтобы замаскировать реализацию службы;
  2. После прохождения прокси-сервера он пройдет через маршрутизатор маршрутизации и модуль балансировки нагрузки LoadBalance, целью которого является выбор наиболее подходящей машины поставщика услуг из множества информации о поставщике услуг, полученной из реестра, для вызова. Кроме того, он также будет проходить через такие модули, как мониторинг монитора;
  3. Затем он пройдет через модуль кодека службы кодирования.Целью этого модуля является кодирование и декодирование переданного запроса в соответствии с протоколом связи и методом сериализации объекта перед передачей по сети;
  4. Наконец, он пройдет через сетевой коммуникационный модуль Transporter, который передаст кодек-закодированный запрос.

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

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

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

  3. Затем он пройдет через такие модули, как мониторинг, ограничение тока и аутентификация.

  4. Наконец, будет выполнена реальная бизнес-реализация сервиса ServiceImpl, после выполнения результат будет возвращен таким же образом.

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

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

Регистрационный центр

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

Реестры с открытым исходным кодом, которые в настоящее время используются больше:Zookeeper,ETCD,EurekaЖдать.

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

Согласно теории CAP, эти две системы относятся к системе CP, и доступность будет несколько хуже, однако, как малый и средний сервисный реестр, они все еще способны делать это хорошо, и они не так плохи. как говорят некоторые люди. Eureka является частью набора микросервисов Spring Cloud Netflix, к сожалению, работа с открытым исходным кодом Eureka 2.0 прекращена.

Телекоммуникации

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

сервисный код

Прежде чем объект памяти нужно будет передать по сети, нужно сделать две вещи: первая — определить протокол связи, а вторая — сериализовать.

письмо-соглашение

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

Некоторые учащиеся могут не понять, зачем нужны протоколы связи, разве не существуют протоколы TCP и UDP? Это не протокол связи транспортного уровня, это должен быть протокол прикладного уровня, аналогичный HTTP.

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

Для реализации протокола, как правило, крупные производители или сервисные платформы с открытым исходным кодом предпочитают создавать свои собственные протоколы, которые больше ориентированы на сферу услуг. Конечно, такие как Dubbo, некоторые фреймворки напрямую используют HTTP, HTTP/2, например, GRPC использует HTTP/2.

Сериализация

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

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

Существует множество реализаций сериализации, таких как Hessian, JSON, Thrift, ProtoBuf и т. д. Thrift и ProtoBuf могут поддерживать кросс-языковость и имеют лучшую производительность, но при их использовании возникают проблемы с записью IDL-файлов. Hessian и JSON более удобны в использовании, но производительность будет немного хуже.


сервисная маршрутизация

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

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

Существуют также фреймворки, поддерживающие конфигурацию сценариев для стратегий направленной маршрутизации.


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

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

Существует множество алгоритмов балансировки нагрузки, таких как RoundRobin, Random, LeastActive, ConsistentHash и так далее.

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


аутентификация службы

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

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

Реализация аутентификации службы в основном представляет собой схемы аутентификации на основе токенов, такие как аутентификация JWT (JSON Web Token).


Гарантия доступности

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

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

понижение уровня обслуживания

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

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

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

Лимит сервисного тока

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

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

Изоляция службы

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


сервисный мониторинг

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

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

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

Metrics

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

В настоящее время мониторинг метрик с открытым исходным кодом включает Cat Meituan-Dianping, Prometheus SoundCloud и SkyWalking на основе OpenTracking.

Trace

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

Принципы распределенной трассировки ссылок в основном основаны на статье Google Dapper, крупномасштабной инфраструктуре трассировки распределенных систем. Распределенные системы отслеживания ссылок с открытым исходным кодом включают Cat Meituan-Dianping, SkyWalking на основе OpenTracking и ZipKin Twitter.


Центр конфигурации

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

Центры конфигурации с открытым исходным кодом включают Ali's Diamond и Ctrip's Apollo.


Платформа управления

Платформа управления относится к платформе, которая управляет службами.

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

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

разное

На этом подробное объяснение реализации принципа RPC заканчивается.

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

Уведомление об авторских правах:

Автор: Му Шувэй

Источник блога сада:блог woo woo woo.cn на.com/Sanshenghu…

источник на гитхабе:GitHub.com/Саншэншу…

Источник личного блога:sanshengshui.github.io/