Java EE 下利用Servlet filter接口 和 GZIPOutputStream 包装流 解决web应用中网络传输数据量大的问题

本文介绍如何在JavaEE环境下利用Tomcat内置功能及Servlet Filter实现GZIP压缩,有效减少网络传输数据量,提升加载速度。

http://www.cnblogs.com/jbelial/archive/2013/07/21/3204415.html

http://log-cd.iteye.com/blog/423179

Java EE 下利用Servlet filter接口 和 GZIPOutputStream 包装流 解决web应用中网络传输数据量大的问题

一、Tomcat 直接开启GZIP

    gzip是http协议中使用的一种加密算法,客户端向web服务器端发出了请求后,通常情况下服务器端会将页面文件和其他资源,返回到客户端,客户端加载后渲染呈现,这种情况文件一般都比较大,如果开启Gzip ,那么服务器端响应后,会将页面,JS,CSS等文本文件或者其他文件通过高压缩算法将其压缩,然后传输到客户端,由客户端的浏览器负责解压缩与呈现。通常能节省40%以上的流量(一般都有60%左右),一些PHP,JSP文件也能够进行压缩。

    打开Tomcat 目录下的conf下的server.xml,并找到如下信息: 
   Xml代码  收藏代码
<!-- Note : To use gzip compression you could set the following properties :  
   compression="on"   
   compressionMinSize="2048"   
   noCompressionUserAgents="gozilla, traviata"   
   compressableMimeType="text/html,text/xml"  
-->  
把它们加入到你配置的<Connector port="80" .../>中去。如果要压缩css 和 js,加入compressableMimeType="text/html,text/xml,text/css,text/javascript"。还要压缩图片,加入compressableMimeType="text/html,text/xml,text/css,text/javascript,image/gif,image/jpg"。 
 开启后重启Tomcat ,通过浏览器查看headers信息就能看到是否开启。  

二、利用Serlvet 下的filter接口实现GZIP

     1、进行gzip压缩条件:

     a、 请求头:Accept-Encoding : gzip  告诉服务器,该浏览器支持gzip压缩。
     b、响应头:Content-Encoding : gzip.  告诉浏览器,输出信息用gzip进行压缩了。
     c、两个主要类:
        ByteArrayOutputStream : 内存输出流,还有缓存。
        GZIPOutputStream 包装流;
     2 、gzip 压缩步骤:
   a、获取字符的字节数组 byte[] buf = str.getBytes() ;
   b、通过GZIPOutputStream 包装流进行输入:
     创建 GZIPOutputStream 输出流时,需要传一个带有缓冲区的输出流,所以我们ByteArrayOutputStream 输出流。而且,ByteArrayOutputStream还可以获取byte[];
   c、将ByteArrayOutputStream 流中的缓存数据,转换成字节数组。
   d、将 压缩后的字节数组通过response 进行输出。不过输出之前要设置Content-Encoding 响应头,value为gzip。告诉浏览器数据进行了gzip压缩,要使用gzip解压。
             String str = "我是个测试";
            //1\获取字节数组
            byte[] bytes = str.getBytes() ;
             System.out.println("压缩前的长度:" + bytes.length); 
            //2\
            ByteArrayOutputStream baos = new ByteArrayOutputStream() ; 
            GZIPOutputStream  gzip = new GZIPOutputStream(baos) ;
            gzip.write(bytes) ; 
            gzip.close() ;
           //3\
            bytes = baos.toByteArray() ;
            System.out.println("压缩后的长度:" + bytes.length);
复制代码
    数据较小是,压缩的效果不是很明显,不过数据越大,压缩效果越明显。所以,GZIP压缩一般只处理文本内容,对图片、已经压缩过的文件则不进行压缩。这时就要在配置文件时,配置要过滤的资源。

3、GZIPFilter类代码如下

 import itheima.decorator.MyHttpServletResponse;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.util.zip.GZIPOutputStream;
 import javax.servlet.Filter;
 import javax.servlet.FilterChain;
 import javax.servlet.FilterConfig;
 import javax.servlet.ServletException;
 import javax.servlet.ServletRequest;
 import javax.servlet.ServletResponse;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 /**
  * Gzip压缩过滤器
  * @author 贺佐安
  *
  */
 public class GZIPFilter implements Filter{
 
     public void init(FilterConfig filterConfig) throws ServletException {
    }
 
     public void doFilter(ServletRequest req , ServletResponse resp ,
             FilterChain chain) throws IOException, ServletException {
         HttpServletResponse response = (HttpServletResponse) resp ; 
         HttpServletRequest request = (HttpServletRequest) req ;
        //创建HttpServletResponse 包装类的实例
        MyHttpServletResponse myResponse = new MyHttpServletResponse(response) ;
        
        chain.doFilter(request, myResponse) ;
         
         //GZIP压缩:
        byte[] buff = myResponse.getBufferedBytes() ;  
         //创建缓存容器:
         ByteArrayOutputStream baos = new ByteArrayOutputStream() ;
         
         GZIPOutputStream gzip = new GZIPOutputStream(baos) ;
         
        gzip.write(buff) ;
         
         gzip.close() ;
         
        buff = baos.toByteArray() ;
          
         //设置响应头;
         response.setHeader("Content-Encoding", "gzip");
         response.setContentLength(buff.length) ;  
        response.getOutputStream().write( buff) ;
     }
 
     public void destroy() {
     }
 
 }
  步骤:
    a、对HttpServletResponse 进行包装 :改写getOutputStream()、getWriter() 方法,并且设置一个临时容器,存储Serlvet处理后要输出的数据。 这里是重点。
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.OutputStreamWriter;
 import java.io.PrintWriter;
 
 import javax.servlet.ServletOutputStream;
 import javax.servlet.http.HttpServletResponse;
 import javax.servlet.http.HttpServletResponseWrapper;
 /**
 * 对HttpServletResponse 进行包装
  * @author 贺佐安
  *
 */
 public class MyHttpServletResponse extends HttpServletResponseWrapper {
    //定义一个容器,用来存储Serlvet 处理完后response 写出的数据
     private ByteArrayOutputStream bos = new ByteArrayOutputStream()  ;
    private PrintWriter printWriter = null;
    public MyHttpServletResponse(HttpServletResponse  response) {
       super(response) ;
    }
    //处理字节流输出的情况
     public ServletOutputStream getOutputStream() throws IOException {
         return new MyServletOutputStream(bos); 
    } 
 
    //处理字符流输出的情况:用字符流时要注意乱码:字节转字符要查码表,字符转字节也要查码表
     public PrintWriter getWriter() throws IOException {
        printWriter  = new PrintWriter(new OutputStreamWriter(bos, super.getCharacterEncoding())) ;
         return printWriter;
   }
     //获取response 写出的数据
    public byte[] getBufferedBytes(){
         try {
             if (printWriter != null) 
                 printWriter.close() ;
            bos.flush() ;
         } catch (IOException e) {
            e.printStackTrace(); 
        }
         byte[] byteArray = bos.toByteArray() ;
        return  byteArray;
     }
 }

    b、改写getOutputStream 方法时,要返回一个SerlvetOutputStream 类实例,因为SerlvetOutputStream是抽象类,不能创建实例,所以要重写SerlvetOutputStream 类:

 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 
 import javax.servlet.ServletOutputStream;
 /**
  * 包装ServletOutputStream ,改写write 方法。
   * @author 贺佐安
  *
  */
 public class MyServletOutputStream extends ServletOutputStream {
     private ByteArrayOutputStream bos = null ; 
     public MyServletOutputStream (ByteArrayOutputStream bos) {
         this.bos = bos ;
    }
     public void write(int b) throws IOException {
         bos.write(b) ;
     }
 } 

       c、将包装过的HttpServletResponse 类的实例放行。
    d、然后获取Servlet 处理过后的数据,然后进行Gzip压缩。
    e、调用ServletResponse 的实例,将压缩后的数据写出去。

      //GZIP压缩:
        byte[] buff = myResponse.getBufferedBytes() ;  
         //创建缓存容器:
         ByteArrayOutputStream baos = new ByteArrayOutputStream() ;
         GZIPOutputStream gzip = new GZIPOutputStream(baos) ;
         gzip.write(buff) ;
         gzip.close() ;
         buff = baos.toByteArray() ;
        //设置响应头;
        response.setHeader("Content-Encoding", "gzip");
        response.setContentLength(buff.length) ;  
        response.getOutputStream().write( buff) ;
  
以上便是用Filter 对一些文本资源进行GIZP压缩的步骤。重点就是第二步,如何获取Servlet 返回的数据。更细一点的流程如下图:

<think>好的,我现在需要回答用户关于Java如何解决数据存储量问题。首先,我要理清楚这个问题的关键点。用户可能是在处理规模数据时遇到了存储上的挑战,想知道Java生态中有哪些解决方案。 首先,我应该从数据存储的基础开始。传统的关系型数据库在处理海量数据时可能会遇到性能瓶颈,比如MySQL。所以,可能需要提到分库分表,这是常见的解决方案。Java中有一些框架,比如ShardingSphere,可以用来实现分库分表,分散数据存储压力。 接下来,用户可能不仅需要存储,还需要高效查询。NoSQL数据库比如MongoDB、Cassandra适合处理非结构化数据高并发场景,这些数据库在Java中都有对应的驱动支持,比如Spring Data MongoDB,可以方便集成。 然后是分布式文件存储系统,比如Hadoop的HDFS或者阿里云的OSS。Java可以通过Hadoop的API或者OSS的SDK来操作这些系统,适合存储量非结构化数据,比如图片、视频等。 另外,缓存机制也是提升性能的重要手段。使用Redis或Memcached作为缓存层,减少对数据库的直接访问。Java中常用的有Jedis、Lettuce等客户端库,Spring Boot也提供了自动配置支持。 数据压缩归档也是一个方向。对于历史数据,可以使用压缩算法如GZIP、Snappy减少存储空间,Java中有相关的库如Deflater类或者第三方库,实现数据压缩存储。 接下来是数据分区冷热分离。将热数据存放在高性能存储,冷数据迁移到低成本存储。Java可以通过定时任务或工作引擎如Quartz或Spring Batch来实现数据的迁移管理。 云存储服务也是一个重要方面,利用云服务提供商的存储解决方案,如AWS S3、阿里云OSS,Java通过SDK集成,实现弹性扩展高可用性。 数据库优化方面,除了分库分表,还可以考虑读写分离,使用主从复制,Java中通过配置数据源路由实现。同时,索引优化、查询优化也是必要的,减少不必要的全表扫描。 列式存储数据库如HBase、ClickHouse适合分析型场景,Java通过HBase客户端或JDBC连接,提升查询效率。 最后,数据清理生命周期管理,定期清理过期数据,设置合理的保留策略,Java的定时任务或结合工作引擎来实现。 在整理这些点时,需要确保结构清晰,逐步引导用户了解每个解决方案的具体实现适用场景。同时,要结合Java生态中的工具框架,让用户知道如何实际操作。可能还需要检查是否有遗漏的解决方案,或者是否有更优化的方法。比如是否要提到NewSQL数据库,或者分布式数据库如TiDB,但可能属于分库分表的扩展。另外,数据分片策略的选择也很重要,需要根据业务需求决定是按范围、哈希还是其他方式分片。 还要注意提到各个方案的优缺点,比如NoSQL可能不支持事务,而分库分表可能增加系统复杂度。缓存层需要注意一致性问题。云存储需要考虑网络延迟成本问题。 总结下来,应该分点列出各个解决方案,每个方案简要说明原理,并结合Java中的具体技术栈或工具,给出实际应用的例子或框架。这样用户能够一步步了解如何在自己的Java项目中实施这些方案,应对数据存储的挑战。</think>Java应对数据存储的挑战可通过以下链路式解决方案实现,逐步优化存储效率与扩展性: --- ### 1. **数据库分库分表** - **原理**:将表水平拆分到多个数据库或表中,分散存储压力。 - **Java工具**: - **ShardingSphere**:支持透明化分片,通过JDBC或Proxy实现。 - **MyCat**:中间件代理,配置分片规则(如按ID哈希)。 - **适用场景**:高并发写入、单表数据量超千万级。 ```java // ShardingSphere配置示例(YAML) rules: - !SHARDING tables: user: actualDataNodes: ds${0..1}.user${0..1} tableStrategy: standard: shardingColumn: user_id shardingAlgorithmName: user_mod ``` --- ### 2. **NoSQL数据库集成** - **选型**: - **MongoDB**:文档存储,适合JSON结构数据。 - **Cassandra**:分布式列存储,高写入吞吐。 - **Java集成**: - **Spring Data MongoDB**:通过`MongoTemplate`操作。 - **Cassandra Driver**:使用`DataStax Java Driver`。 ```java // Spring Boot中MongoDB查询示例 @Repository public interface UserRepository extends MongoRepository<User, String> { List<User> findByAgeGreaterThan(int age); } ``` --- ### 3. **分布式文件存储** - **系统选择**: - **HDFS**:Hadoop生态,适合离线分析。 - **MinIO/OSS**:兼容S3协议的对象存储。 - **Java操作**: - **Hadoop HDFS API**:通过`FileSystem`类读写。 - **AWS SDK for S3**:上传下载文件。 ```java // 使用AWS SDK上传文件到S3 AmazonS3 s3Client = AmazonS3ClientBuilder.standard().build(); s3Client.putObject("bucket-name", "file-key", new File("path/to/file")); ``` --- ### 4. **缓存层加速** - **工具**: - **Redis**:内存缓存热点数据。 - **Caffeine**:本地缓存,减少远程调用。 - **Java实现**: - **Spring Cache**:注解驱动(`@Cacheable`)。 - **Redisson**:分布式锁与数据结构支持。 ```java // Spring Cache注解示例 @Cacheable(value = "users", key = "#userId") public User getUserById(String userId) { // 查询数据库 } ``` --- ### 5. **数据压缩与归档** - **压缩算法**:GZIP、Snappy(快速压缩解压)。 - **Java实现**: - **Deflater类**:原生ZIP压缩。 - **Apache Commons Compress**:支持多种格式。 ```java // 使用GZIP压缩数据 ByteArrayOutputStream bos = new ByteArrayOutputStream(); GZIPOutputStream gzipOS = new GZIPOutputStream(bos); gzipOS.write(data.getBytes()); gzipOS.close(); ``` --- ### 6. **冷热数据分离** - **策略**: - 热数据存SSD/内存,冷数据转至HDD或对象存储。 - **Java调度**: - **Quartz**:定时迁移任务。 - **Spring Batch**:批量处理历史数据。 ```java // Quartz定时任务配置 @Scheduled(cron = "0 0 2 * * ?") // 每天凌晨2点执行 public void archiveColdData() { // 迁移冷数据到OSS } ``` --- ### 7. **云原生存储方案** - **服务**:AWS S3、阿里云OSS、Google Cloud Storage。 - **Java SDK**: - 各厂商提供SDK(如`aliyun-sdk-oss`)。 - 通过REST API直接交互。 --- ### 8. **列式存储优化分析** - **数据库**:HBase(Hadoop生态)、ClickHouse(OLAP)。 - **Java访问**: - **HBase Java API**:`HTable`操作扫描。 - **JDBC驱动**:连接ClickHouse执行聚合查询。 --- ### 9. **数据库调优** - **索引优化**:避免全表扫描,使用覆盖索引。 - **连接池配置**:HikariCP调优最连接数。 - **批处理**:JDBC的`addBatch()`提升写入效率。 ```java // JDBC批处理示例 PreparedStatement ps = conn.prepareStatement("INSERT INTO users VALUES (?)"); for (User user : userList) { ps.setString(1, user.getName()); ps.addBatch(); } ps.executeBatch(); ``` --- ### 总结链路 1. **评估数据特征**:区分结构化/非结构化、冷热数据。 2. **横向扩展存储**:分库分表 → NoSQL → 分布式文件系统。 3. **加速访问**:缓存层 → 列式存储 → 压缩优化。 4. **成本与性能平衡**:冷热分离 → 云存储归档。 5. **持续监控调优**:数据库性能分析工具(如Arthas、Prometheus)。 通过上述组合策略,Java系统可有效应对PB级数据存储挑战,兼顾性能、扩展性与成本。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值