MapReduce词频统计演练进阶

news2024/11/25 20:46:03

 

目录

创建词频统计映射器类

创建词频统计驱动器类

第一次测试运行 

修改词频统计映射器类WordCoutMapper类

修改词频统计驱动器WordCountDriver类

 第二次测试运行

创建词频统计归并器类

第三测试运行

修改词频统计归并器类

 第四次测试运行

修改词频统计驱动器类,设置分区数量

 第五次测试运行

打包jar包上传hadoop运行

创建新词频统计驱动器类

将三个类合并成一个类完成词频统计

在上一篇中我们体验了MapReduce的基本流程所以本次将会在此基础上再进行词频统计的深入演练

Hadoop的MapReduce基本流程体验_open_test01的博客-CSDN博客

创建词频统计映射器类

在项目包中新建WordCountMapper类

继承泛型参数解读:

KEYIN :输入的key类型:LongWritable

VALUEIN:输入的value类型:Text

KEYOUT:输出的key类型:LongWritable

VALUEOUT:输出的value类型:Text

import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;

import java.io.IOException;

public class WordCountMapper extends Mapper<LongWritable, Text, LongWritable, Text> {
    @Override
    protected void map(LongWritable key, Text value, Context context)
            throws IOException, InterruptedException {
        // 直接将键值对数据传到下一个阶段
        context.write(key, value);
    }
}

创建词频统计驱动器类

在项目包中新建WordCountDriver类

这里使用BigData目录中的数据进行输入使用

 输出位置为新建的output目录

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FSDataInputStream;
import org.apache.hadoop.fs.FileStatus;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IOUtils;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;

import java.net.URI;

public class WordCountDriver {
    public static void main(String[] args) throws Exception {
        // 创建配置对象
        Configuration conf = new Configuration();
        // 设置数据节点主机名属性
        conf.set("dfs.client.use.datanode.hostname", "true");

        // 获取作业实例
        Job job = Job.getInstance(conf);
        // 设置作业启动类
        job.setJarByClass(WordCountDriver.class);

        // 设置Mapper类
        job.setMapperClass(WordCountMapper.class);
        // 设置map任务输出键类型
        job.setMapOutputKeyClass(LongWritable.class);
        // 设置map任务输出值类型
        job.setMapOutputValueClass(Text.class);

        // 定义uri字符串
        String uri = "hdfs://master:9000";
        // 创建输入目录
        Path inputPath = new Path(uri + "/BigData");
        // 创建输出目录
        Path outputPath = new Path(uri + "/output");

        // 获取文件系统
        FileSystem fs =  FileSystem.get(new URI(uri), conf);
        // 删除输出目录(第二个参数设置是否递归)
        fs.delete(outputPath, true);

        // 给作业添加输入目录(允许多个)
        FileInputFormat.addInputPath(job, inputPath);
        // 给作业设置输出目录(只能一个)
        FileOutputFormat.setOutputPath(job, outputPath);

        // 等待作业完成
        job.waitForCompletion(true);

        // 输出统计结果
        System.out.println("======统计结果======");
        FileStatus[] fileStatuses = fs.listStatus(outputPath);
        for (int i = 1; i < fileStatuses.length; i++) {
            // 输出结果文件路径
            System.out.println(fileStatuses[i].getPath());
            // 获取文件系统数据字节输入流
            FSDataInputStream in = fs.open(fileStatuses[i].getPath());
            // 将结果文件显示在控制台
            IOUtils.copyBytes(in, System.out, 4096, false);
        }
    }
}

第一次测试运行 

 注意:运行时先检查本地主机hadoop环境是否正常

 hadoop解压到本机目录下

 

本地环境变量配置

 下载对应版本的winutils.exehadoop.dll,放在hadoop安装目录的bin子目录里

  • winutils/winutils.exe at master · cdarlint/winutils · GitHub

  •  https://github.com/cdarlint/winutils/blob/master/hadoop-3.2.2/bin/hadoop.dll

 

 再将hadoop.dll放到C:/windows/system32文件夹下

 本地运行环境没有问题那么我们就可以开始运行了

查看WordCountDriver类在IDEA的运行结果

 如果不想看到统计结果之前的大堆信息 可以修改log4j.properties文件,将INFO改为ERROR

在Hadoop WebUI界面查看结果文件

 

修改词频统计映射器类WordCoutMapper类

  • 行首数字对于我们做单词统计没有任何用处,只需要拿到每一行内容,按空格拆分成单词,每个单词计数1,因此,WordCoutMapper的输出应该是单词个数,于是,输出键类型为Text,输出值类型为IntWritable
  • 将每行按空格拆分成单词数组,输出<单词, 1>的键值对

 修改该方法的泛型参数键值类型

public class WordCountMapper extends Mapper<LongWritable, Text, Text, IntWritable> {
    @Override
    protected void map(LongWritable key, Text value, Context context)
            throws IOException, InterruptedException {
        // 获取行内容
        String line = value.toString();
        // 按空格拆分得到单词数组
        String[] words = line.split(" ");
        // 遍历单词数组,生成输出键值对
        for (int i = 0; i < words.length; i++) {
            context.write(new Text(words[i]), new IntWritable(1));
        }
    }
}

修改词频统计驱动器WordCountDriver类

修改map任务输出键值类型使其与WordCoutMapper类泛型参数键值类型对应

 第二次测试运行

输出结果以键值对形式出现

 

对于这样一组键值对,传递到reduce阶段,按键排序,其值构成迭代器

   <1>

af <1,1,1>

as <1,1>

faf <1>

gaf <1>

映射任务与归并任务示意图

 

创建词频统计归并器类

在项目包中创建WordCountReducer类

 

import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

public class WordCountReducer extends Reducer<Text, IntWritable, Text, Text> {
    @Override
    protected void reduce(Text key, Iterable<IntWritable> values, Context context)
            throws IOException, InterruptedException {
        // 定义整数数组列表
        List<Integer> integers = new ArrayList<>();
        // 遍历输入值迭代器
        for (IntWritable value : values) {
            // 将每个值添加到数组列表
            integers.add(value.get()); // 利用get()方法将hadoop数据类型转换成java数据类型
        }
        // 输出新的键值对,注意要将java字符串转换成hadoop的text类型
        context.write(key, new Text(integers.toString()));
    }
}

修改 WordCountDriver类与WordCoutMapper类中的泛型参数键值类型

设置词频统计的Reducer类及其输出键类型和输出值类型(Text,Text)

在 WordCountDriver类中写入Reducer输出设置

第三测试运行

现在我们需要修改词频统计归并器,将每个键(单词)的值迭代器进行累加,得到每个单词出现的总次数

 

修改词频统计归并器类

修改WordCountReducer类

输出键值类型改为IntWritable,遍历值迭代器,累加得到单词出现次数

import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;

import java.io.IOException;

public class WordCountReducer extends Reducer<Text, IntWritable, Text, IntWritable> {
    @Override
    protected void reduce(Text key, Iterable<IntWritable> values, Context context)
            throws IOException, InterruptedException {
        // 定义键出现次数
        int count = 0;
        // 遍历输入值迭代器
        for (IntWritable value : values) {
            count += value.get(); // 其实针对此案例,可用count++来处理
        }
        // 输出新的键值对,注意要将java的int类型转换成hadoop的IntWritable类型
        context.write(key, new IntWritable(count));
    }
}

 修改WordCountDriver类

 第四次测试运行

可以看到每个单词出现的次数

 

修改词频统计驱动器类,设置分区数量

写入WordCountDriver类

 第五次测试运行

 并产生了3个结果文件

 

打包jar包上传hadoop运行

 

 拷贝WordCountDriverr 驱动类全类名

 hadoop中执行命令运行jar包

执行命令

 hadoop jar MRWordCount-1.0-SNAPSHOT.jar mpr.WordCountDriver
 
 hadoop jar 使用的jar包 全类名

 

 

 

 运行结果如下

 

这样可以执行但是路径却是被写死了 我们用一种给main传递路径参数args[索引]

所以下面我们将对其修改

创建新词频统计驱动器类

创建一个新的WordCountDriver类 并命名为WordCountDriverNew

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FSDataInputStream;
import org.apache.hadoop.fs.FileStatus;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IOUtils;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;

import java.net.URI;

public class WordCountDriverNew {
    public static void main(String[] args) throws Exception {
        // 创建配置对象
        Configuration conf = new Configuration();
        // 设置数据节点主机名属性
        conf.set("dfs.client.use.datanode.hostname", "true");

        // 获取作业实例
        Job job = Job.getInstance(conf);
        // 设置作业启动类
        job.setJarByClass(WordCountDriverNew.class);

        // 设置Mapper类
        job.setMapperClass(WordCountMapper.class);
        // 设置map任务输出键类型
        job.setMapOutputKeyClass(Text.class);
        // 设置map任务输出值类型
        job.setMapOutputValueClass(IntWritable.class);

        // 设置Reducer类
        job.setReducerClass(WordCountReducer.class);
        // 设置reduce任务输出键类型
        job.setOutputKeyClass(Text.class);
        // 设置reduce任务输出值类型
        job.setOutputValueClass(IntWritable.class);

        // 设置分区数量(reduce任务的数量,结果文件的数量)
        job.setNumReduceTasks(3);

        // 定义uri字符串
        String uri = "hdfs://master:9000";
        // 声明输入目录
        Path inputPath = null;
        // 声明输出目录
        Path outputPath = null;
        // 判断输入参数个数
        if (args.length == 0) {
            // 创建输入目录
            inputPath = new Path(uri + "/wordcount/input");
            // 创建输出目录
            outputPath = new Path(uri + "/wordcount/output");
        } else if (args.length == 2) {
            // 创建输入目录
            inputPath = new Path(uri + args[0]);
            // 创建输出目录
            outputPath = new Path(uri + args[1]);
        } else {
            // 提示用户参数个数不符合要求
            System.out.println("参数个数不符合要求,要么是0个,要么是2个!");
            // 结束应用程序
            return;
        }

        // 获取文件系统
        FileSystem fs = FileSystem.get(new URI(uri), conf);
        // 删除输出目录(第二个参数设置是否递归)
        fs.delete(outputPath, true);

        // 给作业添加输入目录(允许多个)
        FileInputFormat.addInputPath(job, inputPath);
        // 给作业设置输出目录(只能一个)
        FileOutputFormat.setOutputPath(job, outputPath);

        // 等待作业完成
        job.waitForCompletion(true);

        // 输出统计结果
        System.out.println("======统计结果======");
        FileStatus[] fileStatuses = fs.listStatus(outputPath);
        for (int i = 1; i < fileStatuses.length; i++) {
            // 输出结果文件路径
            System.out.println(fileStatuses[i].getPath());
            // 获取文件系统数据字节输入流
            FSDataInputStream in = fs.open(fileStatuses[i].getPath());
            // 将结果文件显示在控制台
            IOUtils.copyBytes(in, System.out, 4096, false);
        }
    }
}

然后重新打包上传到hadoop执行

执行命令 这次将可以指定路径进行运行

 hadoop jar MRWordCount-1.0-SNAPSHOT.jar /BigData /outputs

 hadoop jar 使用的jar包 全类名 /输入数据原文件 /输出路径目录

 

 在webUI上查看运行结果

 

将三个类合并成一个类完成词频统计

新建WordCount类 并将功能合并 处理词频统计任务

 

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.conf.Configured;
import org.apache.hadoop.fs.FSDataInputStream;
import org.apache.hadoop.fs.FileStatus;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IOUtils;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.Reducer;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import org.apache.hadoop.util.Tool;
import org.apache.hadoop.util.ToolRunner;

import java.io.IOException;
import java.net.URI;

public class WordCount extends Configured implements Tool {

    public static class WordCountMapper extends Mapper<LongWritable, Text, Text, IntWritable> {
        @Override
        protected void map(LongWritable key, Text value, Context context)
                throws IOException, InterruptedException {
            // 获取行内容
            String line = value.toString();
            // 清洗所有英文标点符号(\p——属性[property],P——标点符号[Punctuation])
            line = line.replaceAll("[\\pP]", "");
            // 按空格拆分得到单词数组
            String[] words = line.split(" ");
            // 遍历单词数组,生成输出键值对
            for (int i = 0; i < words.length; i++) {
                context.write(new Text(words[i]), new IntWritable(1));
            }
        }
    }

    public static class WordCountReducer extends Reducer<Text, IntWritable, Text, IntWritable> {
        @Override
        protected void reduce(Text key, Iterable<IntWritable> values, Context context)
                throws IOException, InterruptedException {
            // 定义输出键出现次数
            int count = 0;
            // 历输出值迭代对象,统计其出现次数
            for (IntWritable value : values) {
                count = count + value.get();
            }
            // 生成键值对输出
            context.write(key, new IntWritable(count));
        }
    }

    @Override
    public int run(String[] strings) throws Exception {
        // 创建配置对象
        Configuration conf = new Configuration();
        // 设置数据节点主机名属性
        conf.set("dfs.client.use.datanode.hostname", "true");

        // 获取作业实例
        Job job = Job.getInstance(conf);
        // 设置作业启动类
        job.setJarByClass(WordCountDriver.class);

        // 设置Mapper类
        job.setMapperClass(WordCountMapper.class);
        // 设置map任务输出键类型
        job.setMapOutputKeyClass(Text.class);
        // 设置map任务输出值类型
        job.setMapOutputValueClass(IntWritable.class);

        // 设置Reducer类
        job.setReducerClass(WordCountReducer.class);
        // 设置reduce任务输出键类型
        job.setOutputKeyClass(Text.class);
        // 设置reduce任务输出值类型
        job.setOutputValueClass(IntWritable.class);

        // 设置分区数量(reduce任务的数量,结果文件的数量)
        job.setNumReduceTasks(3);

        // 定义uri字符串
        String uri = "hdfs://master:9000";
        // 创建输入目录
        Path inputPath = new Path(uri + "/wordcount2/input");
        // 创建输出目录
        Path outputPath = new Path(uri + "/wordcount2/output");

        // 获取文件系统
        FileSystem fs = FileSystem.get(new URI(uri), conf);
        // 删除输出目录(第二个参数设置是否递归)
        fs.delete(outputPath, true);

        // 给作业添加输入目录(允许多个)
        FileInputFormat.addInputPath(job, inputPath);
        // 给作业设置输出目录(只能一个)
        FileOutputFormat.setOutputPath(job, outputPath);

        // 等待作业完成
        boolean res = job.waitForCompletion(true);

        // 输出统计结果
        System.out.println("======统计结果======");
        FileStatus[] fileStatuses = fs.listStatus(outputPath);
        for (int i = 1; i < fileStatuses.length; i++) {
            // 输出结果文件路径
            System.out.println(fileStatuses[i].getPath());
            // 获取文件系统数据字节输入流
            FSDataInputStream in = fs.open(fileStatuses[i].getPath());
            // 将结果文件显示在控制台
            IOUtils.copyBytes(in, System.out, 4096, false);
        }

        if (res) {
            return 0;
        } else {
            return -1;
        }
    }

    public static void main(String[] args) throws Exception {
        int res = ToolRunner.run(new WordCount(), args);
        System.exit(res);
    }
}

查看运行结果

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/89140.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

模拟实现字符串相关函数

上篇的延续 assert #include <stdio.h> #include <string.h> #include <assert.h> void my_strcpy(char* dest, char* src) {assert(src!NULL);//断言 满足某个条件 不允许发生什么事情的时候用断言assert(dest!NULL);while (*dest *src)//非0为真 0为假…

一文搞懂Vue2源码实现原理~ 手写Vue2系列~

Iterator&#xff08;遍历器&#xff09;的概念 JavaScript 原有的表示“集合”的数据结构&#xff0c;主要是数组&#xff08;Array&#xff09;和对象&#xff08;Object&#xff09;&#xff0c;ES6 又添加了Map和Set。这样就有了四种数据集合&#xff0c;用户还可以组合使…

KEIL调试正在运行的程序

问题现象 有时程序跑着跑着就飞了&#xff0c;但此时却没有接仿真器&#xff0c;不能停下来看运行状态。如果重新启动调试&#xff0c;会破坏现场。有没有办法attach到应用程序上调试呢&#xff1f; 答案是肯定的。 解决措施 在调试选项中&#xff0c;取消以下选择项 Option…

vue使用docker+node+nginx+linux自动化部署

假定你已经有一个vue项目了&#xff0c;并且已经用github进行管理了&#xff0c; 假定你还有一台免费的linux服务器&#xff0c;想用自动化部署的方式解放双手&#xff0c; 假定你已经了解dockerhub使用&#xff0c;想玩转docker容器&#xff1a;docker构建vue项目镜像并发布…

【QT 5 学习笔记-学习绘图相关+画图形图片等+绘图设备+基础学习(2)】

【QT 5 学习笔记-学习绘图相关画图形图片等绘图设备基础学习&#xff08;2&#xff09;】1、说明2、实验环境3、参照连接4、自己的学习与理解5、学习与实践代码&#xff08;1&#xff09;移动图片测试实验&#xff08;1&#xff09;继续之前的工程&#xff08;2&#xff09;引入…

不知道变年轻特效软件有哪些?这些有趣的app建议收藏

现在刷短视频几乎已经成为我们消遣时间的主要项目之一&#xff0c;因为里面涵盖了多种方面的内容&#xff0c;例如情景短剧、知识点讲解、酷炫的卡点视频、有趣的照片特效等等&#xff0c;能满足不同人群的喜好。 而最近变年轻的特效再次流行起来&#xff0c;你们是不是跟我一样…

K_A09_008 基于 STM32等单片机驱动ES08A SG90舵机按键控制正反转

目录 一、资源说明 二、基本参数 参数 型号&#xff1a;SG90 型号&#xff1a;ES08A 引脚说明 三、驱动说明 SG90舵机 ES08A 舵机 对应程序: 四、部分代码说明 接线说明 STC89C52RCES08A SG90舵机 STM32F103C8T6ES08A SG90舵机 五、基础知识学习与相关资料下载 六、视频…

AI遮天传 DL-深度学习在自然语言中的应用

本文简要介绍一些深度学习在自然语言应用的基本任务&#xff0c;词表示&#xff0c;文本翻译和机器翻译。 一、典型任务 词性标注和句法分析问答和对话系统文本/文档分类情感分析和观点挖掘机器翻译文本生成......1.1 词性标注和句法分析 词性(POS)标注即对句子里的每个词给出…

WPF入门第二篇 MVVM与Binding

MVVM与Binding MVVM&#xff0c;即Model-View-ViewModel的首字母缩写&#xff0c;在这种开发模式下常用binding来对View和ViewModel进行绑定。 添加三个文件夹&#xff0c;分别命名为Models、Views、ViewModels。 在Model文件夹中&#xff0c;添加Student类&#xff0c;并将…

Java学到什么程度可以找工作?这10点赶紧自查!

最近收到了不少私信&#xff0c;询问Java学到什么程度可以找工作。 我也去问了几个同组大佬的想法&#xff0c;总结了10点&#xff0c;大家可以对照自查一下&#xff0c;看看你都做到了吗&#xff1f; 基本技能自查 1、Java SE基础 推荐学习Java8&#xff0c;这依旧是个有代表…

【2023最新】腾讯云注册域名及服务器使用宝塔绑定域名教程

1 在腾讯云注册域名 在官网&#xff1a;https://buy.cloud.tencent.com/domain&#xff0c;注册想要的域名&#xff0c;需要认证信息模板 注册好以后&#xff0c;在右上角输入框&#xff0c;输入域名&#xff0c;查找并进入到域名控制台 在域名控制台&#xff0c;添加记录&…

基础算法系列之排序算法(一)[快速排序,归并排序,二分查找]

文章目录前言快速排序关键点实现选角排序重复实现稳定性分析记忆模板归并排序关键点实现二分查找总结前言 先来一波预热&#xff0c;本次寒假将要更新的博文系列为&#xff1a;基础算法部分&#xff0c;最新前言论文研读&#xff08;不包含论文复现-耗时太长&#xff09;&…

day21【代码随想录】二叉树的层序遍历、二叉树的层序遍历|| 、二叉树的层平均值 、二叉树的锯齿形层序遍历 、二叉树的右视图 、N叉树的层序遍历

文章目录前言一、二叉树的层序遍历&#xff08;力扣102&#xff09;二、二叉树的层序遍历||&#xff08;力扣107&#xff09;三、二叉树的层平均值&#xff08;力扣637&#xff09;四、二叉树的锯齿形层序遍历&#xff08;力扣103&#xff09;五、二叉树的右视图&#xff08;力…

吃透Chisel语言.39.Chisel实战之单周期RISC-V处理器实现(一)——需求分析和初步设计

Chisel实战之单周期RISC-V处理器实现&#xff08;一&#xff09;——需求分析和初步设计 需求分析 首先明确我们要做的是什么&#xff0c;这个在标题里面已经说明了&#xff0c;我们要做的是一个单周期RISC-V处理器。 但光是个短语不足以支撑我们开展项目&#xff0c;我们需…

大数据学习:shell基础(3)

文章目录history命令参数说明任务一&#xff1a;查看历史操作记录任务二&#xff1a;查看最近10条历史命令任务三&#xff1a;查看最开始10条历史命令任务四&#xff1a;曾多少次使用vim编辑文本文件&#xff1f;任务五&#xff1a;执行历史第5条命令任务六&#xff1a;执行上一…

【深度学习】李宏毅2021/2022春深度学习课程笔记 - Recurrent Neural NetWork(RNN)

文章目录一、Slot Filling二、Recurrent Neural NetWork&#xff08;RNN&#xff09;三、Bidirectional RNN&#xff08;双向RNN&#xff09;四、Long Short Term Memory&#xff08;LSTM&#xff09;五、Learning Target六、RNN 很难 Train七、Helpful Techniques7.1 LSTM7.2 …

CSDN竞赛14期·12月11日考试

CSDN竞赛14期12月11日考试 1、题目名称&#xff1a;字符串全排列 // 请关闭中文输入法&#xff0c;用英文的字母和标点符号。 // 如果你想运行系统测试用例&#xff0c;请点击【执行代码】按钮&#xff0c;如果你想提交作答结果&#xff0c;请点击【提交】按钮&#xff0c; //…

半入耳式蓝牙耳机哪款音质好?音质好的半入耳式蓝牙耳机推荐

对于喜欢听歌的朋友来讲&#xff0c;你只佛会关注到蓝牙耳机的佩戴舒适度&#xff0c;音质清晰这种情况&#xff0c;入耳式的带有耳塞&#xff0c;往往更加佩戴有更好的密闭性&#xff0c;半入耳的不完全进入耳道&#xff0c;佩戴更加舒适&#xff0c;下面整理了几款音质不错的…

[附源码]Python计算机毕业设计非处方药的查询与推荐系统Django(程序+LW)

该项目含有源码、文档、程序、数据库、配套开发软件、软件安装教程 项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等…

Prometheus+Grafana监控一网打尽

PrometheusGrafana监控一、Prometheus介绍二、监控组件node-exportermysqld-exportercadvisorprometheus三、Grafana 展示平台docker启动配置Data sources导入Dashboard模板Linux主机监控Mysql监控Nginx监控Redis监控PostgreSQL监控Kafka监控ElasticSearch监控一、Prometheus介…