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

12345678910
<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

123456789101112131415161718192021222324252627282930313233343536
/** * @author: kefeng.wang * @date: 2018-06-29 16:34 * @description: 学生定义(作为输入参数) **/@ThriftStructpublic 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

12345678910111213141516171819202122232425262728293031323334353637383940414243444546
/** * @author: kefeng.wang * @date: 2018-06-29 16:41 * @description: 性别定义(作为输出参数) **/@ThriftEnumpublic 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

12345678910111213
/** * @author: kefeng.wang * @date: 2018-06-29 16:19 * @description: Thrift 接口定义 **/@ThriftServicepublic 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

12345678910
<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

1234567891011121314151617
/** * @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

12345678910111213141516171819202122232425
/** * @author: kefeng.wang * @date: 2018-06-29 17:45 * @description: Thrift 发布(服务提供者) **/@Configurationpublic 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

1234567891011
/** * @author: kefeng.wang * @date: 2018-06-29 17:50 * @description: 启动(服务提供者) **/@SpringBootApplicationpublic 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

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

3.3.2 DemoServiceConsumerConfig.java

1234567891011121314151617181920212223242526272829303132
/** * @author: kefeng.wang * @date: 2018-06-29 18:01 * @description: Thrift 消费者 **/@Configurationpublic 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

1234567891011121314151617181920212223
/** * @author: kefeng.wang * @date: 2018-06-29 18:10 * @description: Thrift 演示的外部入口 **/@RestControllerpublic 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

1234567891011
/** * @author: kefeng.wang * @date: 2018-06-29 17:50 * @description: 启动(服务消费者) **/@SpringBootApplicationpublic 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、耗时等)。

美团 OCTO 分布式服务治理系统的更多相关文章

  1. 美团分布式服务通信框架及服务治理系统OCTO

     一.什么是OCTO 定义: OCTO是美团的分布式服务通信框架及服务治理系统,属于公司级基础设施,目前尚未开源. 目标: 为公司所有业务提供统一的服务通信框架,使业务具备良好的服务运营能力,轻松实现 ...

  2. Leaf——美团点评分布式ID生成系统 UUID & 类snowflake

    Leaf——美团点评分布式ID生成系统 https://tech.meituan.com/MT_Leaf.html

  3. 分布式服务治理框架Dubbo的前世今生及应用实战

    Dubbo的出现背景 Dubbo从开源到现在,已经出现了接近10年时间,在国内各大企业被广泛应用. 它到底有什么魔力值得大家去追捧呢?本篇文章给大家做一个详细的说明. 大规模服务化对于服务治理的要求 ...

  4. Leaf——美团点评分布式ID生成系统

    背景 在复杂分布式系统中,往往需要对大量的数据和消息进行唯一标识.如在美团点评的金融.支付.餐饮.酒店.猫眼电影等产品的系统中,数据日渐增长,对数据分库分表后需要有一个唯一ID来标识一条数据或消息,数 ...

  5. 个人学习分布式专题(二)分布式服务治理之Dubbo框架

    目录 Dubbo框架 1.1 Dubbo是什么 1.2 Dubbo企业级应用示例(略) 1.3 Dubbo实现原理及架构剖析 1.4 Dubbo+Spring集成 Dubbo框架 1.1 Dubbo是 ...

  6. 个人学习分布式专题(二)分布式服务治理之分布式协调技术Zookeeper

    分布式协调技术Zookeeper 2.1 zookeeper集群安装部署(略) 2.2 zookeeper的基本原理,数据模型 2.3 zookeeper Java api的使用 2.4 zookee ...

  7. 分布式服务治理框架dubbo

    Dubbo最主要功能有两个 1 RPC调用 2 SOA服务治理方案 Dubbo的架构 Dubbo常见的注册中心有2中,zookeeper以及redis 这篇文章讲解的是采用的zookeeper,要求读 ...

  8. 美团技术分享:深度解密美团的分布式ID生成算法

    本文来自美团技术团队“照东”的分享,原题<Leaf——美团点评分布式ID生成系统>,收录时有勘误.修订并重新排版,感谢原作者的分享. 1.引言 鉴于IM系统中聊天消息ID生成算法和生成策略 ...

  9. 美团关于分布式ID实践方案

    在复杂分布式系统中,往往需要对大量的数据和消息进行唯一标识.如在美团点评的金融.支付.餐饮.酒店.猫眼电影等产品的系统中,数据日渐增长,对数据分库分表后需要有一个唯一ID来标识一条数据或消息,数据库的 ...

随机推荐

  1. Python2.7-robotparser

    robotparser 模块,用于解析网站的 robots.txt 文件,robots.txt 文件是用于指定搜索引擎爬虫的访问权限的,此模块在 python3 中重命名为 urllib.robotp ...

  2. eclipse中查看安装的所有插件,并选择性的将其卸载

    牢骚:前一段时间安装了一个将错误日志回传到邮箱的eclipse插件,安装此插件对捕获debug虽然很方便,但是也带了我不小的困扰,比如:eclipse加载速度慢.打开速度慢,有时还会引起一些异常,这不 ...

  3. cloudstack secondary vm starting

    等1个小时,差不多可以进入虚拟机,看日志/var/log/cloud.log

  4. pv,uv的意义

    PV(page view),即页面浏览量,或点击量;通常是衡量一个网络新闻频道或网站甚至一条网络新闻的主要指标. 高手对pv的解释是,一个访问者在24小时(0点到24点)内到底看了你网站几个页面.这里 ...

  5. Django:admin后台汉化问题

    1.设置admin站点中文显示,即汉化admin后台管理站点. 方法一:修改settings文件 LANGUAGE_CODE = 'en-us' TIME_ZONE = 'UTC' 更改为: LANG ...

  6. Android应用安全之脆弱的加密

    程序员希望通过加密来提升程序的安全性性,但却缺乏专业的密码学背景知识,使得应用对数据的保护非常薄弱.本文将介绍可能出现在Android应用中的一些脆弱的加密方式,以及对应的攻击方法. 造成脆弱加密的主 ...

  7. c# Findwindow sendMessage

    using System; using System.Collections.Generic; using System.Text; using System.Runtime.InteropServi ...

  8. mfc 类模板

    类模板 创建类模板 添加成员变量 添加成员函数 定义类模板对象 一.创建类模板 template <class T,class T2> template <class T> 二 ...

  9. Java中的Calendar日历用法详解

    第一部分 Calendar介绍 public abstract class Calendar implements Serializable, Cloneable, Comparable<Cal ...

  10. Centos 7 部署Kubernetes(K8S)集群

    资源链接:https://pan.baidu.com/s/1-PT_QQAf7cTu_znX-S-r9Q 密码:33sr 转发:http://blog.51cto.com/lizhenliang/19 ...