目录:
分布式链路追踪-SkyWalking
为什么需要链路追踪
在这个微服务系统中,用户通过浏览器的 H5页面访问系统,这个用户请求会先抵达微服务网关组件,然后网关再把请求分发给各个微服务。所以你会发现,用户请求从发起到结束要经历 很多个微服务的处理,这里面还涉及到消息组件的集成。
存在的问题:
- 服务之间的依赖与被依赖的关系如何能够清晰的看到?
- 出现异常时如何能够快速定位到异常服务?
- 出现性能瓶颈时如何能够迅速定位哪个服务影响的?
链路
可以将链路理解为物理电路图
- 链路和电路图都有起点和终点。
- 链路和电路图都通过可视化得技术手段,来模拟真实组件得运行轨迹。
- 链路和电路图都非常复杂,并且随着系统复杂程度得增加,其可视化复杂度也会成几何倍数增加。
解决:
为了能够在分布式架构中快速定位问题,分布式链路追踪应运而生。将一次分布式请求还原成调用链路,进行日志记录,性能监控并将一次分布式请求的调用情况集中展示。比如各个服务节点上的耗时、请求具体到达哪台机器上、每个服务节点的请求状态等等。
常见链路追踪技术有那些
市面上有很多链路追踪的项目,其中也不乏一些优秀的,如下:
Sleuth:SpringCloud 提供的分布式系统中链路追踪解决方案。很可惜的是阿里系并没有链路追踪相关的开源项目,我们可以采用Spring Cloud Sleuth+Zipkin来做链路追踪的解决方案。
zipkin:由Twitter公司开源,开放源代码分布式的跟踪系统,用于收集服务的定时数据,以解决微服务架构中的延迟问题,包括:数据的收集、存储、查找和展现。该产品结合spring-cloud-sleuth使用较为简单, 集成很方便, 但是功能较简单。
pinpoint:韩国人开源的基于字节码注入的调用链分析,以及应用监控分析工具。特点是支持多种插件,UI功能强大,接入端无代码侵入
Skywalking:SkyWalking是本土开源的基于字节码注入的调用链分析,以及应用监控分析工具。特点是支持多种插件,UI功能较强,接入端无代码侵入。目前已加入Apache孵化器。
什么是SkyWalking
SkyWalking是开源的可观测平台,主要用于收集,分析,聚合和可视化服务和云原生基础设施的数据。Skywalking提供了一种简单得方式来维护分布式系统得视图关系。
SkyWalking
SkyWalking是开源的一款分布式追踪,分析,告警的工具,现已属于Apache旗下开源项目, SkyWalking为服务提供了自动探针代理,将数据通过gRPC或者HTTP传输给后端平台,后端平台将数据存储在Storage中,并且分析数据将结果展示在UI中。
优点:
- 多种监控手段多语言自动探针,Java,.NET Core 和 Node.JS
- 轻量高效,不需要大数据
- 模块化,UI、存储、集群管理多种机制可选,
- 支持告警
- 社区活跃
缺点:
较为新兴,成熟度不够高
SkyWalking核心概念
注意:
- 上部分 Agent :负责从应用中,收集链路信息,发送给 SkyWalking OAP 服务器。目前支持
SkyWalking、Zikpin、Jaeger 等提供的 Tracing 数据信息。而我们目前采用的是,SkyWalking Agent
收集 SkyWalking Tracing 数据,传递给服务器。- 下部分 SkyWalking OAP :负责接收 Agent 发送的
Tracing 数据信息,然后进行分析(Analysis Core) ,存储到外部存储器( Storage ),最终提供查询( Query
)功能。- 左部分 Storage :Tracing 数据存储。目前支持 ES、MySQL、Sharding Sphere、TiDB、H2
多种存储器。而我们目前采用的是 ES ,主要考虑是 SkyWalking 开发团队自己的生产环境采用 ES 为主。- 右部分SkyWalking UI :负责提供控台,查看链路等等。
什么是探针Java Agent
探针产生的背景
在开发过程中,开发人员经常会使用IDEA的Debug功能(包含本地和远程)调试应用,在JVM进程期间获取应用运行的JVM信息,变量信息等。这些个技术通过Java Agent来实现的,那么Java Agent到底是啥,为啥这么吊?
哪里有Java Agent:
- idea得Debug功能
- 热部署 JRebel
- 各种线上诊断工具
- 代码覆盖率工具
- 性能分析
什么是探针
Java Agent 又叫做 Java 探针,是在 JDK1.5 引入的一种可以动态修改 Java 字节码的技术。Java 类编译之后形成字节码被 JVM 执行,在 JVM 在执行这些字节码之前获取这些字节码信息,并且通过字节码转换器对这些字节码进行修改,来完成一些额外的功能。
Java探针工具技术原理
流程:
- 在JVM加载class二进制文件的时候,利用ASM动态的修改加载的class文件,在监控的方法前后添加计时器功能,用于计算监控方法耗时;
- 将监控的相关方法 和 耗时及内部调用情况,按照顺序放入处理器;
- 处理器利用栈先进后出的特点对方法调用先后顺序做处理,当一个请求处理结束后,将耗时方法轨迹和入参map输出到文件中;
- 然后区分出耗时的业务,转化为xml格式进行解析和分析。
Java探针日志监控实现之环境搭建
引入依赖
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
编写HelloController
@RestController
public class HelloController {
@GetMapping("/hello")
public String hello() {
return "hello agent";
}
}
打包SpringBoot项目
Java探针日志监控实现之探针实现
如何使用Java Agent
JDK1.5引入了java.lang.instrument包,开发者可以很方便的实现字节码增强。其核心功能由java.lang.instrument.Instrumentation接口提供。
Instrumentation 有两种使用方式:
- 在应用运行之前,通过premain()方法来实现,在应用启动时侵入并代理应用。
- 在应用运行之后,通过调用Attach AP和agentmain()方法来实现
注意:
javassist是一个库,实现ClassFileTransformer接口中的transform()方法。ClassFileTransformer
这个接口的目的就是在class被装载到JVM之前将class字节码转换掉,从而达到动态注入代码的目的。
新建Maven项目javaagentdemo
Pom引入依赖
<dependencies>
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.22.0-GA</version>
</dependency>
</dependencies>
<build>
<finalName>javaagent</finalName>
<plugins>
<!--
META-INF 下 MANIFEST.MF 文件 内容
Manifest-Version: 1.0
Premain-Class: com.jenson.TestAgent
下面Maven插件可以自动实现
-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.0.0</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<manifestEntries>
<Premain-Class>com.itbaizhan.PreMainTraceAgent</Premain-Class>
</manifestEntries>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
编写探针类TestAgent
package com.jenson;
import java.lang.instrument.Instrumentation;
/**
* @author Jenson
*/
public class TestAgent {
/**
* 在main方法运行前,与main方法运行于同一JVM中
*
* @param agentArgs agentArgs是premain函数得到的程序参数,随同"-javaagent"一同传入,
* 与main函数不同的是,该参数是一个字符串而不是一个字符串数组,
* 如果程序参数有多个,程序将自行解析这个字符串
* @param inst 一个java.lang.instrument.Instrumentation实例,由JVM自动传入,
* java.lang.instrument.Instrumentation是instrument包中的一个接口,
* 也是核心部分,集中了其中几乎所有的功能方法,例如类定义的转换和操作
*/
public static void premain(String agentArgs, Instrumentation inst) {
System.out.println("==============premain1 执行===================");
System.out.println("agentArgs : " + agentArgs);
// 添加Transformer
inst.addTransformer(new TestTransformer());
}
}
编写TestTransformer
/**
* @author Jenson
*/
public class TestTransformer implements ClassFileTransformer {
public final String TEST_CLASS_NAME = "com.jjy.controller.HelloController";
public final String METHOD_NAME = "hello";
public byte[] transform(ClassLoader loader,
String className,
Class<?> classBeingRedefined,
ProtectionDomain protectionDomain,
byte[] classfileBuffer) throws IllegalClassFormatException {
// System.out.println("className : " + className);
String finalClassName = className.replace("/", ".");
if (TEST_CLASS_NAME.equals(finalClassName)) {
System.out.println("class name 匹配上了 !");
CtClass ctClass;
try {
ctClass = ClassPool.getDefault().get(finalClassName);
System.out.println("ctClass is OK !");
CtMethod ctMethod = ctClass.getDeclaredMethod(METHOD_NAME);
System.out.println("CtMethod is OK !");
ctMethod.insertBefore("System.out.println(\"字节码添加成功,打印日志 !\");");
return ctClass.toBytecode();
} catch (Exception e) {
e.printStackTrace();
System.out.println(e.getMessage());
}
}
return null;
}
}
项目测试打包
添加javaagent启动参数
java -javaagent:agent路径 -jar 项目名.jar
启动,打印出日志
发送请求http://localhost:8080/hello
搭建 Elasticsearch
准备一台搭载有CentOS7系统的虚拟机,使用XShell连接虚拟机
关闭防火墙,方便访问ES
#关闭防火墙:
systemctl stop firewalld.service
#禁止防火墙自启动:
systemctl disable firewalld.service
- 配置最大可创建文件数大小
#打开系统文件:
vim /etc/sysctl.conf
#添加以下配置:
vm.max_map_count=655360
#配置生效:
sysctl -p
- 由于ES不能以root用户运行,我们需要创建一个非root用户,此处创建一个名为es的用户:
#创建用户:
useradd es
安装服务
使用rz命令将linux版的ES上传至虚拟机
解压ES
#解压:
tar -zxvf elasticsearch-8.10.4-linux-x86_64.tar.gz
#重命名:
mv elasticsearch-8.10.4 elasticsearch1
#移动文件夹:
mv elasticsearch1 /usr/local/
#es用户取得该文件夹权限:
chown -R es:es /usr/local/elasticsearch1
- 启动ES服务:
#切换为es用户:
su es
#进入ES安装文件夹:
cd /usr/local/elasticsearch1/bin/
#启动ES服务:
./elasticsearch
SkyWalking服务环境搭建
前置条件
- 安装jdk17
- 安装Elasticsearch
下载SkyWalking包
解压SkyWalking包
tar -zxvf apache-skywalking-apm-9.7.0.tar.gz -C /usr/local/
SkyWalking包目录介绍
介绍:
- webapp: Ul前端(web 监控页面)的jar包和配置文件
- oap-libs:后台应用的jar包,以及它的依赖jar包
- config:启动后台应用程序的配置文件,是使用的各种配置
- bin:各种启动脚本,一般使用脚本startup.*来启动web页面和对应的后台应用
- agent:代理服务jar包
修改配置文件
修改/usr/local/apache-skywalking-apm-bin/config/application.yml
启动 SkyWalking OAP 服务
bin/oapService.sh
SkyWalking OAP started successfully!
是否真正启动成功,打开 logs/skywalking-oap-server.log
日志文件,查看是否有错误日志。 首次启动时,因为 SkyWalking OAP 会创建 Elasticsearch 的索引,所以会“疯狂”的打印日志。
友情提示:
因为首次启动会创建 Elasticsearch 索引,所以可能会比较慢。
最终,我们看到如下日志,基本可以代表 SkyWalking OAP 服务启动成功:
启动 SkyWalking UI 服务
bin/webappService.sh
SkyWalking Web Application started successfully!
测试服务
请求http://192.168.66.100:8080
搭建微服务
引入依赖
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.49</version>
<!-- <scope>runtime</scope>-->
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.4</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
编写核心配置
创建application.yml文件
spring:
application:
# 应用名字
name: cloud-payment-provider
cloud:
nacos:
discovery:
# Nacos注册中心的地址
server-addr: 192.168.47.100:8848
# 数据库配置
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/test?useSSL=false&useUnicode=true&characterEncoding=utf8&useSSL=false&useTimezone=true&serverTimezone=GMT%2B8
username: root
password: 123456
server:
port: 8003
编写主启动类
@MapperScan("com.jjy.mapper")
@SpringBootApplication
@Slf4j
public class PaymentMain8003 {
public static void main(String[] args) {
SpringApplication.run(PaymentMain8003.class,args);
log.info("********* 服务提供者启动成功 ******");
}
}
现有一张 User 表,其表结构如下:
id | name | age |
---|---|---|
1 | Jone | 18 |
2 | Jack | 20 |
3 | Tom | 28 |
4 | Sandy | 21 |
其对应的数据库 Schema 脚本如下:
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user`
(
id BIGINT NOT NULL COMMENT '主键ID',
name VARCHAR(30) NULL DEFAULT NULL COMMENT '姓名',
age INT NULL DEFAULT NULL COMMENT '年龄',
PRIMARY KEY (id)
);
其对应的数据库 Data 脚本如下:
DELETE FROM `user`;
INSERT INTO `user` (id, name, age) VALUES
(1, 'Jone', 18),
(2, 'Jack', 20),
(3, 'Tom', 28),
(4, 'Sandy', 21),
(5, 'Billie', 24);
编写实体类 User.java
@Data
@TableName("`user`")
public class User {
private Long id;
private String name;
private Integer age;
private String email;
}
编写 Mapper 包下的 UserMapper
接口
public interface UserMapper extends BaseMapper<User> {
}
微服务接入SkyWalking探针
探针,用来收集和发送数据到归集器。
下载官方提供探针
网址https://skywalking.apache.org/downloads/
拷贝探针文件到项目中
修改项目的VM运行参数
点击菜单栏中的 Run -> EditConfigurations…,此处我们以 cloud-gateway-gateway9527 项目为例,修改参数如下:
-javaagent:C:\Users\Administrator\IdeaProjects\jjy_cloud\jjy_cloud\skywalking-agent\skywalking-agent.jar
-DSW_AGENT_NAME=consumer-order
-DSW_AGENT_COLLECTOR_BACKEND_SERVICES=192.168.47.100:11800
参数:
-javaagent:用于指定探针路径。
-DSW_AGENT_NAME:服务名字
-DSW_AGENT_COLLECTOR_BACKEND_SERVICES:连接地址
测试监控
SkyWalking日志
POM中引入相关依赖
Skywalking8.4.0版本开始才支持收集日志功能,同时pom需引用以下依赖。
<dependency>
<groupId>org.apache.skywalking</groupId>
<artifactId>apm-toolkit-logback-1.x</artifactId>
**<version>9.1.0</version>
</dependency>
Logback配置
在resource文件中创建logback.xml并且加入配置。
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<!-- 日志输出编码 -->
<encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
<layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.TraceIdPatternLogbackLayout">
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level logger_name:%logger{36} - [%tid] - message:%msg%n</pattern>
</layout>
</encoder>
</appender>
<root level="info">
<appender-ref ref="console"/>
</root>
</configuration>
Skywalking通过gRPC上报日志
gRPC报告程序可以将收集到的日志发送给Skywalking OAP服务器上。
<appender name="log" class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.log.GRPCLogClientAppender">
<!-- 日志输出编码 -->
<encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
<layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.TraceIdPatternLogbackLayout">
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level logger_name:%logger{36} - [%tid] - message:%msg%n</pattern>
</layout>
</encoder>
</appender>
<root level="info">
<appender-ref ref="console"/>
<appender-ref ref="log"/>
</root>
打开你的agent/config/agent.config配置文件,添加如下配置信息,注意skywalking的log通信用的grpc:
# 指定要向其报告日志数据的GRPC服务器主机
plugin.toolkit.log.grpc.reporter.server_host=${SW_GRPC_LOG_SERVER_HOST:192.168.47.100}
# 指定要向其报告日志数据的GRPC服务器端口
plugin.toolkit.log.grpc.reporter.server_port=${SW_GRPC_LOG_SERVER_PORT:11800}
# 指定GRPC客户端要报告的日志数据的最大大小
plugin.toolkit.log.grpc.reporter.max_message_size=${SW_GRPC_LOG_MAX_MESSAGE_SIZE:10485760}
# 客户端向上游发送数据时将超时多长时间,单位是秒
plugin.toolkit.log.grpc.reporter.upstream_timeout=${SW_GRPC_LOG_GRPC_UPSTREAM_TIMEOUT:30}
注意:
注:gRPC报告程序可以将收集到的日志转发到SkyWalking OAP服务器或SkyWalking Satellite卫星。
测试
SkyWalioking告警
什么是skyWalking告警功能
SkyWalking是一个开源的分布式系统追踪和性能监控工具。除了提供实时的系统性能监控和分析功能外,SkyWalking还提供了告警功能,用于监控系统的指标数据,并在数据超过预设阈值时触发告警。
Skywalking默认支持7中通知:
web、grpc、微信、钉钉、飞书、华为weLink、slack
为什么要使用skyWalking的告警功能
- 及时发现异常情况
- 提高故障处理效率
- 避免数据丢失和损坏
- 提升系统性能和稳定性
如何使用skyWalking的告警功能
- 告警规则
告警规则定义了要监控的指标数据、阈值和触发条件。可以根据实际需求,定义多个告警规则。例如,可以设置当系统的平均响应时间超过100ms时,触发告警。 - 告警通知
告警通知定义了当告警触发时,要发送通知的方式和接收人员。可以通过邮件、短信。钉钉等方式发送告警通知。 - 告警持续时间
告警持续时间定义了告警状态的持续时间。当告警触发后,会持续发送通知,直到告警状态解除或达到设定的持续时间。
#告警配置文件: alarm-settings.yml
service_response_time rule:
#指定的规则
metrics-name : service _resp_time
op: ">"
# 阈值
threshold:1 #单位毫秒
#多久检查一次当前的指标数据是否符合告警规则
period:5
# 达到多少次告警后,发送告警消息
count:1
#告警消息内容
message:服务{name}最近5分钟以内响应时间超过了1ms
Skywalking自定义告警规则
默认告警规则
rules:
service_resp_time_rule:
metrics-name: service_resp_time
op: ">"
threshold: 1000
period: 10
count: 3
silence-period: 5
message: Response time of service {name} is more than 1000ms in 3 minutes of last 10 minutes.
service_sla_rule:
metrics-name: service_sla
op: "<"
threshold: 8000
period: 10
count: 2
silence-period: 3
message: Successful rate of service {name} is lower than 80% in 2 minutes of last 10 minutes.
service_p90_sla_rule:
metrics-name: service_p90
op: ">"
threshold: 1000
period: 10
count: 3
silence-period: 5
message: 90% response time of service {name} is more than 1000ms in 3 minutes of last 10 minutes.
service_instance_resp_time_rule:
metrics-name: service_instance_resp_time
op: ">"
threshold: 1000
period: 10
count: 2
silence-period: 5
message: Response time of service instance {name} is more than 1000ms in 2 minutes of last 10 minutes.
webhooks:
- http://127.0.0.1/notify/
- http://127.0.0.1/go-wechat/
以上文件定义了默认的4种规则
- 最近3分钟内服务的平均响应时间超过1秒
- 最近2分钟服务成功率低于80%
- 最近3分钟90%服务响应时间超过1秒
- 最近2分钟内服务实例的平均响应时间超过1秒 规则中的参数属性如下
自定义告警规则
修改Skywalking配置文件。vim alarm-settings.yml
# vim config/alarm-settings.yml
service_response_time_rule:
metrics-name: service_resp_time
op: ">"
threshold: 1
period: 1
count: 1
message: 服务{name}最近1分钟以内响应时间超过了1ms
温馨提示:注意预警属性对其
属性参照表
属性 | 含义 |
---|---|
metrics-name | oal脚本中的度量名称 |
threshold | 阈值,与metrics-name和下面的比较符号相匹配 |
op | 比较操作符,可以设定>,<,= |
period | 多久检查一次当前的指标数据是否符合告警规则,单位分钟 |
count | 达到多少次后,发送告警消息 |
silence-period | 在多久之内,忽略相同的告警消息 |
message | 告警消息内容 |
include-names | 本规则告警生效的服务列表 |
测试
SkyWalking网络钩子Webhooks
Webhooks网络钩子
Webhok可以简单理解为是一种Web层面的回调机制。告警就是一个事件,当事件发生时Skywalking会主动调用一个配置好的接口,这个接口就是所谓的Webhook;
注意:
Skywalking的告警消息会通过借HTTP请求进行发送,请求方法为POST (Content-Type
为application/json。其JSON数据实基于List<org.apache.skywalking.oap.server.core.alarm.AlarmMessage>进行序列化的。
JSON数据示例
[{
"scopeId": 1,
"scope": "SERVICE",
"name": "serviceA",
"id0": "12",
"id1": "",
"ruleName": "service_resp_time_rule",
"alarmMessage": "alarmMessage xxxx",
"startTime": 1560524171000
}]
创建项目
引入依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.22</version>
</dependency>
</dependencies>
创建接收实体类AlarmMessageDto
https://github.com/apache/skywalking/blob/v8.5.0/docs/en/setup/backend/backend-alarm.md
import lombok.Data;
@Data
public class AlarmMessageDto {
private int scopeId;
private String scope;
private String name;
private String id0;
private String id1;
private String ruleName;
private String alarmMessage;
private List<Tag> tags;
private long startTime;
private transient int period;
private transient boolean onlyAsCondition;
@Data
public static class Tag{
private String key;
private String value;
}
}
编写钩子接口
/**
* 订单机器人通知的
*/
@PostMapping("/alarm")
public void sendDinging(@RequestBody List<AlarmMessageDto> alarmMessageDtoList) {
StringBuilder builder = new StringBuilder();
alarmMessageDtoList.forEach(info -> {
builder.append("\nscopeId:").append(info.getScopeId())
.append("\nScope实体:").append(info.getScope())
.append("\n告警消息:").append(info.getAlarmMessage())
.append("\n告警规则:").append(info.getRuleName())
.append("\n\n--------------")
.append("----------\n\n");
});
}
配置网络钩子
alarm-settings.yml 增加alarm接口
SkyWalking短信通知
申请阿里短信服务
访问阿里云https://www.aliyun.com/,完成登录
进入短信服务
开通短信服务
购买短信条数
购买完成进入阿里云短信控制台https://dysms.console.aliyun.com/overview
绑定测试手机号
点击调用API发送短信,可以看到发送短信的JAVA代码。
申请阿里云秘钥,该秘钥在发送短信时会作为参数传入
添加相关依赖
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>dysmsapi20170525</artifactId>
<version>2.0.24</version>
</dependency>
在通用模块编写发送短信服务接口
/**
* 短信服务
*/
public interface MessageService {
/**
* 发送短信
*
* @param phoneNumber 手机号
* @param code 验证码
* @return 返回结果
*/
BaseResult sendMessage(String phoneNumber, String code);
}
在短信服务模块编写发送短信实现类
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import lombok.SneakyThrows;
/**
* 短信服务实现类
*/
@Service
public class MessageServiceImpl implements MessageService {
/**
* 访问密钥ID
*/
@Value("${message.accessKeyId}")
private String accessKeyId;
/**
* 访问密钥秘密
*/
@Value("${message.accessKeySecret}")
private String accessKeySecret;
/**
* 使用AK&SK初始化账号Client
*
* @param accessKeyId 访问密钥ID
* @param accessKeySecret 访问密钥秘密
* @return Client Client实例
* @throws Exception 可能抛出的异常
*/
@SneakyThrows
private Client createClient(String accessKeyId, String accessKeySecret) {
Config config = new Config()
.setAccessKeyId(accessKeyId)
.setAccessKeySecret(accessKeySecret);
// 访问的域名
config.endpoint = "dysmsapi.aliyuncs.com";
return new Client(config);
}
/**
* 发送短信
*
* @param phoneNumber 手机号
* @param code 验证码
* @return BaseResult 返回结果
*/
@SneakyThrows
@Override
public BaseResult sendMessage(String phoneNumber, String code) {
Client client = createClient(accessKeyId, accessKeySecret);
SendSmsRequest sendSmsRequest = new SendSmsRequest()
.setSignName("阿里云短信测试")
.setTemplateCode("SMS_154950909")
.setPhoneNumbers(phoneNumber)
.setTemplateParam("{\"code\":\"" + code + "\"}");
RuntimeOptions runtime = new RuntimeOptions();
// 复制代码运行请自行打印API的返回值
SendSmsResponse sendSmsResponse = client.sendSmsWithOptions(sendSmsRequest, runtime);
SendSmsResponseBody body = sendSmsResponse.getBody();
if ("OK".equals(body.getCode())) {
return new BaseResult(200, body.getCode(), body.getMessage());
} else {
return new BaseResult(500, body.getCode(), body.getMessage());
}
}
}
SkyWalking邮件告警
邮件发送原理
SMTP 协议全称为 Simple Mail Transfer Protocol,译作简单邮件传输协议,它定义了邮件客户端软件与 SMTP 服务器之间,以及 SMTP 服务器与 SMTP 服务器之间的通信规则。
授权过程
所以在使用springboot发送邮件之前,要开启POP3和SMTP协议,需要获得邮件服务器的授权码,这里以qq邮箱为例,展示获取授权码的过程:pkzljvxjbzvgbeci
在账户的下面有一个开启SMTP协议的开关并进行密码验证:
成功后会出现
POM引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
配置邮箱基本信息
spring:
mail:
# 配置 SMTP 服务器地址
host: smtp.qq.com
# 发送者邮箱
username: 877910962@qq.com
# 配置密码,注意不是真正的密码,而是刚刚申请到的授权码
password: pkzljvxjbzvgbeci
# 默认的邮件编码为UTF-8
default-encoding: UTF-8
properties:
mail:
smtp:
# 需要验证用户名密码
auth: true
# 设置为配置SMTP连接的属性。要使用STARTTLS,必须设置以下属性
starttls:
enable: true
required: true
注意:
- 126邮箱SMTP服务器地址:smtp.126.com,端口号:465或者994
- 163邮箱SMTP服务器地址:smtp.163.com,端口号:465或者994
- yeah邮箱SMTP服务器地址:smtp.yeah.net,端口号:465或者994
- qq邮箱SMTP服务器地址:smtp.qq.com,端口号465或587
编写接口
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
* 使用REST API发送邮件
*/
@RestController
public class EmailController {
/**
* 注入JavaMailSender对象
*/
@Autowired
private JavaMailSender javaMailSender;
/**
* 发送邮件
*
* @param alarmMessages 告警消息列表
*/
@GetMapping("sendMail")
public void sendEmail(@RequestBody List<AlarmMessage> alarmMessages) {
alarmMessages.forEach(info -> {
SimpleMailMessage simpleMailMessage = new SimpleMailMessage();
// 发件人
simpleMailMessage.setFrom("877910962@qq.com");
// 收件人
simpleMailMessage.setTo("877910962@qq.com");
// 邮件主题
simpleMailMessage.setSubject(info.getScope());
// 邮件内容
simpleMailMessage.setText(info.getAlarmMessage());
// 发送邮件
javaMailSender.send(simpleMailMessage);
});
}
}
如果我的内容对你有帮助,请点赞,评论,收藏。创作不易,大家的支持就是我坚持下去的动力