當前位置:網站首頁>OCTO作為美團的高性能服務通信框架,究竟能不能稱得上是殺手鐧呢?

OCTO作為美團的高性能服務通信框架,究竟能不能稱得上是殺手鐧呢?

2022-05-14 22:18:42Java愛好狂

OCTO 是美團千億調用量的分布式服務通信框架及服務治理的系統,可實現服務注册、服務自動發現、服務管理、容錯處理、數據可視化、服務監控報警、服務分組等。本文總結了 OCTO 架構原理、Java 應用的集成方法、以其控制臺的使用。

1 概述

OCTO 是 octopus(章魚) 的縮寫。是美團公司級基礎設施,為公司所有業務提供統一的高性能服務通信框架,使業務具備良好的服務運營能力,輕松實現服務注册、服務自動發現、負載均衡、容錯、灰度發布、數據可視化、監控告警等功能,提昇服務開放效率、可用性及服務運維效率。

【特別說明】OCTO 是美團內部專用系統,未開源,外部無法搭建該系統。 【本文意義】OCTO 是國內重量級的服務治理系統,目前調用量上千億,通過對該系統的架構原理和使用方法的梳理,可幫助我們深化對分布式服務的認識。

1.1 在美團技術架構中的比特置

1.2 功能特性

  • 命名服務 - 服務注册;服務自動發現
  • 服務管理 - 服務狀態監測;服務啟動、停止;服務負載均衡
  • 容錯處理 - 實時屏蔽异常的服務,自動調配請求流量
  • 流量分發 - 灰度發布、節點動態流量分配等場景
  • 數據可視化 - 服務調用統計上報分析,提供清晰的數據圖錶展示,清晰了解服務間依賴關系
  • 服務分組 - 支持服務動態自動歸組與不同場景下的自定義分組,解决在多機房場景下跨機房調用穿透、Sandbox等問題
  • 服務監控報警 - 支持服務與接口級別多指標、多維度的監控,支持多種報警方式
  • 統一配置管理 - 支持服務配置統一管理,靈活設置不同環境間差异,支持曆史版本,配置項變更後實時下發
  • 分布式服務跟踪 - 輕松診斷服務訪問慢、异常抖動等問題
  • 過載保護 - 靈活定義服務消費者的配額,當其調用量超出最大閥值時,基於不同服務消費者進行QoS區分,觸發過載保護。
  • 服務訪問控制

1.3 環境劃分

服務提供者的環境分線上(IDC)和線下(辦公雲)兩套系統,線下系統是對線上系統的模擬。 每套系統中都有 test/staging/prod 三個環境。

1.4 調用流程

  • 各方(provider/consumer)在 OCTO 上注册自己專用的 appkey,比如 appkey-provider/appkey-consumer
  • provider 在 OCTO 上注册服務(標記為 appkey-provider),同一個 appkey 在三個環境中都有部署;
  • 假設在 staging 環境 的 consumer 在 OCTO 上請求服務(標記自己 appkey-consumer,目標 appkey-provider)
  • OCTO 查詢 staging 環境的 appkey-provider 的服務列錶,並發送給 consumer
  • consumer 通過 mtthrift 訪問 IP:PORT 服務

1.5 理解 appkey

以 Nginx 為參照來理解。 對於傳統的配置方式,域名與物理服務器的映射關系由 Ngnix 維護,物理服務器的增减需要運維人員調整,無法動態完成:

對於 appkey 的配置方式,新增了 appkey 一層:

  • 域名與 appkey 的映射關系由 Nginx 配置,以後無需調整;
  • 而 appkey 與物理服務器的映射關系,可以動態調整。

Thrift 同理,對於客戶端請求(appkey:port),Thrift Server 通過 appkey 找到物理服務器(IP:port)。

2 整體架構

2.1 MTransport(服務通信框架)

  MTthrift 是基於 Thrift(由 Facebook 來源為 Apache Thrift )二次開發,是一個分布式服務通訊框架,致力於提供高性能和透明化的RPC遠程服務調用方案,是 OCTO 服務治理方案的核心框架,每天為4000+服務提供2000億+次訪問量支持,被廣泛應用於新美大各個業務線。

  MTransport 是多語言的服務通信框架,它屏蔽了底層高性能網絡通信的實現細節, 從而實現簡單高效的服務開發。MTransport 支持 Thrift/HTTP/pigeon 等協議。其中 Thrift 包括 MTthrift(Java)、PThrift(PHP)、CThrift(C/C++)、Turbo Thrift(NodeJS)等,Thrift 支持不同語言版本的代碼實現, 保持通信協議的一致性,支持服務注册、服務自動發現、分布式服務調用跟踪等。HTTP 目前也支持JAVA、NodeJs以及C++。

  MTthrift 提供服務模板管理, 代碼生成引擎等高效工具.

2.2 HLB(彈性負載均衡器)

HLB 是 Hardware Load Balance 的縮寫。 所有HTTP請求/應答流量都會穿過這個系統,類似amazon elb。

2.3 SG_agent(服務治理代理)

SG 是 Service Governance 的縮寫。 SG_agent 部署在各服務節點(服務的提供者和消費者),通過與MNS進行通信,提供服務注册/發現、配置更新、訪問控制、配額限制等功能,並將調用統計上報給性能監控平臺。

2.4 MNS(美團命名服務)

MNS 是 Meituan Naming Service 的縮寫。 MNS 是服務注册路由中心,基於 ZooKeeper 構建,為公司各類分布式服務提供穩健可靠的命名服務管理組件, 快速實現服務注册、路由、服務自動發現。

主要提供服務概要、節點IP/Port、節點權重、配額等信息的存儲/訪問,及服務健康狀態檢測等。

  1. 可靠性: (1)一致性:不論連接到集群的哪一臺服務器節點,展示的都是一致的數據視圖。 (2)原子性:節點的更新要麼成功,要麼失敗。 (3)高可用性:在2n+1臺機器組成的集群中,即使n臺機器失敗,仍不影響集群的高可用性。
  2. 去中心化:中心的MNS主要提供服務注册、發現、路由策略等功能,其他服務主要由在各服務節點的SG_agent提供。

2.5 Data-center(服務數據中心)

收集公司所有接入OCTO業務的上報日志數據,為各業務線提供系統的性能指標、健康狀况、基礎告警等

2.6 Scanner(健康檢查系統)

掃描各服務的健康狀况,不可用時從 MNS 中剔除。

2.7 MCC(美團配置中心)

MCC 是 Meituan Config Center 的縮寫。 統一配置中心,提供統一配置管理服務, 實現配置與代碼分離、配置信息實時更新、高可用性、版本控制, 提高服務開發效率,降低運維成本。 其原理是將 JSON 格式的配置文件存儲在 ZooKeeper 目錄下,當用戶在 MSGP 更改配置時,由 MSGP 通知 SG_agent 進行數據拉取,將zk中的配置數據落地到本機的指定目錄中。

2.8 MSGP(美團服務治理平臺)

MSGP 是 Meituan Service Governance PlatForm 的縮寫。 目標:為公司各類服務提供注册、治理、診斷,配置,配額等功能的一站式管理平臺。

3 接入方法

使用thrift 提供的@ThriftService、@ThriftMethod、@ThriftStruct、@ThriftField等注解,注解於普通的Java類,使其成為thrift的數據模型(model)和服務接口(service)。其使用模式與 Dubbo 非常相似:服務的提供者和消費者基於共同的一套接口定義。

下面基於 SpringBoot 創建
interface/provider/consumer 三個模塊。 在本機上運行 provider,即可成功注册至 dev 環境的 OCTO 上。 在本機上運行 consumer,即可成功消費 provider 提供的服務。 本例已在美團內網跑通。

3.1 公用接口 service-interface

引入依賴包,並定義接口 DemoThriftService。 其中用到的參數 StudentParam / GenderEnum 必須用相關注解標注。

3.1.1 pom.xml

<dependency>
    <groupId>com.meituan.service.mobile</groupId>
    <artifactId>mtthrift</artifactId>
    <version>1.8.5</version>
</dependency>
<dependency>
    <groupId>com.meituan.mtrace</groupId>
    <artifactId>mtrace</artifactId>
    <version>1.1.14</version>
</dependency>

3.1.2 StudentParam.java

/**
 * @author: kefeng.wang
 * @date: 2018-06-29 16:34
 * @description: 學生定義(作為輸入參數)
 **/
@ThriftStruct
public class StudentParam {
    private Integer id;
    private String name;

    @ThriftConstructor
    public StudentParam(Integer id, String name) {
        this.id = id;
        this.name = name;
    }

    @ThriftField
    public Integer getId() {
        return id;
    }

    @ThriftField(1)
    public void setId(Integer id) {
        this.id = id;
    }

    @ThriftField
    public String getName() {
        return name;
    }

    @ThriftField(2)
    public void setName(String name) {
        this.name = name;
    }
}

3.1.3 GenderEnum.java

/**
 * @author: kefeng.wang
 * @date: 2018-06-29 16:41
 * @description: 性別定義(作為輸出參數)
 **/
@ThriftEnum
public enum GenderEnum {
    GENDER_MALE(1, "male", "男性"),
    GENDER_FEMALE(2, "female", "女性"),
    GENDER_UNKNOWN(0, "unknown", "未知性別");

    private Integer id;
    private String value;
    private String desc;

    GenderEnum(Integer id, String value, String desc) {
        this.id = id;
        this.value = value;
        this.desc = desc;
    }

    // @ThriftEnumValue
    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getValue() {
        return value;
    }

    public void setValue(String value) {
        this.value = value;
    }

    public String getDesc() {
        return desc;
    }

    public void setDesc(String desc) {
        this.desc = desc;
    }
}

3.1.4 DemoThriftService.java

/**
 * @author: kefeng.wang
 * @date: 2018-06-29 16:19
 * @description: Thrift 接口定義
 **/
@ThriftService
public interface DemoThriftService {
    @ThriftMethod
    String getVersion() throws TException;

    @ThriftMethod
    StudentParam getGenderStudent(GenderEnum gender) throws TException;
}

3.2 服務提供者 service-provider

引入依賴包:service-interface 是剛定義的公用接口,hystrix 用於容錯。 本模塊中,先是實現公用接口,再定義發布相關配置,再運行
ServiceProviderApplication 啟動服務提供者。

3.2.1 pom.xml

<dependency>
    <groupId>com.meituan</groupId>
    <artifactId>service-interface</artifactId>
    <version>1.0.0</version>
</dependency>
<dependency>
    <groupId>com.netflix.hystrix</groupId>
    <artifactId>hystrix-javanica</artifactId>
    <version>1.5.12</version>
</dependency>

3.2.2 DemoThriftServiceImpl.java

/**
 * @author: kefeng.wang
 * @date: 2018-06-29 17:29
 * @description: Thrift 接口實現(服務提供者)
 **/
public class DemoThriftServiceImpl implements DemoThriftService {
    @Override
    public String getVersion() throws TException {
        return "1.0.0";
    }

    @Override
    @HystrixCommand
    public StudentParam getGenderStudent(GenderEnum gender) throws TException {
        return new StudentParam(1, "張三");
    }
}

3.2.3 DemoServiceProviderConfig.java

/**
 * @author: kefeng.wang
 * @date: 2018-06-29 17:45
 * @description: Thrift 發布(服務提供者)
 **/
@Configuration
public class DemoServiceProviderConfig {
    @Resource(name = "serviceProcessor")
    private DemoThriftService serviceProcessor;

    @Bean(name = "serviceProcessor")
    public DemoThriftService getDemoThriftService() {
        return new DemoThriftServiceImpl();
    }

    @Bean(name = "serverPublisher", initMethod = "publish", destroyMethod = "destroy")
    public ThriftServerPublisher getThriftServerPublisher() {
        ThriftServerPublisher serverPublisher = new ThriftServerPublisher();
        serverPublisher.setServiceInterface(DemoThriftService.class); // [MUST] 接口類
        serverPublisher.setServiceImpl(serviceProcessor); // [MUST] 實現類
        serverPublisher.setAppKey(APPKEY_TEST_SERVER); // [MUST] 服務提供者 appkey
        serverPublisher.setPort(9001); // [MUST] 服務提供者監聽端口
        return serverPublisher;
    }
}

3.2.4 ServiceProviderApplication.java

/**
 * @author: kefeng.wang
 * @date: 2018-06-29 17:50
 * @description: 啟動(服務提供者)
 **/
@SpringBootApplication
public class ServiceProviderApplication {
    public static void main(String[] args) {
        SpringApplication.run(ServiceProviderApplication.class, args);
    }
}

3.3 服務消費者 service-consumer

引入依賴包:service-interface 是剛定義的公用接口。 本模塊中,先是指定服務提供者、消費選項,再使用共用接口定義 Controller 來調用,再運行
ServiceConsumerApplication 啟動服務消費者。 啟動瀏覽器訪問
http://localhost:8080/demo,即可調用成功。

3.3.1 pom.xml

<dependency>
    <groupId>com.meituan</groupId>
    <artifactId>service-interface</artifactId>
    <version>1.0.0</version>
</dependency>

3.3.2 DemoServiceConsumerConfig.java

/**
 * @author: kefeng.wang
 * @date: 2018-06-29 18:01
 * @description: Thrift 消費者
 **/
@Configuration
public class DemoServiceConsumerConfig {
    @Bean(name = "thriftPoolConfig")
    public MTThriftPoolConfig getMTThriftPoolConfig() {
        MTThriftPoolConfig thriftPoolConfig = new MTThriftPoolConfig();
        thriftPoolConfig.setMaxActive(100);
        thriftPoolConfig.setMaxIdle(20);
        thriftPoolConfig.setMinIdle(5);
        thriftPoolConfig.setMaxWait(3000);
        thriftPoolConfig.setTestOnBorrow(true);
        thriftPoolConfig.setTestOnReturn(false);
        thriftPoolConfig.setTestWhileIdle(false);
        return thriftPoolConfig;
    }

    @Bean(name = "demoThriftService", destroyMethod = "destroy")
    public ThriftClientProxy getThriftClientProxy(MTThriftPoolConfig thriftPoolConfig) {
        ThriftClientProxy thriftClientProxy = new ThriftClientProxy();
        thriftClientProxy.setMtThriftPoolConfig(thriftPoolConfig); // [可選]配置
        thriftClientProxy.setServiceInterface(DemoThriftService.class); // [MUST]接口類
        thriftClientProxy.setAppKey(APPKEY_TEST_CLIENT); // [MUST]服務消費者 appkey
        thriftClientProxy.setRemoteAppkey(APPKEY_TEST_SERVER); // [MUST]服務提供者 appkey
        thriftClientProxy.setRemoteServerPort(9001); // [常用]服務提供者 port
        thriftClientProxy.setTimeout(30000); // [常用]調用超時
        return thriftClientProxy;
    }
}

3.3.3 DemoConsumerController.java

/**
 * @author: kefeng.wang
 * @date: 2018-06-29 18:10
 * @description: Thrift 演示的外部入口
 **/
@RestController
public class DemoConsumerController {
    private static final Logger logger = LoggerFactory.getLogger(DemoConsumerController.class);

    @Resource
    private DemoThriftService demoThriftService;

    @GetMapping("/demo")
    public StudentParam demo() {
        try {
            return demoThriftService.getGenderStudent(GenderEnum.GENDER_MALE);
        } catch (TException e) {
            logger.warn(e.getMessage(), e);
        }

        return null;
    }
}

3.3.4 ServiceConsumerApplication.java

/**
 * @author: kefeng.wang
 * @date: 2018-06-29 17:50
 * @description: 啟動(服務消費者)
 **/
@SpringBootApplication
public class ServiceConsumerApplication {
    public static void main(String[] args) {
        SpringApplication.run(ServiceConsumerApplication.class, args);
    }
}

4 發布平臺(plus)

MtThrift是在 Thrift 上做了定制化修改,使得能代碼在 Plus 發布後能够被 OCTO 平臺發現處理。

5 服務治理平臺(MSGP)

test / staging / prod 各環境都有相應的WEB管理平臺(公司內部或者通過VPN才能訪問)。

出於信息安全考慮,相關截圖不一一提供了。常用功能有:

  • 服務詳情 / 服務提供者:列出當前 appkey 的各主機(主機名/IP/PORT),可新增或删除主機,可調整權重,可啟用或禁用;
  • 服務詳情 / 服務消費者:分時段查看當前 appkey 的消費者及其消費的提供者主機、調用量等;
  • 服務運營 / 服務分組:可設置同中心優先、同機房優先;
  • 數據分析 / 來源分析:按時段統計當前 appkey 上遊服務的調用(調用量、QPS、耗時等);
  • 數據分析 / 去向分析:按時段統計當前 appkey 下遊服務的調用(調用量、QPS、耗時等);
  • 數據分析 / 主機分析:按時段統計當前 appkey 各主機的被調用情况(調用量、QPS、耗時等)。

 出處:
https://kefeng.wang/2018/06/29/distributed-octo/

版權聲明
本文為[Java愛好狂]所創,轉載請帶上原文鏈接,感謝
https://cht.chowdera.com/2022/134/202205141820530577.html

隨機推薦