zoukankan      html  css  js  c++  java
  • 关于Redis 二进制内容的 可视化尝试

     二进制内容的 能否可视化?  网上的资料比较少啊!

    ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

    有时候通过 可视化工具,比如redis DesktopManager 查看 redis 的key 的值的时候,发现是 二进制内容, 如下:

    出现二进制的内容, 丝毫不奇怪,因为我set的时候value就是key。怪的是, 为什么有的 英文字母 能够展示出来, 其他就都是x ,可能是 这个工具本身做了一些处理吧。  但其实不然, 如果命令行登录进去一看,发现也是一样的 结果:

    127.0.0.1:6379[2]> get DISCUSS:TOPIC:3d28016e7cb34119aab718da2d4d1fe5::count
    "xacxedx00x05srx00x11java.lang.Integerx12xe2xa0xa4xf7x81x878x02x00x01Ix00x05valuexrx00x10java.lang.Numberx86xacx95x1dx0bx94xe0x8bx02x00x00xpx00x00x00x00"

    可以看到其中 java.lang.Integer java.lang.Number 都是 类名。

    另外注意到 如果 notepad++打开一个二进制文件, 比如class文件,那么也会看到这样的 乱码和 英文夹杂的情况。 为什么会这样 ?

    写个程序测试下:

    package com.lkk;

    import com.alibaba.fastjson.JSONObject;
    import com.baomidou.mybatisplus.plugins.Page;
    import com.lkk.ppm.discuss.domain.entity.ELComment;
    import io.lettuce.core.RedisClient;
    import io.lettuce.core.RedisURI;
    import org.apache.commons.codec.binary.Hex;
    import org.junit.Assert;
    import org.junit.Test;
    import org.springframework.data.redis.connection.RedisConnection;
    import org.springframework.data.redis.connection.RedisStringCommands;
    import org.springframework.data.redis.connection.lettuce.LettuceConnection;
    import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;
    import org.springframework.data.redis.serializer.RedisSerializationContext;
    import org.springframework.data.redis.serializer.RedisSerializer;
    import org.springframework.data.redis.util.ByteUtils;

    import javax.xml.bind.DatatypeConverter;
    import java.io.ByteArrayInputStream;
    import java.io.FileInputStream;
    import java.net.URL;
    import java.nio.ByteBuffer;

    /**
     * @author Administrator
     * @date 2019/9/5 0005 12:40
     * @Description
     */
    public class TestRedisSerializer {

        private static final int CURRENT = 111;
        private static final int SIZE = 222;

        static RedisSerializer serial;
        static RedisSerializationContext.SerializationPair serializationPair;
        static RedisStringCommands redisStringCommands;

        static {
            RedisURI redisURI = RedisURI.create("192.168.11.200", 6380);
            redisURI.setPassword("Redis!123");
            long jedis = 100000L;
            RedisConnection connection = new LettuceConnection(jedis, RedisClient.create(redisURI));
            connection.select(2);

            redisStringCommands = connection.stringCommands();
            // 切换 序列化组件
            serial = new JdkSerializationRedisSerializer();// 默认就是 JdkSerializationRedisSerializer

    //          StringRedisSerializer 只能够序列化字符串, 不能序列化 对象。 好像没啥用!
    //       serial = new StringRedisSerializer();

            // GenericFastJsonRedisSerializer 可以将对象序列化为 json格式字符串
    //        serial = new GenericFastJsonRedisSerializer();

            serializationPair = RedisSerializationContext.SerializationPair.fromSerializer(serial);
        }

        @Test
        public void testHex() throws Exception {
    //        ByteUtils.getBytes(this.cacheConfig.getValueSerializationPair().write(value)
    //         ByteUtils.getBytes(this.cacheConfig.getValueSerializationPair().write(value)
    //        org.apache.commons.compress.utils.ByteUtils.

            String foo = "hello";
            byte[] bytes = foo.getBytes("unicode");
            bytes = foo.getBytes();
    //        bytes = foo.getBytes("gb2312");

            /**
             * 虽然foo 是纯英文字母,但 下面的方法都 无法将bytes 还原到上面的hello 了。。 TODO
             */
            System.out.println( Hex.encodeHexString( bytes ) );// 结果是 纯16进制的内容 68656c6c6f,不带 x 无法查看
            String s = DatatypeConverter.printHexBinary(bytes);;// 结果是 纯16进制的内容 68656c6c6f, 无法查看
            System.out.println("s = " + s);
    //        byte[] decoded = Hex.decodeHex("00A0BF");
    //        System.out.println("decoded = " + new String(decoded));

            /**
             *  还原到上面的hello
             */
            dumpBytesToHex(bytes);

            // 尝试读取class文件
            // 当前文件
            String fileFullName = "E:\dev\erdp2\erdp_discuss\erdp_discuss_service\target\test-classes\com\lkk\TestRedisSerializer.class";
            FileInputStream fis = new FileInputStream(fileFullName);
    //        fis.getChannel()
    //        ByteArrayInputStream
    //        fis.read()
            int available = fis.available();
            byte[] classBytes = new byte[available];
            int read = fis.read(classBytes);

            String s1 = dumpBytesToHex(classBytes);// 除了换行符,基本上 得到了 和 notepad++ 一样的效果。
            Assert.assertTrue(s1.contains("TestRedisSerializer"));

            ClassLoader classLoader = TestRedisSerializer.class.getClassLoader();


        }

        @Test
        public void testWrite() throws Exception {
            String key = "RESOURCE:ITEM:FILE::9fd04b83e33844b1a21ffa1c05978fc1_content.js";
    //        key = "RESOURCE:ITEM:FILE::3fa4bbdff660490092ba9af971980838_template.html";
    //        key = "DISCUSS:TOPIC:3d28016e7cb34119aab718da2d4d1fe5::count";
            key = "DISCUSS:TOPIC:xxx::count";
            write(key);
        }
        
        private void write(Object key) throws Exception {
            /**
             * CURRENT SIZE 测试read 要用到
             */
            Page<ELComment> page = new Page<>(CURRENT, SIZE);
            String s2 = JSONObject.toJSONString(page);
    //        byte[] bytes = ByteUtils.getBytes(serializationPair.write(key));

            //如果是 JdkSerializationRedisSerializer : write 1 结果是  "xacxedx00x05srx00x11java.lang.Integerx12xe2xa0xa4xf7x81x878x02x00x01Ix00x05valuexrx00x10java.lang.Numberx86xacx95x1dx0bx94xe0x8bx02x00x00xpx00x00x00x01"
    //        byte[] bytes = ByteUtils.getBytes(serializationPair.write(1));

            byte[] bytes = ByteUtils.getBytes(serializationPair.write(page));
            Boolean set = redisStringCommands.set(key.toString().getBytes(), bytes);
    //        System.out.println("set = " + set);
    //        byte[] bytes = ByteUtils.getBytes(RedisSerializationContext.SerializationPair.fromSerializer(serial).write(new String("abc阿斯蒂芬")));
    //        System.out.println("bytes = " + bytes.length);
            String s = dumpBytesToHex(bytes);
            Assert.assertTrue(s.contains("com.baomidou.mybatisplus.plugins.Page"));
        }
        
        @Test
        public void testRead() throws Exception {
            String key = "DISCUSS:TOPIC:xxx::count";
            read(key);
        }

        private void read(String key) {
            byte[] bytes1 = redisStringCommands.get(key.getBytes());
            System.out.println("TestRedis.aaa");
            System.out.println("bytes1 === " + new String(bytes1));// 直接打印 二进制内容
            System.out.println("TestRedis.bbb");
            dumpBytesToHex(bytes1);// 打印 16 进制内容

            Page<ELComment> read = (Page<ELComment>) serializationPair.read(ByteBuffer.wrap(bytes1));
            System.out.println("read = " + read);
            int pages = read.getPages();
            int size = read.getSize();
            System.out.println("size = " + size);
            Assert.assertEquals(read.getSize(), SIZE);
            Assert.assertEquals(read.getCurrent(), CURRENT);
        }

        /**
         * 转换为16进制 再打印
         * @param bytes
         * @return
         */
        public static String dumpBytesToHex(byte[] bytes) {
            String s = bytesToHex(bytes);
            System.out.println("Hex String: " + s);
            return s;
        }

        private static final char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray();

        public static String bytesToHex66(byte[] bytes) {
            StringBuffer sb = new StringBuffer();
            char[] hexChars = new char[bytes.length * 2];
            for (int j = 0; j < bytes.length; j++) {
                byte aByte = bytes[j];
                if (aByte > 33 && aByte < 128) {
                    sb.append((char) aByte);
                    continue;
                }
                int v = bytes[j] & 0xFF;
                hexChars[j * 2] = HEX_ARRAY[v >>> 4];
                hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F];

                String hex = Integer.toHexString(aByte & 0xFF);
                sb.append("\x");
                sb.append(hex);
                if (hex.length() < 2) {
                    if (aByte == 0) {
    //                    continue; // Fixme ,
                    } else {
                    }
                    sb.append(0); // Fixme ,
                }
            }
    //        return new String(hexChars);
            return sb.toString();
        }

        /**
         * @param bytes
         * @return
         */
        public static String bytesToHex(byte[] bytes) {
            StringBuffer sb = new StringBuffer();
            for (int i = 0; i < bytes.length; i++) {
                byte aByte = bytes[i];

                /**
                 * 这一段是做 ascii码字符 转换
                 */
                if (aByte > 33 && aByte < 128) {
                    sb.append((char) aByte);
                    continue;
                }
                String hex = Integer.toHexString(aByte & 0xFF);
                sb.append("\x");
                sb.append(hex);
                if (hex.length() < 2) {
                    if (aByte == 0) {
    //                    continue;
                    } else {
                    }
                    sb.append(0);
                }
            }
            return sb.toString();
        }

    }

    Binary Viewer 查看的 结果是:

    -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

    总结:

    二进制的内容, 可以用hex 查看/编辑器 进行查看, 一般情况下 是无法直接转换为 字符串的!! 二进制的内容 如果 用hex 查看器查看,发现确实有字符串,那么可能就是 它本身就是 刚好被查看器支持直接展示了吧。

    网上很多的二进制转 16进制的 所谓工具, 其实 其结果并不是Redis 客户端展示的结果,不是我想要的。。 因为它对 字符串的不能直接解析出来。。

    16 进制不能阅读, 可以尝试转换为ascii。 二进制其实就是内存的内容,任何文件都可以转换为二进制。 软件把它展示出来, 它需要按照特定的 编码格式。文本内容是 天生可以用 文本编辑器 查看的。 其他的 , 比如 图片, 需要图片查看器, 依次类推。 class文件 呢?   其实也有专门的 查看器。。

    注意到 其中 x 其实是一个 方便终端展示的一个 程序添加的 字符。 并不是 二进制内容自带的。

    StringRedisTemplate默认使用的是StringRedisSerializer, 默认只能用来存储value类型为 string 的值。。

    Redis 自带的序列化器是的JdkSerializationRedisSerializer,也就是我们 生成我们 class 文件的 二进制序列化器。 其生成的结果很臃肿, 效率是比较低的!

    参考:

    https://www.jianshu.com/p/23f2c4c92093

    https://blog.csdn.net/Eric_Blog_CSDN/article/details/78904679

  • 相关阅读:
    在循环中禁止remove/add
    [算法竞赛入门]WERTYU
    [算法竞赛入门]Tex Quotes
    [算法竞赛入门]竖式问题
    [算法竞赛入门]蛇形填数
    [C++面试]关于const的使用方法
    [C++面试]单例模式-设计模式
    [C++面试]C++的三种继承(public/protected/private继承)
    用微服务架构,有哪些好处?
    东软数据可视化分析, 已经方便成这样?!
  • 原文地址:https://www.cnblogs.com/FlyAway2013/p/11530244.html
Copyright © 2011-2022 走看看