定时器
定时器的数值放到配置文件,如果使用@Scheduled(fixedDelay = 类中的变量)这种方式试过不行
@Scheduled(fixedDelayString = "${fixedDelayString}")
public void open() {
}
fixedDelayString=30000
RocketMQ
下载源码或者二进制包
# 源码安装
# 解压文件
unzip rocketmq-all-5.2.0-source-release.zip
cd rocketmq-all-5.2.0-source-release/
# 编译
mvn -Prelease-all -DskipTests -Dspotbugs.skip=true clean install -U
# 编译后的二进制的目录,配置文件在这里
cd ./distribution
修改配置文件
# 修改配置文件
# 修改内存配置,虚拟机的内存太小了
# vim ./bin/runserver.sh
JAVA_OPT="${JAVA_OPT} -server -Xms512m -Xmx521m -Xmn256g -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=320m"
# vim ./bin/runbroker.sh
JAVA_OPT="${JAVA_OPT} -server -Xms512m -Xmx512m"
启动
# 第一步.启动Name Server
sh bin/mqnamesrv
The Name Server boot success...
# 第二步.启动Broker
sh bin/mqbroker -n localhost:9876
The broker[%s, 172.30.30.233:10911] boot success...
测试发送消息
export NAMESRV_ADDR=localhost:9876
# 发送消息
sh bin/tools.sh org.apache.rocketmq.example.quickstart.Producer
# 接收消息
sh bin/tools.sh org.apache.rocketmq.example.quickstart.Consumer
关闭服务
sh bin/mqshutdown broker
sh bin/mqshutdown namesrv
监控
# 记得checkout release-rocketmq-console-1.0.0分支
git clone https://github.com/apache/rocketmq-externals/tree/release-rocketmq-console-1.0.0
# 修改配置文件里的rocketMQ远程地址
NameSvrAddrList = 192.168.254.124:9876
# 或者
rocketmq.config.namesrvAddr=192.168.254.124:9876
代码
单个生产者消费者
public class Provider {
public static void main(String[] args) throws Exception {
DefaultMQProducer producer = new DefaultMQProducer("group1");
producer.setNamesrvAddr("192.168.254.190:9876");
producer.start();
String msg = "什么鬼,咋莫名其妙就收到了";
Message message = new Message("topic1", "tag1", msg.getBytes());
SendResult sendResult = producer.send(message);
System.out.printf("%s%n", sendResult);
producer.shutdown();
}
}
public class Consumer {
public static void main(String[] args) throws MQClientException {
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("group1");
consumer.setNamesrvAddr("192.168.254.190:9876");
consumer.subscribe("topic1", "*");
consumer.registerMessageListener((MessageListenerConcurrently) (list, consumeConcurrentlyContext) -> {
// 业务逻辑
list.forEach(one->{
byte[] body = one.getBody();
System.out.println(new String(body));
});
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
});
consumer.start();
System.out.println("消费者启起来了");
}
}
单个生产者多个消费者
相同的Topic,相同的Group-负载均衡
默认就是负载均衡,多个消费者,每个人收一条信息,不重复

public class Provider {
public static void main(String[] args) throws Exception {
DefaultMQProducer producer = new DefaultMQProducer("group2");
producer.setNamesrvAddr("192.168.254.190:9876");
producer.start();
for (int i = 0; i < 10; i++) {
String msg = "默认接受者是负载均衡"+i;
Message message = new Message("topic2", "tag1", msg.getBytes());
SendResult sendResult = producer.send(message);
System.out.printf("%s%n", sendResult);
}
producer.shutdown();
}
}
public class Consumer {
public static void main(String[] args) throws MQClientException {
// 谁来收
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("group2");
// 从哪里收
consumer.setNamesrvAddr("192.168.254.190:9876");
// 监听队列
consumer.subscribe("topic2", "*");
// 监听器
consumer.registerMessageListener((MessageListenerConcurrently) (list, consumeConcurrentlyContext) -> {
// 业务逻辑
list.forEach(one->{
byte[] body = one.getBody();
System.out.println(new String(body));
});
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
});
consumer.start();
System.out.println("消费者启起来了");
}
}
// 第一个消费者
默认接受者是负载均衡0
默认接受者是负载均衡1
默认接受者是负载均衡4
默认接受者是负载均衡5
默认接受者是负载均衡8
默认接受者是负载均衡9
// 第二个消费者
默认接受者是负载均衡2
默认接受者是负载均衡3
默认接受者是负载均衡6
默认接受者是负载均衡7
相同的Topic,相同的Group-轮训
只是添加了消费者的一段代码:consumer.setMessageModel(MessageModel.BROADCASTING);

public class Provider {
public static void main(String[] args) throws Exception {
// 谁来发
DefaultMQProducer producer = new DefaultMQProducer("group3");
// 发给谁
producer.setNamesrvAddr("192.168.254.190:9876");
producer.start();
for (int i = 0; i < 10; i++) {
String msg = "默认接受者是负载均衡"+i;
Message message = new Message("topic3", "tag1", msg.getBytes());
SendResult sendResult = producer.send(message);
System.out.printf("%s%n", sendResult);
}
producer.shutdown();
}
}
public class Consumer {
public static void main(String[] args) throws MQClientException {
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("group3");
consumer.setNamesrvAddr("192.168.254.190:9876");
consumer.subscribe("topic3", "*");
consumer.setMessageModel(MessageModel.BROADCASTING);
// 监听器
consumer.registerMessageListener((MessageListenerConcurrently) (list, consumeConcurrentlyContext) -> {
// 业务逻辑
list.forEach(one->{
byte[] body = one.getBody();
System.out.println(new String(body));
});
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
});
consumer.start();
System.out.println("消费者启起来了");
}
}
// 两个消费者都是收到相同的消息
默认接受者是负载均衡0
默认接受者是负载均衡1
默认接受者是负载均衡2
默认接受者是负载均衡3
默认接受者是负载均衡4
默认接受者是负载均衡5
默认接受者是负载均衡6
默认接受者是负载均衡7
默认接受者是负载均衡8
默认接受者是负载均衡9
不同的Topic,不同的Group

// 消费者1
public class Consumer {
public static void main(String[] args) throws MQClientException {
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("group4");
consumer.setNamesrvAddr("192.168.254.190:9876");
consumer.subscribe("topic4", "*");
// 监听器
consumer.registerMessageListener((MessageListenerConcurrently) (list, consumeConcurrentlyContext) -> {
// 业务逻辑
list.forEach(one->{
byte[] body = one.getBody();
System.out.println(new String(body));
});
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
});
consumer.start();
System.out.println("消费者启起来了");
}
}
// 消费者2
public class Consumer {
public static void main(String[] args) throws MQClientException {
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("group4");
consumer.setNamesrvAddr("192.168.254.190:9876");
consumer.subscribe("topic4", "*");
// 监听器
consumer.registerMessageListener((MessageListenerConcurrently) (list, consumeConcurrentlyContext) -> {
// 业务逻辑
list.forEach(one->{
byte[] body = one.getBody();
System.out.println(new String(body));
});
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
});
consumer.start();
System.out.println("消费者启起来了");
}
}
// 消费者3
public class Consumer {
public static void main(String[] args) throws MQClientException {
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("group4-1");
consumer.setNamesrvAddr("192.168.254.190:9876");
consumer.subscribe("topic4", "*");
// 监听器
consumer.registerMessageListener((MessageListenerConcurrently) (list, consumeConcurrentlyContext) -> {
// 业务逻辑
list.forEach(one->{
byte[] body = one.getBody();
System.out.println(new String(body));
});
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
});
consumer.start();
System.out.println("消费者启起来了");
}
}
// 消费者1
默认接受者是负载均衡2
默认接受者是负载均衡3
默认接受者是负载均衡6
默认接受者是负载均衡7
// 消费者2
默认接受者是负载均衡0
默认接受者是负载均衡1
默认接受者是负载均衡4
默认接受者是负载均衡5
默认接受者是负载均衡8
默认接受者是负载均衡9
// 消费者3
默认接受者是负载均衡0
默认接受者是负载均衡1
默认接受者是负载均衡2
默认接受者是负载均衡3
默认接受者是负载均衡4
默认接受者是负载均衡5
默认接受者是负载均衡6
默认接受者是负载均衡7
默认接受者是负载均衡8
默认接受者是负载均衡9
Swagger
<!-- 引入Swagger3依赖 -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-boot-starter</artifactId>
<version>3.0.0</version>
</dependency>
import io.swagger.annotations.ApiOperation;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.oas.annotations.EnableOpenApi;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
/**
* Swagger配置类
*/
@EnableOpenApi
@Configuration
public class SwaggerConfig {
@Bean
public Docket docket(){
return new Docket(DocumentationType.OAS_30)
.apiInfo(apiInfo()).enable(true)
.select()
//apis: 添加swagger接口提取范围
.apis(RequestHandlerSelectors.basePackage("com.example.study.controller"))
.apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
.paths(PathSelectors.any())
.build();
}
private ApiInfo apiInfo(){
return new ApiInfoBuilder()
.title("XX项目接口文档")
.description("XX项目描述")
.contact(new Contact("作者", "作者URL", "作者Email"))
.version("1.0")
.build();
}
}
# 原因是在springboot 2.6.0中将SpringMVC 默认路径匹配策略从AntPathMatcher
# 更改为PathPatternParser,导致出错,解决办法是切换回原先的AntPathMatcher
spring.mvc.pathmatch.matching-strategy=ant_path_matcher
knife4j
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-boot-starter</artifactId>
<version>3.0.3</version>
</dependency>
application.properties
spring.mvc.pathmatch.matching-strategy=ant_path_matcher
knife4j.enable: true
@RestController
@RequestMapping("/test")
@Api(tags = "测试2")
// 排序
@ApiSort(value = 1)
public class TestController2 {
@GetMapping("/3")
@ApiOperation("3")
public String method(){
return "hello wolrd";
}
@GetMapping("/4")
@ApiOperation("4")
public String method2(){
return "hello wolrd";
}
}
Log4j2
Log4j2
日志所有级别级别从上到下 ,级别从低到高,报错(更重要)这种,级别最高,info,debug相对不怎么重要
- all
- trace
- debug
- info
- warn
- error
- fail
Log4j2.xml配置例子
info级别输出到info.log
warn级别输出warn.log
error级别输出到error.log
debug级别输出到debug.log
<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
<!--<Configuration status="WARN" monitorInterval="30"> -->
<properties>
<property name="LOG_HOME">./service-logs</property>
</properties>
<Appenders>
<!--*********************控制台日志***********************-->
<Console name="consoleAppender" target="SYSTEM_OUT">
<!--设置日志格式及颜色-->
<PatternLayout
pattern="%style
基本结构是:
* Configuration
* properties 属性配置,可以在这里配置全局变量,可以在xml别的地方引入
* Appenders 具体配置日志框架该如何收集日志的动作
* Console 控制台输出
* RollingFile 滚动日志文件
* Loggers
* Root 理论上只有一个Root,level属性,是全局日志级别,如果AppenderRef没有配置level,就使用全局级别
* AppenderRef 引用具体配置的动作,level 没有的话,就使用全局级别,就像`CSS`属性那样,标签里的属性优先级最高
* Logger 单独配置一些类,level 没有的话,就使用全局级别,就像`CSS`属性那样,标签里的属性优先级最高
过滤器的具体使用方法
```xml
<Filters>
<!--过滤掉info及更高级别日志-->
<ThresholdFilter level="info" onMatch="DENY" onMismatch="NEUTRAL"/>
</Filters>
level指定日志等级的阈值。
onMatch 定义当日志事件等级等于或高于 level 属性指定的等级时的行为。
ACCEPT:接受日志事件,让它通过过滤器。DENY:拒绝日志事件,不让它通过过滤器。NEUTRAL:对日志事件不做决定,继续应用其他的过滤规则。
onMismatch 定义当日志事件等级低于 level 属性指定的等级时的行为。
ACCEPT:接受日志事件,让它通过过滤器。DENY:拒绝日志事件,不让它通过过滤器。NEUTRAL:对日志事件不做决定,继续应用其他的过滤规则。
if 日志的级别 >= level配置的级别
ACCEPT 接受日志
DENY 拒绝日志
NEUTRAL 对日志事件不做决定,继续应用其他的过滤规则
if 日志的级别 < level配置的级别
ACCEPT 接受日志
DENY 拒绝日志
NEUTRAL 对日志事件不做决定,继续应用其他的过滤规则
还是没有搞清楚 onMismatch="NEUTRAL" 的作用是怎样的
使用ChatGPT提供的demo,没有验证
info级别 记录到info.log error级别 记录到error.log info级别和error级别 记录到dev.log
<Configuration><Appenders><!-- Info 级别日志的 Appender --><File name="InfoAppender" fileName="logs/info.log"><ThresholdFilter level="INFO" onMatch="ACCEPT" onMismatch="DENY"/><PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss} [%t] %-5level %logger{36} - %msg%n"/></File><!-- Error 级别日志的 Appender --><File name="ErrorAppender" fileName="logs/error.log"><ThresholdFilter level="ERROR" onMatch="ACCEPT" onMismatch="DENY"/><PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss} [%t] %-5level %logger{36} - %msg%n"/></File><!-- 同时记录 Info 和 Error 级别日志的 Appender --><File name="DevAppender" fileName="logs/dev.log"><Filters><ThresholdFilter level="INFO" onMatch="ACCEPT" onMismatch="DENY"/><ThresholdFilter level="ERROR" onMatch="ACCEPT" onMismatch="NEUTRAL"/></Filters><PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss} [%t] %-5level %logger{36} - %msg%n"/></File></Appenders><Loggers><Root level="DEBUG"><AppenderRef ref="InfoAppender"/><AppenderRef ref="ErrorAppender"/><AppenderRef ref="DevAppender"/></Root></Loggers></Configuration>
只是引入web框架
只是引入web框架
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
@GetMapping("/test")
public String test(){
// 格式化当前日期时间
String formattedDateTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
log.debug("当前日期时间:{}", formattedDateTime);
log.info("当前日期时间:{}", formattedDateTime);
log.error("当前日期时间:{}", formattedDateTime);
log.warn("当前日期时间:{}", formattedDateTime);
log.trace("当前日期时间:{}", formattedDateTime);
return formattedDateTime;
}
# 控制台打印
INFO 64710 --- [nio-8080-exec-1] c.e.s.controller.IndexController : 当前日期时间:2024-03-03 21:37:40
ERROR 64710 --- [nio-8080-exec-1] c.e.s.controller.IndexController : 当前日期时间:2024-03-03 21:37:40
WARN 64710 --- [nio-8080-exec-1] c.e.s.controller.IndexController : 当前日期时间:2024-03-03 21:37:40
# 为什么控制台没有打印 trace和debug日志