1.ThreadLocal使用
同一个线程内通过ThreadLocal进行参数传递
ContractDeliverySynService.saveLogInfoMap.set(map);
mapBefore = ContractDeliverySynService.saveLogInfoMap.get();
2.CountDownLatch 判断线程是否执行结束
//1.定义CountDownLatch
CountDownLatch countDownLatch = new CountDownLatch(sqlParam.size());
//2.线程内部对countDownLatch减
OrderSyncCrmTask orderSyncCrmTask = new OrderSyncCrmTask(countDownLatch, resultMap, threadParam, traceId);
pool.submit(orderSyncCrmTask);
//OrderSyncCrmTask内部latch.countDown();
//3.
CountDownLatch.await() 方法在倒计数为0之前会阻塞当前线程,并可设置超时时间
boolean await = countDownLatch.await(OrderSyncCrmTask.staticPushOrderTimeOut, TimeUnit.SECONDS);
if (await) {
log.info("未超时", await);
} else {
log.info("已超时", await);
}
3.javax.net.ssl.SSLHandshakeException: No appropriate protocol (protocol is disabled or cipher suites are inappropriate)报错
原因:JDK版本导致的认证问题
解决:在路径/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.292.b10-1.el7_9.x86_64/jre/lib/security/java.security中找到并修改如下(此前删除TLSv1, TLSv1.1两个认证)
jdk.tls.disabledAlgorithms=SSLv3, RC4, DES, MD5withRSA, DH keySize < 1024,
EC keySize < 224, 3DES_EDE_CBC, anon, NULL
附:
SSL协议的工作流程:
服务器认证阶段:
1)客户端向服务器发送一个开始信息“Hello”以便开始一个新的会话连接;
2)服务器根据客户的信息确定是否需要生成新的主密钥,如需要则服务器在响应客户的“Hello”信息时将包含生成主密钥所需的信息;
3)客户根据收到的服务器响应信息,产生一个主密钥,并用服务器的公开密钥加密后传给服务器;
4)服务器恢复该主密钥,并返回给客户一个用主密钥认证的信息,以此让客户认证服务器。
该认证方式以主服务器为主导
4.通过List.subList实现分页、截取
newList = productBeanList.subList(0, index);
5.手动事务管理
@Autowired
private SqlSessionFactory sqlSessionFactory;
SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH, false);
SyncCrmOrderDetailMapper orderMapper = sqlSession.getMapper(SyncCrmOrderDetailMapper.class);
6.遍历map
jdk8及以后:
Map.forEach方法
7.JDK8时间相关函数
如果是JDK8的应用,可以使用Instant代替Date,LocalDateTime代替Calendar,DateTimeFormatter代替SimpleDateFormat
8.多线程并行处理定时任务
多线程并行处理定时任务时,Timer运行多个TimeTask时,只要其中之一没有捕获抛出的异常,其它任务便会自动终止运行,使用ScheduledExecutorService则没有这个问题
9.多线程count++操作
volatile解决多线程内存不可见问题。对于一写多读,是可以解决变量同步问题,但是如果多写,同样无法解决线程安全问题。
说明:如果是count++操作,使用如下类实现:AtomicInteger count = new AtomicInteger(); count.addAndGet(1); 如果是JDK8,推荐使用LongAdder对象,比AtomicLong性能更好(减少乐观锁的重试次数)。
10.集合初始化时,指定集合初始值大小
说明:HashMap使用HashMap(int initialCapacity) 初始化,如果暂时无法确定集合大小,那么指定默认值(16)即可。
正例:initialCapacity = (需要存储的元素个数 / 负载因子) + 1。注意负载因子(即loader factor)默认为0.75,如果暂时无法确定初始值大小,请设置为16(即默认值)。
反例: HashMap需要放置1024个元素,由于没有设置容量初始大小,随着元素增加而被迫不断扩容,resize()方法总共会调用8次,反复重建哈希表和数据迁移。当放置的集合元素个数达千万级时会影响程序性能。
HashMap在容量不够进行resize时由于高并发可能出现死链,导致CPU飙升,在开发过程中注意规避此风险。
11.在高并发场景中,避免使用”等于”判断作为中断或退出的条件。
说明:如果并发控制没有处理好,容易产生等值判断被“击穿”的情况,使用大于或小于的区间判断条件来代替。
反例:判断剩余奖品数量等于0时,终止发放奖品,但因为并发处理错误导致奖品数量瞬间变成了负数,这样的话,活动无法终止。
12.对于需要使用超大整数的场景,服务端一律使用String字符串类型返回,禁止使用Long类型。
说明:Java服务端如果直接返回Long整型数据给前端,JS会自动转换为Number类型(注:此类型为双精度浮点数,表示原理与取值范围等同于Java中的Double)。Long类型能表示的最大值是2的63次方-1,在取值范围之内,超过2的53次方 (9007199254740992)的数值转化为JS的Number时,有些数值会有精度损失。扩展说明,在Long取值范围内,任何2的指数次整数都是绝对不会存在精度损失的,所以说精度损失是一个概率问题。若浮点数尾数位与指数位空间不限,则可以精确表示任何整数,但很不幸,双精度浮点数的尾数位只有52位。
反例:通常在订单号或交易号大于等于16位,大概率会出现前后端单据不一致的情况,比如,“orderId”: 362909601374617692,前端拿到的值却是: 362909601374617660。
13.HTTP请求通过URL传递参数时,不能超过2048字节
说明:不同浏览器对于URL的最大长度限制略有不同,并且对超出最大长度的处理逻辑也有差异,2048字节是取所有浏览器的最小值。
14.HTTP请求通过body传递内容时,必须控制长度,超出最大长度后,后端解析会出错
说明:nginx默认限制是1MB,tomcat默认限制为2MB,当确实有业务需要传较大内容时,可以通过调大服务器端的限制。
15.页面重定向
服务器内部重定向必须使用forward;外部重定向地址必须使用URL统一代理模块生成,否则会因线上采用HTTPS协议而导致浏览器提示“不安全”,并且还会带来URL维护不一致的问题。
16.Math.random()
注意 Math.random() 这个方法返回是double类型,注意取值的范围 0≤x<1(能够取到零值,注意除零异常),如果想获取整数类型的随机数,不要将x放大10的若干倍然后取整,直接使用Random对象的nextInt或者nextLong方法。
17.任何数据结构的构造或初始化,都应指定大小,避免数据结构无限增长吃光内存。
18.Java 类库中定义的可以通过预检查方式规避的RuntimeException异常不应该通过catch 的方式来处理
Java 类库中定义的可以通过预检查方式规避的RuntimeException异常不应该通过catch 的方式来处理,比如:NullPointerException,IndexOutOfBoundsException等等。 说明:无法通过预检查的异常除外,比如,在解析字符串形式的数字时,可能存在数字格式错误,不得不通过catch NumberFormatException来实现。
正例:if (obj != null) {…}
反例:try { obj.method(); } catch (NullPointerException e) {…}
19.不要在finally块中使用return
说明:try块中的return语句执行成功后,并不马上返回,而是继续执行finally块中的语句,如果此处存在return语句,则在此直接返回,无情丢弃掉try块中的返回点。
20.根据国家法律,网络运行状态、网络安全事件、个人敏感信息操作等相关记录,留存的日志不少于六个月,并且进行网络多机备份。
21.生产环境禁止直接使用System.out 或System.err 输出日志或使用e.printStackTrace()打印异常堆栈。
22.异常信息应该包括两类信息:案发现场信息和异常堆栈信息。如果不处理,那么通过关键字throws往上抛出。
正例:logger.error(“inputParams:{} and errorMessage:{}”, 各类参数或者对象toString(), e.getMessage(), e);
23.MYSQL主键索引命名
主键索引名为pk_字段名;唯一索引名为uk_字段名;普通索引名则为idx_字段名。 说明:pk_ 即primary key;uk_ 即 unique key;idx_ 即index的简称。
24.MySQL过长字段
varchar是可变长字符串,不预先分配存储空间,长度不要超过5000,如果存储长度大于此值,定义字段类型为text,独立出来一张表,用主键来对应,避免影响其它字段索引效率。
25.表必备三字段:id, create_time, update_time。
其中id必为主键,类型为bigint unsigned、单表时自增、步长为1。create_time, update_time的类型均为datetime类型,前者现在时表示主动式创建,后者过去分词表示被动式更新。
26.单表行数超过500万行或者单表容量超过2GB,才推荐进行分库分表
说明:如果预计三年后的数据量根本达不到这个级别,请不要在创建表时就分库分表。
27.利用延迟关联或者子查询优化超多分页场景
说明:MySQL并不是跳过offset行,而是取offset+N行,然后返回放弃前offset行,返回N行,那当offset特别大的时候,效率就非常的低下,要么控制返回的总页数,要么对超过特定阈值的页数进行SQL改写。
正例:先快速定位需要获取的id段,然后再关联: SELECT t1.* FROM 表1 as t1, (select id from 表1 where 条件 LIMIT 100000,20 ) as t2 where t1.id=t2.id
28.SQL性能优化的目标
SQL性能优化的目标:至少要达到 range 级别,要求是ref级别,如果可以是consts最好。
说明:
1) consts 单表中最多只有一个匹配行(主键或者唯一索引),在优化阶段即可读取到数据。
2) ref 指的是使用普通的索引(normal index)。
3) range 对索引进行范围检索。
反例:explain表的结果,type=index,索引物理文件全扫描,速度非常慢,这个index级别比较range还低,与全表扫描是小巫见大巫。
29.如果需要存储表情,那么选择utf8mb4来进行存储,注意它与utf8编码的区别。
30.POJO类的布尔属性不能加is,而数据库字段必须加is_,要求在resultMap中进行字段与属性之间的映射。
31.不要用resultClass当返回参数,即使所有类属性名与数据库字段一一对应,也需要定义;反过来,每一个表也必然有一个与之对
32.分层领域模型规约
• DO(Data Object):此对象与数据库表结构一一对应,通过DAO层向上传输数据源对象。
• DTO(Data Transfer Object):数据传输对象,Service或Manager向外传输的对象。
• BO(Business Object):业务对象,可以由Service层输出的封装业务逻辑的对象。
• Query:数据查询对象,各层接收上层的查询请求。注意超过2个参数的查询封装,禁止使用Map类来传输。
• VO(View Object):显示层对象,通常是Web向模板渲染引擎层传输的对象。
33.二方库依赖
(1)【强制】定义GAV遵从以下规则:
1) GroupID格式:com.{公司/BU }.业务线 [.子业务线],最多4级。
说明:{公司/BU} 例如:alibaba/taobao/tmall/aliexpress等BU一级;子业务线可选。
正例:com.taobao.jstorm 或 com.alibaba.dubbo.register
2) ArtifactID格式:产品线名-模块名。语义不重复不遗漏,先到中央仓库去查证一下。
正例:dubbo-client / fastjson-api / jstorm-tool
3) Version:详细规定参考下方。
(2).二方库版本号命名方式:主版本号.次版本号.修订号
1)主版本号:产品方向改变,或者大规模API不兼容,或者架构不兼容升级。
2) 次版本号:保持相对兼容性,增加主要功能特性,影响范围极小的API不兼容修改。
3) 修订号:保持完全兼容性,修复BUG、新增次要功能特性等。
说明:注意起始版本号必须为:1.0.0,而不是0.0.1。
(3).如果依赖其它二方库
如果依赖其它二方库,尽量是provided引入,让二方库使用者去依赖具体版本号
34.服务器配置
(1).高并发服务器建议调小TCP协议的time_wait超时时间
说明:操作系统默认240秒后,才会关闭处于time_wait状态的连接,在高并发访问下,服务器端会因为处于time_wait的连接数太多,可能无法建立新的连接,所以需要在服务器上调小此等待值。
正例:在linux服务器上请通过变更/etc/sysctl.conf文件去修改该缺省值(秒): net.ipv4.tcp_fin_timeout = 30
(2).调大服务器所支持的最大文件句柄数(File Descriptor,简写为fd)
主流操作系统的设计是将TCP/UDP连接采用与文件一样的方式去管理,即一个连接对应于一个fd。主流的linux服务器默认所支持最大fd数量为1024,当并发连接数很大时很容易因为fd不足而出现“open too many files”错误,导致新的连接无法建立。建议将linux服务器所支持的最大句柄数调高数倍(与服务器的内存数量相关)。
(3).给JVM环境参数设置-XX:+HeapDumpOnOutOfMemoryError参数,让JVM碰到OOM场景时输出dump信息
OOM的发生是有概率的,甚至相隔数月才出现一例,出错时的堆内信息对解决问题非常有帮助