EPICS通道访问介绍以及练习

news2024/10/6 10:38:43

提纲

1) 通道访问概念

2)通道访问API

3) 简单的CA客户端

4)使用回调的简单CA客户端

EPICS概要

搜索和连接过程

搜索请求

1)搜索请求由一系列UDP包组成

  • 只发送给EPICS_CA_ADDR_LIST
  • 从短时间间隔开始,每次加倍
  • 直到它变得大于5秒,接着它保持在5s
  • 在100个包后停止或者它得到了响应
  • 在它见到一个异常beacon或者创建一个新PV前,不再尝试
  • 发完所有100个包的全部时间大约8分钟

2)服务程序必须为每个包进行一次存在测试

3)通常在首个活前几个包时连接

4)不存在的PVs产生大量流量:尝试消除它们

Beacons

1) 一个beacon是一个由服务程序发送的UDP广播包。

2)当它健康时,每个服务程序以规则间隔发送一个UDP广播包(像一个心跳)

EPICS_CA_BEACON_PERIOD默认15秒。

3)当它启动时,每个服务程序广播一个启动序列的UDP beacons

  • 从短间隔开始(25ms, 对于vxWorks 75ms)
  • 每次间隔加倍
  • 直到它变得大于15s,接着它保持在15s。发出大于10个beacons和40s变成准备状态

4) 客户端监视这些beacons

  • 确定连接状态,是否再次发送搜索

虚电路断开

1) 3.13和早期3.14

  • 挂断消息或者30秒内无来自服务程序的响应。
  • 如果不是一个挂断,则客户端发送"你在吗"的请求。
  • 如果5秒无响应,TCP连接被关闭
  • MEDM窗口变白。
  • 客户端再次发送搜索请求

2)3.14.5以及以后

  • 来自服务程序的挂断消息。
  • TCP连接被关闭
  • MEDM变白
  • 客户端再次发送搜索请求

虚拟电路无响应

3.14.5以及以后

1)30秒内无来自服务程序的响应。

2)客户端接着发送"你在吗"的请求

3)如果5秒内无响应,TCP连接不被关闭,至少若干小时

4)MEDM窗口变白

5)客户端不重新发送搜索请求,有利于网络风暴

6)不调用ca_poll的客户端经常见到虚电路断开,即使服务程序可能是正常的。

  • 为3.13编写但使用3.14的客户端会遇到问题
  • 在以后版本中可能被修改。

重要的环境变量

1) EPICS_CA_ADDR_LIST

a.决定搜索哪里
b.是一个列表(由空格分隔)

如:"192.168.1.255 172.16.2.20 10.30.55.50"

c.默认是主机上所有网卡的广播地址

当服务器和客户端在相同子网时有效

d.广播地址
  • 发送给一个子网上所有服务器
  • 示例:192.168.1.255
  • 在UNIX上使用ifconfig -a查找它

2)  EPICS_CA_AUTO_ADDR_LIST

  • YES:在搜索中包含以上默认地址
  • NO:不要再默认地址上搜索
  • 如果你设置了EPICS_CA_ADDR_LIST, 通常设置这个为NO。

 其它重要的环境变量

1)CA客户端
  • EPICS_CA_ADDR_LIST
  • EPICS_CA_AUTO_ADDR_LIST
  • EPICS_CA_CONN_TMO
  • EPICS_CA_BEACON_PERIOD
  • EPICS_CA_REPEATER_PORT
  • EPICS_CA_SERVER_PORT
  • EPICS_CA_MAX_ARRAY_BYTES
  • EPICS_TS_MIN_WEST
2)CA服务器
  • EPICS_CAS_SERVER_PORT
  • EPICS_CAS_AUTO_BEACON_ADDR_LIST
  • EPICS_CAS_BEACON_PERIOD
  • EPICS_CAS_BEACON_PORT
  • EPICS_CAS_INFT_ADDR_LIST
  • EPICS_CAS_IGNORE_ADDR_LIST

3.13和3.14类似

  • 为了使为3.13编写的客户端在没有编码修改下对3.14有效,付出了非常大的努力
  • 甚至像MEDM的大型程序只需要进行少量的修改
  • 这表示已有程序一般不需要被重新编写。
  • 相比较,在切换到3.14中通道访问服务器需要很多更改

3.13和3.14不同

1) 3.14是线程的

你的程序不是必须是线程的。

2)3.14对某些功能有不同的名称。

  • ca_context_create对应ca_task_initialize
  • ca_context_destroy对应ca_task_exit
  • ca_create_channel对应ca_search_and_connect
  • ca_create_subscription对应ca_add_event
  • ca_clear_subscription对应ca_clear_event
  • 新函数可能有更多功能,通常与线程相关
  • 我们将使用新名称。

3)3.14对丢失连接有不同机制

  • 虚电路无响应(3.13中不可用)。
  • 虚电路断开

编写一个通道访问客户端的基本过程

1)初始化通道访问

ca_task_initialize或ca_context_create

2)搜索

ca_search_and_conntect或ca_create_channel

3)进行读或写

ca_get或ca_put

4)监视

ca_add_event或ca_create_subscription

5)让通道访问有机会运行

ca_poll, ca_pend_io, ca_pend_event

6)关闭通道访问

ca_task_exit或ca_context_destroy

所有C或C++程序必须包含cadef.h:#include <cadef.h>

库函数介绍

1)ca_context_create

enum ca_preemptive_callback_select{
    ca_disable_preemptive_callback,
    ca_enable_preemptive_callback
};

int ca_context_create(enum ca_preemptive_callback_select SELECT);
  • 在任何其它调用前被调用一次
  • 设置通道访问
  • 除非你想要使用多线程,否则SELECT=ca_disable_preemptive_callback
  • 为了3.13兼容性,也可以使用ca_task_initialize()

2) ca_context_destroy

void ca_context_destroy()
  • 在退出你的程序前,应该被调用。
  • 关闭通道访问。
  • 为了3.13兼容性,也可以使用ca_task_exit()

3) ca_create_channel

typedef void caCh(struct connection_handler_args ARGS);
int ca_create_channel(
    const char * PVNAME,
    caCh * CALLBACK,
    void * PUSER,
    capri PRIORITY,
    chid * PCHID
);

a) 设置一个通道访问并且开始搜索过程。

b) PVNAME是过程变量的名称。

c) CALLBACK是你连接回调(或者NULL)

  • 当连接状态变化时,包括首次连接,将调用这个回调
  • 有关这个通道的消息被包含在ARGS中
  • 如果你不需要一个回调,使用NULL

d) PUSER是一种传递其它参数的方式

  • 你拥有的任何东西被存储在这个地址中
  • 它被存储在chid中
  • 在C++中,它经常是对应一个类的this指针
  • 如果你不需要它,使用NULL

e) 使用PRIORITY=CA_PRIORITY_DEFAULT

f) 一个chid是一个指向一个不透明struct的指针(地址),通道访问使用它存储有关这个通道大部分信息。chanId与chid相同(typdef chid chanId)

g) PCHID是chid指针的地址(使用&CHID)

  • 在进行调用前,你需要为chid分配空间
  • 通道访问将为这个struct分配空间并且返回它的地址

h) 使用宏访问chid中的信息

  • ca_name(CHID):获取这个过程变量的名称
  • ca_state(CHID):获取连接状态
  • ca_puser(CHID):获取你指定的PUSER

i) ARGS struct在连接回调中包含chid

j) 为了3.13兼容性也可以使用ca_search_and_connect()

4) ca_clear_channel

int ca_clear_channel(chid CHID);
  • 关闭一个通道访问并且回收资源。
  • 在退出这个程序前,应该被调用。
  • CHID与在ca_create_channel中使用相同的chid

5) ca_array_get

int ca_array_get(
    chtype TYPE,
    unsigned long COUNT,
    chid CHID,
    void * PVALUE
);

a) 从过程变量请求一个标量或者值的数组

b) 一般之后为ca_pend_io

c) TYPE是你变量的外部类型

  • 使用db_access.h中DBR_XXX类型之一
  • 例如:DBR_DOUBLE或DBR_STRING

d) COUNT:要读取的数组元素数目。

e) CHID:来自ca_create_channel的通道标识符。

f) PVALUE:你想要获取值存入的位置。必须有足够空间保存这些值。

6) ca_array_get_callback

typedef void (*pCallback)(struct event_handler_args ARGS);
int ca_array_get_callback(
    chtype TYPE,
    unsigned long COUNT,
    chid CHID,
    pCallback USERFUNC,
    void * USERARG
);

a) 使用一个回调,从一个过程变量请求一个标量或者值的数组。

b) TYPE:是你变量的外部类型。

  • 使用db_access.h中DBR_XXXX类型之一。
  • 例如:DBR_DOUBLE或DBR_STRING

c) COUNT:要读取的数组元素数目

d) CHID:是来自ca_create_channel的通道标识符。

e) USERFUNC:在操作结束时要被运行的你的回调的名称。

f) USERARGS:一种传递其它信息给这个回调的方式

  • struct event_handler_args有一个void * usr成员

7) ca_array_put

int ca_array_put(
    chtype TYPE,
    unsigned long COUNT,
    chid CHID,
    const void * PVALUE
);

a) 请求写一个标量或者值数组到一个过程变量

b) 一般之后为ca_pend_io

c) TYPE是你提供的外部类型

  • 使用db_access.h中DBR_XXXX类型之一
  • 例如:DBR_DOUBLE或DBR_STRING

d) COUNT是要写的数组元素的数目。

e) CHID是来自ca_create_channel的通道标识符。

f) PVALUE是从哪里寻找要被写的值。

8) ca_array_put_callback

typedef void (*pCallback)(struct event_handler_args ARGS);
int ca_array_put_callback(
    chtype TYPE,
    unsigned long COUNT,
    chid CHID,
    const void * PVALUE,
    pCallback USERFUNC,
    void * USERARG
);

a) 请求写一个标量或值数组到一个过程变量

b) TYPE是你变量的外部类型

  • 使用db_access.h中DBR_XXXX类型之一
  • 例如:DBR_DOUBLE或DBR_STRING

c) COUNT:要写的数组元素数目。

d)CHID:来自ca_create_channel的通道标识符。

e) PVALUE:查找要被写值的位置。

f) USERFUNC:当操作结束时,要被运行的你的回调的名称。

g) USERARG:一种传递其它信息给这个回调的方法。

  • struct event_handler_args有一个void * usr成员。

9) ca_create_subscription

typedef void (*pCallback)(struct event_handler_args ARGS);
int ca_create_subscriptino(
    chtype TYPE,
    unsigned long COUNT,
    chid CHID,
    unsigned long MASK,
    pCallback USERFUNC,
    void * USERARG,
    evid * PEVID
);

a) 指定一个回调函数,当这个过程变量经历显著状态变化时,被调用。

  • 值,警报状态,警报严重性
  • 这是监视一个过程变量的方法

b) TYPE是你想要返回的外部类型

  • 使用db_access.h中DBR_XXXX类型之一
  • 例如:DBR_DOUBLE或DBR_STRING

c) COUNT是要监视的数组元素数目

d) CHID是来自ca_create_channel的通道标识符。

e) MASK有对应于请求的事件触发类型的位集合

  • DBE_VALUE:值变化。
  • DBE_LOG:超过存档死区。
  • DBE_ALARM:警报状态变化

f) USERFUNC在状态变化发生时要被运行的你的回调的名称。

g) USERARG是一种传递其它信息给回调的方法。

  • struct event_handler_args有一个void *成员。

h) PEVID是一个evid(event id)的地址。

  • 在进行调用前你需要为evid分配空间
  • 类似chid
  • 仅用于清理订阅(如果不需要,可以是NULL)

10) ca_clear_subscription

int ca_clear_subscription(evid EVID);
  • 用于移除一个monitor回调
  • EVID是来自ca_create_subscription的evid

11)ca_add_exception_event

typedef void (* pCallback)(struct exception_handler_args ARGS);
int ca_add_exception_event(
    pCallback USERFUNC,
    void *USERARG
);

a)用于替代默认的exception处理程序。

b) USERFUNC是在一个异常发生时要被运行的你的回调的名称。

  • 使用NULL移除回调。

c) USERARG是一种传递其它信息给这个回调的方式。

  • struct exception_handler_args有一个void * usr成员

请求处理

1) 先前的例程是请求

  • 它们仅排队这个操作
  • 它们基本不出错。返回值基本总是ECA_NORMAL,但它们应该被检查。

2)仅在一下之一被调用时,这些请求才被处理。

  • ca_pend_io:在请求被处理前阻塞。
  • ca_pend_event:阻塞指定的时间。
  • ca_poll:仅处理当前工作。

3)如果这些例程没有被调用,请求不被处理并且后台任务也不被处理。

4)规则是这些之一应该每100ms被调用:允许后台任务运行(beacons等)。

1) ca_pend_io

int ca_pend_io(double TIMEOUT);

a)清空发送缓存。

b) 在以下发生前,最多阻塞TIMEOUT秒 

  • 待处理gets结束
  • 没有回调已经连接的搜索

c) 当gets和搜索结束时,返回ECA_NORMAL

d)返回ECA_TIMEOUT,否则

  • 表示某过程出错
  • get请求可以被再次发出
  • 在ca_clear_channel后,搜索请求可以被再次发出。

e) 通道访问后台任务被执行

  • 除非没有待处理的I/O请求

f) 与不使用回调的搜索,gets和puts一起使用。

2) ca_pend_event

int ca_pend_event(double TIMEOUT);

a) 清空发送缓存。

b) 在TIMEOUT秒内运行后台任务。

  • 在TIMEOUT秒耗尽时才返回。

c) 当你的程序不是必须做任何其它事情时使用这个函数。

d) 使用ca_pend_event替代sleep

3) ca_poll

int ca_poll();

a) 清空发送缓存。

b) 仅运行待处理任务。

  • 当没有待处理任务时退出,否则类似于ca_pend_event

c) 当你的程序有要做的其它事情时,使用这个函数。

  • 例如:大多数GUI程序。

d)确认它至少每100ms被调用。

4) CHID宏

chtype ca_field_type(CHID);
unsigned ca_element_count(CHID);
char * ca_name(CHID);
void * ca_puser(CHID);
void ca_set_puser(chid CHID, void * puser);
enum channel_state{
    cs_never_conn, //有效chid,服务器未找到或者不可用
    cs_prev_conn,  //有效chid,先前连接到服务器
    cs_conn,       //有效chid,连接了服务器
    cs_closed      //用户删除了通道
};
char * ca_host_name(CHID);
char * ca_read_access(CHID);
char * ca_write_access(CHID)

5) ca_connection_handler_args

struct ca_connection_handler_args{
    chidId chid; // 通道id
    long op;     // CA_OP_CONN_UP/CA_OP_CONN_DOWN
};
  • 在连接回调中使用
  • 注意:使用了chanId而不是chid,某些编译器不认chid chid
  •  

6) event_handler_args

typedef struct event_handler_args{
    void * usr;  // 提供给请求的用户参数
    chanId chid;  //通道ID
    long type;  //返回项的类型
    long count;   //返回项的元素数目
    const void * dbr; //指向返回项的指针
    int status;       //请求op的ECA_XXX状态
};
  • 在get, put和monitor回调中使用
  • 如果status不是ECA_NORMAL,不要使用dbr中的值。

7) 通道访问API函数

 

简单的CA客户端

1) 使用以下数据库文件,此数据库文件由两个记录组成一个wavefrom记录和一个stringin记录。

record(waveform, "$(USER):wfin") {
  field(DESC, "A Example Waveform")
  field(SCAN, "Passive")
  field(NELM, "10")
  field(FTVL, "LONG")
}

record(stringin, "$(USER):StrIn") {
  field(DESC, "A Example StringIn")
  field(SCAN, "Passive")
  field(VAL, "HelloWorld")
  field(PINI, "YES")
}

2)将以上数据库加载到一个IOC中产生一个两个记录实例:

epics> dbl
TEST:StrIn
TEST:wfin

3) 用EPICS base自带的通道访问命令测试以上两个记录:

orangepi@orangepi4-lts:~/host_program/host/hostApp$ caget TEST:StrIn
TEST:StrIn                     HelloEveryOne
orangepi@orangepi4-lts:~/host_program/host/hostApp$ caget TEST:wfin
TEST:wfin 10 1 2 3 4 5 6 7 8 9 10

4) 用以上讲解的通道访问API函数写一个自己的通道访问程序,源代码如下:

orangepi@orangepi4-lts:~/host_program/host/hostApp$ cat simpleget.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

/* EPICS header */
#include "cadef.h"
#define epicsAlarmGLOBAL
#include "epicsEvent.h"
#include "epicsMutex.h"

#define TIMEOUT         1.0
#define SCA_OK          1
#define SCA_ERR         0
#define MAX_STRING      40

int main(int argc, char **argv)
{
        int stat;
        chid pCh;
        char pvname[20] = {0};

        char svalue[100];
        int  ivalue[20];
        void * pvalue;

        if (argc != 2){
                printf("Usage: %s channelname\n", argv[0]);
                exit(1);
        }

        strcpy(pvname, argv[1]);
        printf("PVNAME: %s\n", pvname);

        /* Initialize channel access */
        stat = ca_context_create(ca_disable_preemptive_callback);

        if (stat != ECA_NORMAL){
                printf("ca_context_create failed:\n%s\n",ca_message(stat));
                exit(1);
        }
        else{
                printf("channel accesss initialized successfully\n");
        }

        /* create the pv */
        stat = ca_create_channel(pvname, NULL, NULL, CA_PRIORITY_DEFAULT, &pCh);

        if (stat != ECA_NORMAL){
                printf("ca_create_channel failed:\n%s\n", ca_message(stat));
                goto EXIT;
        }
        else{
                printf("PV for channel name %s created successfully\n", pvname);
        }


        /* call ca_pend_io to process the search */
        stat = ca_pend_io(TIMEOUT);
        if (stat != ECA_NORMAL)
        {
                printf("search for PV:[%s] timed out after %g sec", pvname, TIMEOUT);
                goto DESTROY;
        }

        /* Macro TEST */
        printf("MACRO TEST:\n");
        int request_type = ca_field_type(pCh);
        printf("ca_field_type(CHID): %d\n", request_type);
        long request_count = ca_element_count(pCh);
        printf("ca_element_count(CHID): %ld\n", request_count);
        printf("ca_name(CHID):%s\n", ca_name(pCh));
        printf("ca_state(CHID): %d\n", ca_state(pCh));
        printf("ca_host_name(CHID):%s\n",ca_host_name(pCh));
        printf("ca_read_access(CHID):%d\n", ca_read_access(pCh));
        printf("ca_write_access(CHID):%d\n", ca_write_access(pCh));

        printf("DBR_STRING: %d\n", DBR_STRING);
        printf("DBR_LONG: %d\n", DBR_LONG);

        /* Request Get */
        if (request_type == DBR_STRING){
                pvalue = svalue;
        }
        else if(request_type == DBR_LONG){
                pvalue = ivalue;
        }
        else{
                printf("NO SUPPORT TYPE: %d\n", request_type);
                goto DESTROY;
        }

        /* Request the get */
        stat = ca_array_get(request_type, request_count, pCh, pvalue);

        if (stat != ECA_NORMAL){
                printf("ca_array_get failed:\n%s\n", ca_message(stat));
                goto DESTROY;
        }

        /* call ca_pend_io to get the values */
        stat = ca_pend_io(TIMEOUT);
        if (stat != ECA_NORMAL){
                printf("get %s timed out after %g sec\n%s\n", pvname, TIMEOUT, ca_message(stat));
                goto DESTROY;
        }

        if (request_type == DBR_STRING){
                printf("PV[%s] ===> VAlUE[%s]\n", pvname, svalue);
        }
        else if (request_type == DBR_LONG){
                int i, ret = 0;
                for (i = 0; i < request_count; i++){
                        ret += sprintf(svalue + ret, "%d ", ivalue[i]);
                }

                printf("PV[%s] ==> VALUE[%s]\n", pvname, svalue);
        }

DESTROY:
        stat = ca_clear_channel(pCh);
        if (stat != ECA_NORMAL){
                printf("ca_clear_channel failed [%s] \n%s\n",pvname,ca_message(stat));
                goto EXIT;
        }

        /* clean up channel */
EXIT:   ca_context_destroy();
        printf("channel access context destroyed and exits the program\n");


        return 0;
}

编译以上程序代码,并且对以上记录执行,结果如下:

orangepi@orangepi4-lts:~/host_program/host/hostApp$ O.linux-aarch64/simpleget TEST:StrIn
PVNAME: TEST:StrIn
channel accesss initialized successfully
PV for channel name TEST:StrIn created successfully
MACRO TEST:
ca_field_type(CHID): 0
ca_element_count(CHID): 1
ca_name(CHID):TEST:StrIn
ca_state(CHID): 2
ca_host_name(CHID):192.168.50.184:5064
ca_read_access(CHID):1
ca_write_access(CHID):1
DBR_STRING: 0
DBR_LONG: 5
PV[TEST:StrIn] ===> VAlUE[HelloEveryOne]
channel access context destroyed and exits the program
orangepi@orangepi4-lts:~/host_program/host/hostApp$ O.linux-aarch64/simpleget TEST:wfin
PVNAME: TEST:wfin
channel accesss initialized successfully
PV for channel name TEST:wfin created successfully
MACRO TEST:
ca_field_type(CHID): 5
ca_element_count(CHID): 10
ca_name(CHID):TEST:wfin
ca_state(CHID): 2
ca_host_name(CHID):192.168.50.184:5064
ca_read_access(CHID):1
ca_write_access(CHID):1
DBR_STRING: 0
DBR_LONG: 5
PV[TEST:wfin] ==> VALUE[1 2 3 4 5 6 7 8 9 10 ]
channel access context destroyed and exits the program

通过以上实例程序编写和测试,可以进一步加强我们对EPICS通道访问函数的理解。

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

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

相关文章

vue部署在iis的字体获取报错,请求404 - 找不到文件或目录

配置MIME即可 在添加MIME类型中&#xff0c;增加以下信息&#xff1a; 文件扩展名&#xff1a;.woff MIME类型&#xff1a;application/x-font-woff

DHorse v1.3.0 发布,基于k8s的发布平台

综述 DHorse是一个简单易用、以应用为中心的云原生DevOps系统&#xff0c;具有持续集成、持续部署、微服务治理等功能&#xff0c;无需安装依赖Docker、Maven、Node等环境即可发布Java、Vue、React应用&#xff0c;主要特点&#xff1a;部署简单、操作简洁、功能快速。 新增特…

《ChatGPT原理最佳解释,从根上理解ChatGPT》

【热点】 2022年11月30日&#xff0c;OpenAI发布ChatGPT&#xff08;全名&#xff1a;Chat Generative Pre-trained Transformer&#xff09;&#xff0c; 即聊天机器人程序 &#xff0c;开启AIGC的研究热潮。 ChatGPT是人工智能技术驱动的自然语言处理工具&#xff0c;它能够…

深入理解 SQL:从基本查询到高级聚合

目录 背景理论知识示例1211. 查询结果的质量和占比&#xff08;Round group by&#xff09;1204. 最后一个能进入巴士的人 &#xff08;Having limit order by&#xff09;1193. 每月交易 I&#xff08;if group by&#xff09;1179. 重新格式化部门表1174. 即时食物配送 II&am…

关于DC电源模块输入电压范围的问题

BOSHIDA 关于DC电源模块输入电压范围的问题 DC电源模块是一种将交流电转换为直流电的设备&#xff0c;它非常常见且广泛应用于电子设备、通讯设备、工业自动化等领域。而其输入电压范围也是我们在使用和选购DC电源模块时需要特别关注的一个参数。 首先&#xff0c;我们需要了解…

java的空引用null和空字符串““

java中如果字符串变量指向null&#xff0c;表示空引用&#xff0c;此时对字符串求长度会抛出异常。 而""表示一个空字符串&#xff0c;对字符串求长度是可以的&#xff0c;求出来的字符串长度为0。 举例&#xff1a; package com.thb;public class Test6 {public s…

数字电路(一)

1、例题 1、进行DA数模转换器选型时&#xff0c;一般要选择主要参数有&#xff08; A&#xff09;、转换精度和转换速度。 A、分辨率 B、输出电流 C、输出电阻 D、模拟开关 2、下图所示电路的逻辑功能为&#xff08; B&#xff09; A、与门 B、或门 C、与非门 D、或非门 分析该…

导出文件下载进度条简单实现

前言 今天要跟大家分享的是一个导出数据进度条的简单实现&#xff0c;适用场景用在数据量大、组织数据耗时的情况下的简单实现。 一、设计思路 1、导出数据生成文件上传到OSS&#xff0c; 2、导出数据状态存redis缓存&#xff0c; 3、前端发导出请求后&#xff0c;返回的文件k…

Hudi Flink SQL源码调试学习(1)

前言 本着学习hudi-flink源码的目的&#xff0c;利用之前总结的文章Hudi Flink SQL代码示例及本地调试中的代码进行调试,记录调试学习过程中主要的步骤及对应源码片段。 版本 Flink 1.15.4Hudi 0.13.0 目标 在文章Hudi Flink SQL代码示例及本地调试中提到&#xff1a;我们…

分布式事务之本地事务

&#x1f680; 分布式事务 &#x1f680; &#x1f332; AI工具、AI绘图、AI专栏 &#x1f340; &#x1f332; 如果你想学到最前沿、最火爆的技术&#xff0c;赶快加入吧✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;CSDN-Java领域优质创作者&#x1f3c6;&…

大数据课程E1——Flume的概述

文章作者邮箱&#xff1a;yugongshiyesina.cn 地址&#xff1a;广东惠州 ▲ 本章节目的 ⚪ 了解Ganglia的概念&#xff1b; ⚪ 了解Ganglia的拓扑结构和执行流程&#xff1b; ⚪ 掌握Ganglia的安装操作&#xff1b; 一、简介 1. 概述 1. Flume原本是由Cloude…

国内外遥感数据处理软件对比

1.国内遥感数据处理软件概况 1.1北京航天宏图信息技术股份有限公司 1.1.1公司简介 航天宏图信息技术股份有限公司成立于2008年,是国内遥感和北斗导航卫星应用服务商,致力于卫星应用软件国产化、行业应用产业化、应用服务商业化,研发并掌握了具有完全自主知识产权的PIE(Pix…

C# 2的幂

231 2的幂 给你一个整数 n&#xff0c;请你判断该整数是否是 2 的幂次方。如果是&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。 如果存在一个整数 x 使得 n 2x &#xff0c;则认为 n 是 2 的幂次方。 示例 1&#xff1a; 输入&#xff1a;n 1 输出&a…

【RabbitMQ】之消息的可靠性方案

目录 一、数据丢失场景二、数据可靠性方案 1、生产者丢失消息解决方案2、MQ 队列丢失消息解决方案3、消费者丢失消息解决方案 一、数据丢失场景 MQ 消息数据完整的链路为&#xff1a;从 Producer 发送消息到 RabbitMQ 服务器中&#xff0c;再由 Broker 服务的 Exchange 根据…

平面设计软件都有哪些?推荐这7款

优秀的平面广告设计可以给产品带来良好的效益&#xff0c;正确传播品牌的价值和色调&#xff0c;而功能强大、使用方便的平面广告设计软件是创造优秀平面广告设计的关键。本文推荐7款备受好评的平面广告设计软件&#xff0c;易于使用&#xff01; 1.即时设计 即时设计是国内一…

HCIP OSPF+BGP综合实验

题目 1、该拓扑为公司网络&#xff0c;其中包括公司总部、公司分部以及公司骨干网&#xff0c;不包含运营商公网部分。 2、设备名称均使用拓扑上名称改名&#xff0c;并且区分大小写。 3、整张拓扑均使用私网地址进行配置。 4、整张网络中&#xff0c;运行OSPF协议或者BGP协议…

jmeter压力测试指标解释

目录 RT(response time) Throughput 吞吐量 并发用户数 QPS (query per seconds) TPS (transition per seconds) PV和UV 聚合报告&#xff1a; RT(response time) 什么是RT? RT就是指系统在接收到请求和做出相应这段时间跨度 但是值得一提的是RT的值越高,并不真的就能…

性能测试必备监控技能linux篇

前言 如果性能测试的目标服务器是linux系统&#xff0c;在如何使用linux自带的命令来实现性能测试过程的监控分析呢&#xff1f; 对于日常性能测试来讲&#xff0c;在linux下或是类Unix系统&#xff0c;我们必须掌握以下常用的指标查看命令。 ps pstree top free vmstat …

Springboot配置文件数据项加密

1 添加依赖 <!-- jasypt-spring-boot-starter --> <dependency><groupId>com.github.ulisesbocchio</groupId><artifactId>jasypt-spring-boot-starter</artifactId><version>3.0.2</version> </dependency> 2 加密数…

华为数通HCIA-网络参考模型(TCP/IP)

网络通信模式 作用&#xff1a;指导网络设备的通信&#xff1b; OSI七层模型&#xff1a; 7.应用层&#xff1a;由应用层协议&#xff08;http、FTP、Telnet.&#xff09;为应用程序产生对应的数据&#xff1b; 6.表示层&#xff1a;将应用层产生的数据转换成网络设备看得懂…