我正在参加「金石计划」
写在前面
在工作中,我们可能会遇到一些问题,然后通过自己得一顿操作,最终把问题解决了。
可能在一两天之内,我们还是会记得解决这些问题的方法,但是过了一段时间之后,基本上都忘光了。
所以这里,我们应该要养成一个良好的习惯,把每次遇到的问题,如何解决的,都记录下。下次再看文档,这样就想起来了。
<<千与千寻>>有些事情是不可能忘记的,只是一时想不起来
下面,就分享一下,我遇到的一些问题,和相关的解决方法。
可能以前也有分享过一篇,有兴趣的小伙伴,可以查看这里:常用操作合集一、常用操作合集二、常用操作合集三、常用操作合集四
1.Springboot修改配置,并重新设置回spring容器
例如,我们需要对spring.datasource.password
数据库连接密码,进行加密配置。那在spring容器启动的时候,就需要对该值进行解密,然后重新设置回spring容器中,好让spring容器启动成功。
这里,我们可以使用EnvironmentPostProcessor
来实现。
- DbPasswordPostProcessor.java
public class DbPasswordPostProcessor implements EnvironmentPostProcessor {
public static final String SPRING_DATASOURCE_PASSWORD = "spring.datasource.password";
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
System.out.println("environment = " + environment + ", application = " + application);
System.out.println(environment.getPropertySources());
for (PropertySource<?> propertySource : environment.getPropertySources()) {
if (propertySource instanceof OriginTrackedMapPropertySource) {
String password = (String) propertySource.getProperty(SPRING_DATASOURCE_PASSWORD);
System.out.println("加密的密码 : " + password);
HashMap<Object, Object> map = MapUtil.newHashMap(1);
map.put(SPRING_DATASOURCE_PASSWORD, AesUtil.decrypt(password));
System.out.println("解密后的密码: " + AesUtil.decrypt(password));
OriginTrackedMapPropertySource originTrackedMapPropertySource = new OriginTrackedMapPropertySource(SPRING_DATASOURCE_PASSWORD,
map);
environment.getPropertySources().addFirst(originTrackedMapPropertySource);
}
}
}
}
AesUtil.decrypt解密,是一个Aes加解密的工具类,可自行实现即可。
- spring.factories(src/main/resources/META-INF/下面)
org.springframework.boot.env.EnvironmentPostProcessor=com.llsydn.config.DbPasswordPostProcessor
注意:这里spring.datasource.password=${db.password}
,如果这个配置放在nacos这些配置中心,会出现使用EnvironmentPostProcessor获取不到nacos上的配置信息。
即这个时候,走到EnvironmentPostProcessor的时候,都还没去获取nacos的数据,所以你想用这种方式去实现解密肯定是不行的,ApplicationContextInitializer
这个倒是还挺不错,回调时机在读取nacos数据后面执行。
- DbPasswordInitializer.java
public class DbPasswordInitializer implements ApplicationContextInitializer {
public static final String DB_PASSWORD = "db.password";
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
ConfigurableEnvironment environment = applicationContext.getEnvironment();
System.out.println("environment = " + environment + ", application = " + applicationContext);
System.out.println(environment.getPropertySources());
for (PropertySource<?> propertySource : environment.getPropertySources()) {
if (propertySource instanceof BootstrapPropertySource) {
String password = (String) propertySource.getProperty(DB_PASSWORD);
if (StringUtils.isNotEmpty(password)) {
System.out.println("加密的密码 : " + password);
HashMap<Object, Object> map = MapUtil.newHashMap(1);
map.put(DB_PASSWORD, AesUtil.decryptIfNeed(password));
System.out.println("解密后的密码: " + AesUtil.decryptIfNeed(password));
OriginTrackedMapPropertySource originTrackedMapPropertySource = new OriginTrackedMapPropertySource(DB_PASSWORD,
map);
environment.getPropertySources().addFirst(originTrackedMapPropertySource);
}
}
}
}
}
- spring.factories(src/main/resources/META-INF/下面)
org.springframework.context.ApplicationContextInitializer=com.llsydn.config.DbPasswordInitializer
ApplicationContextInitializer 和 EnvironmentPostProcessor
两者都是在refresh spring的上下文之前调用,是spring boot预留的扩展接口,需要在必须要在META-INF/spring.factories文件中去注册,并且注册的是全类名。
两者的调用顺序是,先调用EnvironmentPostProcessor 的实现类,在去调用ApplicationContextInitializer的实现类。
2.mysql忘记密码怎么重置
#关闭mysql
systemctl stop mysqld
#无验证方式启动mysql服务
mysqld --skip-grant-tables
#进入mysql控制台
mysql
#修改密码
use mysql;
alter user 'root'@'localhost' identified by "mysql12qw!@P@ssw0rd";
flush privileges;
#重启mysql
systemctl restart mysqld
3.RestControllerAdvice全局异常如何排除指定的异常?
有时候,我们使用了@RestControllerAdvice全局异常拦截,这样就会导致所有通过throw的异常,都被拦截了。
如果一些底层框架,就是要throw抛出异常,底层框架才能处理后面的业务逻辑;@RestControllerAdvice全局异常,就影响了后面的业务逻辑代码执行,即是截断了底层的实现。
例如:oauth2框架,在调用/oauth/authorize
接口时,如果没有登录,会跳到login登录页面。但是由于@RestControllerAdvice全局异常,导致返回了json异常数据,无法跳转到login登录页面。
oauth2,未登录,抛出的异常是
InsufficientAuthenticationException
那@RestControllerAdvice全局异常,怎么可以排除我们指定的异常呢?
@RestControllerAdvice
@Slf4j
public class ExceptionHandlerAdvice {
.....
/**
* 处理oauth2异常
* @return
*/
@ExceptionHandler(InsufficientAuthenticationException.class)
public void handleInsufficientAuthenticationException() {
throw new InsufficientAuthenticationException("oauth2异常");
}
}
定义一个
InsufficientAuthenticationException
异常的ExceptionHandler
拦截,在方法中,重新throw抛出InsufficientAuthenticationException
异常即可。
4.mysql查询优化,索引使用情况
- using index、using where、using index condition
using index :使用覆盖索引的时候就会出现,测试发现主键索引也会生效
using where:在查找使用索引的情况下,需要回表去查询所需的数据
using index condition:查找使用了索引,不需要回表查询,因为要过滤的字段在索引中
using index & using where:查找使用了索引,但是需要的数据都在索引列中能找到,所以不需要回表查询数据(联合索引很容易出现这样的结果)
SELECT B.AAC002, B.AAC003, A.BCC002, B.CAC001 AS CAC001, A.AAE005, A.BCC205, A.CCD018, A.BDE126, A.BHD305
FROM CCA2 A
LEFT JOIN GXK.AC01 B ON A.AAC001 = B.AAC001
- explain结果如下:
1.由于数据库两张表的字段编码不一致导致的。
#查看这两个表的字段编码
show full columns from CCA2;
show full columns from GXK.AC01;
2.由于Using filesort排序导致的。
去掉ORDER BY后Using filesort自然会消失。
filesort消失了,但是查询速度还是没有跟上来,所以主要的原因还是没有找到。
3.由于没有走索引导致的。
Using join buffer (hash join)
:在连接查询执行过程中,当被驱动表不能有效的利用索引加快访问速度,MySQL一般会为其分配一块名叫join buffer的内存块来加快查询速度,也就是我们所讲的基于块的嵌套循环算法
上面的查询,应该是没有用到索引。
- 输出的各个列的作用
列名 | 描述 |
---|---|
id | 在一个大的查询语句中每个SELECT关键字都对应一个唯一的id |
select_type | SELECT关键字对应的那个查询的类型 |
table | 表名 |
partitions | 匹配的分区信息 |
type | 针对单表的访问方法 |
possible_keys | 可能用到的索引 |
key | 实际上使用的索引 |
key_len | 实际使用到的索引长度 |
ref | 当使用索引列等值查询时,与索引列进行等值匹配的对象信息 |
rows | 预估的需要读取的记录条数 |
filtered | 某个表经过搜索条件过滤后剩余记录条数的百分比 |
Extra | 一些额外的信息 |
type
结果值从上到下依次变坏,至少达到 range 级别,要求 ref 级别,最好是 const 级别。
类型 | 说明 |
---|---|
system | 当表中只有一条记录 并且该表使用的存储引擎的统计数据是准确的如MyISAM 、Memory ,那么对该表的访问方法就是system |
const | 当我们根据主键或者唯一二级索引列与常数进行等值匹配时,对单表的访问方法就是const |
eq_ref | 在连接查询时,如果被驱动表是通过主键或者唯一二级索引列等值匹配的方式进行访问的(如果该主键或者唯一二级索引是联合索引的话,所有的索引列都必须进行等值比较),则对该被驱动表的访问方法就是eq_ref |
ref | 当通过普通的二级索引列与常量进行等值匹配时来查询某个表,那么对该表的访问方法就可能是ref |
fulltext | The join is performed using a FULLTEXT index. |
ref_or_null | 当对普通二级索引进行等值匹配查询,该索引列的值也可以是NULL 值时,那么对该表的访问方法就可能是ref_or_null |
index_merge | 单表访问方法时在某些场景下可以使用Intersection 、Union 、Sort-Union 这三种索引合并的方式来执行查询 |
unique_subquery | 针对在一些包含IN 子查询的查询语句中,如果查询优化器决定将IN 子查询转换为EXISTS 子查询,而且子查询可以使用到主键进行等值匹配的话,那么该子查询执行计划的type 列的值就是unique_subquery |
index_subquery | This join type is similar to unique_subquery. It replaces IN subqueries, but it works for nonunique indexes in subqueries |
range | 如果使用索引获取某些范围区间 的记录,那么就可能使用到range 访问方法 |
index | 当我们可以使用索引覆盖,但需要扫描全部的索引记录时,该表的访问方法就是index |
ALL | 全表扫描 |
5.docker容器操作Permission denied无权限问题
解决办法:
加上 --user=root
参数
docker exec -it --user=root nginx /bin/bash
如果不行,最后的参数可以换成/bin/sh
harbor重启:
#首先停掉harbor
docker-compose down
#启动harbor
docker-compose up -d
6.docker清理占用空间
docker占用磁盘太大,如何清理
docker 占用的空间可以通过下面的命令查看:
docker system df
TYPE 列出了docker 使用磁盘的 4 种类型,TOTAL 表示该类型资源的总数,ACTIVE 表示当前正在使用的数量,SIZE 表示该类型资源的总大小,RECLAIMABLE 表示可以回收的空间大小。
- Images: 所有镜像占用的空间,包括拉取下来的镜像,和本地构建的。
- Containers: 运行的容器占用的空间,表示每个容器的读写层的空间。
- Local Volumes:容器挂载本地数据卷的空间。
- Build Cache: 镜像构建过程中产生的缓存空间(只有在使用 BuildKit 时才有,Docker 18.09 以后可用)。
镜像的磁盘占用 删除悬挂状态的镜像,即 none 状态的镜像
docker image prune
如果想删除所有镜像,可以使用下面的命令:
docker image rm $(docker image ls -q)
注: 正在被容器使用的镜像是不能被删除的。
容器的磁盘占用 删除所有停止的容器
docker container prune
如果想删除所有容器(包括停止的、正在运行的)
docker rm -f $(docker ps -aq)
#或者
docker container rm -f $(docker container ls -aq)
数据卷的磁盘占用 删除不再使用的数据卷
docker volume prune
Build Cache 的磁盘占用 删除 build cache 可以使用命令
docker builder prune
一键清理 通过上面的说明,我们知道了像容器、镜像、数据卷都提供了 prune这个子命令,帮助我们回收空间。 其实,docker 系统层面也有 prune 这个子命令,可以一键清理没用的空间:
docker system prune
这个命令用于清理 Docker 系统中的未使用资源,包括未使用的镜像、停止的容器、未被使用的网络和挂载点等。 这个命令慎用。
检查正在运行的容器大小
想知道正在运行的 Docker 容器的大小,可以使用 docker ps 命令:
docker ps --size
> docker ps --size
Emulate Docker CLI using podman. Create /etc/containers/nodocker to quiet msg.
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES SIZE
8900fc2086b3 docker.io/library/nginx:latest nginx -g daemon o... 14 seconds ago Up 14 seconds ago 0.0.0.0:8099->80/tcp nginx-test 1.11kB (virtual 142MB)
7.maven打包jar和源码包,发布到nexus私服
- pom.xml加入相关插件
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>3.2.0</version>
<!-- 绑定source插件到Maven的生命周期,并在生命周期后执行绑定的source的goal -->
<executions>
<execution>
<!-- 绑定source插件到Maven的生命周期 -->
<id>attach-sources</id>
<phase>package</phase>
<!--在生命周期后执行绑定的source插件的goals -->
<goals>
<goal>jar-no-fork</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
maven-source-plugin作用: 在构建过程中将项目的源代码进行打包,并作为一个jar文件附着在主构件上,在 pom.xml 中添加如下内容,使用 maven 生成 jar 的同时生成 sources 包
使用install命令,就可以将相应的jar包和sources源码包,打包到本地的maven仓库。
- nexus私服地址配置,在pom.xml加入下面配置
<distributionManagement>
<!--nexus私服-->
<repository>
<!--正式版本-->
<id>releases</id>
<url>http://127.0.0.1:8081/nexus/content/repositories/maven-releases</url>
</repository>
</distributionManagement>
maven的setting.xml文件节点配置
<?xml version="1.0" encoding="UTF-8"?>
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd">
<localRepository>E:\Program Files\maven\repository</localRepository>
<interactiveMode>true</interactiveMode>
<offline>false</offline>
<pluginGroups>
<pluginGroup>org.mortbay.jetty</pluginGroup>
<pluginGroup>org.jenkins-ci.tools</pluginGroup>
</pluginGroups>
<!--配置权限,使用默认用户-->
<servers>
<!--llsydn-->
<server>
<id>nexus-releases</id>
<username>admin</username>
<password>admin</password>
</server>
</servers>
<mirrors>
<!--nexus私库-->
<mirror>
<id>nexus</id>
<mirrorOf>*</mirrorOf>
<url>http://127.0.0.1:8081/nexus/repository/maven-public/</url>
</mirror>
<!--阿里云公共仓库-->
<mirror>
<id>alimaven</id>
<name>aliyun maven</name>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
<mirrorOf>central</mirrorOf>
</mirror>
</mirrors>
<profiles>
<!--llsydn-->
<profile>
<id>llsydn</id>
<repositories>
<!--nexus私库地址-->
<repository>
<id>nexus</id>
<url>http://127.0.0.1:8081/nexus/repository/maven-public/</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
<pluginRepositories>
<!--nexus插件库地址-->
<pluginRepository>
<id>nexus</id>
<url>http://127.0.0.1:8081/nexus/repository/maven-public/</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
</profile>
</profiles>
<!--激活profile-->
<activeProfiles>
<activeProfile>llsydn</activeProfile>
</activeProfiles>
</settings>
使用deply命令,就可以将相应的jar包和sources源码包,发布到nexus私服。
8.mysql集群方案
MySQL集群方案:
1、MySQL Replication主从方案,mysql官方方案,一般大家使用的是这种,主挂了这种是不带自动选主的功能
2、MySQL Fabirc主从方案,mysql官方方案,是带自动选主的功能
3、MySQL Cluster
多主多从方案,mysql官方方案,这个方案是多主,挂了一个还有别的
4、MHA主从方案,第三方的方案,这个方案也是带主挂了,自动选主功能
9.mysql跨库查询方案
10.没有telnet,nc,namp命令下检测端口
/dev/tcp/<hostname>/<port>
hostname替换ip,port替换端口号
特殊的文件,允许通过该接口进行TCP网络通信
echo > /dev/tcp/127.0.0.1/3306
结果没有返回,说明端口是通的。
结果返回如下,说明端口是不通的。
-bash: connect: Connection refused
-bash: /dev/tcp/127.0.0.1/3306: Connection refused
注:该命令不适用于docker容器里面
容器里面可以采用下面的命令:结果和上面一样(alpine镜像除外)
-i >& /dev/tcp/127.0.0.1/3306 0>&1;
11.docker迁移默认的/var/lib/docker 到指定数据盘目录
#1、先停止docker 服务
systemctl stop docker
#2、创建docker挂载数据盘目录
mkdir -p /data/docker/lib
#3、 安装迁移工具
yum install rsync -y
#4、 迁移数据到新目录
rsync -azP /var/lib/docker /data/docker/lib
#5、修改docker 配置文件docker.service
vim /usr/lib/systemd/system/docker.service
在ExecStart=/usr/bin/dockerd 后添加--graph=/data/docker/lib/docker
#6、重启docker 服务
systemctl daemon-reload && systemctl restart docker
#7、确定docker 正常,删除原目录
rm -rf /var/lib/docker
12.Harbor如何通过命令行创建项目
有时候在工作中,我们会遇到在没有内网windows机器的情况下,想要创建项目。这时候就需要命令行来进行直接创建。
说明:我这里的harbor版本是v2.6.2的
- 命令行创建私有项目
假设需要创建名为jxbp的私有项目
curl -k -u 'admin:Harbor12345' -XPOST -H "Content-Type:application/json" -d '{"project_name":"llsydn", "public": false}' "http://127.0.0.1:88/api/v2.0/projects"
上面参数说明:
-k
:用于执行不安全的 SSL 连接,通常在使用自签名证书时使用。-u 'admin:Harbor12345'
:提供了基本身份验证的用户名和密码。确保将这里的用户名和密码替换为你的 Harbor 实例的实际凭证。-XPOST
:指定 HTTP 请求的方法为 POST。-H "Content-Type:application/json"
:设置请求的内容类型为 JSON。-d '{"project_name":"llsydn", "public": true}'
:提供与 POST 请求一起发送的数据。在这里,JSON 对象指定了项目名称为 "llsydn" 并将其设置为私有。"http://127.0.0.1:88/api/v2.0/projects"
:指定发送 POST 请求的 URL。确保将这里的 URL 替换为你的 Harbor 实例的实际 URL。
好了,以上就是我个人的实操了。可能有些不对,大家伙,轻点喷!!!
个人理解,可能也不够全面,班门弄斧了。
好了,今天就先到这里了!!!^_^
如果觉得有收获的,帮忙点赞、评论、收藏
一下,再走呗!!!