此次项目依托于微信小程序,用于线上预定酒店的产品。
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、代码优化
验收:技术博客、项目整体验收
注:不同公司有不同的规范,需要遵循。
命名上:单词与单词使用下滑线连接
不能简写单词,要见名知意
规范上:表中字段大小写统一,别混合大小写
表中不能有外键
少用中间表
表中字段多数尽量设置不能为空,避免空指针异常
每个字段长度,要合理
前缀不加is,例如:不用 is_status类似的作为状态
金额、评分都使用整型
表中有创建时间、最后一次执行时间
正确使用update_time
表中得有逻辑删除
注:为什么不使用is作为前缀?
如果加的话哪个mybatisGenerator生成代码的时候会自动生成is_,这样序列化就会报错,如果改也比较麻烦
命名上: 接口别使用驼峰,使用杠(-)来命名,例如:enum-list
前台统一使用,/项目名/模块/方法 ,例如:/hotel/search/enum-list
后台统一使用 /admin/项目名/模块/方法,例如:/admin/hotel/search/enum-add
规范上: 不使用RestFul风格
数据结构统一字段名
es部分: 查询接口
搜索接口
枚举值四个接口,增删改查
命名上: 使用驼峰命名
规范上: try-catch异常内容写入日志
统一返回值
关键地方使用try-catch
所有异常都回滚,拼接字符串的使用
写完整的注释
设置非空验证,进行校验
过长代码能进行抽取尽量抽取出来封装成类或者方法,代码需要整洁
service调service
使用lamba表达式、流
异常在service层写
实现类不加I,只有接口加I,例如:HotelService-> IHotelService
data里面传true,尽量别用null
在controller层返回的结果,不论失败还是成功都返回成功的结果。
对于一些常量,需要提取出来
1、在这次项目中,学到了项目中的开发流程,熟悉产品、数据库设计、接口设计、代码编写、前端联调,每一阶段做什么,遵循什么规范,团队协作技巧
学到了一些编码规范,代码规范比如常见的判断非空,使用哪些需要判断为空的工具,对一些hutool工具的使用,业务层中使用IService等
代码优化方面,对一些能代码比较多的能提取出来的就提取出来或封装成一个方法,尽量代码简洁。
2、每次会议也学到许多技巧的,比如面试技巧,一些细节注意点,工作中遇到的问题,每次都了解了一些行业情况。
3、不足的地方也挺多的,比如编码能力、相关技术的学习、相关技术底层原理了解等都还不够。
网关: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.设置非空验证
官网: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/
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;
...
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,才推荐进行分库分表。
说明:如果预计三年后的数据量根本达不到这个级别,请不要在创建表时就分库分表。
命名:
A) service / DAO层力法可名机约
1)获取单个对象的方法用get做前缀。
2)获取多个对象的方法用list做前缀,复数结尾,如: listObjects
3)获取统计值的方法用count做前缀。
4)插入的方法用save / insert做前缀。
5)删除的方法用remove / delete做前缀。
6)修改的方法用update做前缀。
es思路:
经纬度的查询,圆形、正方形、多个点的、点与点的之间的
数据库设计工具:https://www.oschina.net/p/chiner
接口工具:https://www.apipost.cn/
注:下方答案不完整
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操作
3、双亲委派机制
4、如何用两个栈实现队列
5、数据库什么时候会索引失效?
or 中的条件如若都走索引就不会索引失效
索引失效
注:索引失效
6、可达性分析算法?
导致Full GC的原因:老年代被写满、永久代(Perm)被写满和System.gc()被显式调用
7、可达性分析可以避免引用计数法无法挥手循环引用的情况
8、链表、cs
通过hash运算后两个相同hash值的key在HashMap中是如何存储的? 先equals 再 比较内存地址?
不是key相同,是key的hash值相同,key值不同
双向链表 , cs 直接操作内存,保证原子性
9、链地址法解决hash冲突
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、浅拷贝和深拷贝是什么
浅拷贝和深拷贝?浅,创建指针,指向原来的地址,,深拷贝是创建新内存,复制进去
20、HashMap如何实现的??产生闭环链表
21、hashCode()与 equals()的相关规定
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% 左
右的性能提升,但却要冒多线程不安全的风险。
对于三者使用的总结:
23、 System.arraycopy()和Arrays.copyOf()方法 区别:
arraycopy()需要目标数组,将原数组拷贝到你自己定义的数组里,而且可以选择拷贝的起点和长 度以及放入新数组中的位置
copyOf()是系统自动在内部新建一个数组,并返回该数组