初始化git 基础代码

This commit is contained in:
bao
2026-01-14 08:37:21 +08:00
commit e52b622a52
295 changed files with 18426 additions and 0 deletions

View File

@@ -0,0 +1,109 @@
# Redis连接问题修复指南
## 问题描述
应用无法连接到Redis服务器 `192.168.200.130:6379`,导致任务调度服务异常。
## 解决步骤
### 1. 检查Redis服务状态
#### 在Linux服务器上检查
```bash
# 检查Redis进程
ps aux | grep redis
# 检查Redis端口是否监听
netstat -tlnp | grep 6379
# 或
ss -tlnp | grep 6379
```
#### 在Windows服务器上检查
```powershell
# 检查Redis进程
Get-Process | Where-Object {$_.ProcessName -like "*redis*"}
# 检查端口
netstat -ano | findstr 6379
```
### 2. 启动Redis服务
#### Linux:
```bash
# 如果使用systemd
sudo systemctl start redis
sudo systemctl enable redis # 设置开机自启
# 或直接启动
redis-server /path/to/redis.conf
```
#### Windows:
```powershell
# 如果Redis作为服务安装
net start redis
# 或直接运行
redis-server.exe
```
### 3. 验证Redis连接
```bash
# 测试连接
redis-cli -h 192.168.200.130 -p 6379 ping
# 应该返回: PONG
```
### 4. 检查防火墙
确保防火墙允许6379端口
```bash
# Linux
sudo firewall-cmd --add-port=6379/tcp --permanent
sudo firewall-cmd --reload
# 或使用iptables
sudo iptables -A INPUT -p tcp --dport 6379 -j ACCEPT
```
### 5. 检查Redis配置
确保Redis配置文件 `redis.conf` 中:
```conf
# 允许外部连接(如果需要)
bind 0.0.0.0
# 或
bind 192.168.200.130
# 保护模式(如果只允许本地,需要关闭)
protected-mode no
```
### 6. 检查网络连通性
从应用服务器测试:
```bash
# 测试网络连通性
ping 192.168.200.130
# 测试端口连通性
telnet 192.168.200.130 6379
# 或
nc -zv 192.168.200.130 6379
```
## 临时解决方案
如果Redis暂时无法启动可以考虑
1. 使用本地Redis修改配置文件中的Redis地址
2. 使用Docker运行Redis
```bash
docker run -d -p 6379:6379 redis:latest
```
## 验证修复
修复后重启应用服务观察日志中不再出现Redis连接错误。

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<module version="4">
<component name="FacetManager">
<facet type="Spring" name="Spring">
<configuration />
</facet>
</component>
</module>

View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>heima-leadnews-service</artifactId>
<groupId>com.heima</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>heima-leadnews-schedule</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
</project>

View File

@@ -0,0 +1,31 @@
package com.heima.schedule;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.scheduling.annotation.EnableScheduling;
@SpringBootApplication
@MapperScan("com.heima.schedule.mapper")
@EnableScheduling // 开启定时任务
public class ScheduleApplication {
public static void main(String[] args) {
SpringApplication.run(ScheduleApplication.class,args);
}
/**
* mybatis-plus乐观锁支持
* @return
*/
@Bean
public MybatisPlusInterceptor optimisticLockerInterceptor(){
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return interceptor;
}
}

View File

@@ -0,0 +1,48 @@
package com.heima.schedule.feing;
import com.heima.apis.schedule.IScheduleClient;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.schedule.dtos.Task;
import com.heima.schedule.service.TaskService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@RestController
public class ScheduleClient implements IScheduleClient {
@Autowired
private TaskService taskService;
/**
* 添加延迟任务
*
* @param task
* @return
*/
@PostMapping("/api/v1/task/add")
public ResponseResult addTask(@RequestBody Task task) {
return ResponseResult.okResult(taskService.addTask(task));
}
/**
* 取消任务
*
* @param taskId
* @return
*/
@GetMapping("/api/v1/task/{taskId}")
public ResponseResult cancelTask(@PathVariable("taskId") long taskId){
return ResponseResult.okResult(taskService.cancelTask(taskId));
}
/**
* 按照类型和优先级拉取任务
*
* @param type
* @param priority
* @return
*/
@GetMapping("/api/v1/task/{type}/{priority}")
public ResponseResult poll(@PathVariable("type") int type,@PathVariable("priority") int priority) {
return ResponseResult.okResult(taskService.poll(type,priority));
}
}

View File

@@ -0,0 +1,17 @@
package com.heima.schedule.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.heima.model.schedule.pojos.TaskinfoLogs;
import org.apache.ibatis.annotations.Mapper;
/**
* <p>
* Mapper 接口
* </p>
*
* @author itheima
*/
@Mapper
public interface TaskinfoLogsMapper extends BaseMapper<TaskinfoLogs> {
}

View File

@@ -0,0 +1,23 @@
package com.heima.schedule.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.heima.model.schedule.pojos.Taskinfo;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import java.util.Date;
import java.util.List;
/**
* <p>
* Mapper 接口
* </p>
*
* @author itheima
*/
@Mapper
public interface TaskinfoMapper extends BaseMapper<Taskinfo> {
public List<Taskinfo> queryFutureTime(@Param("taskType")int type, @Param("priority")int priority, @Param("future")Date future);
}

View File

@@ -0,0 +1,29 @@
package com.heima.schedule.service;
import com.heima.model.schedule.dtos.Task;
public interface TaskService {
/**
* 添加任务
* @param task
* @return
*/
public long addTask(Task task);
/**
* 取消任务
* @param taskId
* @return
*/
public boolean cancelTask(long taskId);
/**
* 按照类型和优先级获取任务
* @param type
* @param priority
* @return
*/
public Task poll(int type, int priority);
}

View File

@@ -0,0 +1,261 @@
package com.heima.schedule.service.impl;
import com.alibaba.fastjson.JSON;
import com.heima.common.constants.ScheduleConstants;
import com.heima.common.redis.CacheService;
import com.heima.model.schedule.dtos.Task;
import com.heima.model.schedule.pojos.Taskinfo;
import com.heima.model.schedule.pojos.TaskinfoLogs;
import com.heima.schedule.mapper.TaskinfoLogsMapper;
import com.heima.schedule.mapper.TaskinfoMapper;
import com.heima.schedule.service.TaskService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Calendar;
import java.util.Date;
import java.util.Set;
@Service
@Transactional
@Slf4j
public class TaskServiceImpl implements TaskService {
/**
* 添加延迟任务
*
* @param task
* @return
*/
@Override
public long addTask(Task task) {
//1.添加任务到数据库中
boolean success = addTaskToDb(task);
if (success) {
//2.添加任务到redis
addTaskToCache(task);
}
return task.getTaskId();
}
@Autowired
private CacheService cacheService;
/**
* 把任务添加到redis中
*
* @param task
*/
private void addTaskToCache(Task task) {
String key = task.getTaskType() + "_" + task.getPriority();
//获取5分钟之后的时间 毫秒值
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.MINUTE, 5);
long nextScheduleTime = calendar.getTimeInMillis();
//2.1 如果任务的执行时间小于等于当前时间存入list
if (task.getExecuteTime() <= System.currentTimeMillis()) {
cacheService.lLeftPush(ScheduleConstants.TOPIC + key, JSON.toJSONString(task));
} else if (task.getExecuteTime() <= nextScheduleTime) {
//2.2 如果任务的执行时间大于当前时间 && 小于等于预设时间未来5分钟 存入zset中
cacheService.zAdd(ScheduleConstants.FUTURE + key, JSON.toJSONString(task), task.getExecuteTime());
}
}
@Autowired
private TaskinfoMapper taskinfoMapper;
@Autowired
private TaskinfoLogsMapper taskinfoLogsMapper;
/**
* 添加任务到数据库中
*
* @param task
* @return
*/
private boolean addTaskToDb(Task task) {
boolean flag = false;
try {
//保存任务表
Taskinfo taskinfo = new Taskinfo();
BeanUtils.copyProperties(task, taskinfo);
taskinfo.setExecuteTime(new Date(task.getExecuteTime()));
taskinfoMapper.insert(taskinfo);
//设置taskID
task.setTaskId(taskinfo.getTaskId());
//保存任务日志数据
TaskinfoLogs taskinfoLogs = new TaskinfoLogs();
BeanUtils.copyProperties(taskinfo, taskinfoLogs);
taskinfoLogs.setVersion(1);
taskinfoLogs.setStatus(ScheduleConstants.SCHEDULED);
taskinfoLogsMapper.insert(taskinfoLogs);
flag = true;
} catch (Exception e) {
e.printStackTrace();
}
return flag;
}
/**
* 取消任务
*
* @param taskId
* @return
*/
@Override
public boolean cancelTask(long taskId) {
boolean flag = false;
//删除任务,更新任务日志
Task task = updateDb(taskId, ScheduleConstants.CANCELLED);
//删除redis的数据
if (task != null) {
removeTaskFromCache(task);
flag = true;
}
return flag;
}
/**
* 删除redis中的数据
*
* @param task
*/
private void removeTaskFromCache(Task task) {
String key = task.getTaskType() + "_" + task.getPriority();
if (task.getExecuteTime() <= System.currentTimeMillis()) {
cacheService.lRemove(ScheduleConstants.TOPIC + key, 0, JSON.toJSONString(task));
} else {
cacheService.zRemove(ScheduleConstants.FUTURE + key, JSON.toJSONString(task));
}
}
/**
* 删除任务,更新任务日志
*
* @param taskId
* @param status
* @return
*/
private Task updateDb(long taskId, int status) {
Task task = null;
try {
//删除任务
taskinfoMapper.deleteById(taskId);
//更新任务日志
TaskinfoLogs taskinfoLogs = taskinfoLogsMapper.selectById(taskId);
taskinfoLogs.setStatus(status);
taskinfoLogsMapper.updateById(taskinfoLogs);
task = new Task();
BeanUtils.copyProperties(taskinfoLogs, task);
task.setExecuteTime(taskinfoLogs.getExecuteTime().getTime());
} catch (Exception e) {
log.error("task cancel exception taskId={}", taskId);
}
return task;
}
/**
* 按照类型和优先级拉取任务
*
* @param type
* @param priority
* @return
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Task poll(int type, int priority) {
Task task = null;
try {
String key = type + "_" + priority;
//从redis中拉取数据 pop
String task_json = cacheService.lRightPop(ScheduleConstants.TOPIC + key);
if (StringUtils.isNotBlank(task_json)) {
task = JSON.parseObject(task_json, Task.class);
//修改数据库信息
updateDb(task.getTaskId(), ScheduleConstants.EXECUTED);
}
} catch (org.springframework.data.redis.RedisConnectionFailureException e) {
// Redis连接失败记录日志并快速返回避免事务长时间等待
log.error("Redis connection failed in poll method, type={}, priority={}: {}",
type, priority, e.getMessage());
// 直接返回null避免事务长时间等待导致连接池耗尽
return null;
} catch (Exception e) {
log.error("poll task exception, type={}, priority={}", type, priority, e);
// 对于其他异常也直接返回null避免事务问题
return null;
}
return task;
}
/**
* 未来数据定时刷新
*/
@Scheduled(cron = "0 */1 * * * ?")
public void refresh() {
String token = cacheService.tryLock("FUTRUE_TASK_SYNC", 1000 * 30);
if(StringUtils.isNotBlank(token)){
log.info("未来数据定时刷新---定时任务");
//获取所有未来数据的集合key
Set<String> futureKeys = cacheService.scan(ScheduleConstants.FUTURE + "*");
for (String futureKey : futureKeys) {//future_100_50
//获取当前数据的key topic
String topicKey = ScheduleConstants.TOPIC + futureKey.split(ScheduleConstants.FUTURE)[1];
//按照key和分值查询符合条件的数据
Set<String> tasks = cacheService.zRangeByScore(futureKey, 0, System.currentTimeMillis());
//同步数据
if (!tasks.isEmpty()) {
cacheService.refreshWithPipeline(futureKey, topicKey, tasks);
log.info("成功的将" + futureKey + "刷新到了" + topicKey);
}
}
}
}
}

View File

@@ -0,0 +1,12 @@
server:
port: ${server.port:51701}
spring:
application:
name: leadnews-schedule
cloud:
nacos:
discovery:
server-addr: 192.168.200.130:8848
config:
server-addr: 192.168.200.130:8848
file-extension: yml

View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.heima.schedule.mapper.TaskinfoMapper">
<select id="queryFutureTime" resultType="com.heima.model.schedule.pojos.Taskinfo">
select *
from taskinfo
where task_type = #{taskType}
and priority = #{priority}
and execute_time <![CDATA[<]]> #{future,javaType=java.util.Date}
</select>
</mapper>

View File

@@ -0,0 +1,56 @@
package com.heima.schedule.service.impl;
import com.heima.model.schedule.dtos.Task;
import com.heima.schedule.ScheduleApplication;
import com.heima.schedule.service.TaskService;
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.Date;
@SpringBootTest(classes = ScheduleApplication.class)
@RunWith(SpringRunner.class)
class TaskServiceImplTest {
@Autowired
private TaskService taskService;
/**
* 添加任务
*/
@Test
void addTask() {
Task task = new Task();
task.setTaskType(100);
task.setPriority(50);
task.setParameters("task test".getBytes());
task.setExecuteTime(new Date().getTime());
long taskId = taskService.addTask(task);
System.out.println(taskId);
}
/**
* 取消任务
*/
@Test
public void cancelTask(){
taskService.cancelTask(1976546541397213185L);
}
/**
* 获取任务
* type: 任务类型
* priority: 任务优先级
*/
@Test
public void testPoll(){
Task poll = taskService.poll(100, 50);
System.out.println(poll);
}
}

View File

@@ -0,0 +1,117 @@
package com.heima.schedule.test;
import com.alibaba.fastjson.JSON;
import com.heima.common.redis.CacheService;
import com.heima.model.schedule.dtos.Task;
import com.heima.schedule.ScheduleApplication;
import com.heima.schedule.service.TaskService;
import com.heima.schedule.service.impl.TaskServiceImpl;
import edu.umd.cs.findbugs.annotations.Nullable;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.Date;
import java.util.List;
import java.util.Set;
@SpringBootTest(classes = ScheduleApplication.class)
@RunWith(SpringRunner.class)
public class RedisTest {
@Autowired
private CacheService cacheService;
@Autowired
private TaskServiceImpl taskServiceImpl;
@Test
public void testList(){
// 添加数据
cacheService.lLeftPush("list_001","hello,redis");
// 查询并删除数据
String list001 = cacheService.lRightPop("list_001");
System.out.println(list001);
}
@Test
public void testZset(){
// 添加数据到zset中 分值
cacheService.zAdd("zset_001","hello,redis 001",1000);
cacheService.zAdd("zset_001","hello,redis 002",10000);
cacheService.zAdd("zset_001","hello,redis 003",100000);
cacheService.zAdd("zset_001","hello,redis 004",1000000);
cacheService.zAdd("zset_001","hello,redis 005",10000000);
// 按照分值获取数据
Set<String> zset001 = cacheService.zRangeByScore("zset_001", 0, 100000);
System.out.println(zset001);
}
@Test
public void testKeys(){
Set<String> keys = cacheService.keys("future_*");
System.out.println(keys);
Set<String> scan = cacheService.scan("future_*");
System.out.println(scan);
}
//耗时6264
@Test
public void testPiple1(){
long start =System.currentTimeMillis();
for (int i = 0; i <10000 ; i++) {
Task task = new Task();
task.setTaskType(1001);
task.setPriority(1);
task.setExecuteTime(new Date().getTime());
cacheService.lLeftPush("1001_1", JSON.toJSONString(task));
}
System.out.println("耗时"+(System.currentTimeMillis()- start));
}
@Test
public void testPiple2(){
long start = System.currentTimeMillis();
//使用管道技术
List<Object> objectList = cacheService.getstringRedisTemplate().executePipelined(new RedisCallback<Object>() {
@Nullable
@Override
public Object doInRedis(RedisConnection redisConnection) throws DataAccessException {
for (int i = 0; i <10000 ; i++) {
Task task = new Task();
task.setTaskType(1001);
task.setPriority(1);
task.setExecuteTime(new Date().getTime());
redisConnection.lPush("1001_1".getBytes(), JSON.toJSONString(task).getBytes());
}
return null;
}
});
System.out.println("使用管道技术执行10000次自增操作共耗时:"+(System.currentTimeMillis()-start)+"毫秒");
}
@Autowired
private TaskService taskService;
@Test
public void addTask() {
for (int i = 0; i < 5; i++) {
Task task = new Task();
task.setTaskType(100+i);
task.setPriority(50);
task.setParameters("task text".getBytes());
task.setExecuteTime(new Date().getTime() + 500 * i);
long taskId = taskService.addTask(task);
}
}
}

View File

@@ -0,0 +1,12 @@
server:
port: ${server.port:51701}
spring:
application:
name: leadnews-schedule
cloud:
nacos:
discovery:
server-addr: 192.168.200.130:8848
config:
server-addr: 192.168.200.130:8848
file-extension: yml

View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.heima.schedule.mapper.TaskinfoMapper">
<select id="queryFutureTime" resultType="com.heima.model.schedule.pojos.Taskinfo">
select *
from taskinfo
where task_type = #{taskType}
and priority = #{priority}
and execute_time <![CDATA[<]]> #{future,javaType=java.util.Date}
</select>
</mapper>