docker ps
可以查看当前docker中运行的所有容器的状态
ps命令中常见的选项如下:
ps命令显示的标题内容如下
docker stop 可以停止正在运行的容器
stop只是停止容器.并不会删除容器
如果想删除容器需要使用rm命令
注意:这里rm删除的是容器,不是本地镜像,和rmi命令要区分
首先使用docker ps命令查询所有正在运行的容器
docker stop [容器id]
docker rm [容器id]
rm的选项有:
-f:强制删除容器,无论是否运行,如果不加,运行的容器无法删除。
搜索redis
拉取镜像参考之前的笔记
自己搜docker启动Redis的命令
docker run -itd --name redis-test -p 6379:6379 redis
启动之后可以本地访问客户端
docker exec -it redis-test redis-cli
如果当前windows系统要连接Linux中的资源
一般都要关闭Linux的防火墙
实际开发中,不会彻底关闭防火墙,而是开放指定的端口号
systemctl stop firewalld
system:系统
ctl:control:控制
给大家的大压缩包
Virtualbox 4.15G
VMware 3.55G
解压之后启动
Linux:
用户名:root
密码:12345678(不要用小键盘)
启动后使用
ifconfig | more
观察ip地址
可以利用HS尝试连接虚拟机的数据库
一般情况下ip地址为:192.168.137.150
数据库密码是:
tarena2017Up;
注意分号也是密码的一部分,不写连不上
注意如果需要停止虚拟机,选择快速休眠,不要关机
因为现版本酷鲨商城连接的都是本地项目
下面我们要连接的是虚拟机软件,所以要修改一下java项目的配置文件
front
addr: 192.168.137.150
mall-leaf
leaf.properties
leaf.jdbc.url=jdbc:mysql://192.168.137.150:3306/leafdb?useUnicode=true&characterEncoding=utf8&useSSL=false&allowPublicKeyRetrieval=true
leaf.jdbc.password=tarena2017Up;
mall-order
addr: 192.168.137.150
password: tarena2017Up;
mall-product
addr: 192.168.137.150
password: tarena2017Up;
mall-search
addr: 192.168.137.150
mall-seckill
addr: 192.168.137.150
password: tarena2017Up;
rabbitmq:
host: ${my.server.addr}
port: 5672
username: user
password: 123456
virtual-host: /
mall-sso
addr: 192.168.137.150
# 有两个密码要改!!!!
admin
password: tarena2017Up;
user
password: tarena2017Up;
因为上面我们启用了虚拟机
我们在虚拟机中安装的redis是一个特殊版本的Redis
这个版本内置了lua脚本,支持布隆过滤的方法
我们可以直接使用,实现布隆过滤器
csmall-stock-webapi的pom文件 添加依赖
org.springframework.boot
spring-boot-starter-data-redis
在dev-yml文件中添加redis的配置
spring:
redis:
host: 192.168.137.150
port: 6379
password:
操作布隆过滤器有一个专门的类
实现对布隆过滤器的新增元素,检查元素等方法的实现
在酷鲨前台大项目中的seckill-webapi下的utils包里
RedisBloomUtils类复制到需要使用布隆过滤器的项目中
当前Stock模块
有一个周期输出时间的方法
我们可以借助这个运行,测试布隆过滤器的功能(如果这个方法已经编写了别的代码可以先注释掉)
quartz包下QuartzJob
// 操作支持布隆过滤器redis的对象
@Autowired
private RedisBloomUtils redisBloomUtils;
@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
// 实现一个简单的任务做演示
// 例如输出当前时间
System.out.println("-------------------"+ LocalDateTime.now() +
"--------------------");
// 先定义要保存到布隆过滤器的元素数组
String[] colors={"red","origin","yellow","green","blue","pink","white"};
// 执行向布隆过滤器中保存上面数组元素的指令
final String COLOR_BLOOM="color_bloom";
redisBloomUtils.bfmadd(COLOR_BLOOM,colors);
// 下面开始检查一个指定的字符串是否在布隆过滤器中
String elm="zhanghl";
// 判断该元素是否在数组中
System.out.println(elm+"是否在定义的数组中:"+
redisBloomUtils.bfexists(COLOR_BLOOM,elm));
}
我们在开发秒杀业务时,事先进行了准备工作
在秒杀开始前5分钟,预热了sku的库存数和spu的随机码
但是没有将当前批次的spuId保存在布隆过滤器中
导致业务有漏洞
现在,我们准备了支持布隆过滤器的Redis
我们可以将允许用户查询到的秒杀商品,保存到布隆过滤器中
这样非当前秒杀商品,用户就不能查询数据库了,防止缓存穿透
在seckill-webapi模块中
seckill.timer.job包中,新建SeckillBloomInitialJob
public class SeckillBloomInitialJob implements Job {
@Autowired
private RedisBloomUtils redisBloomUtils;
@Autowired
private SeckillSpuMapper seckillSpuMapper;
@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
// 首先查询下个批次的所有数据
// 有需求要求查询两个批次或更多批次的数据
// 因为可能允许更多批次的商品被用户提前浏览
// 我们可以预热今天和明天的秒杀商品(我们的需求是一天一批)
// 获取今天的key(正在进行秒杀的商品)
// "spu:bloom:filter:2022-09-13"
String bloomTodayKey= SeckillCacheUtils.getBloomFilterKey(LocalDate.now());
// 获取明天的key
String bloomTomorrowKey=
SeckillCacheUtils.getBloomFilterKey(LocalDate.now().plusDays(1));
// 实际开发中,可以按照实际去查询对应批次的所有秒杀商品
// 学习过程中因为数据库数据量少,所以只查询同一批次即可
// 根据时间,查询在这个时间正在进行秒杀的商品的所有id数据
Long[] spuIds=seckillSpuMapper.findAllSeckillSpuIds();
// 布隆过滤器支持保存的是字符串数组
// 所以我们要将Long[]转换成String[]
String[] spuIdsStr=new String[spuIds.length];
// 遍历spuIds,将其中元素转换赋值到spuIdsStr
for(int i=0;i
下面在seckill.timer.config包中添加布隆过滤器相关的调度配置
继续在QuartzConfig类中添加绑定信息
// 布隆过滤器的加载
@Bean
public JobDetail seckillBloomJobDetail(){
return JobBuilder.newJob(SeckillBloomInitialJob.class)
.withIdentity("SeckillBloom")
.storeDurably()
.build();
}
@Bean
public Trigger seckillBloomTrigger(){
return TriggerBuilder.newTrigger()
.forJob(seckillBloomJobDetail())
.withIdentity("SeckillBloomTrigger")
.withSchedule(CronScheduleBuilder.cronSchedule("0 0/1 * * * ?"))
.build();
}
下面可以测试布隆过滤器的运行
保证虚拟机启动正常
启动product\seckill
如果没有虚拟机的同学,敲一遍代码熟悉即可
现在Redis中保存了布隆过滤器
我们需要用户根据SpuId查询商品时,进行判断和过滤
如果spuId不存在,就应该发生异常,给出提示
SeckillSpuServiceImpl类中getSeckillSpu进行修改,添加布隆过滤器的判断
@Autowired
private RedisBloomUtils redisBloomUtils;
// 根据SpuId查询Spu详情(包含秒杀信息和spu普通信息)
@Override
public SeckillSpuVO getSeckillSpu(Long spuId) {
// 先使用布隆过滤器对参数有spuId进行判断
// 如果判断结果是spuId不存在于数据库中,直接抛出异常
// 获得本次布隆过滤器的Key
String bloomTodayKey=SeckillCacheUtils.getBloomFilterKey(LocalDate.now());
log.info("当前批次布隆过滤器的key为:{}",bloomTodayKey);
if(!redisBloomUtils.bfexists(bloomTodayKey,spuId+"")){
// 进入这个if表示当前spuId不在布隆过滤器保存的数据中
// 为了防止缓存穿透,抛出异常,终止程序
throw new CoolSharkServiceException(ResponseCode.NOT_FOUND,
"您访问的商品不存在(布隆过滤器判断)");
}
// 声明返回值类型的对象
SeckillSpuVO seckillSpuVO=null;
// 其它代码无变化略......
}
重启Seckill模块
访问10007
查询SpuId如果不存在于秒杀表中,是否能被过滤器拦截
ELK:
E:Elasticsearch 全文搜索引擎
L:logstash 日志采集工具
K:Kibana ES的可视化工具
ELK是当今业界非常流行的日志采集保存和查询的系统
我们编写的程序,会有很多日志信息,但是日志信息的保存和查询是一个问题
idea控制台是临时显示的位置,我们可以将它保存在文件中
但是即使保存在文件中,海量日志信息要想查询需要的条目也是问题
所以我们使用ELK来保存
保存并能够快速便捷的查询查看日志信息就是新出现的需求了
ELK这个组合可以完成这个任务
Elasticsearch负责将日志信息保存,查询时可以按关键字快速查询
那么这些日志怎么收集呢?
利用logstash这个软件可以监听一个文件,将这个文件中出现的内容经过处理发送到指定端口
我们就可以监听我们程序输出的日志文件,然后将新增的日志信息保存到ES中
Kibana来负责进行查询和查看结果
日志的管理工具还有一套叫链路追踪
和ELK有类似的效果,感兴趣的同学可以自己搜索
Logstash是一款开源的日志采集,处理,输出的软件,每秒可以处理数以万计条数据,可以同时从多个来源采集数据,转换数据,然后将数据输出至自己喜欢的存储库中(官方推荐的存储库为Elasticsearch)
上面图片数据源可以是任何产生数据的介质,数据库,redis,java的日志文件均可
输出目标一般也是能够保存数据的媒体,数据库,redis,ES等
LogStash内部有3个处理数据的步骤
其中采集数据的用法比较多样,还支持各种插件
logstash还有一个非常常见的用法
就是能够自动完成数据库数据和ES中数据的同步问题
实现原理
我们可以配置logstash监听数据库中的某个表
一般设计为监听表中数据的变化,在规范的数据表结构中,logstash可能监听gmt_modified列
只要gmt_modified列数据有变化,就收集变化的数据行,将这行数据的信息更新到ES
下面我们就在虚拟机环境下实现搜索操作
之前我我们已经修改了yml文件,将搜索的目标更换为虚拟机中的ES
在虚拟机的连接环境中,我们使用SpuEntity来实现ES的连接
我们可以看到SpuEntity类中没有任何编写分词的属性
原因是为了更高效的实现分词,logstash将所有需要分词的列拼接组合成了一个新列search_text
当需要查询时只需要查询search_text字段即可
在search-webapi模块中的repository包下,创建新的持久层接口SpuEntityRepository
@Repository
public interface SpuEntityRepository extends
ElasticsearchRepository {
// 根据用户输入的关键字查询ES中匹配的数据
// Logstash将所有查询字段拼接成了search_text字段,SpuEntity并不存在
// 所以我们的查询直接搜索search_text,而且不能利用SpringData给的定义方法名查询的功能
@Query("{\"match\":{\"search_text\":{\"query\":\"?0\"}}}")
Page querySearchByText(String keyword, Pageable pageable);
}
因为实体类泛型类型的变化
ISearchService接口中的返回值泛型也要修改一下
public interface ISearchService {
// ES分页查询spu的方法
// ↓↓↓↓↓↓↓↓↓↓↓
JsonPage search(String keyword, Integer page, Integer pageSize);
// 向ES中加载数据的方法
void loadSpuByPage();
}
原来的业务逻辑层实现类,要废弃,可以删除
如果不想删除也要把之前代码注释,还有必须删除@Service注解
原有SearchServiceImpl修改为
//@Service
//@Slf4j
@Deprecated
public class SearchServiceImpl {
// 其中所有代码均可注释
}
新建SearchRemoteServiceImpl实现ISearchService接口,代码如下
// 实现查询远程服务器(虚拟机Linux系统)ELK系统的业务逻辑层
@Service
@Slf4j
public class SearchRemoteServiceImpl implements ISearchService {
@Autowired
private SpuEntityRepository spuEntityRepository;
@Override
public JsonPage search(String keyword, Integer page, Integer pageSize) {
// 执行调用按关键字进行查询的方法
Page spuEntities=spuEntityRepository
.querySearchByText(keyword, PageRequest.of(page-1,pageSize));
// 将Page转换为JsonPage
JsonPage jsonPage=new JsonPage<>();
// 赋值相关分页信息
jsonPage.setPage(page);
jsonPage.setPageSize(pageSize);
//总条数和总页数
jsonPage.setTotal(spuEntities.getTotalElements());
jsonPage.setTotalPage(spuEntities.getTotalPages());
// 将查询到的数据赋值到jsonPage
jsonPage.setList(spuEntities.getContent());
// 最后别忘了返回
return jsonPage;
}
// 加载和同步数据完全由logstash完成,无需编写下面加载数据的方法
@Override
public void loadSpuByPage() {
}
}
修改SearchController中的代码
// ↓↓↓↓↓↓↓↓↓↓↓
public JsonResult> searchByKeyword(
String keyword,Integer page, Integer pageSize){
// ↓↓↓↓↓↓↓↓↓↓↓
JsonPage jsonPage=
searchService.search(keyword,page,pageSize);
return JsonResult.ok(jsonPage);
}
启动product/passport/search
先进行jwt登录的复制,复制到search模块的全局参数中
可以搜索手机查询效果
启动虚拟机
如果不能查询出数据库中正确的对应的信息
需要在数据库工具软件中运行下面代码
USE mall_pms;
UPDATE pms_spu SET gmt_modified=NOW() WHERE 1=1;
以激活logstash对spu表的监听,并向ES中保存数据
这个操作可能有些延迟,稍等即可
再次查询,就有能够正确搜索信息了!
安装配置好相关软件后
logstash会自动监听指定的表(一般指定监听gmt_modified列)
当gmt_modified列值变化时,logstash就会收集变化的行的信息
周期性的向ES进行提交
数据库中的变化的数据就会自动同步到ES中了
这样,我们在程序中,就无需编写任何同步ES和数据库的代码
所谓配置中心:将项目需要的配置信息保存在配置中心,需要读取时直接从配置中心读取,方便配置管理的微服务工具
我们可以将部分yml文件的内容保存在配置中心
一个微服务项目有很多子模块,这些子模块可能在不同的服务器上,如果有一些统一的修改,我们要逐一修改这些子模块的配置,由于它们是不同的服务器,所以修改起来很麻烦
如果将这些子模块的配置集中在一个服务器上,我们修改这个服务器的配置信息,就相当于修改了所有子模块的信息,这个服务器就是配置中心
使用配置中心的原因就是能够达到高效的修改各模块配置的目的
Nacos既可以做注册中心,也可以做配置中心
Nacos做配置中心,支持各种格式\类型的配置文件
properties\yaml(yml)\txt\json\xml等
namespace:命名空间
group:分组
Service/DataId:具体数据
命名空间
namespace是Nacos提供的最大的数据结构
一个Nacos可以创建多个命名空间
一个命名空间能够包含多个group
每一个group中又可以包含多条配置信息
在nacos中创建命名空间
在上图连接的位置可以新增命名空间,填写命名空间名称和描述即可
Nacos有默认的命名空间public不能删除和修改
添加命名空间后,我们在Nacos中注册的服务或添加的配置就可以指定命名空间了
因为多个命名空间可以隔离项目,每个项目使用自己的命名空间,互不干扰
分组
一个命名空间中可以有多个分组,进行进一步分离
我们使用时,如果不需要进一步分组,推荐使用group名称:DEFAULT_GROUP
服务或配置
确定了命名空间和分组之后
我们就可以添加服务或配置了
之前我们启动的各种模块都是服务,之前都是默认保存在public命名空间中
下面我们主要使用配置中心的功能,在命名空间中添加配置
添加配置就是设置DataId
实际在Nacos中定位一个配置的结构为
Namespace>Group>DataId
Nacos首页->配置管理->配置列表->添加配置(右侧的大"+"号)
添加cart模块数据库连接配置