Android端RSA加密数据送Java服务端解密时出现BadPaddingException


原因:Android系统使用的虚拟机(dalvik)跟SUN标准JDK是有所区别的,其中他们默认的RSA实现就不同。即Android端用Cipher.getInstance(“RSA”)方法进行加密时,使用的provider是Bouncycastle Security provider,Bouncycastle Security provider默认实现的是“RSA/None/NoPadding”算法,而服务器(PC)端用Cipher.getInstance(“RSA”)进行解密时,使用的是Sun的security provider,实现的是“RSA/None/PKCS1Padding”算法,所以,解密时会失败。

正确的设置方法:Java服务端代码

Cipher cipher = Cipher.getInstance(“RSA”);

Android端代码

Cipher cipher = Cipher.getInstance(“RSA/None/PKCS1Padding”);

Advertisements

解决Cassandra节点一直报告”received an invalid gossip generation”问题


有一个月多月的时间Cassandra节点一直报告“received an invalid gossip generation for peer xxx.xxx.xxx.xxx; local generation = 1414613355, received generation = 1450978722”,导致不能创建表。

试过重启报告问题的节点,以及集群内所有节点逐个重启,问题都没有解决。

最近通过Nodes showing DN in nodetool status with “invalid gossip generation” warning in logs文章找到了long-running cluster sees bad gossip generation when a node restarts文章,有朋友报告,重启整个集群解决了问题,突然来了灵感,找了一个业务空闲的时间,将所有集群节点全部停止,再次重启,问题解决。

之前一直有个误区,认为重启集群,是集群中的节点逐个重启一遍,原来是需要停掉集群所有节点,再次启动。

Nginx配置只能通过域名访问,禁止通过IP访问


为了避免客户直接使用IP地址访问我们的网站,导致运维需要变更域名对应IP时引发客户报障,我们可以通过Nginx进行域名访问的限制性配置,具体配置如下:

修改nginx.conf 文件
修改成

server {
    listen 80;
    server_name www.example.com; #这里是你自己指定的域名
    ...
}

再在上个server后继续添加一段:
如果没有找到域名配置的server,会继续向后查找server,走到默认server直接返回403

server {
    listen 80 default_server;
    server_name _;
    return 403;
}

通过以上配置,当使用IP访问时就会返回403错误

Nginx限速配置心得


参考:Rate Limiting with NGINX and NGINX Plus

  • 令牌桶设置技巧——不做缓冲令牌桶,超速直接返回
    nginx做限速,设置不做缓冲,burst=0不能写在配置文件里,直接不写burst参数默认为0
    增加nodelay参数,立即返回错误

    limit_req zone=xxxx nodelay;

  • 超速默认返回503(Service Unavailable),设置limit_req_status参数可指定返回的错误代码

    limit_req_status 429;

    429(Too Many Requests),参考:RFC 6585 – Additional HTTP Status Codes – IETF Tools

  • limit_req可以配置成Server,Location多个位置

Servlet, Struts2 Action, SpringMVC Control是否单例测试结果


同事咨询Servlet, Struts2 Action, SpringMVC Control的应用情况,针对是否单例,运行工程实际测试了一下。

测试方法:

  • 在Servlet.service方法中打印对象的hashcode值
  • 在Action.excute方法中打印对象的hashcode值
  • 在Control.XXX方法中打印对象的hashcode值

测试结果:

  • Servlet是单例的,多个请求共用一个对象。
  • Struts2 Action是多例的,每次请求都新生成一个实例对象。
  • SpringMVC Control是单例的,多个请求共用一个对象。

结论:

对于高并发的Web应用,建议不使用Struts,每个请求新生成一个对象,是个比较大的开销,同时Struts在开发上限制太多和直接写Servlet无太大区别,缺乏灵活性。

 

使用POI输出超过65536行大Excel(SXSSF技术)


一直以来都用POI做Excel导出,之前导出的Excel格式都是2003版的——XLS,最近客户要求导出超过65536条数据,超出了XLS的上限,所以研究起XSSFWorkbook的用法。

原本以为只是把HSSFWorkbook替换成XSSFWorkbook就能导出数据,无奈总是报告内存溢出。放狗查找资料,最终锁定SXSSFWorkbook类。

首先参考Upgrading to POI 3.5, including converting existing HSSF Usermodel code to SS Usermodel (for XSSF and HSSF)完成HSSFWorkbook转换为XSSFWorkkbook的转换,注意使用CreationHelper类。

再参考SXSSF (Streaming Usermodel API)完成大文件的输出

import junit.framework.Assert;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.util.CellReference;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;

public static void main(String[] args) throws Throwable {
    SXSSFWorkbook wb = new SXSSFWorkbook(100); // keep 100 rows in memory, exceeding rows will be flushed to disk
    Sheet sh = wb.createSheet();
    for(int rownum = 0; rownum < 1000; rownum++){
        Row row = sh.createRow(rownum);
        for(int cellnum = 0; cellnum < 10; cellnum++){
            Cell cell = row.createCell(cellnum);
            String address = new CellReference(cell).formatAsString();
            cell.setCellValue(address);
        }

    }

    // Rows with rownum < 900 are flushed and not accessible
    for(int rownum = 0; rownum < 900; rownum++){
      Assert.assertNull(sh.getRow(rownum));
    }

    // ther last 100 rows are still in memory
    for(int rownum = 900; rownum < 1000; rownum++){
        Assert.assertNotNull(sh.getRow(rownum));
    }

    FileOutputStream out = new FileOutputStream("/temp/sxssf.xlsx");
    wb.write(out);
    out.close();    // dispose of temporary files backing this workbook on disk
    wb.dispose();
}

写在最后

  • SXSSF通过把数据缓存到磁盘临时文件,完成超大Excel的输出。
  • 一个Sheet对应一个临时文件
  • 通过SXSSFWorkbook构造函数指定多少条写入临时文件,实际测试证明,维持默认100条能够获得较好的性能。
  • 最后不要忘记使用SXSSFWorkbook.dispose()方法清除临时文件,该方法返回true,则表示所有的临时文件都已经清除。
    该方法实际最终执行SheetDataWriter.dispose(),此方法内部执行java.io.Writer.close()方法释放资源,同时执行java.io.File.delete()方法删除临时文件。
  • 如果碰到临时文件空间的问题,可以尝试启动GZIP,用CPU来换空间了。

GZIP启用方法:

SXSSFWorkbook wb = new SXSSFWorkbook(); 
  wb.setCompressTempFiles(true); // temp files will be gzipped