深入Ambari Metrics 机制分析

摘要:
图1AMS架构图1.AMS指标采集对于AMS本身来说,涉及的主要模块有MetricsMonitor、HadoopSinks以及MetricsCollector。Master模块便是MetricsCollector,Slave则是MetricsMonitor和HadoopSinks。Slave模块负责收集信息,并发送给Collector。接下来再看一下FlumeTimelineMetricsSinkjar包的结构,其中就包含一个FlumeTimelineMetricsSink类,继承自AbstractTimelineMetricsSink抽象类并实现MetricsSink接口,如上所示的所有服务的sink包基本都采用这样的结构。FlumeTimelineMetricsSink类中内置一个TimelineMetricsCollector线程,在flume启动FlumeTimelineMetricsSinkjar包时,其就通过其start方法中的线程调度器来轮询调度TimelineMetricsCollector线程,而在此线程中主段代码如下所示。

0.简介

Ambari作为一款针对大数据平台的运维管理工具,提供了集群的创建,管理,监控,升级等多项功能,目前在业界已经得到广泛使用。

Ambari指标系统( Ambari Metrics System,以下简称AMS)主要负责监控平台各类服务及主机的运行情况,提供各类服务及主机的相关指标,从而达到判断集群健康情况的目的,其重要性不言而喻。

本文是在深入阅读AMS源代码的基础之上,力求能够从监控指标的采集、存储、聚合及指标获取4个层面详细阐述AMS的整个工作机制。


这里写图片描述
图 1 AMS架构图

1.AMS指标采集

对于 AMS 本身来说,涉及的主要模块有 Metrics Monitor、Hadoop Sinks(此处统称,其中还包含kafka,storm,flume等服务的sinks,严格地来说应叫service sinks) 以及 Metrics Collector。

AMS 也是一个 Master-Slave 结构的框架。Master 模块便是 Metrics Collector,Slave 则是 Metrics Monitor 和 Hadoop Sinks。Slave 模块负责收集信息,并发送给 Collector。

当然 Metrics Monitor 和 Hadoop Sinks 也有不同的职责,前者主要负责收集机器本身相关的指标,例如 CPU、Mem、Disk 相关信息;后者则负责收集 Hadoop 相关 Service 模块的性能数据,例如该模块Namenode占用了多少 Mem,以及该模块的 CPU 占用率等。

1.1 指标收集

关于指标的采集,此处以Flume服务为例,剖析AMS是如何采集Flume运行的相应指标的。Ambari内置了FlumeTimelineMetricsSink这样的jar包,通过Ambari启动flume服务,ambari会在flume的启动脚本参数中加入以下两项:

-Dflume.monitoring.type=org.apache.hadoop.metrics2.sink.flume.FlumeTimelineMetricsSink 
-Dflume.monitoring.node=<AMS_HOST>:6188

其中即为AMS collector的节点名字,而6188则是collector中的Timeline Server对外提供的默认端口,以此来向Timeline Server推送数据。
接下来再看一下FlumeTimelineMetricsSink jar包的结构,其中就包含一个FlumeTimelineMetricsSink类,继承自AbstractTimelineMetricsSink抽象类并实现MetricsSink接口,如上所示的所有服务的sink包基本都采用这样的结构。
FlumeTimelineMetricsSink类中内置一个TimelineMetricsCollector线程,在flume启动FlumeTimelineMetricsSink jar包时,其就通过其start方法中的线程调度器来轮询调度TimelineMetricsCollector线程,而在此线程中主段代码如下所示。

@Override
  public voidstart() {
    LOG.info("Starting Flume Metrics Sink");
    TimelineMetricsCollector timelineMetricsCollector = newTimelineMetricsCollector();
    if (scheduledExecutorService == null || scheduledExecutorService.isShutdown() ||scheduledExecutorService.isTerminated()) {
      scheduledExecutorService =Executors.newSingleThreadScheduledExecutor();
    }
    scheduledExecutorService.scheduleWithFixedDelay(timelineMetricsCollector, 0,
        pollFrequency, TimeUnit.MILLISECONDS);
  }  }

从上面可看出Start方法中采取线程池的方法,以pollFrequency(可配置)的周期间隔,调度TimelineMetricsCollector线程,再细看一下TimelineMetricsCollector线程的详细说明,其主要代码如下所示。

classTimelineMetricsCollector implements Runnable {
    @Override
    public voidrun() {
      try{
        Map<String, Map<String, String>> metricsMap =JMXPollUtil.getAllMBeans();
        long currentTimeMillis =System.currentTimeMillis();
        for(String component : metricsMap.keySet()) {
          Map<String, String> attributeMap = metricsMap.get(component);
          LOG.debug("Attributes for component " +component);
          processComponentAttributes(currentTimeMillis, component, attributeMap);
        }
      } 

TimelineMetricsCollector线程轮循从服务的JMX端口中获取指标数据,形成Map对象,并通过processComponentAttributes方法进行逻辑转换后再发送。

1.2 指标推送

由上面源码可以看出,本质上,AMS的监控数据还是从各服务JMX端口中取得的,再通过processComponentAttributes方法逻辑上转换成AMS的内部的TimelineMetrics,通过emitMetrics方法post到Timeline Server(emitMetrics方法正是从AbstractTimelineMetricsSink类继承而来),其接口为:
http://:6188/ws/v1/timeline/metrics
如下是emitMetrics方法的部分片段,从中可以看出,emit方法最终还是将指标数据转化成json格式的数据,通过接口推送至TimelineServer。

protected voidemitMetrics(TimelineMetrics metrics) throws IOException {
    String connectUrl =getCollectorUri();
    try{
      String jsonData =mapper.writeValueAsString(metrics);
      StringRequestEntity requestEntity = new StringRequestEntity(jsonData, "application/json", "UTF-8");
      PostMethod postMethod = newPostMethod(connectUrl);
      postMethod.setRequestEntity(requestEntity);
      int statusCode = httpClient.executeMethod(postMethod);

若是转换成curl命令的形式,则通过以下这样一条命令进行推送数据:

curl -i -X POST -H "Content-Type: application/json" -d "${json}" ${url}

其中json为转化成json的metrics数据,url为上面接口。
emitMetrics方法或curl命令发送的url最终会被Timeline server所截获,再通过TimelineMetricStore类以phonenix接口方式存储到hbase数据库中,如下文TimelineWebServices类代码所示。

@Path("/metrics")
@POST
@Consumes({ MediaType.APPLICATION_JSON /*, MediaType.APPLICATION_XML */})
  publicTimelinePutResponse postMetrics(
    @Context HttpServletRequest req,
    @Context HttpServletResponse res,
    TimelineMetrics metrics) {
    init(res);
    if (metrics == null) {
      return newTimelinePutResponse();
    }
    try{
      //TODO: Check ACLs for MetricEntity using the TimelineACLManager.
      //TODO: Save owner of the MetricEntity.
      returntimelineMetricStore.putMetrics(metrics);
}

上文描述了Flume服务指标推送的大概过程,服务运行时主动推送指标, AMS接收推送指标。其它各类服务如hadoop,kafka,storm均以此种方式进行指标的推送,在此不再作详细讨论。

2.AMS指标存储

AMS采集到的服务指标通过http/https的方式推送到timeline server,timeline server内部嵌入了一个hbase,通过phoenix(nosql之上的sql查询)将指标数据存入到hbase中。
Hbase库中总共有7张表,其相应表名如下表所示。


表2-1 Metrics指标存储表
这里写图片描述

虽然库中一共有7张表,但是实际存储指标数据的只有METRIC_RECORD表,其它各表是在其基础之上做相应的统计及聚合而成的,下表是METRIC_RECORD表详细说明。


表2-2 METRIC_RECORD表字段说明
这里写图片描述

该表是所有表中唯一存储实际metrics数据的表,其它表都是在此表的基础之上进行时间段的相应统计。
(1)针对采集的hosts指标,即由monitor发送的指标值
采集的metric记录,由{时间戳1:值,时间戳2:值,….}这样的记录组成,其中数目表现在Metric_count上,对于monitor发送的metric。为12条,每条间隔5秒种,然后一分钟向timelineServer发送一次,存入表中。
(2)针对采集的hadoop sink指标
采集的metric记录,由{时间戳1:值,时间戳2:值,….}这样的记录组成,每条间隔10秒钟,每隔70秒发送一次,采集7条,所以metric_count为7,一分钟向timelineServer发送一次,存入表中。
表METRIC_RECORD_MINUTE是按分钟进行统计的,默认一次统计时间是5min(可配置),该表实则是以METRIC_RECORD表的数据作为统计的基准。下表对METRIC_RECORD_MINUTE做了详细说明。


表2-3 METRIC_RECORD_MINUTE字段说明
这里写图片描述

假设5分钟统计一次,以mem_free为例,则本次统计是以主机为单位,假设在metric_record表中,某主机每隔一分钟发送一条mem_free的Record,一条record中有12条metric values,则本次统计共有5条Record,metric_count则为60条。同样的,这五分钟内的最大,最小和总和,只需要比对提取Metric_record中这60条的Record的最大,最小,以及5条总和即能统计出这5分钟内相应的属性。
类似于这样几条语句得以统计:

1select hostname,max(metric_max) from metric_record where metric_name='mem_free' and server_time>=1471503845324 and server_time<1471504146520 group by hostname;------统计5分钟内,每台主机上该metric的最大值。
  (2select hostname,min(metric_min) from metric_record where metric_name='mem_free' and server_time>=1471503845324 and server_time<1471504146520 group by hostname; ------统计5分钟内,每台主机上该metric的最小值。
  (3select hostname,sum(metric_sum) from metric_record where metric_name='mem_free' and server_time>=1471503845324 and server_time<1471504146520 group by hostname; ------统计5分钟内,每台主机上该metric值总和。
  (4select hostname,sum(metric_count) from metric_record where metric_name='mem_free' and server_time>=1471503845324 and server_time<1471504146520 group by hostname; ------统计5分钟内,每台主机上统计的该metric数量和。

至于METRIC_RECORD_HOURLY以及METRIC_RECORD_DAILY表其原理均是参照MINUTE表的原理,只是时间区间扩大了,已经参照的数据表变更了,METRIC_RECORD_HOURLY以METRIC_RECORD_MINUTE的数据为基准,而METRIC_RECORD_DAILY则以METRIC_RECORD_HOURLY的数据为基准进行统计,在此就不再描述了。

3.AMS指标聚集

上文中所统计的7张表,除了以METRIC_RECORD前缀的表之外,还有METRIC_AGGREGATE作为前缀的表,这就是集群的指标聚集表,在聚集表中不区分host,只是以service(APP_ID)
进行分组统计,其数据来源也是从METRIC_RECORD表中进行查询后然后再进行聚集的,下表是表字段的详细说明。


表3-1 METRIC_AGGREGATE表字段详细说明
这里写图片描述

以Metric_Name为主要指标,由于是集群级别的统计,所以不再有HOSTNAME相关字段的说明,在此表中增加了HOSTS_COUNT的字段,即聚集的Metric来自主机的数量。
实际中不存在表METRIC_AGGREGATE_MINUTE,但是在图3-1中可以看到一个单独的TimelineMetricClusterAggregatorMinute聚集线程类,每2分钟聚集一次,其聚集结果采取了分片的方式,30秒一个片区记录,即0-30秒,30-60秒,60-90秒,90-120秒分成4个片区,每个时间段采集的记录分别存到对应的片区记录中,而各个片区记录直接存入到METRIC_AGGREGATE表中,该表间隔记录为30秒一条,是在表METRIC_AGGREGATE_MINUTE的基础之上进行分片的,之所以这么设定,也是为了防止频繁的聚集对AMS造成过大负载。
这个分片时间是由参数timeline.metrics.cluster.aggregator.minute.timeslice.interval进行设定的,所以METRIC_AGGREGATE表其实是METRIC_AGGREGATE_MINUTE表聚合统计的结果之上,再对其结果按时间进行分片而形成的。
指标聚集由TimelineMetricAggregator起始,其作为一个接口,也是一个线程,其实现子类如下图所示。


这里写图片描述
图3-1 TimelineMetricAggregator层次图

在AbstractTimelineAggregator类中实现了run方法,其中通过轮循调度doWork方法来实现聚集,调度时间可配置,如下是doWork程序代码。

@Override
  public boolean doWork(long startTime, longendTime) {
    boolean success = true;
    Condition condition =prepareMetricQueryCondition(startTime, endTime);
    Connection conn = null;
    PreparedStatement stmt = null;
    ResultSet rs = null;
    try{
      conn =hBaseAccessor.getConnection();
      stmt =PhoenixTransactSQL.prepareGetMetricsSqlStmt(conn, condition);
      if(condition.doUpdate()) {
        int rows =stmt.executeUpdate();
        conn.commit();
      } else{
        rs =stmt.executeQuery();
      }
      aggregate(rs, startTime, endTime);
      }

通过以上代码可以看到,在doWork方法中,首先还是通过phoenix接口查询到数据集,再对数据集进行聚集(aggregate方法中),针对不同的AbstractTimelineAggregator的子类,具有不同的aggregate方法,最终存入到METRIC_AGGREGATE表中,如此实现整个AMS的指标聚集功能。

4.AMS指标获取

AMS提供了2种获取指标的接口,分别是Collector提供的API以及Ambari Server的API接口。其中前一种方式更接近原生的指标数据,而后一种方式更为常用,应该说整个Ambari上层获取指标的方式都是采取后者,而后者在底层本质上还是调用的第一种方式,拿到库中的原生数据,再进行加工及逻辑处理,最后返回到WEB端。

4.1 Collector API

http://<AMS_HOST>:6188/ws/v1/timeline/metrics?metricNames=<>&hostname=<>&appId=<>&startTime=<>&endTime=<>&precision=<>

如上是AMS Collector的总体API,其参数说明如下表所示。


表4-1 Collector API参数说明
这里写图片描述

当以此接口获取指标数据时,首先此URL会被TimelineWebServices捕获到,其类中相应代码如下所示。

@GET
@Path("/metrics")
@Produces({ MediaType.APPLICATION_JSON })
  publicTimelineMetrics getTimelineMetrics(
    @Context HttpServletRequest req,
    @Context HttpServletResponse res,
    @QueryParam("metricNames") String metricNames,
    @QueryParam("appId") String appId,
    @QueryParam("instanceId") String instanceId,
    @QueryParam("hostname") String hostname,
    @QueryParam("startTime") String startTime,
    @QueryParam("endTime") String endTime,
    @QueryParam("precision") String precision,
    @QueryParam("limit") String limit,
    @QueryParam("grouped") String grouped,
    @QueryParam("topN") String topN,
    @QueryParam("topNFunction") String topNFunction,
    @QueryParam("isBottomN") String isBottomN,
    @QueryParam("seriesAggregateFunction") String seriesAggregateFunction
 ) {
    init(res);
    try{
      returntimelineMetricStore.getTimelineMetrics(
        parseListStr(metricNames, ","), parseListStr(hostname, ","), appId, instanceId,
        parseLongStr(startTime), parseLongStr(endTime),
        Precision.getPrecision(precision), parseIntStr(limit),
        parseBoolean(grouped), parseTopNConfig(topN, topNFunction, isBottomN),
        seriesAggregateFunction);
}

如上代码所示,在Timeline server捕获到请求后,会调用TimelineMetricStore.getTimelineMetrics方法,并传入相应的请求参数,获取指标数据。再深入到TimelineMetricStore中可以看到此类为一个接口,唯一的实现子类为HBaseTimelineMetricStore,通过其getTimelineMetrics方法取得指标数据,其主要代码如下所示。

@Override
  public TimelineMetrics getTimelineMetrics(List<String>metricNames,
      List<String>hostnames, String applicationId, String instanceId,
      Long startTime, Long endTime, Precision precision, Integer limit,
      boolean groupedByHosts, TopNConfig topNConfig, String seriesAggregateFunction) throws SQLException, IOException {
    TimelineMetricsSeriesAggregateFunction seriesAggrFunctionInstance = null;
    ……………………………………….
    Multimap<String, List<Function>> metricFunctions =
      parseMetricNamesToAggregationFunctions(metricNames);
    ConditionBuilder conditionBuilder = new ConditionBuilder(new ArrayList<String>(metricFunctions.keySet()))
      .hostnames(hostnames)
      .appId(applicationId)
      .instanceId(instanceId)
      .startTime(startTime)
      .endTime(endTime)
      .precision(precision)
      .limit(limit)
      .grouped(groupedByHosts);
………………………………………….
………………………………………
    Condition condition =conditionBuilder.build();
    TimelineMetrics metrics;
    if (hostnames == null ||hostnames.isEmpty()) {
      metrics =hBaseAccessor.getAggregateMetricRecords(condition, metricFunctions);
    } else{
      metrics =hBaseAccessor.getMetricRecords(condition, metricFunctions);
    }
    metrics =postProcessMetrics(metrics);
    ………………………………..
    returnseriesAggregateMetrics(seriesAggrFunctionInstance, metrics);
  }


从上文代码中也可以看出,HBaseTimelineMetricStore类在其内部通过注解映射查询的条件构建用于查询的Condition对象,其次向HBaseAccessor传入此条件用于查询,根据查询参数中是否有无hostname从而决定是查询聚集表还是主机表,最终取得相应的查询结果,其中代码在此不再详述,感兴趣可以自行阅读,至此,通过Collector API取得指标数据的流程就打通了。

4.2 Ambari Server API

Ambari Server API总体上分为3个层次,具有3种类型的API,无论是哪种类型的API,在其底层取数据时,最终都是利用Collector的API,如下是整体的架构详细图。


这里写图片描述
图4-1 AMS工作机制图

4.2.1 主机类型指标API

http://<ambari-server>:8080/api/v1/clusters/<cluster-name>/hosts/<host-name>?fields=metrics/cpu/cpu_user[1430844610,1430848210,15]

此API是主机类型的API,在其中具有/hosts/的前缀和参数的补充,fileds后面跟的是指标名称,[]内描述的是时间起始。上文中说过,Server的API在其底层还是调用Collector的API,那么此API在其对应的底层便是Collector的API加上hostname及appId可选参数,其中appId设为HOST即可,其后的时间戳便是在startTime和endTime中描述。

4.2.2 组件类型指标API

http://<ambari-server>:8080/api/v1/clusters/<cluster-name>/services/HDFS/components/DATANODE?fields=metrics/dfs/datanode/DfsUsed[1430844610,1430848210,15]

组件类型的API取消了hostname的参数,其主要针对服务整体的聚集查询,其所查询的表也是METRIC_AGGREGATE类型的表,对应于Collector的API则是在其中取消了hostname的参数字段,则API默认去聚集表(METRIC_AGGREGATE)中查询。

4.2.3 主机组件类型指标API

http://<ambari-server>:8080/api/v1/clusters/<cluster-name>/hosts/<host-name>/host_components/NAMENODE?fields=metrics/jvm/memHeapCommittedM[1430847303,1430850903,15]
  • 1

主机组件类型的API实则和主机类型的API类似,只是主机类型直接是针对主机相关指标如cpu,mem类型的指标的获取,而主机组件则是真多host之上的部署的服务的指标的获取,所以在其API中新增了host_components参数。对应于底层Collector的API,只需要将主机类型对应的Collector的API中的appId由HOST替换成相应的服务名称即可。
以上3种类型的API,无论哪一种,在其底层最终都是调用Collector API取得metric数据的,主机类型指标则是在Collector API中补充上hostname属性,组件指标则是在Collector API去掉hostname属性,让其做聚集查询(去METRIC_AGGREGATE查询数据),而主机组件指标类型则是同时补充上appId与hostname两个参数进行查询,下图描述了通过Server API获取metric数据时主要实现类及其继承结构。


这里写图片描述
图 4-2 AMSPropertyProvider类层次图

当向Server发送Metric的请求URL时,最终都会在Server端通过AMSPropertyProvider转化成一个MetricRequest,MetricRequest是AMSPropertyProvider的内部类,再由MetricRequest.populateResources方法进行请求的处理,下文是其主要核心代码。

public Collection<Resource>populateResources() throws SystemException {
if (!hostComponentHostMetrics.isEmpty()) {
        String hostComponentHostMetricParams = getSetString(processRegexps(hostComponentHostMetrics), -1);
         setQueryParams(hostComponentHostMetricParams, hostnames, true, componentName);
          TimelineMetrics metricsResponse = null;
          try{
            metricsResponse =getTimelineMetricsFromCache(
              getTimelineAppMetricCacheKey(hostComponentHostMetrics,
                componentName, uriBuilder.toString(),hostnames), componentName);
          } 

此方法首先针对metric的请求类型做了判断,针对不同的指标分别进行不同的处理。此处便是hostComponentHostMetrics散列表不为空的情况下(即此为主机指标的请求),针对此请求,首先利用setQueryParams查询参数设置,其次执行getTimelineMetricsFromCache取得返回的metrics,在其中传入了uriBuilder,此对象即为Collector的API,构建完成之后即通过此URL取得相应的metrics数据。
接下来直接进入getTimelineMetricsFromCache方法,其代码如下所示。

privateTimelineMetrics getTimelineMetricsFromCache(TimelineAppMetricCacheKey metricCacheKey,String componentName) throws IOException {
      //Cache only the component level metrics
      //No point in time metrics are cached
      if (metricCache != null
          && !StringUtils.isEmpty(componentName)
          && !componentName.equalsIgnoreCase("HOST")
          && metricCacheKey.getTemporalInfo() != null) {
        returnmetricCache.getAppTimelineMetricsFromCache(metricCacheKey);
      }
      returnrequestHelper.fetchTimelineMetrics(metricCacheKey.getSpec());
    }

从代码中看到,其针对有无AMS缓存的情况分别进行了处理,在原始无缓存的情况下,则是通过requestHelper.fetchTimelineMetrics的方法取得相应的metric数据,而传入的参数实则就是上文的URL。
紧接着进入fetchTimelineMetrics方法,从下文代码可以看到,最终是通过streamProvider.readFrom(spec)此方法取得metrics数据的,而streamProvider则是一个封装好的URL读取数据对象。

publicTimelineMetrics fetchTimelineMetrics(String spec) throws IOException {
    LOG.debug("Metrics request url = " +spec);
    BufferedReader reader = null;
    TimelineMetrics timelineMetrics = null;
    try{
      reader = new BufferedReader(newInputStreamReader(streamProvider.readFrom(spec)));
      timelineMetrics =timelineObjectReader.readValue(reader);
}

5.总结

本文主要是从AMS指标的采集、存储、聚集、获取四个层面,详细描述了AMS整个内在工作机制,并详细整理了AMS对外各API不同点及其来龙去脉。由于源码的复杂性,本文只罗列相关的主要功能代码及其相应的流程,并未细化到最底层的实现,不足之处,敬请见谅。同时,也期待您的指导与帮助。

参考资料:

1.http://www.ibm.com/developerworks/cn/opensource/os-cn-ambari-metrics/index.html
2.http://blog.csdn.net/bluishglc/article/details/48155265

免责声明:文章转载自《深入Ambari Metrics 机制分析》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇软件的版本控制Minimum Snap轨迹规划详解(3)闭式求解下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

【Android】Android中线程的应用

1. Android进程    在了解Android线程之前得先了解一下Android的进程。当一个程序第一次启动的时候,Android会启动一个LINUX进程和一个主线程。默认的情况下,所有该程序的组件都将在该进程和线程中运行。同时,Android会为每个应用程序分配一个单独的LINUX用户。Android会尽量保留一个正在运行进程,只在内存资源出现不足...

Flask-SQLAlchemy操作

Flask-SQLAlchemy SQLAlchemy 一. 介绍 SQLAlchemy是一个基于Python实现的ORM框架。该框架建立在 DB API之上,使用关系对象映射进行数据库操作,简言之便是:将类和对象转换成SQL,然后使用数据API执行SQL并获取执行结果。 pip3 install sqlalchemy 组成部分: Engine,框架...

vue,基于element的tree组件封装

封装组件代码 // 组件:树 /* 参数说明-属性: 1.treeData:展示数据(array) 2.treeEmptyText:内容为空的时候展示的文本(String) 3.treeNodeKey:每个树节点用来作为唯一标识的属性,整棵树应该是唯一的(String) 4.treeRenderAfterExpand:是否在第一次展开某个树节点后才渲染其子...

Android中的设计思想一

Android设计思想 直接上示例 ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ xml布局 <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/r...

python的paramiko模块

    paramiko是用python语言写的一个模块,遵循SSH2协议,支持以加密和认证的方式,进行远程服务器的连接。paramiko支持Linux, Solaris, BSD, MacOS X, Windows等平台通过SSH从一个平台连接到另外一个平台。利用该模块,可以方便的进行ssh连接和sftp协议进行sftp文件传输。   paramiko常...

VB 的字符串处理函数

一、InStr 返回 Variant (Long),指定一字符串在另一字符串中最先出现的位置。 语法 InStr([start, ]string1, string2[, compare]) start 可选参数。为数值表达式,设置每次搜索的起点。如果省略,将从第一个字符的位置开始。如果 start 包含 Null,将发生错误。如果指定了 compar...