JWT 入门

文章目录

    • 使用JWT的原因
    • JWT结构
    • JWT入门案例
        • Token拦截

使用JWT的原因

为了保护项目之中的数据资源,那么一定就需要采用认证检测机制,于是SpringCloud进行认证处理,就可以使用SpringSecurity 来实现了,但是如果你真的去使用了SpringSecurity进行开发,因为维护的成本实在是太高了。

在后来的时候有很多的开发者开始尝试通过OAuth2统一认证来进行SpringCloud认证与授权服务,这种操作也属于较早期的实现了,这种实现最大的问题在于随着版本的更新会出现代码不稳定的情况,很多的开发者就开始尝试自己去独立的实现认证与授权的操作机制,于是就有了JWT开发技术,使用JWT最大的特点在于不需要维护所有的数据的状态。

使用JWT最大的特点在于不需要维护所有的数据的状态,同时可以自己包含有过期的时间,以及通过附加数据的形式传送所需要的额外的数据内容((可以保存认证以及授权信息)。
在WEB开发中需要维护Session的状态,所以如果用户访问量过大,那么必然会出现Session内容过多而导致服务器处理性能下降的惨剧,所以后面就需要引入WEB服务器的集群,但是为了便于服务器集群之中的Session管理,那么又需要进行分布式的Session存储,总之就一点:有状态的用户需要维护Session,Session维护成本很高。

对于JWT需要提供有一个专属的开发组件,这些组件可以实现JWT数据的生成以及各种检测操作机制,最重要的一点所有的认证可以直接通过网关来进行过滤,可以考虑通过网关来进行认证状态的检查,而后每一个具体的微服务实现权限的排查。
在这里插入图片描述

JWT结构

基于Token的单点登录技术可以有效的节约开发成本,但是这里面最重要的一点就是JWT数据的组成结构定什么?在实际的项目开发中,JWT主要是为了实现用户认证数据的处理,所以第三方应用客户端要想进行用户统一登录的操作,只需要传入用户认证所需要的数据信息,即可成功的获取到Token令牌,考虑到令牌的安全性以及实用性,在每一个JWT数据中会包含有三类信息项: Header头部信息、Payload负载信息、Signature数字签名。

在这里插入图片描述

使用JWT的结构特点,可以有效的实现用户数据信息的携带,每次进行服务调用时都需要传递此JWT数据,目标微服务依靠此数据实现用户登录状态检测,同时也可以根据其保存的用户角色数据,来进行当前操作执行的合法性校验
在这里插入图片描述

JWT入门案例

在实际项目开发中,不同的项目会存在有不同的数字签名、发布者等数据信息,考虑到JWT使用的便捷性,可以直接通过application.yml配置JWT的相关属性内容,随后将这些属性注入到指定的配置类中

1、【microboot项目】创建一个新的子模块“microboot-jwt”子模块,随后修改build.gradle配置文件添加依赖:

dependencies.gradle

ext.versions = [jjwt                                : '0.9.1', // JWT依赖库jaxb                                : '2.3.1', // JAXB依赖库 JDK11需要加这个依赖,8不需要
]
ext.libraries = [// 以下的配置为JWT所需要的依赖库'jjwt': "io.jsonwebtoken:jjwt:${versions.jjwt}",'jaxb-api': "javax.xml.bind:jaxb-api:${versions.jaxb}"
]

build.gradle

project('microboot-jwt') { // 子模块dependencies { // 配置子模块依赖compile('org.springframework.boot:spring-boot-starter-web')compile(libraries.'fastjson')compile(libraries.'jjwt')compile(libraries.'jaxb-api')}
}

2、【microboot-jwt子模块】创建 application.yml配置文件,配置JWT的相关环境属性,同时考虑到了很多的信息需要自定义,同时也需要保存有应用的名称,所以要添加“spring.application.name”配置项

spring:application:name: microboot-jwt # 应用名称
muyan: # 自定义配置项config: # 配置项定义jwt: # 配置JWT相关属性sign: muyan # JWT证书签名issuer: MuyanYootk # 证书签发人secret: www.yootk.com # 加密密钥expire: 10 # 有效时间(单位:秒)

如果你现在要做的是一个非常大型的JWT系统,那么这个时候可以考虑通过数据库来实现以上的配置项

3、【microboot-jwt子模块】所有的配置项一定要被程序进行读取,可以创建有一个配置类

package com.yootk.config;import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;@Data
@Component
@ConfigurationProperties(prefix = "muyan.config.jwt")
public class JWTConfigProperties { // 保存JWT配置项private String sign;private String issuer;private String secret;private long expire;
}

在之前application.yml中所配置的相关属性,实际上都是为了JWT数据服务准备的,因为JWT在整个的项目设计之中是一个核心的结构,所以最佳的做法是通过一个服务的包装类,来包裹jjwt依赖库里面所提供的各个组件。

4、【microboot-jwt子模块】既然要进行服务的开发,首先就应该要提供有一个完善的业务接口。

package com.yootk.service;import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.JwtException;import javax.crypto.SecretKey;
import java.util.Map;public interface ITokenService { // 实现JWT的相关操作接口public SecretKey generalKey(); // 获取当前JWT数据加密KEY/*** 生成一个合法的Token数据* @param id 这个Token的唯一ID(随意存储,本次可以考虑存储用户ID)* @param subject 所有附加的信息内容,本次直接接收了一个Map,但是最终存储的时候存放JSON* @return 返回一个有效的Token数据字符串*/public String createToken(String id, Map<String, Object> subject);/*** 是根据Token的字符串内容解析出其组成的信息(头信息与附加信息)* @param token 要解析的Token完整数据* @return Jws接口实例* @throws JwtException 如果Token失效或者结构错误*/public Jws<Claims> parseToken(String token) throws JwtException;/*** 校验当前传递的Token数据是否正确* @param token 要检查的Token数据* @return true表示合法、false表示无效*/public boolean verifyToken(String token);/*** Token存在有效时间的定义,所以一定要提供有Token刷新机制* @param token 原始的Token数据* @return 新的Token数据*/public String refreshToken(String token);
}

5、【microboot-jwt子模块】定义ITokenService业务接口子类,并且完成所有的抽象方法

package com.yootk.service.impl;import com.alibaba.fastjson.JSONObject;
import com.yootk.config.JWTConfigProperties;
import com.yootk.service.ITokenService;
import io.jsonwebtoken.*;
import org.apache.tomcat.util.codec.binary.Base64;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;@Service
public class TokenServiceImpl implements ITokenService {@Autowiredprivate JWTConfigProperties jwtConfigProperties; // JWT的相关配置属性@Value("${spring.application.name}")private String applicationName;private SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256; // 签名算法@Overridepublic SecretKey generalKey() { // 获取加密KEYbyte[] encodedKey = Base64.decodeBase64(Base64.encodeBase64(this.jwtConfigProperties.getSecret().getBytes()));SecretKey key = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");return key;}@Overridepublic String createToken(String id, Map<String, Object> subject) {Date nowDate = new Date(); // 获取当前的日期时间// 当前的时间 + 失效时间配置的秒数 = 最终失效的日期时间Date expireDate = new Date(nowDate.getTime() + this.jwtConfigProperties.getExpire() * 10000);Map<String, Object> claims = new HashMap<>(); // 附加的Claims信息claims.put("site", "www.yootk.com"); // 添加信息内容claims.put("book", "SpringBoot就业编程实战");  // 添加信息内容claims.put("company", "沐言科技");  // 添加信息内容Map<String, Object> headers = new HashMap<>(); // 保存的头信息headers.put("author", "李老师");headers.put("module", this.applicationName); // 保存应用的名称headers.put("desc", "喜欢你。");JwtBuilder builder = Jwts.builder().setClaims(claims)    // 保存Claims信息.setHeader(headers) // 保存Headedr信息.setId(id) // 保存ID内容.setIssuedAt(nowDate) // 证书签发日期时间.setIssuer(this.jwtConfigProperties.getIssuer()) // 证书签发者.setSubject(JSONObject.toJSONString(subject)) // 附加信息.signWith(this.signatureAlgorithm, this.generalKey()) // 签名算法.setExpiration(expireDate); // Token失效时间return builder.compact(); // 生成Token}@Overridepublic Jws<Claims> parseToken(String token) throws JwtException {if (this.verifyToken(token)) {  // 检查当前的Token是否正确Jws<Claims> claims = Jwts.parser().setSigningKey(this.generalKey()).parseClaimsJws(token);return claims;}return null;}@Overridepublic boolean verifyToken(String token) {try {Jwts.parser().setSigningKey(this.generalKey()).parseClaimsJws(token).getBody();return true;// 没有异常,解析成功} catch (JwtException exception) {return false;}}@Overridepublic String refreshToken(String token) {if (this.verifyToken(token)) {  // 正确的Token是可以进行刷新的Jws<Claims> claimsJws = this.parseToken(token); // 解析数据return this.createToken(claimsJws.getBody().getId(), JSONObject.parseObject(claimsJws.getBody().getSubject(), Map.class));}return null;}
}

6、【测试】

package com.yootk.test;import com.alibaba.fastjson.JSONObject;
import com.yootk.StartJWTApplication;
import com.yootk.config.JWTConfigProperties;
import com.yootk.service.ITokenService;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.context.web.WebAppConfiguration;import java.util.HashMap;
import java.util.Map;
import java.util.UUID;@ExtendWith(SpringExtension.class) // Junit5测试工具
@WebAppConfiguration    // 表示需要启动Web配置才可以进行测试
@SpringBootTest(classes = StartJWTApplication.class)  // 定义要测试的启动类
public class TestTokenService {@Autowiredprivate ITokenService tokenService;private String token = "eyJhdXRob3IiOiLniIblj6_niLHnmoTlsI_mnY7ogIHluIgiLCJtb2R1bGUiOiJtdXlhbi15b290ay10b2tlbiIsImFsZyI6IkhTMjU2IiwiZGVzYyI6IuaIkeaYr-S4gOS4quW-iOaZrumAmueahOiAgeW4iO-8jOWWnOasouaVmeWtpu-8jOiupOecn-aQnuecn-ato-eahOaVmeiCsuOAgiJ9.eyJzdWIiOiJ7XCJyaWRzXCI6XCJVU0VSO0FETUlOO0RFUFQ7RU1QO1JPTEVcIixcIm5hbWVcIjpcIuaykOiogOenkeaKgC3mnY7lhbTljY5cIixcIm1pZFwiOlwibXV5YW5cIn0iLCJzaXRlIjoid3d3Lnlvb3RrLmNvbSIsImJvb2siOiJTcHJpbmdCb2905bCx5Lia57yW56iL5a6e5oiYIiwiaXNzIjoiTXV5YW5Zb290ayIsImNvbXBhbnkiOiLmspDoqIDnp5HmioAiLCJleHAiOjE2MjQ3Njk5NzgsImlhdCI6MTYyNDc1OTk3OCwianRpIjoieW9vdGstNGQ2YzdkMzItZmE5Mi00ZTc4LWJkN2YtNzE1MGMxMDA3MDRlIn0.B7f11ckb4etMTcxzdzTh_1VubQSHnifl43t2-3atrD4";@Testpublic void testCreate() { // 创建Token数据Map<String, Object> map = new HashMap<>(); // 保存subject数据信息map.put("mid", "muyan");map.put("name", "沐言科技-李兴华");map.put("rids", "USER;ADMIN;DEPT;EMP;ROLE"); // 保存角色数据String id = "yootk-" + UUID.randomUUID(); // 随机生成IDSystem.out.println(this.tokenService.createToken(id, map));}@Testpublic void testParse() {Jws<Claims> claims = this.tokenService.parseToken(token); // 解析得到的Token数据claims.getHeader().forEach((name, value) -> {System.out.println("【JWT头信息】name = " + name + "、value = " + value);});System.err.println("------------------------------------------------------------------");claims.getBody().forEach((name, value) -> {System.out.println("【JWT主题信息】name = " + name + "、value = " + value);});System.err.println("------------------------------------------------------------------");Map<String, Object> map = JSONObject.parseObject(claims.getBody().get("sub").toString(), Map.class); // 用户配置的信息map.entrySet().forEach(entry -> {System.out.println("【用户数据】key = " + entry.getKey() + "、value = " + entry.getValue());});}@Testpublic void testVerifyJWT() {System.out.println(this.tokenService.verifyToken(token));}@Testpublic void testRefreshToken() {System.out.println(this.tokenService.refreshToken(token));}
}

Token拦截

本次的Token主要是为了保护最终微服务的资源的,但是所有的资源保护都有一个基个前提,那么就是要有相应的拦截处理,也就是说可以通过拦截器的形式来进行资源的保护,但是毕竟有些资源是需要保护的,而有一些资源是需要保护的,此时可以考虑通过一个自定义注解的形式来区分保护与非保护资源。

1、【microboot-jwt子模块】创建一个用于区分是否要使用Token保护的注解

package com.yootk.annotation;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Target({ElementType.METHOD}) // 该注解主要用于方法上
@Retention(RetentionPolicy.RUNTIME) // 运行时生效
public @interface JWTCheckToken { // JWT的检查注解public boolean required() default true; // 是否要启用Token检查
}

2、【microboot-jwt子模块】为了便于该注解的使用,可以创建一个MessageAction程序类,并且进行信息响应

package com.yootk.action;import com.yootk.annotation.JWTCheckToken;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
@RequestMapping("/message/*") // 父路径
public class MessageAction {@RequestMapping("echo")@JWTCheckToken // 这个资源需要被检查public Object echo(String msg) {return "【ECHO】" + msg;}
}

3、【microboot-jwt子模块】如果最终需要此注解生效,那么就需要定义拦截器。

package com.yootk.interceptor;import com.yootk.annotation.JWTCheckToken;
import com.yootk.service.ITokenService;
import jdk.jfr.Frequency;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;public class JWTAuthenticationInterceptor implements HandlerInterceptor { // 认证拦截器// Token可以通过参数传递也可以通过头信息传递private static final String TOKEN_NAME = "yootkToken"; // Token参数名称@Autowiredprivate ITokenService tokenService; // Token业务接口@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {if (!(handler instanceof HandlerMethod)) { // 不处理拦截操作return true;}HandlerMethod handlerMethod = (HandlerMethod) handler; // 类型转换Method method = handlerMethod.getMethod(); // 获取当前要执行的Action方法反射对象if (method.isAnnotationPresent(JWTCheckToken.class)) {  // 判断该方法上是否提供有指定的注解JWTCheckToken checkToken = method.getAnnotation(JWTCheckToken.class); // 获取指定注解if (checkToken.required()) {    // true表示要进行Token检查String token = this.getToken(request); // 获取Token数据if (!this.tokenService.verifyToken(token)) {    // 验证失败throw new RuntimeException("Token数据无效,无法访问。");}}}return true;}public String getToken(HttpServletRequest request) {String token = request.getParameter(TOKEN_NAME); // 通过参数获取头信息if (token == null || "".equals(token)) {    // 没有接收到Tokentoken = request.getHeader(TOKEN_NAME); // 通过头信息获取}return token;}
}

4、【microboot-jwt子模块】如果要想让拦截器生效,则需要定义一个配置类。

package com.yootk.config;import com.yootk.interceptor.JWTAuthenticationInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class InterceptorConfig implements WebMvcConfigurer { // 拦截配置类@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(this.getJWTAuthenticationInterceptor()).addPathPatterns("/**");}@Beanpublic HandlerInterceptor getJWTAuthenticationInterceptor() {return new JWTAuthenticationInterceptor();}
}

5、【浏览器】直接通过浏览器进行访问

localhost:8080/message/echo?msg=www.yootk.com&yootkToken=eyJhdXRob3IiOiLniIblj6_niLHnmoTlsI_mnY7ogIHluIgiLCJtb2R1bGUiOiJtdXlhbi15b290ay10b2tlbiIsImFsZyI6IkhTMjU2IiwiZGVzYyI6IuaIkeaYr-S4gOS4quW-iOaZrumAmueahOiAgeW4iO-8jOWWnOasouaVmeWtpu-8jOiupOecn-aQnuecn-ato-eahOaVmeiCsuOAgiJ9.eyJzdWIiOiJ7XCJyaWRzXCI6XCJVU0VSO0FETUlOO0RFUFQ7RU1QO1JPTEVcIixcIm5hbWVcIjpcIuaykOiogOenkeaKgC3mnY7lhbTljY5cIixcIm1pZFwiOlwibXV5YW5cIn0iLCJzaXRlIjoid3d3Lnlvb3RrLmNvbSIsImJvb2siOiJTcHJpbmdCb2905bCx5Lia57yW56iL5a6e5oiYIiwiaXNzIjoiTXV5YW5Zb290ayIsImNvbXBhbnkiOiLmspDoqIDnp5HmioAiLCJleHAiOjE2MjQ3NzE1MTYsImlhdCI6MTYyNDc2MTUxNiwianRpIjoieW9vdGstZGJlZDhhMmYtMjBhYi00Njg1LWI2ZDktNmEzNzI0Y2RhZDZiIn0.DDpaXGOdTYDlUZgOMB59uVlgYTZogDYjLW-pvNuvizE

此时通过JWT数据实现的认证检查处理,要比之前使用SpringSecurity、OAuth2、Shiro都简单许多,一个字符串就可以轻松搞定所有的问题了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/324053.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

当你左右看看没有发现我时,千万千万别往看……

大家好&#xff0c;我是雄雄&#xff0c;欢迎关注微信公众号&#xff1a;雄雄的小课堂。周五&#xff0c;又是一个考试的周五&#xff0c;因为本周都在做项目&#xff0c;还以为同学们没有好好的备考&#xff0c;估计考的不好&#xff0c;没想到今天测试的结果是自从考试以来最…

2018/7/11-纪中某C组题【jzoj1293,jzoj1294,jzoj1295】

前言 今天C组题目有毒&#xff0c;第一题题目玄学&#xff0c;第二题就是不会&#xff0c;第三题考的贼偏。fw(hjw&#xff0c;his博客&#xff1a;https://blog.csdn.net/gx_man_vip)说今天B组题目还行早知道就去做B组了qwq 今日分数 去掉了十分强大的纪中dalao 正题 T1&…

关系型数据库的分片原则

来自Citus的Ozgun Erdogan分享了进行数据库分片的三个原则&#xff0c;并强调应用类型是决定数据库分片最主要的因素。其中B2B类型的应用最容易进行数据库分片。 在数据库很小的时候&#xff0c;可以很容易地通过添加硬件来扩展数据库。但随着数据表数量的不断增长&#xff0c…

分布式架构--基本思想汇总

转载自 分布式架构&#xff0d;&#xff0d;基本思想汇总 在互联网大行其道的今天&#xff0c;各种分布式系统已经司空见惯。搜索引擎、电商网站、微博、微信、O2O平台。。凡是涉及到大规模用户、高并发访问的&#xff0c;无一不是分布式。 关于分布式系统&#xff0c;并没有…

花了100多去KTV不是唱歌,竟然是……

大家好&#xff0c;我是雄雄&#xff0c;欢迎关注微信公众号&#xff1a;雄雄的小课堂。老师&#xff0c;歌词可以实现&#xff0c;不过比较麻烦~也还行你这样 准备几首歌的歌词就行到时候只演示这几首歌如果是其他的哥的话&#xff0c;就显示暂无歌词行&#xff0c;有时间就试…

jzoj4272-序章-弗兰德的秘密【树形dp】

正题 大意 两棵树&#xff0c;它们的相似值是它们留下最多的节点使它们的结构相同。求相似值。 这两颗树就是结构相同的&#xff0c;相似值是8。 解题思路 就是树形dp。可以用f[i][j]f[i][j]表示树1的第ii号节点和它的子树与树2的j" role="presentation" s…

从NIO到Netty开发

转载自 从NIO到Netty开发 1. 从传统BIO到NIO的升级 Client/Server模型是网络编程的基本模型&#xff0c;服务端提供位置信息&#xff0c;客户端通过连接操作向服务端发起连接请求&#xff0c;通过三次握手建立连接&#xff0c;如果连接建立成功&#xff0c;双方就可以通过网…

Orleans入门例子

Orleans是微软开源的分布式actor模型框架.actor模型的原理网络上有很多文章.有许多理论性的文章,深刻地我都不知道怎么应用.在这里我就不赘述了.既然是博客,就说说自己的理解。 对于编程来说&#xff0c;不管是前台还是后台&#xff0c;在现在的计算机环境下&#xff0c;多线程…

JavaScript常用单词整理总结

第一章object对象undefined未定义变量boolean布尔类型sort()对数组排序charAt返回在指定位置的字符toLowerCase()把字符串转换为小写button按钮break结束循环toUpperCase()把字符串转换为大写split(str)将字符串分割为字符串数组length获取数组的长度continue结束当前循环&…

JWT 应用

文章目录JWT工具模块Token认证微服务JWT授权监测网关认证过滤消费端获取JWTJWT工具模块 如果要想在项目之中去使用JWT技术&#xff0c;那么就必须结合到已有的模块之中,最佳的做法就是将JWT的相关的处理 操作做为一个自动的starter组件进行接入 1、【microcloud项目】既然要开…

淘宝秒杀系统设计的几个注意点

转载自 淘宝秒杀系统设计的几个注意点 还记得2013年的小米秒杀吗&#xff1f;三款小米手机各11万台开卖&#xff0c;走的都是大秒系统&#xff0c;3分钟后成为双十一第一家也是最快破亿的旗舰店。经过日志统计&#xff0c;前端系统双11峰值有效请求约60w以上的QPS &#xff0…

.NET Core 2.0 开源Office组件 NPOI

前言 去年 12 月&#xff0c;我移植了大家所熟知 NPOI 到 .NET Core 版本&#xff0c;这里是当时发的博客&#xff0c;当时得到了很多同学的支持&#xff0c;社区反应也很好&#xff0c;在这里非常感谢当时推荐的朋友们。 去年的那个版本是针对于 .NET Core 1.0 的&#xff0…

老师们一直在……

大家好&#xff0c;我是雄雄&#xff0c;欢迎关注微信公众号&#xff1a;雄雄的小课堂。【随便写写】为了了解同学们在公司的情况&#xff0c;和佟老师上了的做了个在线问卷调查&#xff0c;把一些常见的问题设置在调查中&#xff0c;根据调查数据&#xff0c;然后挨个的去解决…

海量数据的分库分表技术演进,最佳实践

转载自 海量数据的分库分表技术演进&#xff0c;最佳实践 每个优秀的程序员和架构师都应该掌握分库分表&#xff0c;移动互联网时代&#xff0c;海量的用户每天产生海量的数量 用户表订单表交易流水表 以支付宝用户为例&#xff0c;8亿&#xff1b;微信用户更是10亿。订单表…

Orleans例子再进一步

步骤 现在我想再添加一个方法,到IGrains项目内,这个方法里面有个延迟3秒,然后返回一个Task<string>.就叫做DelayedMsg吧,如下图所示: 我调用了这个DelayedMsg,同时又调用了SayHello函数,看看效果:注意这个DelayedMsg的调用方法没有await. 虽然我的SayHello的调用时间紧随…

2018/7/12-纪中某C组题【jzoj4272,jzoj4273,jzoj4274】

前言 今天我的想法都是正解&#xff0c;也都写了&#xff0c;结果才160QwQ 今日分数 去掉了十分强大的纪中dalao 正题 T1&#xff1a;jzoj4272-序章-弗兰德的秘密【树形dp】 博客链接&#xff1a;https://blog.csdn.net/mr_wuyongcong/article/details/81021994 T2&#xf…

你,下周可否“报上有名”?

大家好&#xff0c;我是雄雄&#xff0c;欢迎关注微信公众号【雄雄的小课堂】。一周一次周测&#xff0c;一直在延续&#xff0c;一般情况下不会间断。以前我只要一说&#xff0c;同学们&#xff0c;咱们本周周五考试&#xff0c;下面的同学们就沸腾的不行了&#xff0c;有的说…

Redis的3个高级数据结构

转载自 Redis的3个高级数据结构 平常接触最多的是5个入门级数据结构&#xff1a;String&#xff0c;Hash&#xff0c;List&#xff0c;Set&#xff0c;Sorted Set&#xff0c;本文介绍3个高级数据结构&#xff1a;Bitmaps&#xff0c;Hyperloglogs&#xff0c;GEO。 Bitmap…

SpringCloudConfig整合Nacos

SpringCloudConfig 的作用是可以进行配置的更新处理&#xff0c;这个的确是很好&#xff0c;但是原始的SpringCloudNetflix 架构所提供的动态的抓取配置实在是太繁琐了&#xff0c;包括还要使用到SpringCloudBus进行Actuator处理 SpringCloudAlibaba套件之中是基于Nacos 实现的…

使用VS Code开发调试.NET Core 2.0

使用VS Code 从零开始开发调试.NET Core 2.0。无需安装VS 2017 15.3即可开发调试.NET Core 2.0应用。 VS Code 全称是 Visual Studio Code&#xff0c;Visual Studio Code是一个轻量级的跨平台Web集成开发环境&#xff0c;可以运行在 Linux&#xff0c;Mac 和Windows下&#x…