作为一个应届毕业生,进入阅文集团,加入到通用平台中心之后,随着日常工作的逐步深入,我渐渐了解阅文的技术体系,其中尤其以腾讯TARS平台最为重要。目前TARS平台承载了阅文内部绝大多数的服务,每日接口调用最大值近百亿,单业务峰值可在数万每秒,近300个业务服务。作为一个新人,我来讲下我从TARS小白到熟练工的历程中整理的一些知识点。

TARS文件

TARS作为一个服务治理框架,最基础的还是要解决服务间调用的问题,这样就引入一个问题:如何在多个服务之间发布接口协议。TARS创新性的设计了一种接口描述语言,即TARS文件,在TARS文件中,可以编写模型、接口、枚举等。在服务提供方,我们可以通过tars-maven-plugin解析TARS文件,生成对应的模型、接口、枚举代码,然后就可以在接口实现类中进行业务逻辑的编写。在对外提供服务时,调用方只需要拿到服务提供方的TARS文件,就能了解服务提供的所有接口及相关描述,从而生成客户端调用代码,进行服务接口调用。

module TestApp
{
    interface Hello
    {
        string hello(int no, string name);
    };
};

在阅文内部,我们也提供了统一的TARS管理中心,在这里我们可以找到所有已经上传到管理中心的TARS文件,从而避免了通过线下传递TARS文件带来的不便;管理中心支持将TARS文件自动打包成jar包并发布到公司的Maven镜像上,省去了自己编译带来的麻烦。

服务部署

在TARS平台上进行服务部署,只需把代码仓库地址配置到TARS平台,选择分支、选择编译器、编译、打包、分批发布一条龙部署到TARS平台上。目前TARS平台支持物理机部署、Docker部署、物理机和Docker混合部署,极大地提升了使用的体验及满足了服务发布的多样性。同时,TARS平台还支持对每台服务器做特殊化处理,比如可以选择某台服务器设置为无流量做线上接口测试,可以修改某台服务器的私有模板进行服务设置、JVM设置、日志设置等配置的修改。这些都为日常工作中的开发、测试、问题排查提供了更多的方法及可能性。

在发布线上服务时,TARS平台只支持指定tag进行编译打包,从而在流程上规范了服务发布的操作。而且发布是可以自定义选择分批发布、无损发布,最大限度的降低因服务重启引起的线上服务抖动。

服务监控

TARS平台提供了多维度、多样化的服务监控及特性监控。服务监控选项包含了流量、平均耗时、异常率、超时率等,可以自由选择时间范围、主调方业务名、主调方IP、被调方IP、接口名,从而缩小监控范围,方便定位问题。特性监控则针对每一台服务器提供了对JVM内存、堆使用情况、线程、请求队列等特性的监控,从而实现从服务器JVM级别进行问题定位。这些监控目前都是以5分钟为间隔,期待未来TARS平台可以提供更细粒度的时间间隔的监控。

服务监控还提供了实时数据的功能,实现实时观测服务的秒级运行状况,在定位线上问题上发挥了重要的作用。

日志

TARS-JAVA自带一套日志系统,提供了日志类的基础功能。它支持异步输出,支持多种日志级别,并且日志级别可以直接在TARS的管理平台通过修改私有模板或者给TARS服务发送设置日志级别命令来进行实时修改,实现平台一致性。它还支持异常输出,可以方便地打印异常日志。该日志系统不用额外的配置文件,不需要重写加载配置(在services.xml的应用中使用sl4j日志系统时,需要将日志系统的初始化安排到配置文件加载之后;在springboot的应用中使用sl4j日志系统时,需要写重新加载配置文件的逻辑)。目前TARS提供的日志系统在很多项目中都作为唯一日志系统使用。

不过需要注意的是默认的日志系统输出的日志编码是GBK格式的,当你使用以UTF-8解析的远程shell工具查看日志时经常会发现有些日志内容丢失或者时乱码。则可以根据需要,将远程工具的编码格式调整为与GBK兼容的编码,或者使用iconv等工具转换日志编码进行查看。

# encoding converting
cat xx.log | iconv -f=gbk

配置文件

TARS平台提供了服务配置中心来管理配置文件,在这里可以添加多个配置文件。在TARS平台上部署服务时,每台服务器会自动将所有配置文件加载到本地(前提是在服务代码中配置了加载配置文件),放在Classpath路径下,供JAVA服务读取解析。对于每个配置文件,TARS平台还提供了文件内容变更时触发的回调函数。通过回调函数机制,我们可以在修改完一个配置文件之后,在TARS平台上进行配置文件推送,从而触发代码中预先设定的回调函数,达到配置文件热更新的效果。当然,对于数据库连接信息、Redis连接信息这些需要重建连接的配置信息还是推荐通过重启服务来更新。

接口测试

TARS平台提供了对TARS接口进行测试的页面。在测试页面,我们可以为每个接口添加多个测试用例数据,可以任意选择服务所在的一台机器进行测试用例调用。这种调用方式方便、快捷,但是对于自动化脚本不甚友好。针对这种情况,阅文内部提供了使用HTTP接口调用TARS接口的方式,更方便地进行自动化接口测试。期待TARS平台后续能对自动化接口测试提供更多的支持。

编码问题

在开发TARS服务时,尤其需要注意各种编码问题,这里整理了一些可能出现的编码问题以及解决方案。

  • 服务配置文件编码

上面提到TARS平台提供了服务配置中心来管理配置文件,服务配置默认的编码是GBK,并且不支持修改,但阅文这边部署服务所使用的的机器(包括物理机和Docker)一般使用的编码是UTF-8。GBK和UTF-8都对ASCII码兼容,所以当配置文件不涉及中文或其他超过ASCII码覆盖范围的字符时,部署的微服务程序中采用系统默认编码读取配置文件时不会发生问题;但是当配置文件中涉及中文字符时,可能由于操作系统默认编码不与GBK兼容时,程序会读到错误的信息。

解决办法: 在自己写的服务代码中使用GBK编码打开配置文件;使用Unicode编码过的字符串代替原中文字符串。

  • TARS-JAVA的编码

你想尝试一下TARS,于是你把TARS-Java部署在刚刚调教好的Linux上(把本地语言设置成了zh_CN.UTF-8时),当你发布应用时,你会遇到一些奇怪的错误。当你查找原因时,你会发现原来在发布程序使用了GBK去读取Linux的命令行输出。这个时候你只需要改一下TARS-JAVA的源码,将读取命令行输出的编码改成UTF-8,然后重新编译一下即可,非常快捷。当然,也可以不将操作系统的编码设置成zh_CN.UTF-8,很多工具可能无法正确显示中文字符。

  • 服务器文件编码

服务器默认文件编码有时候会出现不是UTF-8的诡异的现象(往往是locale没有配置对UTF-8的支持),这个时候如果代码中使用了默认编码,就会出现乱码的现象。

解决方法:不要使用默认编码,在需要使用编码的时候,一定手动指定编码。

一些Trick分享

在实践TARS服务开发的过程中,积累了一些小技巧,挑选一些献丑。

  • 系统关闭钩子

在TARS平台管理中可以将特定服务的选中实例设置为无流量,以方便做发布、线上验证等操作。不过可惜的是,在TARS服务中无法感知平台的无流量配置,这也就意味着自己编写的TARS服务无法在无流量状态配置前后做一些特别的动作,比如在无流量开启时同时去屏蔽队列消费操作等。但是,TARS服务支持系统的关闭钩子,可以在系统退出前对申请资源进行清理以及确保某些任务完成后再退出。退出TARS服务只需要通过平台对服务的AdminObj发送shutdown命令来完成,然后通过System.exit退出系统。于是可以利用Runtime.getRuntime().addShutdownHook方法,给TARS服务增加系统关闭的钩子。

  • 获取调用方的IP

有时候需要记录调用方的IP,但由于TARS为了高效等目的,没有办法从服务端获取到客户端的标识信息。我们可以从TCP/UDP连接获取调用方的IP和端口信息。

Session session = ContextManager.getContext().request().getIoSession();

String remoteIp = session.getRemoteIp();

int remotePort = session.getRemotePort();
  • 如何规避Communicator实例重用所带来的问题

在TARS服务的实现中,不可避免地会去调用其他现成的服务,并且绝大多数情况下会调用多个服务。这些被调服务可能会要求在调用时使用不同的编码,有的需要使用UTF-8,而有的需要使用GBK。当多种不同编码的客户端混合调用时,可能会遇到编码问题。追本溯源,发现在获取Communicator实例时,会对其使用Map进行缓存,Map的键为locator或者CommunicatorConfig实例,而CommunicatorConfig只使用了locatormoduleName作为hashCodeequals方法的计算要素。于是当使用locatormoduleName相同的CommunicatorConfig实例去获取Communicator实例时,会得到同一个实例,即便charsetName并不一致。 解决的办法一般有两种:

  1. 继承CommunicatorConfig,并重写hashCode和equals方法,加入charsetName作为计算要素,再由这个派生类得到的实例去获取Communicator实例;
  2. 为需要特别配置其他属性的CommunicatorConfig实例设置不同的moduleName。

小编有话说

感谢这位同学的精彩分享,看完这位同学从TARS小白到熟练工的进阶过程中积攒的经验,你是否也想分享一下自己的经验?欢迎向 [email protected] 投稿。

如果你还没有了解过TARS,欢迎到GitHub上围观、交流,觉得好的话请给TARS点个star哦~!

GitHub地址:https://github.com/TarsCloud

作者丨唐靖凯

编辑丨TARS小助手

07-26 00:43