服务治理(LOOM)

2017-01-06  本文已影响2334人  Kungfu猫熊

概述

在介绍LOOM之前, 说一下我对一个互联网公司多语言开发环境的感受. 现在互联网高速发展的时代,产品快速迭代会帮助我们快速产出产品原型,快速获取用户反馈,快速抢占市场. 我们需要使用适合的语言去做一些特定领域的事情. 在小型开发团队中,大概10人左右的开放团队中, 开发语言的选型不用过多的考虑管理成本,只要对产品的快速迭代有帮助的都可以使用.随着团队逐渐壮大, 我们需要更多的考虑不同开发团队之间的沟通成本, 招聘新人的培训成本, 由人员更替产生的交接成本等. 这个时候多语言开发环境带来的弊端逐渐显现出来, 我们需要在这中间找到一个平衡点.

在公司发展到一定规模, 肯定会产生很多业务系统, 这些业务系统需要互相之间进行调用来完成协作. Thrift帮助我们在异构语言直接的沟通建立了桥梁. 只是简单的RPC调用在服务比较少的阶段还能够接受,但是随着服务逐渐的增多,调用关系越来越复杂, 我们迫切的需要一个框架可以对所有服务进行统一的管理,监控,预警. 这是LOOM诞生的直接需求. “LOOM,织布机”,寓意可以将各个网状服务进行很好的治理.

LOOM的大部分需求直接采用的阿里巴巴的服务治理框架—DUBBO, 这里向DUBBO的所有开发人员致敬, DUBBO的出现带动了SOA概念真正的落地, 相信国内很多互联网公司都在用DUBBO进行服务治理. LOOM里面关于RPC相关的工作,我们直接使用了Twitter的finagle-thrift, 它可以帮助我们非常简单的写出高性能的异步的thrift调用. 另外我们可以直接通过zipkin来进行异构语言之间的调用链信息的收集.

loom服务治理需求

一期需求

在大规模服务化之前,应用可能只是通过Thrift,简单的暴露和引用远程服务,通过配置服务的URL地址进行调用,通过HAProxy等软件进行负载均衡。服务治理平台主要的需求可以通过下面几点来体现:

二期需求

一期项目中, 我们完成了服务提供者, 消费者从启动开始总调用量的统计. 其实我们可以收集更详细的信息, 可以细化到哪个应用中的哪个方法的什么时候被调用,调用时长是多少.通过这些信息,我们可以通过实时计算出某台机器的某个方法响应时间变慢了等操作.这些操作会分几个部分:

注册中心Zookeeper数据结构

zookeeper数据结构

loom核心功能

服务注册

服务启动时只需要设置启动端口, zookeeper地址, 即可通过loom将服务资源信息注册到zookeeper中, 相同服务的提供者统一注册到

/loom/serviceName/providers

节点下, 注册节点必须为动态临时节点.

负载均衡

/loom/serviceName/consumers

节点下, 注册节点为动态临时节点.

服务治理

loom 测试用例

功能测试

性能测试(注: 未经说明的都是基于scala-2.9.2版本进行的压测)

zipkin内存溢出 压测调用关系图 压测调用关系图

对象回收正常, 老年代执行了唯一一次回收, 还是我手动进行的GC.
下图是loadrunner压测8小时统计图


压测调用关系图
下图是loadrunner压测14小时统计图
压测调用关系图

下两图为loadrunner压测48小时统计图


压测调用关系图
压测调用关系图
结果: 在每秒1000访问量的压力下 48小时连续测试, scala-2.10版的loom表现正常, 响应时间, 内存回收都表现正常.

loom接入说明

前提:服务提供者和服务消费着都必须是基于finagle生成的代码,并且依赖finagle和thrift相关的包。

<?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xmlns:context="http://www.springframework.org/schema/context"
      xmlns:loom="http://xxx.com/schema/loom"
      default-autowire="byName" default-lazy-init="true"
      xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
                          http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd
                          http://xxx.com/schema/loom http://xxx.com/schema/loom/loom.xsd">

     <!--id是此应用的应用名称,请修改为自己系统的名称, 可以将该应用的负责人填写在owner属性中-->
     <loom:application id="XXXXXXXXX" owner="xxxx" />

     <!-- 如果配置该节点则是开启了简单的统计功能,可以统计某一个服务提供者和消费者的每分钟成功调用测试, 失败的调用次数,建议此功能打开 -->
     <loom:count id="count" ip="#{configManager.getRedisConfig('trade_public_redis','14.1').ip}"
                     port="#{configManager.getRedisConfig('trade_public_redis','14.1').port}"
                     pwd="#{configManager.getRedisConfig('trade_public_redis','14.1').pwd}"/>


      <!-- 如果配置该节点则是开启了zipkin监控功能,默认是注视掉的,如果需要请打开,采样率默认是0.01  -->
      <!-- <loom:monitor id="monitor01" url="#{configManager.getConfigValue('trade_public_zipkin','zipkinUrl')}"
                        port="#{configManager.getConfigValue('trade_public_zipkin','zipkinPort')}"
                        samplerate="0.01"/> -->

       <!--zookeeper集群地址, 如果address为空则默认不实例化该注册中心,用以测试环境脱离注册中心时使用,必须配置-->
       <loom:registry id="zk01" protocol="zookeeper"
                     address="#{configManager.getConfigValue('trade_public_zookeeper','zk01')}"
                     timeout="20000"/> 

      <!-- 如果想作为一个task不提供服务, 但是需要注册到zookeeper中, 则可以是不配置ref和api,端口,threads, 建议port设为0000, 这时服务名一定不能是其他真实服务的名称,可以有一个约定标识, 比如:xxx-task --> 
      <!--服务提供者配置, 如果上面的registry中的address设置为空,则该服务将脱离注册中心,不会注册到注册中心,可以直接通过端口对外提供服务, -->
      <!-- threads 建议是cpu核数的四倍, 服务端提供服务时,服务超时时间, 默认30秒  --> 
      <loom:service id="XXXXX"
                    ref=“xXXImpl"
                    api="com.xxx.finagle.thrift.XXXX.XXXServ"
                    port="12301"
                    threads="32"
                    version="1.0"
                    owner="XXXX"
                    registry="zk01"
                    timeout="30000"
                    remark="这里填写对该服务的描述"/>

      <!--消费其他服务配置,这里surl为具体的服务地址(比如: finagle://192.168.10.5:12306),如果填写具体地址则会脱离注册中心, 该场景是为了简化测试时脱离注册中心 -->
      <loom:reference id="xxxxReference" sid="XXXXXXX"
                      api="com.xxx.finagle.thrift.XXXX.XXXServ" version="1.0"
                      registry="zk01" surl="" timeout="30000"/>
    </beans>

在资源文件夹下添加loomContext.xml并在启动时加载,文件内容如上
添加此配置后工程启动时会自动将服务注册到注册中心,并且自动启动相关服务,如果原来代码中有启动服务的,请关闭。

name值必须是 loom:reference节点的id。尖括号里面是要调用的服务类名

使用方法如下:
可以通过orderServReference直接获取thrift接口中的方法.

同步调用:

String res = Await.result(orderServReference.方法名(参数));

同步调用可以设置超时时间:

String res = Await.result(orderServReference.方法名(参数),new Duration(10 * Duration.NanosPerSecond()));//十秒超时

异步调用:
```
orderServReference.方法名(参数).addEventListener(new FutureEventListener<String>() {//这里的泛型为返回值类型
//如果成功,res为该方法的返回值, 可以拿到该值做后续的业务处理
@Override
public void onSuccess(String res) {
}

    //如果失败
    @Override
    public void onFailure(Throwable throwable) {
    }
});
```

loom-admin使用说明

loom-admin项目是loom中的子项目, 用来对注册中心中的服务提供者, 消费者进行管理. 下面是针对该管理中心的核心功能介绍

提供者管理

当某个服务集群启动时, 会将各自服务提供者的地址注册到zookeeper上, 这时我们可以在提供者维度中看到所有服务的提供者.如上图:

(1) 在大促前期, 如果我们需要估算生产服务器的负载容量, 我们可以通过对某一个服务提供者进行倍权操作,并随时观察该服务器的负载情况和服务的提供情况,当出现性能瓶颈时,这时将是该服务提供者的最大容量,我们可以根据该容量反推出我们需要多少服务器来应对大促情况下的高并发请求.

(2) 当发现某一个服务器的负载明显高于其他服务提供者,则可以将该服务提供者进行半权操作, 以此来降低该服务器的服务请求.降低提供服务出错的几率.

(3) 当要对服务进行发版或者发现严重问题时, 需要先在管理界面上将该服务提供者进行禁用, 然后再停止服务, 这样可以避免因为服务已经停止,还有链接访问该服务并造成出错的可能.

(4) 可以直接对某一个字段进行实时检索.

提供者管理

我们可以在消费者维度查看某个服务都被哪些应用消费了,并且可以定位到具体的ip,应用名. 当发现某个服务消费者出现了问题,可以通过管理界面来屏蔽某个具体的消费者.

调用详情入口 方法级别的调用详情 方法耗时总览

loom常见问题汇总

可能存在java中的codec包版本不兼容问题, 解决办法:
在java服务提供方程序中的pom.xml中引入:
<dependency> <groupId>commons-codec</groupId> <artifactId>commons-codec</artifactId> <version>1.8</version> </dependency>

Failed to retry subscribe {admin://192.168.1.100/h5wap-test?category=providers,consumers,routers,configurators&check=false&classifier=*&enabled=*&group=*&interface=h5wap-test&version=*=[com.xxx.loom.admin.model.RegistryServerSync@5fc625c3]}, waiting for again, cause: Failed to subscribe admin://192.168.1.100/h5wap-test?category=providers,consumers,routers,configurators&check=false&classifier=*&enabled=*&group=*&interface=h5wap-test&version=* to zookeeper zookeeper://192.168.10.66:2181?application=loom-admin&backup=192.168.10.57:2181,192.168.10.58:2181&timeout=20000, cause: URLDecoder: Illegal hex characters in escape (%) pattern - For input string: "co" #####[LOOM]#####  - [com.xxx.loom.registry.zookeeper.ZookeeperRegistry]
com.xxx.loom.core.rpc.RpcException: Failed to subscribe admin://192.168.1.100/h5wap-test?category=providers,consumers,routers,configurators&check=false&classifier=*&enabled=*&group=*&interface=h5wap-test&version=* to zookeeper zookeeper://192.168.10.66:2181?application=loom-admin&backup=192.168.10.57:2181,192.168.10.58:2181&timeout=20000, cause: URLDecoder: Illegal hex characters in escape (%) pattern - For input string: "co"

请检查相应服务节点下的所有节点url进行encode时是否正确.

Caused by: org.xml.sax.SAXParseException; lineNumber: 39; columnNumber: 22; cvc-datatype-valid.1.2.1: '#{configManager.getConfigValue('xxx_public_service_name','trade_subject_serv_2.0')}' 不是 'NCName' 的有效值。
at com.sun.org.apache.xerces.internal.util.ErrorHandlerWrapper.createSAXParseException(ErrorHandlerWrapper.java:198)
at com.sun.org.apache.xerces.internal.util.ErrorHandlerWrapper.error(ErrorHandlerWrapper.java:134)
at com.sun.org.apache.xerces.internal.impl.XMLErrorReporter.reportError(XMLErrorReporter.java:437)
at com.sun.org.apache.xerces.internal.impl.XMLErrorReporter.reportError(XMLErrorReporter.java:368)
at com.sun.org.apache.xerces.internal.impl.XMLErrorReporter.reportError(XMLErrorReporter.java:325)
at com.sun.org.apache.xerces.internal.impl.xs.XMLSchemaValidator$XSIErrorReporter.reportError(XMLSchemaValidator.java:458)
at com.sun.org.apache.xerces.internal.impl.xs.XMLSchemaValidator.reportSchemaError(XMLSchemaValidator.java:3237)
at com.sun.org.apache.xerces.internal.impl.xs.XMLSchemaValidator.processOneAttribute(XMLSchemaValidator.java:2832)
at com.sun.org.apache.xerces.internal.impl.xs.XMLSchemaValidator.processAttributes(XMLSchemaValidator.java:2769)
at com.sun.org.apache.xerces.internal.impl.xs.XMLSchemaValidator.handleStartElement(XMLSchemaValidator.java:2056)
at com.sun.org.apache.xerces.internal.impl.xs.XMLSchemaValidator.emptyElement(XMLSchemaValidator.java:766)
at com.sun.org.apache.xerces.internal.impl.XMLNSDocumentScannerImpl.scanStartElement(XMLNSDocumentScannerImpl.java:355)


如果有类似异常, 是说xml验证时, ID类型的属性不支持特殊字符(el表达式是以"#"开头). 
请升级到1.1.5-SNAPSHOT及以上版本
   
   
- 服务端抛出类似  InvocationTargetException 的异常.
在利用 Method 对象的 invoke 方法调用目标对象的方法时, 若在目标对象的方法内部抛出异常, 会抛出 InvocationTargetException 异常, 该异常包装了目标对象的方法内部抛出异常.
这可能是服务端的业务方法往上抛出了异常, 这个异常被loom框架捕获,但是没有将异常堆栈详情打印出来, 在1.1.6-SNAPSHOT 及以上版本会针对该异常做出特殊处理, 会将targetException的详细信息输出.
 
- 用thrift短连接调用finagle出现客户端接收不到返回值的bug
描述:服务端用loom提供服务,客户端用thrift短连接调用,调用一段时间(随机)会出现客户端读取不到返回值的情况

原因:loom里面用到的commons-codec包和commons-httpclient里面用到包版本不一致造成的.
  
解决办法:将commons-httpclient里面的包排除掉commons-codec解决问题.



## loom版本变更列表

  1.2.1-SNAPSHOT
  1. 统一方法调用统计信息格式为JSON格式
  2. 修复统计信息中的bug
  
  1.2.0-SNAPSHOT
  1. 增加每个方法的调用总次数,一分钟生产力, 平均调用时长的统计功能.
  2. 增加消费者维度,调用远程thrift接口的时长统计, 并将超过200ms的调用打印warn日志,可以结合eagleye进行实时预警.
  3. 增加服务提供者,每个api接口的调用时长统计, 并将超过200ms的调用打印warn日志,可以结合eagleye进行实时预警.
  4. 给application节点增加owner属性, 可以简单标识应该的负责人, 同时可以在loom-admin中消费者维度看到每个消费者的负责人.
  5. 在reference结点上添加owner属性(只是为了配合agent使用,直接只用loom的用户不需要关心).
  6. 在消费者启动逻辑中添加是否是agent判断,如果为agent,则不管是否启动成功都进行注册(直接使用loom的用户不需要关系该细节)
  7. 首页关系图展示, 添加直属关系检索
  8. 增加服务端提供服务时,服务超时时间, 默认30秒
      
  1.1.6-SNAPSHOT
  1. 修复loom:reference节点中timeout无效的bug
  2. 修复loom服务端业务方法抛出异常,只显示InvocationTargetException,并没有详细信息的问题
  
  1.1.5-SNAPSHOT
  1. 修复loom:service 的id不能使用el表达式的问题
  
  1.1.4-SNAPSHOT
  1. 修复通过redis获取心跳时, 当redis不稳定时,不能自动回复的问题
  
  1.1.3
  1. 修复服务调用时间过长抛出Unknown错误的问题
  
  1.1.2
  1. 修复消费者调用服务方法时, 不能传递为null的参数.
  
  1.1.1
  1. 应用kill时主动删除zookeeper上的providers下的节点, 提高其他watch该节点的响应.
  2. 增加服务从启动开始成功调用的总次数和失败总次数.
  3. 如果调用失败, 记录具体请求哪个provider请求失败.
  4. 修复消费者列表,提供者列表详情×显示X的bug.
---


文章题图: 来自冒险岛数据库系统
上一篇 下一篇

猜你喜欢

热点阅读