Demo项目

  • 1、
  • 2、
  • 3、
  • 4、
  • 5、
  • 6、
  • 7、
  • 8、
  • 9、
  • 10、
  • 11、
  • 12、
  • 13、
  • 14、
  • 15、
  • 16、
  • 17、
  • 18、
  • 19、
  • 20、
  • 21、
  • 22、 反射
  • 23、 选择mybatis的原因
  • 24、 mybatis是如何编程的
  • 25、 #{}和${}的区别
  • 26、 mybatis中的mapper传递多个参数
  • 27、 Kafka消息队列的选择:(解耦,异步,削峰)
  • 28、 本地缓存caffine和guava
  • 29、 QuartZ任务调度
  • 30、 权限认证springSecurity
  • 31、springmvc中的常用注解
  • 32、 springMVC的执行流程
  • 33、 请求的顺序
  • 34、 mybatis的一级缓存,二级缓存

1、

  1. 邮件发送:spring集成了发送邮件的功能,可以使用spring-email,可以发送html格式的邮件(图片,链接,文字就都有了)
    1). 先进入qq邮箱,在设置里面,拿到授权码(来代替邮箱的密码)
    2). 导入pom依赖
    3). 在yml中配置邮箱参数(主要是mailproperties类中的内容,有协议,域名,端口,发件人,密码等等)
    4). 使用JavaMailSender发送邮件,mileMessage构建发送模板,然后send发送,JavaMailSender.send(user,title,content)
    5). 如果发送的是html页面,JavaMailSender.sendMail(user,title,模板引擎);TemplateEngiue.process(“html模板”,数据)
  2. 生成验证码:通过kaptcha工具生成验证码图片,发送到前端(其实也可以利用邮件发送验证码)
    1). 导包
    2). 做kaptcha的配置(验证码的宽高,颜色,字体大小,验证码的个数,验证码的范围)
    3). 生成随机数和图片:kaptcha里的核心是produce接口,有一个实现类defaultKapcha,通过实现类拿到配置信息,然后通过实现类调用创建text的方法,再调用将text变成图片的方法,最后将图片通过response返回给前端,同时还要保存一份text信息在服务器端用于验证前端传过来的数据
  3. 验证码存在session中,但是redis分布式部署时会出现session共享的问题,分布式部署就是客户端的访问量很大,服务器需要部署多台服务器处理客户端的请求,那就会session不共享的问题,可以用粘性session来处理(session分布不均匀),也可以单独创建一个只存储session的服务器,也可以同步session(因为要同步所以效率比较差)
  4. 验证码作为高频过期删除的数据存储在redis中,存储在session中在分布式部署时就有数据不共享的出现
  5. MD5加密:spring中默认集成了MD5,也可以直接使用apacha下的MD5加密
    1). 工具类DigestUtils.md5DigestAsHex(bytes[])
    2). apache下的工具类DigestUtils.md3AsHex(string)
    3). md5加密算法用的是一种哈希算法,他是一个单向密码机制,只有明文到密文的不可逆映射,不同长度的明文都被映射为相同长度128位bit的密文,理想情况下不会有哈希碰撞的出现
    4). 最好是用加盐加密的方式,更加安全
    4).使用md5还可以实现数字签名,一个txt文件加密后生成md5,若中途被修改,就会生成不同于之前的md5

2、

  1. 插入数据时,获得数据库中自增的主键ID
    1). 如果使用sql语句插入数据,在配置文件中设置generatedkey=true,在动态sql中加入keyProperties=“主键”
    2). 如果使用注解的方式插入sql,直接加入@optionals注解,参数设置为generatedKey=true,keyProperties=“主键”
  2. 登录的流程
    1). 先验证码(存储在redis中),再验证账号,密码
    2). 成功时,创建loginTicket(包括当前用户的Id,状态,过期时间等)返回登录凭证ticket(存储在redis中)发送给客户端保存到cookie中,
    3). CSRF攻击:用token避免,同时将ticket存储到reids中,但是这是不安全的,当不良网站获取cookie中的ticket时,就会伪装成你的身份去做不好的事,所以返回ticket时在浏览器默认创建隐藏的input的标签是tocken(这是有form表单的时候),token也会保存在请求头中,但是token不会被其他不良网站获取因为不会被保存在本地(token的格式k+v),对于异步请求来说没有form表单,发送异步请求时在header中生成csrf令牌key+value,将token通过请求头发送给浏览器(token的value应该是随机数)(如果不使用CSRF就可以禁用这个)
    3). 失败时,跳回到登录页面
    4).在项目中,使用的是security的授权功能,但是授权是基于认证的,但是项目已经做好了认证的登入登出,所以对认证的环节做了特殊处理,在拦截器中根据登录的方式选择了userpasswotdToken存储认证的信息,然后放到securityContext中,因为security是基于filter的,filter的执行顺序大于过滤器和controller,所以认证是基于上一次的拦截器处理,比如现在成功登录,重定向到index页面,因为这个页面不需要用户权限,也就不需要认证,所以执行到了itercepter拦截器这一步,将认证的用户信息存入securityContext中,再去访问点赞等功能时,会先filter过滤从context中拿到认证的结果,判断有了权限。
  3. 用户未登录时,拒绝访问别的页面:使用拦截器+自定义注解来实现(现在使用security实现:自定义配置类实现webSecurityAdpter接口,重写方法,当用户访问路径用户还未登录时,返回不允许登录,这有两种结果:JSON格式,页面(时根据请求头有没有携带x-requested,有的话就是XML格式,没有就是页面))
    1). 首先自定义注解,在所有需要拦截的方法上加入注解
    2). 配置拦截器,在请求处理前就需要使用反射拿到注解,判断用户是否已经登录;如果未登录不允许访问当前资源
  4. 使用拦截器显示用户的登录信息(不同状态的用户登录状态应该是不同的,用拦截器可以对多个页面uo同意的拦截处理)
    1). 直接继承wevmvcConfig接口,直接使用拦截器(exclude不处理哪些请求;add处理哪些请求)
    2). 这个拦截器要求实现handlerIntercepter接口的实例(所以自定义配置类,重写方法,在请求前做能处理,在请求处理后渲染模板引擎前做处理,在请求处理后渲染模板引擎处理后做处理)
    3). 在处理请求前做的处理是拿到请求cookie中的登录凭证,确定登录对象,放入threadlocal在多线程并发下数据处理也没有问题
  5. 引入security时,浏览器发送form请求时,会有一个隐藏的token对象生成随着cookie返回给浏览器,
    但是对于异步请求来说没有form表单,发送异步请求时在heade中生成令牌的keyvalue,然后将令牌放到请求的消息头中发送给浏览器

  1. 当前用户具备哪些权限,如何体现?
    1). 用户-角色-权限,本来user表中的type字段可以表现用户的角色,但是使用security时需要直观的看到用户的权限,所以需要继承
  2. AuthenticationManage是认证的核心接口;AuthenticationManageBudiler用于构建认证接口;ProviderManage是认证接口的实现类;providerManage持有一组AuthenticationProvider,每个AuthenticationProvider实现一种认证方式(刷脸,密码,第三方)providerManage并不自己实现认证;
    Authentication用于封装认证信息的接口
  3. 认证成功会将结果存入SecurityContext中
  4. 权限控制:
    1)之前使用拦截器做登录检查是否登录,现在使用权限控制
    2)根据用的登录的角色分配不同的权限(授权)
    3)已经做好了认证,使用原来的认证
  5. 授权是基于认证的token的,认证成功后将认证的信息存入token中,token被filter获取到,然后filter将token存入securityContext中(之后的授权都是从context中拿到token,然后做判断的)
  6. 授权在securityConfig文件中配置好,下面需要配置认证,先在userService中给登录的用户和角色做适配,在loginIntercepter的把用户存入threadlocal的方法将用户以及用户角色存入securityContext中

3、

  1. 上传图片类似于上传文件:
  2. 过滤敏感词:使用自定义前缀树(特点:根节点是空的,一个结点就一个字符;子节点包含的字符串是不相同的;从根节点到末子节点都构成敏感词)
    1). 定义前缀树的结构
    +.±3 2). 根据敏感词,实例化前缀树(加入postconstruct注解-在容器实例化bean调用构造器之后,调用该初始化方法)
    3). 搞三个指针,指针移动,用stringbuilder标记字符(条件是指针二越界)

4、

  1. 发布帖子:使用fastjson实现跨语言,跨前后端
  2. starter启动器:第三方框架,导入依赖就可以使用,做好了一些自动化的默认配置
    1). 编写properties类,将propeties文件中的属性注入到这个实例中@configurationproperties
    2). 写service方法
    3). 编写自动化配置类,将propeties类注入,拿到属性值注入到service中,@conditiononclass(service.class)
    4). 在classpath路径下写上spring.factories文件,项目启动时会自动加载这个而文件,每个启动其中都有这个文件
    5). install安装即可

5、

  1. fastjson:是一个Java库,可以调用对象序列化为JSON字符串的API,也有JSON字符串反序列化为对象的API
    1).将对象序列化为JSON,使用的是对象的get方法
    2).将JSON反序列化为对象,使用的是set方法
    3).也可以将对象直接定义成JSON格式:JSON.JsonArray相当于list数组;JSON.JsonObject相当于map集合,最后Json.toJsonString转为json格式
    4).还提供了一个注解@jsonField可以用在字段上,get/set方法上(注解上的属性name,序列化,反序列化等)
  2. 事务管理(因为事务的隔离性造成的问题):第一类丢失更新:一个事务的回滚影响到了另一个事务的更新操作;第二类丢失更新:一个事务的提交影响了另一个事务的更新操作;脏读,幻读,不可重复度(一个事务两次读取数据不一致)
    0). 听说编程式事务需要手动开启,而且业务代码和事务代码是在一起的,这耦合性就不行了
    1). spring中的声明式事务,基于注解和配置,spring并不直接管理事务而是通过transcationManager事务管理器
    2). 和事务管理器有关的三个接口,plateformTransacitionManager(中有三个方法,拿到事务,回滚事务,提交事务),transactionDefault方法只要定义了事务的基本属性(事务的传播特性,隔离级别,过期时间,是否是只读事务等等),transactionStatus事务的状态(事务的回滚点,是否是只会滚等等)
    3). 事务是基于AOP动态代理来实现的,当在一个public方法上加入注解后,会将当前类生成动态代理类,加入注解就会在目标方法前开启事务,回滚提交
    4). 事务会失效,自调用当前类没有注解的方法调用注解的方法,因为AOP代理,可以再注入一次自己
    5). 在添加评论时要开启事务,评论添加,帖子的评论也要添加
  3. mybatis中的#和$
    1). #相当于?占位符替换,先预编译再用双引号取值,是sql拼接再编译2).所以是sql拼接再编译 2).所以是sql拼接再编译2).所以有sql注入的问题(or 1=1),#没有这种问题

6、

  1. in和exists的区别:
    1). in是以子表为驱动表,先查询子表,再和主表做笛卡尔积,最后做筛选,所以in适合外表大有索引的查询
    2). exists以主表为驱动表,查询主表,loop循环和子表做true/false判断,所以exists适合子表大有索引的情况

7、

1. <!--    int updateMessageStatus(List<Integer> ids,int status);-->z<update id="updateMessageStatus">update message set status=#{status} where id in<foreach collection="ids" item="id" open="(" separator="," close=")">#{id}</foreach>
2. 统一异常处理:(一层层往上抛,最后到controller层)1). 使用try..catch代码块一个是代码冗余,再一个是业务代码和处理错误的代码定义到一起了,影响可读性,所以要用统一异常处理2). spring3中新增的controllerAdvice控制器增强作用在类上,在类中定义处理异常的方法,在方法上加入exceptionHandler注解,可以自定义捕获的异常类型,这样一来就可以对不同阶段的不同异常做处理3). 不同阶段:进入controller前的异常:404,参数校验异常,参数绑定异常;进入controller后的异常:数据库链接,IO异常等4). springboot中,在template文件夹下默认有error文件夹,容器自动将错误映射到404,500页面上  5).服务器出错可以直接跳转到错误界面,但是还需要记录日志,且返回的可能不一定都是界面还可能是json格式,所以需要自定义controller类@ControllerAdvice
public class ControllerAdvice1 {@ExceptionHandler(Exception.class)public void error(Exception e, HttpServletRequest request, HttpServletResponse response) throws IOException {System.out.println("我是服务器的错误:"+e.getMessage());StackTraceElement[] stackTrace = e.getStackTrace();for (StackTraceElement es:stackTrace) {System.out.println("我是更加详细的服务器异常:"+es.toString());}//浏览器访问服务器时,要求是异步请求json.还是页面String requestHeader = request.getHeader("x-requested-with");if("XMLHttpRequest".equals(requestHeader)){response.setContentType("application/json");PrintWriter writer = response.getWriter();writer.write(CommunityUtils.getJson(1,"服务器端出错了!!!!"));}else{response.sendRedirect(request.getContextPath()+"/error");}}
}

8、

2022-01-25
1. 使用AOP实现统一日志处理1). AOP思想的实现框架:AspectJ,在编译期将增强织入,有专门的编译器,生成符合Java规范的class文件2). Spring AOP:在运行期通过代理类将增强织入,只支持方法类型的连接点joinpoint3).拦截器也是AOP思想的一种实现3). spring AOP在运行期通过代理织入的两种代理类型:JDK动态代理,cglib代理4). JDK代理要求目标对象必须是接口,在接口的目标实例中织入代码5). cglib动态代理:当目标对象不是接口时,就会创建目标对象的代理对象进行织入增强
-----------------------------------------‘
统一异常页可以用AOP做,统一日志处理也可以用AOP做,拦截器其实也是AOP做的
------------------------------------------6). JDK动态代理的具体实现:1.自定义接口委托类 2.然后自定义实现类代理功能也就是继承InvocationHandler接口的类,实现invoke方法,参数为代理类,被代理的方法,以及参数 3.创建被代理的类通过proxy.newInstance方法,需要的参数有classlocader类加载器以及interface一组接口,所以要使用JDK动态代理要求委托类必须是接口类7). JDK动态代理的两个类:invocationHandler,委托接口类,proxy代理类8). SpringAOP术语:Aspect切面需要在运行期将将通知织入增强到切点对应的连接点上
@Component
@Aspect
public class AopAspectJ {//定义切点,对目标对象的哪个方法做织入,*表示返回值为任何类型,@Pointcut("execution(* com.taiji.disscution.service.*.*(..))")header 1 | header 2
---|---
row 1 col 1 | row 1 col 2
row 2 col 1 | row 2 col 2public void point(){}//通知@Before("point()")public void before(JoinPoint joinPoint){//用户IP,在yyyy-MM-dd时刻,执行了xxx方法ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();HttpServletRequest request = requestAttributes.getRequest();//拿到IPString remoteHost = request.getRemoteHost();//拿到当前日期Date date = new Date(System.currentTimeMillis());String dateNow = new SimpleDateFormat("yyyy-MM-dd").format(date);//拿到包名+类名String target = joinPoint.getSignature().getDeclaringType() + "." + joinPoint.getSignature().getName();System.out.println(String.format("用户[%s],在[%s]时刻,执行了[%s]方法", remoteHost, dateNow, target));}
}

9、

```Java
2022-01-26
1. redis的使用场景:做缓存(访问的数据很频繁),排行表(热点排行,访问频繁的数据),计数器,社交网络(访问频繁的热门数据),做消息队列
2. redis的多种数据结构,存储方式rdb,Aof,应用场景,常用命令1). SDS:简单动态字符串,相比起C中的字符串除了有字符类型的数组还有表示数组的length长度以及free空闲空间2). SDS中的len可以杜绝缓冲区溢出的问题:对于String来说,做字符串拼接可能会有覆盖原来不需要修改的字符串,造成缓冲区溢出,但是有了SD就可以通过len判断是否需要申请内存3). SDS中的free,可以减少因为字符串拼接造成的申请内存空间的次数4). SDS二进制安全的,通过的String,只能存储普通的字符,但是SDS可以存储空格,转义这种字符,也就是可以存储二进制,图片,视频,存储类型更加多样5). list链表:无环双端链表(左右都可以入栈,弹栈),相比于普通链表锁了len长度,还有一些函数(复制节点值,移除节点值等)6). 字典:字典包括哈希表数组,数组的大小,数组已使用结点的数量7). map插入新数据时先计算哈希值,如果哈希碰撞就采用链地址法,当数据扩展哈希表中的数组无法满足再次插入数据时,就会rehash,搞一个新的哈希表长度是原来的2的n次方,然后通过重新计算哈希值进行数据转移,释放原来的哈希表8).zset(有序不重复)的底层数据结构用的是压缩链表和跳表9). 当链表的数量小于128时,或链表每个结点的长度小于64k时,使用压缩链表存储10). 跳表:每两个相邻的结点用一个指针指向,指针指向下下个结点,一次类推,这些指针构成新的链表,长度为原来的一半,查找结点的时间复杂度变为O(logn)相比于原来的O(n)查找效率更高11). 那为什么不使用红黑树或者AVL呢?因为在算法的实现来说,跳表更加好实现;跳表更加适合范围查找,找到小值后,直接从第一次数据一次往后遍历几次就可以找到范围值
3. springboot整合redis的两种方式:jedis和redis template,两者依赖不同1). 对于redis Template引入以来后,在redisAutoConfig配置类中做了配置生成redistemplate和stringredisTempalte两种,但是redisTempalte泛型都是object格式的,而且没有序列化,因为依赖的原理,直接引入的连接池是lettuce-core所以是letture不是jedis
4. redis的事务:事务就是顺序的,排他性的执行队列中的命令(其他客户端的命令不会插入到事务的执行命令中)1). 在multi开启事务之前,执行watch监视keys的命令,如果keys的值发生改变,则命令队列中的所有命令都不会被执行2). watch监视keys,multi标记事务的开始,exec执行队列中的命令,discard放弃执行队列中的所有命令3). 在multi开启事务后,命令一次入队等待执行,但是不会被执行,只有当exec命令后才会执行,也就是无法查看事务中途更新的命令
5. 点赞功能,收到的赞:redis做缓存,点赞取消是高频操作,内存的读写IO要远高于磁盘IO,为了性能考虑redis缓存是比较好的打算(点赞的数据结构是set,存储的是userId,想知道谁点了赞)1). 导入redis依赖,做redis配置2). config配置redisTemplate的序列化方式3). 使用set数据结构,因为要记录点赞的用户ID4). 一次点赞增加赞add,两次点赞取消赞remove,使用set判断isMember是否已经存在了该用户5). 使用事务当add增加时,用户收到的赞也increment增加
redisTemplate.execute(new SessionCallback() {@Overridepublic Object execute(RedisOperations redisOperations) throws DataAccessException {//开始事务支持redisTemplate.setEnableTransactionSupport(true);//watch监视key的变化,如果key发生变化,就不开启事务redisOperations.watch(entityUserLikeKey);//点赞数,第一次点赞就增加赞,第二次点赞就取消赞,要放在开启事务之前,因为在开启事务后,查询不到Boolean isMember = redisOperations.opsForSet().isMember(likeKeys, userId);redisOperations.multi();if(isMember){redisOperations.opsForSet().remove(likeKeys,userId);redisOperations.opsForValue().decrement(entityUserLikeKey);}else{ redisOperations.opsForSet().add(likeKeys,userId);redisOperations.opsForValue().increment(entityUserLikeKey);}return null;}});
6. 开发点赞,关注功能,并且展示在个人主页上(其中关注功能包括关注列表,粉丝列表)1). set中判断key是否存在用isMember2). zset中判断key是或否存在用score3). zcard相当于size求长度4). 本来实体包括帖子,回复,用户,但是现在只做了对用户的关注等
7. redis的数据持久化也可以使用QuartZ动态定时任务,将数据持久化到DB中

10、

2022-02-02
1. 使用redis存储验证码,登录凭证,用户信息,因为是高频数据,而且不需要长期存储,可以过期删除,而且使用redis存储还解决了session的共享问题
2. redis中的数据怎么没了?当达到了redis的最大存储量时,就会自然丢弃不常用的数据
3. redis中的数据明明都过期了,查也查不到了,怎么还在占用内存(答案就是过期策略)
4. redis中的过期策略:1).直接在set key value时,设置过期时间2).那么当到达过期时间后,redis是怎么删除过期key的:定时删除(每隔一段时间都会随机检查设置了过期时间的key,过期就会被删除,但是当很大的数据量都设置了过期时间,通过定时删除无法删干净,就用到惰性删除了),惰性删除(惰性删除就是在获取key时,先检查过期时间,如果过期直接删除,不会返回值)3).那如果定时删除也没有删掉,惰性删除也没有删掉,如何处理大量的过期key呢?答:使用删除策略4).一共有6中删除策略:删除最新的key,删除最近最少使用的key,删除随机的key,删除设置了过期时间的随机key,删除设置了过期时间的最少使用的key
5.手写LRU算法:public class LRUCache<K,V> extends LinkedHashMap<K,V>{private final int CACHE_SIZE;//传进来的参数就是最多能缓存的数据public LRUCache(int cacheSize){//最后的参数就是让linkedhashmap按照顺序进行排序,最先访问的在前面,最老访问的在尾部super((int) Math.ceil(cacheSize/0.75)+1,0.75f,true);CACHE_SIZE = cacheSize;}@Overrideprotected boolean removeEldestEntry(Map.Entry eldest){return size()>CACHE_SIZE;//当map中的数量大于指定缓存的个数时,删除最老的数据}
5. redis存储验证码,登录凭证,用户信息 1).验证码的过期时间前端和后端同时设置为60s,因为是高频数据,过期删除,所以使用redis2).登录凭证在用户登录时生成,放入数据库存储,每次做操作前,都会使用拦截器验证登录凭证,现在需要放入redis中,因为没必要一直存储到数据库中,因为当用户退出登录后,登录凭证随之消失3).
6. redis基本的工作原理:1).单线程IO多路复用的线程模型2).单线程是因为文件事件处理器是一整个组件,是单线程的3).客户端和redis的单线程交互:客户端首先和redis建立连接创建server socket套接字,然后产生AE_REABLE事件,通过IO多路复用处理器对事件做轮询处理压入队列,然后文件事件分派器根据socket对应的事件来让不同的文件事件处理器做处理(连接处理器,命令处理器,应答处理器)首先是连接处理器,依次类推。。。4).单线程效率还高:(1)非阻塞的IO多路复用,IO多路复用负责监听产生事件的socket然后压入队列,事件是由不同的文件事件处理器来处理的(2)
7. redis和memchached的区别:1).redis支持的数据结构类型相比于memchached更多,所以适合多种应用场景2).redis是单线程的3).redis原生就支持集群模式,而mechached需要借助一些客户端(不支持原生的cluster)
8. redis缓存的持久化机制:1). RDB快照文件,会记录某一时刻数据的状态,缺点是无法记录最近一次跟新的数据记录2). AOF持久化文件,记录的是每条操作命令,使用AOF做恢复时,会依次执行这些命令,但是使用set命令时,记录的有最终态数据和中间态数据,当中间态数据太大时AOF文件太大,就会触发rewrite覆盖技术,自动清理中间态数据3). 现在大多数采用的RDB+AOF,当到达最大内存值时,会清理所有的持久化文件,先使用RDB做快照持久化备份,之后再使用AOF记录追加的操作命令

11、

2022-02-05
1. 使用kafka来处理系统发送的消息
2. 线程通信使用:wait/nodify或者阻塞队列(满足生产消费者模式,使用阻塞队列是为了在两者间建立缓冲,让生产者线程和消费者线程达到平衡,让CPU的利用率提高),阻塞队列的实现类:ArrayBlockingQueue,LinkedBlockingQueue,SychrinoziedQueue
3. 生产者往阻塞队列中存入数据时使用put,当队列已满,方法被阻塞,take同理
4. ArrayBlockingQueue实现异步发送消息示例:public class BlockingQueueTest {public static void main(String[] args) {BlockingQueue queue = new ArrayBlockingQueue(10);new Thread(new producer(queue)).start();new Thread(new customer(queue)).start();new Thread(new customer(queue)).start();}
}
class producer implements Runnable{private BlockingQueue queue;public producer(BlockingQueue queue){this.queue=queue;}@Overridepublic void run() {try {for (int i=0;i<100;i++) {queue.put(i);System.out.println("当前生产线程"+Thread.currentThread()+"阻塞队列的长度"+queue.size());Thread.sleep(20);}} catch (InterruptedException e) {e.printStackTrace();}}
}
class customer implements Runnable{private BlockingQueue queue;public customer(BlockingQueue queue){this.queue=queue;}@Overridepublic void run() {try {while (true){queue.take();System.out.println("当前消费线程"+Thread.currentThread()+"阻塞队列的长度"+queue.size());Thread.sleep(new Random().nextInt(1000));}} catch (InterruptedException e) {e.printStackTrace();}}
}

12、

2022-02-06

  1. 此系统使用消息队列的原因:主要功能包括点赞,评论,关注可以用kafka中的topic去处理,而且线程在完成点赞,评论后把消息写入消息队列后,可以去处理别的业务,消费者直接去消费消息即可,这种异步操作,系统的效率更高
  2. kafka真正实现的时候用的是:事件(点赞,评论等)驱动业务,所以需要封装事件,生产的是事件,消费的也是事件
  3. 同时用户置顶,加精改变帖子状态,要更新到ES服务器中时,也是放到消息队列kafka中的
  4. 拿点赞举例:生产者就是触发了一个点赞事件,把点赞的用户,主题,给那种实体点赞,点赞的实体属于那个用户都放到事件中,把事件放到消息队列中,消费者消费就是拿到事件中的内容,然后插入数据到消息表中
  5. 总的消息显示:包括私信消息和通知消息,使用拦截器因为每次请求都可能会对消息有影响所以使用拦截器
  6. Broker:消息的中转站,把消息转给不同的队列去处理,一个broker相当于一个kafka结点,这些broker会组成集群/zookeeper:管理kafka的集群/topic:对消息进行归类/partition:对topic的分区(分区是为了提高容错率,通过副本的方式)/offset:分区中存放数据的索引位置/leader:主副本/follow:从副本

13、

  1. 启动zookeeper
  2. 启动kafka
  3. 创建主题topic,表示位置,同时表示消息的分类
  4. 以生产者的身份向主题发送消息
  5. 新开cmd,以消费者的身份消费主题中的消息
  6. 关闭zookeeper和kafka时,不能直接关闭命令行窗口,需要执行stop命令来关闭
  7. 先关闭zookeeper
  8. 然后再关闭kafka-server-stop.bat

14、

  1. kafka消息队列满足生产者消费者模式,所以访问kafka时生产者kafkaTemplate.send(topic,“message”)
  2. kafka的消费者
    @kafkaLisenter(topic={“topic的名字”},还可以指定不同主题,不同分区,从哪个偏移量开始消费)
    public void handlerMessage(ConsumerRecord record){}
  3. 项目使用消息队列kafka的原因:论坛系统人数基数大,而且功能是点赞,评论,关注,可以使用主题topic进行区分
  4. 使用kafka解决问题:先要触发事件(点赞,评论,关注),然后封装事件,开发事件的生产者和消费者
  5. 为什么使用消息队列:
    1).消息队列异步处理数据,保证系统吞吐量,效率更高,同时还可以使用分布式事务保证最终是成功的(消息队列的通信是异步的)
  6. 生产者发送消息到broker结点的topic主题中(消息会被记录到IP的日志当中),这个过程是有序,通过offset偏移量来描述有序性
  7. 单播消息:多个消费者在同一个组,只能有一个消费者可以消费topic中的消息(就是为了保证消费者消费消息的顺序性)
  8. 多播消息:多个消费者在不同的组,一个组只能有一个消费者消费topic中的消息
  9. 一个消费者可以消费多个partion,尽量保证消费者的数量小于等于partion的数量,不然多余的消费者消费不到消息
  10. 分区:为了避免topic的消息过多过大,提高读写的吞吐量(异步进行的);为了解决高并发的问题kafka默认对cosumer_offset主题创建50个分区用来存储消费者消费主题分区的偏移量(所以副本没有这些分区,且记录最后一次偏移量的位置)
  11. leader:kafka的读写操作都在leader上进行,并且负责将数据同步给follow
  12. 副本:是对分区的备份实现高可用,不同的副本应该放在不同的broker中
  13. ISR:可以同步和已同步的broker结点放到ISR中,如果ISR中的结点性能较差就会被踢出ISR,当leader挂掉之后,从ISR中进行选举
  14. 集群中有多个broker,broker中可以有多个主题,可以为主题创建多个分区,同时在不同的broker中部署为不同分区创建的多个副本

15、

2022-02-08
1. 生产者同步发送消息:生产者发送消息没有收到ACK确认时,会等待3s然后重发,重发3次为止(综上:同步使用更多)
2. 生产者异步发送消息:发送完消息后生产者执行以后的业务,broker异步调用callback函数,如果中途出现网络动荡消息发送失败,就会出现发送消息失败的情况
3. ACK的配置:如果生产者使用同步发送消息,在收到partion的ack确认之前会被一直阻塞,那大概什么时候会返回ACK呢?和ACK的配置有关1)ACK=0,当发送消息给broker后,直接返回ACK,不会阻塞,效率最高,但是不安全2)ACK=1,当发送消息给broker后,当leader接收消息且把消息写进log日志文件后,参会返回ACK3)ACK=-1/all,默认有个min.replices=2(默认为1,推荐配置为大于等于2),表示接收消息后,会有两个broker将消息写进日志log,这种方式最安全,但是性能最差
4. 消息发送的缓冲区:1).Kafka默认创建一个32M的缓冲区2).本地线程一次性拉 满16k的数据作为生产者发送数据,如果数据不满16k,那么就等10ms,然后将拉到数据发送
5. 消费者自动提交和手动提交offset:1).自动提交:消费者poll下来消息后自动提交offset(会有消息丢失的情况出现,消费者挂了)2).手动提交:消费者poll下来消息消费完之后提交offset3).同步手动提交:消费者提交offset后,会被阻塞直到集群clusetr返回ack确认(使用最多)4).异步手动提交:消费者提交offset后,去执行其他的任务
6. 消费者长轮询poll拉取消息1). 默认情况下在1s内最多poll拉取500条消息(可以根据消费者的消费速度做调整),这把又两个局限:1s以内,最多poll500条数据2). 两次poll的时间间隔超过了30s,kafaka就会认为消费者的消费能力过弱,从而踢出消费组,触发reblance(将分区分配给其他的消费者)但是reblance这种机制会造成性能开销,所以可以设置一次poll的条数减少一点
7. 消费者的健康状态检查(心跳机制):如果cluster超过10s,没有收到消费者的心跳机制就会将消费者踢出消费组,触发reblance机制(我们必须保证消费者都在工作)
8. controller的关联知识点:zk分布式锁
9. controller:每个broker都会在zookeeper上创建临时序号节点,最小的序号节点就是controller,作用:1). 在集群中,分区partion的leader挂掉后,controller会选取ISR最左边的broker作为分区leaedr2). controller会监视分区和broker的状态,当发生变化时,controller会通知其他的broker3). ISR存放的是leader和与其同步的broker和已同步的broker(ISR的顺序和同步能力有关)
10. rebalance:当消费组里的消费组新增或者挂了,又或者分区新增或者挂了就会触发rebalance机制,还有一个就是消费者超过10s不发生送心跳机制,消费者组就认为消费者挂了,踢出消费者组1): 分区的分配策略:range,轮询,sticky2). range:公式计算3). 轮询处理4). sticky:在原来分配的基础上继续做处理,使用轮询或range
11. HW:高水位所在的位置是分区和所有副本同步数据后坐在的最后位置,consumer只能消费HW之前的数据,HW是为了保证数据一致性,同时避免重复消费数据

16、

  1. kafka如何防止消息丢失:
    1)生产者同步ACK=1或者-1时都是集群接收消息并且把消息写进日志log中,异步和ACK=0的都容易消息丢失
    2)消费者:自动提交改为手动提交(自动提交:poll拉取下数据来当消费者还没来得及消费数据就挂了,那么数据就丢失了,因为已经提交了offset)
  2. kafka如何防止消息被重复消费:(如何保证幂等性)
    1)对于生产者端因为网络抖动,发送了两次消息,那cluster就有两条相同的消息,然后消费者端就会消费到两条相同的消息
    2)幂等:多次请求访问的结果是一样的,get是幂等的,post是非幂等的
    3)加入消费者端的是新增插入数据操作,每次在插入数据库之前都先插入redis的set中,做一个过滤去重的效果
    4)还有就是唯一键做约束保证不会插入重复数据
    5)分布式锁
  3. kafka不是读写分离的都是在leader中进行的,是因为读写分离有明显的缺点:
    1)数据不一致
    2)延时问题
    4.kafka的如何做到顺序消费:
    1)首先保证生产者的顺序发送数据:使用同步发送并且ACK不等于0是为了避免数据丢失
    2)消费者端保证:只有一个分区且消费者组中只有一个消费者,性能不高,这是单线程性能不高
    3)那么多线程操作下:可以将key相同的数据存储到queue中,不同的queue用不同的线程消费
  4. kafka的消息积压问题:
    1)积压的原因:消费者的消费速度远远小于生产者的生产效率,然后再集群中的消息越过,消费者寻址便困难
    2)解决方案一:使用多线程,再一个通过提升业务性能提升消费能力
    3)解决方案二:增加分区数量,增加消费组和消费者
    6.kafka的延时队列:
  5. kafka的应用场景:是一个分布式的发布订阅系统:用到我的项目上是涉及到了点赞,评论,关注,这些都符合发布订阅场景所以用到了kafka
  6. kafka相比于其他MQ的优点是:可扩展性好可以使用broker结点做成cluster集群,容错性高为分区创建了副本

1.再字符转义上出错,无法实现系统发送消息,但是大致思路应该没问题

17、

  1. ES搜索引擎可以对英文分词,但是需要安装IK分词器
  2. 打开ES服务器时,先检查es的健康状态,查看健康状态: curl -X GET “localhost:9200/_cat/health?v”
  3. ES索引=数据库;ES类型=表;ES文档=一条数据;ES字段=字段(ES6之后废弃了类型,ES索引=表)
  4. ES搜索引擎对词条做全文搜索,这对于数据库来说是做不到的
  5. ES的步骤:
    1).导入依赖
    2).配置文件配置集群名clusterName和集群结点clusterNodes
  6. ES和redis的底层都是基于netty的,两者同时使用会有冲突,进入netty4Utils中可以看到直接抛出异常,所以要在抛出异常之前终止程序的运行
  7. 在实体类中加入注解@Id,@Field可以让ES的字段和实体的属性建立联系
    1).如果要查询的数据在content字段上,就在该字段上加入属性:存储解析器设置为max最大,这样就会创建更多的词条,增加命中率
    2).查询解析器设置为smart最小,比如”互联网春招“,就会按照最小的查菲,分别是互联网和春招
  8. java中有两种直接操作ES服务器的bean:ElasticSearchTemplate和ElasticSearchReposity(底层也是基于template的,里面直接定义好了写好的CRUD方法)
  9. 项目中开发社区搜索功能:
    1).发布帖子或者评论给帖子时,将帖子封装成事件异步提交消息队列
    2).在消费者组件中,增加一个消费发帖事件
    3).ES搜索时:需要构建搜索条件,排序,分页,高亮等,条件构建好之后通过Template或者repoity进行搜索

18、

  1. 置顶,加精,删除,除了要将帖子的状态改变,还要更新将ES中的帖子状态,这一步是通过消息队列实现的
  2. 不同权限的用户看到不同的功能,使用security实现(在实现webSecurityConfigAdapter接口的类中,在config方法中做授权)

19、

  1. 使用HyperLogLog统计日访问量UA(统计IP,包括游客,那种没有被分配ID的用户),适合数据量大,占用空间小(12M)的,不要求太精确的(只能精确到80%)可以使用超级日志
  2. 使用bitmap统计网站的日活跃用户数DAU(日活跃用户,只统计那些有userId的IP),位图可以当作是特殊的字符串,字符数组,存储0或1
  3. Date和String之间的日期转换
    new SimpleDateFormat().format(date)可以将date转为String
    new SimpleDateFormat().parse(String)可以将String转为date

20、

  1. 分布式定时任务:QuartZ,任务的调度和执行肯定和线程池有关
  2. JDK和spring都有线程池,那为什么不用这些呢?
    1)在分布式部署下,定时任务放到内存种无法实现数据共享
    2)使用QuartZ可以将数据放到DB中,可以共享让同一时刻只有一个线程访问(使用DB存储时,需要在排位置文件中做配置本来是memeoy内存,现在改成JDBC数据库)
  3. 热帖排行:用QuartZ来实现:热度和点赞,加精,评论,帖子分数以及发布时间有关
    1)吧发帖,点赞,评论,加精的帖子都存入redis中
    2)使用QuzrtZ定时更新帖子的分数,然后同步到ES中
  4. QuartZ有关的几个概念:
    1)Scheduler:调度器,所有的任务都交给调度器管理
    2)job:执行的任务,业务逻辑
    3)jobDetail:对job的补充,设置一些分组的属性等
    4)Trigger:真正执行任务的
  5. 由job名和组名可以确定唯一的job

21、

  1. redis缓存存放的都是和用户息息相关的数据,比如用户的登录凭证等
  2. 本地缓存相较于redis缓存好的一点是:更快,更方便,没有了网络传输这一步;本地缓存适合存储一些不经常更新的数据(所以把热门数据存到本地缓存中,热门帖子根据分数排序,而分数做了定时任务)
  3. 用户访问的步骤:请求->本地缓存->redis缓存->DB(然后DB将数据更新到本地缓存和redis缓存中)
  4. 在业务层定义从本地缓存获取数据的方法,通过Caffeine.newInstance构建cache缓存,调用caffeine的API时,会自动将load方法的返回值存储到本地缓存中,下一次可以通过key拿到缓存到本地的值

22、 反射

  1. 自定义注解了元注解:@target说明这个注解作用在字段上,方法上,还是类上
  2. @Document说明这个注解可以被文档化
  3. @Inherit说明使用了这个注解类的子类也有相同的作用
  4. @Retention:如果是class说明注解一直被放到类中,直到编译运行;如果是runtime说明这个注解在编译期被反射执行

  1. 定义成功注解之后:如果注解作用在方法上:拿到方法判断方法上时候存在注解isAnnotation;如果有拿到Annotaton注解,再做操作

23、 选择mybatis的原因

  1. 内部封装了JDBC连接,不用频繁的加载驱动,创建连接,大大减少代码量
  2. sql语句放到xml文件中,和业务代码解耦合,便于sql语句的统一管理
  3. 很好的集成各种数据库
  4. 可以和spring整合
  5. 支持实体对象和数据库关系字段的半自动化映射(程序员自己编写sql语句,执行结果映射成java对象)

24、 mybatis是如何编程的

  1. 创建sqlSessionFactory
  2. 通过通常创建sqlSession
  3. sqlession去操作数据库
  4. sqlsession.commit提交事务
  5. sqlsession.close关闭事务

25、 #{}和${}的区别

  1. #{}占位符,预编译处理时会替换成?;${}字符串替换,调用preparedStatement来赋值
  2. 所以#{}可以有效的防止sql注入;${}无法防止sql注入

26、 mybatis中的mapper传递多个参数

  1. dao层的接口参数上加入@param注解
  2. 封装成map

27、 Kafka消息队列的选择:(解耦,异步,削峰)

  1. kafka最大的优点就是吞吐量很高,使用Jmater测试一个线程组(好像是10万条数据来着)60s,服务端处理的事务数TPS就达到了7000/s
  2. kafka支持集群部署,做分区,副本;对主题做分区可以提高并发量,做副本去提高容错率(当leader挂了,controller就去ISR中选择最左边的broker结点做leader,ISR放的都是已经同步了的结点)
  3. 消费者可以实现顺序消费(生产者顺序存放;消费者组中的消费者是单线程的;如果是多线程的把相同key的放到queue队列中,让一个线程去消费)
  4. 消费者可以通过做config配置不让消息丢失
  5. 像推特这种大公司也在使用kafka

  1. 还有消息队列是rocketMQ:github社区活跃度不高,遇到bug可以不易被解决

  1. rabbitMQ的吞吐量不如kafka,而且是基于erlang语言做的,源码也看不懂呀

28、 本地缓存caffine和guava

  1. 市面上就两种,caffine和guava
    2.caffine是在guava的基础上做的二次拓展,我查了一下caffine的读写速率大概是guava的四倍左右
  2. 所以就是用了caffine做本地缓存

29、 QuartZ任务调度

  1. Java自带的timer可以串行化的完成任务调度,到时间有多个任务需要被调度执行,但是同一时刻只有一个线程允许调度任务,效率低下
  2. 后面引入了ScheduledThreadPool支持多线程调度任务,但是不支持根据指定时间做任务调度(5月2日)
  3. springboot2.0开始就集成了QuartZ实现定时任务,QuartZ是将信息持久化到数据库中,并且通过行锁实现同一时刻只允许一个线程去调度任务。为了高可用还支持集群化部署,多节点去做任务调度的时候通过抢占数据库的行锁保证只有一个结点去执行(QuartZ满足基本的任务调度,但是无法实时管理和监控任务,这就可以使用xxl-job)
  4. 好像xxl-job的社区活跃度还蛮高的,我下一步的话可能会学习xxl-job

30、 权限认证springSecurity

  1. springSecurtity的优势就是他是spring家族的,和spring集成的很好,做权限更加方便
  2. springSecurity的社区活跃度更高,说明使用的人多,出现bug后容易解决
  3. springSecurity中还有防止CSRF攻击的token

31、springmvc中的常用注解

  1. @requestMapping:请求路径,可以指定请求方式
  2. @requestParam:请求对象中的参数和controller中的方法参数相对应
  3. @requestBody:从请求体中拿到参数
  4. @pathVariable:请求路上的参数和controller中方法的参数相对应
  5. @responseBody:相应的结果由Java对象变为JSON
  6. @controller
  7. @autowired都是

32、 springMVC的执行流程

  1. 用户发送请求给dispatcherServelt,然后通过处理器映射器找到对应的controller
  2. 用户再次通过DispatcherServlet找到对应的处理器适配器对controller做处理
  3. 返回ModelAndView给dispatcherServlet,然后再通过视图解析解析ModelAndView

33、 请求的顺序

  1. 请求->过滤器->拦截器->controller
  2. 过滤器需要继承webSecurityConfigAdapter,我在过滤器里完成对静态资源的过滤,以及授权(对于不同权限的角色访问不同的资源);过滤器是springSecurity的
  3. 拦截器需要继承webMvcConfig,重写添加拦截器的方法addInterceptor,在方法中做拦截器时需要继承了HandlerInterceptor接口的实现类,实现类中有三个方法分别包括:请求处理前的操作,请求处理后的操作,以及模板引擎处理后的操作

34、 mybatis的一级缓存,二级缓存

  1. 当做写入操作时,先去一级缓存中;清除操作时,先清除一级缓存中的数据;查询操作时,直接查询一级缓存
  2. 一级缓存是HashMap类型的:key放的是sqlId+sql语句,value放的是sql查询映射到的Java对象
  3. 二级缓存默认是不会开启的

总结项目笔记Demo相关推荐

  1. 初出茅庐的小李第113篇博客项目笔记之机智云智能浇花器实战(2)-基础Demo实现

    初出茅庐的小李第112篇博客项目笔记之机智云智能浇花器实战(1)-基础Demo实现 接(1) 继电器实现 继电器原理图 继电器采用的是5V继电器,控制端是RELAY-1 继电器代码实现 #includ ...

  2. 品优购项目笔记(十四):微信支付

    品优购项目笔记(十四) 订单 订单三张表关系 提交订单 二维码 介绍 优势 容错级别 qrious二维码生成插件 微信支付 微信支付流程 项目支付流程 生成支付链接 查询是否支付成功 订单 订单三张表 ...

  3. jimogsh 推荐《Mathematica演示项目笔记》

    jimogsh 同学现在是一名兰州大学四年级的学生,我认识他的时候,他还在念两年级.当时他对 Mathematica 充满了好奇,在论坛中提了许多的问题.后来jimogsh 用 Mathematica ...

  4. 《BI项目笔记》用Excel2013连接和浏览OLAP多维数据集

    <BI项目笔记>用Excel2013连接和浏览OLAP多维数据集 原文:<BI项目笔记>用Excel2013连接和浏览OLAP多维数据集 用Excel2013连接和浏览OLAP ...

  5. 《BI项目笔记》创建标准维度、维度自定义层次结构

    原文:<BI项目笔记>创建标准维度.维度自定义层次结构

  6. 【Andorid X 项目笔记】动态设置ViewPager的Adapter问题(2)

    由于没有找到嵌套FragmentActivity的方法,只好打算用不同的FragmentPagerAdapter来动态切换ViewPager的,如下: /**      * 首页切换的三个界面     ...

  7. 《BI项目笔记》数据源视图设置

    原文:<BI项目笔记>数据源视图设置 目的 数据源视图是物理源数据库和分析维度与多维数据集之间的逻辑数据模型.在创建数据源视图时,需要在源数据库中指定包含创建维度和多维数据集所需要的数据表 ...

  8. 【Andorid X 项目笔记】禁用ListView的Fling功能(1)

    前言 新的项目正在紧张开发中,初步估计2个月时间开发完成第一版,我负责Android端开发,由于不便过早公布,本系列将命名为"X项目笔记",并于项目结束后最终公布名称.本系列主要记 ...

  9. AppFuse项目笔记(1)

    AppFuse项目笔记(1) 一.Appfuse简介 Appfuse是Matt Raible 开发的一个指导性的入门级J2EE框架,它对如何集成流行的Spring.Hibernate.ibatis.s ...

最新文章

  1. 每天九点十分开始每半小时一次执行一个cron_趣讲 PowerJob 超强大的调度层,开始表演真正的技术了...
  2. 中艺人脸识别考勤机使用方法_人脸识别考勤机的使用方法及注意事项 - 全文
  3. 鸿蒙应用学习笔记01:搭建鸿蒙应用开发环境
  4. Java面试之ArrayList为什么线程不安全?
  5. x86架构和arm架构_苹果Mac弃用英特尔芯片成真,ARM架构CPU真比X86香?
  6. Vue3动态组件、缓存组件、分发组件
  7. C# DataTable Compute方法的使用
  8. 编译安装PHP出现Cannot load /usr/local/apache/modules/libphp5.so
  9. 基于人脸识别的课堂签到管理系统(五)---启动/结束签到,以及在百度智能云创建用户组
  10. Java语法总结 - 方法
  11. Maya vray XYZ皮肤贴图材质节点连接
  12. 网联兴,银联苦:一文看清支付清算市场新局面的矛盾和疑惑
  13. 如何快速进入/打开cmd--快捷键
  14. matlab解符号高次方程,matlab解高次方程的问题
  15. 捋一捋字符串和字节序列的关系
  16. 打破“中规中矩”,手机QQ何以萌翻众人?
  17. BUPT计导第三次机考12.8数组+二分答案详解
  18. Leetcode:125.验证回文串,917仅仅反转字母,387字符串的第一个唯一字符
  19. java反射机制是什么_java中的反射机制是什么?
  20. 推荐系统引擎——模型(1)

热门文章

  1. xmind 8 安装后启动失败
  2. 基于原生开发的全新中文安卓应用开发平台
  3. C语言|职工工资管理系统
  4. 请问OCP上面有没有证书编号?
  5. python数据可视化编程实战链接
  6. 全球最值得听的100首英文歌
  7. python so反编译_使用cython把python编译so
  8. 2022艾灸展,艾健康展,艾棒展,山东艾制品展,艾绒纺织展
  9. 【采用】信贷业务的25个风险点
  10. --如何用PhotoShop制作真人头像表情包--