前面 FLink 的文章中我们已经介绍了说 Flink 已经有很多自带的 Connector。

Flink-Connectors(连接器)(3)RabbitMQ flink 提供了专门操作RabbitMQ的连接器,使用起来更方便,配置连接信息即可快速实现数据读取与输出,但目前仅支持Queue模式,如需使用交换机模式,仍需要自定义RabbitMQ 数据源读取与数据 阅读详情

https://www.cnblogs.com/Allen-rg/p/11593528.html

1、《从0到1学习Flink》—— Data Source 介绍

2、《从0到1学习Flink》—— Data Sink 介绍

其中包括了 Source 和 Sink 的,后面我也讲了下如何自定义自己的 Source 和 Sink。

那么今天要做的事情是啥呢?就是介绍一下 Flink 自带的 ElasticSearch Connector,我们今天就用他来做 Sink,将 Kafka 中的数据经过 Flink 处理后然后存储到 ElasticSearch。

准备

安装 ElasticSearch,这里就忽略,自己找我以前的文章,建议安装 ElasticSearch 6.0 版本以上的,毕竟要跟上时代的节奏。

下面就讲解一下生产环境中如何使用 Elasticsearch Sink 以及一些注意点,及其内部实现机制。

 

Elasticsearch Sink

添加依赖

1

2

3

4

5

<dependency>

    <groupId>org.apache.flink</groupId>

    <artifactId>flink-connector-elasticsearch6_${scala.binary.version}</artifactId>

    <version>${flink.version}</version>

</dependency>

  

上面这依赖版本号请自己根据使用的版本对应改变下。

下面所有的代码都没有把 import 引入到这里来,如果需要查看更详细的代码,请查看我的 GitHub 仓库地址:

https://github.com/zhisheng17/flink-learning/tree/master/flink-learning-connectors/flink-learning-connectors-es6

这个 module 含有本文的所有代码实现,当然越写到后面自己可能会做一些抽象,所以如果有代码改变很正常,请直接查看全部项目代码。

ElasticSearchSinkUtil 工具类

这个工具类是自己封装的,getEsAddresses 方法将传入的配置文件 es 地址解析出来,可以是域名方式,也可以是 ip + port 形式。

addSink 方法是利用了 Flink 自带的 ElasticsearchSink 来封装了一层,传入了一些必要的调优参数和 es 配置参数,下面文章还会再讲些其他的配置。

ElasticSearchSinkUtil.java

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

public class ElasticSearchSinkUtil {

 

    /**

     * es sink

     *

     * @param hosts es hosts

     * @param bulkFlushMaxActions bulk flush size

     * @param parallelism 并行数

     * @param data 数据

     * @param func

     * @param <T>

     */

    public static <T> void addSink(List<HttpHost> hosts, int bulkFlushMaxActions, int parallelism,

                                   SingleOutputStreamOperator<T> data, ElasticsearchSinkFunction<T> func) {

        ElasticsearchSink.Builder<T> esSinkBuilder = new ElasticsearchSink.Builder<>(hosts, func);

        esSinkBuilder.setBulkFlushMaxActions(bulkFlushMaxActions);

        data.addSink(esSinkBuilder.build()).setParallelism(parallelism);

    }

 

    /**

     * 解析配置文件的 es hosts

     *

     * @param hosts

     * @return

     * @throws MalformedURLException

     */

    public static List<HttpHost> getEsAddresses(String hosts) throws MalformedURLException {

        String[] hostList = hosts.split(",");

        List<HttpHost> addresses = new ArrayList<>();

        for (String host : hostList) {

            if (host.startsWith("http")) {

                URL url = new URL(host);

                addresses.add(new HttpHost(url.getHost(), url.getPort()));

            } else {

                String[] parts = host.split(":", 2);

                if (parts.length > 1) {

                    addresses.add(new HttpHost(parts[0], Integer.parseInt(parts[1])));

                } else {

                    throw new MalformedURLException("invalid elasticsearch hosts format");

                }

            }

        }

        return addresses;

    }

}

  

 

Main 启动类

Main.java

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

public class Main {

    public static void main(String[] args) throws Exception {

        //获取所有参数

        final ParameterTool parameterTool = ExecutionEnvUtil.createParameterTool(args);

        //准备好环境

        StreamExecutionEnvironment env = ExecutionEnvUtil.prepare(parameterTool);

        //从kafka读取数据

        DataStreamSource<Metrics> data = KafkaConfigUtil.buildSource(env);

 

        //从配置文件中读取 es 的地址

        List<HttpHost> esAddresses = ElasticSearchSinkUtil.getEsAddresses(parameterTool.get(ELASTICSEARCH_HOSTS));

        //从配置文件中读取 bulk flush size,代表一次批处理的数量,这个可是性能调优参数,特别提醒

        int bulkSize = parameterTool.getInt(ELASTICSEARCH_BULK_FLUSH_MAX_ACTIONS, 40);

        //从配置文件中读取并行 sink 数,这个也是性能调优参数,特别提醒,这样才能够更快的消费,防止 kafka 数据堆积

        int sinkParallelism = parameterTool.getInt(STREAM_SINK_PARALLELISM, 5);

 

        //自己再自带的 es sink 上一层封装了下

        ElasticSearchSinkUtil.addSink(esAddresses, bulkSize, sinkParallelism, data,

                (Metrics metric, RuntimeContext runtimeContext, RequestIndexer requestIndexer) -> {

                    requestIndexer.add(Requests.indexRequest()

                            .index(ZHISHENG + "_" + metric.getName())  //es 索引名

                            .type(ZHISHENG) //es type

                            .source(GsonUtil.toJSONBytes(metric), XContentType.JSON));

                });

        env.execute("flink learning connectors es6");

    }

}

  

配置文件

配置都支持集群模式填写,注意用 , 分隔!

1

2

3

4

5

6

7

8

9

10

kafka.brokers=localhost:9092

kafka.group.id=zhisheng-metrics-group-test

kafka.zookeeper.connect=localhost:2181

metrics.topic=zhisheng-metrics

stream.parallelism=5

stream.checkpoint.interval=1000

stream.checkpoint.enable=false

elasticsearch.hosts=localhost:9200

elasticsearch.bulk.flush.max.actions=40

stream.sink.parallelism=5

  

运行结果

执行 Main 类的 main 方法,我们的程序是只打印 flink 的日志,没有打印存入的日志(因为我们这里没有打日志):

 

 

 

 

 

所以看起来不知道我们的 sink 是否有用,数据是否从 kafka 读取出来后存入到 es 了。

你可以查看下本地起的 es 终端或者服务器的 es 日志就可以看到效果了。

es 日志如下:

 

 

 

 

上图是我本地 Mac 电脑终端的 es 日志,可以看到我们的索引了。

如果还不放心,你也可以在你的电脑装个 kibana,然后更加的直观查看下 es 的索引情况(或者直接敲 es 的命令)

我们用 kibana 查看存入 es 的索引如下:

 

 

 

 

程序执行了一会,存入 es 的数据量就很大了。

扩展配置

上面代码已经可以实现你的大部分场景了,但是如果你的业务场景需要保证数据的完整性(不能出现丢数据的情况),那么就需要添加一些重试策略,因为在我们的生产环境中,很有可能会因为某些组件不稳定性导致各种问题,所以这里我们就要在数据存入失败的时候做重试操作,这里 flink 自带的 es sink 就支持了,常用的失败重试配置有:

1

2

3

4

5

6

7

8

9

10

11

12

13

1、bulk.flush.backoff.enable 用来表示是否开启重试机制

 

2、bulk.flush.backoff.type 重试策略,有两种:EXPONENTIAL 指数型(表示多次重试之间的时间间隔按照指数方式进行增长)、CONSTANT 常数型(表示多次重试之间的时间间隔为固定常数)

 

3、bulk.flush.backoff.delay 进行重试的时间间隔

 

4、bulk.flush.backoff.retries 失败重试的次数

 

5、bulk.flush.max.actions: 批量写入时的最大写入条数

 

6、bulk.flush.max.size.mb: 批量写入时的最大数据量

 

7、bulk.flush.interval.ms: 批量写入的时间间隔,配置后则会按照该时间间隔严格执行,无视上面的两个批量写入配置

  

看下啦,就是如下这些配置了,如果你需要的话,可以在这个地方配置扩充了。

 

 

 

FailureHandler 失败处理器

写入 ES 的时候会有这些情况会导致写入 ES 失败:

1、ES 集群队列满了,报如下错误

12:08:07.326 [I/O dispatcher 13] ERROR o.a.f.s.c.e.ElasticsearchSinkBase - Failed Elasticsearch item request: ElasticsearchException[Elasticsearch exception [type=es_rejected_execution_exception, reason=rejected execution of org.elasticsearch.transport.TransportService$7@566c9379 on EsThreadPoolExecutor[name = node-1/write, queue capacity = 200, org.elasticsearch.common.util.concurrent.EsThreadPoolExecutor@f00b373[Running, pool size = 4, active threads = 4, queued tasks = 200, completed tasks = 6277]]]]

 

是这样的,我电脑安装的 es 队列容量默认应该是 200,我没有修改过。我这里如果配置的 bulk flush size * 并发 sink 数量 这个值如果大于这个 queue capacity ,那么就很容易导致出现这种因为 es 队列满了而写入失败。

当然这里你也可以通过调大点 es 的队列。参考:https://www.elastic.co/guide/en/elasticsearch/reference/current/modules-threadpool.html

2、ES 集群某个节点挂了

这个就不用说了,肯定写入失败的。跟过源码可以发现 RestClient 类里的 performRequestAsync 方法一开始会随机的从集群中的某个节点进行写入数据,如果这台机器掉线,会进行重试在其他的机器上写入,那么当时写入的这台机器的请求就需要进行失败重试,否则就会把数据丢失!

 

 

 

3、ES 集群某个节点的磁盘满了

这里说的磁盘满了,并不是磁盘真的就没有一点剩余空间的,是 es 会在写入的时候检查磁盘的使用情况,在 85% 的时候会打印日志警告。

 

 

 

 

这里我看了下源码如下图:

 

 

 

 

 

如果你想继续让 es 写入的话就需要去重新配一下 es 让它继续写入,或者你也可以清空些不必要的数据腾出磁盘空间来。

解决方法

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

DataStream<String> input = ...;

 

input.addSink(new ElasticsearchSink<>(

    config, transportAddresses,

    new ElasticsearchSinkFunction<String>() {...},

    new ActionRequestFailureHandler() {

        @Override

        void onFailure(ActionRequest action,

                Throwable failure,

                int restStatusCode,

                RequestIndexer indexer) throw Throwable {

 

            if (ExceptionUtils.containsThrowable(failure, EsRejectedExecutionException.class)) {

                // full queue; re-add document for indexing

                indexer.add(action);

            } else if (ExceptionUtils.containsThrowable(failure, ElasticsearchParseException.class)) {

                // malformed document; simply drop request without failing sink

            } else {

                // for all other failures, fail the sink

                // here the failure is simply rethrown, but users can also choose to throw custom exceptions

                throw failure;

            }

        }

}));

  

如果仅仅只是想做失败重试,也可以直接使用官方提供的默认的 RetryRejectedExecutionFailureHandler ,该处理器会对 EsRejectedExecutionException 导致到失败写入做重试处理。如果你没有设置失败处理器(failure handler),那么就会使用默认的 NoOpFailureHandler 来简单处理所有的异常。

总结

本文写了 Flink connector es,将 Kafka 中的数据读取并存储到 ElasticSearch 中,文中讲了如何封装自带的 sink,然后一些扩展配置以及 FailureHandler 情况下要怎么处理。(这个问题可是线上很容易遇到的)

原创地址为:http://www.54tianzhisheng.cn/2018/12/30/Flink-ElasticSearch-Sink/

flink-connector中kafka和upsertkafka的介绍 标题: flink-connector中kafka和upsertkafka的介绍 日期: 2021-08-03 16:46:43 标签: [flink, kafka, upsert kafka, 实时数仓] 分类: 数据仓库 今天来说下flink sql中常用到的connector:kafka,它承接了实时的消息数据,进行处理,当然,这些消息的特点有可能不一样,怎样处理,得到实时的结果,提供给分析、运营、营销等等。下面来看看具体有什么区别,怎么使用。 kafka中的实时消息,它也可以是关系型数据库的c. 阅读详情

相关推荐

手把手构建基于 GBase8s 的 Flink connector

​ 简介 本篇文章,首先会向大家阐述什么是 Flink connector 和 CDC , 然后会通过手把手的方式和大家一起构建一个简单的GBase8s的Flink connector,并完成实践项目,即通过Mysql CDC实时通过connector同步数据到GBase8s中。 什么是 Flink connector Flink内置了一些基本数据源和接收器,这些数据源和接收器始终可用。该预定义的数据源包括文件、Mysql、RabbitMq、Kafka、ES等,同时也支持数据输出到文件、My..

麒思妙想 1869

Flink SQL 自定义 Connector

flink sql 自定义 connector

lck_csdn的博客 3476

flink重温笔记(七):Flink 流批一体 API 开发—— Connector 连接器

前言:今天是学习 flink 的第七天啦!学习了 flink 中 connector(数据连接器) 部分知识点,这一部分只要是解决数据处理之后,数据到哪里去的问题,主要学习了数据存储到以下三处: 1、关系型数据库 mysql ; 2、消息队列:kafka; 3、非关系型数据库:redis 我觉得还是比较有意思的,这些是以后工作要用到的技能,我一定要好好掌握! Tips:“莫道春光难揽取,浮云过后艳阳天!”明天周一,又是新的一天,要深入学习 flink 的四大基石属性!

m0_60732994的博客 2421

1、Apache Flink Contributor之路

Flink Contributor 之路

江7城子的专栏 808

【Flink】使用JDBC connector写入ClickHouse

1.概述 转载并且添加 使用JDBC connector写入ClickHouse 2.背景信息 Flink 1.10.1 + flink-jdbc Flink 1.11.0 + flink-connector-jdbc 本节主要介绍如何利用 ClickHouse JDBC connector,使用不同版本的 Flink 写入数据到 ClickHouse 中。 2.1 背景信息 Flink 在 1.11.0 版本对其 JDBC connector 进行了一次较大的重构: 重构之前(1.10.1 及之前版本.

九师兄 2130

基于MQTT与Flink的物联网设备高效接入与流处理平台

本项目是一个面向物联网场景的设备接入与数据处理平台,核心采用MQTT协议实现海量设备连接,结合Flink流处理框架构建实时计算能力。系统提供设备认证、主题订阅、消息路由等基础通信功能,并通过Flink窗口计算实现设备数据的实时聚合、异常检测等流式处理。平台支持水平扩展以应对高并发设备接入,提供可视化监控界面展示设备状态与数据流处理结果,适用于智能家居、工业物联网等需要低延迟传输与实时分析的场景。

Flink Connectors 介绍与 Kafka Connector

文章目录1. Streaming Connectors预定义的 source 和 sinkBoundled connectorsApache Bahir 中的连接器异步 IO2. Flink Kafka Connector2.1 Flink Kafka Consumer1)反序列化2)消费起始位置设置3)topic 和 partition 动态发现4)commit offset 方式5)Times...

maben996 1540

基于flink最新的[FLIP-27]架构对MQTT connector的实现详细文档+全部资料.zip

【资源说明】 基于flink最新的[FLIP-27]架构对MQTT connector的实现详细文档+全部资料.zip 【备注】 1、该项目是个人高分项目源码,已获导师指导认可通过,答辩评审分达到95分 2、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 3、本项目适合计算机相关专业(人工智能、通信工程、自动化、电子信息、物联网等)的在校学生、老师或者企业员工下载使用,也可作为毕业设计、课程设计、作业、项目初期立项演示等,当然也适合小白学习进阶。 4、如果基础还行,可以在此代码基础上进行修改,以实现其他功能,也可直接用于毕设、课设、作业等。 欢迎下载,沟通交流,互相学习,共同进步!

Flink实战之自定义flink sql connector

背景 最近工作中需要自定义开发一些flink sql的connector,因为官方提供的connector毕竟有限,在我们工作中可能会用到各种各样的中间件。所以官方没有提供的就需要我们自定义开发。 就是如: CREATE TABLE XXX( A STRING, B BIGINT) WITH( ‘connect.type’ = ‘kafka’, … ) 所以开发一个自己的connector需要做哪些,本文就来总结一下开发的主要步骤,以及我遇到的问题怎么解决的。 开发 自定义Factory,根据需要实现S

weixin_41608066的博客 5058

基于MQTT的物联网设备接入平台 使用Flink流处理框架

【作品名称】:基于MQTT的物联网设备接入平台。使用Flink流处理框架 【适用人群】:适用于希望学习不同技术领域的小白或进阶学习者。可作为毕设项目、课程设计、大作业、工程实训或初期项目立项。 【项目介绍】:基于MQTT的物联网设备接入平台。使用Flink流处理框架

项目风险管理全流程解析

按技术、资源、市场、外部环境等类型对风险进行分类,便于团队快速识别风险,提高风险管理效率,避免风险遗漏。项目结束后,总结实际发生的风险和应对效果,找出风险管理中的经验和教训,为后续项目提供参考。

qq_34062333的博客 1472

详述Flink SQL Connector写入clickhouse的问题与方法

文章目录1. 问题描述2. 解决办法2.1 扩展jdbc connector支持clickhouse2.2 使用flink-connector-clickhouse 1. 问题描述 笔者使用Flink1.11 SQL(jdbc connector)将实时数据写入Clickhouse时报以下异常: Exception in thread "main" org.apache.flink.table.api.SqlParserException: SQL parse failed. Encountered "de

search-lemon的博客 1万+

不只是安装:用OrbbecSDK_ROS2玩转深度相机点云、滤波与DDS调优

本文深入探讨了如何通过OrbbecSDK_ROS2实现深度相机的高级应用,包括点云处理、深度图优化和DDS调优。详细介绍了点云采集与可视化技巧、多种滤波算法的配置与组合策略,以及针对高带宽数据传输的DDS高级调优方法,帮助开发者在机器人导航和三维重建等场景中提升性能。

weixin_29246195的博客 789

flink mysql connector_如何正确使用 Flink Connector?

简介: 本文主要分享 Flink connector 相关内容,分为以下三个部分的内容:第一部分会首先介绍一下 Flink Connector 有哪些。第二部分会重点介绍在生产环境中经常使用的 kafka connector 的基本的原理以及使用方法。第三部分答疑,对社区反馈的问题进行答疑。(因为内容较多,进行了文章删减,阅读全文可以通过“点击这里”进行浏览)文章摘自:阿里云开发者社区Flink ...

weixin_31134719的博客 681

SOI技术解析:绝缘体上硅的结构优势与应用场景

本文深入解析了SOI(绝缘体上硅)技术的核心结构、性能优势与应用场景。SOI通过在硅衬底与器件层之间引入绝缘的埋氧层,实现了优异的电隔离,从而显著降低芯片功耗、提升工作频率并增强抗干扰能力。该技术已广泛应用于5G射频前端、高性能计算、汽车电子及MEMS传感器等关键领域,是推动半导体性能持续提升的重要技术路径。

jj890的博客 405

DIY天文摄影:如何用NMP溶剂安全去除佳能CMOS传感器的拜耳滤镜(附详细步骤)

本文为天文摄影爱好者提供了一份详尽的DIY指南,介绍如何使用NMP溶剂安全去除佳能CMOS传感器的拜耳滤镜,将其改造为高灵敏度单色相机。文章深入剖析了NMP溶剂的作用原理与关键风险,并系统性地拆解了从材料准备、分步操作到后期验证的完整实践流程,旨在帮助具备动手能力的爱好者实现深空天体摄影的进阶目标。

w3x4y的博客 310

Flink JDBC Connector:Flink 与数据库集成最佳实践

整理:陈政羽(Flink 社区志愿者) ...

程序员小陶的大数据分享 7637

基于PyTorch+Transformer实现谣言检测系统

本项目使用基于`PyTorch+Transformer`的谣言检测模型,将文本中的谣言事件进行连续向量化,通过一维卷积神经网络的学习训练来挖掘表示文本深层的特征,避免了特征构建的问题,并能发现那些不容易被人发现的特征,从而产生更好的效果。

优快云 精品推荐 6352

Flink SQL 自定义 redis connector

一般情况下,我们不需要创建新的 connector,因为 Flink SQL 已经内置了丰富的 connector 供我们使用,但是在实际生产环境中我们的存储是多种多样的,所以原生的 co...

JasonLee实时计算 1563

boost::circular_buffer缓行缓冲区

1、circular_buffer实现了一个环形缓冲区,空间大小是固定的,当到达容器末尾时,将自动利用容器另一端的位置空间。 2、当程序需要容纳大量的元素,但多数情况下仅保存较少数量元素时可以使用circular_buffer_space_optimized,他可以有效的减少内存的占用,提高程序的运行效率。 3、主要接口: 4、实现一个生产者---消费者模式示例: #inc...

小米的修行之路 792

RK3588 Buildroot 新建板级DTS

在rk3588 linux开发过程中,新创建板型主要基于硬件差异化、软件适配与优化、项目定制化等多方面需求。

501846585_QQAndroid_java 1300

flink+mysql+connector_Flink实战之自定义flink sql connector

背景最近工作中需要自定义开发一些flink sql的connector,因为官方提供的connector毕竟有限,在我们工作中可能会用到各种各样的中间件。所以官方没有提供的就需要我们自定义开发。就是如:CREATE TABLE XXX(A STRING,B BIGINT)WITH('connect.type' = 'kafka',...)所以开发一个自己的connector需要做哪些,本文就来总结...

weixin_30479433的博客 1175

Unreal 从入门到精通之VR常用操作

本文总结了使用Unreal5开发VR项目时遇到的常见问题及其解决方案。首先,介绍了如何通过VRPawn的TryTeleport函数设置玩家视角位置,确保头显移动时安全区域的正确传送。其次,详细说明了如何在Unreal中播放视频,包括导入视频文件、创建媒体播放器和媒体纹理,并将其应用到3D物体上。接着,讲解了如何播放VR全景视频,建议使用MediaPlate媒体板并调整几何体为球体。最后,讨论了如何手动打开和关闭VR模式,以便在PC模式和头显模式之间切换。这些方法为VR开发提供了实用的技术指导。

TxNet 953

如何正确使用 Flink Connector

文章目录Flink Streaming Connector 本文内容出自 Apache Flink公开课系列 作者:董亭亭 本文主要分享 Flink connector 相关内容,分为以下三个部分的内容:第一部分会首先介绍一下 Flink Connector 有哪些。第二部分会重点介绍在生产环境中经常使用的 kafka connector 的基本的原理以及使用方法。第三部分答疑,对社区反馈的问题进行答疑。 Flink Streaming Connector ...

qq_24095055的博客 842

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

40、Flink 的Apache Kafka connector(kafka sink的介绍及使用示例)-2

1、Flink 部署、概念介绍、source、transformation、sink使用示例、四大基石介绍和示例等系列综合文章链接13、Flink 的table api与sql的基本概念、通用api介绍及入门示例 14、Flink 的table api与sql之数据类型: 内置数据类型以及它们的属性 15、Flink 的table api与sql之流式概念-详解的介绍了动态表、时间属性配置(如何处理更新结果)、时态表、流上的join、流上的确定性以及查询配置 16、Flink 的table api与sql之

alanchanchn的专栏 6万+

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

40、Flink 的Apache Kafka connector(kafka source的介绍及使用示例)-1

1、Flink 部署、概念介绍、source、transformation、sink使用示例、四大基石介绍和示例等系列综合文章链接13、Flink 的table api与sql的基本概念、通用api介绍及入门示例 14、Flink 的table api与sql之数据类型: 内置数据类型以及它们的属性 15、Flink 的table api与sql之流式概念-详解的介绍了动态表、时间属性配置(如何处理更新结果)、时态表、流上的join、流上的确定性以及查询配置 16、Flink 的table api与sql之

alanchanchn的专栏 6万+

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

上一篇: java 内部类和静态内部类的区别
下一篇: Flink:异步I/O访问外部数据
王树民
博客等级 码龄10年 912粉丝 560原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值