• 记录一次接入xxl-job的踩坑记录


    最近记录了一些java中常踩的坑、设计思路和小知识点,大家可以看看
    详细记录一次接入xxl-job的踩坑路径
    30s快速解决循环依赖
    idea中一个小小的操作竟能解决如此多的问题
    docker中的服务接入xxljob需要注意的一点
    关于一次fullgc的告警分析
    mysql中的int类型竟变成了它?
    jpa中的字段总是自己莫名更新?
    获取不到类上的注解?空指针?
    学会这招,再也不怕依赖冲突!
    redis的热点key还能这么处理?
    领导让我设计一个任务系统
    当服务重启时,大部分人没考虑这点
    参数还能这么优雅校验?
    文件上传报错,全局异常处理!
    常见的点赞功能如何实现,如何防止刷赞

    1. 接入企业公用邮箱的邮箱告警改造

    因为xxl-job是自带邮箱告警的,但是我需要的场景是,使用企业内部邮箱,发件人是企业公用账户,由于一些文档的缺失,导致这块接起来也废了点劲,主要需要改造下EmailJobAlarm这个类。
    原本发送邮件的部分是这样的,这种适用于我已经有了发件人的邮箱账号和密码,是可以直接使用无需改造的。

    MimeMessage mimeMessage = XxlJobAdminConfig.getAdminConfig().getMailSender().createMimeMessage();
    MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true);
    helper.setFrom(XxlJobAdminConfig.getAdminConfig().getEmailFrom(), personal);
    helper.setTo(email);
    helper.setSubject(title);
    helper.setText(content, true);
    XxlJobAdminConfig.getAdminConfig().getMailSender().send(mimeMessage);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    但是对于公用账户而言,密码是动态的,所以需要去动态获取设置,改造为

    JavaMailSenderImpl mailSender = XxlJobAdminConfig.getAdminConfig().getMailSender();
    mailSender.setPassword(getMailPassword(mailSender.getUsername(), token));
    MimeMessage mimeMessage = mailSender.createMimeMessage();
    MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true, StandardCharsets.UTF_8.name());
    helper.setFrom(XxlJobAdminConfig.getAdminConfig().getEmailFrom());
    helper.setTo(email);
    helper.setSubject(title);
    helper.setText(content, true);
    mailSender.send(mimeMessage);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    主要就是将XxlJobAdminConfig中的mailSender的实现类换为JavaMailSenderImpl,这样我们就可以给他动态设置password,然后再自己写个根据token获取密码的http接口即可。

    最后,因为很多企业邮箱是缺少证书的,而xxl-job中默认是开启了邮箱安全验证的,这个时候就会报类似

    Caused by: javax.net.ssl.SSLHandshakeException:                 
      sun.security.validator.ValidatorException: PKIX path building failed: 
      sun.security.provider.certpath.SunCertPathBuilderException:  
        unable to find valid certification path to requested target 
    
    • 1
    • 2
    • 3
    • 4

    网上很多办法是虚拟一个证书之类的,但是没必要,我是直接把xxl-job的邮箱安全验证关掉的。
    把他们注释掉就好了

    #spring.mail.properties.mail.smtp.auth=true
    #spring.mail.properties.mail.smtp.starttls.enable=true
    #spring.mail.properties.mail.smtp.starttls.required=true
    #spring.mail.properties.mail.smtp.socketFactory.class=javax.net.ssl.SSLSocketFactory
    
    • 1
    • 2
    • 3
    • 4

    2. 关于xxl-job接入eureka的坑

    客户端接入eureka教程很简单,大家自己去搜,我这里列一下我踩的坑

    1. 关于pom依赖的导致的注册失败
      我一开始引入的依赖是idea自动引入的
    
    <dependency>
    	<groupId>org.springframework.cloudgroupId>
    	<artifactId>spring-cloud-netflix-eureka-clientartifactId>
    	<version>2.2.2.RELEASEversion>
    dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    要改成这样

    
    <dependency>
    	<groupId>org.springframework.cloudgroupId>
    	<artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
    dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5

    首先是引入的包发生了变化,只有下面这个才会自动注册(原因还在分析)
    其次是版本号去掉,eureka要和springboot版本号匹配(有些是不兼容的)
    但是直接在这里写版本号不利于统一管理,idea也会报错,所以groupId一致的最好交由父项目统一管理

    <dependencyManagement>
    		<dependencies>
    			<dependency>
    				<groupId>org.springframework.bootgroupId>
    				<artifactId>spring-boot-starter-parentartifactId>
    				<version>${spring-boot.version}version>
    				<type>pomtype>
    				<scope>importscope>
    			dependency>
    			<dependency>
    				<groupId>org.springframework.cloudgroupId>
    				<artifactId>spring-cloud-dependenciesartifactId>
    				<version>2021.0.3version>
    				<type>pomtype>
    				<scope>importscope>
    			dependency>
    		dependencies>
    	dependencyManagement>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    这里统一管理了org.springframework.bootorg.springframework.cloud两种依赖的版本,其中org.springframework.cloud是我后来加的,因为eureka用的是它。
    但是springCloud和springBoot也是有兼容关系的!
    兼容关系如下(大家复制以json格式打开)

    {"git":{"branch":"ab69b84e03e0cd44f38dab35bc3ae1cdcc35416e","commit":{"id":"ab69b84","time":"2022-08-23T15:02:55Z"}},"build":{"version":"0.0.1-SNAPSHOT","artifact":"start-site","versions":{"spring-boot":"2.7.3","initializr":"0.13.0-SNAPSHOT"},"name":"start.spring.io website","time":"2022-08-23T15:04:28.319Z","group":"io.spring.start"},"bom-ranges":{"codecentric-spring-boot-admin":{"2.4.3":"Spring Boot >=2.3.0.M1 and <2.5.0-M1","2.5.6":"Spring Boot >=2.5.0.M1 and <2.6.0-M1","2.6.8":"Spring Boot >=2.6.0.M1 and <2.7.0-M1","2.7.4":"Spring Boot >=2.7.0.M1 and <3.0.0-M1","3.0.0-M4":"Spring Boot >=3.0.0-M1 and <3.1.0-M1"},"solace-spring-boot":{"1.1.0":"Spring Boot >=2.3.0.M1 and <2.6.0-M1","1.2.1":"Spring Boot >=2.6.0.M1 and <2.7.0-M1"},"solace-spring-cloud":{"1.1.1":"Spring Boot >=2.3.0.M1 and <2.4.0-M1","2.1.0":"Spring Boot >=2.4.0.M1 and <2.6.0-M1","2.3.0":"Spring Boot >=2.6.0.M1 and <2.7.0-M1"},"spring-cloud":{"Hoxton.SR12":"Spring Boot >=2.2.0.RELEASE and <2.4.0.M1","2020.0.6":"Spring Boot >=2.4.0.M1 and <2.6.0-M1","2021.0.0-M1":"Spring Boot >=2.6.0-M1 and <2.6.0-M3","2021.0.0-M3":"Spring Boot >=2.6.0-M3 and <2.6.0-RC1","2021.0.0-RC1":"Spring Boot >=2.6.0-RC1 and <2.6.1","2021.0.3":"Spring Boot >=2.6.1 and <3.0.0-M1","2022.0.0-M1":"Spring Boot >=3.0.0-M1 and <3.0.0-M2","2022.0.0-M2":"Spring Boot >=3.0.0-M2 and <3.0.0-M3","2022.0.0-M3":"Spring Boot >=3.0.0-M3 and <3.0.0-M4","2022.0.0-M4":"Spring Boot >=3.0.0-M4 and <3.1.0-M1"},"spring-cloud-azure":{"4.3.0":"Spring Boot >=2.5.0.M1 and <3.0.0-M1"},"spring-cloud-gcp":{"2.0.11":"Spring Boot >=2.4.0-M1 and <2.6.0-M1","3.3.0":"Spring Boot >=2.6.0-M1 and <2.7.0-M1"},"spring-cloud-services":{"2.3.0.RELEASE":"Spring Boot >=2.3.0.RELEASE and <2.4.0-M1","2.4.1":"Spring Boot >=2.4.0-M1 and <2.5.0-M1","3.3.0":"Spring Boot >=2.5.0-M1 and <2.6.0-M1","3.4.0":"Spring Boot >=2.6.0-M1 and <2.7.0-M1","3.5.0":"Spring Boot >=2.7.0-M1 and <3.0.0-M1"},"spring-geode":{"1.3.12.RELEASE":"Spring Boot >=2.3.0.M1 and <2.4.0-M1","1.4.13":"Spring Boot >=2.4.0-M1 and <2.5.0-M1","1.5.14":"Spring Boot >=2.5.0-M1 and <2.6.0-M1","1.6.10":"Spring Boot >=2.6.0-M1 and <2.7.0-M1","1.7.2":"Spring Boot >=2.7.0-M1 and <3.0.0-M1","2.0.0-M4":"Spring Boot >=3.0.0-M1 and <3.1.0-M1"},"spring-shell":{"2.1.1":"Spring Boot >=2.7.0 and <3.0.0-M1"},"vaadin":{"14.8.16":"Spring Boot >=2.1.0.RELEASE and <2.6.0-M1","23.1.7":"Spring Boot >=2.6.0-M1 and <2.8.0-M1"},"wavefront":{"2.0.2":"Spring Boot >=2.1.0.RELEASE and <2.4.0-M1","2.1.1":"Spring Boot >=2.4.0-M1 and <2.5.0-M1","2.2.2":"Spring Boot >=2.5.0-M1 and <2.7.0-M1","2.3.0":"Spring Boot >=2.7.0-M1 and <3.0.0-M1"}},"dependency-ranges":{"native":{"0.9.0":"Spring Boot >=2.4.3 and <2.4.4","0.9.1":"Spring Boot >=2.4.4 and <2.4.5","0.9.2":"Spring Boot >=2.4.5 and <2.5.0-M1","0.10.0":"Spring Boot >=2.5.0-M1 and <2.5.2","0.10.1":"Spring Boot >=2.5.2 and <2.5.3","0.10.2":"Spring Boot >=2.5.3 and <2.5.4","0.10.3":"Spring Boot >=2.5.4 and <2.5.5","0.10.4":"Spring Boot >=2.5.5 and <2.5.6","0.10.5":"Spring Boot >=2.5.6 and <2.5.9","0.10.6":"Spring Boot >=2.5.9 and <2.6.0-M1","0.11.0-M1":"Spring Boot >=2.6.0-M1 and <2.6.0-RC1","0.11.0-M2":"Spring Boot >=2.6.0-RC1 and <2.6.0","0.11.0-RC1":"Spring Boot >=2.6.0 and <2.6.1","0.11.0":"Spring Boot >=2.6.1 and <2.6.2","0.11.1":"Spring Boot >=2.6.2 and <2.6.3","0.11.2":"Spring Boot >=2.6.3 and <2.6.4","0.11.3":"Spring Boot >=2.6.4 and <2.6.6","0.11.5":"Spring Boot >=2.6.6 and <2.7.0-M1","0.12.0":"Spring Boot >=2.7.0-M1 and <2.7.1","0.12.1":"Spring Boot >=2.7.1 and <3.0.0-M1"},"okta":{"1.4.0":"Spring Boot >=2.2.0.RELEASE and <2.4.0-M1","1.5.1":"Spring Boot >=2.4.0-M1 and <2.4.1","2.0.1":"Spring Boot >=2.4.1 and <2.5.0-M1","2.1.6":"Spring Boot >=2.5.0-M1 and <3.0.0-M1"},"mybatis":{"2.1.4":"Spring Boot >=2.1.0.RELEASE and <2.5.0-M1","2.2.2":"Spring Boot >=2.5.0-M1"},"camel":{"3.5.0":"Spring Boot >=2.3.0.M1 and <2.4.0-M1","3.10.0":"Spring Boot >=2.4.0.M1 and <2.5.0-M1","3.13.0":"Spring Boot >=2.5.0.M1 and <2.6.0-M1","3.17.0":"Spring Boot >=2.6.0.M1 and <2.7.0-M1","3.18.1":"Spring Boot >=2.7.0.M1 and <3.0.0-M1"},"picocli":{"4.6.3":"Spring Boot >=2.4.0.RELEASE and <3.0.0-M1"},"open-service-broker":{"3.2.0":"Spring Boot >=2.3.0.M1 and <2.4.0-M1","3.3.1":"Spring Boot >=2.4.0-M1 and <2.5.0-M1","3.4.1":"Spring Boot >=2.5.0-M1 and <2.6.0-M1","3.5.0":"Spring Boot >=2.6.0-M1 and <2.7.0-M1"}}}
    
    • 1

    然后这里是springCloud的版本
    最后因为xxl-job的springBoot版本是2.6.7,所以选出了2021.0.3这个版本的springCloud。
    启动,注册成功。
    所以总结一下,就是首先eureka引入的依赖要正确,其次它的版本不能与springBoot冲突,不然就会报各种错,服务都起不来。

    2021-12-22 18:06:42.658 ERROR 12172 — [nfoReplicator-0] c.n.d.s.t.d.RedirectingEurekaHttpClient : Request execution error. endpoint=DefaultEndpoint{ serviceUrl='http://localhost:8761/eureka/}at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:49) [spring-boot-devtools-2.2.5.RELEASE.jar:2.2.5.RELEASE]
    Caused by: java.net.ConnectException: Connection refused: connect at java.net.DualStackPlainSocketImpl.waitForConnect(Native Method) ~[na:1.8.0_41]

    3. 关于xxl-job注册到容器中

    这个其实不算坑了,算是一个需要注意的点吧,就是服务注册到实体机和容器中的一个小区别。
    就是注册的host和port最好是获取容器对外暴漏的host与port,相应的也要根据部署环境的不同,写不同的配置文件,这个因为每个人的场景可能都不一样,所以不细说了。

    4. 关于调度器无法访问容器中的执行器的坑

    调度器和执行器都注册成功以后,发现调度器无法访问执行器了,奇怪,明明本地试的时候还可以。
    一开始报错Full authentication is required to access this resource,一查,应该是spring安全策略的问题,可是如果这样的话本地按理说也会有这个报错,所以感觉不是这个问题。
    但还是试了试把报错的url加入到了安全白名单,然后试了一下发现404了。
    所以判断应该不是安全策略的问题,应该是访问路径出了问题。
    最后发现试执行器注册的信息有误,正确的注册信息应该是

    @Bean
        public XxlJobSpringExecutor assetsXxlJobExecutor() {
            log.info(">>>>>>>>>>> xxl-job config init.");
            XxlJobSpringExecutor xxlJobSpringExecutor = new XxlJobSpringExecutor();
            xxlJobSpringExecutor.setAdminAddresses(adminAddresses);
            xxlJobSpringExecutor.setAppname(appname);
            xxlJobSpringExecutor.setAccessToken(accessToken);
            xxlJobSpringExecutor.setLogPath(logPath);
            // xxlJobSpringExecutor.setIp(ip);
            xxlJobSpringExecutor.setPort(innerPort);
            xxlJobSpringExecutor.setAddress("http://{ip_port}/".replace("{ip_port}", IpUtil.getIpPort(ip, externalPort)));
            return xxlJobSpringExecutor;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    这里的innerPort是容器内部端口,也就是执行器启动的时候要使用的端口,一般会在容器部署的时候分配一个端口。
    这里的externalPort是容器对外暴漏的端口,也就是容器真正起来以后,容器内部端口所分配到的对外暴露端口。

    这里的内部端口就类似于 8081 8080之类的,每个容器中都有一套,但一个实体机上会有多个容器,所以这个端口其实不是真实的。
    对外暴露的端口可能就是31239之类的,其实容器所在实体机上的真正端口,也就是对外暴露的端口,用做执行器注册与调度通信,它与容器内部端口是有映射关系的。

    然后这里注册的setAddress是用于 “执行器注册” 和 “调度中心请求并触发任务”,这点xxl-job官方文档中也有写,setPort传进去是单独用做执行器启动的。
    这样设置以后,执行器和调度器就可以正常通信了。

  • 相关阅读:
    3.Netty中Channel通道概述
    使用MediatR实现CQRS
    JAVA 基础网络编程(后一部分)
    SaaSBase:什么是SaleSmartly?
    <图像处理> 图像插值算法
    设计模式--六大原则
    R数据分析:扫盲贴,什么是多重插补
    Protobuf 和JSON 性能分析
    CESM笔记——输出文件剪切
    智慧电力解决方案-最新全套文件
  • 原文地址:https://blog.csdn.net/qq_31363843/article/details/126520184