自己从头到尾写完了黑马头条项目所有代码,在过程中遇到的所有报错和疑问。其中可能有我理解错误或修改错误的地方,请友善评论。(黑马头条相关思考-CSDN博客--是我在写的过程中产生的一些对于原理和底层的思考)
DAY1
1.依赖版本错误
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
解决:版本改成2.9.7
2.redis
原因:后续需要用到redis但目前没有连接,找不到redis
解决:
解决方案一:
在heima-leadnews-user服务中的bootstrap.yml文件中添加配置忽略,如下:
spring:
#忽略掉配置类
autoconfigure:
exclude: org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration
注意:后期如果heima-leadnews-user服务需要依赖于redis,则需要把上述配置注释或删除
解决方案二:
在heima-leadnews-user服务中的bootstrap.yml文件中添加redis配置(需要安装redis服务)
spring:
redis:
host: localhost
port: 6379
password: leadnews(自己的密码)
3.Mysql 报错
原理:大概版本有问题//CST 2022 WARN:不建议在没有服务器身份验证的情况下建立SSL连接。 根据MySQL 5.5.45+、5.6.26+和5.7.6+的要求,如果没有设置显式选项,默认必须建立SSL连接。 为了符合不使用SSL的现有应用程序,verifyServerCertificate属性被设置为’false’。 您需要通过设置useSSL=false显式禁用SSL,或者设置useSSL=true并为服务器证书验证提供信任存储区。
解决:1.他提供的nacos,需要修改nacos中的密码成自己的
2.如仍报错修改配置在url后添加&useSSL=false
4. SLF4J: Class path contains multiple SLF4J bindings.
原因:大概是.jar包重复引入依赖
解决:未解决
解决过程:猜测没有影响,网上查到原因是依赖重复,在pom文件中可以排除有关重复依赖的,但源代码已写,仍报错
5.swagger和knife4j空显示
报错:ERROR s.d.s.w.p.DocumentationPluginsBootstrapper - Unable to scan documentation context 1.0
ERROR s.d.s.w.p.DocumentationPluginsBootstrapper - Unable to scan documentation context default
大概意思就是找不到指定的扫描包
原因:user-service代码有错,不知道哪错了,大概是配置或注解扫描包错误
解决:从头再写一遍或直接给资料代码拿过来
6. heima-leadnews-common包依赖错误
原因:弃用的配置属性
解决:alt+enter
7.日志文件
原因:没有E盘
解决:修改到自己有的
DAY2
1. FreeMarker文件修改后配置了template_update_delay: 0
仍不自动更新(正确情况:文件改变何以自动更新但改变传入数据得重新启动)
解决:
IDEA 修改Freemarker后,ftl页面不会立即显示修改的内容_freemarker image刷新不出-CSDN博客
2.Maven不能clean说依赖重复(freemarker-demo-1.0v)
解决:1.删除heima-leadnew下的pom文件中的
<module>heima-leadnews-test\freemarker-dome</module>和mioin的
2.在heima-leadnew-test中添加
<modules>
<module>freemarker-demo</module>
<module>minio-demo</module>
</modules>
DAY3
1.sql脚本执行失败
解决:手动创建leadnews_wemedia架构,在这个架构中执行sql脚本
2.对文章进行判空没用
原因:前端错误,在content中即使输入为空但是仍会传回“请在这里输入正文”的内容
3.答疑
List<Map> maps = JSON.parseArray(content, Map.class);
将ISON数组中的每个元素都转化为一个Map,解析完后,将这些map存放到maps中,这个list每一个元素都是一个map
例:[{"type":"text","value":"杨澜回应一秒变脸杨澜回应一秒变脸杨澜回应一秒变脸杨澜回应一秒变脸杨澜回应一秒变脸杨澜回应一秒变脸"},
{"type":"image","value":"http://192.168.200.130/group1/M00/00/00/wKjIgl892wKAZLhtAASZUi49De0836.jpg"},
{"type":"text","value":"杨澜回应一秒变脸杨澜回应一秒变脸杨澜回应一秒变脸杨澜回应一秒变脸杨澜回应一秒变脸杨澜回应一秒变脸杨澜回应一秒变脸杨澜回应一秒变脸"},
{"type":"text","value":"请在这里输入正文"}]把这段话命名为content,那么List<Map> maps = JSON.parseArray(content, Map.class);输出的结果是- 第一个 `Map` 元素包含两个键值对,键 `"type"` 对应的值是 `"text"`,键 `"value"` 对应的值是很长的关于"杨澜回应一秒变脸"的字符串。
- 第二个 `Map` 元素包含两个键值对,键 `"type"` 对应的值是 `"image"`,键 `"value"` 对应的值是图片的URL(`http://192.168.200.130/group1/M00/00/00/wKjIgl892wKAZLhtAASZUi49De0836.jpg`)。
- 第三个 `Map` 元素包含两个键值对,键 `"type"` 对应的值是 `"text"`,键 `"value"` 对应的值是很长的关于"杨澜回应一秒变脸"的字符串。
- 第四个 `Map` 元素包含两个键值对,键 `"type"` 对应的值是 `"text"`,键 `"value"` 对应的值是 "请在这里输入正文"。
4.在判断if(dbMaterials.size() != materials.size())时,如果文章内容有两张一样的图片,那么传来的图片数量多于查到的,所以在遍历maps时,要判断当前的url是否已经存在
for (Map map : maps) {
if(map.get("type").equals("image")) {
String imgUrl = (String) map.get("value");
if (!materials.contains(imgUrl)) {
materials.add(imgUrl);
}
}
}
5.自管理的敏感词过滤时要加上标题,并且每次都需要初始化不合理。
6.在提取图片内容审核时,可以在审核完直接判断
boolean isSensitive = true;
if(!isSensitive) {
return;
}
可以提高效率
DAY4
1.mysql报错com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException: Public Key Retrieval is not allowed
原因:版本问题
解决:在nacos的mysql的url配置后加&allowPublicKeyRetrieval=true
2.上传html文件到minio时,如果为更新不需要先删除已存在的,因为是根据id生成url
DAY5
1.updateDb中的代码必须与讲的一致,在try外面创建task对象且赋值为null,如果在外面直接创建new一个,那么在下面的判断是否为空时会始终为true,在try内部new是为了在删除mysql中数据失败时让redis不删除。
2.从数据库添加新的任务时添加到list的头部cacheService.lLeftPush,拉取数据时是从尾部拉取cacheService.lRightPop,从zset中刷新数据到list中放到尾部stringRedisConnection.rPush,虽然都是现在需要执行的但是顺序不准确
3.删除redis中的数据时比对当前时间和提交时间来判断是FUTURE还是TOPIC
不合理,因为当前时间是变化的,当时存入redis中时用的是当时存入的时间和提交时间来比较从而得到的前缀,现在时间发生了变化不应该使用当前时间和提交时间来比较获得前缀判断在list中还是zset中,从而使得mysql中的数据修改和删除,但是redis中的zset中的数据无法删除。
但是在后面会对zset中的数据每分钟定时扫描,如果提交时间小于当前时间,就会存放到list中,那么如果提交时间是刚扫描完后几秒,那么这个任务还是在zset中,但是时间过去几秒后,这个任务还在zset中,如果在这个时间取消任务就会产生和上面一样的报错。但是这个的应用场景是在拉取任务执行时失败了,并多次重试后才会进行取消,所以拉取都是一定在list中的,就不会发生错误,代码中else的目的就是为了解决任务还在zset中的问题。综上这里写的很对。
4. public List<Object> refreshWithPipeline(String future_key,String topic_key,Collection<String> values){
List<Object> objects = stringRedisTemplate.executePipelined(new RedisCallback<Object>() {
@Nullable
@Override
public Object doInRedis(RedisConnection redisConnection) throws DataAccessException {
StringRedisConnection stringRedisConnection = (StringRedisConnection)redisConnection;
String[] strings = values.toArray(new String[values.size()]);
stringRedisConnection.rPush(topic_key,strings);
stringRedisConnection.zRem(future_key,strings);
return null;
}
});
return objects;
}
以下是对这段代码的解释:
1. 方法定义
`public List<Object> refreshWithPipeline(String future_key, String topic_key, Collection<String> values)`
- 这是一个公共方法,返回值为`List<Object>`类型。它接受三个参数:`future_key`(大概是与未来某个操作或标识相关的键,从命名推测为字符串类型)、`topic_key`(主题相关的键,字符串类型)和`values`(一个字符串集合)。
2. 执行Redis管道操作
`List<Object> objects = stringRedisTemplate.executePipelined(new RedisCallback<Object>() {... })`
- 使用`stringRedisTemplate`来执行一个Redis的管道(pipelined)操作。管道操作允许将多个Redis命令一次性发送到服务器,减少网络开销并提高性能。这里创建了一个匿名的`RedisCallback`内部类实例。
3. 实现`RedisCallback`的`doInRedis`方法
`@Nullable`
`@Override`
`public Object doInRedis(RedisConnection redisConnection) throws DataAccessException {`
- 在这个方法中定义了在Redis连接内要执行的操作。
3.1 转换连接类型并准备数据
`StringRedisConnection stringRedisConnection = (StringRedisConnection)redisConnection;`
`String[] strings = values.toArray(new String[values.size()]);`
- 首先将传入的`RedisConnection`转换为`StringRedisConnection`类型,以方便操作字符串相关的Redis命令。然后将`values`集合转换为字符串数组。
3.2 执行Redis命令
`stringRedisConnection.rPush(topic_key,strings);`
`stringRedisConnection.zRem(future_key,strings);`
- 第一个命令`rPush`是将`strings`中的值逐个追加到名为`topic_key`的Redis列表的末尾。第二个命令`zRem`是从名为`future_key`的有序集合(sorted - set)中删除`strings`中的元素。
4. 返回结果
`return null;`
- 在`doInRedis`方法里返回`null`,这是因为管道操作的特性,实际要获取的结果在管道执行后可以从外部的`executePipelined`方法返回值中得到。
5. 最终返回结果
`return objects;`
- 将`stringRedisTemplate.executePipelined`返回的结果(尽管之前在`doInRedis`方法里返回`null`,但`executePipelined`的外部结果包含有意义的信息)作为该方法的最终结果返回。
5.public String tryLock(String name, long expire) {
name = name + "_lock";
String token = UUID.randomUUID().toString();
RedisConnectionFactory factory = stringRedisTemplate.getConnectionFactory();
RedisConnection conn = factory.getConnection();
try {
//参考redis命令:
//set key value [EX seconds] [PX milliseconds] [NX|XX]
Boolean result = conn.set(
name.getBytes(),
token.getBytes(),
Expiration.from(expire, TimeUnit.MILLISECONDS),
RedisStringCommands.SetOption.SET_IF_ABSENT //NX
);
if (result != null && result)
return token;
} finally {
RedisConnectionUtils.releaseConnection(conn, factory,false);
}
return null;
}
以下是对这段代码的解释:
1. 方法功能
这段Java代码定义了一个名为 `tryLock` 的方法,该方法旨在尝试获取一个基于Redis的分布式锁。如果成功获取锁,它将返回一个代表锁的唯一标识(token),如果获取锁失败,则返回 `null`。
2. 锁名称处理
name = name + "_lock";
这行代码将传入的 `name` 参数后面添加了 `_lock` 后缀。这样做可能是为了构建一个符合特定命名规则的、用于表示锁的键(key)名称。
3. 生成唯一标识token
String token = UUID.randomUUID().toString();
这行代码使用Java的 `UUID`(通用唯一识别码)类来生成一个随机的、全球唯一的字符串标识,这个标识将用于唯一地代表本次获取到的锁。
4. 与Redis建立连接
RedisConnectionFactory factory = stringRedisTemplate.getConnectionFactory();
RedisConnection conn = factory.getConnection();
这两行代码首先获取一个 `RedisConnectionFactory`(这可能是Spring框架集成Redis时提供的用于创建Redis连接的工厂类,尽管代码中没有明确是Spring相关,但很可能基于此框架模式),然后通过这个工厂获取一个 `RedisConnection`,即与Redis服务器建立了连接。
5. 尝试设置键值对(获取锁)
Boolean result = conn.set(
name.getBytes(),
token.getBytes(),
Expiration.from(expire, TimeUnit.MILLISECONDS),
RedisStringCommands.SetOption.SET_IF_ABSENT //NX
);
这部分代码在Redis中尝试执行一个 `SET` 操作:
- 将处理后的 `name`(作为键)对应的字节数组和生成的 `token`(作为值)对应的字节数组设置到Redis中。
- 设置键值对时,指定了过期时间为 `expire`(以毫秒为单位)。
- 使用了 `SET_IF_ABSENT`(相当于Redis命令中的 `NX` 选项),这意味着只有当这个键不存在时才会设置成功。这个操作体现了获取锁的原子性:如果键不存在(即锁未被占用),则当前线程可以获取到锁;如果键已经存在(锁已被占用),则当前线程获取锁失败。
DAY6
1.Collections.singletonList()
快速创建一个只包含一个元素的List
1.简洁明了
singletonList 方法非常简洁明了,可以快速创建一个只包含一个元素的不可修改列表。
2.节省内存空间
由于 singletonList 只包含一个元素,因此在创建大量只包含一个元素的列表时,使用 singletonList 可以节省大量的内存空间。
3.避免 null 值
使用 singletonList 方法可以避免添加 null 元素的问题,因为当参数为 null 时,该方法会抛出 NullPointerException 异常。
4.安全可靠
由于 singletonList 是不可修改的,可以避免在多线程环境下出现不可预期的结果。因此,使用 singletonList 可以使代码更加安全可靠。
2.直接可以上下架是对的,如果下架后不修改文章可以直接上架,如果点了编辑那么就会走发布或修改文章的方法,如果存入草稿不会显示上下架,如果选择发布不会显示上下架。
DAY7
1.问题:监听事件插入es语句时报错{"error":{"root_cause":[{"type":"cluster_block_exception","reason":"index [app_info_article] blocked by: [FORBIDDEN/12/index read-only / allow delete (api)];"}],"type":"cluster_block_exception","reason":"index [app_info_article] blocked by: [FORBIDDEN/12/index read-only / allow delete (api)];"},"status":403}
原因:当Elasticsearch出现read-only/allow delete错误时,可能是由于内存不足或磁盘空间紧张导致。内存不足时,ES会触发保护机制禁止写入(debug运行测试除发送创建index请求外其他代码无异常,参数传输正常,执行try中的语句时下一步会跳转到异常其中参数同上)
解决:centos磁盘不足,扩大内存,视频搜B站,要具体到扩展挂载卷(vmware中直接扩大时如果无法扩大就是没删除快照或者没删干净,没删干净的重新创建一个快照并删除这个就行了)
2.stream中的代码,这段代码的目的是对一段时间内的操作进行集合处理,累计当前同一操作的值并创建新的返回结果,然后发送新的请求
2.报错:catch exception:Illegal/unsupported escape sequence near index 4
原因:使用split 正则时,“\\”报错,Illegal/unsupported escape sequence java 预编译,使用“\\\\”即可解决或直接删除
DAY8
黑马头条Day08-平台管理_黑马头条day8-CSDN博客
1.注意创建的位置
2.问题:没有在yml中配置redis但是启动微服务时报错Redis health check failed
原因:(可能)即使没有在`yml`配置文件中配置`Redis`,如果项目的依赖中引入了与`Redis`相关的组件,例如`Spring Boot`项目中引入了`spring - boot - starter - data - redis`依赖,在启动时可能会默认尝试连接`Redis`服务进行一些初始化操作,如健康检查。
解决:
# 禁止Actuator监控Redis连接
management:
health:
redis:
enabled: false
可以不用管
DAY9
黑马头条Day09-用户行为_黑马头条 day9-CSDN博客
1.问题:kafka无法连接
原因:centos磁盘不足
解决:扩大内存,视频搜B站,要具体到扩展挂载卷(vmware中直接扩大时如果无法扩大就是没删除快照或者没删干净,没删干净的重新创建一个快照并删除这个就行了)
2.stream中的代码,这段代码的目的是对一段时间内的操作进行集合处理,累计当前同一操作的值并创建新的返回结果,然后发送新的请求
DAY10
1.问题:启动mysql时Error response from daemon: mkdir /var/lib/docker/overlay2/91724c1730add730cbde4a759c23d1d2fb117b62f3459f935a84c2ae40ab5bd0/merged: no space left on device
Error: failed to start containers: mysql57
原因:centos磁盘不足
解决:同上
2.测试时catcheTagToRedis报错,如果没有数据可以把时间改为五年前或者把数据库中的时间修改,如果数据过少可以从虚拟机的数据库粘贴过来,需要注意需要粘贴两张表article和config。如果仍然报错说明channelId有的字段为空填上数据即可.
DAY11
1.问题:git用户名密码
解决:进入控制面板》用户账号》凭据管理器》windows凭据》普通凭据,在里面找到git,如果没有就创建Gitee - 基于 Git 的代码托管和研发协作平台,用户名就是网站上的@什么,填的时候不加@
2.问题:fatal: couldn't find remote ref HEAD
原因:没有分支
解决:下载TortoiseGit和视频上的就一样了,如果看不见.git文件打开隐藏就行了
DAY12
1.遇到无法打包可以现在idea的maven中test,如果失败可以根据提示修改,
如果显示va.lang.IllegalStateException: Failed to load ApplicationContext
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'xxlJobConfig': Injection of autowired dependencies failed; nested exception is java.lang.IllegalArgumentException: Could not resolve placeholder 'xxl.job.admin.addresses' in value "${xxl.job.admin.addresses}"
Caused by: java.lang.IllegalArgumentException: Could not resolve placeholder 'xxl.job.admin.addresses' in value "${xxl.job.admin.addresses}"
给article中的test里的两个都删了或者全部注释
2.test中的报错同上注释了就行