2024年1月

转载至我的博客
https://www.infrastack.cn
,公众号:架构成长指南

今天给各位分享一个非常牛的实时分析型数据库Apache Doris,几乎国内的一二线大厂都在使用它做数据分析,如下图,这只是一小部分

同时我司也在使用它,他目前支撑了我们亿级业务数据的多维实时查询分析,而且性能很不错

Doris 介绍

官方地址:
https://doris.apache.org/

Apache Doris源于百度2008年启动的产品Palo在2018年捐献给Apache基金会,是一个基于 MPP 架构的高性能、实时的分析型数据库,他非常简单易用,而且性能还不错,仅需亚秒级响应时间即可获得查询结果,不仅支持高并发的查询场景,也可以支持高吞吐的复杂分析场景,比如你可以基于它做用户行为分析、日志检索平台、用户画像分析、订单分析等应用。

Doris的架构非常简洁,易于运维,并且可以支持10PB以上的超大数据集

特性

这里特性很多,但是如果没接触过大数据的同学,可能不是特别了解,但是注意这个特性,
支持SQL 语言,兼容MySQL
,比如:通过Mybatis 写好 sql,就可以调用查询,而且他能支持亿级数据检索响应,以前还是想分库分表呢,现在有了它可以在考虑一下它了,看分库分表有必要吗,但是这里要注意下,它是一个 OLAP 引擎与 OLTP还是有点区别,如果业务场景,新增多后期更新少,同时查询场景多,那么可以在 mysql 中保存一段时间的热点数据,来进行相关业务操作,而报表查询都走Doris

这里可能有些人员不懂什么是 OLAP,下面是一个OLAP与OLTP对比图

架构

Doirs只有两个主进程模块。一个是 Frontend(FE),另一个是Backend(BE)

Frontend(FE)

主要负责用户请求的接入、查询计划的解析、元数据的存储和集群管理相关工作, Doris采用Paxos协议以及Memory + Checkpoint + Journal的机制来确保元数据的高性能及高可靠。

Leader、follower和 observer它们三个构成一个可靠的服务
,保证单节点宕机的情况下,元数据能够实时地在线恢复,而不影响整个服务,与zookeeper角色一样。

Backend(BE)

BE主要负责数据存储、查询计划的执行。

  • BE管理tablet副本, tablet是table经过分区分桶形成的子表, 采用列式存储。

  • BE受FE指导, 创建或删除子表。

  • BE接收FE分发的物理执行计划与其他BE共同协作完成执行。

  • BE读本地的列存储引擎, 获取数据, 通过索引和谓词下沉快速过滤数据。

  • BE后台执行compact任务, 减少查询时的读放大。

以上FE和 BE支持动态弹性扩容,而且在扩容过程中对应用无影响,同时Doris 不依赖
zk

hdfs
等,所以架构很简单,这种架构设计极大的简化了运维成本,
其实一个好的产品就应该这样,把复杂留给自己,把简单留给用户

OLAP对比

在我们解决大数据查询分析时,也调研了比较知名的一些产品,下面是一个对比

TIDB

TIDB 是一个非常优秀的国产分布式数据库,他的主要优势OLTP处理上,但是也支持OLAP,但是在我们调研过程中,抱着使用一个TIDB 完全替代掉Mysql 想法,我们进行了测试,我们使用的版本是
v5.3.3
,但是在测试过程中效果不理想,因为 TIDB 解析引擎会对SQL进行分析,来决定走 OLTP 还是OLAP,比如我们期望走OLAP 但是走了OLTP导致性能有所降低

ClickHouse

提到 Doris 不得不提ClickHouse,CK是
由俄罗斯IT公司Yandex为Yandex.Metrica网络分析服务开发的
开发的实时数仓,以性能著称,但是经过测试,与 Doris在不同场景各有优劣, 但是它的架构复杂、运维成本高,同时对 sql 语法兼容性没有Doris好,因此没有选择,不过国内也有不少公司在使用

Doris

运维成本低、兼容Mysql 语法、架构足够简单、社区支持性好(非常活跃),同时经过百度内部长达10 多年的大规模使用,成熟度不容置疑,没有理由不选它

性能测试报告

Doris 版本:0.15.0,目前最新版本是:2.03

  • 1 FE + 3 BE 独立部署
  • CPU:8core 16G Intel(R) Xeon(R) Platinum 8163 CPU @ 2.50GHz
  • 内存:16GB
  • 硬盘:1块机械硬盘
  • 网卡:万兆网卡

测试1

6亿数据进行多表查询,响应
1.98s

SELECT SUM(lo_revenue), d_year, p_brand FROM lineorder, date, part, supplier WHERE lo_orderdate = d_datekey AND lo_partkey = p_partkey AND lo_suppkey = s_suppkey AND p_category = 'MFGR#12' AND s_region = 'AMERICA' GROUP BY d_year, p_brand ORDER BY d_year, p_brand;

测试2

ods_test_mysql_test_record_s数据量:148399619,67 个字段

ods_test_mysql_test_barrier_s数据量:1385,12个字段

执行以下 sql,耗时:
0.277s

SELECT
    i.a1 AS p_code,
    IFNULL(IFNULL(i.a56, b.a2), '0') AS gateway_no,
    1 AS inout_type,
    DATE_FORMAT(i.a2, '%Y-%m-%d') AS report_date,
    DATE_FORMAT(i.a2, '%Y') AS report_year,
    DATE_FORMAT(i.a2, '%c') AS report_month,
    1 AS total_num,
    (CASE WHEN i.a6 = 1 THEN 1 ELSE 0 END) AS big_car_num,
    (CASE WHEN i.a6 != 1 THEN 1 ELSE 0 END) AS small_car_num,
    (CASE WHEN i.a44 REGEXP '[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领A-Z]{1}[A-Z]{1}(([0-9]{5}[DABCEFGHJK])|([DABCEFGHJK][A-HJ-NP-Z0-9][0-9]{4}))$' THEN 1 ELSE 0 END) AS new_power_car_num,
    (CASE WHEN i.a61 = 2 OR i.a61 = 3 THEN 1 ELSE 0 END) AS reserve_num,
    (CASE WHEN i.a61 = 1 THEN 1 ELSE 0 END) AS white_list_num,
    (CASE WHEN i.a6 = 1 AND (i.a61 = 2 OR i.a61 = 3) THEN 1 ELSE 0 END) AS big_car_reserve_num,
    (CASE WHEN i.a6 = 1 AND i.a61 = 1 THEN 1 ELSE 0 END) AS big_car_white_list_num,
    (CASE WHEN i.a6 != 1 AND (i.a61 = 2 OR i.a61 = 3) THEN 1 ELSE 0 END) AS small_car_reserve_num,
    (CASE WHEN i.a6 != 1 AND i.a61 = 1 THEN 1 ELSE 0 END) AS small_car_white_list_num,
    (CASE WHEN (i.a61 = 2 OR i.a61 = 3) AND i.a44 REGEXP '[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领A-Z]{1}[A-Z]{1}(([0-9]{5}[DABCEFGHJK])|([DABCEFGHJK][A-HJ-NP-Z0-9][0-9]{4}))$' THEN 1 ELSE 0 END) AS new_power_reserve_num,
    (CASE WHEN i.a61 = 1 AND i.a44 REGEXP '[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领A-Z]{1}[A-Z]{1}(([0-9]{5}[DABCEFGHJK])|([DABCEFGHJK][A-HJ-NP-Z0-9][0-9]{4}))$' THEN 1 ELSE 0 END) AS new_power_white_list_num,
    CURRENT_TIMESTAMP() AS update_time
FROM
    ods_test_mysql_test_record_s i
    LEFT JOIN ods_test_mysql_test_barrier_s b ON i.a22 = b.a1
WHERE
    i.a67 = 0
    AND i.a1 = '100280023213'
    AND i.a2 < '2021-11-20 00:00:00'
    AND (
        i.a2 BETWEEN DATE_FORMAT(i.a2, '%Y-%m-%d 00:00:00') AND FROM_UNIXTIME(UNIX_TIMESTAMP(DATE_FORMAT(i.a2, '%Y-%m-%d 00:00:00')) + 90000, '%Y-%m-%d %H:%i:%s')
    )
LIMIT 1000;

测试3

只查询小于
2022-12-30 00:00:00
数据,响应时间:
0.241s

SELECT
    i.a1 AS p_code,
    IFNULL(IFNULL(i.a56, b.a2), '0') AS gateway_no,
    1 AS inout_type,
    DATE_FORMAT(i.a2, '%Y-%m-%d') AS report_date,
    DATE_FORMAT(i.a2, '%Y') AS report_year,
    DATE_FORMAT(i.a2, '%c') AS report_month,
    1 AS total_num,
    (CASE WHEN i.a6 = 1 THEN 1 ELSE 0 END) AS big_car_num,
    (CASE WHEN i.a6 != 1 THEN 1 ELSE 0 END) AS small_car_num,
    (CASE WHEN i.a44 REGEXP '[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领A-Z]{1}[A-Z]{1}(([0-9]{5}[DABCEFGHJK])|([DABCEFGHJK][A-HJ-NP-Z0-9][0-9]{4}))$' THEN 1 ELSE 0 END) AS new_power_car_num,
    (CASE WHEN i.a61 = 2 OR i.a61 = 3 THEN 1 ELSE 0 END) AS reserve_num,
    (CASE WHEN i.a61 = 1 THEN 1 ELSE 0 END) AS white_list_num,
    (CASE WHEN i.a6 = 1 AND (i.a61 = 2 OR i.a61 = 3) THEN 1 ELSE 0 END) AS big_car_reserve_num,
    (CASE WHEN i.a6 = 1 AND i.a61 = 1 THEN 1 ELSE 0 END) AS big_car_white_list_num,
    (CASE WHEN i.a6 != 1 AND (i.a61 = 2 OR i.a61 = 3) THEN 1 ELSE 0 END) AS small_car_reserve_num,
    (CASE WHEN i.a6 != 1 AND i.a61 = 1 THEN 1 ELSE 0 END) AS small_car_white_list_num,
    (CASE WHEN (i.a61 = 2 OR i.a61 = 3) AND i.a44 REGEXP '[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领A-Z]{1}[A-Z]{1}(([0-9]{5}[DABCEFGHJK])|([DABCEFGHJK][A-HJ-NP-Z0-9][0-9]{4}))$' THEN 1 ELSE 0 END) AS new_power_reserve_num,
    (CASE WHEN i.a61 = 1 AND i.a44 REGEXP '[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领A-Z]{1}[A-Z]{1}(([0-9]{5}[DABCEFGHJK])|([DABCEFGHJK][A-HJ-NP-Z0-9][0-9]{4}))$' THEN 1 ELSE 0 END) AS new_power_white_list_num,
    CURRENT_TIMESTAMP() AS update_time
FROM
    ods_test_mysql_test_record_s i
    LEFT JOIN ods_test_mysql_test_barrier_s b ON i.a22 = b.a1
WHERE i.a2 < '2022-12-30 00:00:00' LIMIT 10000;

测试 4

最新官方性能测试报告:
https://doris.apache.org/zh-CN/docs/benchmark/ssb/

高可用测试

如下图FE3台组建一个高可用集群,分别为:

  • 172.17.0.3 FOLLOWER
  • 172.17.0.4 FOLLOWER
  • 172.17.0.5 MASTER

下面对会对常见一些异常场景进行测试验证,以下部分测试案例

MASTER下线测试

测试结果

成功,所有操作符合预期

测试过程

登录172.17.0.5 服务器杀掉FE节点

查看FE节点状态是否正常, 登录172.17.0.3服务器查看状态,执行以下命令

SHOW PROC '/frontends';

可以看到172.17.0.4成为了master,而172.17.0.5显示已经不在线了 Alive=false

执行插入数据操作,可以看到执行成功。

启动172.17.0.5,查看是否能加入集群

 sh /root/fe/bin/start_fe.sh 172.17.0.3:9010 --daemon

通过日志可以看到启动成功,并成为了FOLLOWER节点

修复缺失或损坏副本

测试结果

成功,所有操作符合预期

测试过程

1.执行如下命令看到分区10099的副本损坏,需要对他进行恢复创建空白副本

ADMIN SHOW REPLICA STATUS FROM table2 PARTITION (p201706, p201707,p201708);

2.登录master fe

ADMIN SET FRONTEND CONFIG ("recover_with_empty_tablet" = "true");

3.查看数据副本状态

ADMIN SHOW REPLICA STATUS FROM table2 PARTITION (p201706, p201707,p201708);

4.恢复设置

ADMIN SET FRONTEND CONFIG ("recover_with_empty_tablet" = "false");

系统Down机重启副本测试

测试结果

成功,所有操作符合预期

测试过程

1.停止0.5服务

2.查询副本状态

ADMIN SHOW REPLICA STATUS FROM table4;

3.重启服务,副本状态恢复正常

总结

以上介绍了Doris架构、性能、故障恢复、动态弹性扩容等特性,正因为这些特性,使不少大厂都在使用它,如果贵司有大数据处理需求,可以深入了解一下它,同时想深入了解 Doris,可以加我微信,拉你进官方社区群

扫描下面的二维码关注我们的微信公众帐号,在微信公众帐号中回复◉加群◉即可加入到我们的技术讨论群里面共同学习。

一:背景

1. 讲故事

有朋友在微信里面问我,为什么用
ThreadStatic
标记的字段,只有第一个线程拿到了初始值,其他线程都是默认值,让我能不能帮他解答一下,尼玛,我也不是神仙什么都懂,既然问了,那我试着帮他解答一下,也给后面类似疑问的朋友解个惑吧。

二:为什么值不一样

1. 问题复现

为了方便讲述,定义一个 ThreadStatic 的变量,然后用多个线程去访问,参考代码如下:


internal class Program
{
    [ThreadStatic]
    public static int num = 10;

    static void Main(string[] args)
    {
        Test();

        Console.ReadLine();
    }

    /// <summary>
    /// 1. 特性方式
    /// </summary>
    static void Test()
    {
        var t1 = new Thread(() =>
        {
            Debugger.Break();
            var j = num;
            Console.WriteLine($"tid={Thread.CurrentThread.ManagedThreadId}, num={j}");

        });
        t1.Start();
        t1.Join();

        var t2 = new Thread(() =>
        {
            Debugger.Break();
            var j = num;
            Console.WriteLine($"tid={Thread.CurrentThread.ManagedThreadId}, num={j}");
        });

        t2.Start();
    }
}

从代码中可以看到,确实如朋友所说,一个是
num=10
,一个是
num=0
,那为什么会出现这样的情况呢?

2. 从汇编上寻找答案

作为C#程序员,真的需要掌握一点汇编,往往就能找到问题的突破口,先看一下thread1 中的
var j = num;
所对应的汇编代码,参考如下:


D:\code\MyApplication\ConsoleApp7\Program.cs @ 27:
08893737 b9a0dd6808      mov     ecx,868DDA0h
0889373c ba04000000      mov     edx,4
08893741 e84a234e71      call    coreclr!JIT_GetSharedNonGCThreadStaticBase (79d75a90)
08893746 8b4814          mov     ecx,dword ptr [eax+14h]
08893749 894df8          mov     dword ptr [ebp-8],ecx

从汇编上可以看到,这个 num=10 是来自于
eax+14h
的地址上,而 eax 是 JIT_GetSharedNonGCThreadStaticBase 函数的返回值,言外之意核心逻辑是在此方法里,可以到 coreclr 中找一下这段代码,简化后如下:


HCIMPL2(void*, JIT_GetSharedNonGCThreadStaticBase, DomainLocalModule *pDomainLocalModule, DWORD dwClassDomainID)
{
    FCALL_CONTRACT;

    // Get the ModuleIndex
    ModuleIndex index = pDomainLocalModule->GetModuleIndex();

    // Get the relevant ThreadLocalModule
    ThreadLocalModule * pThreadLocalModule = ThreadStatics::GetTLMIfExists(index);

    // If the TLM has been allocated and the class has been marked as initialized,
    // get the pointer to the non-GC statics base and return
    if (pThreadLocalModule != NULL && pThreadLocalModule->IsPrecomputedClassInitialized(dwClassDomainID))
        return (void*)pThreadLocalModule->GetPrecomputedNonGCStaticsBasePointer();

    // If the TLM was not allocated or if the class was not marked as initialized
    // then we have to go through the slow path

    // Obtain the MethodTable
    MethodTable * pMT = pDomainLocalModule->GetMethodTableFromClassDomainID(dwClassDomainID);

    return HCCALL1(JIT_GetNonGCThreadStaticBase_Helper, pMT);
}

这段代码非常有意思,已经把
ThreadStatic
玩法的骨架图给绘制出来了,大概意思是每个线程都有一个
ThreadLocalBlock
结构体,这个结构体下有一个
ThreadLocalModule
的字典,key 为 ModuleIndex, value 为 ThreadLocalModule,画个简图如下:

从图中可以看到 num 是放在 ThreadLocalModule 中的,具体的说就是此结构的
m_pDataBlob
数组中,可以用 windbg 验证下。


0:008> r
eax=03077810 ebx=08baf978 ecx=79d75c10 edx=03110568 esi=053faa18 edi=053fa9b8
eip=08893746 esp=08baf8d8 ebp=08baf908 iopl=0         nv up ei pl zr na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246
ConsoleApp7!ConsoleApp7.Program.<>c.<Test>b__2_0+0x46:
08893746 8b4814          mov     ecx,dword ptr [eax+14h] ds:002b:03077824=0000000a

0:008> dt coreclr!ThreadLocalModule 03077810
   +0x000 m_pDynamicClassTable : (null) 
   +0x004 m_aDynamicEntries : 0
   +0x008 m_pGCStatics     : (null) 
   +0x00c m_pDataBlob      : [0]  ""

0:008> dp 03077810+0x14 L1
03077824  0000000a

有了这些前置知识后,接下来就简单了,如果当前的 ThreadLocalModule 不存在就会调用 JIT_GetNonGCThreadStaticBase_Helper 函数在 m_pTLMTable 字段中添加一项,接下来观察下这个函数代码,简化如下:


HCIMPL1(void*, JIT_GetNonGCThreadStaticBase_Helper, MethodTable * pMT)
{
    // Get the TLM
    ThreadLocalModule * pThreadLocalModule = ThreadStatics::GetTLM(pMT);

    // Check if the class constructor needs to be run
    pThreadLocalModule->CheckRunClassInitThrowing(pMT);

    // Lookup the non-GC statics base pointer
    base = (void*) pMT->GetNonGCThreadStaticsBasePointer();

    return base;
}

PTR_ThreadLocalModule ThreadStatics::GetTLM(ModuleIndex index, Module * pModule) //static
{
    // Get the TLM if it already exists
    PTR_ThreadLocalModule pThreadLocalModule = ThreadStatics::GetTLMIfExists(index);

    // If the TLM does not exist, create it now
    if (pThreadLocalModule == NULL)
    {
        // Allocate and initialize the TLM, and add it to the TLB's table
        pThreadLocalModule = AllocateAndInitTLM(index, pThreadLocalBlock, pModule);
    }

    return pThreadLocalModule;
}

上面这段代码的步骤很清楚。

  • 创建 ThreadLocalModule

  • 初始化 MethodTable 类型的字段 pMT

这个 pMT 非常重要,训练营里的朋友都知道 MethodTable 是 C# 的 class 承载,言外之意就是判断下这个 class 有没有被初始化,如果没有初始化那就调
静态构造函数
,接下来的问题是 class 到底是哪一个类呢?

结合刚才汇编中的
mov edx,4
以及源码发现是取 IL 元数据中的 Program,参考代码及截图如下:


    FORCEINLINE MethodTable * GetMethodTableFromClassDomainID(DWORD dwClassDomainID)
    {
        DWORD rid = (DWORD)(dwClassDomainID) + 1;
        TypeHandle th = GetDomainFile()->GetModule()->LookupTypeDef(TokenFromRid(rid, mdtTypeDef));
        MethodTable * pMT = th.AsMethodTable();
        return pMT;
    }

也可以用 windbg 在 JIT_GetNonGCThreadStaticBase_Helper 方法的 return 处下一个断点,参考如下:


0:008> r ecx
ecx=0564ef28
0:008> !dumpmt 0564ef28
EEClass:             056d14d0
Module:              0564db08
Name:                ConsoleApp7.Program
mdToken:             02000005
File:                D:\code\MyApplication\ConsoleApp7\bin\x86\Debug\net6.0\ConsoleApp7.dll
AssemblyLoadContext: Default ALC - The managed instance of this context doesn't exist yet.
BaseSize:            0xc
ComponentSize:       0x0
DynamicStatics:      false
ContainsPointers:    false
Slots in VTable:     8
Number of IFaces in IFaceMap: 0

到这里就真相大白了,thread1 在执行时,用 CheckRunClassInitThrowing 方法发现 Program 没有被静态构造过,所以就执行了,即
num=10
,当 thread2 执行时,发现已经被构造过了,所以就不再执行静态构造函数,所以就成了默认值
num=0

3. 如何复验你的结论

刚才我说 thread1 做了一个是否执行静态构造的判断,其实这里我可以做个手脚,在 Main 之前先把 Program 静态函数给执行掉,按理说 thread1 和 thread2 此时都会是默认值
num=0
,对不对,哈哈,试一试呗,简化代码如下:


    internal class Program
    {
        [ThreadStatic]
        public static int num = 10;

        /// <summary>
        /// 先于 main 执行
        /// </summary>
        static Program()
        {
        }

        static void Main(string[] args)
        {
            Test();

            Console.ReadLine();
        }
    }

哈哈,此时都是 0 了,也就再次验证了我的结论。

三:总结

在 C# 开发中经常会有一些疑惑,如果不了解汇编,C++ ,相信你会陷入到很多的魔法使用中而苦于不能独自解惑的遗憾。

图片名称

标签:Zookeeper3.8 ,Curator5.5;

一、简介

ZooKeeper是一个集中的服务,用于维护配置信息、命名、提供分布式同步、提供组服务。分布式应用程序以某种形式使用所有这些类型的服务。

二、环境搭建

1、修改配置文件

# 1、拷贝一份样本配置文件
cp zookeeper-3.8.3/conf/zoo_sample.cfg zookeeper-3.8.3/conf/zoo.cfg

# 2、修改数据文件地址,注意这里用本地路径
dataDir=/local-path/zookeeper-3.8.3/data

# 3、添加一个配置,处理启动日志的提示:ZooKeeper audit is disabled.
audit.enable=true

2、服务启动

# 1、启动服务端
zookeeper-3.8.3/bin/zkServer.sh start

# 2、停止服务端
zookeeper-3.8.3/bin/zkServer.sh stop

# 3、启动客户端
zookeeper-3.8.3/bin/zkCli.sh

3、客户端测几个增删查的命令

[zk: localhost:2181(CONNECTED) 0] create /cicada smile1
Created /cicada
[zk: localhost:2181(CONNECTED) 1] get /cicada
smile1
[zk: localhost:2181(CONNECTED) 2] ls /
[cicada, zookeeper]
[zk: localhost:2181(CONNECTED) 3] delete /cicada

三、工程搭建

1、工程结构

2、依赖管理

Curator是一组Java库,它让ZooKeeper的使用变得更加容易,这里的依赖实际是查询匹配版本的时候走了个捷径,也可以参考
integration-redis
包,熟悉下Spring的封装策略。

<!-- Zookeeper组件 -->
<dependency>
    <groupId>org.apache.zookeeper</groupId>
    <artifactId>zookeeper</artifactId>
    <version>${zookeeper.version}</version>
</dependency>
<!-- 包含Curator组件 -->
<dependency>
    <groupId>org.springframework.integration</groupId>
    <artifactId>spring-integration-zookeeper</artifactId>
    <version>${spring-integration.version}</version>
</dependency>

3、配置文件

配置脚本

zookeeper:
  #服务器地址
  connectString: 127.0.0.1:2181
  #会话超时时间
  sessionTimeoutMs: 3000
  #连接超时时间
  connectionTimeoutMs: 60000
  #最大重试次数
  maxRetries: 3
  #初始休眠时间
  baseSleepTimeMs: 1000

配置类

@Configuration
public class ZookeeperConfig {

    @Value("${zookeeper.connectString}")
    private String connectString;
    
    @Value("${zookeeper.baseSleepTimeMs}")
    private int baseSleepTimeMs;

    @Value("${zookeeper.maxRetries}")
    private int maxRetries ;

    @Value("${zookeeper.connectionTimeoutMs}")
    int connectionTimeoutMs ;

    @Value("${zookeeper.sessionTimeoutMs}")
    int sessionTimeoutMs ;

    private static CuratorFramework client = null ;
    /**
     * 初始化
     */
    @PostConstruct
    public void init (){
        // 重试策略
        RetryPolicy policy = new ExponentialBackoffRetry(baseSleepTimeMs, maxRetries);
        // 创建Curator
        client = CuratorFrameworkFactory.builder()
                .connectString(connectString)
                .connectionTimeoutMs(connectionTimeoutMs)
                .sessionTimeoutMs(sessionTimeoutMs)
                .retryPolicy(policy).build();
        //开启连接
        client.start();
    }

    @Bean
    public CuratorFramework getClient (){
        return client ;
    }
}

四、ZooKeeper用法

测试几个API方法,节点创建和添加数据,以及判断和查询数据,还有就是基于ZooKeeper提供的读写锁能力。

public class ConfigTest {

    @Autowired
    private CuratorFramework client ;

    @Test
    public void testCreate () throws Exception {
        // 创建一个持久化节点,断开连接时不会自动删除
        client.create().creatingParentsIfNeeded().withMode(CreateMode.PERSISTENT).forPath("/path1");
    }

    @Test
    public void testExists () throws Exception {
        // 判断节点是否存在,path2不存在所以stat2是null
        Stat stat1 = client.checkExists().forPath("/path1");
        System.out.println(stat1);
        Stat stat2 = client.checkExists().forPath("/path2");
        System.out.println(stat2);
    }

    @Test
    public void testSetData () throws Exception {
        // 设置节点数据
        client.setData().forPath("/path1", "data1".getBytes(StandardCharsets.UTF_8));
    }

    @Test
    public void testCreateAndSet () throws Exception {
        // 创建一个持久化节点并设置节点数据
        client.create().creatingParentsIfNeeded().withMode(CreateMode.PERSISTENT)
                .forPath("/path3","data3".getBytes(StandardCharsets.UTF_8));
    }

    @Test
    public void testGetData () throws Exception {
        // 查询节点数据
        byte[] data = client.getData().forPath("/path3");
        System.out.println(new String(data,StandardCharsets.UTF_8));
    }

    @Test
    public void testDelete () throws Exception {
        // 删除节点
        client.delete().guaranteed().deletingChildrenIfNeeded().forPath("/path3");
    }

    @Test
    public void testReadLock () throws Exception {
        // 读写锁-读
        InterProcessReadWriteLock lock = new InterProcessReadWriteLock(client,"/lock-read");
        lock.readLock().acquire();
        System.out.println("获取-ReadLock");
        lock.readLock().release();
    }

    @Test
    public void testWriteLock () throws Exception {
        // 读写锁-写
        InterProcessReadWriteLock lock = new InterProcessReadWriteLock(client,"/lock-write");
        lock.writeLock().acquire();
        System.out.println("获取-WriteLock");
        lock.writeLock().release();
    }
}

五、参考源码

文档仓库:
https://gitee.com/cicadasmile/butte-java-note

源码仓库:
https://gitee.com/cicadasmile/butte-spring-parent

物联网浏览器(IoTBrowser)是用于开发人机界面(HMI)或数据采集与监督控制系统(SCADA) 的工具,使用HTML或Vue前端技术开发物联网终端用户界面,支持串口、RFID、电子秤等硬件协议,支持js控制关机、全屏等工控操作。

一、示例功能展示

1.窗口界面能力

常用的功能界面全屏、设置屏幕大小等。

2.系统
应用能力

常用的功能界面系统关机、设置标题、设置图标等。

使用F12快捷键可以快速调起开发者工具,方便调试前端功能。管理密码为:123

3.Web串口调试

Web串口调试,使用WebSocket双向通讯,实时采集并展示到界面。可以借助echarts、three.js等前端界面框架实现漂亮界面。

二、示例源码介绍

1.配置文件

2.代码目录

三、二次开发和打包

1.HBuilderX开发

a.导入目录

b.双击运行

c.修改代码

修改文件并保存

修改代码并应用

右击调出右击菜单点击刷新,或者使用快捷键F5即可快速刷新查看修改后效果

2.VS下开发和打包

a.修改图标

替换logo.ico

2.F5运行即可

3.生成打包exe

注意:发行只要需要exe文件,此exe为免安装版,运行目录就是安装目录。

抓住已知的,迎面未知的。

编者按:
大模型、降本、出海,是多媒体从业者交流的高频词,内容与交互的需求层出不穷,大模型与AI的演进目不暇接,让增速低走的视频云迎面新的机遇和挑战。作为一个跨越中美多媒体行业20年的亲历者(阿里云视频云负责人何亚明),与他的对话展现出一番场景,他没有否认多媒体生态当下的问题,但他说新的机会就在眼前,更重要的是,他对多媒体满怀希望。

策划 撰写 / LiveVideoStack、IMMENSE

从微软、Facebook到阿里云,何亚明跨越了中美两大最活跃的经济体,走过PC互联网、移动互联网到视频化的20多年,一直与多媒体为伴。

他认为,无论技术和产品如何演进,音视频作为最贴近用户的展示方式大概率不会改变,这是他对多媒体生态依然充满信心的原因之一。不过,随着大模型向AGI不断演进直至实现,多媒体从业者需要将AGI融入到多媒体技术中,甚至改变原本的工作流。比如在微软,Azure media service“退役”了,但团队并没有流失,而是进入到Copilot ,让Copilot与多媒体更好地结合。

在阿里云视频云,何亚明和团队正在探索通过大模型提高视频处理和生产的效率,
希望构建一个属于视频的大模型
,通过这个系统可以极大地提升业务决策效率,让系统也变得更加的简单。

在他看来,
智能时代也是人机交互的新时代,将带来交互方式的变化,这也带来了对视频技术的新需求。
新需求主要体现在算力和时延两个方面。算力方面,视频技术会更多和AI相结合,会消耗更多的算力。算力也会从服务端逐步向移动端扩展,视频处理和生产会变得更高效、更智能。时延方面,随着Vision Pro、Quest 3和Meta Smart Glasses上市,对延时的要求会变得更高,为了用户体验可能会诞生新的传输格式、压缩算法来进一步降低时延。即便死守着多媒体技术老本行,依然有许多工作要做。这是信心的第二个来源。

第三,何亚明认为
AIGC会开始逐步商业化落地。
比如目前
传统的服务行业(需要和人沟通的场景,需要24小时在线的)对数字人就有很大的需求。
数字人被许多业内同行认为是当下为数不多的增量市场,也是多媒体技术与大模型结合非常成功的场景。包括电商直播、医疗咨询、保险客服等都有非常明确的客户需求和业务落地。

谈到出海,他觉得
国内公司积攒了很多能力,而海外的技术服务相对更标准化,更纯粹地比拼技术能力,中国厂商能够吃到非常多的红利。
尤其在社交、电商场景的应用创新,在海外给用户的体验依然是颠覆性的。

最后,何亚明希望
(多媒体企业出海)能像电动车那样,到海外有真正的定价权
,因为我们拥有处于领导地位的质量和技术。他坦言,我们的行业需要更多的协同和规范,从纯粹的竞争到取得行业共识,需要大家共同去努力。

总之,无论国内海外,面对多媒体的未来,一切都很难预测,但就像何亚明说的,
有时候科技的发展就像一辆高速驶来的列车,你远远地看着,疑惑怎么还不到?但当它真的从你旁边经过时,可能一不小心就过去了。

以下是何亚明的对话实录:

二十年,一场多媒体的缘分与螺旋

Q1

LiveVideoStack
:我记得应该是2018年,亚明老师回国。我印象特别深刻。我接到了一封邮件,然后我一看,这个人好厉害:之前在Facebook、微软,目前在阿里巴巴,我当时在想,我们有这么大吸引力吗?当时特别欣喜,后来顺理成章地邀请亚明过来做2018 年LiveVideoStackCon北京的分享,后续也有陆陆续续的交流,现在想起来还有点梦幻。

我想从两个视角提问。

首先是全球的视角,你的(工作)经历基本上在流媒体这个圈子里,差不多20多年了,是行业的前辈。第二,从国内的视角看,你回国这几年正好赶上流媒体爆发式的增长,从千播大战、教育、社交、游戏等,包括疫情所带来的远程办公需求的暴增,但现在(生态)又进入到一个调整阶段,国内互联网的流量在下降,用户在逐步回归到混合或传统的线下交流,同时大环境又受到整个经济周期的影响。如果将这两个视角结合在一起,你怎么来回顾总结自己20多年与流媒体相爱相伴,简单地说说你的感受。

何亚明 :
这让我回到了2018年的时候跟你第一次接触的场景,我觉得在中国有一个LVS这样一直在音视频圈子里做交流的组织者还是非常感谢的,也非常感谢你的坚持,
我们只是在不同的战线都在坚持着。

言归正传,我最早踏入音视频圈子是2000年,那个时候刚刚去微软,
这是视频(应用)的第一波爆发
,那时解决的问题就是把
视频在线化
,无论是Windows media player还是Real的RM,微软的WAV,H.263、H.264才刚刚出来,所有人都想的是“我们把视频在线化”,于是各种player、各种各样的媒体文件的format涌现出来,但还没有进化到流媒体,还停留在container这种format。

第二波则是直播行业出现。
一开始是体育、赛事这样的一些直播,那个时候微软也积极地投入到各种流媒体协议的设计,从最早的media stream,smooth streaming (Dash的前身),然后Dash、HLS的各种协议,迎来了(视频应用)第二波爆发。

第三波是RTC时代。
不管是Facebook的Messenger、Zoom、微信还是钉钉,特别是疫情以后,第三波RTC爆发把延迟从5到10秒降低到百毫秒级别,这也是我们过去经历的关键阶段。

我觉得音视频技术一直是呈螺旋式上升的
,过程是起起伏伏的,任何基础技术都是一样的,它不可能永远都在喷发。当你解决了技术的基本问题之后,投资和炒作就会降温,但技术一直在发展。从我刚入行到现在,每天都在解决不同的技术问题,不会存在一个阶段无事可做。不管怎样起伏,我对音视频还是一直抱有热爱和期望的。

紧跟着,下一波又到来。
下一波应该是AI和AIGC相结合的
,它有可能引爆我们视频行业里非常多的需求增长。比如视频编码,以前不管H.264、H.265、H.266或AV1、AVS,它都是基于人为的块划分,而基于大模型的编码方式是不是能更加符合人眼特征来做?这实际上是一个机会。
每次当一个新技术出现的时候,音视频的底层技术也会出现一个大爆发,需求也会相应地增长。

总结来看(音视频)是一个螺旋式的发展,即使陷入增速低谷,很多事情我们还要做,整体上技术都是在往前推进。

从微软到Meta,视频技术是创新必备的基础力

Q2

LiveVideoStack
:你横跨中美,经历了20多年的行业发展,如果对比国外与国内多媒体生态,你有什么不同的感受?

何亚明
:前段时间跟前同事聊天,聊到AI时代对流媒体的冲击,可能有些表面看起来是负面的,但实际上背后可能并非如此。

可能你听说了微软把Azure media service“退役”了,Azure media service提供包括转码等各种基础多媒体能力,微软认为这是非常成熟的技术,很多供应商都可以来做。但微软并没有裁掉任何一个人,团队全体进入到Windows Copilot,让 Copilot与多媒体更好地结合。虽然(团队)仍然做多媒体的老本行,但是要面临Copilot AI这个新命题,这个是微软的例子。

我也和很多Meta的同事聊,他们也经历了非常多的变化,从All in元宇宙遇到各种挫折,但所有从事流媒体的人都还在,他们把媒体当作一项基本的能力,(鼓励)大家去做创新的小项目,比如带两个摄像头的手表,听起来挺无厘头的,但在不断地尝试(流媒体)跟硬件、AI结合,把视频技术用到各个小的创新点上,这个趋势跟国内的确不太一样。

Q3

LiveVideoStack
:最近有本关于FFmpeg的新书,也许未来讲流媒体底层的书可能会越来越少,可能会出现“如何用Copilot做流媒体开发”,甚至通过自然语言,就可以做转码服务,推流,流媒体技术会更大众化。

何亚明
:是的。底层转封装、转格式这些能力,不会成为多媒体从业者的竞争力。因为你会FFmpeg,所以你有竞争优势,(未来)一定不是这样的。但是我们一直在做To B,每天服务很多客户,需要深度了解多媒体各种应用场景,了解业务的本质,对从业者的要求变得更高了。接下来你还要了解各种大模型,如何在大模型基础上做fine tuning,做各种各样下游的任务,这些都是我们所要具备的这个能力。

AI的决策提效,大模型的交互变革

Q4

LiveVideoStack
:谈到人工智能大模型, 流媒体经过了一个快速发展期,现在进入到相对饱和的阶段。如果从信息论的角度看,视频编码我们已经接近信道理论的极限了,可能还有1%-2%需要突破,但遗留下来的问题,相对于我们的付出,回报已经很有限了。相较之下,大模型所带来的增益非常明显,甚至在某些场景下的编码效率已经超过了传统的编码框架,从业者们应该以什么样的心态来看待这个行业?

这里面有两个视角,一个是相对狭隘的视角,还在做流媒体技术栈的这些人该怎么办?还有更广义的视角,流媒体可以包容任何技术栈,无论是人工智能还是GPT,都可以为我所用,我们虽然做的是流媒体,但不会拘泥于自身的技术栈。从这个角度来看,是不是流媒体未来还有非常大的空间,你怎么看?

何亚明
:首先我觉得GPT是一个范式的变化。我们现在经历的互联网时代,在很大程度上解决了信息不对称的问题,减少了信息差。尽管我们以前也用AI,但现在大模型下AI、AGI能力的涌现让我觉得它
不是一个简单的增加效率或降低成本的问题,而更多带来的是决策效率上的提升。
所以大模型会给所有行业带来变化,焦虑是正常的,但是我们也会看到后面更多的机会。

回到流媒体,
不论这波AI(发展成)什么样,音视频一直是离用户最近的
,不论AI技术怎样呈现(文生图、文生视频),它展现的载体依然是视频类这种流媒体形式的。虽然现在可能只是简单地用AI把视频画质提升了,但我相信未来一定会带来一些新的变化。

回顾互联网的历史,每次交互方式的变化都会带来一场革命,而大模型带来范式的变化必然会引发交互形式的变化。
从最早Windows、Mac这些GUI的图形界面带来了交互方式的飞跃,促成了互联网的发展;用手指操作的手机进入到移动互联网,又带来新的交互方式。

在当下这个时代我们马上面临的是用自然语言来交互的界面,通过视觉跟空间感知的新的交互形式,实际上现在已经可以看到一些雏形了。与ChatGPT交流已经很惊艳了,但交互方式依然是文字,不过ChatGPT新版支持语音交互,我经常问ChatGPT一些书籍的问题,甚至已经不需要那些传统的听书软件了。

除了这种交互方式,下一个变化,就是我们
对这个世界的感知
,而面对空间交互的感知又要依赖数字内容的涌现。
总结来看,AI、AGI会让数字资产、数字内容海量地增长,很多时候(这些内容)都是用视频来承载的,这对于视频从业者其实是个机会。

另外,
交互方式的变化也带来了对视频技术的新需求。
不管是苹果的Vision Pro,或者是 Meta Quest,又或是Smart Glasses,从大方向上来看,视频仍然是跟AI非常紧密的,它仍然可以站在AI技术的风口浪尖上面。

回到狭义的多媒体技术,我觉得视频从业者需要有更多的改变,因为我们经过这么多年的努力(开源、标准化),现在视频的使用门槛已经变得非常低了,所以要主动去拥抱这波AIGC。

这也是为什么阿里云视频云传统做工程的同学也积极投入到视频算法的研究里面。但是好在开源的东西特别多,多模态大模型也很多,根据我们专有的知识库来使用它,这是我们希望要解决的问题,也是每个多媒体从业者的机会。
因为不管各种 AI 技术如何涌现、爆发,最后它还是要回到“你用什么方式展现给你的客户、你的用户”上面来
,就要回到流媒体技术上来。

反过来说,流媒体技术也会有新的发展,包括苹果的Vision Pro,由此可能诞生新的format、新的传输的格式,再把交互的延迟降低,算力与AI、多媒体能力结合,虽然这些流媒体基础技术方面听起来并不是非常sexy,但都是基础工作。

另外,AI让内容爆发,结合交互方式的变化,视频不仅简单地应用到泛娱乐的互联网上,而是应用到各行各业中。对于多媒体从业者来说,是非常大的机会。

多媒体与AI互渗式发展,视频云尝到了甜头

Q5

LiveVideoStack
:大模型结合流媒体的小模型,或者细分场景,你和你的团队最近在做哪些探索?有没有一些工程的落地?帮助客户在效率上有比较大的提升?

何亚明
:对,实际上刚才讲的AI跟流媒体的结合很早就有了,以前我们有各种各样的算法,包括窄带高清视频增强的算法,超高清、插帧、超分和抠图等等。过去几年AI跟视频的结合一直在演进,但是这波AIGC的确是让我们看到了一些新机会。比如突破了一些技术瓶颈,比如利用大模型抠图的效果(比传统方式)会好很多,通过SAM、CLIP做视频的理解比以前传统的打标签精度和准确度会提高很多。

在阿里云视频云,我们认为
AI与多媒体的结合分为三步。第一步,让AI更好地融入到多媒体引擎里。
AI很多时候需要跑在GPU上,而一般的多媒体处理还跑在CPU上,内部的封装格式都不一样,你可能需要YUV,我可能需要压缩格式,AI和多媒体处理都是基于各自的算子,各自独立。于是第一步我们做的就是在架构底层上,让AI(能力)是媒体能力可以在最小的帧级别进行融合,AI能力变成像FFmpeg的一个filter,在架构上让AI与多媒体有更好的结合。

第二步,我们正在做的用大模型平替传统方法的工作。
比如刚刚提到的抠图,以及视频理解,都可以从平替中尝到“甜头"。

第三
,我们一直在考虑,
通过大模型参与到业务决策和客户沟通中来。
我们现在非常多的业务决策还是靠模板、人工配置,需要不断地跟客户沟通需求,这是我们在实施To B服务过程中的效率瓶颈。大模型可能提供决策能力,
我们希望构建一个属于视频的大模型,贴近我们的业务本质。
我们有大量的客户需求、案例与视频内容,基于这些资源,在通用大模型的基础上,在多模态大模型基础上fine tuning,定义好奖励机制,最终构形出一个决策系统。我们希望这个大模型可以极大地提升决策效率,也让系统也变得更加的简单。这还要一步一步来实现。

Q6

LiveVideoStack
:有没有一些业务单点上可以看到比较明显的收益了?

何亚明
:在2023年的云栖大会上我们展示了一些案例,
其中一个,是在云导播台上利用大模型抠图
,能够在多人复杂场景下实时抠图,现在已经落地到产品里了。广电传媒与电视台的很多的同事看了以后觉得非常好。以前大家都觉得广电传媒有自己专业的工具效果更好,同时担心数据安全问题不愿意上云,云导播台的实时抠图确实打开了一个突破口。

第二,是大家都在聊的数字人,
(通过大模型)让数字人的体态更加自然,可以很快地训练出相似度非常高的、非常自然的语音,再结合云剪辑技术,批量生成数字人内容,云栖大会现场,大家对这种技术也是非常关注的,央视CCTV-2也对我们的这项“数字人X云剪辑”的技术应用做了现场报道。

第三,视频自动标签。
做媒体、做新闻是非常依赖视频标签的,来搜索和过滤内容。以前都是通过小模型打标签、人工打标,周期长、成本高。现在通过多模态自动打标签,泛化性非常好,目前正在落地中。在不同的场景具体的需求不同,比如在传媒领域对内容的精度要求非常高。我们在和各行业的同事交流过程中发现,他们对于这项技术都非常感兴趣,
大模型帮我们打开了除互联网泛娱乐之外的空间,可以真正地解决耗时耗力的工作效率问题。

Q7

LiveVideoStack
:数字人应用比较主流的场景是什么?电商直播带货、游戏、社交陪聊?

何亚明
:首先,直播带货是一个很大的场景,比如24小时无人讲解带货。第二,在医疗和保险,比如小病的咨询、保险客服等。这里并不是简单地订两个数字人,而是针对每个员工做数字分身,而且又不能让平台客户觉得这是一个数字人(要给客户更强的信任感),这就对数字人的要求很高,让其具备了一些问题解答能力,更要在声音和形态上都非常逼真,目前我们的数字人声复刻能力已经达到和真人难辨的水平,而且训练门槛和成本也很低。我发现,
传统的服务行业(需要和人沟通的场景,需要24小时在线的)对数字人的需求非常大。

AI时代,翻新视频云的每一环技术

Q8

LiveVideoStack
:刚刚也提到,大模型带来的是交互方式的变革,也会降低流媒体从技术到应用的门槛。你们正在为未来做哪些准备?

何亚明
:围绕着AGI带来的这一波,整个的交互方式变得更真实,一切都是围绕着交互的内容来做,来应对相应的挑战。

第一,交互形式的变化会带来算力的挑战。
在服务端,现在GPU卡很难买,阿里云也在着手解决这些问题,尤其是与CPU的厂商联合起来优化算法,(在推理的时候)尽可能摆脱GPU。虽然可能损失一些精度,但很多时候是可用的。同时为了节省GPU,让CPU协助GPU,结合场景做优化,(一定程度上)弥补算力的不足。

另外在终端上有很多大模型落地,包括硬件厂商,比如高通骁龙8 Gen3上已经可以跑大语言模型,未来移动终端硬件的性能会进一步提升。
基于大模型大量的算力要做端上的架构优化,实际上也有很多端上推理的架构,优化端智能是我们现在看好的方向,让算力在云和端之间做好平衡。

第二,交互还带来了延迟的挑战。
在高保真虚拟现实的场景下,(控制)延迟是非常重要的,不管是算力的延迟还是传输的延迟,都需要一张很好的传输网络,这也是我们一直在做的,以MediaUni(GRTN)
一张网
同时支持标准与低延迟直播、实时音视频通话、云渲染,数据传输、远程控制等多元化业务,同时实现算网融合,达到高可靠和低成本的动态平衡。

整体来看,作为重要的融合,我们一直在打造
这张传输与算力网,同时,在边缘侧部署了GPU算力,在上面提供智能的媒体服务,把更多的多媒体引擎和AI引擎相结合。
目前,我们的MediaService在帧级别融合媒体和AI能力,完成从传统媒体处理到AIGC的顶层设计和进化,用AI重塑业务能力,最终实现媒体服务的智能化、多样化、高效化。

在视频编码方面我们推动MediaCodec
智能编码
,将传统编码与AI深度结合,从商业视角,在努力达到视频质量、成本、算力需求的最佳平衡。同时,深入行业场景,提供简单、低门槛的接入能力,通过MediaBox
一体化
终端套件,满足视频能力在行业化、场景化、智能化下能够快速上线。

总结来看,
在AI时代需要把以前做过的事情都再升级、再进化,视频云的整个架构要迎接AI时代。
我感觉AI时代发展很快,每天好像都有新兴事物出来,日新月异。

悲观者是对的,但乐观者会成功

Q9

LiveVideoStack
:国内已经进入到存量市场,内卷严重。在去年结束的LiveVideoStack深圳大会上,约1/3的话题都和降本有关。很多企业也在出海寻找机会,你认为行业什么时候能够回暖?

何亚明
:的确,国内和国外的环境有很大区别。国内谈降本谈得很多,国内基本上所有行业都面临经济寒冬或资本寒冬,大家都想要先活下来。实际上可能有点过于悲观了,
当然我认为悲观者永远是对的,但是只有乐观者才有可能成功。

阿里云视频云去年也做了大量的降本工作,包括降低直播带宽成本、服务器成本,刚刚也讲了端智能,CPU与GPU混合计算等。另外在业务侧,我们也通过技术让直播的架构从传统的三层到灵活的一层或两层,包括对冷流优化等进一步降本。

国内竞争环境还是比较激烈的,更多追求的还是眼前,国外的环境更能看到未来的很多东西,所以诚恳地讲,在中国做视频行业是比较难的。但还是希望商业环境能够越来越好,可能美国也经历过很粗暴的原始积累,但如今它的多媒体行业是有比较清晰的界限的,做CDN的、做流媒体的,大家都有自己focus的方向,行业规则也比较成熟、标准化。我相信中国各个互联网厂商未来一定会意识到,建立行业的一些标准来共同发展。

回到你的问题,关于多媒体行业的回暖爆发时间,我很难预测,但我感觉会很快。
有时候科技的发展就像一辆高速驶来的列车,你远远地看着,疑惑怎么还不到?但当它真的从你旁边经过时,可能一不小心就过去了。
现在不像以往可以很safe地说3到5年的发展,已经很难知道下一代技术爆发什么时候到来,也许就是2024。

前阵子Meta的朋友给我演示了Smart Glasses,通过摄像头采集画面,可以语音提问这是什么东西?AI Glass背后是Meta的大语言模型,能够回答你的各种问题,这个demo挺震撼的。当然他们也碰到包括延迟和响应速度的问题,(对于技术人和团队而言)这些都是机会。
如果(像Smart Glasses这样的)消费级别的产品爆发,它会推着流媒体技术往新的方向走
,我们可以在这些新的方向再来“卷”。

Q10

LiveVideoStack
:最后一个问题,国内非常卷。大量的企业包括个人都想去海外,或者已经在做出海的事情了。你怎么看出海?阿里云视频云出海面临怎么样的局面?

何亚明
:出海这个战略应该是中国所有互联网公司都在做的一个事儿。阿里的财报也提到,海外业务增长是最快的。我们在国内积攒了很多能力,可以去东南亚、中东和欧洲这些区域,能够把我们的能力快速地应用到他们的产品上。而且海外的技术服务相对更标准化,更纯粹地比拼技术能力,中国厂商会吃到非常多的红利,包括
我们在社交、电商场景的应用创新,在中国非常司空见惯了,但在海外给用户的体验还是颠覆性的。

在音视频的技术领域,中国和美国是走在前列的,出海也能够把我们的技术输出,帮助当地把他想要的应用快速孵化出来。我觉得这是一个双赢的局面。
我希望(多媒体企业出海)能像电动车那样,到海外有真正的定价权,因为我们拥有处于领导地位的质量和技术。这是一个长期的赛道,我们的行业需要更多的协同和规范,从纯粹的竞争到取得行业的共识,甚至形成真正的联盟,这需要大家共同去努力的。

同时可以看到,国内与海外的音视频服务生态存在很大区别。海外的AWS、Azure media service,都是标准化和模块化的,很少提供端到端一体化服务,非常强调文档的标准化、接口的标准化以及各个产品之间统一的规范。这是需要整个生态来支撑的,不是某一家自己能做的。
希望我们能够共同改变一些东西,真正把(多媒体技术服务)做到标准化、灵活化。