在本文中,我们将结合IBM™Persistence API使用IBM DB2作为数据源,描述PCC系统中的数据存储。 此外,我们讨论了如何使用Graphite来检测代码库和工作负载。 最后,我们描述了用于分析数据的工具。
IBM DB2和Java Persistence API
DB2是IBM关系数据库服务器,已在整个PCC系统中广泛用作持久数据存储。 我们往返于DB2的主要接口是通过Java的持久性API(JPA2)。 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中,每个持久性单元都描述了一个JPA数据源。 该提供程序包含JPA实现的类名,并充当数据源初始化的入口点。 在提供者之后列出的是OpenJPA编译阶段应增强的类。 这些类是映射到关系数据的Java对象。 它们将在本文后面进一步详细探讨。 最后,在最后的properties元素中指定任何自定义配置属性。 在属性中可以配置诸如连接URL,数据库模式名称和缓存配置之类的详细信息。 应该将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增强程序有关对象以及对象映射哪些数据库信息或架构的详细信息的主要方法。 在最上面,@ @Entity
告诉OpenJPA该类表示数据库关系,并且@Table(name = "PLAYERS")
添加有关应该从中检索信息的表的信息。 因此,每个PlayerDAO对象都代表PLAYERS表中的一行。 @IdClass(PlayerId.class)
注释告诉OpenJPA在哪里可以找到代表表的组合键的类。 复合主键包含数据库中一个表的多个字段,以检查行的唯一性。 我们为玩家使用了组合键,因此锦标赛中的每个玩家将在PLAYERS表中单独占据一行。
根据Java Persistence API规范,每个实体都必须实现Serializable接口,而我们的实体还必须实现Player,以提供对许多getter和setter方法的可见性。 PlayerDAO中的字段也被注释, @Id
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表中选择与名称ausopen
(澳大利亚公开赛)相匹配并在2016年期间正在播放的所有行。此查询将返回多个结果,因此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年Masters锦标赛添加到名为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的可视化工具的使用。
石墨,StatsD和Grafana
Graphite是一个数字时间序列数据库和前端,可实现简单的仪表和测量。 石墨具有三个主要成分:
- 碳 。 侦听并接受新数据的守护程序。
- 耳语 。 设计用于有效存储时间序列数据的数据库。
- 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\.
,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中描述的保留允许更长的保留,但也可以在经过一段时间后聚合数据,以防止Whisper数据库过大。 例如,HTTP统计信息在第一天被汇总为10s间隔,然后在接下来的30天中合并为1分钟间隔,在接下来的90天中合并为5分钟间隔。 最终将它们完全从数据库中删除。
Graphite的其他重要方面之一是Statsd,它是一个守护程序,用于侦听信息并根据简单的UDP消息创建有意义的统计度量,例如计数器,时间和量规。 例如,echo“ 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.检测HTTP的JavaX筛选器为Predictive Cloud Computing系统
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仪表板

用于特征提取可视化的石墨
PCC系统中的预测模型应用了多元回归模型,该模型经过一系列独立变量的训练。 模型的输入是从模拟游戏锦标赛中提取的功能,包括游戏统计,社交知名度和Web服务器行为。 模型的输出产生了预期的原始服务器负载的单个数字表示。 总体而言,该模型与传统的周期性预测相结合,可以说明网球或高尔夫内容的激增或高需求时期。
每个特征提取器都将计数和计时统计信息发送给Graphite。 例如,每次将网球或高尔夫Twitter计数因子应用于模拟游戏比赛时,时间和计数指标都会被推入Graphite数据存储中。 每种算法都包装在“非结构化信息管理体系结构-异步扩展”开源项目中。 结果,许多算法并行运行,这导致大量消息发送到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。 或者,在几种情况下对算法进行了优化,以确保接近实时的操作。 如果流量为零或与其他算法相比相对减少,则会发现其他类型的系统和代码级错误。 症状通常表示算法正在分页并且需要更多内存,或者内存泄漏正在损害特征提取器。
结论
在本教程中,我们展示了PCC如何使用基于SQL的存储(例如DB2)和noSQL存储(例如Graphite)。 在代码内,使用启用了JPA2和Liquibase的数据库更改可追溯,可重复且可靠。 noSQL数据存储Graphite作为一个时间序列数据库非常有价值,该数据库支持PCC的分析组件并用于诊断有问题的运行时症状。 几个示例,代码块和配置描述了具体的实现细节。
在系列的第8部分中,我们将讨论在整个PCC系统中使用的分布式特征提取和预测模型。