设为首页 收藏本站
查看: 921|回复: 0

[经验分享] Redis编码问题

[复制链接]
累计签到:1 天
连续签到:1 天
发表于 2015-7-21 10:32:08 | 显示全部楼层 |阅读模式

       最近搞redis存储对象出了点问题,大概说一下背景,项目原有的东东以前存的是redis,存储的直接是对象模型,没有问题,这里存储对象存储任何信息事都没有问题的。但是现在调整为存储序列化的json字符串,此时获取对象信息发生了问题,不是报错就是有乱码似的东东,一开始以为是编码问题,其实不准确,现在来一步步看一看到底是什么问题(这里的测试只是为了简单,命名等都不规范,大家凑活着看了解问题就行)

   




public class test
{
public string Name { get; set; }
public string View { get; set; }

public IList list { get; set; }
}

public class AAA
{
public string Name { get; set; }
public string View { get; set; }
}

public class CartController : Controller
{
public void Index2()
{
test tes = new test();
tes.Name = "z中文";
tes.View = null;
tes.list = new List();
AAA d = new AAA();
d.Name = "123";
d.View = "asd";
AAA b = new AAA();
b.Name = "我是特殊符号~!^?*$#\\";
b.View = "我是单引号\"";
tes.list.Add(d);
tes.list.Add(b);
//var aa = Newtonsoft.Json.JsonConvert.SerializeObject(tes);
RedisManager.Execute(redis => redis.Set("test", tes));
var ggg = RedisManager.Execute(redis => redis.Get("test"));
}
}

  
DSC0000.jpg

直接存储对象是没有问题的,看看其中的set和get吧,

   




public bool Set(string key, T value)
{
byte[] numArray = (object) value as byte[];
if (numArray != null)
{
base.Set(key, numArray);
return true;
}
else
{
string str = JsonSerializer.SerializeToString(value);
this.SetEntry(key, str);
return true;
}
}

public static string SerializeToString(T value)
{
if ((object) value == null)
return (string) null;
if (typeof (T) == typeof (object) || typeof (T).IsAbstract || typeof (T).IsInterface)
{
if (typeof (T).IsAbstract || typeof (T).IsInterface)
JsState.IsWritingDynamic = true;
string str = JsonSerializer.SerializeToString((object) value, value.GetType());
if (typeof (T).IsAbstract || typeof (T).IsInterface)
JsState.IsWritingDynamic = false;
return str;
}
else
{
StringBuilder sb = new StringBuilder();
using (StringWriter stringWriter = new StringWriter(sb, (IFormatProvider) CultureInfo.InvariantCulture))
{
if (typeof (T) == typeof (string))
JsonUtils.WriteString((TextWriter) stringWriter, (object) value as string);
else
JsonWriter.WriteObject((TextWriter) stringWriter, (object) value);
}
return ((object) sb).ToString();
}
}

public void SetEntry(string key, string value)
{
byte[] numArray = value != null ? ServiceStack.Text.StringExtensions.ToUtf8Bytes(value) : (byte[]) null;
this.Set(key, numArray);
}  
恩恩,看样子应该是,存储的时候序列化了,并且使用utf8编码,那好吧,get肯定也就是utf8编码反序列化成对象取出来的,所以泛型的存取数据并没有跟编码有什么关系,那问题出在哪里呢。

   




public T Get(string key)
{
if (!(typeof (T) == typeof (byte[])))
return JsonSerializer.DeserializeFromString(this.GetValue(key));
else
return (T) base.Get(key);
}
public byte[] Get(string key)
{
return this.GetBytes(key);
}

public byte[] GetBytes(string key)
{
if (key == null)
throw new ArgumentNullException("key");
return this.SendExpectData(Commands.Get, StringExtensions.ToUtf8Bytes(key));
}
上面代码解释了为什么,对象怎么存储都没有问题,再来看看string类型的信息。




public void Index2()
{
test tes = new test();
tes.Name = "z中文";
tes.View = null;
tes.list = new List();
AAA d = new AAA();
d.Name = "123";
d.View = "asd";
AAA b = new AAA();
//b.Name = "我是特殊符号~!^?*$#\\";
b.Name = "2";
//b.View = "\"";
b.View = "\\";
tes.list.Add(d);
tes.list.Add(b);

var aa = Newtonsoft.Json.JsonConvert.SerializeObject(tes);
RedisManager.Execute(redis => redis.Set("niutaotao_cart", aa));           
var ggg = RedisManager.Execute(redis => redis.Get("niutaotao_cart"));
var ii = RedisManager.Execute(redis => redis.Get("niutaotao_cart"));

var json="{\"Name\":\"z中文\",\"View\":null,\"list\":[{\"Name\":\"123\",\"View\":\"asd\"},{\"Name\":\"2\",\"View\":\"\\\"}]}";
RedisManager.Execute(redis => redis.Set("ddd", json));
var o = RedisManager.Execute(redis => redis.Get("ddd"));
var pp = RedisManager.Execute(redis => redis.Get("ddd"));
}

  可以看看监视的结果,不多说直接上图。大概能看出点区别了。
DSC0001.png
问题来了,1.json编码后首先转移符并没有特殊处理,而是直接写进了json格式字符串中

    2. 重现向上看取数据的时候 1.双引号有问题 ,貌似都变为了转移符+双引号

                 2.转移符有问题,具体规律也看不大出来,貌似就是之前都加了两个转移符

                 3。中文编码也有问题(这尼玛很奇怪啊,从上面代码来看,应该跟编码没关系才对)

我们继续看代码,看看set存储数据的时候有什么特别的地方。我们可以看到对象和字符串的处理是不同的,嗯,估计问题就应该在这里了,看代码。




{"Name":"z中文","View":null,"list":[{"Name":"123","View":"asd"},{"Name":"2","View":"\\"}]}

public static void WriteString(TextWriter writer, string value)
{
if (value == null)
writer.Write("null");
else if (!JsonUtils.HasAnyEscapeChars(value))
{
writer.Write('"');
writer.Write(value);
writer.Write('"');
}
else
{
char[] chArray = new char[4];
writer.Write('"');
int length = value.Length;
for (int index = 0; index < length; ++index)
{
switch (value[index])
{
case '\b':
writer.Write("\\b");
break;
case '\t':
writer.Write("\\t");
break;
case '\n':
writer.Write("\\n");
break;
case '\f':
writer.Write("\\f");
break;
case '\r':
writer.Write("\\r");
break;
case '"':
case '\\':
writer.Write('\\');
writer.Write(value[index]);
break;
default:
if ((int) value[index] >= 32 && (int) value[index]  57343)
{
JsonUtils.IntToHex((int) value[index], chArray);
writer.Write("\\u");
writer.Write(chArray);
break;
}
else
break;
}
}
writer.Write('"');
}
}

  
好了基本上找到原因了,问题就出在方法中列出的”\,””等特殊符号的问题,而且看((int) value[index] < 55296 || (int) value[index] > 57343)句话应该是对这个范围之外的符号进行了转码,具体什么转码方式小弟不清楚,

所以呢,解决办法就来了,我们是不是可以在存储之前将这些被视为特殊符号,特殊处理的字符进行处理呢,然后区出来之后再解码是不是就可以了。

好了试一把。我们就用UrlEncode试一下吧




System.Web.HttpUtility.UrlEncode( ““, Encoding.UTF8);
System.Web.HttpUtility. UrlDecode ( ““, Encoding.UTF8);

var aa = Newtonsoft.Json.JsonConvert.SerializeObject(tes);
var jj = System.Web.HttpUtility.UrlEncode(aa, Encoding.UTF8);
RedisManager.Execute(redis => redis.Set("niutaotao_cart", jj));           
var ggg = RedisManager.Execute(redis => redis.Get("niutaotao_cart"));
var ii = RedisManager.Execute(redis => redis.Get("niutaotao_cart"));
var zz = System.Web.HttpUtility.UrlDecode(ii,Encoding.UTF8);

  
看看结果

DSC0002.jpg


呵呵,尼玛可以了。这里解决问题就可以了,关于redis的存储结构和实现,可以学习下下面两篇文章。

关于redis的存储结构大家可以看看(http://www.iyunv.com/shanyou/archive/2012/09/04/2670972.html)

关于redis的实现可以看看(http://www.searchtb.com/2011/05/redis-storage.html)

redis官网(http://www.redis.cn/)

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-88986-1-1.html 上篇帖子: Redis 性能问题的记录 下篇帖子: Simple example using Redis Lists
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表