OCTO 是美团千亿调用量的分布式服务通信框架及服务治理的系统,可实现服务注册、服务自动发现、服务管理、容错处理、数据可视化、服务监控报警、服务分组等。本文总结了 OCTO 架构原理、Java 应用的集成方法、以其控制台的使用。
OCTO 是 octopus(章鱼) 的缩写。是美团公司级基础设施,为公司所有业务提供统一的高性能服务通信框架,使业务具备良好的服务运营能力,轻松实现服务注册、服务自动发现、负载均衡、容错、灰度发布、数据可视化、监控告警等功能,提升服务开放效率、可用性及服务运维效率。
【特别说明】OCTO 是美团内部专用系统,未开源,外部无法搭建该系统。 【本文意义】OCTO 是国内重量级的服务治理系统,目前调用量上千亿,通过对该系统的架构原理和使用方法的梳理,可帮助我们深化对分布式服务的认识。
服务提供者的环境分线上(IDC)和线下(办公云)两套系统,线下系统是对线上系统的模拟。 每套系统中都有 test/staging/prod 三个环境。
以 Nginx 为参照来理解。 对于传统的配置方式,域名与物理服务器的映射关系由 Ngnix 维护,物理服务器的增减需要运维人员调整,无法动态完成:
对于 appkey 的配置方式,新增了 appkey 一层:
Thrift 同理,对于客户端请求(appkey:port),Thrift Server 通过 appkey 找到物理服务器(IP:port)。
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 提供服务模板管理, 代码生成引擎等高效工具.
HLB 是 Hardware Load Balance 的缩写。 所有HTTP请求/应答流量都会穿过这个系统,类似amazon elb。
SG 是 Service Governance 的缩写。 SG_agent 部署在各服务节点(服务的提供者和消费者),通过与MNS进行通信,提供服务注册/发现、配置更新、访问控制、配额限制等功能,并将调用统计上报给性能监控平台。
MNS 是 Meituan Naming Service 的缩写。 MNS 是服务注册路由中心,基于 ZooKeeper 构建,为公司各类分布式服务提供稳健可靠的命名服务管理组件, 快速实现服务注册、路由、服务自动发现。
主要提供服务概要、节点IP/Port、节点权重、配额等信息的存储/访问,及服务健康状态检测等。
收集公司所有接入OCTO业务的上报日志数据,为各业务线提供系统的性能指标、健康状况、基础告警等
扫描各服务的健康状况,不可用时从 MNS 中剔除。
MCC 是 Meituan Config Center 的缩写。 统一配置中心,提供统一配置管理服务, 实现配置与代码分离、配置信息实时更新、高可用性、版本控制, 提高服务开发效率,降低运维成本。 其原理是将 JSON 格式的配置文件存储在 ZooKeeper 目录下,当用户在 MSGP 更改配置时,由 MSGP 通知 SG_agent 进行数据拉取,将zk中的配置数据落地到本机的指定目录中。
MSGP 是 Meituan Service Governance PlatForm 的缩写。 目标:为公司各类服务提供注册、治理、诊断,配置,配额等功能的一站式管理平台。
使用thrift 提供的@ThriftService、@ThriftMethod、@ThriftStruct、@ThriftField等注解,注解于普通的Java类,使其成为thrift的数据模型(model)和服务接口(service)。其使用模式与 Dubbo 非常相似:服务的提供者和消费者基于共同的一套接口定义。
下面基于 SpringBoot 创建
interface/provider/consumer 三个模块。 在本机上运行 provider,即可成功注册至 dev 环境的 OCTO 上。 在本机上运行 consumer,即可成功消费 provider 提供的服务。 本例已在美团内网跑通。
引入依赖包,并定义接口 DemoThriftService。 其中用到的参数 StudentParam / GenderEnum 必须用相关注解标注。
- <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>
- /**
- * @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;
- }
- }
- /**
- * @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;
- }
- }
- /**
- * @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;
- }
引入依赖包:service-interface 是刚定义的公用接口,hystrix 用于容错。 本模块中,先是实现公用接口,再定义发布相关配置,再运行
ServiceProviderApplication 启动服务提供者。
- <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>
- /**
- * @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, "张三");
- }
- }
- /**
- * @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;
- }
- }
- /**
- * @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);
- }
- }
引入依赖包:service-interface 是刚定义的公用接口。 本模块中,先是指定服务提供者、消费选项,再使用共用接口定义 Controller 来调用,再运行
ServiceConsumerApplication 启动服务消费者。 启动浏览器访问
http://localhost:8080/demo,即可调用成功。
- <dependency>
- <groupId>com.meituan</groupId>
- <artifactId>service-interface</artifactId>
- <version>1.0.0</version>
- </dependency>
- /**
- * @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;
- }
- }
- /**
- * @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;
- }
- }
- /**
- * @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);
- }
- }
MtThrift是在 Thrift 上做了定制化修改,使得能代码在 Plus 发布后能够被 OCTO 平台发现处理。
test / staging / prod 各环境都有相应的WEB管理平台(公司内部或者通过VPN才能访问)。
出于信息安全考虑,相关截图不一一提供了。常用功能有:
出处:
https://kefeng.wang/2018/06/29/distributed-octo/