spring整合redis客戶端及緩存介面設計

来源:http://www.cnblogs.com/xumanbu/archive/2016/01/26/5160765.html
-Advertisement-
Play Games

一、寫在前面緩存作為系統性能優化的一大殺手鐧,幾乎在每個系統或多或少的用到緩存。有的使用本地記憶體作為緩存,有的使用本地硬碟作為緩存,有的使用緩存伺服器。但是無論使用哪種緩存,介面中的方法都是差不多。筆者最近的項目使用的是memcached作為緩存伺服器,由於memcached的一些限制,現在想換re...


一、寫在前面

緩存作為系統性能優化的一大殺手鐧,幾乎在每個系統或多或少的用到緩存。有的使用本地記憶體作為緩存,有的使用本地硬碟作為緩存,有的使用緩存伺服器。但是無論使用哪種緩存,介面中的方法都是差不多。筆者最近的項目使用的是memcached作為緩存伺服器,由於memcached的一些限制,現在想換redis作為緩存伺服器。思路就是把memached的客戶端換成redis客戶端,介面依然是原來的介面,這樣對系統可以無損替換,介面不變,功能不變,只是客戶端變了。本文不介紹緩存的用法,不介紹redis使用方法,不介紹memcached與redis有何區別。只是實現一個redis客戶端,用了jedis作為第三方連接工具。

二、一些想法

首先貼一下現項目中同事編寫的緩存介面:

/**
 * @ClassName: DispersedCachClient
 * @Description: 分散式緩存介面,每個方法:key最大長度128字元,valueObject最大1Mb,預設超時時間30天
 * @date 2015-4-14 上午11:51:18
 *
 */
public interface DispersedCachClient {
	
	
	/**
	 * add(要設置緩存中的對象(value),)
	 *
	 * @Title: add
	 * @Description: 要設置緩存中的對象(value),如果沒有則插入,有就不操作。
	 * @param key	鍵
	 * @param valueObject	緩存對象
	 * @return  Boolean true 成功,false 失敗
	 */
	public Boolean add(String key, Object valueObject);
	
	/**
	 * add(要設置緩存中的對象(value),指定保存有效時長)
	 *
	 * @Title: add
	 * @Description: 要設置緩存中的對象(value),指定有效時長,如果沒有則插入,有就不操作。
	 * @param key	鍵
	 * @param valuObject	緩存對象
	 * @param keepTimeInteger	有效時長(秒)
	 * @return  Boolean true 成功,false 失敗
	 */
	public Boolean add(String key, Object valueObject, Integer keepTimeInteger);
	
	/**
	 * 
	 * add(要設置緩存中的對象(value),指定有效時間點。)
	 *
	 * @Title: add
	 * @Description: 要設置緩存中的對象(value),指定有效時間點,如果沒有則插入,有就不操作。
	 * @date 2015-4-14 上午11:58:12
	 * @param key	鍵
	 * @param valuObject	緩存對象
	 * @param keepDate	時間點
	 * @return  Boolean true 成功,false 失敗
	 */
	public Boolean add(String key, Object valueObject, Date keepDate);
	
	/**
	 * 
	 * set(要設置緩存中的對象(value),)
	 *
	 * @Title: set
	 * @Description: 如果沒有則插入,如果有則修改
	 * @date 2015-4-14 下午01:44:22
	 * @param key	鍵
	 * @param valueObject	緩存對象
	 * @return  Boolean true 成功,false 失敗	
	 */
	public Boolean set(String key,Object valueObject) ;
	
	/**
	 * 
	 * set(要設置緩存中的對象(value),指定有效時長)
	 *
	 * @Title: set
	 * @Description: 指定有效時長,如果沒有則插入,如果有則修改
	 * @date 2015-4-14 下午01:45:22
	 * @param key	鍵
	 * @param valueObject	緩存對象
	 * @param keepTimeInteger	保存時長(秒)
	 * @return  Boolean true 成功,false 失敗
	 */
	public Boolean set(String key, Object valueObject, Integer keepTimeInteger);
	
	/**
	 * 
	 * set(要設置緩存中的對象(value),指定有效時間點)
	 *
	 * @Title: set
	 * @Description: 指定有效時間點,如果沒有則插入,如果有則修改
	 * @date 2015-4-14 下午01:45:55
	 * @param key	鍵
	 * @param valueObject	緩存對象
	 * @param keepDate	有效時間點
	 * @return  Boolean true 成功,false 失敗
	 */
	public Boolean set(String key, Object valueObject, Date keepDate);
	
	/**
	 * 
	 * replace(要設置緩存中的對象(value),有效)
	 *
	 * @Title: replace
	 * @Description: 有效,如果沒有則不操作,如果有則修改
	 * @date 2015-4-14 下午01:47:04
	 * @param key	鍵
	 * @param valueObject	緩存對象
	  * @return  Boolean true 成功,false 失敗  
	 */
	public Boolean replace(String key,Object valueObject) ;
	
	/**
	 * 
	 * replace(要設置緩存中的對象(value),指定有效時長)
	 *
	 * @Title: replace
	 * @Description: 指定有效時長,如果沒有則不操作,如果有則修改
	 * @date 2015-4-14 下午01:47:30
	 * @param key	鍵
	 * @param valueObject	緩存對象
	 * @param keepTimeInteger	緩存時長(秒)
	  * @return  Boolean true 成功,false 失敗  
	 */
	public Boolean replace(String key, Object valueObject, Integer keepTimeInteger);
	
	/**
	 * 
	 * replace(要設置緩存中的對象(value),指定有效時間點)
	 *
	 * @Title: replace
	 * @Description: 指定有效時間點,如果沒有則不操作,如果有則修改
	 * @date 2015-4-14 下午01:48:09
	 * @param key	鍵值對
	 * @param valueObject	緩存對象
	 * @param keepDate	有效時間點
	 * @return  Boolean true 成功,false 失敗  
	 */
	public Boolean replace(String key, Object valueObject, Date keepDate);
	
	/**
	 * 
	 * get(獲得一個緩存對象)
	 *
	 * @Title: get
	 * @Description: 獲得一個緩存對象,響應超時時間預設
	 * @date 2015-4-14 下午04:18:16
	 * @param key	鍵
	 * @return  Obeject  
	 */
	public Object get( String key );
	
	/**
	 * 
	 * getMulti(獲得Map形式的多個緩存對象)
	 *
	 * @Title: getMulti
	 * @Description: 獲得Map形式的多個緩存對象,響應超時時間預設
	 * @date 2015-4-14 下午04:53:07
	 * @param keys	鍵存入的string[]
	 * return  Map<String,Object>  
	 */
	public Map<String,Object> getMulti( List<String> keys );
	
	/**
	 * 
	 * gets(獲得一個帶版本號的緩存對象)
	 *
	 * @Title: gets
	 * @Description: 獲得一個帶版本號的緩存對象
	 * @date 2015-4-16 上午09:15:57
	 * @param key	鍵
	 * @return  Object
	 */
	public Object gets(String key);
	
	/**
	 * 
	 * getMultiArray(獲得數組形式的多個緩存對象)
	 *
	 * @Title: getMultiArray
	 * @Description: 獲得數組形式的多個緩存對象
	 * @date 2015-4-16 上午09:27:29
	 * @param keys	鍵存入的string[]
	 * @return Object[]
	 * @throws
	 */
	public Object[] getMultiArray( List<String> keys );
	
	/**
	 * 
	 * cas(帶版本號存緩存,與gets配合使用)
	 *
	 * @Title: cas
	 * @Description: 帶版本號存緩存,與gets配合使用,超時時間預設
	 * @date 2015-4-16 上午09:53:39
	 * @param key	鍵
	 * @param valueObject	緩存對象
	 * @param versionNo		版本號
	 * @return  Boolean true 成功,false 失敗  
	 */
	public boolean cas(String key, Object valueObject, long versionNo);
	
	
	/** cas(帶版本號存緩存,與gets配合使用)
	 *
	 * @Title: cas
	 * @Description: 帶版本號存緩存,與gets配合使用,指定超時時長
	 * @date 2015-4-16 上午09:58:06
	 * @param key	鍵
	 * @param valueObject	緩存對象
	 * @param keepTimeInteger	超時時長
	 * @param versionNo		版本號
	 * @return  Boolean true 成功,false 失敗
	 */
	public boolean cas(String key, Object valueObject, Integer keepTimeInteger, long versionNo);
	
	/**
	 * 
	 * cas(帶版本號存緩存,與gets配合使用)
	 *
	 * @Title: cas
	 * @Description: 帶版本號存緩存,與gets配合使用,指定超時時間點
	 * @date 2015-4-16 上午10:02:38
	 * @param key	鍵
	 * @param valueObject	緩存對象
	 * @param keepTime	超時時間點
	 * @param versionNo		版本號
	 * @return  Boolean true 成功,false 失敗  
	 */
	public boolean cas(String key, Object valueObject, Date keepDate, long versionNo);
	/**
	 * 
	 * delete(刪除緩存)
	 *
	 * @Title: delete
	 * @Description: 刪除緩存
	 * @date 2015-4-16 上午11:20:13
	 * @param key	鍵
	 */
	public boolean delete(String key);
	
}

 

這個介面用起來總有一些彆扭,我總結了一下:

1、介面名稱命名為DispersedCachClient 這個命名含義是分散式緩存客戶端(cache少了一個字母),其實這個介面跟分散式一點關係都沒有,其實就是緩存介面;

2、介面方法太多了,實際在項目中並沒有方法使用率只有20%左右,所有有精簡的必要;

3、這個介面很多方法設計是套用memcached客戶端設計的,也就是說換成redis後會不通用。

這裡沒有說這個介面寫的不好,而是說還有優化的空間,其次也給自己提個醒,在設計一些使用公共的介面時有必要多花些心思,因為一旦設計後,後面改動的可能性比較小。 

三、代碼實現

使用jedis客戶端需要使用連接池,使用連接後需要將連接放回連接池,失效的連接要放到失效的連接池,類似jdbc需要進行連接的處理,為了避免寫重覆噁心的代碼,參照了spring的JdbcTemple模板設計方式。廢話沒有,直接上代碼吧。

1、重新設計的緩存客戶端介面,這個介面就一個特點“簡單”,目的是為了做到通用,故命名為SimpleCache。

/**
 * @ClassName: DistributedCacheClient
 * @Description: 緩存介面
 * @author 徐飛
 * @date 2016年1月26日 上午11:41:27
 *
 */
public interface SimpleCache {

	/**
	 * @Title: add
	 * @Description: 添加一個緩衝數據
	 * @param key 字元串的緩存key
	 * @param value 緩衝的緩存數據
	 * @return
	 * @author 徐飛
	 */
	boolean add(String key, Object value);

	/**
	 * @Title: add
	 * @Description: 緩存一個數據,並指定緩存過期時間
	 * @param key
	 * @param value
	 * @param seconds
	 * @return
	 * @author 徐飛
	 */
	boolean add(String key, Object value, int seconds);

	/**
	 * @Title: get
	 * @Description: 根據key獲取到一直值
	 * @param key 字元串的緩存key
	 * @return
	 * @author 徐飛
	 */
	Object get(String key);

	/**
	 * @Title: delete
	 * @Description: 刪除一個數據問題
	 * @param key 字元串的緩存key
	 * @return
	 * @author 徐飛
	 */
	long delete(String key);

	/**
	 * @Title: exists
	 * @Description: 判斷指定key是否在緩存中已經存在
	 * @param key 字元串的緩存key
	 * @return
	 * @author 徐飛
	 */
	boolean exists(String key);

}

  

2、JedisTemple :Jedis 操作模板類,請參照Spring的JdbcTemple封裝重覆但又必要的操作

 1 /**
 2  * @ClassName: JedisTemple
 3  * @Description: Jedis 操作模板類,為啥要這個?請參照{@link JdbcTemple} 封裝重覆不必要的操作
 4  * @author 徐飛
 5  * @date 2016年1月26日 下午2:37:24
 6  *
 7  */
 8 public class JedisTemple {
 9 
10     /** 緩存客戶端 **/
11     private JedisPool jedisPool;// 非切片連接池
12 
13     public JedisTemple(JedisPool jedisPool) {
14         this.jedisPool = jedisPool;
15     }
16 
17     /**
18      * @Title: execute
19      * @Description: 執行{@link RedisPoolCallback#doInJedis(Jedis)}的方法
20      * @param action
21      * @return
22      * @author 徐飛
23      */
24     public <T> T execute(RedisPoolCallback<T> action) {
25         T value = null;
26         Jedis jedis = null;
27         try {
28             jedis = jedisPool.getResource();
29             return action.doInJedis(jedis);
30         } catch (Exception e) {
31             // 釋放redis對象
32             jedisPool.returnBrokenResource(jedis);
33             e.printStackTrace();
34         } finally {
35             // 返還到連接池
36             returnResource(jedisPool, jedis);
37         }
38 
39         return value;
40     }
41 
42     /** 
43     * 返還到連接池 
44     * @param pool  
45     * @param redis 
46     */
47     private void returnResource(JedisPool pool, Jedis redis) {
48         // 如果redis為空不返回
49         if (redis != null) {
50             pool.returnResource(redis);
51         }
52     }
53 
54     public JedisPool getJedisPool() {
55         return jedisPool;
56     }
57 
58     public void setJedisPool(JedisPool jedisPool) {
59         this.jedisPool = jedisPool;
60     }
61 
62 }

3、RedisPoolCallback:redis操作回調介面,此介面主要為JedisTemple模板使用

 1 import redis.clients.jedis.Jedis;
 2 
 3 /**
 4  * @ClassName: RedisPoolCallback
 5  * @Description: redis操作回調介面,此介面主要為JedisTemple模板使用
 6  * @author 徐飛
 7  * @date 2016年1月26日 下午2:35:41
 8  *
 9  * @param <T>
10  */
11 public interface RedisPoolCallback<T> {
12     /**
13      * @Title: doInJedis
14      * @Description: 回調執行方法,需要重新此方法,一般推薦使用匿名內部類
15      * @param jedis
16      * @return
17      * @author 徐飛
18      */
19     T doInJedis(Jedis jedis);
20 }

 

4、RedisCacheClient :redis客戶端實現類

 1 import redis.clients.jedis.Jedis;
 2 import redis.clients.util.SafeEncoder;
 3 
 4 import com.cxypub.baseframework.sdk.util.ObjectUtils;
 5 
 6 /**
 7  * @ClassName: RedisCacheClient
 8  * @Description: redis緩存客戶端
 9  * @author 徐飛
10  * @date 2015-4-16 上午10:42:32
11  *
12  */
13 public class RedisCacheClient implements SimpleCache {
14 
15     private JedisTemple jedisTemple;
16 
17     public RedisCacheClient(JedisTemple jedisTemple) {
18         this.jedisTemple = jedisTemple;
19     }
20 
21     @Override
22     public boolean add(final String key, final Object valueObject) {
23         try {
24             jedisTemple.execute(new RedisPoolCallback<Boolean>() {
25                 @Override
26                 public Boolean doInJedis(Jedis jedis) {
27                     jedis.set(SafeEncoder.encode(key), ObjectUtils.object2Byte(valueObject));
28                     return true;
29                 }
30 
31             });
32         } catch (Exception e) {
33             e.printStackTrace();
34             return false;
35         }
36         return true;
37     }
38 
39     @Override
40     public Object get(final String key) {
41 
42         return jedisTemple.execute(new RedisPoolCallback<Object>() {
43             @Override
44             public Object doInJedis(Jedis jedis) {
45                 byte[] cacheValue = jedis.get(SafeEncoder.encode(key));
46                 if (cacheValue != null) {
47                     return ObjectUtils.byte2Object(cacheValue);
48                 }
49                 return null;
50             }
51 
52         });
53     }
54 
55     @Override
56     public long delete(final String key) {
57         return jedisTemple.execute(new RedisPoolCallback<Long>() {
58             @Override
59             public Long doInJedis(Jedis jedis) {
60                 return jedis.del(key);
61             }
62         });
63     }
64 
65     @Override
66     public boolean add(final String key, Object value, final int seconds) {
67         try {
68             this.add(key, value);
69             jedisTemple.execute(new RedisPoolCallback<Long>() {
70                 @Override
71                 public Long doInJedis(Jedis jedis) {
72                     return jedis.expire(key, seconds);
73                 }
74             });
75         } catch (Exception e) {
76             e.printStackTrace();
77             return false;
78         }
79         return true;
80     }
81 
82     @Override
83     public boolean exists(final String key) {
84         return jedisTemple.execute(new RedisPoolCallback<Boolean>() {
85             @Override
86             public Boolean doInJedis(Jedis jedis) {
87                 return jedis.exists(key);
88             }
89         });
90     }
91 
92 }

 


5、實現了代碼,下麵就開始將客戶端整合進spring就行了,上配置文件

redis.properties:

 1 # Redis settings
 2 redis.host=192.168.1.215
 3 redis.port=6379
 4 redis.pass=
 5 
 6 # 控制一個pool可分配多少個jedis實例,通過pool.getResource()來獲取;
 7 # 如果賦值為-1,則表示不限制;如果pool已經分配了maxActive個jedis實例,則此時pool的狀態為exhausted(耗盡)。
 8 redis.maxTotal=600
 9 # 控制一個pool最多有多少個狀態為idle(空閑的)的jedis實例。
10 redis.maxIdle=300
11 # 表示當borrow(引入)一個jedis實例時,最大的等待時間,如果超過等待時間,則直接拋出JedisConnectionException;
12 redis.maxWaitMillis=1000
13 # 在borrow一個jedis實例時,是否提前進行validate操作;如果為true,則得到的jedis實例均是可用的;
14 redis.testOnBorrow=true

applicationContext-redis.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
           http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
           http://www.springframework.org/schema/aop 
           http://www.springframework.org/schema/aop/spring-aop-2.0.xsd
           http://www.springframework.org/schema/tx 
           http://www.springframework.org/schema/tx/spring-tx-2.0.xsd"
    default-autowire="autodetect" default-lazy-init="false">

    <!-- jedis 配置 -->
    <bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">  
        <property name="maxIdle" value="${redis.maxIdle}" />  
        <property name="maxTotal" value="${redis.maxTotal}" />  
        <property name="maxWaitMillis" value="${redis.maxWaitMillis}" />  
        <property name="testOnBorrow" value="${redis.testOnBorrow}" />  
    </bean>
    
    <!-- jedis 連接池  -->
    <bean id="jedisPool" class="redis.clients.jedis.JedisPool">
        <constructor-arg ref="jedisPoolConfig" />
        <constructor-arg value="${redis.host}" />
        <constructor-arg value="${redis.port}" type="java.lang.Integer" />
    </bean>
    
    <!-- jedis 操作 temple  -->
    <bean id="jedisTemple" class="com.cxypub.baseframework.sdk.cache.JedisTemple">
        <constructor-arg ref="jedisPool" />
    </bean>
    
    <!-- jedis 客戶端,真正提供給系統使用的客戶端,當然如果這個客戶端的方法不滿足,可以使用jedisTemple -->
    <bean id="jedisClient" class="com.cxypub.baseframework.sdk.cache.RedisCacheClient">
        <constructor-arg ref="jedisTemple" />
    </bean>
    

</beans>

 

 

6、這樣在項目中就可以將jedisClient 註入到任何類中了,我這裡寫了一個測試客戶端,這個直接運行的,一同貼上。

package com.cxypub.baseframework.sdk.cache;

import java.util.Date;

import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

import com.cxypub.baseframework.sdk.dictionary.entity.Dictionary;

public class RedisTest {
    public static void main(String[] args) {
        JedisPoolConfig config = new JedisPoolConfig();
        config.setMaxTotal(500);
        config.setMaxIdle(5);
        config.setMaxWaitMillis(1000 * 100);
        config.setTestOnBorrow(true);
        JedisPool jedisPool = new JedisPool(config, "192.168.1.215", 6379);
        JedisTemple jedisTemple = new JedisTemple(jedisPool);
        RedisCacheClient client = new RedisCacheClient(jedisTemple);
        Dictionary dict = new Dictionary();
        dict.setId("qwertryruyrtutyu");
        dict.setDictChineseName("上海");
        dict.setCreateTime(new Date());
        client.add("xufei", dict);
        Dictionary dict2 = (Dictionary) client.get("xufei");
        System.out.println(dict2);
        System.out.println(dict == dict2);
    }
}

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 問題描述 問題解決 首先檢查編輯器是否已經設置為1.7: Right click your project > Properties > Java Compiler and set “Compiler compliance level” to 1.7 然後,檢查Project Facts: Next...
  • $obj_sheet->setCellValueExplicit($cells[$_counter].$i, (isset($val[$_value_key]) ? $val[$_value_key] : ''),PHPExcel_Cell_DataType::TYPE_STRING);
  • 一. commit 和 push 的區別git作為支持分散式版本管理的工具,它管理的庫(repository)分為本地庫、遠程庫。git commit操作的是本地庫,git push操作的是遠程庫。git commit是將本地修改過的文件提交到本地庫中。git push是將本地庫中的最新信息發送給遠...
  • 在CMD命令行中啟動運行startup.bat,啟運程式總是閃退,查看日誌發現如下錯誤:26-Jan-2016 18:12:34.463 SEVERE [main] org.apache.coyote.AbstractProtocol.init Failed to initialize end po...
  • 關於AOP、之前我已寫過一個系列的隨筆: 《自己實現簡單的AOP》,它的關註點在於實現、實現語言是C#,實現方式為 自定義實現RealProxy 抽象類、重寫Invoke方法,以便進行方法調用的攔截、藉此實現AOP。感興趣的園友可以去瞅瞅。 今天、我們來看一下Spring中的AOP,本隨筆著重...
  • 本文翻譯自《effective modern C++》,由於水平有限,故無法保證翻譯完全正確,歡迎指出錯誤。謝謝!如果你曾經同過久的編譯時間鬥爭過,那麼你肯定對Pimpl("point to implementation",指向實現)機制很熟悉了。這種技術讓你把類的數據成員替換成指向一個實現類(或....
  • python3應該是python的趨勢所在,當前目前爭議也比較大,這篇隨筆的主要目的是記錄在linux6.4下搭建python3環境的過程 以及碰到的問題和解決過程。
  • 在任何迴圈語句的主體部分,均可用break控制迴圈的流程。break用於強行退出迴圈,不執行迴圈中剩餘的語句。(break語句還可用於多支語句switch中)continue 語句用於迴圈語句體中,用於終止某次迴圈過程,即跳過迴圈體中尚未執行的語句,接著進行下一次是否執行迴圈的判定。【break的例...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...