Spring-session(spring-session-data-redis)实现分布式下Session共享_ycb1689的博客-程序员秘密

技术标签: java  

由于公司项目是单节点的,同事在开发过程中并未做Session共享,由于流量上升后,领导未经过了解直接加机器、加节点;大家可想而知了,项目中运行过程出现很多问题,经过同事们系列排查,才知道是由于上面原因导致拿不到Session。

大家在项目采用Spring-Session时一定要注意项目中Spring的版本,否则会给你带成很多坑,首先,Spring-Session所需的最低SPring版本是3.2.14.RELEASE

一、由于Redis采用Cluster集群方式所以在项目中引入以下版本Jar:

<dependency>
    <groupId>org.springframework.session</groupId>
	<artifactId>spring-session-data-redis</artifactId>
	<version>1.3.1.RELEASE</version>
</dependency>
<dependency>
	<groupId>org.springframework.session</groupId>
	<artifactId>spring-session</artifactId>
	<version>1.3.1.RELEASE</version>
</dependency> 

需要注意Spring的版本,下面所引用jar是在Spring 4.3.10.RELEASE所测试,如果低于此版本会报错,项目无法启动

错误如下:

ERROR [RMI TCP Connection(7)-127.0.0.1] - Context initialization failed
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'redisMessageListenerContainer' defined in class path resource [org/springframework/session/data/redis/config/annotation/web/http/RedisHttpSessionConfiguration.class]: Unsatisfied dependency expressed through constructor argument with index 1 of type [org.springframework.session.data.redis.RedisOperationsSessionRepository]: : Error creating bean with name 'sessionRepository' defined in class path resource [org/springframework/session/data/redis/config/annotation/web/http/RedisHttpSessionConfiguration.class]: Unsatisfied dependency expressed through constructor argument with index 0 of type [org.springframework.data.redis.core.RedisOperations]: : Error creating bean with name 'sessionRedisTemplate' defined in class path resource [org/springframework/session/data/redis/config/annotation/web/http/RedisHttpSessionConfiguration.class]: Invocation of init method failed; nested exception is java.lang.NoSuchMethodError: org.springframework.core.serializer.support.DeserializingConverter.<init>(Ljava/lang/ClassLoader;)V; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'sessionRedisTemplate' defined in class path resource [org/springframework/session/data/redis/config/annotation/web/http/RedisHttpSessionConfiguration.class]: Invocation of init method failed; nested exception is java.lang.NoSuchMethodError: org.springframework.core.serializer.support.DeserializingConverter.<init>(Ljava/lang/ClassLoader;)V; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'sessionRepository' defined in class path resource [org/springframework/session/data/redis/config/annotation/web/http/RedisHttpSessionConfiguration.class]: Unsatisfied dependency expressed through constructor argument with index 0 of type [org.springframework.data.redis.core.RedisOperations]: : Error creating bean with name 'sessionRedisTemplate' defined in class path resource [org/springframework/session/data/redis/config/annotation/web/http/RedisHttpSessionConfiguration.class]: Invocation of init method failed; nested exception is java.lang.NoSuchMethodError: org.springframework.core.serializer.support.DeserializingConverter.<init>(Ljava/lang/ClassLoader;)V; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'sessionRedisTemplate' defined in class path resource [org/springframework/session/data/redis/config/annotation/web/http/RedisHttpSessionConfiguration.class]: Invocation of init method failed; nested exception is java.lang.NoSuchMethodError: org.springframework.core.serializer.support.DeserializingConverter.<init>(Ljava/lang/ClassLoader;)V
	at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:752)
 

二、在Web.xml文件加入Spring-Session的入口依赖

 <filter>
        <filter-name>springSessionRepositoryFilter</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>springSessionRepositoryFilter</filter-name>
        <url-pattern>/*</url-pattern>
        <dispatcher>REQUEST</dispatcher>
        <dispatcher>ERROR</dispatcher>
    </filter-mapping>

注意此处filter放置位置一定要放在第一行或是<context-param>标签后面,否则出现希奇古怪问题。

<context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath*:config/applicationContext.xml</param-value>
    </context-param>

三、本人在项目是采用@Bean注解方式,不是传统的XML注入Bean方式,至于传统XML可以自己去网上百度

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisClusterConfiguration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisSentinelConfiguration;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.session.data.redis.config.ConfigureRedisAction;
import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
import org.springframework.session.web.http.CookieHttpSessionStrategy;
import org.springframework.session.web.http.CookieSerializer;
import org.springframework.session.web.http.DefaultCookieSerializer;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPoolConfig;


/**
 * RedisHttpSessionConfiguration的配置文件
 * 引入RedisHttpSessionConfiguration.class
 * maxInactiveIntervalInSeconds设置session在redis里的超时
 */
@Configuration
@EnableRedisHttpSession(maxInactiveIntervalInSeconds=3600)
public class RedisHttpSessionConfiguration {
    final static Logger logger = LoggerFactory.getLogger(RedisHttpSessionConfiguration.class);

    //是否为集群模式
    @Value("#{configproperties_disconf['redis.cluster']}")
    private boolean cluster;

    //redis地址(集群下多台使用,号隔开)
    @Value("#{configproperties_disconf['redis.hostAndPort']}")
    private String hostAndPort;

    //密码
    @Value("#{configproperties_disconf['redis.password']}")
    private String password;

    //连接超时时间(单台redis有效)
    @Value("#{configproperties_disconf['redis.connectionTimeout']}")
    private String connectionTimeout;

    //获取连接重试次数(单台redis有效)
    @Value("#{configproperties_disconf['redis.failTimes']}")
    private String failTimes;

    //设置socket调用InputStream读数据的超时时间
    @Value("#{configproperties_disconf['redis.soTimeout']}")
    private String soTimeout;

    //缓存默认过期时间
    @Value("#{configproperties_disconf['redis.expire']}")
    private String expire;

    //单个缓存最大存储字节数
    @Value("#{configproperties_disconf['redis.max.value.size']}")
    private String maxValueSize;

    //
    @Value("#{configproperties_disconf['redis.maxIdle']}")
    private Integer maxIdle;

    //
    @Value("#{configproperties_disconf['redis.maxTotal']}")
    private Integer maxTotal;
    //Cookie域名,
    @Value("#{configproperties_disconf['redis.maxTotal']}")
    private String cookieDomainName;

    private String[] hostAndPorts;

    @Autowired
    private JedisPoolConfig jedisPoolConfig;

    @Autowired
    private JedisConnectionFactory jedisConnectionFactory;

	@Bean
    public static ConfigureRedisAction configureRedisAction() {
        return ConfigureRedisAction.NO_OP;
    }


/*    @Bean
    public CookieHttpSessionStrategy cookieHttpSessionStrategy() {
	    CookieHttpSessionStrategy strategy = new CookieHttpSessionStrategy();
	    strategy.setCookieSerializer(cookieSerializer());
	    return strategy;
	}*/

    @Bean
    public CookieSerializer cookieSerializer() {
        final DefaultCookieSerializer serializer = new DefaultCookieSerializer();
        serializer.setCookieName("JSESSIONID_CSDN");
        //如果项目是在子域名下使用时,建议直接配置成主域名如下
        serializer.setDomainName("csdn.net");

        serializer.setCookiePath("/");
        //serializer.setDomainNamePattern("^.+?\\.(\\w+\\.[a-z]+)$");
        //serializer.setCookieMaxAge(sessionCookieConfig.getMaxAge());
        //serializer.setJvmRoute();
        //serializer.setUseSecureCookie();
        //serializer.setUseBase64Encoding();
        //serializer.setUseHttpOnlyCookie(false);
        //serializer.setRememberMeRequestAttribute(SpringSessionRememberMeServices.REMEMBER_ME_LOGIN_ATTR);
        return serializer;
    }


    /**
     * JedisPoolConfig 配置
     *
     * 配置JedisPoolConfig的各项属性
     *
     * @return
     */

    @Bean
    public JedisPoolConfig jedisPoolConfig(){
        JedisPoolConfig jedisPoolConfig= new JedisPoolConfig();
        //连接耗尽时是否阻塞, false报异常,ture阻塞直到超时, 默认true
        jedisPoolConfig.setBlockWhenExhausted(true);

        //是否启用pool的jmx管理功能, 默认true
        jedisPoolConfig.setJmxEnabled(true);

        //jedis调用returnObject方法时,是否进行有效检查
        jedisPoolConfig.setTestOnReturn(true);

        //最大空闲连接数, 默认8个
        jedisPoolConfig.setMaxIdle(maxIdle);

        //最大连接数, 默认8个
        jedisPoolConfig.setMaxTotal(maxTotal);

        //获取连接时的最大等待毫秒数(如果设置为阻塞时BlockWhenExhausted),如果超时就抛异常, 小于零:阻塞不确定的时间,  默认-1
        jedisPoolConfig.setMaxWaitMillis(-1);

        //逐出连接的最小空闲时间 默认1800000毫秒(30分钟)
        jedisPoolConfig.setMinEvictableIdleTimeMillis(1800000);

        //最小空闲连接数, 默认0
        jedisPoolConfig.setMinIdle(maxIdle);

        //每次逐出检查时 逐出的最大数目 如果为负数就是 : 1/abs(n), 默认3
        jedisPoolConfig.setNumTestsPerEvictionRun(3);

        //对象空闲多久后逐出, 当空闲时间>该值 且 空闲连接>最大空闲数 时直接逐出,不再根据MinEvictableIdleTimeMillis判断  (默认逐出策略)
        jedisPoolConfig.setSoftMinEvictableIdleTimeMillis(1800000);

        //在获取连接的时候检查有效性, 默认false
        jedisPoolConfig.setTestOnBorrow(false);

        //在空闲时检查有效性, 默认false
        jedisPoolConfig.setTestWhileIdle(false);

        //逐出扫描的时间间隔(毫秒) 如果为负数,则不运行逐出线程, 默认-1
        jedisPoolConfig.setTimeBetweenEvictionRunsMillis(-1);

        return jedisPoolConfig;
    }

    @Bean
    public JedisConnectionFactory jedisConnectionFactory() {
    	 try {
             if (StringUtils.isBlank(hostAndPort)) {
                 logger.error("not set redis server Host");
                 return null;
             }

             hostAndPorts = hostAndPort.split(",");
             if (StringUtils.isNotBlank(hostAndPort)) {

             }
             if (cluster) {
                 logger.info("redis sever enable cluster model");
                 RedisClusterConfiguration redisClusterConfiguration = new RedisClusterConfiguration();

                 for (String hp : hostAndPorts) {
                     String[] args = hp.split(":");
                     logger.error(args[0]+ "=="+args[1]);
                     redisClusterConfiguration.clusterNode(args[0], Integer.valueOf(args[1]).intValue());
                 }

                 JedisConnectionFactory connectionFactory = new JedisConnectionFactory(
                         redisClusterConfiguration, jedisPoolConfig);
                 connectionFactory.setTimeout(3600);
                 return connectionFactory;
             } else {
                 //哨兵模式
                 if ( hostAndPorts!= null && hostAndPorts.length > 1) {
                     logger.info("redis sever enable single sentinel model");
                     RedisSentinelConfiguration redisSentinelConfiguration= new RedisSentinelConfiguration();
                     for (String hp : hostAndPorts) {
                         String[] args = hp.split(":");
                         redisSentinelConfiguration.sentinel(args[0], Integer.valueOf(args[1]));
                     }
                     JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory(redisSentinelConfiguration, jedisPoolConfig);
                     jedisConnectionFactory.setTimeout(3600);
                     return jedisConnectionFactory;
                 } else {//单机模式
                     logger.info("redis sever enable single model");
                     String[] args = hostAndPort.split(":");
                     JedisConnectionFactory jedisConnectionFactory =  new JedisConnectionFactory(jedisPoolConfig);
                     jedisConnectionFactory.setHostName(args[0]);
                     jedisConnectionFactory.setPort(Integer.valueOf(args[1]).intValue());
                     jedisConnectionFactory.setTimeout(3600);
                     return jedisConnectionFactory;
                 }
             }
         } catch (Exception e) {
             logger.error("redis connection error");
         }
         return null;
    }
    

    /**
     * RedisTemplate配置
     *
     * @param redisConnectionFactory
     * @return
     */
    @Bean
    public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        StringRedisTemplate template = new StringRedisTemplate(redisConnectionFactory);
        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<Object>(
                Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        template.setValueSerializer(jackson2JsonRedisSerializer);// 如果key是String
        // 需要配置一下StringSerializer,不然key会乱码
        // /XX/XX
        template.afterPropertiesSet();
        return template;
    }

    /**
     * 管理缓存
     *
     * @param redisTemplate
     * @return
     */
    /*@SuppressWarnings("rawtypes")
    @Bean
    public CacheManager cacheManager(RedisTemplate redisTemplate) {
        CustomRedisCacheManager cacheManager = new CustomRedisCacheManager(redisTemplate);
        // rcm.setCacheNames(cacheNames)
        // 设置缓存过期时间
        // rcm.setDefaultExpiration(60);秒
        // 设置value的过期时间
        Map<String, Long> expiresMap = new ConcurrentHashMap<String, Long>();
        // map.put("test", 60L);
        Set<String> cacheNames = new HashSet<>();
        cacheNames.add("device-data");
        cacheNames.add("alarm-event-data");
        cacheNames.add("gps-data");
        cacheNames.add("alarm-event-new-data");
        cacheNames.add("relation-organ-data");
        cacheNames.add("relation-police-data");
        cacheNames.add("platform-config-data");
        cacheNames.add("organ-data");
        cacheNames.add("intercom-platform-data");
        cacheNames.add("alarm-seat-data");
        //expiresMap.put("alarm-seat-data", Long.parseLong(systemConfigRepository.findByKey("expires_alarm-seat-data").getValue()));
        expiresMap.put("alarm-event-new-data", 120L);
        // 设置 by chen
        cacheNames.add("relation-organ-data-new");
        expiresMap.put("relation-organ-data-new", 600L);
        // end
        cacheManager.setCacheNames(cacheNames);
        cacheManager.setExpires(expiresMap);
        cacheManager.setUsePrefix(true);
        return cacheManager;
    }*/
    public static void main(String[] args) {  
        Jedis jedis = new Jedis("25.30.9.4",6379);  
        //ping通显示PONG  
        System.out.println(jedis.ping());//去ping我们redis的主机所在ip和端口  
    }
 
}

下面代码是用于Redis缓存管理使用 

 

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cache.Cache;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.util.Assert;
 
 
public class CustomRedisCacheManager extends RedisCacheManager {
    private static Logger logger = LoggerFactory.getLogger(CustomRedisCacheManager.class);
 
    public CustomRedisCacheManager(RedisOperations redisOperations) {
        super(redisOperations);
    }
 
    @Override
    public Cache getCache(String name) {
        return new CustomRedisCacheManager.RedisCacheWrapper(super.getCache(name));
    }
 
    protected static class RedisCacheWrapper implements Cache {
 
        private final Cache delegate;
 
        public RedisCacheWrapper(Cache redisCache) {
            Assert.notNull(redisCache, "delegate cache must not be null");
            this.delegate = redisCache;
        }
 
        @Override
        public String getName() {
            try {
                return delegate.getName();
            } catch (Exception e) {
                return handleException(e);
            }
        }
 
        @Override
        public Object getNativeCache() {
            try {
                return delegate.getNativeCache();
            } catch (Exception e) {
                return handleException(e);
            }
        }
 
        @Override
        public Cache.ValueWrapper get(Object key) {
            try {
                return delegate.get(key);
            } catch (Exception e) {
                return handleException(e);
            }
        }
 
 
 
 
        @Override
        public void put(Object key, Object value) {
            try {
                delegate.put(key, value);
            } catch (Exception e) {
                handleException(e);
            }
        }
 
 
 
        @Override
        public void evict(Object o) {
            try {
                delegate.evict(o);
            } catch (Exception e) {
                handleException(e);
            }
        }
 
        @Override
        public void clear() {
            try {
                delegate.clear();
            } catch (Exception e) {
                handleException(e);
            }
        }
 
        private <T> T handleException(Exception e) {
            logger.error("redis连接异常", e);
            return null;
        }
    }
}

四、测试代码


import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;
import java.util.Map;

@Controller
@RequestMapping("/session")
public class SessionController { 
    private static final Logger logger = LoggerFactory.getLogger(SessionController.class);
 
    
    @ResponseBody
    @RequestMapping(value = "/getSession", method = RequestMethod.GET)
    public Map<String, Object>  getSession(HttpServletRequest request) {
    	request.getSession().setAttribute("testKey", "[email protected]");
        request.getSession().setMaxInactiveInterval(10*1000);
        String testKey = (String)request.getSession().getAttribute("testKey");
        //SessionManager.setAttribute("kes", "Hello World");
        return ResultUtils.getSuccessResultData(SessionManager.getAttribute("testKey") + "===="+ testKey );
         
    }
 

}

五、可以通过Redis客户端查看是否存入Redis中

使用spring session+redis存储的session如何查看.

​​​​127.0.0.1:6379> keys *
              1) "spring:session:expirations:133337740000"
              2) "spring:session:sessions:eefscef3ae-c8ea-4346-ba6b-9b3b26eee578"
127.0.0.1:6379> type spring:session:sessions:eeefefeae-c8ea-4346-ba6b-9b3b26eee578
                hash
127.0.0.1:6379> hkeys spring:session:sessions:eeefefeae-c8ea-4346-ba6b-9b3b26eee578
              1) "maxInactiveInterval"
              2) "creationTime"
              3) "lastAccessedTime"

存储在redis中的key的简单介绍说明:

//存储 Session 数据,数据类型hash,可以使用type查看
Key:spring:session:sessions:eeefefeae-c8ea-4346-ba6b-9b3b26eee578

//Redis TTL触发Session 过期。(Redis 本身功能),数据类型:String
Key:spring:session:sessions:expires:133337740000

//执行 TTL key ,可以查看剩余生存时间
//定时Job程序触发Session 过期。(spring-session 功能),数据类型:Set
Key:spring:session:expirations:133337740000

六、简单介绍一下Spring-Session在Redis中存储时数据结构形式

查看redis中的值:

127.0.0.1:6379> keys *
1) "spring:session:sessions:expires:fc454e71-c540-4097-8df2-92f88447063f"
2) "spring:session:expirations:1515135000000"
3) "spring:session:index:org.springframework.session.FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME:user"
4) "spring:session:sessions:fc454e71-c540-4097-8df2-92f88447063f"

Redis中的存储说明:
​1、spring:session是默认的Redis HttpSession前缀(redis中,我们常用’:’作为分割符)。
2、每一个session都会创建3组数据:

第一组:hash结构,spring-session存储的主要内容

spring:session:sessions:fc454e71-c540-4097-8df2-92f88447063f

hash结构有key和field,如上面的例子:hash的key为"spring:session:sessions"前缀加上fc454e71-c540-4097-8df2-92f88447063f,该key下的field有:

  • field=sessionAttr:qwe,value=123                
  • field=creationTime,value=                         //创建时间
  • field=maxInactiveInterval,value=             //
  • field=lastAccessedTime,value=              //最后访问时间

见上面图

第二组:String结构,用于ttl过期时间记录

spring:session:sessions:expires:fc454e71-c540-4097-8df2-92f88447063f

key为“spring:session:sessions:expires:”前缀+fc454e71-c540-4097-8df2-92f88447063f

value为空

第三组:set结构,过期时间记录

spring:session:expirations:1515135000000

set的key固定为“spring:session:expirations:1515135000000”,set的集合values为:

  • expires:c7fc28d7-5ae2-4077-bff2-5b2df6de11d8  //(一个会话一条)
  • expires:fc454e71-c540-4097-8df2-92f88447063f  //(一个会话一条)

简单提一下:redis清除过期key的行为是一个异步行为且是一个低优先级的行为,用文档中的原话来说便是,可能会导致session不被清除。于是引入了专门的expiresKey,来专门负责session的清除,包括我们自己在使用redis时也需要关注这一点。在开发层面,我们仅仅需要关注第三个key就行了。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/ycb1689/article/details/88529965

智能推荐

Ubuntu系统下载(清华大学开源软件镜像站)(ubuntu-20.04.1-desktop-amd64.iso)_-借我杀死庸碌的情怀-的博客-程序员秘密

清华大学开源网站镜像站网址:https://mirrors.tuna.tsinghua.edu.cn/进入之后在搜索框中搜索“ubuntu”

解决ScrollView嵌套RecyclerView只显示一行的一种比较简单的方法_AkersMan的博客-程序员秘密

解决ScrollView嵌套RecyclerView只显示一行的一种比较简单的方法ScrollView嵌套RecyclerView用多了,有些时候就会发现RecyclerView的数据只显示一行。具体情况不是很清楚,只是知道一种解决的办法,话不多说 <RelativeLayout android:layout_width="mat

一个正整数分解为几个连续的正整数之和_梓逸宸的博客-程序员秘密

题目: 给定你一个数字 如:15 15可分解为7+84+5+61+2+3+4+5再如:88不可分解为任何连续的正整数之和所以输出NONE此题就是给定一个数字如果这个数字可以分解为几个连续的正整数之和那么就输出所有的形式,如果不能就输出NONE今天这道题困扰了我好久,最后发现,一开始求和的时候算错了。 输入数n,设置起始位置i,再遍历连续正整数的长度k,由公式计算出 s

BZOJ 3625 [Codeforces Round #250]小朋友和二叉树 ——NTT 多项式求逆 多项式开根_ducode的博客-程序员秘密

生成函数又有奇妙的性质。$F(x)=C(x)*F(x)*F(x)+1$然后大力解方程,得到一个带根号的式子。多项式开根有解只与常数项有关。发现两个解只有一个是成立的。然后多项式开根、求逆。不太会算复杂度为什么是$n\log {n}$的。开根号里套了一个求逆,不应该是两个$\log$?#include &lt;map&gt;#include &lt;cmath...

.net core EFCORE以数据库为中心创建实体类_ef core 根据数据库创建实体_JunRuyuL的博客-程序员秘密

1、使用EFCORE 需要下载nuget 包 在程序包管理器输入Install-Package Microsoft.EntityFrameworkCore.SqlServer,Install-Package Microsoft.EntityFrameworkCore.Tools,Install-Package Microsoft.EntityFrameworkCore.SqlServer.Desi...

echarts实现一个页面同时显示多个图表_AinUser的博客-程序员秘密

今天群里有小姐姐问同一个页面显示多个图表,如何操作?先抛出一个官方的例子:https://gallery.echartsjs.com/editor.html?c=xhjub4szmz&amp;v=1下面的原文链接:看不懂可以参考一下这个:https://blog.csdn.net/weixin_38981413/article/details/78917155还有一个思路就是多容器承载...

随便推点

ARCGIS中某字段递增赋值_gis递增数字代码_Andy是个男子名的博客-程序员秘密

ARCGIS中某字段递增赋值在地段计算器中使用一下代码:rec=0def autoIncrement(): global rec pStart = 1 pInterval = 1 if (rec == 0): rec = pStart else: rec = rec + pInterval return rec然后在字段里输入autoIncrement(...

http和tcp以及websocket协议_websocket http tcp_MarryOnlyQueen的博客-程序员秘密

解析常见的协议TCP/IP协议HTTP协议什么是超文本什么是传输什么是协议优点总结HTTPSSSL/TLS协议分层模型TCP/IPOSI一个HTTP的请求过程二层转发以及三层路由HTTP/1HTTP/1.0HTTP/1.1队头阻塞问题HTTP/2websocket特性TCP/IP协议三次握手和四次挥手三次握手第一次握手:建立连接时,客户端发送syn包(syn=j)到服务器,并进入SYN_SENT状态,等待服务器确认;SYN:同步序列编号(Synchronize Sequence Numbers

ODOO11开发手册_yanhaiwang的博客-程序员秘密

今看到一篇ODOO11开发文档,希望能够分享给大家。原文地址http://www.docin.com/DocinViewer-2137881329-144.swf

音频编解码_音乐解码_一摩尔自由的博客-程序员秘密

https://www.cnblogs.com/skyofbitbit/p/3651270.htmlhttps://www.cnblogs.com/imstudy/p/10289944.html音频编解码常用的实现方案有三 种: 第一种就是采用专用的音频芯片对 语音信号进行采集和处理,音频编解码算法集成在硬件内部,如MP3编解码芯片、语音合成 分析芯片等。使用这种方案的优点就...

nginx php 静态化,nginx下WordPress伪静态化与静态化_城闭喧的博客-程序员秘密

在强大的模板、插件的支持下,WordPress从一个博客后台工具,直接升级为CMS内容发布系统,已经是没有任何疑义的一个事实。但是WordPress的文章链接,以xxx.xxx.xxx.xxx/?p=123之类的形式存在,对于网站的SEO来说,确实是不太友好。对于这个问题,其实wordpress本身是有支持的。在wordpress里,链接地址可以是“固定链接”的形式。这种形式,我们很多时候称它为“...

RPC框架对比_Bagba的博客-程序员秘密

由于项目需要,准备开发一个分布式限速服务,参考:分布式限速,会应用到RPC服务,所以需要先调研主流RPC服务的性能,可靠,易用性。Go RPCgolang原生的rpc服务GRPC底层协议基于HTTP2RPCX...

推荐文章

热门文章

相关标签