flume--为搬砖而生,日志传输的一把好手
(一)flume的产生
为什么会有flume
随着互联网的发展,人们对网络日志产生的信息也越来越重视。不仅如此,我们的服务器,比如Nginx,每天都会产生大量的日志。我们要将这些日志收集到指定的地方,比如hdfs平台,进行分析。但是大量的日志产生的位置比较分散,可能来自于Tomcat、Nginx、甚至是数据库等等,而且存储的目的地也不一样,这就导致了数据采集的复杂性。然鹅最关键的问题是,如果在采集的过程中,出现问题了该怎么办?比如我们要把日志收集到hdfs平台上进行分析,我们可以通过hdfs shell的方式,但是我们无法对日志采集的状况进行监控。针对以上种种问题,flume就诞生了,因此看到这里小伙伴也能猜到flume是干什么的了。
flume是什么
关于flume是什么?官网给出了定义,我们来看一下。因为flume是Apache的一个顶级项目,所以官网就是flume.apache.org
flume是一个分布式、高可靠、高可用的服务,用于高效地收集、聚合、移动大量的数据集。flume具有一个简单、灵活并基于数据流的架构,它具备可调节的可靠机制和许多故障转移以及恢复机制,并具有很强的健壮性和容错性。通过使用一种简单可扩展的数据模型来提供应用程序的在线分析。
我们从图中,可以看到一个长方形,里面围住的部分就是flume的一个agent。我们通过flume移动数据就相当于在写一个配置文件。source:源。表示我们要从哪里收集,比如这里来自于web服务器。sink:下沉。表示我们要把数据沉到什么地方去,这里是hdfs。那么中间还有一个channel是干什么的呢?channel:管道,我们不会把刚收集到的日志立刻sink到hdfs上面,那样效率太慢,而是会先存到channel里面,这个channel就是消息队列,一般是kafka,当管道满了,然后再把管道里面的数据一次性sink的目的地,这里是hdfs。
因此通俗一点的说:
flume就是一个日志采集工具,或者理解为一个搬运工,将日志从一个地方搬到另一个地方去。特征是:具备高可用、高可靠、分布式处理模式。里面定制了很多的source和sink,支持我们从不同的源端读取日志文件,然后sink到不同的地方去。
flume的特征
flume可以高效率地从多个服务器中收集日志并存储到hdfs/hbase中
除了日志信息,flume也可以用来接入收集规模宏大的社交网络节点事件数据,比如Facebook、Twitter等等
支持各种不同数据源,并能够输送到不同的地方
支持多路径,多管道输入,多管道输出,上下文路由等等
什么意思呢?意思就是我们sink的时候,也可以作为下一个agent的source,不一定要是hdfs等平台,并且多个sink还可以到同一个source里面。
同理,一个source也可以到多个channel,然后sink到多个地方
可以水平扩展
flume的应用场景
flume其实还可以有其他的功能,比如数据的处理,但是在大数据实际应用中,flume基本上只用于日志收集工作,根据不同的业务场景,编写不同的配置文件。
(二)flume的client
flume的客户端
什么是flume的客户端呢?实际上,部署flume的机器就可以称之为一个客户端。部署flume,是不是需要一些环境呢?
根据官网的要求,至少要满足以下几点
- 1.jdk1.8或更高,大数据的这些组件都和java脱不开关系
- 2.内存。因为flume是基于流式的,因此内存也是必须的
- 3.磁盘空间,我们不会把数据一直保存到内存里面,最终要刷到磁盘上,因此磁盘空间也是必须的。
- 4.目录权限,你要往一个目录里面写文件的话,总要有写或者读的权限吧
感觉2、3、4都是废话,这不是显然的吗?
(三)flume的source
我们之前说过,一个agent就相当于一个配置文件,里面有三剑客,分别是source、channel、sink。通过编写配置文件,配置source、channel、sink,来将数据从一端传输到另一端。
我们来看看source。
source就是我们的源端,从不同的数据源读取日志信息放到channel里,然后sink取出channel里面的日志信息到其他的地方,比如hdfs、hbase等等。
因此flume的架构很好理解,就这三个组成部分。既然flume的source支持不同的数据源,那么我们就来看看,它都支持哪些不同的数据源呢?
source支持的数据源
支持以下几种source
Avro Source
flume第一种数据来源,可以监听IP和端口,并不一定是本机的IP,可以监听其他机器的IP和port,用于获取数据。从外部的Avro client streams接收events,当与另一个flume agent内置的avro sink配对时,可以创建分层搜集的拓扑结构。感觉官网说的好阳春白雪,其实说白了,还是下面这张图,一个agent的source可以是上一个agent的sink,之间通过avro去连接
字体加粗的属性必须进行设置
因为source的类型是avro,因此type也要指定为avro
Thrift Source
和avro source基本类似,但是avro可以支持ip过滤,但是thrift不支持。
因为source是thrift,所以type要改成thrift。是什么样的source,那么type就改成什么。
Exec Source
比较常用的一种source,可以运行Unix命令,然后该命令会一直执行,读取文件,作为flume数据的来源,需要配置相应的channel、type、以及运行的命令。并且根据官网介绍,本方式不保证数据百分百会传送到指定的位置,如果出现问题,数据可能丢失。可以使用Spooling Directory等其他手段,保证数据传输
JMS Source
本方式会从JMS消息队列的目的地,比如一个队列或者一个topic中读取数据;需要在plugins.d即插件目录下放置相应的jar包;不过此来源一般用的较少,因为局限性比较强。
Spooling Directory Source
Spooling Directory Source监测配置的目录下新增的文件,并将文件中的数据读取出来。其中,Spool Source有2个注意地方,第一个是拷贝到spoolDir指定的目录下的文件不可以再打开编辑,第二个是spool目录下不可包含相应的子目录。这个主要用途作为对日志的准实时监控。
加粗的表示必须填写,其中spooldir,就是我们指定的目录,当然type也要指定为spooldir
自定制source
其实flume提供的source还是不能完全满足我们的需求,因此我们也可以自定义source。
当然还有其它source,不过不常用。这里可能有点抽象,先来清楚一下概念,了解一下flume的架构、各个组成部分的功能,后续会进行演示
(四)flume的channel
还是这张放了好几遍的图,source我们已经介绍过了,下面介绍channel。
channel我们其实我们之前也说过了,是一个管道,类似于golang里面的channel。或者我们把它理解为一个缓存也行,source收集过来的日志并不直接sink到hdfs上面,而是会放到缓存里面,等缓存满了再一次性sink到指定的地方。
为什么要这么设计呢?介绍source收集日志的速度很快,但是sink的速度很慢,如果source直接对接sink的话,那么就会导致读写速度不一致了,这就跟双十一要引入消息队列一样,如果上游的订单系统直接对接下游的库存系统,那么上游爆发的流量很容易将下游冲垮,所以才需要引入消息队列进行削峰填谷,两者的道理是类似的。
关于缓存,有两种。一种是基于内存的缓存,另一种是基于磁盘的缓存。
基于内存的缓存:速度快,但是不安全,而且容量有限
基于磁盘的缓存:可靠性高,容量大,但是读写速度会慢一些。
flume官方为我们提供了如下channel
基于内存的channel,我们很少使用,因为数据容易丢失。File channle和kafka channel很常用
比如file channel,那么这里的type要指定为file,因为这里的缓存使用文件来实现的。配置的话可以指定大小
(五)flume的sink
我们之前说过,source是用于收集数据,channel是用于缓存数据,sink是用于将数据写到外部的存储设备。那我们外部的存储设备不一样,那么sink的类型就会不一样,因此sink实际上也是可定制的,可配置的。对于不同的外部地址,那么sink就有不同的实现。
flume也为我们提供了很多sink,比如hdfs sink、hive sink等等。当然还有avro sink,这个在之前介绍过,是为了对接下一个agent的avro source
当然每一个sink都有很多额外的配置,比如文件的前缀啊、回滚间隔、回滚策略啊等等
(六)flume的interceptor
flume的拦截器,这是什么?
flume可以在移动数据时修改/删除事件,这是在拦截器的帮助下完成的。所有的拦截器都是实现了org.apache.flume.interceptor.Interceptor接口的类,拦截器可以基于开发人员所选择的任意条件来修改、甚至删除事件。并且flume还支持链式的拦截器(补充:意思就是可以设置多个拦截器,然后顺序的调用。),这可以通过在配置中指定多个拦截器的类名组成的列表来实现。拦截器在源配置中通过一系列的空格进行分隔,拦截器被指定的顺序也是它们被调用的顺序,每一个拦截器所返回的事件在列表中会链式的传递到下一个拦截器,并且可以修改或者删除事件。如果一个拦截器需要删除事件,那么它只需要在列表中将该返回的事件不进行返回即可。如果要删除所有事件,只需要简单地返回一个空列表即可。拦截器是一个命名组件,以下是如何通过配置创建拦截器的一个例子。
拦截器主要用于source端,我们可以将获取的数据拦截下来,然后加上一个键值对,可以指定文件的名字、来源、时间戳等等,然后sink到不同的地方去。比如我们 收集了很多日志,比如登陆日志、行为日志等等,那么我们在拦截的时候,便可以指定哪些日志都是什么类型,然后输出的时候,不同的日志输出到不同的地方去
(七)flume的selector
我们的source在收集完数据之后要写到哪一个channel里面呢?比如我们搜集的日志有三种,一种是只包含字母,另一种只包含数字,最后一种只包含符号(假设是这样蛤)。hdfs上面有三个文件,正好对应三种日志类型。现在我们的问题是,不同类型的日志怎么存到不同的文件里面去呢?既然要存到不同的文件里面,那么首先要存到不同的channel里面,channel1只包含字母、channel2只包含数字、channel3只包含符号,然后sink1对接channel1、sink2对接channel2、sink3对接channel3,理论上情况是这样的。
但是现在问题又来了,怎么存到不同的channel里面呢?这个时候选择器就出现了
官网提供了哪些selector呢
Replicating Channel Selector (default)
这也是默认的channel,type指定为replicating。意思是数据会复制到每一个channel中。
Multiplexing Channel Selector
复分模式的channel,不会把数据拷贝了,而是将数据整体分发到不同的channel中,当然要指定type为multiplexing。可以看到不管是source、channel、sink还是这里的selector,都有不同的类型,然后在各自的type中指定即可。
Custom Channel Selector
自己实现的channel,不同的数据分发到不同的channel中,相当于对Multiplexing Channel Selector进行了自定制,这样就可以按照我们自己的逻辑来决定数据分发到哪一个channel当中。
(八)flume的event
例如一些消息队列,发送的数据并不是原生的数据,而是对数据要进行一个封装,我们可以通过python往activemq里面发送数据,然后使用golang去接。但是python最终发到mq里面不是一个简单的数据,golang接收的也不是一个简单的数据,而是对数据进行了一个封装。同理flume也是一样,在flume中,有一个event,我们好像在拦截器的时候提到过,那么它是什么呢?event在flume当中叫做事件,flume在传输日志的时候,每一条数据都会被封装成一个event。
为什么要有event呢?
首先我们可以想一下,数据格式有很多种,source也不同,channel不同、sink也各不相同,那么我如何才能将其统一起来呢?这个时候就需要event了,将每一条记录都封装为大家都认识event不就行了吗?这就是为什么要有event。当然event主要包含两个部分,一个是header,一个body。body是一个字节数组,里面记录了日志信息,header则是一个hashmap,我们可以添加一些自己的信息,如果只有body没有header的话,那么我们的信息就会打到body里面,这样就对我们要传送的数据造成污染了,具有侵入性,所以要有header
(九)flume的agent
agent是flume的运行核心,是flume运行的最小单位。一个flume的agent就是包含了source、channel、sink的一个jvm进程,用于日志的收集和传输。
首先我们到目前为止介绍的都是概念,其实flume的概念也很好理解,主要就是各个组件的那些配置还没有介绍,后面会介绍,目前先了解flume的基础架构即可。因此之前也说过,用flume传输日志,就相当于在指定agent中各个组件的配置。一个agent可以包含多个source、多个channel、多个sink
A Flume event is defined as a unit of data flow having a byte payload and an optional set of string attributes. A Flume agent is a (JVM) process that hosts the components through which events flow from an external source to the next destination (hop).
一个flume事件被定义为一个数据流单元,具有字节负载和可选的字符属性集。一个flume agent就是一个jvm进程,承载着各个组件(source、channel、sink),事件可以通过这些组件从外部源流向下一个目的地。
当然不要忘记,agent还是可以串联的,一个agent的sink对接下一个agent的source
(十)flume的安装
flume下载地址:http://archive.cloudera.com/cdh5/cdh/5
选择flume即可,为什么不到官网下载,因为cdh版本的话可以避免很多问题,比如jar包的冲突。
因此大数据组件安装的话,推荐去这个网站下载,不建议使用社区版本的。
我用的是阿里云的云服务器,所有大数据相关的软件都放在/opt目录下,因此我们在/opt下面新建一个flume目录,然后把相应的安装包放进去解压
解压完成之后,我们来看看flume的结构
我们进入conf目录下,修改一下配置。如果我们只修改一个配置的话,那么需要修改什么呢?不用想,肯定是jdk的路径。对,我们需要在flume-env.sh中指定JAVA_HOME,但是进入目录发现没有这个文件,但是有一个flume-env.sh.template,这是模板,我们用这个模板生成一个即可
然后将flume加到环境变量里面去
export FLUME_HOME=/opt/flume/apache-flume-1.6.0-cdh5.7.5-bin
export PATH=$FLUME_HOME/bin:$PATH
然后source一下
输入flume-ng version查看版本,和其他框架不一样,flume的话需要加上-ng,至于原因后面会说。
(十一)flume的两种版本
flume分为两个版本,一个1.x之前的版本,叫做flume og,另一个是1.x版本,叫做flume ng
至于差别就不介绍了,总之现在用的话就是用flume 1.x版本的就行了。
(十二)flume的avro_source
任务:使用avro_source收集数据,打印到控制台
终于到实战部分了,我们下面就通过编写配置文件的方式来移动数据,之前也说了,使用flume就是编写配置文件。
至于channel,我们就无所谓了,使用内存channel还是磁盘channel都无所谓,我们这里主要是介绍source。而sink则就是控制台
那么配置怎么写呢?
# 这里的a1就是我们agent的名字
a1.sources = r1 # 命名agent的source为r1
a1.channels = c1 # 同理c1是channel的名字
a1.sinks = k1 # 同理k1是sink的名字
a1.sources.r1.type = avro # 配置r1的的类型avro
a1.sources.r1.bind = localhost # 绑定的端口为本地
a1.sources.r1.port = 4141 # 端口为4141
a1.channels.c1.type = memory # 指定c1的类型为内存
a1.channels.c1.capacity = 1000 # 指定c1的容量为1000个event
a1.channels.c1.transcactionCapacity = 1000 # 指定c1的每次处理容量为1000个event
a1.sinks.k1.type = logger # 表示不产生实体文件,就打印在控制台
# 将source和sink与channel绑定起来
a1.sources.r1.channels = c1 # source r1的channel指定为c1
a1.sinks.k1.channel = c1 # sink k1的channel指定为c1。但是这里不再是channels,而是channel,要注意
我们随便在一个目录,新建一个文件,将上面的内容去掉注释然后写进去
那么如何启动flume呢?
flume-ng agent -c $FLUME_HOME/conf -f 我们写的配置文件的路径 -n a1 -Dflume.root.logger=INFO,console
参数解释:
-c $FLUME_HOME/conf:首先flume启动是有默认配置的,这个配置就在$FLUME_HOME/conf下面
-f 我们写的配置文件的路径:这个很好理解,我们自己写的配置可以通过-f来指定
-n a1:我们在配置文件中指定的agent名字
-Dflume.root.logger=INFO,console:表示flume自身运行状态的日志,按需配置,详细信息控制台打印
我们启动服务,由于我们自己的conf就在当前目录下,所以直接flume-ng agent -c $FLUME_HOME/conf -f ./conf -n a1 -Dflume.root.logger=INFO,console启动即可
服务已经启动了,监听本地的4141端口,那么我们就往这个端口里面发送数据。
发送数据,我们可以发送一个文件,可以使用flume自带的一个方法。
flume-ng avro-client -c $FLUME_HOME/conf -H localhost -p 4141 -F ~/.bashrc
avro-client:创建一个客户端
-c $FLUME_HOME/conf:指定配置,刚才说了
-H localhost:指定目的ip
-p 4141:指定目的端口
-F ~/.bashrc:指定发送的文件
显示内容已经接受到了,会将文件打开,每一条都是一个event,包含header(我们这里没有指定)和event(是一个字节数组)
(十三)flume的exec_source
下面来看看flume的exec_source,flume之所以有这么多source、channel、sink,主要是为了解决不同场景的业务,那么这个exec_source是用来干什么的呢?
exec_source可以实时监控一个文本文件,如果文件有变化,可以打印出来
下面就来写配置文件,在当前目录新建一个conf1,写上如下内容(不含注释)
a1.sources = r1 # 命名agent的source为r1
a1.channels = c1 # 同理c1是channel的名字
a1.sinks = k1 # 同理k1是sink的名字
a1.sources.r1.type = exec # 配置r1的的类型exec
# a1.sources.r1.bind = localhost # 绑定的端口为本地
# a1.sources.r1.port = 4141 # 端口为4141
# 由于是监控一个文件,所以不需要在绑定ip和端口了
a1.sources.r1.command = tail -F /root/mashiro.txt # 指定命令,监控这个文件
a1.channels.c1.type = memory # 指定c1的类型为内存
a1.channels.c1.capacity = 1000 # 指定c1的容量为1000个event
a1.channels.c1.transcactionCapacity = 1000 # 指定c1的每次处理容量为1000个event
a1.sinks.k1.type = logger # 表示不产生实体文件,就打印在控制台
# 将source和sink与channel绑定起来
a1.sources.r1.channels = c1 # source r1的channel指定为c1
a1.sinks.k1.channel = c1 # sink k1的channel指定为c1。但是这里不再是channels,而是channel,要注意
启动flume,flume-ng agent -c $FLUME_HOME/conf -f ./conf1 -n a1 -Dflume.root.logger=INFO,console
此时服务启动成功,但是还没有东西过来
(十四)file channel
基于内存的channel,我们在介绍source的时候,演示过了,下面来看看基于文件的channel。
a1.sources = r1
a1.channels = c1
a1.sinks = k1
a1.sources.r1.type = exec
a1.sources.r1.command = tail -F /root/mashiro.txt
a1.channels.c1.type = file # 指定类型为file
a1.channels.c1.checkpointDir = /root/checkPoint # checkpoint将被存储的目录
a1.channels.c1.dataDirs = /root/data # 数据的存储位置
a1.sinks.k1.type = logger # 表示不产生实体文件,就打印在控制台
# 将source和sink与channel绑定起来
a1.sources.r1.channels = c1 # source r1的channel指定为c1
a1.sinks.k1.channel = c1 # sink k1的channel指定为c1。但是这里不再是channels,而是channel,要注意
先来看看/root目录的内容
然后执行命令,flume-ng agent -c $FLUME_HOME/conf -f ./conf2 -n a1 -Dflume.root.logger=INFO,console
再来查看当前目录
可以看到checkPoint和data两个目录,而且这是flume帮我们创建的,我们来往文件里面写点东西
(十五)hdfs sink
下面介绍sink,这里介绍一下hdfs sink,因为收集过来的日志主要放到hdfs平台上面。
既然要把收集的数据放大hdfs上面,那么flume就必须能连接hdfs,需要把依赖的jar包,放到$FLUME_HOME/lib下面,需要哪些jar包呢?又从哪里找呢?我们可以从$HADOOP_HOME/share/hadoop目录里面找。
commons-configuration-x.x.jar
hadoop-auth-x.x.x.jar
hadoop-common-x.x.x.jar
hadoop-hdfs-x.x.x.jar
# jar前面的x表示版本号,在找的时候可以使用模糊查询,比如我现在在$HADOOP_HOME/share/hadoop目录下,要找commons-configuration-x.x.jar 的话,便可以通过find ./ -name commons-configuration*即可。
如果你的flume版本是1.99的话,那么还需要以下两个jar包,否则的话就不需要
commons-io-x.x.jar
htrace-core-.x.x.x-incubating.jar
找到之后,cp到$FLUME_HOME/lib下面
下面就是编写配置文件
# 给source、channel、sink指定名字
a1.sources = r1
a1.channels = c1
a1.sinks = k1
# source的配置
a1.sources.r1.type = exec
a1.sources.r1.command = tail -F /root/test.log
# channel的配置
a1.channels.c1.type = memory
a1.channels.c1.capacity = 1000
a1.channels.c1.transactionCapacity = 100
# sink的配置
a1.sinks.k1.type = hdfs
a1.sinks.k1.hdfs.path = hdfs://localhost:9000
# 这条配置是给文件起一个前缀
a1.sinks.k1.hdfs.prefix = hive-
# 绑定channel
a1.sources.r1.channels = c1
a1.sinks.k1.channel = c1
flume--为搬砖而生,日志传输的一把好手的更多相关文章
- 目标检测---搬砖一个ALPR自动车牌识别的环境
目标检测---搬砖一个ALPR自动车牌识别的环境 参考License Plate Detection and Recognition in Unconstrained Scenarios@https: ...
- 基于Flume+Kafka+ Elasticsearch+Storm的海量日志实时分析平台(转)
0背景介绍 随着机器个数的增加.各种服务.各种组件的扩容.开发人员的递增,日志的运维问题是日渐尖锐.通常,日志都是存储在服务运行的本地机器上,使用脚本来管理,一般非压缩日志保留最近三天,压缩保留最近1 ...
- SQL Server 事务日志传输
概述 可以使用日志传送将事务日志不间断地从一个数据库(主数据库)发送到另一个数据库(辅助数据库).不间断地备份主数据库中的事务日志,然后将它们复制并还原到辅助数据库,这将使辅助数据库与主数据库基本保持 ...
- sqlserver2008 复制,镜像,日志传输及故障转移集群区别
一, 数据库复制 SQL Server 2008数据库复制是通过发布/订阅的机制进行多台服务器之间的数据同步,我们把它用于数据库的同步备份.这里的同步备份指的是备份服务器与主服务器进行 实时数据同步, ...
- dataguard 日志传输服务
1.日志传输可以分为 lgwr和ARCH,默认是arch,其中lgwr传输可以分为async和syncsync:同步服务,只有在事物参数的日志成功的传输到备库的目的地,事物才能提交.虽然同步服务没有限 ...
- SqlServer 事务日志传输
基本概念 可以使用日志传送将事务日志不间断地从一个数据库(主数据库)发送到另一个数据库(辅助数据库).不间断地备份主数据库中的事务日志,然后将它们复制并还原到辅助数据库,这将使辅助数据库与主数据库基本 ...
- 如何使用银联卡充值美元到BTC-E以及比特币搬砖教程
1,名词解释 搬砖:就是在价格低的平台买入比特币,然后转移到价格高的平台卖出, 一般而言,BTC-E是国外三大比特币交易所中比特币单价最低的一个站,因为其需要用美元充值,相对不方便.之前国内比特币价格 ...
- sql 事务日志传输
原文:sql 事务日志传输 概述 可以使用日志传送将事务日志不间断地从一个数据库(主数据库)发送到另一个数据库(辅助数据库).不间断地备份主数据库中的事务日志,然后将它们复制并还原到辅助数据库,这将使 ...
- ELK菜鸟手记 (四) - 利用filebeat和不同端口把不同服务器上的log4j日志传输到同一台ELK服务器
1. 问题描述 我们需要将不同服务器(如Web Server)上的log4j日志传输到同一台ELK服务器,介于公司服务器资源紧张(^_^) 2. 我们需要用到filebeat 什么是filebeat ...
随机推荐
- WPF Slider Tickbar 中显示数值
class CustomTickBar : TickBar { protected override void OnRender(System.Windows.Media.DrawingContext ...
- Linux 查找特定程序 whereis
Linux 查找特定程序 whereis whereis 命令主要用于查找程序文件,并提供这个文件的二进制可执行文件.源代码文件和使用手册存放位置. 1.查找命令程序 例如,查找 touch 命令 [ ...
- JPA 或者Hibernate 实体类说明
这里简单介绍Hibernate的Annotation注解 一.声明实体 @Entity对实体注释.任何Hibernate映射对象都要有这个注释@Table声明此对象映射到数据库的数据表,通过它可以为实 ...
- JavaScript(3):JSON
<!DOCTYPE html> <html> <body> <p>JSON</p> <script> // JSON 值可以是: ...
- maridb 主从复制
maridb(>10.0)主从复制 1.环境说明 测试环境 系统版本:Ubuntu 16.04.5 LTS内核版本:4.4.0-141-generic x86_64MariaDB版本:mysql ...
- PJzhang:QQ输入法用户许可协议和隐私政策阅读
猫宁!!! 参考链接:http://qq.pinyin.cn/ 中国用户量最大的输入法是搜狗输入法,百度输入法也有上亿的用户量. 我下载了7个输入法,查看他们的用户许可协议和隐私政策情况.这里说的都是 ...
- bert系列一:《Attention is all you need》论文解读
论文创新点: 多头注意力 transformer模型 Transformer模型 上图为模型结构,左边为encoder,右边为decoder,各有N=6个相同的堆叠. encoder 先对inputs ...
- Django学习笔记(一)Django基础
新建项目 django-admin startproject my_site #会在当前目录新建my_site目录,可自行修改目录名 django-admin startproject my_site ...
- 建立EF访问数据库架构时,出现One or more validation errors were detected during model generation
原因是因为我在写实体类的时候没有为实体类中的属性声明一个主键,即用[key]特性标注在属性上,这样DbContext才能为我们在数据库上找到对应的主键 using System.ComponentMo ...
- NModbus4的使用
步骤1:打开串口 SerialPort port = new SerialPort("COM7") port.BaudRate = ; port.BaudRate = ; port ...