项目笔记整理

一、背景介绍

此次项目依托于微信小程序,用于线上预定酒店的产品。

二、使用到的技术(技术架构)

nginx、springCloud-gateway、SpringBoot、Nacos、Docker 、ElasticSearch、Kafaka、Beats、Logstash、kibana

DNS解析:客户端、WAF、CDN、防火墙、Nginx集群

服务监控:spring Boot Admin
注册中心:Nacos注册中心 —> 注册与发现、服务注册
Webflux网关:springCloud GateWay -----> Ribbon、Sentinel(熔断降级)
认证接受、动态路由:Seatinel+Shard

配置中心:Nacos配置中心 ----> 动态配置、配置管理
业务集群:QAuth2.0认证、Spring Security、SpringBoot应用、OpenFeign
持续化: Master Slave、Mysql
消息队列:Mirror、RabbitMQ

监控中心:Skywalking -----> 链路追踪、监控警报
分布式 主键:分布式锁、分布式事务、CAP、BASE
全文检索:Shard、ElasticSearch
对象存储:OSS

运维报警系统:Prometheus、Grafana、Alertmanager -----> 短信、微信、邮件
日志系统:Kafaka、Beats、Logstash、kibana、ElasticSearch
任务管理: XXL-JOB

Developer—> Github —> docker -----> Kubermeters API ------> K8S UAT/PROD-----> Jenkins Plpeline -----> OP运维

三、项目流程概述

主线:梳理产品流程、数据库设计(E-R图)、接口设计、编码、前后端联调、项目上线、整理成文档、项目总结。
支线:数据库设计前:环境搭建、项目熟悉、开发工具、阿里规范手册
           项目任务:基础服务、功能分配
           阶段性任务:第一阶段需求开发、技术需求
                                 第二阶段需求开发、技术需求
           优化:代码 review、代码优化
           验收:技术博客、项目整体验收

四、 问题注意事项梳理

注:不同公司有不同的规范,需要遵循。

4.1 数据库设计注意事项

命名上:单词与单词使用下滑线连接
              不能简写单词,要见名知意
规范上:表中字段大小写统一,别混合大小写
               表中不能有外键
               少用中间表
                表中字段多数尽量设置不能为空,避免空指针异常
                每个字段长度,要合理
                前缀不加is,例如:不用 is_status类似的作为状态
                金额、评分都使用整型
                表中有创建时间、最后一次执行时间
                正确使用update_time
                表中得有逻辑删除

注:为什么不使用is作为前缀?
如果加的话哪个mybatisGenerator生成代码的时候会自动生成is_,这样序列化就会报错,如果改也比较麻烦

4.2 接口设计注意事项

命名上: 接口别使用驼峰,使用杠(-)来命名,例如:enum-list
                前台统一使用,/项目名/模块/方法 ,例如:/hotel/search/enum-list
                后台统一使用 /admin/项目名/模块/方法,例如:/admin/hotel/search/enum-add

规范上: 不使用RestFul风格
                数据结构统一字段名

es部分: 查询接口
                搜索接口
                枚举值四个接口,增删改查

4.3 代码编写注意事项

命名上: 使用驼峰命名

规范上: try-catch异常内容写入日志
                统一返回值
                关键地方使用try-catch
                所有异常都回滚,拼接字符串的使用
                写完整的注释
                设置非空验证,进行校验
                过长代码能进行抽取尽量抽取出来封装成类或者方法,代码需要整洁
                service调service
                使用lamba表达式、流
                异常在service层写
                实现类不加I,只有接口加I,例如:HotelService-> IHotelService
                data里面传true,尽量别用null
                在controller层返回的结果,不论失败还是成功都返回成功的结果。
                对于一些常量,需要提取出来

五、 总结

1、在这次项目中,学到了项目中的开发流程,熟悉产品、数据库设计、接口设计、代码编写、前端联调,每一阶段做什么,遵循什么规范,团队协作技巧
学到了一些编码规范,代码规范比如常见的判断非空,使用哪些需要判断为空的工具,对一些hutool工具的使用,业务层中使用IService等
代码优化方面,对一些能代码比较多的能提取出来的就提取出来或封装成一个方法,尽量代码简洁。
2、每次会议也学到许多技巧的,比如面试技巧,一些细节注意点,工作中遇到的问题,每次都了解了一些行业情况。
3、不足的地方也挺多的,比如编码能力、相关技术的学习、相关技术底层原理了解等都还不够。

附件:

1.1 项目中接触到的中间件:

网关:Nginx、Kong、Zuul
缓存:Redis、MemCached、OsCache、EhCache
搜索:ElasticSearch、Solr
熔断:Hystrix、resilience4j
负载均衡:DNS、F5、LVS、Nginx、OpenResty、HAproxy
注册中心:Eureka、Zookeeper、Redis、Etcd、Consul
认证鉴权:JWT、SpringSecurity
消费队列:RabbitMQ、Kafka、RocketMQ、ActiveMQ、Redis
系统监控:Grafana、Prometheus、Influxdb、Telegraf、Lepus
文件系统:OSS、NFS、FastDFS、MogileFS
RPC框架: Dubbo、Motan、Thrift、grpc
构建工具:Maven、Gradle
集成部署:Docker、Jenkins、Git、Maven
分布式配置:Disconf、Apollo、Spring Cloud Config、Diamond
压测:LoadRunner、JMeter、AB、webbench
数据库:MySQL、Redis、MongoDB、PostgreSQL、Memcache、HBase
网络:专用网络VPC、弹性公网IP、CDN
数据库中间件:DRDS、Mycat、360 Atlas、Cobar
分布式框架:Dubbo、Motan、Spring-Could
分布式任务:XXL-JOB、Elastic-Job、Saturn、Quartz
分布式追踪:Pinpoint、CAT、zipkin
分布式日志:elasticsearch、logstash、Kibana 、redis、kafka
版本发布:蓝绿部署、A/B测试、灰度发布/金丝雀发布

1.2 项目时间线

项目笔记整理_第1张图片
1.3接口设计

1.3 编码细节

1.设置非空验证
官网:http://hibernate.org/validator
例如:

public class Car {
​
   @NotNull
   private String manufacturer;
​
   @NotNull
   @Size(min = 2, max = 14)
   private String licensePlate;
​
   @Min(2)
   private int seatCount;
​
   // ...
}

2.一些配置不要放在业务层
例如:

@Configuration
public class DruidConfig {
​
    /**
     * 配置Druid 监控启动页面
     *
     * @return servletRegistrationBean
     */
    @Bean
    @ConditionalOnMissingBean
    public ServletRegistrationBean druidStartViewServlet() {
        ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(new StatViewServlet(), "/druid/*");
        // 白名单
		// servletRegistrationBean.addInitParameter("allow", "127.0.0.1");
        // 黑名单
        servletRegistrationBean.addInitParameter("deny", "192.168.1.100");
        // 登录查看信息的账密,用于登录Druid监控后台
        servletRegistrationBean.addInitParameter("loginUsername", "druid");
        servletRegistrationBean.addInitParameter("loginPassword", "druid");
        // 是否能够重置数据
        servletRegistrationBean.addInitParameter("resetEnable", "true");
        return servletRegistrationBean;
    }

3.不用try catch
使用全局捕捉异常,例如:GlobalExceptionHandler、ApiException直接引common模块使用。
4.if else问题
可以直接if return让代码简洁。

if(){
    ...
}
return ...

5.日志打印问题
主要流程关键环节加日志 , 错误的不用加入日志,目的节约资源
6.接口注释写好增加可读性。
7.if else特别多时用switch语句,让代码干净整洁些。
8.相应模块写在对应模块里,例如admin的写admin模块里。
9.使用mybatis-plus ,例如Service直接继承Iservice的方法简洁代码
例如:

public interface IEnumService extends IService {
    
    /**
     * 查询所有枚举
     *
     * @return 枚举
     */
    List getDictItem();
}

例如:接口需要带一个 I

public interface IEnumService extends IService {

    /**
     * 查询所有枚举
     *
     * @return 枚举
     */
    List getDictItem();
 }

10.minio上传文件单独写一个service
例如:
minio官网:http://www.minio.org.cn/
项目笔记整理_第2张图片

11.字符串拼接问题
12.redis的使用要修改
13.mybatis-plus使用LambdaQueryWrapper通过方法引用的方式来使用实体字段名的操作
例如:

LambdaQueryWrapper lambda = Wrappers.lambdaQuery();
lambda.like(User::getName, "xaoming").lt(User::getAge, 40);

14.lamba表达式和流的使用
例如:

List collect = list1.stream().map(item -> {
    HotelDTO entity = new HotelDTO();
    entity.setCityName(item.getCityName());
    entity.setKeyWord(item.getName());
    return entity;
}).collect(Collectors.toList());

15.大小写问题,例如使用DTO,而不使用Dto;使用VO,而不使用Vo;使用DAO,而不使用Dao;
例如:DTO的使用

@Data
@AllArgsConstructor
@NoArgsConstructor
@Validated
public class HotelDTO {
    @ApiModelProperty(value = "关键字/城市/品牌/酒店名")
    private String queryText;
    @ApiModelProperty(value = "附近地标")
    ...
 }

VO的使用

@Data
@AllArgsConstructor
public class ImageVO {
    private String breviaryPicture; // 缩略图地址
    private String detailPicture; // 详细大图
}

DAO的使用

@Mapper
public interface HotelImageDAO extends BaseMapper {

}

16.非空用hutool工具
判断集合是否为空,为什么这么使用呢,size可能为0,而不是使用==null
CollUtil.isEmpty()

17.对lombok插件的使用
自定义了构造方法之后,lombok就不会生成有参无参构造方法
18.一些固定的常量提取出,单独封装
例如:
部分代码:

			//查询价格高低使用到"floor_special_price";
			//提取出来为:SearchCode.FLOOR_SPECIAL_PRICE
    		public static final String FLOOR_SPECIAL_PRICE = "floor_special_price";
    		...
			//价格低高排序
            case SearchCode.PRICE_ASC:
                nativeSearchQueryBuilder.withSort(SortBuilders.fieldSort(SearchCode.FLOOR_SPECIAL_PRICE).order(SortOrder.ASC));
                break;
                ...

1.4 Java开发手册

嵩山版与黄山版的区别:
项目笔记整理_第3张图片在这里插入图片描述

2022黄山版
【强制】前后端交互的API,需要明确协议、域名、路径、请求方法、请求内容、响应码、响应体。
在这里插入图片描述
3. 【强制】在varchar字段上建立素引时,必须指定索引长度,没必要对全字段建立索引,根据实际文本区
分度决定索引长度。
说明:索引的长度与区分度是-对矛盾休, -般对字符申类型数据,长度为20的索引,区分度会高达90%以上,可以使
用count(distinct left(列名,索引长度)) / count(*)的区分度来确定.

4、 【强制】如果系统中某个功能的调用链路上的涉及对象超过3个,使用时序图来表达并且明确各调用环
节的输入与输出。

6、. 【强制】得使用外键与级联,-切外键概念必须在应用层解决。
说明: (概念解释) 学生表中的student id是主键,那么成绩表中的student id则为外键。如果更新学生表中的
student_ id,同时触发成绩表中的student id更新,即为级联更新。外键与级联更新适用于单机低并发,不适合分布工
高并发生群:级联更新是强阳塞,存在数据库更新风年的风险:外键影响数据库的插入速度。

7、. 【强制】更新数据表记录时,必须同时更新记录对应的update_ time字段值为当前时间。
7.1【强制】禁止使用存储过程,存储过程难以调试和扩展,更没有移植性。

8、 【强制】任何货币金额,均以最小货币单位且为整型类型进行存储。
9、 【强制】表必备三字段: id, create_ time,update time。
9.1 【参考】 @Transactional 事务不要滥用。事务会影响数据库的QPS,另外使用事务的地方需要考虑各
方面的回滚方案,包括缓存回滚.搜索引擎回滚,消息补偿、统计修正等。

9.2 【强制】 POJO类中的任何布尔类型的变量,都不要加is前缀,否则部分框架解析会引起序列化错误。
说明:本文MySQL规约中的建表约定第1条,表达是与香的变量采用is. )ox的命名方式,所以需要在
设置从is. _)o0 到xxx的映射关系。
反例:定义为基本数据类型Boolean isDeleted的属性,它的方法也是isDeleted0.框架在反向解析时,“误以为" 对
应的属性名称是deleted,导致属性获取不到,进而抛出异常,

直接使用deleted字段就可以,如果加的话哪个mybatisGenerator生成代码的时候会自动生成is_,这样序列化就会报错,如果改也比较麻烦
10、 【强制】在数据库中不能使用物理删除操作,要使用逻辑删除。
说明:逻辑删除在数据删除后可以追溯到行为操作。不过会使得一些情况下的唯一主键变得
情解决。.

10.1 [强制] BigDecimal 的等值比较应使用compareTo()方法,而不是equals0 方法。」
说明: equals0方法会比较值和精度(1.0 与1.00返回结果为false)。而compareTo0则会忽略精度。」

这条规则我们直接在数据库定义的时候,表示是与否的情况不加 is 前缀
是说在代码实体类中,不要加is,数据库中要加吗? 都不加。
在这里插入图片描述
11.1 【强制】并发修改同- -记录时,避免更新丟失,需要加锁。要么在应用层加锁,要么在缓存加锁,要么
在数据库层使用乐观锁,使用version作为更新依据。
说明:如果每次访问冲突概率小于20%,推荐使用乐观锁,否则使用悲观锁。乐观锁的重试次数不得小于3次.
11.2 【参考】创建索引时避免有如下极端误解:
1)索引宁滥勿缺。认为一一个查询就需要建一个索引。
2)吝啬索引的创建。认为索引会消耗空间、严重拖慢记录的更新以及行的新增速度。
3)抵制唯一索引.认为唯一索引一律需要在应用层通过“先查后插”方式解决。

11.2 【推荐】防止NPE,是程序员的基本修养,注意NPE产生的场景:
1)返回类型为基本数据类型,return 包装数据类型的对象时,自动拆箱有可能产生NPE
反例: public int method( { return Integer对象; },如果为null,自动解箱抛NPE.
2)数据库的查询结果可能为null.
3)集合里的元素即使isNotEmpty,取出的数据元素也可能为null。
4)远程调用返回对象时,-律要求进行空指针判断,防止NPE.
5)对于Session中获取的数据,建议进行NPE检查,避免空指针。
6)级联调用obj.getA().getB).getC0;-连串调用, 易产生NPE.
正例:使用JDK8的Optional类来防止NPE问题。

11.3【推荐】表的命名最好是遵循“业务名称表的作用”。
正例: alipay_task/force_project/trade_ config / tes_question
12. [推荐]库名与应用名称尽量一致。
在这里插入图片描述
12. 2【强制】杜绝完全不规范的英文缩写,避免望文不知义。
反例: AbstractClass “缩写”成AbsClass; condition “缩写” 成condi; Function “缩写”成Fu,此类随意缩写
严重降低了代码的可阅读性,
13. 【推荐】资金相关的金融敏感信息,使用悲观锁策略。
说明:乐观锁在获得锁的同时已经完成了更新操作,校验逻胡容易出现漏洞。另外,乐观锁对冲突的解决策路有较复杂
的要求,处理不当容易造成系统压力或数据异常,所以资金相关的金融敏感信息不建议使用乐观锁更新。
正例:悲观锁遵循- -锁二判三更新四释放的原则。
14. 【推荐】字段允许适当冗余,以提高查询性能,但必须考虑数据一 致。冗余字段应遵循:
1)不是频繁修改的字段。
2)不是唯- -索引的字段。
3)不是varchar超长字段,更不能是text字段。
正例:各业务线经常冗余存储商品名称,避免查询时需要调用IC服务获取。
15. 【推荐】单表行数超过500万行或者单表容量超过2GB,才推荐进行分库分表。
说明:如果预计三年后的数据量根本达不到这个级别,请不要在创建表时就分库分表。

项目笔记整理_第4张图片
ORM映射

  1. [强制]在表查询中,- -律不要使用*作为查询的字段列表,需要哪些字段必须明确写明。
    说明:
    1)增加查询分析器解析成本。
    2)增减字段容易与resultMap配置不- -致。
    3)无用字段增加网络消耗,尤其是text类型的字段。

命名:
A) service / DAO层力法可名机约
1)获取单个对象的方法用get做前缀。
2)获取多个对象的方法用list做前缀,复数结尾,如: listObjects
3)获取统计值的方法用count做前缀。
4)插入的方法用save / insert做前缀。
5)删除的方法用remove / delete做前缀。
6)修改的方法用update做前缀。

es思路:
经纬度的查询,圆形、正方形、多个点的、点与点的之间的

1.5 工具推荐

数据库设计工具:https://www.oschina.net/p/chiner
接口工具:https://www.apipost.cn/

面试题部分整理(答案在Java中高级核心知识全面解析.pdf中)

注:下方答案不完整

基础篇

1、索引有哪些?普通索引、唯一索引…

2、事务的隔离级别:读未提交–>遇到的问题
读已提交–>遇到的问题

3、锁有哪些锁?共享锁、排他锁、行锁、悲观锁、乐观锁

4、进程线程的区别

5、守护线程是什么?

6、线程的生命周期?创建进程,调用runstart可运行状态
创建,就绪,运行,阻塞,结束

7、数组有什么特征?下标从0开始

8、java为什么能跨平台?

9、jdk与jre的区别???jvm、jre、jdk的区别

10、事务有哪些?

11、哪些集合是线程安全的?

12、springBoot常用注解,

13、SpringMVC执行流程(重点)
DispatcherServlet --> HandlerMapping --> Handler --> View

14、SpringBoot中starter为什么自动注入到容器中

15、SpringMVC 中aop(面向切面)跟ioc(控制反转)
环绕通知

16、springCloud工具集常用的五个?

17、hashMap和threeMap区别?
hashMap无,threeMap有序,linked

18、mvcc是什么?

19、ArrayList和linklist的特点
ArrayList可以动态扩容,扩容一半,查询快
linklist没有扩容,插入、删除快。

进阶篇

1、redis与mysql数据怎么能保持一致性?

2、内存屏障
cup三条指令,执行顺序
禁止cpu操作
项目笔记整理_第5张图片
3、双亲委派机制

4、如何用两个栈实现队列

5、数据库什么时候会索引失效?
or 中的条件如若都走索引就不会索引失效
索引失效
项目笔记整理_第6张图片
注:索引失效

6、可达性分析算法?
导致Full GC的原因:老年代被写满、永久代(Perm)被写满和System.gc()被显式调用

7、可达性分析可以避免引用计数法无法挥手循环引用的情况

8、链表、cs

通过hash运算后两个相同hash值的key在HashMap中是如何存储的? 先equals 再 比较内存地址?
不是key相同,是key的hash值相同,key值不同

双向链表 , cs 直接操作内存,保证原子性

9、链地址法解决hash冲突
项目笔记整理_第7张图片
10、redis为什么那么快?
多路复用,

11、selector 选择多个频道?

12、sql执行纪录
sql执行计划是指个执行顺序吗from - where -group by -having - select - order by-limit

13、日志有哪些?
binlog
redo log 重做日志 undo log 回滚日志

14、数据库底层存储引擎哪些?

15、数据库底层存储数据结构哪些?B+树
MyISAM B- InnoDB B+

16、什么是聚合索引?
聚合索引-----> 数据和索引放一起就是聚簇索引

17、select语句加锁没?默认不加锁的,

18、怎么加共享锁、排他锁
后面加个关键字 for update(加的是排他锁) 加锁

19、浅拷贝和深拷贝是什么
浅拷贝和深拷贝?浅,创建指针,指向原来的地址,,深拷贝是创建新内存,复制进去

  1. 浅拷贝:对基本数据类型进行值传递,对引用数据类型进行引用传递般的拷贝,此为浅拷贝。
  2. 深拷贝:对基本数据类型进行值传递,对引用数据类型,创建一个新的对象,并复制其内容,此为 深拷贝。

20、HashMap如何实现的??产生闭环链表

21、hashCode()与 equals()的相关规定

  1. 如果两个对象相等,则 hashcode 一定也是相同的
  2. 两个对象相等,对两个对象分别调用 equals 方法都返回 true
  3. 两个对象有相同的 hashcode 值,它们也不一定是相等的
  4. 因此,equals 方法被覆盖过,则 hashCode 方法也必须被覆盖
  5. hashCode() 的默认行为是对堆上的对象产生独特值。如果没有重写 hashCode(),则该 class 的两 个对象无论如何都不会相等(即使这两个对象指向相同的数据)

22、String StringBuffer 和 StringBuilder 的区别是什么? String 为什么是不可变的?
简单的来说: String 类中使用 final 关键字修饰字符数组来保存字符串, private final char
value[] ,所以 String 对象是不可变的
而 StringBuilder 与 StringBuffer 都继承自 AbstractStringBuilder 类,在
AbstractStringBuilder 中也是使用字符数组保存字符串 char[]value 但是没有用 final 关键字
修饰,所以这两种对象都是可变的
StringBuilder 与 StringBuffer 的构造方法都是调用父类构造方法也就是
AbstractStringBuilder 实现的
线程安全性
String 中的对象是不可变的,也就可以理解为常量,线程安全。 AbstractStringBuilder 是
StringBuilder 与 StringBuffer 的公共父类,定义了一些字符串的基本操作,如
expandCapacity 、 append 、 insert 、 indexOf 等公共方法。 StringBuffer 对方法加了同步锁或
者对调用的方法加了同步锁,所以是线程安全的。 StringBuilder 并没有对方法进行加同步锁,所以
是非线程安全的。
性能
每次对 String 类型进行改变的时候,都会生成一个新的 String 对象,然后将指针指向新的
String 对象。 StringBuffer 每次都会对 StringBuffer 对象本身进行操作,而不是生成新的对象
并改变对象引用。相同情况下使用 StringBuilder 相比使用 StringBuffer 仅能获得 10%~15% 左
右的性能提升,但却要冒多线程不安全的风险。
对于三者使用的总结:

  1. 操作少量的数据: 适用 String
  2. 单线程操作字符串缓冲区下操作大量数据: 适用 StringBuilder
  3. 多线程操作字符串缓冲区下操作大量数据: 适用 StringBuffer

23、 System.arraycopy()和Arrays.copyOf()方法 区别:
arraycopy()需要目标数组,将原数组拷贝到你自己定义的数组里,而且可以选择拷贝的起点和长 度以及放入新数组中的位置
copyOf()是系统自动在内部新建一个数组,并返回该数组

你可能感兴趣的:(真实项目实战,Java,java,微服务,分布式)