总结总结小总结

缓存开始

SpringCache

  • 使用spring-cache注解, 存储数据注意事项

  • 当使用@Cacheable缓存了数据后, 有别的方法进行修改时,

    #缓存该方法的值
    @Cacheable(value = "category",key = "#root.methodName")
    

    1, 可以使用@CacheEvict删除对应的值, 让@Cacheable的方法重新载入redis

    #字符串需要加 单引号, 不然他以为是表达式
    @CacheEvict(value = {"category"},key = "'getCatalogJson'")
    

    2, 使用@Caching删除多个缓存

    @Caching(
            evict = {
                    @CacheEvict(value = {"category"},key = "'getCatalogJson1'")
                    ,@CacheEvict(value = {"category"},key = "'getCatalogJson2'")
            }
    )
    

    3, 根据分区删除数据, 删除某个分区下的所有数据

    @CacheEvict(value = "category",allEntries = true)
    

双写模式

  • 使用@CachePut, 将更新的数据在redis中再存储一份, 即删除之前的又写入当前的, 双写模式

    @CachePut
    @CacheEvict(value = {"category"},key = "'getCatalogJson'")
    @Transactional
    @Override
    public ... updateCascade(CategoryEntity category) {
        this.updateById(category);
        		  categoryBrandRelationService.updateCategory(category.getCatId(),category.getName());
        return ...;
    }
    

读模式

  • 缓存穿透: 就是大量请求查询一个null数据;

    spring-cache解决方式:

    #redis是否缓存空值, 解决缓存穿透
    spring.cache.redis.cache-null-values=true
    

    缓存击穿: 大量请求同时查询一个正好过期的数据;

    解决方式: 加锁, 默认spring-cache是不加锁的, 可以通过sync指定, 注意如果数据可能存在数据击穿, 再加sync

    spring-cache解决方式:

    @Cacheable(value = "category",key = "#root.methodName",sync = true)
    

    缓存雪崩: 大量的key同时过期;

    spring-cache解决方式: 让key有随机不同的过期时间

    #redis中key的存活时间, 单位是ms
    spring.cache.redis.time-to-live=3600000
    

写模式

1, 读写加锁

2, 引入cannal, 动态更新mysql

3, 读多写多, 直接去查询mysql

redis存数据总结

常规数据(读多写少, 即时性, 一致性要求不高的数据), 可以使用spring-cache存储, 写的数据加过期时间, 按点更新

特殊数据: 要即时性, 一致性要求高的数据, 使用cannal同步, 加读写锁

Redis分布式锁

  • (可以参考谷粒商城gulimall的CategoryServiceImpl的阶段1, 2, 3, 4笔记)

    引入redission, 解决分布式锁, 主要是使用Redisson的LockWatchdogTime看门狗

    简单引入:

    RLock lock = redisson.getLock("CatalogJson-lock");
    

    具体参考:

      public Map<String, List<Catelog2Vo>> getCatalogJsonFromDbWithRedissonlock() {
    
            // 1 锁的名字。锁的粒度
    //        获取一把锁, 只要锁的名字一样, 就是同一把锁
            RLock lock = redisson.getLock("CatalogJson-lock");
    //        加锁, 这里加的Lock是可重入锁
            lock.lock(); //阻塞式等待
    //        1, 锁的自动续费, 如果业务超长, 运行期间会自动给锁续费30s, 不用担心业务太长, 锁自动过期删除, 专业名词叫, Redisson看门狗
    //        2, 加锁的业务只要运行完成, 就不会给当前锁续费, 即使不手动解锁, 锁默认也会在30s后自动删除, 因此即使服务加锁后宕机, 也不会有死锁问题
    
    //        lock.lock(10, TimeUnit.SECONDS);
    //        设置锁的过期时间是10s, 
    //        用了这个方法, 锁到期以后, 不会自动续费, 虽然可以保证即使程序宕机, 也可以删除锁,
    //        但是这个方法要慎用, 一定要保证业务代码在10s内可以执行完毕, 不然会造成删除别的线程的锁
    
    //        lock.lock(10, TimeUnit.SECONDS);不会自动续费,原理就是:
    //            1, 如果我们传递了锁的超时时间, 就发送给redis执行脚本, 进行占锁, 默认超时就是我们指定的时间  底层也是调用lua脚本, 让redis在指定的时间内, 删除锁
    //            2, 如果我们未指定锁的超时时间, 就使用 30 * 1000[ LockWatchdogTime看门狗的默认时间]
    //                    只要加锁成功, 就会启动一个定时任务 -> 重新给锁设置过期时间, 新的过期时间就是看门狗的默认时间30s
    //                                                    -> 而这个定时任务的执行时间, 执行的时间就是  看门狗的时间/3  10s后执行一次定时任务 底层是调用lua脚本, 给快要过期的key续费
    
    //        最佳实践方法:
    //            使用lock.lock(10, TimeUnit.SECONDS);指定解锁时间,且这个时间要大于业务代码执行时间
            Map<String, List<Catelog2Vo>> dataFromDB;
            try {
                dataFromDB = getDataFromDB();
            } finally {
                lock.unlock();
            }
            return dataFromDB;
        }
    

Session

单一应用的session工作原理:
1,浏览器访问服务器, 进行登录, 服务器登录后, 后端服务器的内存空间里会记录一个session(可以理解为一个Map), 里边可以存很多信息, 然后服务器给浏览器response的时候, 会命令浏览器保存一个cookie, 也就是说, 用浏览器保存的这个cookie和服务器的session做对应,
相当于, 我们去银行, 银行给你记录一大堆东西,然后返回给你一个银行卡号
2,之后浏览器每次访问后端服务器, 都会带着cookie,
你下次再过去办业务,就直接告诉他卡号就行了
3, 直到浏览器关闭了, cookie清除会话,
银行关门, 或者你走了, 直接把卡注销了
4, 下次再访问, 没有了cookie, 那就重新登录, 重新创建会话, cookie
再进来就重新办卡
5, 也正因为这样子, 所以只有在后端应用是单机的时候才好使, 如果是多个分布式, 微服务, 换域名(auth.gulimall.com登录, 在gulimall.com里就不好使了), 之类这样子的就歇菜了, 就得用分布式Session了

在这里插入图片描述

session不同步问题

1, 子域session不共享, 不通的域名下, 不能跨域名共享, 可以通过自己new Cookie(“JSESSIONID11”,“tt”).setDomain(“”);然后用serverletResponse返回给浏览器, 通过放大作用域, 即使是子域名发的卡, 父域名也可以用;
2, 同一个域名下, 但是后端多个服务, 比如有三个auth服务, 在1号里登录了, 2号,3号不认, 所以session也不好使, 这个可以用redis存储session解决;
3, 最终选择用Spring Session



缓存结束


VMware结束

用了一段时间不用了, 突然网络不能访问了

sudo vim /etc/sysconfig/network-scripts/ifcfg-ens33 
	将ONBOOT = no修改为ONBOOT = yes
service network restart
然后就可以访问百度了

VMware结束


Linux开始

打通ssh

ssh-keygen -t rsa

ssh-copy-id -i ~/.ssh/id_rsa.pub   #这一步骤, 其实就是将生成的public_key加到id_rsa.pub文件里
cmd直接连接linux, sftp上传下载文件
sftp -o port=30107 jt_bsdm@10.153.81.27
Tk@jt_bsdm596cc0dd

ssh -p 30107 jt_bsdm@10.153.81.27
Tk@jt_bsdm596cc0dd

进入到本地文件路径
lcd windows路径
进入到服务器路径
cd linux路径
将本地的test.jar上传到服务器的root文件夹下
put test.jar /home/root
下载服务器的文件到本地, 注意他会下载到你当前所在的本地路径下, 也就是你lls下当前的路径
get xx.file 
lls

Linux结束


ZK开始

安装集群ZK失败

Invalid config, exiting abnormally org.apache.zookeeper.server.quorum.Quorum

Error contacting service. It is probably not running

或者zk提示started但是实际没启动, 或者启动起来了, 后台进程有id, 但是一查询zk的status就提示zk没运行

解决办法:

1,删除zk的安装目录, 重新解压

2,创建data目录, 里边创建myid

​ 服务器1: echo 1 > myid 服务器2: echo 2 > myid 服务器3: echo 3 > myid

3, 修改conf里的zoo.cfg文件的datadir为 zk安装目录/data, 添加server信息

​ server.1=hong3:2888:3888
​ server.2=hong2:2888:3888
​ server.3=hong1:2888:3888

4, 三台服务器都修改完后, zkServer.sh start | status

ZK结束


数据库开始

数据库密钥生成

java  -cp  druid-1.0.31.jar com.alibaba.druid.filter.config.ConfigTools  数据库密码
mysqldump -h 192.168.1.1 -uroot -p123456 bifrost(数据库的名字) > all.sql

提前新建好bifrost-copy数据库
mysql -h 192.168.1.1 -uroot -p123465 bifrost-copy < all.sql

数据库结束


多线程开始

TD线程池的工具类	
	拒绝策略使用: new ThreadPoolExecutor.CallerRunsPolicy();
	队列使用: new LinkedBlockingDeque<>(Integer.MAX_VALUE / 2000);
当线程存在关联关系, 先后顺后的的时候使用CompletableFuture, 异步编排

CompletableFuture 简单使用

package com.atguigu.gulimall.search.thread;

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.*;

@Slf4j
public class CompletableFutureTest {
    static ThreadPoolExecutor executors = new ThreadPoolExecutor(5, 10,
            10, TimeUnit.MILLISECONDS,
            new LinkedBlockingDeque<>(Integer.MAX_VALUE / 2000));


    public static void main(String[] args) throws ExecutionException, InterruptedException {
        log.info("main start ---");

        //线程串行化
        //1, thenRunAsync 不能获取到上一步的执行结果
        CompletableFuture.supplyAsync(() -> {
            log.info("current thread is :" + Thread.currentThread().getId());
            int i = 10 / 2;
            return i;
        }, executors).thenRunAsync(() -> {
            log.warn(" future1 new Thread is running :{}", Thread.currentThread().getName());
        });
        //2, thenAcceptAsync, 可以获取到上一次执行的结果
        CompletableFuture.supplyAsync(() -> {
            log.info("current thread is :" + Thread.currentThread().getId());
            int i = 10 / 2;
            return i;
        }, executors).thenAcceptAsync(res -> {
            log.warn(" future2 new Thread is running :{}, thread result is :{}", Thread.currentThread().getName(), res);
        }, executors);


        log.info("main end ---");

    }
}

CompletableFuture 等待两个线程执行完毕后执行

package com.atguigu.gulimall.search.thread;

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.*;

@Slf4j
public class CompletableFutureTest2 {
    static ThreadPoolExecutor executors = new ThreadPoolExecutor(5, 10,
            10, TimeUnit.MILLISECONDS,
            new LinkedBlockingDeque<>(Integer.MAX_VALUE / 2000));


    /*
    thenCombine: 组合两个future, 获取两个futrue的返回结果, 并返回当前任务的返回值
    thenAcceptBoth: 组合两个future, 获取两个future任务的返回结果, 然后处理任务, 没有返回值
    runAfterBoth: 组合两个futrue, 不需要获取futrue的结果, 只需要两个futrue处理完任务后, 处理该任务
     */
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        log.info("main start ---");

        //两个任务都完成后, 得到两个的结果, 再执行第三个任务, 将结果返回出去
        CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> {
            log.warn("job1  is :" + Thread.currentThread().getId());
            return "job1 end";
        }, executors);

        CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
            log.warn("job2  is :" + Thread.currentThread().getId());
            return "job2 end";
        }, executors);

        //不感知上次任务执行的返回值
        future1.runAfterBothAsync(future2,()->{
            log.warn("job3 is :" + Thread.currentThread().getId());
        },executors);

        //获取到上几次任务的返回值
        future1.thenAcceptBothAsync(future2,(res1,res2)->{
            log.warn("future1 result is :{}, future2 result is :{}",res1,res2);
            log.warn("job3 is :" + Thread.currentThread().getId());
        });

        //得到上几次线程的结果, 并且执行当前线程, 返回最终结果
        CompletableFuture<String> futrue3 = future1.thenCombineAsync(future2, (res1, res2) -> {
            return res1 + res2 + "job3 end";
        }, executors);
        log.warn(futrue3.get());
        log.info("main end ---");

    }
}

CompletableFuture 等待两个任务中任意一个执行完成后执行,

package com.atguigu.gulimall.search.thread;

import ch.qos.logback.core.util.TimeUtil;
import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.*;

@Slf4j
public class CompletableFutureTest3 {
    static ThreadPoolExecutor executors = new ThreadPoolExecutor(5, 10,
            10, TimeUnit.MILLISECONDS,
            new LinkedBlockingDeque<>(Integer.MAX_VALUE / 2000));


    /*
    applyToEither: 两个任务有一个执行完成, 获取他的返回值, 处理任务并[有新的返回值]
    acceptEither: 两个任务有一个执行完成, 获取他的返回值, 处理任务, [没有新的返回值]
    runAfterEither: 两个任务有一个执行完成, 不需要获取future的结果, 处理任务, 也没有返回值
     */
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        log.info("main start ---");

        //两个任务都完成后, 得到两个的结果, 再执行第三个任务, 将结果返回出去
        //注意这里必须保证, job1 和job2 都需要有相同类型的返回值, 可以使用Object
        CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> {
            log.warn("job1  is :" + Thread.currentThread().getId());
            return "job1 end";
        }, executors);

        CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            log.warn("job2  is :" + Thread.currentThread().getId());
            return "job2 end";
        }, executors);

        //两个任务只要有一个任务执行完成, 就执行任务
        //不感知结果, 自己也没有返回值
        future1.runAfterEitherAsync(future2,()->{
            log.warn("job3 is running end");
        },executors);


        //感知结果, 自己也没有返回值
        future1.acceptEitherAsync(future2,(res)->{
            log.warn("上几次任务执行的结果:{}"+res);
            log.warn("job3 is running end");
        },executors);

        //感知结果, 自己有返回值
        CompletableFuture<String> future3 = future1.applyToEitherAsync(future2, result -> {
            return result + "job3 end..";
        }, executors);
        log.warn(future3.get());

        log.info("main end ---");

    }
}

CompletableFuture 多任务编排

package com.atguigu.gulimall.search.thread;

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.*;

@Slf4j
public class CompletableFutureTest4 {
    static ThreadPoolExecutor executors = new ThreadPoolExecutor(5, 10,
            10, TimeUnit.MILLISECONDS,
            new LinkedBlockingDeque<>(Integer.MAX_VALUE / 2000));


    /*
    多任务组合:
        allOf: 等待所有任务完成
        anyOf: 只要有一个任务完成
     */
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        log.info("main start ---");

        //两个任务都完成后, 得到两个的结果, 再执行第三个任务, 将结果返回出去
        //注意这里必须保证, job1 和job2 都需要有相同类型的返回值, 可以使用Object
        CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> {
            log.warn("查询商品图片信息完成");
            return "hello.jpg";
        }, executors);

        CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
            log.warn("查询商品属性");
            return "黑色+256G";
        }, executors);

        CompletableFuture<String> future3 = CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            log.warn("查询商品介绍");
            return "华为";
        }, executors);

        //
        CompletableFuture<Void> allFuture = CompletableFuture.allOf(future1, future2, future3);
        allFuture.get(); //阻塞等待所有结果完成
        log.info("all Thread end :{},{},{}",future1.get(),future2.get(),future3.get());


        CompletableFuture<Object> anyFuture = CompletableFuture.anyOf(future1, future2, future3);
        anyFuture.get(); //阻塞等待所有结果完成
        log.warn("any Thread end --- :{}",anyFuture.get());

        log.info("main end ---",future1.get(),future2.get(),future3.get());

    }
}

多线程结束


docker开始

docker 启动es
docker run -d --name es-tk -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" docker.elastic.co/elasticsearch/elasticsearch:7.4.2



docker run --name kibana-tk -e ELASTICSEARCH_HOSTS=http://192.168.19.128:9200 -p 5601:5601 -d docker.io/kibana:7.4.2 

docker结束


Maven开始

Mave 查看依赖结构

可以查看mven的依赖结构, 方便排除某些包, 比如当初log4j漏洞的时候, 可以用这个看看你的项目到底是否还有低版本的log4j的jar
查看依赖结构树 mvn dependency:tree > tree.txt
参考文章: https://www.cnblogs.com/ptqueen/p/7985198.html

Maven打包

maven的打包也很重要, 不然即使你已经写正确了程序, 但是因为打包没打好结果造成程序调用失败,岂不是亏了
将所有的依赖都打进jar包里

<build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-assembly-plugin</artifactId>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>single</goal>
                        </goals>
                        <configuration>
                            <archive>
                                <manifest>
                                    <mainClass>
                                        cn.tongdun.test.AppMain
                                    </mainClass>
                                </manifest>
                            </archive>
                            <descriptorRefs>
                                <descriptorRef>jar-with-dependencies</descriptorRef>
                            </descriptorRefs>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>8</source>
                    <target>8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>

maven范围

  • compile:编译、测试、运行都会依赖,会打进包中。
  • rumtime:不存于编译,后期运行和测试都会参与,会打进包中。
  • test:只在test classpath下
  • provider:提供编译期使用,但是不会打进包中
  • import:下面说到了子项目使用父项目的dependencyManagement,但是这种情况需要继承,但是在maven中也体现了面向对象的思想,只能继承一个。那么就无法对另一个依赖做依赖管理了。
  • maven提供了组合形式:import,可以将另一个依赖导入进来做依赖管理:

Maven 结束

大数据开始

livy

livy设置kerberos认证

livy启动的时候就会进行kerberos认证
livy.server.launch.kerberos.principal=root@test.COM
livy.server.launch.kerberos.keytab=/data/test.keytab

让livy进行kerberos认证, 可以不设置, 主要是上边两个
livy.server.auth.type=kerberos
livy.server.auth.kerberos.principal=HTTP/root@test.COM
livy.server.auth.kerberos.keytab=/data/test.keytab
livy.impersonation.enabled=true

大数据结束


  • 技术笔记 2 遍
  • CSDN 技术博客 3 篇
  • 习的 vlog 视频 1 个

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值