黑马头条项目经验&BUG

这篇具有很好参考价值的文章主要介绍了黑马头条项目经验&BUG。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

项目经验

bootstrap.yaml

同样是配置文件,但与application.yml有所不同

  1. bootstrap.yml的加载比application.yml早
  2. bootstrap.yml作用范围更广,可以被多个应用程序共享(可以在每个服务的application.yml中配置spring: cloud: config: (name)url,从而配置bootstrap.yml的位置)
  3. bootstrap.yml具有更高的优先级,可以覆盖application.yml中相同的配置信息

密码加盐加密

加盐:是指给每个用户随机生成一个字符串,需要和password进行结合
加密:md5由于每次对相同的明文生成的密文相同,因此对加盐后的密码再进行md5加密DigestUtils.md5DigestAsHex(password.getBytes()

JWT

JWT是token的一种具体实现方式

token

token用于身份验证阶段,由服务端签发给客户端;客户端再次登录时可将token加入请求头Header中,服务器可验证token,验证成功即可向客户端返回请求的数据。

可使用拦截器Interceptor或过滤器Filter拦截请求,然后使用HttpServletRequest.addHeader(String name,String value)将token加入请求头
示例:

request.addHeader("Authorization", "Bearer " + token);
Interceptor
//创建拦截器对象
public class TokenInterceptor extends HandlerInterceptorAdapter {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String token = "your_token_value"; // 替换为实际的Token值
        request.addHeader("Authorization", "Bearer " + token);
        return true;
    }
}

//在配置类中注册拦截器
@Configuration
public class AppConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new TokenInterceptor()).addPathPatterns("/**");
    }
}
Filter
//创建过滤器对象
public class TokenFilter implements Filter {
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        String token = "your_token_value"; // 替换为实际的Token值
        request.addHeader("Authorization", "Bearer " + token);
        filterChain.doFilter(servletRequest, servletResponse);
    }
}

//在配置类中注册过滤器
@Configuration
public class AppConfig {
    @Bean
    public FilterRegistrationBean<TokenFilter> tokenFilter() {
        FilterRegistrationBean<TokenFilter> registrationBean = new FilterRegistrationBean<>();
        registrationBean.setFilter(new TokenFilter());
        registrationBean.addUrlPatterns("/**");
        return registrationBean;
    }
}

服务端则可使用request.getHeader("name")获取请求头中的对应字段信息进而解析token

上述Filter基于FilterRegistrationBean实现,另一种方法是直接在Filter的实现类上加上@WebFilter(urlPatterns = "/**")的注解,省去了编写配置类的操作

  • Interceptor是Spring框架提供的一种机制,它可以直接与Spring的上下文和生命周期集成。
  • Filter是Java Servlet规范提供的一种机制,它是在Servlet容器级别进行请求和响应的过滤。

jwt结构

JWT由3部分组成:标头(Header)、有效载荷(Payload)和签名(Signature)。在传输的时候,会将JWT的3部分分别进行Base64编码(可理解为一种加密方式)后用.进行连接形成最终传输的字符串

  1. Header
    JWT由3部分组成:标头(Header)、有效载荷(Payload)和签名(Signature)。
    在传输的时候,会将JWT的3部分分别进行Base64编码后 用.进行连接形成最终传输的字符串
{
  "alg": "HS256",
  "typ": "JWT"
}
  1. Payload
    有效载荷部分,是JWT的主体内容部分,也是一个JSON对象,包含需要传递的数据。 JWT指定七个默认字段供选择
iss:发行人
exp:到期时间
sub:主题
aud:用户
nbf:在此之前不可用
iat:发布时间
jti:JWT ID用于标识该JWT

这些预定义的字段并不要求强制使用。
除以上默认字段外,还可以自定义私有字段,一般会把包含用户信息的数据放到payload中

{
  "sub": "1234567890",
  "name": "Helen",
  "admin": true
}

默认情况下JWT是未加密的,因为只是采用base64算法,拿到JWT字符串后可以转换回原本的JSON数据,任何人都可以解读其内容,因此不要构建隐私信息字段,比如用户的密码一定不能保存到JWT中,以防止信息泄露。JWT只是适合在网络中传输一些非敏感的信息

  1. Signature
    签名哈希部分是对上面两部分数据签名,需要使用base64编码后的header和payload数据,通过指定的算法生成哈希,以确保数据不会被篡改。首先,需要指定一个密钥(secret)。该密码仅仅为保存在服务器中,并且不能向用户公开。然后,使用header中指定的签名算法(默认情况下为HMAC SHA256)根据以下公式生成签名
HMAC SHA256(base64UrlEncode(header)+"."+base64UrlEncode(payload),secret)

在计算出签名哈希后,JWT头,有效载荷和签名哈希的三个部分组合成一个字符串,每个部分用.分隔,就构成整个JWT对象

jwt的使用

java-jwt

  1. 引入依赖
<dependency>
    <groupId>com.auth0</groupId>
    <artifactId>java-jwt</artifactId>
    <version>3.10.3</version>
</dependency>
  1. 生成jwt的token
public class JWTTest {
    @Test
    public void testGenerateToken(){
        // 指定token过期时间为10秒
        Calendar calendar = Calendar.getInstance();
        calendar.add(Calendar.SECOND, 10);

        String token = JWT.create()
                .withHeader(new HashMap<>())  // Header
                .withClaim("userId", 21)  // Payload
                .withClaim("userName", "baobao")
                .withExpiresAt(calendar.getTime())  // 过期时间
                .sign(Algorithm.HMAC256("!34ADAS"));  // 签名用的secret

        System.out.println(token);
    }
}
  1. 解析JWT
@Test
public void testResolveToken(){
    // 创建解析对象,使用的算法和secret要与创建token时保持一致
    JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256("!34ADAS")).build();
    // 解析指定的token
    DecodedJWT decodedJWT = jwtVerifier.verify("eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyTmFtZSI6ImJhb2JhbyIsImV4cCI6MTU5OTkyMjUyOCwidXNlcklkIjoyMX0.YhA3kh9KZOAb7om1C7o3vBhYp0f61mhQWWOoCrrhqvo");
    // 获取解析后的token中的payload信息
    Claim userId = decodedJWT.getClaim("userId");
    Claim userName = decodedJWT.getClaim("userName");
    System.out.println(userId.asInt());
    System.out.println(userName.asString());
    // 输出超时时间
    System.out.println(decodedJWT.getExpiresAt());
}

网关过滤器

创建一个类,实现OrderedGlobalFilter接口,并重写需要的方法

  • getOrder():设置优先级,值越小,优先级越高
  • Mono<void> filter(ServerWebExchange exchange,GatewayFilterChain chain)

示例代码:

public class AuthFilter implements Ordered,GlobalFilter{
	@Override
	public int getOrder(){
		return 0;
	}

	@Override
	public Mono<void> filter(ServerWebExchange exchange,GatewayFilterChain chain){
		//1.获取request和response对象
		ServerHttpRequest request = exchange.getRequest();
		ServerHttpResponse response = exchange.getResponse();
		//2.判断是否是登录
		if(request.getURI().getPath().contains("/login")){
			//放行
			return chain.filter(exchange);
		}
		//3.获取token
		//这里是需要在登录的时候将token加入请求头中
		String token = request.getHeaders().getFirst("token");
		// 4.判断token是否存在
		if(StringUtils.isBlank(token){
			response.setStatusCode(HttpStatus.UNAUTHORIZED);	//该状态码为401
			return response.setComplete();
		}
		// 5.判断token是否有效
		// TODO(这里需要根据token获取Claim,再根据Claim中的字段判断token是否有效)
		// 如果token无效,就类似步骤4的处理;如果有效就接步骤6放行
		// 6.放行
		return chain.filter(exchange);
	}
}

FreeMarker

模板引擎
黑马头条项目经验&BUG,项目经验和BUG,bug
FreeMarker是一款模板引擎:即一种基于模板和要改变的数据,并用来生成输出文本(HTML网页,电子邮件,配置文件,源代码等)的通用工具。它不是面向最终用户的,而是一个Java类库,是一款程序员可以嵌入他们所开发产品的组件。

FreeMarker的优势:性能好,强大的模板语言、轻量

使用方法

  1. 导入依赖
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
  1. 编写配置文件application.yaml
server:
	port: 8881 #服务端口
spring:
	application:
		name : test-freemarker #指定服务名
	freemarker:
		cache: false	#关闭模板缓存,方便测试
		settings:
			template_update_delay: 0 #检查模板更新延迟时间,设置为o表示立即检查,如果时间大于o会有缓存不方便进行模板测试
		suffix : .ftl #指定Freemarker模板文件的后缀名(FreeMarker的默认配置是.ftlh);html等文件格式也支持
		template-loader-path: classpath:/templates # 这也是FreeMarker的默认路径

  1. resources包下创建templates目录,作为freemarker的默认模板存放目录
  2. templates下创建01-basic.ftl模板
<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8">
	<title>Hello world !</title>
</head>
<body>
<b>普通文本String展示:</b><br><br>
Hello ${name} <br>

<hr>

<b>对象Student中的数据展示:</b><br/>
姓名: ${stu.name}<br/>
年龄: ${stu.age}
<hr>

</body></html>

其中的${}内容为插值表达式,后续需要替换为需要的值

  1. 写方法对该模板处理

freemarker作为springmvc一种视图格式,默认情况下SpringMVC支持freemarker视图格式。

代码如下:

@GetMapping("basic")
public String test(Model model) {
	//1.纯文本形式的参数
	model.addAttribute("name", "freemarker");
	//2.实体类相关的参数
	Student student = new Student();student.setName("小明");
	student.setAge(18);
	model.addAttribute("stu", student);
	return "01-basic";
}

因为在导入FreeMarker的依赖后,其在spring.factories中完成了对FreeMarker的自动配置
而FreeMaker基于SpringMVC,因此Controller能够自动获取Model并通过返回值构建对应的视图

  1. 生成静态文件
    黑马头条项目经验&BUG,项目经验和BUG,bug
public class FreeMarker{
	//自动注入的是freemarker中的Configuration
	@Autowired
	private Configuration configuration

	public void test(){
		Template template = configuration.getTemplate("xxx.ftl")
		/**
	 	 * 合成方法
		 * 两个参数
		 * 第一个参数:模型数据
		 * 第二个参数:输出流
		 */
		template.process(getData() ,new Filewriter("d:/xxx.html"));
	}
	private Map getData(){
		Map map = new HashMap();
		//这里将步骤5中的Model改为Map
		//即用Map存数据,然后和模板中的内容进行转换
		return map;
	}
}

基本语法

  1. 注释<#-- -->
  2. 插值${}
  3. FTL指令<# >...</#> 示例:<#list>...</#list>
  4. 文本:纯文本(无注释),不处理,直接输出

list集合

<#list ListName as Entity>
	<tr>
		<td>${Entity_index}</td> <#--获取元素下标,第一个元素为0-->
		<td>${Entity.property1}</td>
		<td>${Entity.property2}</td>
	</tr>
</#list>

map集合

  1. 方式1${MapName['KeyName'].property}即 使用model中attribute设置的Map名称获取到map对象,然后使用['KeyName']即在[]中添加键名称来获取Value值,如果Value是类,则可类似地获取属性。
  2. 方式2${MapName.KeyName.property}
  3. 获取所有key<#list MapName?keys as key></#list>,即将所有的key封装为list

if表达式

<#if>
<#else>
</#if>

freemarker中1个等于和2个等于作用相同
尽量用lt和gt代替小于和大于,其他符号相同
在变量后接两个问号,可判断该元素是否为空,比如ListName??
在变量后接叹号再接值,能设定空值情况下的默认值,比如${name!'------'},name为空返回'------'

内建函数

内建函数语法格式:变量+?+函数名称
示例:
${ListName?size}
${today?datetime}
${today?string("yyyy年MM月")}

MinIO

分布式文件系统,容易实现扩容

优点

  1. 性能高,准硬件条件下它能达到55GB/s的读、35GB/s的写速率
  2. 部署自带管理界面
  3. MinIO.Inc运营的开源项目,社区活跃度高
  4. 提供了所有主流开发语言的SDK

使用

  1. 环境配置

①拉取镜像

docker pull minio/minio

②运行容器

docker run -p 9000:9000 --name minio -d --restart=always -e "MINO_ACCESS_KEY=minio" -e "MINTO_SECRET_KEY=minio123" -v /home/data:/data -v /home/config:/root/.minio minio/minio server /data

③访问minio:http://192.168.133.128:9000

  1. 导入依赖
<dependency>
	<groupId>io.minio</groupId>
	<artifactId>minio</artifactId>
	<version>7.1.0</version>
</dependency>
  1. 实现代码
try {
	FileInputStream fileInputStream = new FileInputStream("D:\\xxx.html");
	//1.获取minio的链接信息创建一个minio的客户端
	//其中包含用户名,密码,地址
	MinioClient minioClient = MinioClient.builder().credentials("minio", "minio123").endpoint("http://192.168.133.128:9000").build();
	//2.上传
	//这里其实也就是需要提供3个字符串类型的参数,对应bucketName、objectName、filePath、contentType
	PutObjectArgs putObjectArgs = PutObjectArgs.builder()
		.object("xxx.html")	//文件名称
		.contentType("text/html")	//文件类型
		.bucket("BucketName")	//桶名称 与minio管理界面创建的桶一致即可
		.stream(fileInputStream,fileInputstream.available(), -1).build();
	minioClient.putObject(putObjectArgs);
	//访问路径
	System.out.println("http://192.168.133.128:9000/BucketName/xxx.html");
}catch (Exception e) {
	e.printStackTrace();
}

想从Minio获取文件操作类似,调用MinioClient的getObject()方法即可,参数少一个contentType
获取到的文件存到FileOutputStream中,也是getObject()方法的一个参数

在这里面也可以将其中的链接信息抽成配置文件

minio: 
	accessKey: minio
	secretKey: minio123
	bucket: BucketName
	endpoint: http://192.168.133.128:9000
	readPath: http://192.168.133.128:9000

然后写一个实体类,用@ConfigurationProperties(prefix = "minio")去获取配置信息即可

分布式ID

  1. redis
    生成全局连续递增的数字型主键(但必须保证redis可用)
  2. UUDI
    全局唯一(但由36个字符组成,占用空间较大)
  3. 雪花算法
    全局唯一,数字类型,存储占用小(但机器规模大于1024则无法使用)
    snowflake是Twitter开源的分布式ID生成算法,结果是一个long型的ID。其核心思想是:使用41bit作为毫秒数,10bit作为机器的ID(5个bit是数据中心,5个bit的机器ID),12bit作为毫秒内的流水号(意味着每个节点在每毫秒可以产生4096个 ID),最后还有一个符号位,永远是0
      1. 在实体类的主键ID属性上加上注解@TableId(value = "id", type = IdType.ID_WORKER)
      1. 第二:在application.yml文件中配置数据中心id和机器id
mybatis-plus:
	mapper-locations: classpath*:mapper/*.xml	# 设置别名包扫描路径,通过该属性可以给包中的类注册别名
	type-aliases-package: com.rainbow.model.article.pojos
	global-config:
		datacenter-id: 1
		workerId: 1

异步调用

  1. 在需要异步调用的方法上用@Async注解
  2. 在启动类上用@EnableAysnc注解
    该异步调用底层是通过SpringBoot的AOP技术,通过创建代理对象的方式,拦截用@Async注解过的方法,然后将该方法交给线程池进行异步处理

延迟任务

  1. DelayQueue
    JDK自带的延时获取元素的阻塞队列,内部采用优先队列存储元素,延迟期满时才从队列中获取任务进行处理。但是任务都存在内存中,如果程序挂了,消息便会丢失。
    黑马头条项目经验&BUG,项目经验和BUG,bug
  2. RabbitMQ
  • TTL:消息存活时间
  • 死信队列:Dead Letter Exchange,当消息成为Dead Message后,可以重新发送给另外一个交换机
@Configuration
public class RabbitMQConfig {
    // 定义普通队列
    @Bean
    public Queue delayQueue() {
        return QueueBuilder.durable("delay.queue") // 队列名称
                .withArgument("x-dead-letter-exchange", "dead-letter-exchange") // 设置死信交换器
                .withArgument("x-dead-letter-routing-key", "dead-letter-routing-key") // 设置死信路由键
                .withArgument("x-message-ttl", 5000) // 设置消息的TTL(单位:毫秒)
                .build();
    }

    // 定义死信队列
    @Bean
    public Queue deadLetterQueue() {
        return QueueBuilder.durable("dead-letter.queue")
                .build();
    }

    // 定义死信交换器
    @Bean
    public DirectExchange deadLetterExchange() {
        return new DirectExchange("dead-letter-exchange");
    }

    // 绑定死信队列和死信交换器
    @Bean
    public Binding deadLetterBinding(Queue deadLetterQueue, DirectExchange deadLetterExchange) {
        return BindingBuilder.bind(deadLetterQueue)
                .to(deadLetterExchange)
                .with("dead-letter-routing-key");
    }
}

该配置中,delay.queue的消息TTL到后,将被发送到dead-letter-exchange绑定的dead-letter.queue中,然后只需要监听dead-letter.queue即可
黑马头条项目经验&BUG,项目经验和BUG,bug
3. redis
zset数据类型:利用去重有序的特点去实现延迟任务。
可以按时间顺序将任务进行排序,然后根据当前时间去获取队列中的任务进行处理

黑马头条项目经验&BUG,项目经验和BUG,bug

乐观锁

在对数据修改时并不加锁,而是在最后存入数据库时检查版本号,如果版本号和预期相同则完成修改。

  1. 在数据库中加入version字段
  2. 在实体类中对应的version属性上加上@Version注解
  3. 在启动类中向容器中放入乐观锁的拦截器
@Bean
public MybatisPlusInterceptor optimisticLockerInterceptor(){
	MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
	interceptor.addInnerInterceptor(new optimisticLockerInnerInterceptor());
	return interceptor;
}

分布式锁

redis的sexnx (SET if not exists)命令在指定的key不存在时,为key设置指定的值。
黑马头条项目经验&BUG,项目经验和BUG,bug

	//方式一
	RedisConnection connection = stringRedisTemplate.getConnectionFactory().getConnection();
	Boolean result = connection.set(name.getBytes(),
			   						token-getBytes(),
			   						Expiration.from(expire,TimeUnit.MILLISECONDS),
			   						RediSstringCommands.SetOption.SET_IF_ABSENT);	//NX

	//方式二
	private static final String LOCK_KEY = "mylock";
    private static final long LOCK_EXPIRE_TIME = 30000; // 锁的过期时间,单位:毫秒
    
	@Autowired
    private RedisTemplate<String, String> redisTemplate;

    public boolean acquireLock() {
        // 使用 SETNX 命令尝试获取锁
        Boolean lockAcquired = redisTemplate.opsForValue().setIfAbsent(LOCK_KEY, "locked");
        if (lockAcquired != null && lockAcquired) {
            // 设置锁的过期时间,防止锁在持有者崩溃时一直存在
            redisTemplate.expire(LOCK_KEY, LOCK_EXPIRE_TIME);
            return true; // 获取到了锁
        }
        return false; // 锁已被其他进程持有
    }

    public void releaseLock() {
        // 释放锁,使用 DEL 命令删除锁的键
        redisTemplate.delete(LOCK_KEY);
    }

BUG

Mybatis-plus使用getOne查询时抛出异常

原因是在com.baomidou.mybatisplus.extension.service这个包下的getOne()方法:T getOne(Wrapper<T> queryWrapper, boolean throwEx);其中第二个参数用于决定是否在查询到多个结果时抛出异常,默认情况是true

解决办法:用list()方法去替代,然后根据需要去获取元素,如果无特殊要求获取第一个元素就行

但使用mybatis-plus相关方法时,需要判断返回值是否为null,为null需要额外处理文章来源地址https://www.toymoban.com/news/detail-621503.html

到了这里,关于黑马头条项目经验&BUG的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【经验与Bug】tensorflow草记

    conda activate tf 在anaconda prompt使用,进入名为tf的虚拟环境。 pip install 包名== 可以查看指定包能被找到的所有版本。 pip install 包名 -i https://pypi.org/simple 从官方源下载包。 绘制多图 : Colab挂载谷歌云端硬盘 。 python连接列表 。 同样的,元组也可以使用 “+” 进行连接运算。

    2023年04月23日
    浏览(33)
  • AJAX——黑马头条-数据管理平台项目

    功能: 登录和权限判断 查看文章内容列表(筛选,分页) 编辑文章(数据回显) 删除文章 发布文章(图片上传,富文本编辑器) 技术: 基于Bootstrap搭建网站标签和样式 集成wangEditor插件实现富文本编辑器 使用原生JS完成增删改查等业务 基于axios与黑马头条线上接口交互

    2024年04月27日
    浏览(32)
  • 【黑马头条之项目部署_持续集成Jenkins】

    本笔记内容为黑马头条项目的项目部署_持续集成部分 目录 一、内容介绍 1、什么是持续集成 2、持续集成的好处 3、今日内容 二、软件开发模式 1、软件开发生命周期 2、软件开发瀑布模型 3、软件的敏捷开发 三、Jenkins安装配置 1、Jenkins介绍 2、Jenkins环境搭建 1.Jenkins安装配置

    2024年02月10日
    浏览(45)
  • 2023黑马头条.微服务项目.跟学笔记(三)

    1.自媒体前后端搭建 1.1 后台搭建 ①:资料中找到heima-leadnews-wemedia.zip解压 拷贝到heima-leadnews-service工程下,并指定子模块 执行leadnews-wemedia.sql脚本 添加对应的nacos配置 ②:资料中找到heima-leadnews-wemedia-gateway.zip解压 拷贝到heima-leadnews-gateway工程下,并指定子模块 添加对应的n

    2024年02月13日
    浏览(45)
  • 黑马头条项目学习--Day3: 自媒体文章发布

    自媒体后台搭建 ①:资料中找到heima-leadnews-wemedia.zip解压 拷贝到heima-leadnews-service工程下,并指定子模块 执行leadnews-wemedia.sql脚本 添加对应的nacos配置 ②:资料中找到heima-leadnews-wemedia-gateway.zip解压 拷贝到heima-leadnews-gateway工程下,并指定子模块 添加对应的nacos配置 图片上传的

    2024年02月13日
    浏览(67)
  • 黑马头条 SpringBoot+SpringCloud+ Nacos等企业级微服务架构项目

    各位爷,完整项目gitee如下,求star heima-leadnews-master: 《黑马头条》项目采用的是SpringBoot+springcloud当下最流行的微服务为项目架构,配合spring cloud alibaba nacos作为项目的注册和配置中心。新课程采用快速开发的模式,主要解决真实企业开发的一些应用场景。详情请看博客:htt

    2024年02月08日
    浏览(60)
  • 《黑马头条》SpringBoot+SpringCloud+ Nacos等企业级微服务架构项目

    各位爷,完整项目gitee如下,求star heima-leadnews-master: 《黑马头条》项目采用的是SpringBoot+springcloud当下最流行的微服务为项目架构,配合spring cloud alibaba nacos作为项目的注册和配置中心。新课程采用快速开发的模式,主要解决真实企业开发的一些应用场景。详情请看博客:htt

    2024年02月15日
    浏览(50)
  • 客户线上反馈:从信息搜集到疑难 bug 排查全流程经验分享

    写在前面:本文是我在前端团队的第三次分享,应该很少会有开发者写客户反馈处理流程以及 bug 排查的心得技巧,全文比较长,写了一个多星期大概1W多字(也是我曾经2年工作的总结),如果你有耐心阅读,我相信在未来的问题排查上,一定会对你的思路拓展有些许帮助,

    2024年02月06日
    浏览(50)
  • 微服务—Redis实用篇-黑马头条项目用户签到功能(使用bitmap实现)与UV统计

    1.1、用户签到-BitMap功能演示 我们针对签到功能完全可以通过mysql来完成,比如说以下这张表 用户一次签到,就是一条记录,假如有1000万用户,平均每人每年签到次数为10次,则这张表一年的数据量为 1亿条 每签到一次需要使用(8 + 8 + 1 + 1 + 3 + 1)共22 字节的内存,一个月则

    2024年02月05日
    浏览(40)
  • 微服务—Redis实用篇-黑马头条项目-达人探店功能(使用set与zset实现)

    1.1、达人探店-发布探店笔记 发布探店笔记 探店笔记类似点评网站的评价,往往是图文结合。对应的表有两个: tb_blog:探店笔记表,包含笔记中的标题、文字、图片等 tb_blog_comments:其他用户对探店笔记的评价 具体发布流程 上传接口 注意:同学们在操作时,需要修改Syste

    2024年02月05日
    浏览(47)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包