内容简介:针对职业高尔夫和网球赛的 Predictive Cloud Computing 项目,第 7 部分: 大数据存储与分析 - IBM...
针对职业高尔夫和网球赛的 Predictive Cloud Computing 项目,第 7 部分
大数据存储与分析 - IBM DB2 与 Graphite
, , Brian O'Connell , 和 Nicholas McCrory
2017 年 5 月 24 日发布
系列内容:
此内容是该系列 9 部分中的第 # 部分: 针对职业高尔夫和网球赛的 Predictive Cloud Computing 项目,第 7 部分
http://www.ibm.com/developerworks/library/?search_by=predictive+cloud+computing+for+professional+golf+and+tennis
敬请期待该系列的后续内容。
此内容是该系列的一部分: 针对职业高尔夫和网球赛的 Predictive Cloud Computing 项目,第 7 部分
敬请期待该系列的后续内容。
在本文中,我们将介绍 PCC 系统中的数据存储,它使用 IBM DB2 作为数据源,还使用了 Java™ Persistence API。此外,我们还将讨论如何使用 Graphite 来检测代码库和工作负载。最后将介绍用于分析该数据的工具。
IBM DB2 和 Java Persistence API
DB2 是一个 IBM 关系数据库服务器,在整个 PCC 系统中被广泛用作持久数据存储。我们主要通过 Java Persistence API (JPA2) 连接 DB2。Java Persistence API 提供了一种途径来将 Java 对象映射到关系数据,比如表中的行或数据库中的视图。我们还使用了 Liquibase 来更新 DB2 数据库模式,因为它提供了模式版本控制和回滚功能。模式更新是在 XML 标记中描述的,每个模式更改都包含在一个单独的更改集条目中。这样方便独立地将更小、更快的更改部署到数据库模式。
“ Predictive Cloud Computing 系统利用 IBM DB2 来存储从源数据生成的聚合信息,并利用 Graphite 来分析指标和描绘我们的代码库。每个 工具 都使得 PCC 系统能够存储、分析和检索大量数据。 ”
PCC 系统使用了 JPA2 的 Apache OpenJPA 实现;而且为了通过 JPA2 与数据源建立连接,还在一个 persistence.xml 文件中描述了数据源。清单 1 给出了一个 persistence.xml 文件示例。
清单 1. OpenJPA persistence.xml 配置文件
<?xml version="1.0" encoding="UTF-8"?> <persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"> <persistence-unit name="Aviator-Unit" transaction-type="RESOURCE_LOCAL"> <provider>org.apache.openjpa.persistence.PersistenceProviderImpl</provider> <class>com.ibm.ei.persistence.jpa.CloudStatisticsDAO</class> <class>com.ibm.ei.persistence.jpa.CrawlerChecksum</class> <class>com.ibm.ei.persistence.jpa.EventPredictionCountDAO</class> <class>com.ibm.ei.persistence.jpa.EventStatisticsDAO</class> <class>com.ibm.ei.persistence.jpa.LogCount</class> <class>com.ibm.ei.persistence.jpa.HistoricalLogCount</class> <class>com.ibm.ei.persistence.jpa.Path</class> <class>com.ibm.ei.persistence.jpa.PlayerContentAnalysisDAO</class> <class>com.ibm.ei.persistence.jpa.PlayerDAO</class> <class>com.ibm.ei.persistence.jpa.CrawlerPlayerPopularity</class> <class>com.ibm.ei.persistence.jpa.SiteDAO</class> <class>com.ibm.ei.persistence.jpa.golf.FeaturedGroupDAO</class> <class>com.ibm.ei.persistence.jpa.golf.HoleDAO</class> <class>com.ibm.ei.persistence.jpa.golf.RoundDAO</class> <class>com.ibm.ei.persistence.jpa.tennis.Match</class> <class>com.ibm.ei.persistence.jpa.tennis.MatchStatus</class> <class>com.ibm.ei.persistence.jpa.tennis.TennisCourt</class> <class>com.ibm.ei.persistence.jpa.twitter.Mention</class> <class>com.ibm.ei.persistence.jpa.twitter.PlayerSummary</class> <class>com.ibm.ei.persistence.jpa.twitter.Retweet</class> <class>com.ibm.ei.persistence.jpa.twitter.TweetDAO</class> <class>com.ibm.ei.persistence.jpa.twitter.ReachDAO</class> <class>com.ibm.ei.persistence.jpa.twitter.User</class> <class>com.ibm.ei.persistence.jpa.HistoricalLogCount</class> <exclude-unlisted-classes>true</exclude-unlisted-classes> <properties> <property name="openjpa.DynamicEnhancementAgent" value="true"/> <property name="openjpa.RuntimeUnenhancedClasses" value="unsupported"/> <property name="openjpa.ConnectionDriverName" value="org.h2.Driver"/> <property name="openjpa.ConnectionURL" value="jdbc:h2:mem:test;DB_CLOSE_DELAY=-1"/> <property name="openjpa.jdbc.Schema" value="eiblueus"/> <property name="openjpa.DataCache" value="false"/> <property name="openjpa.QueryCache" value="false"/> <property name="openjpa.RemoteCommitProvider" value="sjvm"/> <property name="openjpa.Multithreaded" value="false"/> <property name="openjpa.QueryCompilationCache" value="false"/> <property name="openjpa.jdbc.FinderCache" value="false"/> </properties> </persistence-unit> </persistence>
在 persistence.xml 中,每个 persistence-unit 都描述了一个 JPA 数据源。提供者包含 JPA 实现的类名,充当初始化数据源的入口点。在提供者后列出的是应通过 OpenJPA 编译阶段增强的类。这些类是映射到关系数据的 Java 对象;本文后面会更详细地分析它们。最后的 properties 元素中指定任何自定义配置属性。连接 URL、数据库模式名称和缓存配置等细节都可以在 properties 中配置。persistence.xml 文件应包装到 Java jar META-INF 目录中,该目录可供 JPA 在运行时读取。
配置 JPA 数据源后,可增强 persistence.xml 中列出的类并使用它们访问数据库数据。清单 2 给出了 PlayerDAO,该类包含填充高尔夫球或网球运动员的信息的字段。为了简便起见,该类中的方法已省略。
清单 2. PlayerDAO 类能够访问数据库中的 PLAYERS 表。
@Entity @Table(name = "PLAYERS") @IdClass(PlayerId.class) public class PlayerDAO implements Serializable, Player { @Id @Index @Column(name = "ID", length = 20) private String playerId; @Id @ManyToOne private SiteDAO site; @ElementCollection @Enumerated(EnumType.STRING) @Column(name = "PLAYER_TYPES") private Set<PlayerType> playerTypes; @Column(name = "FIRST_NAME") private String firstName; @Column(name = "LAST_NAME") private String lastName; @Column(name = "SEED", nullable = true) private Short seed; @Column(name = "RANK", nullable = true) private Short rank; @Column(name = "GENDER", length = 1) @Enumerated(EnumType.STRING) private Gender gender; … }
此类中有许多注释。注释是向 JPA Enhancer 告知对象细节和对象对应于哪些数据库信息或模式的主要途径。在顶部, @Entity
告诉 OpenJPA 该类表示一种数据库关系, @Table(name = "PLAYERS")
添加应从中检索信息的表的相关信息。因此,每个 PlayerDAO 对象都表示 PLAYERS 表中的一行。 @IdClass(PlayerId.class)
注释告诉 OpenJPA 在何处查找表示该表的组合键的类。组合主键包含来自某个数据库的一个表的多个字段,用于检查某一行的唯一性。我们对运动员使用了一个组合键,以便用 PLAYERS 表中的单独一行来描述比赛中的每位运动员。
依据 Java Persistence API 规范,每个实体都需要实现 Serializable 接口,我们的实体还实现了 Player,该接口提供了一些 getter 和 setter 方法的可视性。PlayerDAO 中的字段也添加了注释, @Id
注释标识了组成类的组合键的字段。 @Column
注释将表中的列映射到对象中的字段。还有其他一些注释,比如 @Enumerated
将数据库表中的一个字符串映射到一个 Java Enum 类型, @ElementCollection
在该字段与另一个表之间建立一对多的关系。
清单 3. 清单 2 中的 PlayerDAO 实体对象的 PlayerID 类
public class PlayerId { SiteId site; String playerId; public PlayerId(final String playerId, final SiteId site) { this.playerId = playerId; this.site = site; } public PlayerId() { } @Override public boolean equals(final Object obj) { if (obj == this) { return true; } if (obj instanceof PlayerId) { final PlayerId other = (PlayerId) obj; return Objects.equal(playerId, other.playerId) && Objects.equal(site, other.site); } return false; } @Override public int hashCode() { return Objects.hashCode(playerId, site); } @Override public String toString() { return Objects.toStringHelper(this).add("Player ID", playerId).add("Site ID", site).toString(); } }
在清单 3 中,给出了前面提到的 PlayerDAO 类的 PlayerID 类。该类是一个简单的 “普通 Java 对象” (POJO),包含两个构造函数,以及 equals、hashcode 和 toString
方法。最低限度上,任何组合键类都需要共享映射到前一个对象中的 @Id
字段的字段和一个空构造函数。为了遵循 Java 最佳实践,我们还实现了其他方法。
清单 4. 如何使用 Java Persistence API 查询 2016 年澳网公开赛的所有运动员
// load out persistence unit to the data source EntityManagerFactory emf = Persistence.createEntityManagerFactory("Aviator-Unit"); //create a connection to the database EntityManager manager = emf.createEntityManager(); //load all the players for this tournament final CriteriaBuilder criteriaBuilder = manager.getCriteriaBuilder(); final CriteriaQuery<PlayerDAO> criteria = criteriaBuilder.createQuery(PlayerDAO.class); final Root<PlayerDAO> playerRoot = criteria.from(PlayerDAO.class); final Predicate siteCondition = criteriaBuilder.and(criteriaBuilder.equal(playerRoot.get(PlayerDAO_.site).get(SiteDAO_.name), "ausopen"), criteriaBuilder.equal(playerRoot.get(PlayerDAO_.site).get(SiteDAO_.year), 2016)); //create the search critera criteria.select(playerRoot); criteria.where(siteCondition); final TypedQuery<PlayerDAO> query = manager.createQuery(criteria); List<PlayerDAO> players = query.getResultList()
在清单 4 中,我们结合使用了来自 Java Persistence API 的 DAO、JPA 和组合键,给出了在数据库中查询所有 2016 年澳网公开赛运动员的示例。这里的步骤综合了本文前面所述的全部内容。JPA EntityManagerFactory
解析 persistence.xml 文件,可与数据源建立新连接,这些连接通过 EntityManager
对象表示。一个 EntityManager
表示一个与数据库的不同连接。它可用于在数据库中保存、更新或查询数据,这些数据将映射到 Java 对象,比如 PlayerDAO。在清单 4 中,我们使用 JPA 条件构建器来构建一条 SQL 语句,该语句将从 PLAYERS 表中选择 2016 年在与名称 ausopen
(澳网公开赛)匹配的地点参加比赛的所有运动员。此查询将返回多个结果,所以 query.getResultList
会根据来自增强型 JPA 类的元模型返回一个对象列表。
使用关系数据库时的一个挑战是,更新表的模式可能是一个非常痛苦的过程,在必须保留表中的现有信息时,这种痛苦会加倍。Liquibase 是一个强大的工具,可用于以标记格式创建有版本控制的、描述性的更改集。我们使用了 Liquibase 标记的 XML 版,但它也支持 JSON 和原始 SQL 更改集。每个更改集都是一个 XML 标记,描述要应用于数据库的更改,无论是简单地向现有表插入一个新行,还是向一个表添加新索引,或者为现有表设计新列。
清单 5. 通过 Liquibase 向现有表添加新行的更改集
<changeSet author="boc" id="1.0.2"> <comment>Add 2015 masters site</comment> <insert schemaName="eiblueus" tableName="sites"> <column name="NAME" value="mast"/> <column name="YEAR" valueNumeric="2015"/> <column name="URL" value="http://www.masters.com/en_US/tournament/"/> <column name="LATITUDE" valueNumeric="33.4667"/> <column name="LONGITUDE" valueNumeric="81.9667"/> <column name="TOURNAMENT_SCHEDULE_START" valueDate="2015-04-09"/> <column name="TOURNAMENT_SCHEDULE_END" valueDate="2015-04-12"/> <column name="PRELIMINARY_SCHEDULE_START" valueDate="1970-01-01"/> <column name="PRELIMINARY_SCHEDULE_END" valueDate="1970-01-01"/> </insert> </changeSet>
清单 5 中的更改集展示了 Brian O'Connell 将 2015 年大师赛添加到数据库的 eiblueus
模式的名为 sites
的表中。每一列都有更改集的值,而且包装在 <insert>
XML 标记中。插入是 Liquibase 的最简单用法,但使用标准方法(比如 JDBC 或 JPA 连接)插入数据也很简单。
清单 6. 使用 Liquibase 更改集更新表模式
<changeSet author="cnmcavoy" id="1.0.3"> <comment>Add twitter follower, klout, retweet data</comment> <addColumn schemaName="eiblueus" tableName="TWEET"> <column name="KLOUT" type="INTEGER"/> <column name="FOLLOWERS" type="INTEGER"/> <column name="FRIENDS" type="INTEGER"/> <column name="RETWEETS" type="INTEGER"/> </addColumn> <createIndex indexName="I_TWEET_USER" schemaName="eiblueus" tableName="TWEET" unique="false"> <column name="USER" type="varchar(255)"/> </createIndex> </changeSet>
清单 6 给出了一个更复杂的 Liquibase 更改集,它向一个表添加多个新列,然后在同一个表上创建一个索引。这些操作自动关联回滚命令,但是如果需要更加定制化的行为,可以在更改集内的 <rollback> 元素中指定该行为。默认行为是撤销创建新列和索引,如果在修改模式期间发生错误,则从表中丢弃这些新列和索引。每个更改集都是以单个事务的形式被应用,如果事务执行失败,则会发生回滚。这使得开发人员能够创建不同的小模式更新,每次单独更新的危险性没有大规模表更新那么高,如果需要更新,还可执行安全检查和更高级的回滚处理措施。
清单 7. Liquibase 的完整数据库更改日志,包括前面的示例
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog/1.9" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <changeSet author="boc" id="1.0.2"> <comment>Add 2015 masters site</comment> <insert schemaName="eiblueus" tableName="sites"> <column name="NAME" value="mast"/> <column name="YEAR" valueNumeric="2015"/> <column name="URL" value="http://www.masters.com/en_US/tournament/"/> <column name="LATITUDE" valueNumeric="33.4667"/> <column name="LONGITUDE" valueNumeric="81.9667"/> <column name="TOURNAMENT_SCHEDULE_START" valueDate="2015-04-09"/> <column name="TOURNAMENT_SCHEDULE_END" valueDate="2015-04-12"/> <column name="PRELIMINARY_SCHEDULE_START" valueDate="1970-01-01"/> <column name="PRELIMINARY_SCHEDULE_END" valueDate="1970-01-01"/> </insert> </changeSet> <changeSet author="cnmcavoy" id="1.0.3"> <comment>Add twitter follower, klout, retweet data</comment> <addColumn schemaName="eiblueus" tableName="TWEET"> <column name="KLOUT" type="INTEGER"/> <column name="FOLLOWERS" type="INTEGER"/> <column name="FRIENDS" type="INTEGER"/> <column name="RETWEETS" type="INTEGER"/> </addColumn> <createIndex indexName="I_TWEET_USER" schemaName="eiblueus" tableName="TWEET" unique="false"> <column name="USER" type="varchar(255)"/> </createIndex> </changeSet> <changeSet author="cnmcavoy" id="1.0.4"> <comment>Add twitter gnip rules</comment> <createTable schemaName="eiblueus" tableName="TweetDAO_gnipRules"> <column name="TWEETDAO_ID" type="VARCHAR(255)"/> <column name="element" type="VARCHAR(255)"/> </createTable> <createIndex indexName="I_TWTDRLS_TWEETDAO_ID" schemaName="eiblueus" tableName="TweetDAO_gnipRules" unique="false"> <column name="TWEETDAO_ID" type="varchar(255)"/> </createIndex> </changeSet> <changeSet author="cnmcavoy" id="1.0.5"> <comment>Add player id index</comment> <createIndex indexName="I_PLAYERS_ID" schemaName="eiblueus" tableName="PLAYERS" unique="false"> <column name="ID" type="varchar(255)"/> </createIndex> </changeSet> <changeSet author="boc" id="1.0.6"> <comment>Add Reach Table and Indexes</comment> <createTable schemaName="eiblueus" tableName="REACH"> <column name="INSTANT" type="BIGINT"> <constraints nullable="false"/> </column> <column name="TYPE" type="VARCHAR(255)"> <constraints nullable="false"/> </column> <column name="REACH" type="BIGINT"> <constraints nullable="false"/> </column> </createTable> <addPrimaryKey columnNames="INSTANT, TYPE" schemaName="eiblueus" tableName="REACH"/> <createIndex indexName="I_REACH_INSTANT" schemaName="eiblueus" tableName="REACH" unique="false"> <column name="INSTANT" type="BIGINT"/> </createIndex> </changeSet> </databaseChangeLog>
IBM DB2 HADR 与高可用性
PCC 系统的 DB2 必须高度可用,才符合灾难避免原则。图 1 给出了数据库系统的架构。PCC 系统受到一种两站点地理区域架构的支持。因此,DB2 之间的对等复制可以确保地理区域 P1 与地理区域 P3 之间的内容是同步的,从而实现数据一致性。每个地理区域被复制到一个开发服务器 z10095。
每个复制的数据库都被用于避免灾难。如果一个数据库或两个数据库都丢失了磁盘,就会使用已复制的数据库来重建数据状态。从 P1 和 P3 的单向复制为流数据创建了一个只读数据库。开发团队可使用来自生产服务器的实时数据来测试大数据算法,测试动态实时数据的可视化。因此,在部署到生产之前,数据损坏或不受支持的格式会被检测出来。
图 1.DB2 系统在生产使用期间高度可用,并为开发用途建立了镜像。
PCC 系统需要 noSQL 数据存储,这种存储需要在数据元素内和之间建立不同类型的关系。需要对 PCC 的数据执行一般性的趋势预测,可以使用时序数据库来完成该测试。也需要按照指定的时间节奏来收集和聚合监视应用程序的内部功能的所有应用程序分析结果。下一节将探讨如何使用 Graphite、StatsD 和一个名为 Grafana 的可视化工具。
Graphite、StatsD 和 Grafana
Graphite 是一个数字时序数据库和前端,可用于执行简单的检测和测量。Graphite 有 3 个主要组件:
- Carbon 。一个监听和接受新数据的守护进程。
- Whisper 。一个设计用于高效存储时序数据的数据库。
- webapp 。提供了一个 RESTful API 和渲染功能来绘制图表。
信息可通过 AMQP 或简单 UDP 消息发送到 Carbon 守护进程。用于 Graphite 的 Whisper 数据库是一个固定大小数据库,它压缩旧指标,以便存储更新的、更高分辨率的指标。指标的准确压缩率可在存储模式中进行配置。默认存储模式如清单 8 所示。
清单 8. 默认 Carbon 存储模式配置设置
[carbon] pattern = ^carbon\. retentions = 60:90d [default_1min_for_1day] pattern = .* retentions = 60s:1d
默认配置最多存储 60 个与正则表达式 ^carbon\.
匹配的数据点,Carbon 使用它们对自己执行内部剖析。其他所有数据点与 .*
表达式相匹配,每个指标每隔 60 秒存储一次,并保留一天。这些默认设置非常适用于测试,但对于更大型的分析,需要更长的保留期限。
清单 9. Predictive Cloud Computing 系统存储模式配置设置
[carbon] pattern = ^carbon\. retentions = 60:90d [http] pattern = http\. retentions = 10s:1d,1m:30d,5m:90d [tasks] pattern = tasks\. retentions = 60s:1d,5m:30d,10m:180d [default] pattern = .* retentions = 60s:1d,5m:30d,10m:1y
清单 9 中给出的 retentions 允许保留更长时间,但也会聚合超时后的数据,以便预防 Whisper 数据库增长过大。例如,在第一天以 10 秒为间隔来聚合 HTTP 统计数据,在接下来的 30 天合并为 1 分钟间隔,随后的 90 天合并为 5 分钟间隔。最终,会从数据库完全丢弃这些统计数据。
Graphite 的另一个重要方面是 Statsd,这个守护进程监听信息并创建有意义的统计度量,比如来自简单 UDP 消息的计数器、时间和测度 (gauge)。例如,回发 “ test.metric:1:c | nc –u –w0 127.0.0.1 8125
” 将向本地机器上的 Statsd 守护进程发送一条 UDP 消息,递增指标 ‘test.metric’ 的计数器。java-statsd-client 库使得将指标从 PCC 系统发送到 Statds 和 Graphite 变得同样简单。
清单 10. 如何使用 java-statsd-client 向 StatsD 和 Graphite 发送数据
StatsDClient stats = new NonBlockingStatsDClient("test", "127.0.0.1", 8125); stats.incrementCounter("metric"); stats.close()
在 PCC 系统内,检测了所有 HTTP 请求来度量完成请求所需的时间,以及返回 2XX(成功)还是错误(4XX 或 5XX)HTTP 状态代码来响应请求。JavaX servlet 过滤器使得解释对应用程序中每个 JaxRS 端点的请求变得很简单。
清单 11. 检测对 Predictive Cloud Computing 系统的 HTTP 调用的 JavaX 过滤器
public class GraphiteStatusFilter implements Filter { private TournamentConfiguration config; @Override public void init(FilterConfig filterConfig) throws ServletException { this.config = (TournamentConfiguration) filterConfig.getServletContext().getAttribute(AbstractBigEngineJob.CONFIG_CONTEXT_KEY); } @Override public void doFilter(ServletRequest request, ServletResponse resp, FilterChain chain) throws IOException, ServletException { final StatsDClient stats; if (request instanceof HttpServletRequest) { HttpServletRequest req = (HttpServletRequest)request; stats = getStatsDClient(req); } else { stats = new NoOpStatsDClient(); } stats.increment("request"); final Stopwatch time = Stopwatch.createStarted(); try { chain.doFilter(request, resp); if (resp instanceof HttpServletResponse) { HttpServletResponse response = (HttpServletResponse) resp; if (response.getStatus() / 100 == 2) { stats.increment("success"); } else { stats.increment("failure"); } } } catch (IOException e) { stats.increment("failure"); throw e; } catch (ServletException e) { stats.increment("failure"); throw e; } finally { stats.recordExecutionTime("execution", (int) time.elapsed(TimeUnit.MILLISECONDS)); stats.stop(); } } private StatsDClient getStatsDClient(HttpServletRequest req) { if (config != null && config.getSite() != null) { String siteName = config.getSite().getName(); String siteYear = String.valueOf(config.getSite().getYear()); if (req.getQueryString() != null) { final Map<String, String> map = QueryParams.parseQueryParameters(req.getQueryString()); if (map.containsKey("site") && map.containsKey("year")) { siteName = map.get("site"); siteYear = map.get("year"); } } final StringBuilder statsPrefix = new StringBuilder(config.getPlexID()).append("."); statsPrefix.append(siteName).append("."); statsPrefix.append(siteYear).append(".http."); statsPrefix.append(req.getPathInfo()).append(".").append(req.getMethod().toLowerCase()); try { return new NonBlockingStatsDClient(statsPrefix.toString(), config.getRemoteDataAggregator().getHost(), config.getRemoteDataAggregator().getPort()); } catch (StatsDClientException e) { } } return new NoOpStatsDClient(); } @Override public void destroy() { } }
在收集统计数据后,所检测的代码库就会分析或可视化统计值,以便供人类使用。Graphite 的 Web 应用程序提供了一个图形渲染功能有限的前端,但 Graphite 数据有更强大的开源前端,比如 Grafana。Grafana 是一个为指标创建仪表板的工具,可以将它插入许多后端数据库中,包括 Graphite。
图 2.一个显示服务器信息的 Grafana 仪表板
使用 Graphite 进行特征提取可视化
PCC 系统中的预测建模应用了一种多元回归模型,该模型已使用多样化的独立变量进行了训练。该模型的输入是从一次未来的模拟比赛中提取的特征,包括比赛统计数据、社会流行度和 Web 服务器行为。模型的输出生成预期的原始服务器负载的单一数值表示。该模型与传统循环预测从整体相结合,以解释网球或高尔夫球内容的需求峰值或高需求时段。
所有特征提取器都将数量和时间统计数据发送给 Graphite。例如,每次将 Tennis or Golf Twitter Count Factor 应用于一次模拟比赛时,都会将一个时间和数量指标推送到 Graphite 数据存储中。每个算法都被包装到 Unstructured Information Management Architecture—Asynchronous Scaleout 开源项目中。最终,许多算法并行运行,导致大量消息被发送到 Graphite。
清单 12. 预测未来一次比赛期间针对每位运动员的推文数量的算法
public void process(JCas jCas) throws AnalysisEngineProcessException { logger.debug("UIMA: Running Twitter Count Factor"); StatsDClient statsCount = new NoOpStatsDClient(); StatsDClient statsRunTime = new NoOpStatsDClient(); StatsDClient statsContributeCount = new NoOpStatsDClient(); TournamentConfiguration tournamentConfig = null; AnnotationIndex<Annotation> dataAggIndex = jCas.getAnnotationIndex(com.ibm.ei.zepplin.factors.common.model.RemoteDataAggregator.type); Iterator<Annotation> dataAggIt = dataAggIndex.iterator(); if (dataAggIt.hasNext()) { RemoteDataAggregator remoteProps = (RemoteDataAggregator) dataAggIt.next(); String countTag = FactorState.buildTag(remoteProps.getHostTag(), FactorState.SOCIAL, FactorState.COUNT); statsCount = getStatsDClient(countTag, remoteProps.getHostName(), remoteProps.getHostPort()); String runTimeTag = FactorState.buildTag(remoteProps.getHostTag(), FactorState.SOCIAL, FactorState.RUNTIME); statsRunTime = getStatsDClient(runTimeTag, remoteProps.getHostName(), remoteProps.getHostPort()); String runContribCountTag = FactorState.buildTag(remoteProps.getHostTag(), FactorState.SOCIAL, FactorState.CONTRIBUTE, FactorState.COUNT); statsContributeCount = getStatsDClient(runContribCountTag, remoteProps.getHostName(), remoteProps.getHostPort()); } Stopwatch stopwatch = Stopwatch.createStarted(); … stopwatch.stop(); statsRunTime.recordExecutionTime(this.getClass().getSimpleName(), (int) (stopwatch.elapsed(TimeUnit.MILLISECONDS))); statsCount.increment(this.getClass().getSimpleName()); statsContributeCount.increment(this.getClass().getSimpleName());
每场高尔夫球比赛都利用了总共 15 个不同的特征提取器,而网球比赛实现了 18 种不同的算法。对结果数据趋势的不断预测提供了两个功能。第一个功能是一个可视界面,供最终用户和系统管理员理解每个算法的处理比例和时间。图 3 描绘了该可视界面,其中的泡泡图显示了处理时间,圆环图是发送到每种算法的消息比例。
图 3.圆环图显示了 UIMA-AS 内的消息比例
第二个功能是调试应用程序。本文前面提到的 Grafana 为每个算法提供了运行时间和流量摘要。如果一个算法运行时间太长,代码可能会收到太多消息,以至于 UIMA-AS 需要将它们重新分发到多个机器上。在某些情况下,还可以优化一种算法来确保获得近实时的运算。如果流量为 0,或者与其他算法相比,流量相对降低了,则会发现其他类型的系统和代码级错误。该症状通常意味着一个算法在执行分页且需要更多内存,或者内存泄漏降低了特征提取器的性能。
结束语
在本教程中,我们展示了 PCC 如何使用基于 SQL 的存储(比如 DB2)和 noSQL 存储(比如 Graphite)。在代码内,JPA2 和 Liquibase 的使用使得数据库更改变得可追溯、可重复且可靠。作为时序数据库,noSQL 数据存储 Graphite 非常有用,它支持 PCC 的分析组件,而且支持诊断存在问题的运行时症状。一些示例、代码块和配置描述了实际的实现细节。
在本系列的第 8 部分中,将讨论整个 PCC 系统中使用的分布式特征提取和预测模型。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
随机密码生成器
多种字符组合密码
html转js在线工具
html转js在线工具