java grpc初体验
定义proto
1 | syntax = "proto3"; |
mybatis SQL注入
mybatis SQL注入
order by
mybatis中#{}表示预编译,${}表示拼接,order by不能做预编译,假如给Flast_update_time排序,预编译后,就变成了order by “Flast_update_time”,所以还是需要使用拼接,这里要注意防注入。
我理解本质原因是预编译后,会给传入的字符串加上双引号。所以order by这种是无法做预编译的,只能拼接。
解决方法
order by的字段不直接拼接前端传入的字符串,而是通过映射转换为实际的字段,这样就可以避免注入
like
如果使用like模糊查询时,如果这样使用,语法会有问题,还是因为双引号。
1 | select * from xxx where xxx like '%#{title}%' |
解决方法
使用concat函数做拼接。
1 | select * from xxx where xxx like concat('%',#{xxx}, '%') |
in查询
解决方法
使用Mybatis自带循环指令
参考
java填坑
java异常
java gson fastjson
rest接口,class上注解@RestController,然后返回的对象会自动转换为json格式,使用的是Jackson,如果返回的对象中有datetime格式的字段,返回的格式是这样的
1 | 2020-04-24T07:31:40.000+0000 |
需要设置jackson格式
1 | spring.jackson.date-format=yyyy-MM-dd HH:mm:ss |
排除全局包
- 模块启动时报错
1
2
3
4
5
6java -jar web_vb_base_ekyc.jar
SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/data/apps/web_vb_base_ekyc/web_vb_base_ekyc.jar!/BOOT-INF/lib/slf4j-nop-1.7.29.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/data/apps/web_vb_base_ekyc/web_vb_base_ekyc.jar!/BOOT-INF/lib/logback-classic-1.2.3.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
SLF4J: Actual binding is of type [org.slf4j.helpers.NOPLoggerFactory]
在pom.xml中增加
1 | <dependency> |
这样相当于全局排除了slf4j-nop
jar包内部构造
打印db查询语句
在mybatis-config.xml中设置
1 | <setting name="logImpl" value="SLF4J"/> |
在logback.xml中配置
1 | <logger name="com.fusionbank.base.ekyc.dao" level="DEBUG" /> |
打印中文乱码
中文解密出来后,首先编码为UTF-8格式
1 | public String decryptString(String sourceStr) |
写入文件时,指定UTF-8
1 | BufferedWriter bw = new BufferedWriter( |
Spring Boot多模块踩坑
多模块pom.xml
packaging
packaging是项目的打包类型,常用的有如下几种:
- war:打包为war包
- jar:打包为jar包
- pom:用在父级工程或者聚合工程中,做jar包的版本控制
module如果访问了外部的程序,需要在pom中引入依赖
1 | <dependency> |
注入问题

解决方法
添加包扫描
1
2
3
4
5
6@SpringBootApplication(scanBasePackages={"com.fusionbank"})
public class HelloWorldApplication {
public static void main(String[] args) {
SpringApplication.run(HelloWorldApplication.class, args);
}
}移动application文件到com.fusionbank层级,本质也是为了扫描到这个层级下的包
异常
写了一个java的middle server,在里面调用的middle版本的server,请求过去后,看java的日志,只打印了
1 | 2020-03-29 11:50:48.542 [nioEventLoopGroup-3-3] ^[[39mDEBUG^[[0;39m c.f.m.a.framework.server.RpcRequestProcessService - middle call service:get_id |
后来发现
1 | public Builder setTableName( |
这里set时传入的字符串未设置,抛出来了异常没有捕捉
redis连接断开
背景
va_list的bug,修改后,在开发环境验证时,发现执行redisCommand函数时,会卡住,直到进程被杀掉重启。为啥生产环境直接进入了第二次循环,没有卡住呢?
两种情况
- redis没有切过master,老的连接有时也不能用,报错
Connection reset by peer,这时会自动重连,大概一天2次这种报错,为啥连接会中断,这个还没找到原因。 - 为何我验证的情景下,请求老的连接时会卡住呢?是因为我是通过封禁redis端口的方法触发redis切换的。切换master后,分两种情况:
redis之前的master变成了slave,再次请求时,报错
server closed the connection redis,这种情况也会自动重连。如果把master的端口禁用,redis切换master后,业务模块再请求之前的连接会卡住,这种情况需要设置超时时间。
1
2
3
4
5
6iRet = redisSetTimeout(m_pstRedisLink, timeout);
if(iRet != 0)
{
m_strErrMsg = "redis set timeout fail";
return iRet;
}
所以当前发到生产环境的版本,redis服务重启的情况,业务模块是可以重连的,网络中断的话,业务模块请求redis时会卡住,直到网络恢复或者middle杀掉进程重启。
分析
封禁端口,网卡就不再处理这个端口的包,相当于网线断开了。这种情况下,客户端收不到服务器的任何回包,就会一直等待,如果没有设置超时时间,就卡住了。
redis有两个配置,一个是timeout,这个参数如果设置为0,表示服务端不会主动断开连接,非0,表示会清理空闲时间超过这个值的连接。另一个是tcp-keepalive,这个参数表示服务器定时发送ack包探测客户端是否存活。

http multipart/form-data
背景
tws从来未处理过视频流。这次主要研究一下怎么接收解析视频信息。
实验
我大概知道Content-Type是multipart/form-data格式,所以首先不做任何转换,将Content-Type和Post直接透传到人脸服务,服务响应正常。证明这两个字段是没有问题的
multipart/form-data
上面的实验没有问题,说明tws只是将原始的数据全部解析到post字段,我们取出来之后,需要按照multipart/form-data协议进行解析。
- 从Content-Type字段中解析boundary字段
- 从post字段中查找以
--boundary字段开始,\r\n\r\n结束的字符串,这个字符串就是每一段的描述信息 - 从描述信息中可以解析出字段名或者文件名
- 继续查找以
\r\n\r\n开始,\r\n--boundary结束的字符串,这个字符串就是字段的value或者文件的具体内容。这里要注意的是,查找时不要用\r\n作为结束标志查找,因为可能文件中刚好有这个字符,造成读取文件不完整。我就遇到了解析出来的视频信息播放有问题,最终发现是这个原因 - 按照上述流程循环解析,直到步骤2没有找到
- 进行后续业务处理
redis中的数据结构
sorted set
sorted set是有序键值对集合,查询结果按照分数排序
1 | zrange key 0 -1 withscores |
还可以根据field获取到score的值
1 | zscore key field |
那么内部的是怎么实现的呢?
跳跃表
跳跃表是链表的升级,查询效率是O(log(N)),比链表查询效率高。新增节点和删除节点时,只需要更新临近节点的指针指向,比平衡树插入和删除时操作简单,属于空间换时间。
但是
- 根据field查询score
- 根据field查询rank
- 查询rank范围内的数据
这三种场景如果在跳跃表中实现,需要遍历,复杂度O(N)。所以,
redis同时使用了哈希表,键为field,值为score,这样就支持了O(1)的复杂度根据field查询score。为了能够支持rank相关查询,redis里对跳跃表做了改进,增加了range,使得查询rank相关的操作也能达到O(log(N))。
bitmap
redis的bitmap结构可以支持位图相关操作,有个命令是bitcount,用来计算为1的位个数,这里引申出来了汉明重量,redis使用的是variable-precision SWAR算法。