java项目
java项目
牛客论坛
·初识SpringBoot
搭建开发环境
Maven

Mvn构建:mvn archetype:generate -DgroupId=com.nowcoder.mavendemo1 -DartifactId=mavendemo1 -DarchetypeArtifactId=maven-archetype-quickstart -DarchetypeVersion=1.4 -DinteractiveMode=false
MVN编译:mvn compile

MVN清理: mvn clean
MVN测试: mvn test pom.xml 将版本从1.7改成1.8+

MVN搜包

IDEA
配置maven

创建maven项目

Spring Initializr


Spring Boot入门示例

Spring入门




Spring容器管理Bean
创建

初始化、销毁 (在容器中默认是单例singleton 多例需改为prototype)


装配第三方Bean
1.主动获取

2.依赖注入

IoC结课 主程序-Controller-Service-Dao

Spring MVC入门

MVC 分层是为了解决表现层的问题。执行流程

DispatcherServlet组成部分

DispatcherServlet执行流程
模板引擎:生成动态HTML

示例演示

请求
get请求的两种方式

post请求的处理方式

响应
响应html数据

响应json数据:异步请求

MyBatis入门

配置mysql
设置ini文件

初始化 lHIai%enl0o;

启动服务--修改密码

建库建表,导入数据

配置WorkBench


了解Mybatis

spring boot配置mysql连接池与mybatis
# DataSourceProperties
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/community?characterEncoding=utf-8&useSSL=false&serverTimezone=Hongkong
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.type=com.zaxxer.hikari.HikariDataSource
spring.datasource.hikari.maximum-pool-size=15
spring.datasource.hikari.minimum-idle=5
spring.datasource.hikari.idle-timeout=30000
# MybatisProperties
mybatis.mapper-locations=classpath:mapper/*.xml
mybatis.type-aliases-package=com.nowcoder.community.entity
mybatis.configuration.useGeneratedKeys=true
mybatis.configuration.mapUnderscoreToCamelCase=true
user-mapper.xml
<?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.nowcoder.community.dao.UserMapper">
<select id="selectById" resultType="User">
select id, username, password, salt, email, type, status, activation_code, header_url, create_time
from user
where id = #{id}
</select>
</mapper>
遇到难以解释的玄学问题,比如明明一切都ok,但是就是报错。可以尝试清除缓存,删掉target文件,重新打开等操作·····
开发社区首页

// @Param 用于给参数取别名,如果方法只有一个参数,并且在<if>里使用,就必须加别名
int selectDiscussPostRows(@Param("userId") int userId);
自动转换转义字符
th:utext
时间格式化
th:text="${#dates.format(map.post.createTime,'yyyy-MM-dd HH:mm:ss')}
问题:没有启动tomcat

原因:tomcat导包问题

分页处理(*)
<nav class="mt-5" th:if="${page.rows>0}">
<ul class="pagination justify-content-center">
<li class="page-item">
<a class="page-link" th:href="@{${page.path}(current=1)}">首页</a>
</li>
<li th:class="|page-item ${page.current==1?'disabled':''}|">
<a class="page-link" th:href="@{${page.path}(current=${page.current-1})}">上一页</a></li>
<li th:class="|page-item ${i==page.current?'active':''}|" th:each="i:${#numbers.sequence(page.from,page.to)}">
<a class="page-link" href="#" th:text="${i}">1</a>
</li>
<li th:class="|page-item ${page.current==page.total?'disabled':''}|">
<a class="page-link" th:href="@{${page.path}(current=${page.current+1})}">下一页</a>
</li>
<li class="page-item">
<a class="page-link" th:href="@{${page.path}(current=${page.total})}">末页</a>
</li>
</ul>
</nav>
(current=1)->/index?current=1
|固定的数据(静态的值) + ${变量}|
项目调试技巧

302重定向---低耦合实现功能跳转

断点调试
服务端断点 : F8 逐行向下执行 F7 进入当前行方法内部 F8 返回 F9跳到下一个断点管理

客户端断点:F10 向下执行一行 F11进入当前行方法内部 F8跳到下一个断点

日志
logback 按照级别写日志,动态启用日志级别(>=)

# logger
logging.level.com.nowcoder.community=warn

版本控制

初始化配置

IDEA管理git
·登录模块开发
发送邮件

注意版本匹配,javax与jakarta
注册功能

thymeleaf什么时候用@,什么时候用$?

会话管理



分布式部署使用session会有什么问题?
粘性session 负载不均衡
同步session 开销与耦合
共享session 单机挂了服务就没了
数据库集群 硬盘访问数据性能慢 --- redis

验证码生成

登录登出

登录信息


账号设置

什么时候用redirect?
以下代码存在什么问题?
问题描述:第一次点跳转页面正常是http://localhost:8080/community/user/setting,但是在此页面上再点就变成http://localhost:8080/community/user/user/setting
页面跳转 绝对路径 相对路径

将其改成一下代码就不会出现页面的错误跳转,url异常
<a class="dropdown-item text-center" th:href="@{/user/setting}">账号设置</a>
登陆状态

·论坛核心开发
过滤敏感词

1.根节点为空,除了根节点每个结点只有一个字符
2.从根节点到某一个叶节点途径的字符连起来是一个单词
3.每个节点的所有子节点包含的子节点不相同
前缀树构造
快慢指针(*)

规避特殊符号
// 判断是否为符号
private boolean IsSymbol(Character c){
// 0x2E80 - 0x9FFF 是东亚文字范围
return !CharUtils.isAsciiAlphanumeric(c) && c < 0x2E80 || c > 0x9FFF;
}
发布帖子

帖子详情
数据访问层,业务层,表现层

错误勘察1
原因分析:MyBatis 无法找到 com.nowcoder.community.dao.DiscussPostMapper.selectDiscussPostById 的绑定语句,导致了 org.apache.ibatis.binding.BindingException 异常。
单元测试不通过,查包发现xml语句写错位置,写进了user里,所以查找不到。
错误勘察2 spring转义之后,无法识别特殊字符,以至于过滤不成功


事务管理

隔离性







幻读一般是可接受的,前后读取列表行数不同对整体业务影响较小
隔离级别的实现机制


传播机制与隔离级别
// REQUIRED : 支持当前事务(外部事物),如果不存在则创建新事务
// REQUIRED NEW : 创建一个新的事务,并暂停外部事务
// NESTED : 如果当前存在外部事务,则嵌套在该事务中执行(独立的提交和回滚),否则和REQUIRED一致
@Transactional(isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRED)
显示评论

添加评论

问题:回帖导致404错误

排查:方法:对比与替换;对比相同功能代码;替换标准代码测试
语法错误:是花括号不是圆括号
查询私信

设计会话id 小的在前大的在后拼接---一个会话
数据的显示与否
<span class="badge badge-danger" th:text="map.unreadCount" th:if="map.unreadCount!=0">3</span>
a标签的书写格式
<a th:href="@{|/letter/detail/${map.conversation.conversationId}|}"></a>
发送私信

统一异常处理

404 500 error包下
统一日志记录





Redis
入门

C:\Users\ljl31>redis-cli
127.0.0.1:6379> select 1
OK
127.0.0.1:6379[1]> flushdb
OK
127.0.0.1:6379[1]> set test:count 1
OK
127.0.0.1:6379[1]> get test:count
"1"
127.0.0.1:6379[1]> incr test:count
(integer) 2
127.0.0.1:6379[1]> decr test:count
(integer) 1
哈希
127.0.0.1:6379[1]> hset test:user id 1
(integer) 1
127.0.0.1:6379[1]> hset test:user username zhangsan
(integer) 1
127.0.0.1:6379[1]> hget test:user id
"1"
127.0.0.1:6379[1]> hget test:user username
"zhangsan"
列表
127.0.0.1:6379[1]> lpush test:ids 101 102 103
(integer) 3
127.0.0.1:6379[1]> llen test:ids
(integer) 3
127.0.0.1:6379[1]> lindex test:ids 0
"103"
127.0.0.1:6379[1]> lindex test:ids 2
"101"
127.0.0.1:6379[1]> lrange test:ids 0 2
1) "103"
2) "102"
3) "101"
127.0.0.1:6379[1]> rpop test:ids
"101"
127.0.0.1:6379[1]> rpop test:ids
"102"
无序集合
127.0.0.1:6379[1]> sadd test:teachers aaa bbb ccc ddd eee
(integer) 5
127.0.0.1:6379[1]> scard test:teachers
(integer) 5
127.0.0.1:6379[1]> spop test:teachers 随机弹 抽奖
"ccc"
127.0.0.1:6379[1]> spop test:teachers
"eee"
127.0.0.1:6379[1]> smembers test:teachers
1) "aaa"
2) "bbb"
3) "ddd"
有序集合
127.0.0.1:6379[1]> zadd test:students 10 aaa 20 bbb 30 ccc 40 ddd 50 eee
(integer) 5
127.0.0.1:6379[1]> zcard test:students
(integer) 5
127.0.0.1:6379[1]> zscore test:students ccc
"30"
127.0.0.1:6379[1]> zrank test:students ccc
(integer) 2
127.0.0.1:6379[1]> zrange test:students 0 2
1) "aaa"
2) "bbb"
3) "ccc"
查询
127.0.0.1:6379[1]> keys *
1) "test:ids"
2) "test:teachers"
3) "test:students"
4) "test:user"
5) "test:count"
127.0.0.1:6379[1]> keys test*
1) "test:ids"
2) "test:teachers"
3) "test:students"
4) "test:user"
5) "test:count"
127.0.0.1:6379[1]> type test:user
hash
127.0.0.1:6379[1]> exists test:user
(integer) 1
127.0.0.1:6379[1]> del test:user
(integer) 1
127.0.0.1:6379[1]> exists test:user
(integer) 0
127.0.0.1:6379[1]> expire test:students 10 生存时间s
(integer) 1
127.0.0.1:6379[1]> keys *
1) "test:ids"
2) "test:teachers"
3) "test:students"
4) "test:count"
127.0.0.1:6379[1]> keys *
1) "test:ids"
2) "test:teachers"
3) "test:count"
整合

点赞

异步请求 @ResponseBody
同时写js
<a href="javascript:;" th:onclick="|like(this,1,${post.id})|"
function like(btn, entityType, entityId){
$.post(
CONTEXT_PATH + "/like",
{"entityType":entityType, "entityId":entityId},
function (data){
data = $.parseJSON(data);
if(data.code == 0){
$(btn).children("i").text(data.likeCount);
$(btn).children("b").text(data.likeStatus==1?'已赞':'赞');
}else {
alert(data.msg);
}
}
);
}
C:\Users\ljl31>redis-cli
127.0.0.1:6379> select 11
OK
127.0.0.1:6379[11]> keys like*
1) "like:entity:1:234"
2) "like:entity:2:90"
3) "like:entity:2:93"
127.0.0.1:6379[11]>
判断用户未登录则显示赞,用户登录则判断是否为已赞
// 点赞状态
likeStatus = hostHolder.getUser() == null ? 0 :
likeService.findEntityLikeStatus(hostHolder.getUser().getId(), ENTITY_TYPE_COMMENT, reply.getId());
replayVo.put("likeStatus", likeStatus);
<b th:text="${rvo.likeStatus==1?'已赞':'赞' }">赞</b>(<i th:text="${rvo.likeCount}">1</i>)
被赞

用户id如果再访问mysql数据库获取,那就失去了redis的速度,所以将用户id直接作为参数传进来

关注取关

事务启动模板
redisTemplate.execute(new SessionCallback() {
@Override
public Object execute(RedisOperations operations) throws DataAccessException {
...
operations.multi();
...
return operations.exec();
}
});
查表
127.0.0.1:6379[11]> keys *
1) "like:entity:1:234"
2) "like:entity:2:152"
3) "like:entity:2:153"
4) "like:user:112"
5) "followee:152:3"
6) "follower:3:111"
7) "like:entity:2:91"
8) "like:user:111"
9) "like:entity:2:43"
127.0.0.1:6379[11]> zrange followee:152:3 0 -1
1) "111"
关注取关按钮逻辑
<button type="button" th:text="${hasFollowed?'已关注':'关注TA'}" th:if="${loginUser!=null&&loginUser.id!=user.id}">关注TA</button>
展示列表

优化

cookie用于判断验证码存活时间是否还够,验证码存储在redis中
@Deprecated声明过时
只改状态,不删数据---未来可能会用到,比如年鉴
Kafka
阻塞队列

阻塞,避免系统资源的浪费 其他实现类的细节问题
入门

论文原文2011
JMS 规范 队列 - 点对点 ; 主题 - 发布订阅 ; ActiveMQ
AMQP 队列 信箱 绑定 RabbitMQ
主题 topic
分区 partition 偏移 offset 消息记录 Record K-V
主从副本 data Replication Leader Follower
消息代理 Broker
切换路径
d:
cd D:\software\kafka_2.13-3.5.1
启动zookeeper
D:\software\kafka_2.12-2.2.0>bin\windows\zookeeper-server-start.bat config\zookeeper.properties
启动kafka
D:\software\kafka_2.12-2.2.0>bin\windows\kafka-server-start.bat config\server.properties
问题:kafka启动失败
WARN Session 0x0 for server localhost/<unresolved>:2181, unexpected error, closing socket connection and attempting reconnect (org.apache.zookeeper.ClientCnxn)
java.nio.channels.UnresolvedAddressException
at java.base/sun.nio.ch.Net.checkAddress(Net.java:137)
at java.base/sun.nio.ch.Net.checkAddress(Net.java:145)
at java.base/sun.nio.ch.SocketChannelImpl.checkRemote(SocketChannelImpl.java:842)
at java.base/sun.nio.ch.SocketChannelImpl.connect(SocketChannelImpl.java:865)
at org.apache.zookeeper.ClientCnxnSocketNIO.registerAndConnect(ClientCnxnSocketNIO.java:277)
at org.apache.zookeeper.ClientCnxnSocketNIO.connect(ClientCnxnSocketNIO.java:287)
at org.apache.zookeeper.ClientCnxn$SendThread.startConnect(ClientCnxn.java:1021)
at org.apache.zookeeper.ClientCnxn$SendThread.run(ClientCnxn.java:1064)
初步判定为kafka与java版本不匹配

更换为3.9版本 scala版本-kafka版本
依旧不行:zookeeper都无法启动了
PS D:\software\kafka_2.13-3.9.0\kafka_2.13-3.9.0> .\bin\windows\zookeeper-server-start.bat .\config\zookeeper.properties
输入行太长。
命令语法不正确。
将文件移动位置后,输入相同命令,结果为:
D:\software\kafka_2.13-3.9.0>bin\windows\zookeeper-server-start.bat config\zookeeper.properties
命令语法不正确。
因为报错信息有路径问题,尝试将路径改成双斜杠:依旧报同样的错
Kafka安装配置及Java中的使用_kafka java-CSDN博客
暂时找不到解决方案???
去掉前面的注释---依旧是同样的报错

解决:版本问题,换成3.5版本就ok了
PS D:\software\kafka_2.13-3.5.1\bin\windows> .\kafka-topics.bat --create --bootstrap-server localhost:9092 --replication-factor 1 --partitions 1 --topic test
Created topic test.
PS D:\software\kafka_2.13-3.5.1\bin\windows> .\kafka-acls.bat --list --bootstrap-server localhost:9092
生产者&消费者
PS D:\software\kafka_2.13-3.5.1\bin\windows> .\kafka-console-producer.bat --broker-list localhost:9092 --topic test
>hello
>world
PS D:\software\kafka_2.13-3.5.1\bin\windows> .\kafka-console-consumer.bat --bootstrap-server localhost:9092 --topic test --from-beginning
hello
world
如果遇到如下错误
[2025-02-03 17:48:35,758] INFO Cluster ID = b65_iYBuTnq15IgP7TDBWg (kafka.server.KafkaServer)
[2025-02-03 17:48:35,765] ERROR Fatal error during KafkaServer startup. Prepare to shutdown (kafka.server.KafkaServer)
kafka.common.InconsistentClusterIdException: The Cluster ID b65_iYBuTnq15IgP7TDBWg doesn't match stored clusterId Some(4oY_DfVZRb2Nv35v-L1lNg) in meta.properties. The broker is trying to join the wrong cluster. Configured zookeeper.connect may be wrong.
at kafka.server.KafkaServer.startup(KafkaServer.scala:242)
at kafka.Kafka$.main(Kafka.scala:113)
at kafka.Kafka.main(Kafka.scala)
[2025-02-03 17:48:35,767] INFO shutting down (kafka.server.KafkaServer)
[2025-02-03 17:48:35,770] INFO [ZooKeeperClient Kafka server] Closing. (kafka.zookeeper.ZooKeeperClient)
[2025-02-03 17:48:35,894] INFO Session: 0x10001ddf9190000 closed (org.apache.zookeeper.ZooKeeper)
[2025-02-03 17:48:35,894] INFO EventThread shut down for session: 0x10001ddf9190000 (org.apache.zookeeper.ClientCnxn)
[2025-02-03 17:48:35,898] INFO [ZooKeeperClient Kafka server] Closed. (kafka.zookeeper.ZooKeeperClient)
[2025-02-03 17:48:35,903] INFO App info kafka.server for 1 unregistered (org.apache.kafka.common.utils.AppInfoParser)
[2025-02-03 17:48:35,904] INFO shut down completed (kafka.server.KafkaServer)
[2025-02-03 17:48:35,904] ERROR Exiting Kafka due to fatal exception during startup. (kafka.Kafka$)
kafka.common.InconsistentClusterIdException: The Cluster ID b65_iYBuTnq15IgP7TDBWg doesn't match stored clusterId Some(4oY_DfVZRb2Nv35v-L1lNg) in meta.properties. The broker is trying to join the wrong cluster. Configured zookeeper.connect may be wrong.
at kafka.server.KafkaServer.startup(KafkaServer.scala:242)
at kafka.Kafka$.main(Kafka.scala:113)
at kafka.Kafka.main(Kafka.scala)
[2025-02-03 17:48:35,906] INFO shutting down (kafka.server.KafkaServer)
解决方案
1. 检查和修正 meta.properties
直接编辑 Kafka 的 meta.properties 文件,将其中的 cluster.id 修改为日志中显示的正确的集群 ID。
meta.properties 文件通常位于 Kafka 配置的 log 目录下。
2. 验证 ZooKeeper 配置
确保 Kafka 配置文件 server.properties 中的 zookeeper.connect 参数正确指向 ZooKeeper 的地址和端口。
3. 清理数据目录
如果你确定没有重要的数据,可以尝试删除 Kafka 配置的 log 目录,或者直接删除 log 目录下的 meta.properties 文件,然后重新启动 Kafka 服务器。请注意,这将清除所有 Kafka 服务器上存储的数据。
整合

必须要将kafka服务在cmd中开启才能正常访问
发送通知

显示通知

为了不至于因为没有点赞评论关注数据而导致的页面报错,if-else里加入一个null值
if(message != null){
messageVO.put("message", message);
String content = HtmlUtils.htmlUnescape(message.getContent());
Map<String, Object> data = JSONObject.parseObject(content, HashMap.class);
messageVO.put("user", userService.findUserById((Integer) data.get("userId")));
messageVO.put("entityType", data.get("entityType"));
messageVO.put("entityId", data.get("entityId"));
messageVO.put("postId", data.get("postId"));
int count = messageService.findNoticeCount(user.getId(), TOPIC_LIKE);
messageVO.put("count", count);
int unread = messageService.findNoticeUnreadCount(user.getId(), TOPIC_LIKE);
messageVO.put("unread", unread);
}else {
messageVO.put("message", null);
}
未读消息 -- 拦截器
Elasticsearch
入门

类型逐渐被废弃,索引-表,文档-行,字段-列
分片-对索引进一步划分,将一个索引拆分成多个分片
副本-对分片的备份
es配置

中文分词插件 es-analusis-ik 要放在固定位置

模拟web postman
bat启动闪退,控制台排查是版本问题
D:\software\elasticsearch-6.4.3\bin>elasticsearch.bat
Unrecognized VM option 'UseConcMarkSweepGC'
Error: Could not create the Java Virtual Machine.
Error: A fatal exception has occurred. Program will exit.
问询kimi得到的解决方案:
解决方案 2:修改 jvm.options 文件
如果你无法切换到 Java 8,可以修改 Elasticsearch 的 jvm.options 文件,删除或注释掉不兼容的垃圾回收器选项。具体步骤如下:
打开 jvm.options 文件(位于 Elasticsearch 安装目录下的 config 文件夹中)。
找到以下行并删除或注释掉:
-XX:+UseConcMarkSweepGC
-XX:CMSInitiatingOccupancyFraction=75
-XX:+UseCMSInitiatingOccupancyOnly
添加以下行以使用 G1 垃圾回收器(适用于较新的 Java 版本):
-XX:+UseG1GC
保存文件并重新启动 Elasticsearch。
修改完后有了新的报错:
D:\software\elasticsearch-6.4.3\bin>elasticsearch.bat
Exception in thread "main" java.lang.UnsupportedOperationException: The Security Manager is deprecated and will be removed in a future release
at java.base/java.lang.System.setSecurityManager(System.java:429)
at org.elasticsearch.bootstrap.Elasticsearch.main(Elasticsearch.java:78)
kimi解惑
问题的根本原因是 Java 21 中已经废弃了 SecurityManager,而 Elasticsearch 6.4.3 仍然在尝试使用它。这是一个已知的兼容性问题.
解决方案:
1.设置 ES_JAVA_HOME 环境变量
如果你已经安装了 Java 8,但系统中还存在其他 Java 版本,可以通过设置 ES_JAVA_HOME 环境变量来指定 Elasticsearch 使用 Java 8。具体步骤如下:
找到 Java 8 的安装路径,例如 C:\Program Files\Java\jdk1.8.0_XXX。
设置环境变量:
bash
set ES_JAVA_HOME=C:\Program Files\Java\jdk1.8.0_XXX
重新启动 Elasticsearch。
2.升级 Elasticsearch
如果你有条件,可以考虑升级到更高版本的 Elasticsearch,例如 Elasticsearch 7.x 或更高版本。这些版本对较新的 Java 版本有更好的支持。
决定,切换es版本至8.17.1--成功运行
D:\software\elasticsearch-8.17.1\bin>elasticsearch.bat
CompileCommand: dontinline java/lang/invoke/MethodHandle.setAsTypeCache bool dontinline = true
CompileCommand: dontinline java/lang/invoke/MethodHandle.asTypeUncached bool dontinline = true
[2025-02-03T10:26:51,240][INFO ][o.e.n.NativeAccess ] [MUXLZ] Using [jdk] native provider and native methods for [Windows]
使用 curl 或其他工具访问 Elasticsearch API
curl -X GET "localhost:9200/_cat/health?v"
报错:
Invoke-WebRequest : 找不到与参数名称“X”匹配的参数。
所在位置 行:1 字符: 6
+ curl -X GET "localhost:9200/_cat/health?v"
+ ~~
+ CategoryInfo : InvalidArgument: (:) [Invoke-WebRequest],ParameterBindingException
+ FullyQualifiedErrorId : NamedParameterNotFound,Microsoft.PowerShell.Commands.InvokeWebRequestCommand
kimi解惑
在 PowerShell 中运行 curl 命令时出现错误的原因是 PowerShell 中的 curl 是一个别名,指向的是 Invoke-WebRequest 命令,而不是我们通常使用的 curl.exe 工具。Invoke-WebRequest 的语法与 curl 不兼容,因此会导致参数错误。
解决方法
你可以通过以下方法解决这个问题:
方法 1:使用 curl.exe
直接调用 curl.exe,而不是 curl。这样可以确保使用的是标准的 curl 工具,而不是 PowerShell 的 Invoke-WebRequest。例如:
powershell
curl.exe -X GET "http://localhost:9200/_cat/health?v"
cmd与powershell有很多地方命令不兼容
客户端指令返回为空报错
(base) C:\Users\ljl31>curl -X GET "localhost:9200/_cat/health?v"
curl: (52) Empty reply from server
(base) C:\Users\ljl31>curl -X GET "localhost:9200/_cat/health?v"
curl: (7) Failed to connect to localhost port 9200 after 2227 ms: Couldn't connect to server
(base) C:\Users\ljl31>curl -X GET "localhost:9200/_cat/health?v"
curl: (52) Empty reply from server
(base) C:\Users\ljl31>curl -X GET "localhost:9200/_cat/notes?v"
curl: (52) Empty reply from server
(base) C:\Users\ljl31>curl -X GET "localhost:9200/_cat/indices?v"
curl: (52) Empty reply from server
(base) C:\Users\ljl31>curl -X PUT "localhost:9200/test"
curl: (52) Empty reply from server
服务器警告信息分析
警告信息:received plaintext http traffic on an https channel
日志中多次出现以下警告:
[2025-02-03T10:45:16,760][WARN ][o.e.h.n.Netty4HttpServerTransport] [MUXLZ] received plaintext http traffic on an https channel, closing connection Netty4HttpChannel{localAddress=/127.0.0.1:9200, remoteAddress=/127.0.0.1:56547}
这个警告表明 Elasticsearch 配置了 HTTPS,但客户端尝试通过 HTTP 连接,导致连接被关闭
解决方法1:
检查配置文件:确保 elasticsearch.yml 中的 xpack.security.http.ssl.enabled 设置为 true,并且正确配置了 SSL 证书路径。
yaml
复制
xpack.security.http.ssl:
enabled: true
keystore.path: certs/http.p12
解决方法2:让服务器不用https
elasticsearch.yml
# Enable encryption for HTTP API client connections, such as Kibana, Logstash, and Agents
xpack.security.http.ssl:
enabled: false
keystore.path: certs/http.p12
解决方案2修改重启后,客户端回复消息如下:
(base) C:\Users\ljl31>curl -X GET "localhost:9200/_cat/health?v"
{"error":{"root_cause":[{"type":"security_exception","reason":"missing authentication credentials for REST request [/_cat/health?v]","header":{"WWW-Authenticate":["Basic realm=\"security\", charset=\"UTF-8\"","ApiKey"]}}],"type":"security_exception","reason":"missing authentication credentials for REST request [/_cat/health?v]","header":{"WWW-Authenticate":["Basic realm=\"security\", charset=\"UTF-8\"","ApiKey"]}},"status":401}
关闭 Elasticsearch 的安全认证,直接访问而无需用户名和密码:
elasticsearch.yml
# Enable security features
xpack.security.enabled: false
xpack.security.enrollment.enabled: true
如此客户端再次输入指令后返回值如下
(base) C:\Users\ljl31>curl -X GET "localhost:9200/_cat/health?v"
epoch timestamp cluster status node.total node.data shards pri relo init unassign unassign.pri pending_tasks max_task_wait_time active_shards_percent
1738551517 02:58:37 nowcoder red 1 1 0 0 0 0 0 0 2 1.1s NaN%
集群状态为 RED 表示集群中有未分配的分片(unassigned shards),这通常是由于以下原因之一:
索引未正确创建:集群中没有索引,或者索引的分片未正确分配。
节点问题:集群中只有一个节点,且可能存在某些配置问题导致分片无法分配。
(base) C:\Users\ljl31>curl -X GET "localhost:9200/_cat/indices?v"
health status index uuid pri rep docs.count docs.deleted store.size pri.store.size dataset.size
这个命令返回了一个空的结果,表明集群中没有索引。
(base) C:\Users\ljl31>curl -X PUT "localhost:9200/test"
{"acknowledged":true,"shards_acknowledged":true,"index":"test"}
这个命令成功创建了一个名为 test 的索引。
(base) C:\Users\ljl31>curl -X DELETE "localhost:9200/test"
{"acknowledged":true}
(base) C:\Users\ljl31>curl -X GET "http://localhost:9200/_cat/indices?v"
health status index uuid pri rep docs.count docs.deleted store.size pri.store.size dataset.size
查看集群状态的详细信息
(base) C:\Users\ljl31>curl -X GET "http://localhost:9200/_cluster/health?pretty"
{
"cluster_name" : "nowcoder",
"status" : "yellow",
"timed_out" : false,
"number_of_nodes" : 1,
"number_of_data_nodes" : 1,
"active_primary_shards" : 4,
"active_shards" : 4,
"relocating_shards" : 0,
"initializing_shards" : 0,
"unassigned_shards" : 1,
"unassigned_primary_shards" : 0,
"delayed_unassigned_shards" : 0,
"number_of_pending_tasks" : 0,
"number_of_in_flight_fetch" : 0,
"task_max_waiting_in_queue_millis" : 0,
"active_shards_percent_as_number" : 80.0
}
postman进行操作

索引不存在则自动创建 增加与查询 put 与 get


修改 (增加一条 先删除再增加) 删除

搜索 _search 自动分词
localhost:9200/test/_search?q=content:运营实习
复杂搜索

9200 HTTP 9300 TCP
整合

redis与es启动netty冲突 NettyRuntime.java
synchronized void setAvailableProcessors(final int availableProcessors) {
ObjectUtil.checkPositive(availableProcessors, "availableProcessors");
if (this.availableProcessors != 0) {
final String message = String.format(
Locale.ROOT,
"availableProcessors is already set to [%d], rejecting [%d]",
this.availableProcessors,
availableProcessors);
throw new IllegalStateException(message);
}
this.availableProcessors = availableProcessors;
}
idea链接报错,服务端日志分析
[2025-02-04T11:04:57,666][WARN ][o.e.t.TcpTransport ] [MUXLZ] exception caught on transport layer [Netty4TcpChannel{localAddress=/127.0.0.1:9300, remoteAddress=/127.0.0.1:53460, profile=default}], closing connectionjava.lang.IllegalStateException: Received message from unsupported version: [5060099] allowed versions are: [7170099, 6080099]
at org.elasticsearch.server@8.17.1/org.elasticsearch.transport.InboundDecoder.checkHandshakeVersionCompatibility(InboundDecoder.java:237)
at org.elasticsearch.server@8.17.1/org.elasticsearch.transport.InboundDecoder.readHeader(InboundDecoder.java:211)
at org.elasticsearch.server@8.17.1/org.elasticsearch.transport.InboundDecoder.internalDecode(InboundDecoder.java:81)
at org.elasticsearch.server@8.17.1/org.elasticsearch.transport.InboundDecoder.decode(InboundDecoder.java:59)
at org.elasticsearch.server@8.17.1/org.elasticsearch.transport.InboundPipeline.doHandleBytes(InboundPipeline.java:81)
服务器日志显示:
Received message from unsupported version: [5060099] allowed versions are: [7170099, 6080099]
这表明客户端尝试使用版本 5.6.0(5060099)与服务器通信,但服务器只支持版本 7.17.0(7170099)和 6.8.0(6080099)。
被卡死在这儿了,如果想用现在的es必须将java降为8版本,但是一降版本前面适配的高版本模块就会报错
思考解决方案:开虚拟机装jdk8来适配es,本地局域网访问测试
局域网设置ip为0.0.0.0 允许所有ip访问

经过测试,成功访问
#ElasticsearchProperties
spring.data.elasticsearch.cluster-name=nowcoder
spring.data.elasticsearch.cluster-nodes=192.168.3.45:9300
es可以在搜索到关键词后给其前后加标签
// 标准模板
SearchQuery searchQuery = new NativeSearchQueryBuilder()
.withQuery(QueryBuilders.multiMatchQuery("互联网寒冬","title","content"))
.withSort(SortBuilders.fieldSort("type").order(SortOrder.DESC))
.withSort(SortBuilders.fieldSort("score").order(SortOrder.DESC))
.withSort(SortBuilders.fieldSort("createTime").order(SortOrder.DESC))
.withPageable(PageRequest.of(0,10))
.withHighlightFields(
new HighlightBuilder.Field("title").preTags("<em>").postTags("</em>"),
new HighlightBuilder.Field("content").preTags("<em>").postTags("</em>")
).build();
社区搜索

安全
Spring Security


转发与重定向异同(耦合 - 数据携带)


权限控制

CSRF攻击,盗取cookie凭证,模拟身份利用表单访问服务器
X网站可以窃取到cookie,但它无法猜到你的tocken


但是异步请求需要自己额外处理
<!-- 访问该页面时,在此处生成CSRF令牌-->
<meta name="_csrf" th:content="${_csrf.token}">
<meta name="_scrf_header" th:content="${_csrf.headerName}">
// 发送AJAX请求之前,将CSRF令牌设置到请求的消息头中
var token = $("meta[name='_csrf']").attr("content");
var header = $("meta[name='_csrf_header']").attr("content");
$(document).ajaxSend(function (e, xhr, options){
xhr.setRequestHeader(header, token);
});

未解之谜bug:to_id为0,导致评论了但不提示消息

input遗漏,导致回复输入没有targetid,走默认0 不对 推翻,报错

总归就是 ,拿不到targetId --- 研究数据库表的设计,分库分表,数据漂移

方法运行原理
1. 前端表单数据绑定
在你的前端表单中,每个输入字段的 name 属性对应了 Comment 对象中的一个属性。例如:
<input type="text" class="input-size" name="content" th:placeholder="|回复${rvo.user.username}|"/>
<input type="hidden" name="entityType" value="2">
<input type="hidden" name="entityId" th:value="${cvo.comment.id}">
<input type="hidden" name="targetId" th:value="${rvo.user.id}">
这些字段的 name 属性(如 content、entityType、entityId、targetId)与 Comment 类中的属性名称一致。当表单提交时,Spring MVC 会自动将这些字段的值绑定到 Comment 对象的对应属性中。
2. Spring MVC 的数据绑定机制
Spring MVC 使用 @ModelAttribute 注解(或隐式绑定)将表单数据绑定到后端对象。即使你没有显式地使用 @ModelAttribute 注解,Spring 也会自动处理这种绑定。
在你的方法中:
java
复制
@RequestMapping(path = "/add/{discussPostId}", method = RequestMethod.POST)
public String addComment(@PathVariable("discussPostId") int discussPostId, Comment comment) {
comment.setUserId(hostHolder.getUser().getId());
comment.setStatus(0);
comment.setCreateTime(new Date());
commentService.addComment(comment);
}
Comment comment 参数会自动接收前端表单提交的数据。
Spring 会根据表单字段的 name 属性和 Comment 类的属性名称进行匹配,并将表单字段的值赋值给 Comment 对象的对应属性。
置顶加精删除

注意规范写法

xmlns:sec="http://www.thymeleaf.org/extras/spring-security"
thymeleaf控制不同权限显示不同内容
<button type="button" class="btn btn-danger btn-sm" id="topBtn"
th:disabled="${post.type==1}" sec:authorize="hasAnyAuthority('moderator')">置顶</button>
<button type="button" class="btn btn-danger btn-sm" id="wonderfulBtn"
th:disabled="${post.status==1}" sec:authorize="hasAnyAuthority('moderator')">加精</button>
<button type="button" class="btn btn-danger btn-sm" id="deleteBtn"
th:disabled="${post.status==2}" sec:authorize="hasAnyAuthority('admin')">删除</button>
Redis进阶

bitmap面试题:20亿QQ号去重
数据统计


转发依旧在同一个请求之内
任务调度


// 让该方法在多线程环境下,被异步调用
@Async
public void execute1(){
logger.debug("execute1");
}
@Scheduled(initialDelay = 10000, fixedRate = 1000)
public void execute2(){
logger.debug("execute2");
}
手写线程池 C++ win与linux的不同
MySQL数据库通过cmd命令导入和导出sql文件_mysql导入数据库cmd-CSDN博客

// factoryBean可简化Bean的实例化过程
// 1.通过FactoryBean封装Bean的实例化过程
// 2.将FactoryBean装配到Spring容器中
// 3。将FactoryBean注入给其他的Bean
// 4.该Bean得到的是FactoryBean所管理的对象实例
热帖排行

生成长图

D:\Code\java\mavenTest>wkhtmltopdf https://lsptu25.com/ D:\Code\java\mavenTest\wk-pdfs\1.pdf
Loading pages (1/6)
Counting pages (2/6)
Resolving links (4/6)
Loading headers and footers (5/6)
Printing pages (6/6)
Done
D:\Code\java\mavenTest>wkhtmltoimage https://lsptu25.com/ D:\Code\java\mavenTest\wk-imgs\1.png
Loading page (1/2)
Rendering (2/2)
Done
云服务器

优化性能



压力测试 jmeter
不加缓存

加caffeine缓存

用户日志管理类

项目发布
单元测试

用断言Assert判断测试类方法数据正确性,整个Test类进行执行
spring维护的包,父类会下载最合适的版本,所以不用再自己写
项目监控



项目部署



RPC框架
规则之外
项目企业管理:[统筹]十大管理 [项目前期管理]调研法 [人员管理]心理学(弗洛伊德+拉康) [沟通管理]辩论筑基、有效沟通、历史案例
思维模型(十大方面)
哲学:美与浪漫集(存在主义与虚无主义)
电脑硬件:stm32嵌入式开发 硬件选配与组装,魔改矿卡,超频cpu 内存
面试题
Redis
缓存雪崩,缓存击穿,缓存穿透
缓存雪崩定义:在某个时刻多个缓存同时失效,导致大量请求直接访问数据库,从而增加系统负载,影响整体性能。
举例:在仿牛客论坛中,假设你使用Redis存储了用户验证码和登录凭证。假设所有验证码的缓存都设置为相同的过期时间(比如每隔1小时过期),当这个过期时间来临时,大量验证码缓存同时失效,导致用户在注册时频繁请求数据库,增加了后端系统的负担。
解决方案:分散缓存过期时间
- 可以为验证码的缓存设置不同的过期时间,例如:随机化验证码的过期时间,避免所有缓存失效的时刻重合。比如可以将每个验证码的过期时间设置为 1 小时 ± 随机 10 分钟。
【缓存雪崩是大量缓存同时失效,导致请求打到数据库。比如,如果验证码的过期时间都设置相同,可能在某个时间点同时失效,大量请求涌入数据库查询验证码,但实际上验证码可能已经无效了,这时候数据库压力会增大。解决方法可能是给过期时间加随机值,或者用永不过期但定期更新。例如,用户注册时发送的验证码,通常有效时间短,如果大量用户同时注册,缓存同一时间过期,就会引发雪崩。可以给每个验证码的过期时间设置为5分钟加一个随机秒数,分散失效时间。】
举例D:缓存雪崩场景示例 - 验证码集中失效
场景:用户注册时需要短信验证码,所有验证码都设置了固定5分钟过期时间。当某时刻大量用户同时完成注册操作,5分钟后这批验证码缓存同时失效,后续请求可能集中穿透到数据库。
解决方案:
- 随机过期时间:为每个验证码设置基础过期时间+随机偏移量
- 双缓存策略:主缓存设置不同过期时间,备份缓存永不过期(通过异步线程定期刷新)
缓存击穿定义:当大量请求同时查询缓存中不存在的数据时,所有请求都直接访问数据库,从而增加数据库的负担。
举例:在仿牛客论坛中,假设存储了用户登录凭证和用户信息的缓存,而某些用户信息的缓存失效(例如,缓存的登录凭证过期),此时多个用户同时请求该用户的信息缓存。由于缓存已经失效,所有请求都会查询数据库,这导致数据库负载骤增。
解决方案:使用分布式锁(互斥锁)
- 在多次并发请求查询缓存失败时,可以加一个分布式锁。比如,多个用户请求相同的缓存数据时,只有第一个请求会查询数据库并更新缓存,其他请求则等待该请求完成。可以使用Redisson等工具来实现分布式锁。
【缓存击穿是指热点数据过期后,大量请求直接访问数据库。例如,某个热门用户的用户信息缓存过期,这时候大量请求来获取该用户信息,会导致数据库压力。解决方案可以用互斥锁,比如在查询数据库前加分布式锁,只有一个线程去数据库拉数据,其他等待。例如,当用户A的信息缓存失效时,第一个请求发现缓存不存在,就加锁,查数据库并更新缓存,其他请求等待锁释放后直接从缓存获取。】
举例D:缓存击穿场景示例 - 热门用户信息失效
场景:用户"牛牛"是论坛大V,其用户信息缓存在Redis中。当缓存过期瞬间,大量用户同时访问个人主页,导致数据库瞬间压力激增。
解决方案:
- 分布式锁重建缓存
- 逻辑过期时间:在value中存储实际过期时间,异步检测更新
缓存穿透定义:查询缓存和数据库中都不存在的数据,导致每次请求都直接访问数据库,增加了数据库的负载。常见的情况是攻击者构造恶意请求,访问不存在的key,导致数据库不断被查询。
举例:假设在仿牛客论坛中,攻击者通过构造不存在的验证码或者无效的用户信息请求,导致每次请求都直接访问数据库,最终导致数据库过载。
解决方案:使用布隆过滤器
- 使用布隆过滤器来过滤掉一些不存在的请求。布隆过滤器是一空间效率极高的数据结构,可以用来判断某个元素是否存在。当查询的验证码或用户信息不存在时,布隆过滤器会直接过滤掉这些请求,避免查询数据库。
【缓存穿透是查询不存在的数据,比如频繁请求不存在的用户ID。例如,攻击者随机生成用户ID查询,缓存和数据库都没有,每次都会查库。这时候可以用布隆过滤器,在缓存层先过滤掉无效请求。比如,用户信息缓存前,先检查布隆过滤器是否存在该用户ID,如果不存在就直接返回,避免查库。或者对无效的键缓存空值,但需要注意设置较短的过期时间,防止占用太多内存。】
举例D:缓存穿透场景示例 - 恶意用户ID探测
场景:攻击者使用脚本批量生成不存在的用户ID(如1000000-9999999)请求用户信息接口,导致大量无效查询穿透到数据库。
解决方案:
- 布隆过滤器预检
- 空值缓存:对不存在的用户ID也进行短期缓存
- 其他防御措施
- 登录凭证续期策略:用户每次访问时延长凭证有效期
- 请求参数校验:对用户ID进行格式检查(必须是正整数)
- 接口限流:对/user/{id}接口进行滑动窗口限流
架构优化对比表
| 场景 | 传统方案 | 优化方案 | 效果提升点 |
|---|---|---|---|
| 验证码存储 | 固定5分钟过期 | 基础时间+随机偏移量 | 分散缓存失效时间 |
| 用户信息查询 | 直接查库 | 分布式锁+双检锁机制 | 避免热点数据并发穿透 |
| 登录凭证管理 | 固定1小时有效期 | 访问时自动续期 | 减少雪崩风险,提升用户体验 |
| 用户存在性校验 | 直接查询数据库 | 布隆过滤器预检+空值缓存 | 减少99%无效查询 |
| 接口安全性 | 无校验 | ID格式校验+行为分析 | 拦截非法参数和机器人请求 |
Redis限流算法详解
【滑动窗口通常是通过记录一段时间内的请求次数来进行限流,而令牌桶则是以固定速率生成令牌,请求需要获取令牌才能被处理。】
适用场景:验证码的发送接口容易受到高频调用,可能导致资源耗尽或被攻击;用户信息查询接口如果被大量请求可能导致缓存穿透或数据库压力过大;登录凭证的频繁验证也可能需要限流保护。
实现方式:
滑动窗口。有序集合(ZSET)来记录请求的时间戳,每次请求时删除时间窗口外的记录,然后统计剩余的数量来判断是否超过阈值。例如,验证码接口每分钟允许发送5次请求,超过则限流。
令牌桶算法。Redis的计数器(INCR)和过期时间(EXPIRE)来模拟令牌的生成和消耗。例如,用户信息查询接口每秒生成10个令牌,每个请求消耗一个令牌,令牌不足时拒绝请求。
具体项目案例:
在验证码发送接口使用滑动窗口限流,防止用户频繁请求验证码;在用户信息查询接口使用令牌桶,控制数据库访问频率;在登录凭证验证时结合两种方法,防止暴力破解。
不同限流策略的优缺点:
滑动窗口能更精确地控制时间窗口内的请求量,但实现相对复杂;令牌桶则允许一定程度的突发流量,适合需要平滑处理的场景。根据不同的需求选择合适的限流机制。
验证实际效果:
比如通过压力测试观察限流是否生效,监控系统的请求频率和拒绝情况,确保限流策略既保护了系统资源,又不影响正常用户的体验。
滑动窗口限流(精确时间控制)
场景:验证码发送接口防护
问题:恶意用户通过脚本频繁调用/api/captcha/send接口,导致短信费用激增、服务资源耗尽。
实现原理:
- 每个手机号维护一个按时间戳排序的ZSET
- 每次请求移除60秒前的记录
- 检查当前记录数是否超过阈值(5次)
- 未超限时添加新时间戳并续期Key
令牌桶限流(允许突发流量)
场景:用户信息查询接口保护
问题:当大V用户发布新帖时,/api/user/{userId}接口可能面临突发流量,导致缓存击穿。
实现原理:
- 每个用户ID维护一个令牌桶
- 按固定速率(10个/秒)生成令牌
- 桶容量20个允许突发流量
- 每次请求消耗1个令牌,不足时拒绝
方案对比与选型指南
| 维度 | 滑动窗口 | 令牌桶 | 适用场景 |
|---|---|---|---|
| 实现复杂度 | 中等(需维护时间序列) | 较高(需计算令牌生成) | |
| 流量特征 | 严格限制单位时间请求数 | 允许突发流量(桶容量内) | 验证码发送 vs 用户信息查询 |
| 内存消耗 | 高(存储所有时间戳) | 低(只存储令牌数和时间戳) | |
| 精准度 | 精确到毫秒级控制 | 依赖时间计算,可能有微小误差 | 需要严格控制的场景 vs 允许弹性的场景 |
| 典型应用 | 短信验证码发送、登录失败重试 | 热点用户查询、帖子详情访问 |

浙公网安备 33010602011771号