关于java项目一些有趣的问题

项目中遇到的问题之ACS容器平台240秒断开空闲TCP连接:
Eviction:驱逐。 validation:验证。
几个概念:
ACS容器平台:
全称阿里云容器服务平台。使用它可以快速的把应用、服务器部署到阿里云计算平台上。
ACS包括集群管理、应用、服务、容器列表、监控等功能。基本涵盖容器服务应该有的功能。
使用的话首先搭建一个集群,每个集群下有多个节点,每个节点也就是一个 ecs(云服务器),然后用户在自己的阿里云账户下创建集群的时候,自己账户下同时会生成多个ecs实例。然后会把这些实例挂到一个负载均衡上面。然后创建应用放上来。
Nat网络地址转换协议:
当在专用网内部的一些主机本来已经分配到了本地IP地址(即仅在本专用网内使用的专用地址),但现在又想和因特网上的主机通信(并不需要加密)时,可使用NAT方法。
即专用网和因特网通信时使用。
特点:可以用宽带分享。 更加安全:nat内的pc端连接internate的时候,对外显示的ip是nat主机的公用ip。所以比较安全。
TCP三次握手:
TCP是一个面向连接的、可靠的、基于字节流传输的网络传输层应用协议。
在客户端和服务器端的通信都要先经过tcp三次握手建立连接。
1:客户端向服务器端发送TCP头部标志位,SYN=1,并且生成一个序号a,把这个数据包发送给服务器端。客户端进入SYN_SEND状态
2:服务器端收到SYN=1则知道了客户端想要建立连接。所以会把TCP头部标志位的ACK=1,ack=a+1,生成一个序号b,发送数据包给客户端,意思就是我知道你要建立链接了,我准备好了。服务器端进入SYN_REVD状态。
3:客户端收到数据后,检查ACK=1,ack=a+1;就把ack=b+1,ACK=1再发给服务器端,服务器
检查ack=b+1,ACK=1,正确则链接创建完成。客户端和服务器端就可以传输数据了。
https://www.cnblogs.com/onesea/p/13053697.html
TCP协议的三次握手四次挥手

https://my.oschina.net/u/4358820/blog/3907579
Java自定义注解的定义与使用
https://blog.csdn.net/junjunba2689/article/details/82778435
基于socket编程

自定义线程池:
场景:
有一个定时调度任务,是定时拉取凭证回收案件,拉取结束后,会把成功的和不成功的组装成list发给总行,同时需要异步记录成功的流水,这个记录流水用到了:
把通知总行成功的案件list用for循环后调用异步流水记录的方法,
方法内组装请求参数,然后调用到了自定义的一个线程池,通过线程的excute方法执行流水洛库的操作。
为什么一定要用线程池呢,我们的定时任务是在凌晨0到6点之间每隔5分钟拉取一次凭证回收案件,当前五分钟拉取完了,对成功案件进行for循环异步调用落库流水,可能还没有完成的时候,到了下一个五分钟,同样调用该方法,可能会演变为并发编程,所以用线程池保证各个人物互不影响。失败的件用日志记录下来。
自定义的线程池,线程数是从dealut.properties文件中拿的,用的是properties类的一个get属性的方法。其实这种方法和springboot中的自动配置的原理是一样的。
然后定义一个单例模式的类方法是new一个定线程数的池子。用的是Executors
类new的。
单例模式:私有静态最终的类。私有构造。私有静态方法。
目的是保证同一时刻只能有一个线程池实例存在。
如果不做这个,每次new一个自定义线程对象都会生成一个含有60个线程的线程池,除了造成极大的内存资源浪费,同时会导致内存溢出。
用了单例,虽然没有直接去关闭线程池,只要每天的案件都走完,从队列里面,并不会造成队列里的线程越来越多,也避免了内存溢出,所以必须用。
用的这个线程池它的源码其实就是:
NewfixedThreadPool:这个线程池其实创建的时候,构造函数是有参数的,其中的参数是核心线程数,空闲时间0,以及一个无界的队列。
其实在线程进来的时候我们执行的是excute方法,这个方法有很多门道:
这个方法里有几个判断,就是当线程数小于设定的线程大小时会执行addWorker方法进行添加线程,如果大于并且小于无界队列的最大值也就是最大integer的值的时候执行workQueue.offer方法
也就是说比如60个设定线程值,有100个进来有60进入到了池子中,还有40个是在无界队列里排队的。每一个线程其实都是一个对象,如果一下子对象创建的足够多,而你设定的jvm的xmx和xms又小的话,是很容易导致内存溢出的。不过我们的场景不容易出现,我们的xmx和xms都是512m,而且我们定时拉取得数量没那么大,也不会阻塞那么多线程。
它的无界队列实例里面用到了ReentrantLock 来保证入队和出队的原子性。无界队列其实就是一个单向链表,在入队和出队的时候其实用到了一些条件队列的判断notempty和notFull,这些队列存放出队和入队时候的阻塞的线程。其实也是一个生产者消费者模式。
https://blog.csdn.net/wanghao112956/article/details/100511180

线程池:
1;解决频繁创建和销毁线程的资源消耗。
2:提高响应速度。 3:重复利用,线程用完以后会放回线程池重复利用。

Tomcat的OutOfMemoryError: PermGen Space错误:
Java7会产生PermGen 8改成了Metaspace
产生背景就是,在开发核心审核系统的时候,接口开发完毕后,打算用postman测试一下,于是debug启动项目,用的是该系统的uat环境标准的tomcat配置启动的。然后项目启动到最后,报OutOfMemoryError: PermGen Space。
我写的代码是加了一个借口,就是利星行二手车从前端传过来一个下拉框是,我去查库里面车子品牌包含该字符串值的数据,传否,则查出不包含的和null值得数据。内容就在请求类和实体类扩了该栏位,在mybatis中去查询,where条件改一下就好了。
SubbrandName == “Y” tostring() 则 like “N”则 not like or is null
使用 -Xmx 增加堆大小
XX:-UseGCOverheadLimit 取消 GC 开销限制
使用 -XX: MaxPermSize 增加 Permgen 大小
-XX: MaxMetaSpaceSize 增加 metaspace 大小
https://virtual.51cto.com/art/201908/602166.htm

方向:
1:虚拟机栈、堆、方法区都存放什么内容。
JVM内存分为虚拟机栈,本地方法栈、程序计数器(这三个是线程私有的)。
方法区、堆(线程共享的)
虚拟机栈中存放:
局部变量表(八个基本数据类型,类的参数,string等,在编译的时候就确定了内存大小)、操作数栈(寄存区,临时变量在编译时就确定了栈深度)、动态链接(一个方法调用另一个方法的时候会存储一个地址符号)、方法出口()
虚拟机栈的默认大小是1M。
方法区内在1.7的时候主要存放永久代:
就是类的结构信息。在1.8的时候存放的是元空间。元空间相比于永久代与堆内存不再连续,元空间内存不在虚拟机里面,而是直接设置在本地内存上。在永久代的时候需要设置MaxPermSize和PermSize大小,如果不设置默认的大小很小,容易出现oom的问题,设置的话,对大小的可用性不好把握。
存放内容:
类名、类的类型(class/interface)、类修饰符(public\private)、常量、静态变量、方法信息、指向类的实例(class.forname)。

程序计数器:
记录下一条命令的地址。
本地方法栈:
Java底层是调用的c/c++的类库,这个是存储那些的内存,一般不用。
堆内存:
存放的是java对象,和静态变量。
主要分为新生代1/3和老年代2/3两部分。Eden区8/10的新生代,
From survivor1/10新生代,to survivor1/10新生代
-Xms堆内存初始大小设置,1/64系统内存
-Xmx 堆内存最大值,1/4系统内存
堆内存中存放的是java对象,一个新创建的对象会在endn区申请一个内存空间,放得下,放进去,放不下,就进行一次minor GC,然后释放eden区,清理后的数据放入from survivor 然后分代年龄加一。然后当eden区下次再满的时候,会再触发minor GC这个时候会清理eden和from survivor区,放入到to survivor中,然后年龄再加一,这样from和to总有一个闲置,反复出发后当年龄达到15的时候,他们就不再是新生代,而进入老年了。
对象进入老年代的方式:
1:当survivor中的同一年龄的对象空间达到这个survivor的50以上的时候,会把大于等于这个分代年龄的对象进入老年代。
2:大对象直接进入老年代(大于1M)。
3:minor GC之后的对象在survivor中放不下进入老年代。
对象进入老年代之后,如果老年代区域满的话,触发的是FULL GC,或者直接执行system.gc()方法(危险)。
项目中会把XMS和XMX的内存调到一样大,目的是防止内存抖动。
内存抖动:
当频繁的创建销毁对象的时候,内存会被不断地开辟,销毁可能会慢,这样会导致内存不连续,内存不连续浪费空间,容易导致OOM的问题。
存放内容:new的类的实例,数组等。

2:java程序在jvm内运行的流程:
虚拟机三大功能模块:类加载器(方法区)、垃圾回收机制(方法区、堆)、执行引擎(除了本地方法栈的其余四个内存块)。
Javac把.java文件编译成.class文件。类加载器把.class文件存到内存中。执行引擎找到main()方法执行里面的指令。最后垃圾回收。

3:垃圾回收机制中的垃圾回收在什么情况下回收相应的内存。
永久代中的垃圾回收:废弃常量(整个系统没有引用)和无用的类(类加载器被回收,没有被引用)会在垃圾回收的时候回收掉。
新生代和老年代用分代算法回收。

4: 设置虚拟机的配置,怎样去配置几个内存分配。
-Xms 堆内存(启动时堆内存)512
-Xmx 堆内存最大值(运行时堆内存)512
-Xss 线程内存(虚拟机栈内存)默认1M
-Xmn 新生代内存 124
-XX MaxPermSize 永久代最大 1024
-XX PermSize 永久代初始。1024 本来是50.

结束
贷后系统的跨域问题:
从git上pull出贷后服务端和前端项目。前端使用的是react框架。在开发的时候,需要前后端联调。Debug启动后端项目,用的是tomcat的sit(8080)测试环境配置启动,然后前端npm run dev(3000)启动。

查看原因,因为本来使用的是@crossOrign注解来解决跨域问题的。
我的做法是在controller的方法下加上该注解。并且在注解后面加上origins对应的域名,和maxAge允许响应的时间。请求失败后,决定用nginx来解决。
然后想到的是利用nginx反向代理来解决问题。
下载nginx包,打开conf文件,添加一个server,监听一个端口,这个端口是在前端react的env客户端下写的,这里写的是,react访问的后端路径是哪里按照这个去监听,把后端8080的端口地址写进去,然后同样编写location前端地址3000。最后打开nginx,测试80端口,nginx可以正常启动,启动后端,前端,然后按照代理的端口号4396去访问,发现失败。单个启动都没问题。
于是思考整个系统的架构,在我们进如这个系统的时候,需要行内人员给我们配置iam权限,然后我是没有这个权限的,也不给加。后来就在前端去找这个验证权限的方法checkpageauth(),通过这个方法跳转到后端,我在后端切了测试分支,把权限返回接口返回一个固定的有权限的值,同时在tomcat的context文件上把iam启动赋值false。问题解决。

利星行二手车的查询牵扯出的索引问题:
前端需要括一个下拉框,利星行二手车中心传入该值,然后在mybatis层需要去查,因为该子品牌是一个字符串,比如利星行二手车中心_吉利,这样的字符串,本来写的是%利星行二手车中心%,然后在代码review的时候,组长让我调研一下,数据库里的子品牌的数据格式是什么,是不是可以用到索引查询。
利用,新建index,然后我试了一下不用索引的话大概54ms,用的话50ms。
创建索引后什么情况下会走索引查询:
走不走索引其实就是你的查询条件能不能给一个固定的值,比如
1:在使用模糊查询的时候,%在后面的时候走索引,比如like ‘nihao%’.
其他的模糊查询不会走。
2:> ,< ,= ,between IN等等这些你可以给定一个目录值,都是可以用到索引的。
3:当你不能在开头给一个固定值得情况下,是不可以使用的,比如not in ,%…
!= 或则在列中需要函数计算的情况,等等这些是不可以的。

什么样的字段适合创建索引:
1:一般在where\order by\group by后面的字段是有必要加的,可以避免没必要的硬盘开销,不过要适度。2:用于链接其他表的表的外键。3:单表数据太少,或则频繁的更新删除的数据字段最好不建索引,反而会降低效率。

索引的类型:
从物理角度来看:
聚集性:就是主键索引,具有唯一性,一般一个表只能有一个,因为你不能 按照两个去排序。
非聚集性索引:
从逻辑角度来看:
复合索引:复合索引是指把多列绑在一起进行索引,而它索引的顺序是从左 向右,比如你的索引是AB,如果你查A…可以走索引,如果你查B…就不 会走了。
普通索引:
唯一键索引:
主键索引:
空间索引:
https://segmentfault.com/q/1010000003832312
索引的底层原理:
Mysql的索引是b+tree,oracle是b_tree

https://www.cnblogs.com/isykw/p/7190270.html
http://blog.codinglabs.org/articles/theory-of-mysql-index.html

Sqlserver的分页查询:
select * FROM (查询1)as temp where temp.rownum>(25)
查询1:
select TOP (3
5) row_number() over (order by r.KeyId desc) AS rownum,r.* FROM ParamReviewRecord r
就是先写子查询,子查询是用row_number() over关键字查出rownum,并用top去搂出前多少条数据。然后把子查询放入到主查询里作为表,取出表的前多少条数据。
Oracle分页查询:
select rownum r,e.* from emp e where rownum <=10; 查询1
select rownum,t.* from (查询1) t where r>5;
这里是用rownum代替sqlserver的row_number() over,而且它是从0开始的,而sql server是从1开始的。
Mysql:
Select * from table_name limit 5,9

业务问题:
业务需求说的是前端传入子品牌利星行二手车中心下拉框,是或则否,然后我在服务端写查询接口,如果按照他的逻辑,就是一路把Y或则N传到mybatis层中,然后查询之前,先判断是Y还是N,是Y的话,查询like“%二手车%”的数据。
N的话查询NOT LIKE or IS NULL的数据。这样就把查询写死了。下次业务再提需求的话还要改很多东西。于是,我把是或者否写成枚举传进来二手车,一路传下来,我的查询就是like subBrandName或则not like和is null来写,写成传入的变量,而不是硬编译。这样保证了前端达到业务要求也要使得后端下次修改方便。

转人工处理建议参数的逻辑问题:
从渠道端获取三个转人工原因码:映射不同的值,每个原因码对应一个优先级,这是两个映射。比如:年龄不符、共债黑名单、黄金眼黑名单等对应转人工码1,
人工码1对应的优先级为1;然后人工处理参数需要从三个原因码中的优先级最大的值获取对应的人工处理参数的映射值。当时的业务方是认为做一个新增接口,一个从后端取出原因码获取最大值,枚举映射值。我的考虑是新增接口,可以直接加入到库里面,直接从库里查更方便。业务认为新增是为了以后再扩展优先级而准备。

利星行二手车参数复核接口:
场景是征信审核想在参数配置页面配置利星行二手车的参数,生效标识,经销商名称等,配置完成后,需要给经办人员进行复核是新增还是修改还是删除,这里就是复核的接口,是传进去通过/踢腿,然后到达business层,取出从配置类写的获取利星行的标识参数,然后用它去调用新增修改删除中的源业务代码,就是一个接口,一个接口的方法对应很多实现类,其实我们在前面需要做一个Map<String, ParamReviewCallBackStrategy>
的集合,来映射具体走那个参数复核比如利星行,加急件,突发事件等等。然后实现类里面使用的是@compoent注解来说明对应哪个map的key的。

这里面用到了经典模式中的工厂模式:
在这里插入图片描述
在这里插入图片描述

这里的strategyFactory是一个工厂,里面存放的是一个map<String impl是实现类>这样一个集合。当我们传入lxh进入第一步的时候,是获取一个map为lxh的值,然后把它传入工厂,获取一个工厂类实例,当我们操作类型为新增时,进入虚拟的产品类,里面有新增修改删除的产品接口。然后根据你传入的lxh去找到在compoent注解加的值为lxh的具体产品,然后执行复核操作的新增操作。

这个简单的工厂模式如果是按照开闭原则:比如增加个比亚迪二手除了需要修改实现类还需要在工厂上配置这个参数,并不满足开闭模式。但是是满足依赖倒转原则的就是在实现类上扩展,只暴露接口给高端调用。

档案出库的时候客服人员可以个案解抵押,经办人员可以批量解抵押。
有一个瞬间在02秒的时候客服对001案件进行了个案解抵押,正常来说它的状态在07秒的时候会是解抵押申请复核。但是它在04秒的时候对这笔件做了批量解抵押导入,在09秒的时候批量案件才会走到工作流比较它的案件状态是不是当前状态,自然是不是的抛出异常。
因为在批量导入的以后的09秒以前,个案已经完结,所以我们导入的十几个案件肯定有这么一个是不变的,导入不进去的。他们前端是无感的,只以为这笔件坏了,其实我们做一个补偿就行,手动恢复案件状态的补偿,因为加入乐观锁消耗太大,不值得。

关于vincio收口的请求和返回:
如果是3.7的话:
就用对象来请求和接收,在构造请求时直接用对象回收一样。
3.10的时候用json:
请求的时候用 JsonUtils.objectToJsonString
返回的时候用JsonUtils.stringToClassOfT

Const var let的区别:
先了解一个概念:变量提升:
就是变量可以先使用后声明。
1:Var可以变量提升,let和const却不可以。
2:方法体内的var变量只能方法体内用,方法体外的可以整个页面js文件使用。
3:let和const是块级局部变量,也就是在本代码块中使用。
4:const在声明的时候必须赋值而且只能赋值一次,第二次就报错。
5:同一作用域下,const和let只能声明不同变量,而var是可以的。

在这里插入图片描述

  • 0
    点赞
  • 0
    评论
  • 0
    收藏
  • 打赏
    打赏
  • 扫一扫,分享海报

参与评论
请先登录 后发表评论~
©️2021 CSDN 皮肤主题: 数字20 设计师:CSDN官方博客 返回首页

打赏作者

理想是顶级架构师

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值