开门见山,Linux下配个环境真是苦逼死了,这里记录一下,囧

一、环境

  服务端:Ubuntu16.04 LTS(虚拟机,redis官方没有window发布版本,而且在Linux下运行更稳定)

  客户端:win10、vs2015、.net4.0+

二、安装  

sudo su
输入root密码

sudo apt-get install redis-server

一路Enter下去,直到完成,默认安装完成后,提几个被坑过的文件:

1、启动配置文件:/etc/redis/redis.conf,每一个配置的含义参考:http://www.cnblogs.com/kreo/p/4423362.html

2、rdb文件:/var/lib/redis/dump.rdb,搜索在redis.conf的dir可以看到

3、pidfile文件:/var/run/redis/redis-server.pid(配置slave时需要修改)

4、日志文件:/var/log/redis/redis-server.log(配置slave时需要修改)

5、修改个配置,注释bind 127.0.0.1,这样在本机可以使用虚拟机ip来访问,否则只能内网访问 

注意:修改redis.conf之前,需要先更改权限,执行命令 

chmod 777 redis.conf

 三、启动

默认安装好后可以直接使用redis命令,先启动服务端:

redis-server /etc/redis/redis.conf 

注:带conf文件启动的目的,是为了后面区分启动从库实例

查看服务是否启动成功:

ps -ef |grep redis

 

启动客户端:

redis-cli -p 6379

随意测试下

四、设置master-salve

因为是Ubuntu系统,直接双击计算机进入到安装目录:/etc/redis/,可以看到步骤二中的启动主配置文件,redis.conf

右键->在终端打开,执行拷贝文件命令: 

sudo cp redis.conf redis-slave.conf

 同样,需要修改这个文件的权限: 

chmod 777 redis-slave.conf

使用编辑器(vim、vscode等)打开redis-slave.conf,修改几个配置(列出来的是已经修改后的):

1、port 6380

2、pidfile:/var/run/redis/redis-server-6380.pid

3、logfile /var/log/redis/redis-server-slave.log

4、slaveof 127.0.0.1 6379

完成后,启动从库实例:

redis-server /etc/redis/redis-slave.conf

再次查看服务:ps -ef |grep redis

已经有两个redis服务端进程了,说明主从都已经启动了

五、客户端

1、启动主库客户端,重新打开一个终端窗口,执行:

redis-cli  -p 6379

随意执行两个最简单的设置、获取命令:

 根据主从同步的机制,此时slave从库中应该可以get到这个name,重开一个终端窗口,执行:

redis-cli  -p 6380

 get一下,没毛病,耶!

 

六、.Net操作redis

讲代码的园子里有很多,就不多在提了。这里给一个类以供大家学习使用,也是从github上搜刮下来的,地址我真忘记了,对不住作者了(如果有看到请留言,我会附上GitHub地址)   

是基于StackExchange.Redis封装的

  1     public static class StackExchangeRedisHelper
  2     {
  3         private static string Constr = "";
  4 
  5         private static object _locker = new Object();
  6         private static ConnectionMultiplexer _instance = null;
  7 
  8         /// <summary>
  9         /// 使用一个静态属性来返回已连接的实例,如下列中所示。这样,一旦 ConnectionMultiplexer 断开连接,便可以初始化新的连接实例。
 10         /// </summary>
 11         public static ConnectionMultiplexer Instance
 12         {
 13             get
 14             {
 15                 if(Constr.Length == 0)
 16                 {
 17                     throw new Exception("连接字符串为设置!");
 18                 }
 19                 if (_instance == null)
 20                 {
 21                     lock (_locker)
 22                     {
 23                         if (_instance == null || !_instance.IsConnected)
 24                         {
 25                             _instance = ConnectionMultiplexer.Connect(Constr);
 26                         }
 27                     }
 28                 }
 29                 //注册如下事件
 30                 _instance.ConnectionFailed += MuxerConnectionFailed;
 31                 _instance.ConnectionRestored += MuxerConnectionRestored;
 32                 _instance.ErrorMessage += MuxerErrorMessage;
 33                 _instance.ConfigurationChanged += MuxerConfigurationChanged;
 34                 _instance.HashSlotMoved += MuxerHashSlotMoved;
 35                 _instance.InternalError += MuxerInternalError;
 36                 return _instance;
 37             }
 38         }
 39 
 40         static StackExchangeRedisHelper()
 41         {
 42         }
 43 
 44         public static void SetCon(string config)
 45         {
 46             Constr = config;
 47         }
 48 
 49         /// <summary>
 50         /// 
 51         /// </summary>
 52         /// <returns></returns>
 53         public static IDatabase GetDatabase()
 54         {
 55             return Instance.GetDatabase();
 56         }
 57 
 58         /// <summary>
 59         /// 这里的 MergeKey 用来拼接 Key 的前缀,具体不同的业务模块使用不同的前缀。
 60         /// </summary>
 61         /// <param name="key"></param>
 62         /// <returns></returns>
 63         private static string MergeKey(string key)
 64         {
 65             return key;
 66             //return BaseSystemInfo.SystemCode + key;
 67         }
 68 
 69         /// <summary>
 70         /// 根据key获取缓存对象
 71         /// </summary>
 72         /// <typeparam name="T"></typeparam>
 73         /// <param name="key"></param>
 74         /// <returns></returns>
 75         public static T Get<T>(string key)
 76         {
 77             key = MergeKey(key);
 78             return Deserialize<T>(GetDatabase().StringGet(key));
 79         }
 80 
 81         /// <summary>
 82         /// 根据key获取缓存对象
 83         /// </summary>
 84         /// <param name="key"></param>
 85         /// <returns></returns>
 86         public static object Get(string key)
 87         {
 88             key = MergeKey(key);
 89             return Deserialize<object>(GetDatabase().StringGet(key));
 90         }
 91 
 92         /// <summary>
 93         /// 设置缓存
 94         /// </summary>
 95         /// <param name="key"></param>
 96         /// <param name="value"></param>
 97         /// <param name="expireMinutes"></param>
 98         public static void Set(string key, object value, int expireMinutes = 0)
 99         {
100             key = MergeKey(key);
101             if (expireMinutes > 0)
102             {
103                 GetDatabase().StringSet(key, Serialize(value), TimeSpan.FromMinutes(expireMinutes));
104             }
105             else
106             {
107                 GetDatabase().StringSet(key, Serialize(value));
108             }
109             
110         }
111 
112         /// <summary>
113         /// 判断在缓存中是否存在该key的缓存数据
114         /// </summary>
115         /// <param name="key"></param>
116         /// <returns></returns>
117         public static bool Exists(string key)
118         {
119             key = MergeKey(key);
120             return GetDatabase().KeyExists(key); //可直接调用
121         }
122 
123         /// <summary>
124         /// 移除指定key的缓存
125         /// </summary>
126         /// <param name="key"></param>
127         /// <returns></returns>
128         public static bool Remove(string key)
129         {
130             key = MergeKey(key);
131             return GetDatabase().KeyDelete(key);
132         }
133 
134         /// <summary>
135         /// 异步设置
136         /// </summary>
137         /// <param name="key"></param>
138         /// <param name="value"></param>
139         public static async Task SetAsync(string key, object value)
140         {
141             key = MergeKey(key);
142             await GetDatabase().StringSetAsync(key, Serialize(value));
143         }
144 
145         /// <summary>
146         /// 根据key获取缓存对象
147         /// </summary>
148         /// <param name="key"></param>
149         /// <returns></returns>
150         public static async Task<object> GetAsync(string key)
151         {
152             key = MergeKey(key);
153             object value = await GetDatabase().StringGetAsync(key);
154             return value;
155         }
156 
157         /// <summary>
158         /// 实现递增
159         /// </summary>
160         /// <param name="key"></param>
161         /// <returns></returns>
162         public static long Increment(string key)
163         {
164             key = MergeKey(key);
165             //三种命令模式
166             //Sync,同步模式会直接阻塞调用者,但是显然不会阻塞其他线程。
167             //Async,异步模式直接走的是Task模型。
168             //Fire - and - Forget,就是发送命令,然后完全不关心最终什么时候完成命令操作。
169             //即发即弃:通过配置 CommandFlags 来实现即发即弃功能,在该实例中该方法会立即返回,如果是string则返回null 如果是int则返回0.这个操作将会继续在后台运行,一个典型的用法页面计数器的实现:
170             return GetDatabase().StringIncrement(key, flags: CommandFlags.FireAndForget);
171         }
172 
173         /// <summary>
174         /// 实现递减
175         /// </summary>
176         /// <param name="key"></param>
177         /// <param name="value"></param>
178         /// <returns></returns>
179         public static long Decrement(string key, string value)
180         {
181             key = MergeKey(key);
182             return GetDatabase().HashDecrement(key, value, flags: CommandFlags.FireAndForget);
183         }
184 
185         /// <summary>
186         /// 序列化对象
187         /// </summary>
188         /// <param name="o"></param>
189         /// <returns></returns>
190         private static byte[] Serialize(object o)
191         {
192             if (o == null)
193             {
194                 return null;
195             }
196             BinaryFormatter binaryFormatter = new BinaryFormatter();
197             using (MemoryStream memoryStream = new MemoryStream())
198             {
199                 binaryFormatter.Serialize(memoryStream, o);
200                 byte[] objectDataAsStream = memoryStream.ToArray();
201                 return objectDataAsStream;
202             }
203         }
204 
205         /// <summary>
206         /// 反序列化对象
207         /// </summary>
208         /// <typeparam name="T"></typeparam>
209         /// <param name="stream"></param>
210         /// <returns></returns>
211         private static T Deserialize<T>(byte[] stream)
212         {
213             if (stream == null)
214             {
215                 return default(T);
216             }
217             BinaryFormatter binaryFormatter = new BinaryFormatter();
218             using (MemoryStream memoryStream = new MemoryStream(stream))
219             {
220                 T result = (T) binaryFormatter.Deserialize(memoryStream);
221                 return result;
222             }
223         }
224 
225         /// <summary>
226         /// 配置更改时
227         /// </summary>
228         /// <param name="sender"></param>
229         /// <param name="e"></param>
230         private static void MuxerConfigurationChanged(object sender, EndPointEventArgs e)
231         {
232             //LogHelper.SafeLogMessage("Configuration changed: " + e.EndPoint);
233         }
234 
235         /// <summary>
236         /// 发生错误时
237         /// </summary>
238         /// <param name="sender"></param>
239         /// <param name="e"></param>
240         private static void MuxerErrorMessage(object sender, RedisErrorEventArgs e)
241         {
242             //LogHelper.SafeLogMessage("ErrorMessage: " + e.Message);
243         }
244 
245         /// <summary>
246         /// 重新建立连接之前的错误
247         /// </summary>
248         /// <param name="sender"></param>
249         /// <param name="e"></param>
250         private static void MuxerConnectionRestored(object sender, ConnectionFailedEventArgs e)
251         {
252             //LogHelper.SafeLogMessage("ConnectionRestored: " + e.EndPoint);
253         }
254 
255         /// <summary>
256         /// 连接失败 , 如果重新连接成功你将不会收到这个通知
257         /// </summary>
258         /// <param name="sender"></param>
259         /// <param name="e"></param>
260         private static void MuxerConnectionFailed(object sender, ConnectionFailedEventArgs e)
261         {
262             //LogHelper.SafeLogMessage("重新连接:Endpoint failed: " + e.EndPoint + ", " + e.FailureType +(e.Exception == null ? "" : (", " + e.Exception.Message)));
263         }
264 
265         /// <summary>
266         /// 更改集群
267         /// </summary>
268         /// <param name="sender"></param>
269         /// <param name="e"></param>
270         private static void MuxerHashSlotMoved(object sender, HashSlotMovedEventArgs e)
271         {
272             //LogHelper.SafeLogMessage("HashSlotMoved:NewEndPoint" + e.NewEndPoint + ", OldEndPoint" + e.OldEndPoint);
273         }
274 
275         /// <summary>
276         /// redis类库错误
277         /// </summary>
278         /// <param name="sender"></param>
279         /// <param name="e"></param>
280         private static void MuxerInternalError(object sender, InternalErrorEventArgs e)
281         {
282             //LogHelper.SafeLogMessage("InternalError:Message" + e.Exception.Message);
283         }
284 
285         //场景不一样,选择的模式便会不一样,大家可以按照自己系统架构情况合理选择长连接还是Lazy。
286         //建立连接后,通过调用ConnectionMultiplexer.GetDatabase 方法返回对 Redis Cache 数据库的引用。从 GetDatabase 方法返回的对象是一个轻量级直通对象,不需要进行存储。
287 
288         /// <summary>
289         /// 使用的是Lazy,在真正需要连接时创建连接。
290         /// 延迟加载技术
291         /// 微软azure中的配置 连接模板
292         /// </summary>
293         //private static Lazy<ConnectionMultiplexer> lazyConnection = new Lazy<ConnectionMultiplexer>(() =>
294         //{
295         //    //var options = ConfigurationOptions.Parse(constr);
296         //    ////options.ClientName = GetAppName(); // only known at runtime
297         //    //options.AllowAdmin = true;
298         //    //return ConnectionMultiplexer.Connect(options);
299         //    ConnectionMultiplexer muxer = ConnectionMultiplexer.Connect(Coonstr);
300         //    muxer.ConnectionFailed += MuxerConnectionFailed;
301         //    muxer.ConnectionRestored += MuxerConnectionRestored;
302         //    muxer.ErrorMessage += MuxerErrorMessage;
303         //    muxer.ConfigurationChanged += MuxerConfigurationChanged;
304         //    muxer.HashSlotMoved += MuxerHashSlotMoved;
305         //    muxer.InternalError += MuxerInternalError;
306         //    return muxer;
307         //});
308 
309 
310         #region  当作消息代理中间件使用 一般使用更专业的消息队列来处理这种业务场景
311 
312         /// <summary>
313         /// 当作消息代理中间件使用
314         /// 消息组建中,重要的概念便是生产者,消费者,消息中间件。
315         /// </summary>
316         /// <param name="channel"></param>
317         /// <param name="message"></param>
318         /// <returns></returns>
319         public static long Publish(string channel, string message)
320         {
321             ISubscriber sub = Instance.GetSubscriber();
322             //return sub.Publish("messages", "hello");
323             return sub.Publish(channel, message);
324         }
325 
326         /// <summary>
327         /// 在消费者端得到该消息并输出
328         /// </summary>
329         /// <param name="channelFrom"></param>
330         /// <returns></returns>
331         public static void Subscribe(string channelFrom)
332         {
333             ISubscriber sub = Instance.GetSubscriber();
334             sub.Subscribe(channelFrom, (channel, message) =>
335             {
336                 Console.WriteLine((string) message);
337             });
338         }
339 
340         #endregion
341 
342         /// <summary>
343         /// GetServer方法会接收一个EndPoint类或者一个唯一标识一台服务器的键值对
344         /// 有时候需要为单个服务器指定特定的命令
345         /// 使用IServer可以使用所有的shell命令,比如:
346         /// DateTime lastSave = server.LastSave();
347         /// ClientInfo[] clients = server.ClientList();
348         /// 如果报错在连接字符串后加 ,allowAdmin=true;
349         /// </summary>
350         /// <returns></returns>
351         public static IServer GetServer(string host, int port)
352         {
353             IServer server = Instance.GetServer(host, port);
354             return server;
355         }
356 
357         /// <summary>
358         /// 获取全部终结点
359         /// </summary>
360         /// <returns></returns>
361         public static EndPoint[] GetEndPoints()
362         {
363             EndPoint[] endpoints = Instance.GetEndPoints();
364             return endpoints;
365         }
366     }
View Code

使用举例(控制台程序):

string redisCon = "192.168.0.108:6379";
StackExchangeRedisHelper.SetCon(redisCon);
//设置
StackExchangeRedisHelper.Set("uid", "zhangl");
//读取
Console.Write(StackExchangeRedisHelper.Get("uid"));    

 若要把redis用作MQ,最好使用发布订阅模式,例: 

StackExchangeRedisHelper.Subscribe("Order");

一行代码搞定,订阅channel为“Order”的消息,此时我在服务端发布一个消息,控制台立马会输出订阅的消息:

 

7、管理工具

下载地址:https://redisdesktop.com/download

要FQ、FQ、FQ

安装后,连接到我虚拟机的redis主从库,可以看到例子中set的key都在,也就是说它们都同步到slave库了

 

8、总结  

   这篇文章简单的介绍了Ubuntu下redis的一些操作,复杂的问题需要深入研究,此仅为入门

   比如流弊的玩法:http://www.open-open.com/lib/view/open1454502890526.html

        文中如有错误之处,请留言指出,谢谢!

内容来源于网络如有侵权请私信删除
你还没有登录,请先登录注册
  • 还没有人评论,欢迎说说您的想法!