JSD-2204-续Docker命令-布隆过滤器的测试-秒杀业务完善-ELK-配置中心-Day18

1.续 Docker命令

1.1ps命令

docker ps

可以查看当前docker中运行的所有容器的状态

ps命令中常见的选项如下:

  • -a:显示所有容器,如果不加只显示正在启动运行的容器,停止的不会显示。
  • -l:显示最近的启动创建的容器。
  • -n=[数字]:显示最近n个容器。
  • -q:只显示容器id。经常和-a一起使用,获得当前宿主机所有容器id参数集合。

ps命令显示的标题内容如下

  • container id:容器id,很多操作容器命令都需要用到的参数。
  • image:容器创建使用的镜像。
  • command:容器中在运行的进程或者命令。
  • created:创建时间。
  • status:容器状态。
  • ports:容器的端口映射情况,这里没有用到端口。
  • names:容器的名字,启动没有指定--name选项,会默认使用一个名字。

1.2stop\rm命令

docker stop 可以停止正在运行的容器

stop只是停止容器.并不会删除容器

如果想删除容器需要使用rm命令

注意:这里rm删除的是容器,不是本地镜像,和rmi命令要区分

首先使用docker ps命令查询所有正在运行的容器

docker stop [容器id]
docker rm [容器id]

rm的选项有:

-f:强制删除容器,无论是否运行,如果不加,运行的容器无法删除。

1.3启动redis

搜索redis

拉取镜像参考之前的笔记

自己搜docker启动Redis的命令

docker run -itd --name redis-test -p 6379:6379 redis

启动之后可以本地访问客户端

docker exec -it redis-test redis-cli

1.4关闭防火墙

如果当前windows系统要连接Linux中的资源

一般都要关闭Linux的防火墙

实际开发中,不会彻底关闭防火墙,而是开放指定的端口号

systemctl stop firewalld

system:系统

ctl:control:控制

2.酷鲨商城前台虚拟机

给大家的大压缩包

Virtualbox 4.15G

VMware 3.55G

解压之后启动

Linux:

用户名:root

密码:12345678(不要用小键盘)

启动后使用

ifconfig | more

观察ip地址

可以利用HS尝试连接虚拟机的数据库

一般情况下ip地址为:192.168.137.150

数据库密码是:

tarena2017Up;

注意分号也是密码的一部分,不写连不上

注意如果需要停止虚拟机,选择快速休眠,不要关机

2.1酷鲨商城前台项目配置修改

因为现版本酷鲨商城连接的都是本地项目

下面我们要连接的是虚拟机软件,所以要修改一下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;

2.2布隆过滤器的测试

因为上面我们启用了虚拟机

我们在虚拟机中安装的redis是一个特殊版本的Redis

这个版本内置了lua脚本,支持布隆过滤的方法

我们可以直接使用,实现布隆过滤器

csmall-stock-webapi的pom文件 添加依赖

<!--   redis依赖   -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

在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));
}

3.秒杀业务完善

3.1秒杀准备时加载布隆过滤器

我们在开发秒杀业务时,事先进行了准备工作

在秒杀开始前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<spuIds.length;i++){
            spuIdsStr[i]=spuIds[i]+"";
        }
        // 将赋值完毕的字符串数组数据保存到布隆过滤器中
        // 实际开发应该按照对应的批次保存,学习过程中就不分批次了
        redisBloomUtils.bfmadd(bloomTodayKey,spuIdsStr);
        redisBloomUtils.bfmadd(bloomTomorrowKey,spuIdsStr);
        System.out.println("两个批次的布隆过滤器加载完成");

    }
}

下面在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

如果没有虚拟机的同学,敲一遍代码熟悉即可

3.2布隆过滤器判断spuId是否存在

现在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如果不存在于秒杀表中,是否能被过滤器拦截

4.ELK简介

4.1什么是ELK

ELK:

E:Elasticsearch 全文搜索引擎

L:logstash 日志采集工具

K:Kibana ES的可视化工具

ELK是当今业界非常流行的日志采集保存和查询的系统

我们编写的程序,会有很多日志信息,但是日志信息的保存和查询是一个问题

idea控制台是临时显示的位置,我们可以将它保存在文件中

但是即使保存在文件中,海量日志信息要想查询需要的条目也是问题

所以我们使用ELK来保存

4.2为什么需要ELK

保存并能够快速便捷的查询查看日志信息就是新出现的需求了

ELK这个组合可以完成这个任务

Elasticsearch负责将日志信息保存,查询时可以按关键字快速查询

那么这些日志怎么收集呢?

利用logstash这个软件可以监听一个文件,将这个文件中出现的内容经过处理发送到指定端口

我们就可以监听我们程序输出的日志文件,然后将新增的日志信息保存到ES中

Kibana来负责进行查询和查看结果

日志的管理工具还有一套叫链路追踪

和ELK有类似的效果,感兴趣的同学可以自己搜索

4.3Logstash

4.4什么是logstash

Logstash是一款开源的日志采集,处理,输出的软件,每秒可以处理数以万计条数据,可以同时从多个来源采集数据,转换数据,然后将数据输出至自己喜欢的存储库中(官方推荐的存储库为Elasticsearch)

上面图片数据源可以是任何产生数据的介质,数据库,redis,java的日志文件均可

输出目标一般也是能够保存数据的媒体,数据库,redis,ES等

LogStash内部有3个处理数据的步骤

  • input 将数据源的数据采集到Logstash
  • filter (非必要)如果需要可以对采集到的数据进行处理
  • output 将处理好的数据保存到目标(一般就是ES)

其中采集数据的用法比较多样,还支持各种插件

4.5logstash实现数据库和ES数据的同步

logstash还有一个非常常见的用法

就是能够自动完成数据库数据和ES中数据的同步问题

实现原理

我们可以配置logstash监听数据库中的某个表

一般设计为监听表中数据的变化,在规范的数据表结构中,logstash可能监听gmt_modified列

只要gmt_modified列数据有变化,就收集变化的数据行,将这行数据的信息更新到ES

下面我们就在虚拟机环境下实现搜索操作

4.6实现虚拟机ES搜索功能

之前我我们已经修改了yml文件,将搜索的目标更换为虚拟机中的ES

在虚拟机的连接环境中,我们使用SpuEntity来实现ES的连接

我们可以看到SpuEntity类中没有任何编写分词的属性

原因是为了更高效的实现分词,logstash将所有需要分词的列拼接组合成了一个新列search_text

 

当需要查询时只需要查询search_text字段即可

4.7添加新的持久层

在search-webapi模块中的repository包下,创建新的持久层接口SpuEntityRepository

@Repository
public interface SpuEntityRepository extends
                                ElasticsearchRepository<SpuEntity,Long> {
    // 根据用户输入的关键字查询ES中匹配的数据
    // Logstash将所有查询字段拼接成了search_text字段,SpuEntity并不存在
    // 所以我们的查询直接搜索search_text,而且不能利用SpringData给的定义方法名查询的功能
    @Query("{\"match\":{\"search_text\":{\"query\":\"?0\"}}}")
    Page<SpuEntity> querySearchByText(String keyword, Pageable pageable);


}

4.8业务逻辑层接口

因为实体类泛型类型的变化

ISearchService接口中的返回值泛型也要修改一下

public interface ISearchService {

    // ES分页查询spu的方法
    //      ↓↓↓↓↓↓↓↓↓↓↓
    JsonPage<SpuEntity> search(String keyword, Integer page, Integer pageSize);

    // 向ES中加载数据的方法
    void loadSpuByPage();
}

4.9实现业务逻辑层代码

原来的业务逻辑层实现类,要废弃,可以删除

如果不想删除也要把之前代码注释,还有必须删除@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<SpuEntity> search(String keyword, Integer page, Integer pageSize) {
        // 执行调用按关键字进行查询的方法
        Page<SpuEntity> spuEntities=spuEntityRepository
                .querySearchByText(keyword, PageRequest.of(page-1,pageSize));
        // 将Page<SpuEntity>转换为JsonPage<SpuEntity>
        JsonPage<SpuEntity> 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() {

    }
}

4.10修改控制层代码

修改SearchController中的代码

//                        ↓↓↓↓↓↓↓↓↓↓↓
public JsonResult<JsonPage<SpuEntity>> searchByKeyword(
        String keyword,Integer page, Integer pageSize){
    //      ↓↓↓↓↓↓↓↓↓↓↓
    JsonPage<SpuEntity> 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中保存数据

这个操作可能有些延迟,稍等即可

再次查询,就有能够正确搜索信息了!

4.11Logstash下ES的运行流程

安装配置好相关软件后

logstash会自动监听指定的表(一般指定监听gmt_modified列)

当gmt_modified列值变化时,logstash就会收集变化的行的信息

周期性的向ES进行提交

数据库中的变化的数据就会自动同步到ES中了

这样,我们在程序中,就无需编写任何同步ES和数据库的代码

5.配置中心

5.1什么是配置中心

所谓配置中心:将项目需要的配置信息保存在配置中心,需要读取时直接从配置中心读取,方便配置管理的微服务工具

我们可以将部分yml文件的内容保存在配置中心

一个微服务项目有很多子模块,这些子模块可能在不同的服务器上,如果有一些统一的修改,我们要逐一修改这些子模块的配置,由于它们是不同的服务器,所以修改起来很麻烦

如果将这些子模块的配置集中在一个服务器上,我们修改这个服务器的配置信息,就相当于修改了所有子模块的信息,这个服务器就是配置中心

使用配置中心的原因就是能够达到高效的修改各模块配置的目的

5.2配置中心的使用

Nacos既可以做注册中心,也可以做配置中心

Nacos做配置中心,支持各种格式\类型的配置文件

properties\yaml(yml)\txt\json\xml等

5.3Nacos数据结构

namespace:命名空间

group:分组

Service/DataId:具体数据

命名空间

namespace是Nacos提供的最大的数据结构

一个Nacos可以创建多个命名空间

一个命名空间能够包含多个group

每一个group中又可以包含多条配置信息

在nacos中创建命名空间

 

在上图连接的位置可以新增命名空间,填写命名空间名称和描述即可

Nacos有默认的命名空间public不能删除和修改

添加命名空间后,我们在Nacos中注册的服务或添加的配置就可以指定命名空间了

因为多个命名空间可以隔离项目,每个项目使用自己的命名空间,互不干扰

分组

一个命名空间中可以有多个分组,进行进一步分离

我们使用时,如果不需要进一步分组,推荐使用group名称:DEFAULT_GROUP

服务或配置

确定了命名空间和分组之后

我们就可以添加服务或配置了

之前我们启动的各种模块都是服务,之前都是默认保存在public命名空间中

下面我们主要使用配置中心的功能,在命名空间中添加配置

添加配置就是设置DataId

实际在Nacos中定位一个配置的结构为

Namespace>Group>DataId

5.4Nacos添加配置

Nacos首页->配置管理->配置列表->添加配置(右侧的大"+"号)

添加cart模块数据库连接配置

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值