2-Flume之Sink与Channel

Flume

Sink

HDFS Sink

  1. 将数据写到HDFS上。数据以文件形式落地到HDFS上,文件名默认是以FlumeData开头,可以通过hdfs.filePrefix来修改

  2. HDFS Sink默认每隔30s会滚动一次生成一个文件,因此会导致在HDFS上生成大量的小文件,实际过程中,需要通过hdfs.rollInterval来修改,一般设置为3600s或者86400s。如果设置为0,那么表示不滚动,只生成1个文件 。

  3. HDFS Sink默认每1024B会滚动一次生成一个文件,同样会导致产生更多的小文件,实际过程中,需要通过hdfs.rollSize来修改,一般设置为134217728B。如果设置为0,那么表示不滚动,只生成1个文件

  4. HDFS Sink默认每10条数据会滚动一次生成一个文件,同样会导致产生更多的小文件。实际过程中,需要通过hdfs.rollCount来修改。如果设置为0,那么表示不滚动,只生成1个文件

    //不是每次生成文件才一次全部写入。每次启动,就会先创建文件,然后一条一条的写入Event,当达到设置的任一条件时,产生新的文件,并向新的文件写入。

  5. HDFS Sink支持三种文件类型:SequenceFile(序列文件,不能直接cat查看), DataStream(文本文件) or CompressedStream(压缩文件),默认使用的是SequenceFile。如果将文件类型设置为CompressedStream,那么还需要指定属性hdfs.codeC,支持gzip, bzip2, lzo, lzop, snappy

  6. 案例

    1. 格式文件

      a1.sources = s1
      a1.channels = c1
      a1.sinks = k1a1.sources.s1.type = netcat
      a1.sources.s1.bind = 0.0.0.0
      a1.sources.s1.port = 8090a1.channels.c1.type = memory# 配置HDFS Sink
      # 类型必须是
      a1.sinks.k1.type = hdfs
      # 数据在HDFS上的存储路径。路径不存在会自动创建
      a1.sinks.k1.hdfs.path = hdfs://hadoop01:9000/flume_data
      # 文件滚动间隔时间
      a1.sinks.k1.hdfs.rollInterval = 3600
      # 文件滚动大小
      a1.sinks.k1.hdfs.rollSize = 134217728
      # 文件滚动条数
      a1.sinks.k1.hdfs.rollCount = 1000000000
      # 文件类型
      a1.sinks.k1.hdfs.fileType = DataStreama1.sources.s1.channels = c1
      a1.sinks.k1.channel = c1
      
      1. 启动
      flume-ng agent -n a1 -c $FLUME_HOME/conf -f hdfssink.properties -Dflume.root.logger=INFO,console
      
      1. 在新的窗口中通过nc来发送数据
      nc hadoop01 8090
      

Logger Sink

  1. 将数据以日志写入到指定目的地,支持consolefile。实际开发过程中,使用的比较少,一般是教学阶段使用较多
  2. Logger Sink默认要求Event的body部分不能超过16个字节,可以通过maxBytesToLog来调节
  3. Logger Sink对中文支持不好

File Roll Sink

  1. 将数据以文本文件形式存储到本地的磁盘上。可以通过属性sink.serializer来修改,支持TEXTavro_event

  2. 类似于HDFS Sink,File Roll Sink默认也是每隔30s滚动一次生成一个文件,可以通过属性sink.rollInterval来修改

  3. 案例

    a1.sources = s1
    a1.channels = c1
    a1.sinks = k1a1.sources.s1.type = netcat
    a1.sources.s1.bind = 0.0.0.0
    a1.sources.s1.port = 8090a1.channels.c1.type = memory# 配置File Roll Sink
    # 类型必须是file_roll
    a1.sinks.k1.type = file_roll
    # 数据在本地的存储路径
    a1.sinks.k1.sink.directory = /opt/flume_data
    # 文件滚动间隔时间
    a1.sinks.k1.sink.rollInterval = 3600a1.sources.s1.channels = c1
    a1.sinks.k1.channel = c1
    

AVRO Sink

  1. 将数据经过AVRO序列化之后来写出,结合AVRO Source来实现流动模型

  2. 多级流动

    多级流动

    1. 第一个节点

      a1.sources = s1
      a1.channels = c1
      a1.sinks = k1a1.sources.s1.type = exec
      //监控这个文件内容的变化
      a1.sources.s1.command = tail -F /opt/software/flume-1.11.0/data/a.txt
      a1.sources.s1.shell = /bin/sh -ca1.channels.c1.type = memory//数据发送到hadoop02的7000端口
      a1.sinks.k1.type = avro
      a1.sinks.k1.hostname = hadoop02
      a1.sinks.k1.port = 7000a1.sources.s1.channels = c1
      a1.sinks.k1.channel = c1
      
    2. 第二个节点

      a1.sources = s1
      a1.channels = c1
      a1.sinks = k1//监听本机7000端口的数据
      a1.sources.s1.type = avro
      a1.sources.s1.bind = 0.0.0.0
      a1.sources.s1.port = 7000a1.channels.c1.type = memorya1.sinks.k1.type = avroa1.sinks.k1.hostname = hadoop03
      a1.sinks.k1.port = 7000a1.sources.s1.channels = c1
      a1.sinks.k1.channel = c1
      
    3. 第三个节点

      
      a1.sources = s1
      a1.channels = c1
      a1.sinks = k1a1.sources.s1.type = avro
      a1.sources.s1.bind = 0.0.0.0
      a1.sources.s1.port = 7000a1.channels.c1.type = memorya1.sinks.k1.type = loggera1.sources.s1.channels = c1
      a1.sinks.k1.channel = c1
      

    启动顺序:hadoop03 hadoop02 hadoop01

  3. 扇入流动

    扇入流动

    1. 第一个节点

      a1.sources = s1
      a1.channels = c1
      a1.sinks = k1a1.sources.s1.type = exec
      a1.sources.s1.command = tail -F /opt/software/flume-1.11.0/data/a.txt
      a1.sources.s1.shell = /bin/sh -ca1.channels.c1.type = memorya1.sinks.k1.type = avro
      a1.sinks.k1.hostname = hadoop03
      a1.sinks.k1.port = 6666a1.sources.s1.channels = c1
      a1.sinks.k1.channel = c1
      
    2. 第二个节点

      a1.sources = s1
      a1.channels = c1
      a1.sinks = k1a1.sources.s1.type = netcat
      a1.sources.s1.bind = 0.0.0.0
      a1.sources.s1.port = 8000a1.channels.c1.type = memorya1.sinks.k1.type = avro
      a1.sinks.k1.hostname = hadoop03
      a1.sinks.k1.port = 6666a1.sources.s1.channels = c1
      a1.sinks.k1.channel = c1
      
    3. 第三个节点

      a1.sources = s1
      a1.channels = c1
      a1.sinks = k1a1.sources.s1.type = avro
      a1.sources.s1.bind = 0.0.0.0
      a1.sources.s1.port = 6666a1.channels.c1.type = memorya1.sinks.k1.type = loggera1.sources.s1.channels = c1
      a1.sinks.k1.channel = c1
      
  4. 扇出流动

    扇出流动

    1. 注意:在Flume中,可以从同一个数据源采集数据,放到不同的仓库(Channel)存储,但是每一个Sink只能对应1个Channel。一个Channel可对应多个Sink

    2. 第一个节点

      a1.sources = s1
      a1.channels = c1 c2
      a1.sinks = k1 k2a1.sources.s1.type = netcat
      a1.sources.s1.bind = 0.0.0.0
      a1.sources.s1.port = 8000a1.channels.c1.type = memory
      a1.channels.c2.type = memorya1.sinks.k1.type = avro
      a1.sinks.k1.hostname = hadoop02
      a1.sinks.k1.port = 7000a1.sinks.k2.type = avro
      a1.sinks.k2.hostname = hadoop03
      a1.sinks.k2.port = 7000a1.sources.s1.channels = c1 c2
      a1.sinks.k1.channel = c1
      a1.sinks.k2.channel = c2
      
    3. 第二个和第三个节点

      a1.sources = s1
      a1.channels = c1
      a1.sinks = k1a1.sources.s1.type = avro
      a1.sources.s1.bind = 0.0.0.0
      a1.sources.s1.port = 7000a1.channels.c1.type = memorya1.sinks.k1.type = loggera1.sources.s1.channels = c1
      a1.sinks.k1.channel = c1
      

自定义Sink

  1. 自定义Sink的时候,需要定义一个类继承AbstractSink,实现Sink接口,最好还要实现Configurable接口来获取配置。注意,自定义Sink的过程中,需要关注事务问题。依赖和笔记1中用的一样。

  2. public class AuthSinks extends AbstractSink implements Sink , Configurable {String path ;//打印流,打印到文件或者Flume的窗口。sout调用的就是这个类中的方法PrintStream ps;@Overridepublic void configure(Context context) {//从flume中的格式文件中获取路径信息path = context.getString("path");if(path == null || "".equals(path) || !path.startsWith("/"))throw new IllegalArgumentException();}@Overridepublic synchronized void start() {if(!path.endsWith("/"))path = path + "/";path += String.valueOf(System.currentTimeMillis());try {ps =  new PrintStream(path);}catch (Exception e){e.printStackTrace();}}@Overridepublic Status process() throws EventDeliveryException {Channel channel = this.getChannel();Transaction ts = channel.getTransaction();ts.begin();Event e;try {while ((e = channel.take()) != null) {ps.println("headers:");Map<String, String> map = e.getHeaders();for (Map.Entry<String, String> header : map.entrySet()) {ps.println(header.getKey() + "-" + header.getValue());}ps.println("bodys:");byte[] body = e.getBody();ps.println("\t" + new String(body));}ts.commit();return Status.READY;} catch (Exception e1) {ts.rollback();return Status.BACKOFF;} finally {ts.close();}}@Overridepublic synchronized void stop() {if(ps != null) ps.close();}
    }
    
  3. 打成jar包放到lib目录下

    cd /opt/software/flume-1.11.0/lib/
    rz
    
  4. 编辑格式文件

    cd ../data/
    vim authsink.properties
    

    在文件中添加

    a1.sources = s1
    a1.channels = c1
    a1.sinks = k1a1.sources.s1.type = http
    a1.sources.s1.port = 8888a1.channels.c1.type = memory# 配置自定义Sink
    # 类型必须是类的全路径名
    a1.sinks.k1.type = com.fesco.sink.AuthSink
    # 存储路径
    a1.sinks.k1.path = /opt/flume_dataa1.sources.s1.channels = c1
    a1.sinks.k1.channel = c1
    
  5. 启动Flume

  6. 发送HTTP请求

    curl -X POST -d '[{"headers":{"class":"big data","sinktype":"auth"},"body":"testing~~~"}]' http://hadoop01:8888
    

事务

事务流程
  1. Source会先执行doPut操作,将数据放入PutList中,PutList本质上是一个Deque
  2. PutList会试图将数据传输给Channel,如果成功,执行doCommit操作,如果失败,那么执行doRollback
  3. Channel收到数据之后,会试图将数据推送到TakeList,然后由TakeList将数据试图推送给Sink。TakeList本质上也是一个Deque
  4. 如果TakeList成功将数据推送给Sink,那么执行doCommit操作;反之,如果失败,那么执行doRollback操作

Channel

Memory Channel

  1. Memory Channel将数据临时存储到内存队列中,队列默认容量是100,即队列默认最多能存储100条数据,如果队列被放满,那么后续的操作会被阻塞。可以通过属性capacity来调节,实际过程中一般会设置为100000~300000
  2. transactionCapacity:事务容量。每次PutList向Channel推送的数据条数或者Channel向TakeList添加的数据条数,默认是100。实际过程中,这个值一般会调节为1000~3000
  3. 需要注意的是,Memory Channel是将数据临时存储到内存中,所以读写速度相对较快,但是不可靠,因此适应于要求速度但是不要求可靠性的场景

File Channel

  1. File Channel将数据临时存储到磁盘上,所以读写速度相对慢一些,但是可靠,因此适应于要求可靠性但不要求速度的场景

  2. File Channel默认会将数据临时存储到~/.flume/file-channel/data目录下,可以通过属性dataDirs来修改,如果指定了多个数据目录,那么目录之间用逗号隔开

  3. File Channel支持断点续传,默认情况下,会将偏移量记录到~/.flume/file-channel/checkpoint目录下,可以通过属性checkpointDir来修改

  4. 默认File Channel能够存储1000000条数据,可以通过属性capacity来条件

  5. File Channel最多能占用2146435071B的磁盘,可以通过maxFileSize修改

  6. File Channel的transactionCapacity的默认值是10000

  7. 案例

    a1.sources = s1
    a1.channels = c1
    a1.sinks = k1a1.sources.s1.type = netcat
    a1.sources.s1.bind = 0.0.0.0
    a1.sources.s1.port = 8090# 配置File Channel
    # 类型必须是file
    a1.channels.c1.type = file
    # 偏移量的存储位置
    a1.channels.c1.checkpointDir = /opt/flume_data/checkpoint
    # 数据临时存储位置
    a1.channels.c1.dataDirs = /opt/flume_data/dataa1.sinks.k1.type = loggera1.sources.s1.channels = c1
    a1.sinks.k1.channel = c1
    

其他Channel

  1. JDBC Channel:将数据临时存储到数据库,但是JDBC Channel目前只支持Derby数据库。基于Derby(微型、文件型、单连接)的特性,所以实际开发过程中,不适用这个Channel
  2. Spillable Memory Channel:内存溢出Channel。内存中维系一个队列,如果队列被放满,不会阻塞,而是会将数据临时存储到磁盘上,这个Channel目前还在实验阶段,不推荐在生产场景中使用

Selector

概述

  1. Selector并不是一个单独的组件,而是附属于Source的子组件
  2. Selector支持三种模式:
    1. replicating:复制/复用模式。节点收集到数据之后,会将数据复制,然后分发给每一个节点,此时每一个节点收到的数据都是相同的
    2. load balancing:负载均衡模式。节点收集到数据之后,会平均分发到其他的节点上。此时被扇出的节点接收到的数据条数大致相等,但是数据不相同。这种模式是Flume1.10提供的,然后不稳定
    3. multiplexing:路由/分发模式。节点收集到数据之后,会根据headers中的指定键和值,将数据分发给对应的节点来处理,此时每一个节点收到的数据都是不同的
  3. 扇出结构中,如果不指定,默认使用的是replicating模式

multiplexing

  1. 实际过程中,如果需要对数据进行分类处理,那么可以考虑使用路由/分发模式

  2. 案例

    a1.sources = s1
    a1.channels = c1 c2
    a1.sinks = k1 k2a1.sources.s1.type = http
    a1.sources.s1.port = 8000
    # 指定Selector的类型
    a1.sources.s1.selector.type = multiplexing
    # 指定监听的字段
    a1.sources.s1.selector.header = kind
    # 根据kind字段的值分发给对应的Channel//如果请求头中的kind的值是video就传输给c1 通道
    a1.sources.s1.selector.mapping.video = c1
    a1.sources.s1.selector.mapping.music = c2
    a1.sources.s1.selector.default = c2a1.channels.c1.type = memory
    a1.channels.c2.type = memorya1.sinks.k1.type = avro
    a1.sinks.k1.hostname = hadoop02
    a1.sinks.k1.port = 7000a1.sinks.k2.type = avro
    a1.sinks.k2.hostname = hadoop03
    a1.sinks.k2.port = 7000a1.sources.s1.channels = c1 c2
    a1.sinks.k1.channel = c1
    

a1.sinks.k2.channel = c2


启动Flume之后,发送请求```sh
curl -X POST -d '[{"headers":{"kind":"video"},"body":"video log"}]' http://hadoop01:8000
curl -X POST -d '[{"headers":{"kind":"music"},"body":"music log"}]' http://hadoop01:8000
curl -X POST -d '[{"headers":{"kind":"txt"},"body":"txt log"}]' http://hadoop01:8000

Sink Processor

概述

  1. Sink Processor本质上就是Sink Group,是将一个或者多个Sink绑定到一个组中来使用
  2. 目前,官网支持三种模式
    1. default:默认模式。一个Sink就对应一个Sinkgroup,有几个Sink就对应了几个Sinkgroup
    2. Load Balancing:负载均衡。将多个Sink绑定到一个组中,然后将这个组接收到数据平均的发送给每一个Sink。支持round_robin(轮询)和random(随机)。同样,Flume提供的负载均衡模式并不好(能)用
    3. Failover:崩溃恢复。将多个Sink绑定到一个组中,如果现在工作的Sink宕机,同组中的其他Sink可以实现相同的功能,从而避免了单点故障

Failover

  1. 将多个Sink绑定到一个组中,同组的Sink需要配置优先级,数据会优先发送给优先级较高的Sink,如果高优先级的Sink宕机,那么才会发送给低优先级的Sink。当优先级高的Sink恢复后,那么数据又会重新回来。

  2. 案例

    a1.sources = s1
    a1.channels = c1 c2
    a1.sinks = k1 k2# 给Sinkgroup起名
    a1.sinkgroups = g1
    # 给Sinkgroup绑定Sink
    a1.sinkgroups.g1.sinks = k1 k2
    # 指定Sinkgroup的类型
    a1.sinkgroups.g1.processor.type = failover
    # 给Sink指定优先级
    a1.sinkgroups.g1.processor.priority.k1 = 7
    a1.sinkgroups.g1.processor.priority.k2 = 5
    # 发送超时时间
    # 默认是30000ms->30s   。这个属性的作用不是很理解
    a1.sinkgroups.g1.processor.maxpenalty = 10000a1.sources.s1.type = netcat
    a1.sources.s1.bind = 0.0.0.0
    a1.sources.s1.port = 8000a1.channels.c1.type = memory
    a1.channels.c2.type = memorya1.sinks.k1.type = avro
    a1.sinks.k1.hostname = hadoop02
    a1.sinks.k1.port = 7000a1.sinks.k2.type = avro
    a1.sinks.k2.hostname = hadoop03
    a1.sinks.k2.port = 7000a1.sources.s1.channels = c1 c2
    a1.sinks.k1.channel = c1
    a1.sinks.k2.channel = c2
    

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

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

相关文章

使用git下载github/gitee仓库部分或单个文件的方法

前言 有些时候在github或者gitee仓库中我们只需要下载整个项目中的我门需要的那一部分文件夹或文件就行了&#xff0c;不需要下载所有的项目。这样可以节省很多流量和时间 步骤 1.建立一个新的 git 本地仓库 这里我在D:\test中初始化 命令&#xff1a; git init2.在本地仓…

Istio 部署 Spring Coud 微服务应用

Istio 服务部署 这篇文章讲述如何将 Java Spring Cloud 微服务应用部署到 Istio mesh 中。 准备基础环境 使用 Kind 模拟 kubernetes 环境。文章参考&#xff1a;https://blog.csdn.net/qq_52397471/article/details/135715485 在 kubernetes cluster 中安装 Istio 创建一…

Excel双击单元格后弹窗输入日期

Step1. 在VBE界面新建一个窗体(Userform1),在窗体的工具箱的空白处右键,选中添加附件,勾选Calendar control 8.0,即可完成日历的添加。 PS:遗憾的是, Office 64 位没有官方的日期选择器控件。唯一的解决方案是使用Excel 的第三方日历。 参考链接:How to insert calen…

电路笔记 :灯光画 JLC 嘉立创EDA+PCB打样(笔记)

项目介绍 PCB板灯光画(可行性验证)&#xff0c;PCB灯光画无论是电路原理还是焊接都非常简单&#xff0c;所有元器件均采用0805及体积更大的封装&#xff0c;只需要电烙铁和焊锡丝等基础的工具就能制作&#xff0c;新手小白也能轻松复刻&#xff0c;里边还有PCB板实现灯光画效果…

数据交换技术

目录 <线路交换> <报文交换> <分组交换> 1.数据报分组交换 2.虚电路分组交换 计算机网络是以数据交换为目的的技术&#xff0c;从交换技术的发展过程来看&#xff0c;主要经历了线 路交换、报文交换、分组交换的过程。 <线路交换> 线路交换又称为…

vue3+threejs新手从零开发卡牌游戏(十):创建己方战域

首先在game目录下创建site文件夹&#xff0c;用来存放战域相关代码&#xff1a; 这里思考下如何创建战域&#xff0c;我的想法是添加一个平面&#xff0c;将己方战域和对方战域都添加进这个平面中&#xff0c;所以首先我们先添加一个战域plane&#xff0c;site/index.vue代码如…

[蓝桥杯 2020 省 AB1] 网络分析

一开始写的暴力合并 卡n^2过的不是正解 看正解是类似 虚拟点树形DP的思路 很巧妙 记录一下 #include<bits/stdc.h> using namespace std; using ll long long; using pii pair<int,int>; #define int long long const int N 3e510; const int inf 0x3f3f3f3f; …

【RPG Maker MV 仿新仙剑 战斗场景UI (八)】

RPG Maker MV 仿新仙剑 战斗场景UI 八 状态及装备场景代码效果 状态及装备场景 本计划在战斗场景中直接制作的&#xff0c;但考虑到在战斗场景中加入太多的窗口这不太合适&#xff0c;操作也繁琐&#xff0c;因此直接使用其他场景。 代码 Pal_Window_EquipStatus.prototype.…

Word邮件合并

Word邮件合并功能可以解决在Word中批量填写内容的需求&#xff0c;当需要大量格式相同&#xff0c;只修改少数相关内容时&#xff0c;例如利用Word制作工资条&#xff0c;通知函&#xff0c;奖状等等&#xff0c;同时操作也非常简单灵活。下面通过例子来说明邮件合并的使用方法…

突破距离限制 远程级联测径仪 让您使用更安心!

关键词&#xff1a;在线测径仪,测径仪,远程级联 在现代工业领域&#xff0c;测量的准确性和高效性至关重要。在线测径仪不仅具备了这两项特质&#xff0c;更能进行远程级联&#xff0c;能更快速的为您解决软件系统在使用中遇到的问题。 在线测径仪能做到以下几点 精准测量&am…

过了30岁,要具备翻篇的能力

哈喽&#xff0c;你好啊&#xff0c;我是雷工&#xff01; 杨绛先生曾经说过&#xff1a; “人生没有彩排&#xff0c;每一天都是现场直播。只有翻篇&#xff0c;才能重新开始。” 一个人如果整天回顾以前&#xff0c;想着自己做的不好的事情&#xff0c;想着曾经遇到的挫折&am…

vue.js制作学习计划表案例

通俗易懂&#xff0c;完成“学习计划表”用于对学习计划进行管理&#xff0c;包括对学习计划进行添加、删除、修改等操作。 一. 初始页面效果展示 二.添加学习计划页面效果展示 三.修改学习计划完成状态的页面效果展示 四.删除学习计划 当学习计划处于“已完成”状态时&…

Vue使用font-face自定义字体详解

目录 1 介绍2 使用2.1 语法2.2 属性说明2.3 Vue使用案例2.3.1 全局定义字体2.3.2 在页面使用 3 注意事项 1 介绍 font-face 是 CSS 中的一个规则&#xff0c;它允许你加载服务器上的字体文件&#xff08;远程或者本地&#xff09;&#xff0c;并在网页中使用这些字体。这样&am…

2024年3月26日 十二生肖 今日运势

小运播报&#xff1a;2024年3月26日&#xff0c;星期二&#xff0c;农历二月十七 &#xff08;甲辰年丁卯月己丑日&#xff09;&#xff0c;法定工作日。 红榜生肖&#xff1a;鸡、鼠、猴 需要注意&#xff1a;马、狗、羊 喜神方位&#xff1a;东北方 财神方位&#xff1a;…

22.保护性暂停扩展(一对一)

如果需要多个类之间使用GuardedObject对象&#xff0c;作为参数传递不是很方便&#xff0c;因此设计一个解耦的中间类&#xff0c;这样不仅能够解耦结果的等待者和结果生产者&#xff0c;还能够支持多个任务的管理。 Futures就好比居民楼一层的信箱&#xff0c;每个信箱有房间的…

HarmonyOS实战开发-如何使用首选项能力实现一个简单示例。

介绍 本篇Codelab是基于HarmonyOS的首选项能力实现的一个简单示例。实现如下功能&#xff1a; 创建首选项数据文件。将用户输入的水果名称和数量&#xff0c;写入到首选项数据库。读取首选项数据库中的数据。删除首选项数据文件。 最终效果图如下&#xff1a; 相关概念 首选…

Facebook如何使用增强技术提升广告效果?

AR in AD - case study 脸书2021年宣布了引入AR的新方法&#xff0c;以推动其应用套件中的产品发现和购买。但他们首先考虑是技术。据脸书称&#xff0c;技术一直是增强现实在其应用程序中更广泛使用的主要障碍。这就是为什么它现在正在做出改变&#xff0c;使企业主和广告商更…

TextMeshPro图文混排的两种实现方式,不打图集

TMP图文混排 方案一&#xff1a;TMP自带图文混排使用方法打包图集使用 方案二&#xff1a;不打图集&#xff0c;可以使用任何图片 接到一个需求&#xff0c;TextMeshPro 图文混排。 方案一&#xff1a;TMP自带图文混排 优点布局适应优秀&#xff0c;字体左中右布局位置都很不错…

Web常见标签属性

应用软件&#xff1a;c/s&#xff08;客户端与服务端&#xff09; b/s&#xff08;服务器与浏览器架构&#xff09;web前端&#xff1a;html5、css3、JavaScriptHtml5&#xff1a;超文本标记语言 超链接标签 语法规范<标签名> marquee 标签之间可以嵌套属性&#xff1a;…

【项目】均衡代码评测

TOC 目录 项目介绍 开发环境 主要技术 项目实现 公共模块 日志 工具类 编译运行模块 介绍 编译 运行 编译和运行结合起来 业务逻辑模块 介绍 MVC模式框架 模型&#xff08;Model&#xff09; 视图&#xff08;View) 控制器&#xff08;Controller&#xff09…