需求:
①日志生成:
采集订单系统应用打印的日志文件
日志文件使用log4j生成,滚动生成
处理流程:
1、Spout获取外部数据源,数据源是订单的mq,mq有固定的格式,比如json串。
2、对订单mq进行解析,得到一个对象->JavaBean
订单编号、订单时间、支付编号、支付时间、商品编号、商家名称、商品价格、优惠价格、支付金额
3、对指标进行计数
//业务中一个订单包含多个商品,需要对每个商品进行指标计算
//创建订单和取消订单两种类型,在计算总数据的是考虑将取消订单的金额减掉
//订单中有拆单的逻辑,该如何计算
4、保存指标数据到Redis
sz file.type 将文件下载到本地磁盘c盘。
日志监控:
需求分析:①用flume监控日志文件;②用Kafka读取日志数据;③storm数据处理后输出到redis;④redis存储数据查看!
实现流程:
(一)实现flume监控log文件夹下的text.log文件
实现flume的
exec.conf脚本如下:
# Name the components on this agent
a1.sources = r1
a1.sinks = k1
a1.channels = c1
a1.sources.r1.type = exec
a1.sources.r1.command = tail -F /home/hadoop/log/1.log #指定了监控的日志文件
a1.sources.r1.channels = c1
# Describe the sink
#下沉目标
a1.sinks.k1.type =org.apache.flume.sink.kafka.KafkaSink #指定了下沉目标是Kafka
a1.sinks.k1.topic=bbb #指定了flume输入到Kafka的数据的topic
a1.sinks.k1.brokerList=server:9092
a1.sinks.k1.requiredAcks=1
a1.sinks.k1.batchSize=20
a1.sinks.k1.channel = c1
# Use a channel which buffers events in memory
a1.channels.c1.type = memory
a1.channels.c1.capacity = 10000
a1.channels.c1.transactionCapacity = 100
保存后执行: ./bin/flume-ng agent -n a1 -c conf -f
conf/myconf/exec.conf -Dflume.root.logger=INFO,console
即完成对指定日志文件的监控!!
备注:对于Kafka来说,flume就是数据的producer,对Kafka一直生产数据
(二)Kafka消费数据
①启动Kafka消费命令之前,先在每台机器上启动zookeeper
执行:zkServer.sh start 然后用zkServer.sh status来查看状态
②在三台机器上启动Kafka服务:nohup bin/kafka-server-start.sh config/server.properties &
③在主机的客户端执行消费的console命令:
bin/kafka-
console
-consumer.sh --zookeeper server:
2181
--topic bbb #
即在客户端看到添加的数据。
总体数据的流向: 日志文件--> source --flume_channel -- sink -->Kafka的consumer-->KafkaSpout--storm-->Bolt---->redis
<测试好以上两步,接下来需要写的是storm的逻辑代码:Spout采用KafkaSpout实例,Bolt的executor()方法将数据输出测试,或其他逻辑>
(三)storm处理数据
编写好代码后
首先启动storm,(zookeeper已经启动)
server: nohup ./storm nimbus &
三台机器上server,server02,server03执行:nohup ./storm supervisor &
server:执行nohup ./storm ui &
接下来完成storm端的处理:可以本地运行,也可以打包集群运行。
备注:此处的打包是maven打包,打包方法:
maven工程的pom.xml右击选择maven install才是在target里打包!
集群运行模式:上传该依赖包到服务器。
执行
storm jar test.jar pacakge.class
注意:provided调整,集群运行模式调整
至此storm启动完成,数据由Kafka的consumer--输入到Spout--Bolt处理
(四)数据输出到redis数据库
视频中测试的内容是:
①编写了Kafka的producer程序,即将订单数据远远不断的生产,发送到消费者。(并且例子中用消费者控制台也观察到数据的写入)
public class OrderMqSender {
public static void main(String[] args) {
String TOPIC = "ccc";
Properties props = new Properties();
props.put("serializer.class", "kafka.serializer.StringEncoder");
props.put("metadata.broker.list", "server:9092,server02:9092,server03:9092"); //配置Kafka的集群, 端口:9092
props.put("request.required.acks", "1");
props.put("partitioner.class", "kafka.producer.DefaultPartitioner");
//配置好集群,创建一个producer向Kafka写入数据
Producer<String, String> producer = new Producer<String, String>(new ProducerConfig(props));
for (int messageNo = 1; messageNo < 100000; messageNo++) {
producer.send(new KeyedMessage<String, String>(TOPIC, messageNo + "",new OrderInfo().random() ));
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
②消费者:没用到,主要是在客户端启动来查看是否已经写入数据
bin/kafka-console-consumer.sh --zookeeper server:2181 --topic ccc
③storm中编写spout,
用KafkaSpout实例,即将Kafka的consumer作为输入的数据源来读取。所有的集群topic信息都已经在KafkaSpout中配置好
public class KafkaAndStormTopologyMain {
public static void main(String[] args) throws Exception{
//和普通storm程序设置相同,只是spout源改掉,改为KafkaSpout
TopologyBuilder topologyBuilder = new TopologyBuilder();
topologyBuilder.setSpout("kafkaSpout",
new KafkaSpout(new SpoutConfig(new ZkHosts("server:2181,server02:2181,server03:2181"),
"ccc",
"/myKafka", "kafkaSpout")),1);topologyBuilder.setBolt("mybolt1",new ParserOrderMqBolt(),1).shuffleGrouping("kafkaSpout");
Config config = new Config();
config.setNumWorkers(1);
//3、提交任务 -----两种模式 本地模式和集群模式
if (args.length>0) {
StormSubmitter.submitTopology(args[0], config, topologyBuilder.createTopology());
}else {
LocalCluster localCluster = new LocalCluster();
localCluster.submitTopology("storm2kafka", config, topologyBuilder.createTopology());
}
}
}
③在Bolt组件中,主要设定bolt的执行以及输出为redis的window的客户端
public class ParserOrderMqBolt extends BaseRichBolt {
private JedisPool pool;
@Override
public void prepare(Map stormConf, TopologyContext context, OutputCollector collector) {
//change "maxActive" -> "maxTotal" and "maxWait" -> "maxWaitMillis" in all examples
JedisPoolConfig config = new JedisPoolConfig();
//控制一个pool最多有多少个状态为idle(空闲的)的jedis实例。
config.setMaxIdle(5);
//控制一个pool可分配多少个jedis实例,通过pool.getResource()来获取;
//如果赋值为-1,则表示不限制;如果pool已经分配了maxActive个jedis实例,则此时pool的状态为exhausted(耗尽)。
//在borrow一个jedis实例时,是否提前进行validate操作;如果为true,则得到的jedis实例均是可用的;
config.setMaxTotal(1000 * 100);
//表示当borrow(引入)一个jedis实例时,最大的等待时间,如果超过等待时间,则直接抛出JedisConnectionException;
config.setMaxWaitMillis(30);
config.setTestOnBorrow(true);
config.setTestOnReturn(true);
/**
*如果你遇到 java.net.SocketTimeoutException: Read timed out exception的异常信息
*请尝试在构造JedisPool的时候设置自己的超时值. JedisPool默认的超时时间是2秒(单位毫秒)
*/
pool = new JedisPool(config, "127.0.0.1", 6379);
}
@Override
public void execute(Tuple input) {
Jedis jedis = pool.getResource();
//获取kafkaSpout发送过来的数据,是一个json
String string = new String((byte[]) input.getValue(0));
//解析json
OrderInfo orderInfo = (OrderInfo) new Gson().fromJson(string, OrderInfo.class);
//整个网站,各个业务线,各个品类,各个店铺,各个品牌,每个商品
//获取整个网站的金额统计指标 //redis中执行 get totalAmount
// String totalAmount = jedis.get("totalAmount");
jedis.incrBy("totalAmount",orderInfo.getProductPrice());
//获取商品所属业务线的指标信息
String bid = getBubyProductId(orderInfo.getProductId(),"b");
// String bAmout = jedis.get(bid+"Amout");
jedis.incrBy(bid+"Amount",orderInfo.getProductPrice());
// jedis.close();
}
private String getBubyProductId(String productId,String type) {
// key:value
//index:productID:info---->Map
// productId-----<各个业务线,各个品类,各个店铺,各个品牌,每个商品>
Map<String,String> map = new HashMap<>();
map.put("b","3c");
map.put("c","phone");
map.put("s","121");
map.put("p","iphone");
return map.get(type);
}
@Override
public void declareOutputFields(OutputFieldsDeclarer declarer) {
}
}
在Windows客户端执行get
totalAmount 或 get
Amount 来获取key-Value键值对信息
prepare()初始化方法里设置redis以及
(五)LearnRedis
先启动redis-server 再启动redis-cli 即先启动服务,后启动客户端查询!
get
totalAmount //获得简键值对信息
get
Amount