容器环境的MySQL、canal、Elasticsearch数据同步测试

news/2024/12/26 10:32:38/文章来源:https://www.cnblogs.com/wbstudy/p/18632170

回顾一次容器环境的MySQL、canal、Elasticsearch数据同步

MySQL和Elasticsearch安装初始化就不展示了,版本如下:

image-20241225202613205

image-20241225202626384

sql表关键字段如下:

CREATE TABLE `fault_code` (`title` varchar(255) CHARACTER SET utf8mb4 DEFAULT NULL,`description` varchar(512) CHARACTER SET utf8mb4 DEFAULT NULL,`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

主要是同步title和description

Elasticsearch的映射:

PUT /fault_code
{"mappings": {"properties": {"title": {"type": "text","analyzer": "ik_smart"},"description": {"type": "text","analyzer": "ik_smart"}}}
}

为canal专门创建一个db 用户

CREATE USER 'canal'@'%' IDENTIFIED BY 'testcanal';GRANT SELECT, REPLICATION SLAVE, SHOW DATABASES ON *.* TO 'canal'@'%';FLUSH PRIVILEGES;

拉取canal镜像,由于上面MySQL、Elasticsearch版本已经确定,根据官方文档canal版本选择1.1.5

docker pull canal/canal-server:v1.1.5

提前创建 /mydata/canal/conf目录,接着来执行拷贝配置文件命令

启动canaltest,拷贝配置文件出来进行配置
docker run --name canaltest \
-p 11111:11111  \
-id canal/canal-server:v1.1.5
把配置文件拷贝出来
docker cp canaltest:/home/admin/canal-server/conf/example/instance.properties  /mydata/canal/conf/

image-20241225204906534

接着对instance.properties文件进行修改:

canal.instance.mysql.slaveId=22
canal.instance.master.address=192.168.56.10:3306
canal.instance.dbUsername=canal
canal.instance.dbPassword=testcanal
canal.instance.filter.regex=test.fault_code

主要是上面几个属性

删除原来的canal服务:

docker rm -f canaltest 

启动canal服务:

docker run --name canal115 \
-p 11111:11111  \
-v /mydata/canal/conf/instance.properties:/home/admin/canal-server/conf/example/instance.properties \
-id canal/canal-server:v1.1.5

进入canal容器:

docker exec -it canal115 /bin/bash

到指定目录查看日志情况

cd canal-server/logs/example/tail -50 example.log 

image-20241225210451145

第一次查看日志发现问题:

CanalParseException: command : 'show master status' has an error!
Caused by: java.io.IOException: ErrorPacket [errorNumber=1227, fieldCount=-1, message=Access denied; you need (at least one of) the SUPER, REPLICATION CLIENT privilege(s) for this operation, sqlState=42000, sqlStateMarker=#]with command: show master status

重新赋予权限,并重启MySQL之后,canal就可以正常使用

image-20241225211406166

拉取docker pull slpcat/canal-adapter:v1.1.5-jdk8镜像

docker pull slpcat/canal-adapter:v1.1.5-jdk8

启动docker canal adapter

docker run --name canaladapter
-p 8081:8081
-id slpcat/canal-adapter:v1.1.5-jdk8

把两个配置文件拷贝出来:

canaladapter的配置文件拷贝出来
docker cp canaladapter:/opt/canal-adapter/conf/application.yml  /mydata/canaladapter/conf/es的映射文件拷贝出来
docker cp canaladapter:/opt/canal-adapter/conf/es7/faultcode.yml /mydata/canaladapter/conf/

image-20241226000130093

image-20241226000204828

修改两个配置文件

application.yml:

需要改的有下面几行:

canal.tcp.server.host: 127.0.0.1:11111源数据:
srcDataSources:defaultDS:url: jdbc:mysql://192.168.56.10:3306/test?useUnicode=trueusername: canalpassword: testcanal- name: eshosts: 127.0.0.1:9200 # 127.0.0.1:9200 for rest modeproperties:mode: rest # or rest# security.auth: test:123456 #  only used for rest modecluster.name: elasticsearch     

faultcode.yml:

dataSourceKey: defaultDS
destination: example
groupId: g1
esMapping:_index: fault_code_id: idfieldMapping:title: title          # 将 MySQL 的 title 字段映射到 Elasticsearch 的 title 字段description: description        # 将 MySQL 的 description 字段映射到 Elasticsearch 的 description 字段sql: "select fc.id, fc.title, fc.description from fault_code fc"commitBatch: 3000

删除旧的canaladapter

启动新的canaladapter

docker run --name canaladapter \
-p 8081:8081 \
-v /mydata/canaladapter/conf/application.yml:/opt/canal-adapter/conf/application.yml \
-v /mydata/canaladapter/conf/faultcode.yml:/opt/canal-adapter/conf/es7/faultcode.yml \
-id slpcat/canal-adapter:v1.1.5-jdk8

发现启动不了adapter

image-20241226002413230

这个问题应该是格式不对,修改了一下格式,再度重启canal adapter

docker restart 029aa4c345af

重启之后,canal adapter可以正常启动,但是进入容器查看canal adapter日志

docker exec -it cd0b5d5 /bin/bash

cd logs/adapter/

tail -100 adapter.log

2024-12-26 00:28:00.194 [main] INFO  c.a.o.canal.adapter.launcher.loader.CanalAdapterService - ## syncSwitch refreshed.
2024-12-26 00:28:00.194 [main] INFO  c.a.o.canal.adapter.launcher.loader.CanalAdapterService - ## start the canal client adapters.
2024-12-26 00:28:00.206 [main] ERROR c.a.o.canal.adapter.launcher.loader.CanalAdapterService - ## something goes wrong when starting up the canal client adapters:
java.lang.NullPointerException: nullat com.alibaba.otter.canal.adapter.launcher.loader.CanalAdapterLoader.init(CanalAdapterLoader.java:51) ~[client-adapter.launcher-1.1.5-SNAPSHOT.jar:na]at com.alibaba.otter.canal.adapter.launcher.loader.CanalAdapterService.init(CanalAdapterService.java:60) ~[client-adapter.launcher-1.1.5-SNAPSHOT.jar:na]at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_282]at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_282]at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_282]at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_282]at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor$LifecycleElement.invoke(InitDestroyAnnotationBeanPostProcessor.java:365) [spring-beans-5.0.5.RELEASE.jar:5.0.5.RELEASE]at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor$LifecycleMetadata.invokeInitMethods(InitDestroyAnnotationBeanPostProcessor.java:308) [spring-beans-5.0.5.RELEASE.jar:5.0.5.RELEASE]at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor.postProcessBeforeInitialization(InitDestroyAnnotationBeanPostProcessor.java:135) [spring-beans-5.0.5.RELEASE.jar:5.0.5.RELEASE]at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsBeforeInitialization(AbstractAutowireCapableBeanFactory.java:422) [spring-beans-5.0.5.RELEASE.jar:5.0.5.RELEASE]at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1694) [spring-beans-5.0.5.RELEASE.jar:5.0.5.RELEASE]at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:579) [spring-beans-5.0.5.RELEASE.jar:5.0.5.RELEASE]at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:501) [spring-beans-5.0.5.RELEASE.jar:5.0.5.RELEASE]at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$1(AbstractBeanFactory.java:353) [spring-beans-5.0.5.RELEASE.jar:5.0.5.RELEASE]at org.springframework.cloud.context.scope.GenericScope$BeanLifecycleWrapper.getBean(GenericScope.java:390) ~[spring-cloud-context-2.0.0.RELEASE.jar:2.0.0.RELEASE]at org.springframework.cloud.context.scope.GenericScope.get(GenericScope.java:184) ~[spring-cloud-context-2.0.0.RELEASE.jar:2.0.0.RELEASE]at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:350) [spring-beans-5.0.5.RELEASE.jar:5.0.5.RELEASE]at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) [spring-beans-5.0.5.RELEASE.jar:5.0.5.RELEASE]at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1089) ~[spring-context-5.0.5.RELEASE.jar:5.0.5.RELEASE]at org.springframework.cloud.context.scope.refresh.RefreshScope.eagerlyInitialize(RefreshScope.java:126) ~[spring-cloud-context-2.0.0.RELEASE.jar:2.0.0.RELEASE]at org.springframework.cloud.context.scope.refresh.RefreshScope.start(RefreshScope.java:117) ~[spring-cloud-context-2.0.0.RELEASE.jar:2.0.0.RELEASE]at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_282]at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_282]at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_282]at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_282]at org.springframework.context.event.ApplicationListenerMethodAdapter.doInvoke(ApplicationListenerMethodAdapter.java:264) ~[spring-context-5.0.5.RELEASE.jar:5.0.5.RELEASE]at org.springframework.context.event.ApplicationListenerMethodAdapter.processEvent(ApplicationListenerMethodAdapter.java:182) ~[spring-context-5.0.5.RELEASE.jar:5.0.5.RELEASE]at org.springframework.context.event.ApplicationListenerMethodAdapter.onApplicationEvent(ApplicationListenerMethodAdapter.java:144) ~[spring-context-5.0.5.RELEASE.jar:5.0.5.RELEASE]at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:172) ~[spring-context-5.0.5.RELEASE.jar:5.0.5.RELEASE]at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:165) ~[spring-context-5.0.5.RELEASE.jar:5.0.5.RELEASE]at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:139) ~[spring-context-5.0.5.RELEASE.jar:5.0.5.RELEASE]at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:400) ~[spring-context-5.0.5.RELEASE.jar:5.0.5.RELEASE]at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:354) ~[spring-context-5.0.5.RELEASE.jar:5.0.5.RELEASE]at org.springframework.context.support.AbstractApplicationContext.finishRefresh(AbstractApplicationContext.java:888) ~[spring-context-5.0.5.RELEASE.jar:5.0.5.RELEASE]at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.finishRefresh(ServletWebServerApplicationContext.java:161) ~[spring-boot-2.0.1.RELEASE.jar:2.0.1.RELEASE]at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:553) ~[spring-context-5.0.5.RELEASE.jar:5.0.5.RELEASE]at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:140) ~[spring-boot-2.0.1.RELEASE.jar:2.0.1.RELEASE]at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:759) ~[spring-boot-2.0.1.RELEASE.jar:2.0.1.RELEASE]at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:395) ~[spring-boot-2.0.1.RELEASE.jar:2.0.1.RELEASE]at org.springframework.boot.SpringApplication.run(SpringApplication.java:327) ~[spring-boot-2.0.1.RELEASE.jar:2.0.1.RELEASE]at com.alibaba.otter.canal.adapter.launcher.CanalAdapterApplication.main(CanalAdapterApplication.java:19) ~[client-adapter.launcher-1.1.5-SNAPSHOT.jar:na]

这个经过搜索确定是格式上的错误,把canal adapter的格式又修改了一下重启:

server:port: 8081
spring:jackson:date-format: yyyy-MM-dd HH:mm:sstime-zone: GMT+8default-property-inclusion: non_nullcanal.conf:mode: tcp #tcp kafka rocketMQ rabbitMQflatMessage: truezookeeperHosts:syncBatchSize: 1000retries: 0timeout:accessKey:secretKey:consumerProperties:# canal tcp consumercanal.tcp.server.host: 192.168.56.10:11111canal.tcp.zookeeper.hosts:canal.tcp.batch.size: 500canal.tcp.username:canal.tcp.password:srcDataSources:defaultDS:url: jdbc:mysql://192.168.56.10:3306/test?useUnicode=trueusername: canalpassword: testcanalcanalAdapters:- instance: example # canal instance Name or mq topic namegroups:- groupId: g1outerAdapters:- name: logger
#      - name: rdb
#        key: mysql1
#        properties:
#          jdbc.driverClassName: com.mysql.jdbc.Driver
#          jdbc.url: jdbc:mysql://127.0.0.1:3306/mytest2?useUnicode=true
#          jdbc.username: root
#          jdbc.password: 121212
#      - name: rdb
#        key: oracle1
#        properties:
#          jdbc.driverClassName: oracle.jdbc.OracleDriver
#          jdbc.url: jdbc:oracle:thin:@localhost:49161:XE
#          jdbc.username: mytest
#          jdbc.password: m121212
#      - name: rdb
#        key: postgres1
#        properties:
#          jdbc.driverClassName: org.postgresql.Driver
#          jdbc.url: jdbc:postgresql://localhost:5432/postgres
#          jdbc.username: postgres
#          jdbc.password: 121212
#          threads: 1
#          commitSize: 3000
#      - name: hbase
#        properties:
#          hbase.zookeeper.quorum: 127.0.0.1
#          hbase.zookeeper.property.clientPort: 2181
#          zookeeper.znode.parent: /hbase- name: eshosts: 192.168.56.10:9200 # 127.0.0.1:9200 for rest modeproperties:mode: rest # or rest# security.auth: test:123456 #  only used for rest modecluster.name: elasticsearch
#        - name: kudu
#          key: kudu
#          properties:
#            kudu.master.address: 127.0.0.1 # ',' split multi address

启动之后发现还是有问题

2024-12-26 00:50:14.161 [Thread-3] ERROR c.a.otter.canal.adapter.launcher.loader.AdapterProcessor - process error!
com.alibaba.otter.canal.protocol.exception.CanalClientException: java.net.ConnectException: Connection refusedat com.alibaba.otter.canal.client.impl.SimpleCanalConnector.doConnect(SimpleCanalConnector.java:198) ~[na:na]at com.alibaba.otter.canal.client.impl.SimpleCanalConnector.connect(SimpleCanalConnector.java:115) ~[na:na]at com.alibaba.otter.canal.connector.tcp.consumer.CanalTCPConsumer.connect(CanalTCPConsumer.java:59) ~[na:na]at com.alibaba.otter.canal.adapter.launcher.loader.AdapterProcessor.process(AdapterProcessor.java:184) ~[client-adapter.launcher-1.1.5-SNAPSHOT.jar:na]at java.lang.Thread.run(Thread.java:748) ~[na:1.8.0_282]
Caused by: java.net.ConnectException: Connection refusedat sun.nio.ch.Net.connect0(Native Method) ~[na:1.8.0_282]at sun.nio.ch.Net.connect(Net.java:482) ~[na:1.8.0_282]at sun.nio.ch.Net.connect(Net.java:474) ~[na:1.8.0_282]at sun.nio.ch.SocketChannelImpl.connect(SocketChannelImpl.java:647) ~[na:1.8.0_282]at com.alibaba.otter.canal.client.impl.SimpleCanalConnector.doConnect(SimpleCanalConnector.java:150) ~[na:na]... 4 common frames omitted

看到错误猜测是某个连接没配对,再去看看配置,应该是canal.tcp.server.host: 192.168.56.10:11111没有配置成真实地址

改完再试一下

这一次又有新问题:

Extension instance(name: es, class: interface com.alibaba.otter.canal.client.adapter.OuterAdapter) could not be instantiated: class could not be found

image-20241226005806134

这个是name需要修改一下:

  - name: es7hosts: 192.168.56.10:9200 # 127.0.0.1:9200 for rest modeproperties:mode: rest # or rest# security.auth: test:123456 #  only used for rest modecluster.name: elasticsearch

修改完之后application.yml文件如下:

server:port: 8081
spring:jackson:date-format: yyyy-MM-dd HH:mm:sstime-zone: GMT+8default-property-inclusion: non_nullcanal.conf:mode: tcp #tcp kafka rocketMQ rabbitMQflatMessage: truezookeeperHosts:syncBatchSize: 1000retries: 0timeout:accessKey:secretKey:consumerProperties:# canal tcp consumercanal.tcp.server.host: 192.168.56.10:11111canal.tcp.zookeeper.hosts:canal.tcp.batch.size: 500canal.tcp.username:canal.tcp.password:srcDataSources:defaultDS:url: jdbc:mysql://192.168.56.10:3306/test?useUnicode=trueusername: canalpassword: testcanalcanalAdapters:- instance: example # canal instance Name or mq topic namegroups:- groupId: g1outerAdapters:- name: logger
#      - name: rdb
#        key: mysql1
#        properties:
#          jdbc.driverClassName: com.mysql.jdbc.Driver
#          jdbc.url: jdbc:mysql://127.0.0.1:3306/mytest2?useUnicode=true
#          jdbc.username: root
#          jdbc.password: 121212
#      - name: rdb
#        key: oracle1
#        properties:
#          jdbc.driverClassName: oracle.jdbc.OracleDriver
#          jdbc.url: jdbc:oracle:thin:@localhost:49161:XE
#          jdbc.username: mytest
#          jdbc.password: m121212
#      - name: rdb
#        key: postgres1
#        properties:
#          jdbc.driverClassName: org.postgresql.Driver
#          jdbc.url: jdbc:postgresql://localhost:5432/postgres
#          jdbc.username: postgres
#          jdbc.password: 121212
#          threads: 1
#          commitSize: 3000
#      - name: hbase
#        properties:
#          hbase.zookeeper.quorum: 127.0.0.1
#          hbase.zookeeper.property.clientPort: 2181
#          zookeeper.znode.parent: /hbase- name: es7hosts: 192.168.56.10:9200 # 127.0.0.1:9200 for rest modeproperties:mode: rest # or rest# security.auth: test:123456 #  only used for rest modecluster.name: elasticsearch
#        - name: kudu
#          key: kudu
#          properties:
#            kudu.master.address: 127.0.0.1 # ',' split multi address

docker restart cd0b5d5b98b7

进入容器查看日志,发现已经启动成功

image-20241226010403018

测试新增同步:

image-20241226010506746

image-20241226010519313

测试修改同步:

image-20241226010557476

image-20241226010610372

测试删除同步:

image-20241226010632232

image-20241226010647941

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

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

相关文章

从全球到本地:跨境电商如何提升供应链管理效率

一、引言 随着全球化的推进与互联网技术的飞速发展,跨境电商已成为全球贸易的重要组成部分。跨境电商平台通过缩短国际贸易的链条,打破了传统贸易壁垒,使消费者能够方便快捷地购买来自世界各地的商品。然而,跨境电商的成功不仅仅依赖于商品的丰富性和价格竞争力,背后更为复…

违规停放智能监测摄像机

违规停放智能监测摄像机结合高清晰度摄像功能和智能识别算法,可以对违规停放行为进行准确识别和监测。这种设备可根据设定的监测区域和参数,自动识别车辆停放位置和停放时间,一旦发现有车辆违停停放,系统将立即发送警报通知相关部门及时处理。通过这种方式,可以及时发现、…

破解跨境电商的竞争难题:高效市场竞争管理的核心要素

一、引言 随着全球化进程的加速和互联网技术的快速发展,跨境电商成为了全球贸易的新兴力量。跨境电商平台通过打破国界和时间的限制,为消费者和商家提供了更便捷、更高效的购物和销售渠道。然而,随着这一市场的逐步成熟,平台之间的竞争愈加激烈,如何在这样的竞争中脱颖而出…

MIT、OpenAI等震撼力作:AI首次自主发现人工生命!人类窥见上帝造物

就在刚刚,由Transformer八子创立的Sakana AI,联合来自MIT、OpenAI、瑞士AI实验室IDSIA等机构的研究人员,提出了「自动搜索人工生命」的新算法!论文地址:https://arxiv.org/abs/2412.17799 言归正传,ALife,即「人工生命」,是一门跨学科研究,旨在通过模拟生命的行为、特…

AI基坑监测识别摄像机

AI基坑监测识别摄像机是一种利用人工智能技术进行基坑监测的智能设备,其作用是监测基坑工程施工过程中的变化,并识别潜在的风险因素,以提供准确数据和及时预警,确保基坑工程的安全进行。这种摄像机通过高清摄像头实时捕捉基坑工程的图像,然后利用AI算法对基坑的土体稳定性…

波士顿矩阵:如何制定产品战略与组合优化?

引言 在当今竞争激烈的市场环境中,企业面临着众多产品的管理与决策难题。如何合理分配资源,确定哪些产品值得加大投入,哪些产品需要逐步淘汰,是企业实现可持续发展的关键。波士顿矩阵作为一种经典的产品战略分析工具,为企业提供了一个清晰的框架,帮助管理者洞察产品的市场…

电动车违停智能监测摄像机

电动车违停智能监测摄像机可以通过合理设置监控区域和参数,实现对电动车违停情况的自动监测和报警。一旦系统检测到电动车违停情况,比如车辆停放时间过长或占用人行道等,会立即发送警报通知相关部门,以便及时处理并维护交通秩序。电动车违停智能监测摄像机可以广泛应用于城…

Flutter进阶组件(2):CheckboxListTile(复选框列表项)

CheckboxListTile是一个特殊的ListTile,它内嵌了一个复选框(Checkbox)。这使得它非常适合用来创建一个带有标题和可选复选框的列表项,常用于设置界面或需要用户选择多个选项的场景。 一、属性 CheckboxListTile组件提供了以下属性,以支持各种自定义需求:title: 显示的标题…

3种常见的数据库迁移工具对比

3种常见的数据库迁移工具对比神州数码云基地​已认证账号​关注2 人赞同了该文章之前在项目中,收到一个紧急需求,要把数据从 PostgreSQL 迁移到 TiDB 中。由于时间紧任务重,来不及调研高效的方式,直接使用了 Navicat 内置的功能,把数据从 PostgreSQL 迁移到了 TiDB。 项…

img标签的onerror事件有什么作用?

onerror 是 HTML <img> 标签的一个事件属性,它用于指定当图像加载出现错误时执行的 JavaScript 代码。如果指定的图像文件不存在、无法访问,或者由于某种原因无法正确加载到页面上,就会触发这个事件。 例如,你可以使用 onerror 事件来提供一个备用的图像,以便在原图…

java基础3

异常 Java 异常类层次结构图概览:Exception 和 Error 有什么区别? 在 Java 中,所有的异常都有一个共同的祖先 java.lang 包中的 Throwable 类。Throwable 类有两个重要的子类:Exception :程序本身可以处理的异常,可以通过 catch 来进行捕获。Exception 又可以分为 Checked …