ssm+reids緩存整合

来源:https://www.cnblogs.com/sank/archive/2019/08/09/11326842.html
-Advertisement-
Play Games

在說正文之前我們先介紹一下redis: redis是當今比較熱門的非關係型資料庫之一,他使用的是key-value的鍵值對來進行存儲,是一個存在於記憶體之中的資料庫,我們一般用於做數據緩存。當我們需要大量的數據查詢時,如果我們都直接訪問資料庫時,會嚴重影響資料庫性能。所以我們一般的操作就是在db層之上 ...


在說正文之前我們先介紹一下redis:

  redis是當今比較熱門的非關係型資料庫之一,他使用的是key-value的鍵值對來進行存儲,是一個存在於記憶體之中的資料庫,我們一般用於做數據緩存。當我們需要大量的數據查詢時,如果我們都直接訪問資料庫時,會嚴重影響資料庫性能。所以我們一般的操作就是在db層之上的各級使用多級的no-sql來為db提供緩衝。

  因為redis是存在於記憶體之中,那麼問題來了當我們斷電時或者宕機時就會產生數據丟失,所以redis為我們提供了rdb和aof的兩種持久化保存的方式,這也是為什麼同樣是緩存資料庫我們選擇redis而不選擇memcache的原因。而且為了在大流量下提供穩定業務,redis還提供了redis-cluster,twemproxy,codis等集群化方案,為我們搭建分散式系統提供了可能。廢話說了這麼多下麵開始正文。

一.添加redis對應的依賴

  <!-- reids緩存-->
    <!-- https://mvnrepository.com/artifact/org.springframework.data/spring-data-redis -->
    <dependency>
      <groupId>org.springframework.data</groupId>
      <artifactId>spring-data-redis</artifactId>
      <version>${redis.data.version}</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/redis.clients/jedis -->
    <dependency>
      <groupId>redis.clients</groupId>
      <artifactId>jedis</artifactId>
      <version>${redis.clients.version}</version>
    </dependency>

依賴版本大家可以下用的最多的比較穩定。

二.添加相應的spring配置

<?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:context="http://www.springframework.org/schema/context"
       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.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
    <!--開啟aop代理-->
    <aop:aspectj-autoproxy expose-proxy="true"/>
    <!--spring添加註解掃描-->
    <context:annotation-config></context:annotation-config>
    <!--spring 註解掃描但是要排除spring mvc的控制器-->
    <context:component-scan base-package="com">
        <context:exclude-filter type="annotation"
                                expression="org.springframework.stereotype.Controller"></context:exclude-filter>
    </context:component-scan>

    <!--載入資源文件,該標簽全文只能有一個-->
    <context:property-placeholder location="classpath:jdbc.properties,classpath:redisconfig.properties"/>

    <!--配置數據源,阿裡數據連接池-->
    <bean id="dataSource" class="${jdbc.dataType}" destroy-method="close">
        <property name="driverClassName" value="${jdbc.driver}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.usename}"/>
        <property name="password" value="${jdbc.password}"/>

        <!-- 資料庫連接池配置 -->
        <property name="initialSize" value="20"/><!-- 初始化連接數量 -->
        <property name="minIdle" value="5"/>   <!-- 最小空閑連接數量 -->
        <property name="maxActive" value="1500"/> <!-- 最大連接數量 -->
        <property name="maxWait" value="60000"/>  <!-- 最大建立連接等待時間(毫秒)。如果超過此時間將接到異常。設為-1表示無限制-->
        <property name="timeBetweenEvictionRunsMillis" value="60000"/>  <!--  配置間隔多久才進行一次檢測,檢測需要關閉的空閑連接,單位是毫秒   -->
        <property name="minEvictableIdleTimeMillis" value="300000"/>   <!-- 配置一個連接在池中最小生存的時間,單位是毫秒   -->
        <property name="validationQuery" value="SELECT 'x'"/>
        <property name="testWhileIdle" value="true"/>  <!--空閑時是否進行驗證,檢查對象是否有效 -->
        <property name="testOnBorrow" value="false"/>  <!--取得對象時是否進行驗證,檢查對象是否有效 -->
        <property name="testOnReturn" value="false"/>  <!--返回對象時是否進行驗證 -->
        <!--  打開PSCache,並且指定每個連接上PSCache的大小   -->
        <property name="poolPreparedStatements" value="true"/>  <!-- 表明是否開啟statement cache,預設為false,也就是不開啟 -->
        <property name="maxPoolPreparedStatementPerConnectionSize"
                  value="20"/>  <!-- statement cache的大小,預設為-1,也就是不限制 -->

        <!-- 配置監控統計攔截的filters,去掉後監控界面sql無法統計   -->
        <property name="filters" value="stat"/>
    </bean>


    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="configLocation" value="classpath:mybatis-config.xml"/>
        <property name="dataSource" ref="dataSource"/>
        <!-- 掃描entity包 使用別名 -->
        <property name="typeAliasesPackage" value="com.lwc.pojo"/>
        <!-- 掃描sql配置文件:mapper需要的xml文件 -->
        <property name="mapperLocations" value="classpath*:com/lwc/dao/mapper/*.xml"/>
    </bean>
    <!--根據介面名生成對應的代理類-->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.lwc.dao"/>
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
    </bean>


    <!-- 配置事務管理器 -->
    <bean id="transactionManager"
          class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!-- 註入資料庫連接池 -->
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <!--配置事務切麵-->
    <aop:config>
        <aop:advisor advice-ref="txAdvice" pointcut="execution(* com.lwc.service.*.*(..))"></aop:advisor>
    </aop:config>
    <!-- 2 事務詳情(事務通知)  , 在aop篩選基礎上,比如對ABC三個確定使用什麼樣的事務。例如:AC讀寫、B只讀 等
       <tx:attributes> 用於配置事務詳情(屬性屬性)
           <tx:method name=""/> 詳情具體配置
               propagation 傳播行為 , REQUIRED:必須;REQUIRES_NEW:必須是新的
               isolation 隔離級別
   -->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <tx:method name="*" propagation="REQUIRED"/>
        </tx:attributes>
    </tx:advice>
    <!--配置redis緩存-->
    <!--redis配置-->
    <bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">

        <!--控制一個pool最多有多少個狀態為空閑的jedis實例-->
        <property name="maxIdle" value="${redis.maxIdle}"></property>
        <!--當borrow(引入)一個實例時,最大的等待時間,如果超過則拋出jedisConnectionException-->
        <property name="maxWaitMillis" value="${redis.maxWaitMillis}"></property>
        <property name="maxTotal" value="${redis.maxTotal}"></property>
        <!-- 在在borrow一個jedis實例時,是否提前進行validate操作;如果為true,則得到的jedis實例均是可用的-->
        <property name="testOnBorrow" value="${redis.testOnBorrow}"></property>

    </bean>

    <!--redis連接池-->
    <bean id="jedisPool" class="redis.clients.jedis.JedisPool">
        <constructor-arg index="0" ref="jedisPoolConfig"></constructor-arg>
        <constructor-arg index="1" value="${redis.host}"></constructor-arg>
        <constructor-arg index="2" value="${redis.port}"></constructor-arg>
        <constructor-arg index="3" value="${redis.timeout}"></constructor-arg>

    </bean>
    <!-- redis連接工廠 -->
    <bean id="JedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
        <property name="hostName" value="${redis.host}" ></property>
        <property name="port" value="${redis.port}"></property>
        <property name="poolConfig" ref="jedisPoolConfig"></property>
    </bean>
    <!-- redis操作模板,這裡採用儘量面向對象的模板 -->
    <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
        <property name="connectionFactory" ref="JedisConnectionFactory"/>
        <property name="keySerializer" ref="stringReadisSerializer"/>
        <property name="valueSerializer" ref="stringReadisSerializer"/>
        <property name="hashKeySerializer">
            <bean class="org.springframework.data.redis.serializer.StringRedisSerializer"/>
        </property>
        <property name="hashValueSerializer">
            <bean class="org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer"/>
        </property>
        <!--開啟事務-->
        <property name="enableTransactionSupport" value="true"/>
     </bean>


    <!--使用字元串進行序列化-->
    <bean id="stringReadisSerializer" class="org.springframework.data.redis.serializer.StringRedisSerializer"/>
    <!--使用JDK的序列化器進行轉化-->
    <bean id="jdkSerializationRedisSerializer" class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer"/>

    <bean id="getCache" class="com.lwc.redis.aspectj.GetCachAop">
        <property name="redisTemplate" ref="redisTemplate"/>
    </bean>
</beans>

這裡是spring的基本配置,這裡我沒有分開來,反正都有註釋

三.redis的一些基本資源文件

redis.host=localhost
redis.port=6379
redis.maxIdle=50
redis.maxTotal=100
redis.maxWaitMillis=3000
redis.testOnBorrow=true
redis.timeout=5000

這裡我沒有配置密碼,所以就沒有寫pass,但是如果有需要的話可以找到redis的配置文件加上如下語句

這樣就可以了,如果沒有配置密碼而配置了<property name="password" value="${redis.password}" />將會報錯

四.利用aop和自定義註解來進行實現環繞

  下麵是自定義註解,並且配上了一些註解的解釋,還沒有學過自定註解的小伙伴可以看看

package com.lwc.redis.annotation;

import java.lang.annotation.*;

/**自定義註解
 *   2.1.1 Target:表示註解的作用目標 
 *
 *                @Target(ElementType.TYPE)   //介面、類、枚舉、註解
 *
 *     @Target(ElementType.FIELD) //欄位、枚舉的常量
 *
 *     @Target(ElementType.METHOD) //方法
 *
 *     @Target(ElementType.PARAMETER) //方法參數
 *
 *     @Target(ElementType.CONSTRUCTOR)  //構造函數
 *
 *     @Target(ElementType.LOCAL_VARIABLE)//局部變數
 *
 *     @Target(ElementType.ANNOTATION_TYPE)//註解
 *
 *     @Target(ElementType.PACKAGE) ///包
 *
 *   2.1.2 @Documented:說明該註解將被包含在javadoc中;
 *
 *   2.1.3 @Inherited:說明子類可以繼承父類中的該註解 ;
 *
 *   2.1.4 @Retention:註解的保留位置;
 *
 *                @Retention(RetentionPolicy.SOURCE)   //註解僅存在於源碼中,在class位元組碼文件中不包含
 *
 *        @Retention(RetentionPolicy.CLASS)     // 預設的保留策略,註解會在class位元組碼文件中存在,但運行時無法獲得,
 *
 *     @Retention(RetentionPolicy.RUNTIME)  // 註解會在class位元組碼文件中存在,在運行時可以通過反射獲取到
 */
@Target(ElementType.METHOD)//目標為方法
@Retention(RetentionPolicy.RUNTIME)//註解在類中存在,運行時通過反射獲取
@Documented
@Inherited
public @interface GetCache {
    String name() default "";
    String value() default "";
}

定義這個註解是為了讓aop可以直接根據註解來進行切麵環繞,而不需要根據傳統的方法來進行切點,這樣會方便很多,否則的話就需要

定義介面然後對介面需要的方法進行定義切點,在實現該介面,這樣也可以做到切麵環繞,但是會更麻煩點,有興趣的小伙伴可以自己試試

 通知類:

package com.lwc.redis.aspectj;

import com.lwc.redis.annotation.GetCache;
import com.lwc.util.RedisCache;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.io.Serializable;
import java.lang.reflect.Method;

@Component
@Aspect
public class GetCachAop {
    @Autowired
    private RedisTemplate<Serializable,Object> redisTemplate;
   private RedisCache redisCache=new RedisCache();
   //定義切點
   @Pointcut("@annotation(com.lwc.redis.annotation.GetCache)")
    public void getCache(){
       System.out.println("獲取記憶體數據切入點");
   }
   /*在所有標註了GetCache的地方切入*/
   @Around("getCache()")
    public Object beforeExec(ProceedingJoinPoint joinPoint){
       //生成redis中的id,根據自己指定的格式
       String redisKey=getCacheKey(joinPoint);

       //獲取從redis中查詢得到的對象
       Object object=redisCache.getDataFromRedis(redisKey);

       //如果查詢到了
       if(null!=object){
           System.out.println("從redis中獲取到了數據");
           return object;
       }else{
           System.out.println("從資料庫中查詢數據");
           //如果沒有查詢到,則在資料庫中進行查詢
           try {
               object=joinPoint.proceed();
           } catch (Throwable throwable) {
               throwable.printStackTrace();
           }
       }

       //在目標方法執行完之後:將查到的數據放入到redis中
       redisCache.setDataToRedis(redisKey,object);
       return object;
   }

    /**
     * 根據方法名參數名類名獲取唯一的一個鍵值
     * 格式為yourpackage.classname.methodname(int).argsvalue123
     * @param joinPoint
     * @return
     */
    //變數沒有用到時不讓警告
   @SuppressWarnings("unused")
   private String getCacheKey(ProceedingJoinPoint joinPoint){
       //獲取切入方法的一些相關的信息
       MethodSignature ms=(MethodSignature) joinPoint.getSignature();

       Method method=ms.getMethod();
       //獲取註解中設置的對應參數
       String ActionName=method.getAnnotation(GetCache.class).value();
       String fieldList=method.getAnnotation(GetCache.class).name();
       for(String field:fieldList.split("."))
           ActionName+="."+field;

       //獲取切點方法的參數
       String id=null;
       Object[] args=joinPoint.getArgs();
       if(args!=null && args.length>0)
           id=String.valueOf(args[0]);
       ActionName+="="+id;
       String redisKey=ms+"."+ActionName;
       return redisKey;
   }

   public void setRedisTemplate( RedisTemplate<Serializable, Object> redisTemplate){
       this.redisTemplate = redisTemplate;
   }
}

上面代碼都有詳細註釋這裡就不重覆了

下麵是序列化的工具類:

package com.lwc.util;

import java.io.*;

public class SerializableUtil {

    //將位元組數組反序列化為對象
    public static  Object toObject(byte[] bytes){
        Object obj=null;
        try{
            ByteArrayInputStream bis=new ByteArrayInputStream(bytes);
            ObjectInputStream ois=new ObjectInputStream(bis);
            obj=ois.read();
            ois.close();
            bis.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return obj;
    }
    //將對象序列化為位元組數組
    public static byte[] toByteArray(Object obj){
        byte[] bytes =null;
        ByteArrayOutputStream bos =new ByteArrayOutputStream();
        try{
            ObjectOutputStream oos=new ObjectOutputStream(bos);
            oos.writeObject(obj);
            oos.flush();
            bytes=bos.toByteArray();
            oos.close();
            bos.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return bytes;
    }
}

下麵是緩存工具類:

package com.lwc.util;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.stereotype.Component;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
/**
 * redis緩存工具類
 */

public class RedisCache  {

    private static JedisPool jedisPool;
    //靜態類進行參數的初始化
    static {
         ClassPathXmlApplicationContext cxac=new ClassPathXmlApplicationContext("applicationContext.xml");
        jedisPool=(JedisPool) cxac.getBean("jedisPool");
    }
    //從緩存中讀取數據,進行反序列化
    public Object getDataFromRedis(String redisKey){
        Jedis jedis=jedisPool.getResource();
        byte[] result=jedis.get(redisKey.getBytes());
       //如果沒有查詢到,就返回空
        if(null==result)
            return null;
        return SerializableUtil.toObject(result);
    }

    //將資料庫中查到的數據放入redis中
    public void setDataToRedis(String redisKey,Object obj){
        byte[] bytes =SerializableUtil.toByteArray(obj);
        Jedis jedis=jedisPool.getResource();
        String sucess=jedis.set(redisKey.getBytes(),bytes);
        if("OK".equals(sucess)){
            System.out.println("數據保存成功");
        }
    }
}

當然我也看到有人繼承cach類來使用redis緩存,下麵貼出別人的代碼併進行解釋:

package com.ssm.redis;
 
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
 
import org.apache.ibatis.cache.Cache;
import org.springframework.beans.factory.annotation.Autowired;
 
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
 
public class RedisCache implements Cache {
 
    private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
    /**
     * Jedis客戶端
     */
 
    @Autowired
    private Jedis redisClient = createClient();
 
    private String id;
 
    public RedisCache(final String id) {
        if (id == null) {
            throw new IllegalArgumentException("必須傳入ID");
        }
        System.out.println("MybatisRedisCache:id=" + id);
        this.id = id;
    }
 
    @Override
    public void clear() {
        redisClient.flushDB();
    }
 
    @Override
    public String getId() {
        return this.id;
    }
 
    @Override
    public Object getObject(Object key) {
        byte[] ob = redisClient.get(SerializeUtil.serialize(key.toString()));
        if (ob == null) {
            return null;
        }
        Object value = SerializeUtil.unSerialize(ob);
        return value;
    }
 
    @Override
    public ReadWriteLock getReadWriteLock() {
        return readWriteLock;
    }
 
    @Override
    public int getSize() {
        return Integer.valueOf(redisClient.dbSize().toString());
    }
 
    @Override
    public void putObject(Object key, Object value) {
        redisClient.set(SerializeUtil.serialize(key.toString()), SerializeUtil.serialize(value));
    }
 
    @Override
    public Object removeObject(Object key) {
        return redisClient.expire(SerializeUtil.serialize(key.toString()), 0);
    }
 
    protected static Jedis createClient() {
 
        try {
            @SuppressWarnings("resource")
            JedisPool pool = new JedisPool(new JedisPoolConfig(), "127.0.0.1", 6379);
            return pool.getResource();
        } catch (Exception e) {
            e.printStackTrace();
        }
        throw new RuntimeException("初始化連接池錯誤");
    }
 
}

這個其實是使用mybatis自帶的二級緩存,從寫mybatis中的緩存類來進行實現使用這種方法就不需要額外的添加aop之類的只需要在映射的dao文件中添加<cache type="packagename.RedisCache" />這樣便可以直接使用redis而不需要aop配置

言歸正傳,我們接著使用aop來實現redis整合,下麵是service使用註解來進行實現redis存取

package com.lwc.service;

import com.lwc.dao.UserDao;
import com.lwc.pojo.User;
import com.lwc.redis.annotation.GetCache;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class UserService {
    @Autowired
    private UserDao userDao;
    @GetCache(name = "user" ,value = "id")
    public User getUser(Integer id){
        return userDao.selectUser(id);
    }
}

這樣我們每次調用service的這個方法就可以實現從redis存取數據了。其他的UserDao和對應的映射文件我這裡就不貼出來了

值得一說的是這裡的實體類要繼承Serializable 介面,不然會報錯,因為他時間實例化對象進行序列化存入記憶體之中。

以上就完成了基本的redis的使用,下篇博文將會介紹redis的持久化


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

-Advertisement-
Play Games
更多相關文章
  • crontab中的%是換行的意思,在使用時需要使用\做轉義。 ...
  • 0x00 事件 因為本地的伺服器硬體出現故障,導致一臺 Windows 系統的開發環境掛了,且無法短時間內恢復狀態。 應急方案是使用了雲上的系統重建了開發環境。 開發人員說需要掛了的那台 Windows 開發環境中的資料庫數據,因為一些數據只有那個環境裡面有。於是找了把螺絲刀把硬碟拆下來,用移動硬碟 ...
  • Linux 運行jar包命令如下: 方式一: 特點:當前ssh視窗被鎖定,可按CTRL + C打斷程式運行,或直接關閉視窗,程式退出 那如何讓視窗不鎖定? 方式二 java -jar shareniu.jar & java -jar shareniu.jar & &代表在後臺運行。 特定:當前ssh ...
  • I2C兩線式串列匯流排通訊協議,它是由飛利浦開發的,主要用於連接微控制器及其外圍設備之間,它是由數據線SDA和信號線SCL構成的,可發送和接收數據即在MUC和I2C設備之間,I2C和I2C之間進行全雙工信號傳輸,高速I2C匯流排一般可達到400kbps。一般我們也稱為TWI介面。 ...
  • 1 Linux介紹 1.1 常見的操作系統 Windows 它微軟公司開發的一款桌面操作系統(閉源系統)。版本有dos、win98、win NT、win XP、win 7、win vista、win 8、win 10。伺服器操作系統:win server 2003、win server 2008、w ...
  • Step 1:拷貝一份MySQL整個安裝文件,並拷貝一份 my.ini 文件放置安裝目錄下,然後修改以下內容。註意 port 不能設置為預設的3306,這裡我們設置為3307埠。 別忘了創建data文件夾! Step 2:以管理員身份打開CMD視窗,進入到安裝路徑的 bin(後面操作均基於此),然 ...
  • Elasticsearch 如何進行分頁查詢? 如果起始頁、頁大小很大會有性能上的損耗嗎? 本文從分頁查詢開始, 引出其deep paging (即深層分頁) 問題, 並分析其優劣, 給出解決方法. ...
  • 場景 k12線上教育公司的業務場景中,有一些業務場景需要實時統計和分析,如分析線上上課老師數量、學生數量,實時銷售額,課堂崩潰率等,需要實時反應上課的質量問題,以便於對整個公司的業務情況有大致的瞭解。 方案對比 對比了很多解決方案,如下幾種,列出來供參考。 設計方案 實時處理採用Flink SQL, ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...