0%

Tomcat类加载器

java本身的类加载器是双亲委派机制,而tomcat的应用场景是需要web应用类库隔离的,为了避免应用包之间相互影响,所以java原生的双亲委派是不可以在tomcat中使用了

tomcat类加载器

可以看到tomcat实现了自己的类加载器模型,在catalina.properties中进行配置

1
2
3
4
5
common.loader="${catalina.base}/lib","${catalina.base}/lib/*.jar","${catalina.home}/lib","${catalina.home}/lib/*.jar"

server.loader=

shared.loader=

只有指定了server.loader和shared.loader才会真正建立Catalina类加载器和Shared类加载器的实例

阅读全文 »

POI读取合并单元格内容

产品们想到的excel真是千奇百怪,这次又遇到一个读取包含合并单元格的excel文件,一开始直接读取,直接报空了,没办法,只能对于这些列进行判断是否为合并单元格

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
private boolean isMergedRegion(Sheet sheet, int row, int column) {
int sheetMergeCount = sheet.getNumMergedRegions();
for (int i = 0; i < sheetMergeCount; i++) {
CellRangeAddress range = sheet.getMergedRegion(i);
int firstColumn = range.getFirstColumn();
int lastColumn = range.getLastColumn();
int firstRow = range.getFirstRow();
int lastRow = range.getLastRow();
if (row >= firstRow && row <= lastRow) {
if (column >= firstColumn && column <= lastColumn) {
return true;
}
}
}
return false;
}

如果是合并单元格的话,使用单独的方法来进行读取

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public String getMergedRegionValue(Sheet sheet, int row, int column) {
int sheetMergeCount = sheet.getNumMergedRegions();
for (int i = 0; i < sheetMergeCount; i++) {
CellRangeAddress ca = sheet.getMergedRegion(i);
int firstColumn = ca.getFirstColumn();
int lastColumn = ca.getLastColumn();
int firstRow = ca.getFirstRow();
int lastRow = ca.getLastRow();
if (row >= firstRow && row <= lastRow) {
if (column >= firstColumn && column <= lastColumn) {
Row fRow = sheet.getRow(firstRow);
Cell fCell = fRow.getCell(firstColumn,Row.MissingCellPolicy.CREATE_NULL_AS_BLANK);
return fCell.toString();
}
}
}

return null;
}

POI读取excel报错

由于有时候上传的是xls文件有时候上传的是xlsx文件,使用POIFSFileSystem来读取文件

1
2
3
4
5
6
7
POIFSFileSystem poifsFileSystem;
try {
poifsFileSystem = new POIFSFileSystem(new FileInputStream(file));
} catch (IOException e) {
LOGGER.error("open xls file failure", e);
throw new BusinessException(1, "打不开xls文件");
}

经常会遇到

1
The supplied data appears to be in the Office 2007+ XML. You are calling the part of POI that deals with OLE2 Office Documents. You need to call a different part of POI to process this data (eg XSSF instead of HSSF)

解决方法:

使用WorkbookFactory来读取excel文件

1
2
3
4
5
6
7
Workbook workbook;
try {
workbook = WorkbookFactory.create(new FileInputStream(file));
} catch (IOException e) {
LOGGER.error("open xls file failure", e);
throw new BusinessException(1, "打不开xls文件");
}

spark优化

  • 算法性能优化

    • 使用 reduceByKey/aggregateByKey 替代 groupByKey
    • 使用带 Partitions 的 API 进行计算(一次函数调用处理一个 Partition),如 mapPartitions 替代 map
    • 使用手动添加前缀的方式优化由于数据倾斜带来的性能问题
  • 并行度优化

    • 使用带有numPartitions参数的,可以配置分区数量
    • 通过配置spark.default.parallelism来控制Shuffle过程的默认任务数量。spark官方推荐cpu num*2数量的并行度
  • 缓存优化

    • 调用 cache persistunpersist 以便控制哪一个 RDD 需要缓存
    • 缓存 RDD 时考虑使用序列化缓存,进一步考虑压缩
  • 内存优化

    • 使用更节约内存的数据结构:如避免使用 java 的包装类型(boxed),避免使用内置的 Map List 等数据结构(会创建额外的 Entry 对象)等
    • 使用广播变量:对于某个只读的大对象,在一个 Executor 内部共享,而不是每个 task 都复制一份
    • 调整 spark 管理的内存大小:配置 spark.memory 相关参数
    • 调整 JVM 的新生代和老生代内存比例
    • gc 优化:使用 G1 垃圾收集器
  • 其他有用的优化方式

    • 资源:配置 executor 的数量,每个 executor 的核数及内存,driver 的核数和内存
    • 调度:配置是否重启一个较慢的任务,设置 spark.speculation 相关参数
    • IO:使用节约空间的序列化方式,如配置 kryo 序列化,调整本地化程度等待时间 spark.locality.wait 参数
  • 分区问题

    • 小分区合并问题 使用filter进行频繁过滤或过滤掉的数据量过大会造成大量的小分区产生。由于spark是每个数据分区都会分配一个任务执行,如果任务过大,每个任务处理的数据量很小,会造成线程切换开销大,很多任务等待执行,并行度不高。可以使用RDD中重分区repartition进行数据紧缩,减少分区数,将小分区合并为大分区。

    • 倾斜问题 在个别分区上,任务执行时间过长。当少量任务处理的数据量和其他任务差异过大时,任务进度长时间维持在99%。

      产生数据倾斜的原因有几种

      • key的数据分布不均匀
      • 业务数据本身就会产生数据倾斜
      • 结构化数据表设计问题
      • 某些SQL语句会产生数据倾斜

      产生任务倾斜的原因不太好确定,一般是那台机器正在执行的Executor执行时间过长,因为服务器架构,或JVM,也可能是来自线程池的问题。

AES对称加密

AES作为DES的替代者,比DES更为安全

加密

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public static String aesEncode(String content) {
try {
SecureRandom random = SecureRandom.getInstance("SHA1PRNG");//修改后
// 私钥
random.setSeed("PASSWORD_CRYPT_KEY".getBytes());
//1.构造密钥生成器,指定为AES算法,不区分大小写
KeyGenerator keygen = KeyGenerator.getInstance("AES");
//2.根据ecnodeRules规则初始化密钥生成器
//生成一个128位的随机源,根据传入的字节数组
keygen.init(128, random);
//3.产生原始对称密钥
SecretKey original_key = keygen.generateKey();
//4.获得原始对称密钥的字节数组
byte[] raw = original_key.getEncoded();
//5.根据字节数组生成AES密钥
SecretKey key = new SecretKeySpec(raw, "AES");
//6.根据指定算法AES自成密码器
Cipher cipher = Cipher.getInstance("AES");
//7.初始化密码器,第一个参数为加密(Encrypt_mode)或者解密解密(Decrypt_mode)操作,第二个参数为使用的KEY
cipher.init(Cipher.ENCRYPT_MODE, key);
//8.获取加密内容的字节数组(这里要设置为utf-8)不然内容中如果有中文和英文混合中文就会解密为乱码
byte[] byte_encode = content.getBytes(StandardCharsets.UTF_8);
//9.根据密码器的初始化方式--加密:将数据加密
byte[] byte_AES = cipher.doFinal(byte_encode);
//10.将加密后的数据转换为字符串
// 不使用BASE64Encoder由Base64替代
// return new BASE64Encoder().encode(byte_AES);
//11.将字符串返回
return Base64.encodeBase64String(byte_AES);

} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException | BadPaddingException e) {
e.printStackTrace();
}

//如果有错就返加nulll
return null;
}

解密

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public static String aesDncode(String content) {
try {

SecureRandom random = SecureRandom.getInstance("SHA1PRNG");//修改后
// 私钥
random.setSeed("PASSWORD_CRYPT_KEY".getBytes());
//1.构造密钥生成器,指定为AES算法,不区分大小写
KeyGenerator keygen = KeyGenerator.getInstance("AES");
//2.根据ecnodeRules规则初始化密钥生成器
//生成一个128位的随机源,根据传入的字节数组
keygen.init(128, random);
//3.产生原始对称密钥
SecretKey original_key = keygen.generateKey();
//4.获得原始对称密钥的字节数组
byte[] raw = original_key.getEncoded();
//5.根据字节数组生成AES密钥
SecretKey key = new SecretKeySpec(raw, "AES");
//6.根据指定算法AES自成密码器
Cipher cipher = Cipher.getInstance("AES");
//7.初始化密码器,第一个参数为加密(Encrypt_mode)或者解密(Decrypt_mode)操作,第二个参数为使用的KEY
cipher.init(Cipher.DECRYPT_MODE, key);
//8.将加密并编码后的内容解码成字节数组
// byte [] byte_content= new BASE64Decoder().decodeBuffer(content);
byte[] byte_content = Base64.decodeBase64(content);
/*
* 解密
*/
// byte[] resBytes = content.getBytes(StandardCharsets.UTF_8);
byte[] byte_decode = cipher.doFinal(byte_content);
return new String(byte_decode, StandardCharsets.UTF_8);
} catch (NoSuchAlgorithmException | InvalidKeyException | NoSuchPaddingException | BadPaddingException | IllegalBlockSizeException e) {
e.printStackTrace();
}

//如果有错就返加nulll
return null;
}