Java开发常见错误
in the way
这个作者很懒,什么都没留下…
展开
-
30 _md5加密, HTTPS握手流程图
1, 密码保存 使用md5加密 + salt (加盐), 盐需要有一定的长度, 太短很容易破解。 没个密码对应一个盐。因为盐太短、太简单了,如果用户原始密码也很简单,那么整个拼起来的密码也很短,这样一般的MD5破解网站都可以直接解密这个MD5,除去盐就知道原始密码了。相同的盐,意味着使用相同密码的用户MD5值是一样的,知道了一个用户的密码就可能知道了多个。我们也可以使用这个盐来构建一张彩虹表,虽然会花不少代价,但是一旦构建完成,所有人的密码都可以被破解。**更好的做法是,不要使用像MD5这样快速原创 2020-07-18 23:00:55 · 634 阅读 · 0 评论 -
28, 安全兜底:涉及钱时,必须考虑防刷、限量和防重
1, 开放平台资源的使用需要考虑防刷短信验证码这种开放接口,程序逻辑内需要有防刷逻辑。第一种方式,只有固定的请求头才能发送验证码。也就是说,我们通过请求头中网页或App客户端传给服务端的一些额外参数,来判断请求是不是App发起的。其实,这种方式“防君子不防小人”。比如,判断是否存在浏览器或手机型号、设备分辨率请求头。对于那些使用爬虫来抓取短信接口地址的程序来说,往往只能抓取到URL,而难以分析出请求发送短信还需要的额外请求头,可以看作第一道基本防御。第二种方式,只有先到过注册页面才能发送验证码。原创 2020-07-18 12:20:01 · 264 阅读 · 0 评论 -
26,数据存储 Mysql, redis, ES,InfluxDB 业务选型
1, Redis vs MySQLredis 查询快, 效率比mysql(索引)快Redis薄弱的地方是,不擅长做Key的搜索。对MySQL,我们可以使用LIKE操作前匹配走B+树索引实现快速搜索;但对Redis,我们使用Keys命令对Key的搜索,其实相当于在MySQL里做全表扫描@GetMapping("redis2")public void redis2() {Assert.assertTrue(stringRedisTemplate.keys("item71*").size() == 1原创 2020-07-18 11:51:45 · 2044 阅读 · 0 评论 -
25,异步处理
1, 对于MQ消费程序,处理逻辑务必考虑去重(支持幂等),原因有几个:MQ消息可能会因为中间件本身配置错误、稳定性等原因出现重复。自动补偿重复,比如本例,同一条消息可能既走MQ也走补偿,肯定会出现重复,而且考虑到高内聚,补偿Job本身不会做去重处理。人工补偿重复。出现消息堆积时,异步处理流程必然会延迟。如果我们提供了通过后台进行补偿的功能,那么在处理遇到延迟的时候,很 可能会先进行人工补偿,过了一段时间后处理程序又收到消息了,重复处理。我之前就遇到过一次由MQ故障引发的事故,MQ中堆积了几十万条发放原创 2020-07-18 11:05:02 · 738 阅读 · 0 评论 -
23,缓存使用
1,注意缓存雪崩问题 短时间内大量缓存失效的情况。这种情况一旦发生,可能就会在瞬间有大量的数据需要回源到数据库查询,对数据库造成极大的压力,极限情况下甚至导致后端数据库直接崩溃。这就是我们常说的缓存失效,也叫作缓存雪崩从广义上说,产生缓存雪崩的原因有两种:第一种是,缓存系统本身不可用,导致大量请求直接回源到数据库;第二种是,应用设计层面大量的Key在同一时间过期,导致大量的数据回源。第一种原因,主要涉及缓存系统本身高可用的配置,不属于缓存设计层面的问题,所以主要确保大量Key不在同一时间原创 2020-07-14 22:02:48 · 134 阅读 · 0 评论 -
22_1, 接口处理方式要明确同步还是异步, 如文件上传图片返回图片连接和缩略图链接
有一个文件上传服务FileService,其中一个upload文件上传接口特别慢,原因是这个上传接口在内部需要进行两步操作,首先上传原图,然后压缩后上传缩略图。如果每一步都耗时5秒的话,那么这个接口返回至少需要10秒的时间。两个步骤如果都异步会出现下图问题更合理的方式是,让上传接口要么是彻底的同步处理,要么是彻底的异步处理:1. 所谓同步处理,接口一定是同步上传原文件和缩略图的,调用方可以自己选择调用超时,如果来得及可以一直等到上传完成,如果等不及 可以结束等待,下一次再重试;2. 所谓异步处理原创 2020-07-12 20:49:31 · 668 阅读 · 0 评论 -
22, 接口变迁的版本控制策略
以下处理都可以, 第一种和第三种实现方式好一点//通过URL Path实现版本控制@GetMapping("/v1/api/user")public int right1(){return 1;} //通过QueryString中的version参数实现版本控制@GetMapping(value = "/api/user", params = "version=2")public int right2(@RequestParam("version") int version) {retu原创 2020-07-12 20:40:32 · 241 阅读 · 0 评论 -
21, 重复代码处理的两种方式
利用工厂模式+模板方法模式,消除if…else和重复代码**在抽象方法中定义好业务流程, 通用的直接实现, 不同用户的处理方式不同,就抽象出来让子类去实现**public abstract class AbstractCart { //处理购物车的大量重复逻辑在父类实现 public Cart process(long userId, Map<Long, Integer> items) { Cart cart = new Cart(); List<Item> itemLi原创 2020-07-12 20:32:35 · 1068 阅读 · 0 评论 -
17, Spring controller单利, service多例 实现
在service上加上注解@Scope实现多例@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE) controller在调用service时还是service仍然是单利Bean默认是单例的,所以单例的Controller注入的Service也是一次性创建的,即使Service本身标识了prototype的范围也没用解决方式:1, 让Service以代理方式注入。这样虽然Controller本身是单例的,但每次都能从代理获取Serv原创 2020-07-12 10:17:34 · 1182 阅读 · 0 评论 -
16, 日期时间类
1, 定义的static的SimpleDateFormat可能会出现线程安全问题SimpleDateFormat的作用是定义解析和格式化日期时间的模式。这,看起来这是一次性的工作,应该复用,但它的解析和格式化操作是非线程安全的。我们来分析一下相关源码:SimpleDateFormat继承了DateFormat,DateFormat有一个字段Calendar;SimpleDateFormat的parse方法调用CalendarBuilder的establish方法,来构建Calendar;estab原创 2020-07-11 10:42:35 · 179 阅读 · 0 评论 -
15,序列化
关于序列化算法,几年前常用的有JDK(Java)序列化、XML序列化等,但前者不能跨语言,后者性能较差(时间空间开销大);现在RESTful应用最常用的是JSON序列化,追求性能的RPC框架(比如gRPC)使用protobuf序列化,这2种方法都是跨语言的,而且性能不错,应用广泛。序列化和反序列化算法要保持一致Spring中redisTemplate, RedisTemplate默认使用的JDK序列化方式, StringRedisTemplate使用String序列化方式默认情况下,Redi.原创 2020-07-11 09:50:47 · 218 阅读 · 0 评论 -
14.文件IO
第一,如果需要读写字符流,那么需要确保文件中字符的字符集和字符流的字符集是一致的,否则可能产生乱码。**第二,**使用Files类的一些流式处理操作,注意使用try-with-resources包装Stream,确保底层文件资源可以释放,避免产生too many openfiles的问题。第三,进行文件字节流操作的时候,一般情况下不考虑进行逐字节操作,使用缓冲区进行批量读写减少IO次数,性能会好很多。一般可以考虑直接使用缓冲输入输出流BufferedXXXStream,追求极限性能的话可以考虑使用Fi原创 2020-07-09 21:52:25 · 69 阅读 · 0 评论 -
12, 日志记录也会影响性能
slf4j + logback如果我们记录DEBUG日志,并设置只记录>=INFO级别的日志,程序是否也会耗时1秒呢?我们使用三种方法来测试:拼接字符串方式记录slowString;使用占位符方式记录slowString;先判断日志级别是否启用DEBUG。log.debug(“debug1:” + slowString(“debug1”));log.debug(“debug2:{}”, slowString(“debug2”));if (log.isDebugEnabled()){l原创 2020-07-08 22:33:31 · 1616 阅读 · 0 评论 -
12, 异常处理,开发中处理异常
1, 捕获了异常后直接生吞。在任何时候,我们捕获了异常都不应该生吞,也就是直接丢弃异常不记录、不抛出。这样的处理方式还不如不捕获异常,因为被生吞掉的异常一旦导致Bug,就很难在程序中找到蛛丝马迹,使得Bug排查工作难上加难2 ,丢弃异常的原始信息。我们来看两个不太合适的异常处理方式,虽然没有完全生吞异常,但也丢失了宝贵的异常信息。比如有这么一个会抛出受检异常的方法readFile:private void readFile() throws IOException { Files.readAllLi原创 2020-07-06 22:11:30 · 572 阅读 · 0 评论 -
11, 空值处理
NullPointerException是Java代码中最常见的异常,我将其最可能出现的场景归为以下5种:参数值是Integer等包装类型,使用时因为自动拆箱出现了空指针异常;字符串比较出现空指针异常;诸如ConcurrentHashMap这样的容器不支持Key和Value为null,强行putnull的Key或Value会出现空指针异常;A对象包含了B,在通过A对象的字段获得B之后,没有对字段判空就级联调用B的方法出现空指针异常;方法或远程服务返回的List不是空而是null,没有进行判空就原创 2020-07-06 21:31:08 · 251 阅读 · 0 评论 -
10_2, list foreach遍历删除 list.remove(obj)报错ConcurrentModificationException原因
**foreach删除元素报错java.util.ConcurrentModificationException** private static void removeList(){ List<Integer> list = new ArrayList<>(); for (int i = 0; i < 10; i++) { list.add(i); } for (Integer str原创 2020-07-05 10:59:52 · 303 阅读 · 0 评论 -
10_集合类 list操作的坑
Arrays.asList把数据转换为List的三个坑1, 不能直接使用Arrays.asList来转换基本类型数这个List包含的其实是一个int数组,整个List的元素个数是元素类型是整数数组。int[] arr = {1, 2, 3};List list = Arrays.asList(arr); // idea中显示 List<int[]> ints = Arrays.asList(arr);# 修改 使用包装类型 Integer[] array = new In原创 2020-07-05 10:56:05 · 350 阅读 · 0 评论 -
08_ 判等问题
Object超类中的equals默认使用==判等,比较的是对象的引用。原创 2020-07-04 21:09:54 · 114 阅读 · 0 评论 -
06_Spring 事物失效
1, 事务注解的方法要用pulbic声明, private声明会失效理由: CGLIB通过继承方式实现代理类,private方法在子类不可见,自然也就无法进行事务增强@Transactionalprivate void createUserPrivate(UserEntity entity) {}2, 不能调用同类里的事物方法. 必须通过调用事物增强的代理类 的目标方法才能生效。事务是基于动态代理实现的, 调用类里面的方法 self.createUserPublic() (调用的是Use原创 2020-07-04 12:01:09 · 3068 阅读 · 0 评论 -
03_HTTP 超时,重试
1, 配置连接超时和读取超时参数的学问对于HTTP调用,虽然应用层走的是HTTP协议,但网络层面始终是TCP/IP协议。TCP/IP是面向连接的协议,在传输数据之前需要建立连接。几乎所有的网络框架都会提供这么两个超时参数:连接超时参数ConnectTimeout,让用户配置建连阶段的最长等待时间;读取超时参数ReadTimeout,用来控制从Socket上读取数据的最长等待时间。连接超时参数和连接超时的误区有这么两个:连接超时配置得特别长,比如60秒。一般来说,TCP三次握手建立连接需要的时原创 2020-07-04 11:08:02 · 334 阅读 · 0 评论 -
04_连接池
在使用三方客户端进行网络通信时,我们首先要确定客户端SDK是否是基于连接池技术实现的。我们知道,TCP是面向连接的基于字节流的协议:面向连接,意味着连接需要先创建再使用,创建连接的三次握手有一定开销;基于字节流,意味着字节是发送数据的最小单元,TCP协议本身无法区分哪几个字节是完整的消息体,也无法感知是否有多个客户端在使用同一个TCP连接,TCP只是一个读写数据的管道如果客户端SDK没有使用连接池,而直接是TCP连接,那么就需要考虑每次建立TCP连接(3次握手)的开销,并且因为TCP基于字节流.原创 2020-07-04 10:07:53 · 133 阅读 · 0 评论 -
04_2连接池 从连接池获取连接超时, 连接池新建tcp连接超市
有了连接池之后,获取连接是从连接池获取,没有足够连接时连接池会创建连接。这时,获取连接操作往往有两个超时时间:一个是从连接池获取连接的最长等待时间,通常叫作请求超时connectRequestTimeout或等待超时connectWaitTimeout;一个是连接池新建TCP连接三次握手的连接超时,通常叫作连接超时connectTimeout。针对JedisPool、Apache HttpClient和Hikari数据库连接池,你知道如何设置这2个参数吗?假设我们希望设置连接超时5s,获取连接超时原创 2020-07-04 10:07:12 · 457 阅读 · 0 评论