学习目标
1.能够封装头条项目统计数据存储类
类函数
incr
get
2.能够知道需要修正redis存储的统计数据原因
mysql 成功
redis 失败
3.能够知道APScheduler的特点
1.动态添加
2.不依赖linux crontab
4.能够使用APScheduler编写定时任务
1.导入模块
2.创建执行器
3.创建调度器
4.定义定时任务函数
5.添加定时任务
6.启动调度器
7.防止退出的代码
5.能够编写头条项目修正统计数据的定时任务
1.从数据库中获取统计数据
# 用户发表文章数量
# sql: select user_id, count(*) from news_article_basic where status=2 group by user_id;
2.更新redis数据
pl = current_app.redis_master.pipeline()
pl.delete(key)
zadd(key, count1, user_id1, count2, user_id2)
pl.zadd(key, *data)
pl.execute()
6.能够知道RPC的作用
远程调用服务器的方法
7.能够对比HTTP与RPC
1.http更加通用
面向产品
2.RPC效率高,但是不通用
微服务
8.能够知道RPC的使用方法
1.定义接口文件 .proto
2.生成源代码文件
3.编辑客户端和服务端逻辑代码
9.能够使用protobuf编写接口定义文件
1.syntax = "proto3";
2.message Request{
repeated string name=1;
}
message Response{}
3.service Hello{
rpc say(Request) returns (Response)
}
10.能够使用grpc提供的工具编译proto文件生成python代码
python -m grpc_tools.protoc -I. --python_out=. --grpc_python_out=. hello.proto
11.能够编写grpc服务器程序
1.导入模块
2.自定义服务类,继承生成服务类 HelloServicer
重写服务类方法
3.创建rpc 服务器 server
server = grpc.server()
4.把自定义服务类添加到server中
5.绑定ip和端口号
6.启动
7.防止退出
12.能够编写grpc客户端程序
1.导入模块
2.创建通道
3.创建存根 stub, 需要使用到上面创建的通道
4.构建请求参数对象
5.使用stub调用rpc服务类方法
6.处理结果
13.能够实现头条项目首页推荐接口
1.统计数据使用redis进行持久存储
原来统计数据的设计:在user_basic表中,添加字段进行统计。
存在的问题:
1.在高并发的情况下,在统一时刻触发统计的数量是巨大的。
2.为了数据的准确性,又必须加锁。所以对数据库的性能影响是巨大的。
在redis中,每个命令都是原子性的,既能提供高性能的访问,也能保证数据修改的完整性。
黑马头条项目:
统计数据最终的选择方案是使用redis进行缓存。
2.旧统计发布文章数量方案
-
文件:d01_old_statistics.py
-
关键代码:
-
发布文章:
@app.route('/issue_article/<int:user_id>') def issue_article(user_id): # 发布文章 # 为了测试方便,从数据库查一条文章数据 article_obj = Article.query.first() content_obj = ArticleContent.query.get(article_obj.id) # user_id发布文章 user_article = Article( user_id=user_id, channel_id=article_obj.channel_id, title=article_obj.title, cover=article_obj.cover, is_advertising=article_obj.is_advertising, ctime=article_obj.ctime, status=article_obj.status, reviewer_id=article_obj.reviewer_id, review_time=article_obj.review_time, delete_time=article_obj.delete_time, comment_count=article_obj.comment_count, allow_comment=article_obj.allow_comment, reject_reason=article_obj.reject_reason, utime=article_obj.utime ) db.session.add(user_article) # 把sql发送到mysql中执行,并没有commit. db.session.flush() user_content = ArticleContent( id=user_article.id, content=content_obj.content ) db.session.add(user_content) # db.session.commit() user = User.query.get(user_id) user.article_count = user.article_count + 1 db.session.add(user) db.session.commit() # 视图中默认会加上db.session.rollback() return '发布成功'
-
获取文章数量
@app.route('/article_count/<int:user_id>') def article_count(user_id): #获取用户发布文章数量 user = User.query.get(user_id) return str(user.article_count)
3.新统计发布文章数量方案
-
用户发布文章redis数据库设计思路分析:
-
所有用户发布文章数量全部放在一个key中.
-
key: count:user:arts
-
value: 值选择zset类型
member: 选择user_id score: 选择用户发布文章的数量
-
文件:d02_new_statistics.py
-
获取用户发布文章数量
# redis命令: zscore key user_id count = app.redis_slave.zscore('count:user:arts', user_id)
-
发布文章,用户发布文章数量加1
# redis命令: zincrby key increment user_id app.redis_master.zincrby('count:user:arts', user_id, 1)
-
错误的操作:
count = app.redis_slave.zscore('count:user:arts', user_id) # 此时有可能出现并发 count += 1 app.redis_slave.zadd('count:user:arts', count, user_id)
-
备注
执行zincrby命令,是原子性的,不会出现并发改的情况,只有要么成功要么失败。
4.用户发布文章数量统计存储类封装
-
封装成工具类
-
文件: d03_user_article_count_tool.py
class UserArticlesCountStorage(object): """ 用户文章数量 """ key = 'count:user:arts' @classmethod def incr(cls, user_id, increment=1): current_app.redis_master.zincrby(cls.key, user_id, increment) @classmethod def get(cls,