好好学习,天天向上

本文已收录至我的Github仓库DayDayUP:github.com/RobodLee/DayDayUP,欢迎Star,更多文章请前往:目录导航

首页广告介绍

流程

在商城的首页,我们会看到很多广告,而很多时候这些广告内容都是固定的,所以每次访问MySQL获取广告内容效率是非常低的,比较好的做法就是用Redis和OpenResty做多级缓存。如果缓存中有数据就访问缓存,没有的话再去MySQL中获取,可以大大提高性能。

表结构

广告的数据是存放在changgou-content数据库中(我的这份资料里面没有这个数据库,我就自己创建了一个)。里面有两张表,一张是tb_content_catrgory(广告分类表),根据页面的不同位置,广告有不同的分类,比如首页轮播,猜你喜欢等;另一张是tb_content(广告表),这张表里存放了广告的数据。

CREATE TABLE `tb_content_category` (
`id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '类目ID',
`name` VARCHAR(50) DEFAULT NULL COMMENT '分类名称',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8 COMMENT='内容分类';
INSERT INTO `tb_content_category` VALUES (1, '首页轮播广告');
INSERT INTO `tb_content_category` VALUES (2, '今日推荐A');
INSERT INTO `tb_content_category` VALUES (3, '活动专区');
INSERT INTO `tb_content_category` VALUES (4, '猜你喜欢'); CREATE TABLE `tb_content` (
`id` BIGINT(20) NOT NULL AUTO_INCREMENT,
`category_id` BIGINT(20) NOT NULL COMMENT '内容类目ID',
`title` VARCHAR(200) DEFAULT NULL COMMENT '内容标题',
`url` VARCHAR(500) DEFAULT NULL COMMENT '链接',
`pic` VARCHAR(300) DEFAULT NULL COMMENT '图片绝对路径',
`status` VARCHAR(1) DEFAULT NULL COMMENT '状态,0无效,1有效',
`sort_order` INT(11) DEFAULT NULL COMMENT '排序',
PRIMARY KEY (`id`),
KEY `category_id` (`category_id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;
INSERT INTO `tb_content` VALUES (1, 1, '微信广告', 'https://blog.csdn.net/weixin_43461520', 'https://gitee.com/RobodLee/image_store/raw/master/%E5%BE%AE%E4%BF%A1%E5%85%AC%E4%BC%97%E5%8F%B7.png', '1', 1);

Lua

简介

Lua是一种轻量小巧的脚本语言,用标准C语言编写并以源代码形式开放, 其设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能。

安装

cd /usr/local/server								# 切换到想要下载的目录,随意
curl -R -O http://www.lua.org/ftp/lua-5.3.5.tar.gz # 下载Lua5.3.5
tar zxf lua-5.3.5.tar.gz # 解压
cd lua-5.3.5 # 切换到解压后的目录
make linux test # 安装
-------------------------------------------------------------------------------------
[root@localhost lua-5.3.5]# lua # 输入lua,出现下面一行说明安装成功
Lua 5.1.4 Copyright (C) 1994-2008 Lua.org, PUC-Rio

编程方式

Lua有交互式编程和脚本式编程两种方式。

  • 交互式编程

交互式编程是输入lua命令后进入到lua控制台,然后输入lua命令来执行。

[root@localhost lua-5.3.5]# lua
Lua 5.1.4 Copyright (C) 1994-2008 Lua.org, PUC-Rio
> print("Hello World!")
Hello World!
>
  • 脚本式编程

脚本式编程就是创建一个.lua文件,然后输入命令“lua filename.lua”来执行。

基本语法

参考菜鸟教程Lua

OpenResty

简介

OpenResty 是一个强大的 Web 应用服务器,Web 开发人员可以使用 Lua 脚本语言调动 Nginx 支持的各种 C 以及 Lua 模块,更主要的是在性能方面,OpenResty可以快速构造出足以胜任 10K 以上并发连接响应的超高性能 Web 应用系统。就是封装了Nginx,并且集成了Lua脚本,开发人员只需要简单地使用提供的模块就可以实现相关的逻辑,而不再像之前,还需要在nginx中自己编写lua的脚本,再进行调用了。

安装

 yum install yum-utils				# 安装yum-utils,为了使用下面一行的命令
# 添加openresty的仓库,不配置这一行安装不了
yum-config-manager --add-repo https://openresty.org/package/centos/openresty.repo
yum install openresty # 安装openresty,界面会有提示,一路按y就可以了

安装完成之后,不要忘了启动

service openresty start

启动完成之后,用浏览器访问安装了openresty的虚拟机,如果出现了欢迎界面就说明安装成功了。

配置

虽然现在已经可以访问到OpenResty了,但是为了能够直接加载到root目录下的lua脚本,还需要配置一下。

cd /usr/local/openresty/nginx/conf			# 切换到openresty安装目录下的nginx目录中的conf目录中
vi nginx.conf #编辑nginx的配置文件

广告缓存的载入与读取

本节的任务就是:Nginx拦截http://192.168.31.200/read_content?id=1,执行Lua脚本,先从Nginx缓存中加载,没有的话就从Redis中加载,再没有的话就从MySQL中加载,然后MySQL——>Redis——>Nginx——>浏览器。

定义Nginx缓存模块

cd /usr/local/openresty/nginx/conf		# nginx的配置目录
vi nginx.conf # 编辑nginx的配置文件

在http里面配置Nginx的缓存模块

Lua脚本

然后,准备好lua脚本,在root/lua目录下创建一个read_content.lua文件,填入以下内容:

ngx.header.content_type="application/json;charset=utf8"
local uri_args = ngx.req.get_uri_args(); -- 获取uri中的所有参数
local id = uri_args["id"]; -- 获取名为id的参数
--获取本地缓存
local cache_ngx = ngx.shared.dis_cache; -- 加载Nginx缓存模块,需要先定义
--根据ID 获取本地缓存数据
local contentCache = cache_ngx:get('content_cache_'..id); --[[
Nginx中有缓存就输出缓存,没有的话就从Redis中加载
--]]
if contentCache == "" or contentCache == nil then
local redis = require("resty.redis"); -- 依赖Redis模块
local red = redis:new() -- 创建Redis对象
red:set_timeout(2000) -- 超时
red:connect("192.168.31.200", 6379) -- 连接Redis
local rescontent=red:get("content_"..id); -- 从Redis中读数据
-- Redis中没有就从MySQL中加载
if ngx.null == rescontent then
local cjson = require("cjson"); -- 依赖json模块
local mysql = require("resty.mysql"); -- 依赖mysql模块
local db = mysql:new(); -- 创建mysql对象
db:set_timeout(2000) -- 设置过期时间
-- mysql的参数信息
local props = {
host = "192.168.31.200",
port = 3306,
database = "changgou_content",
user = "root",
password = "root"
}
local res = db:connect(props); -- 连接mysql
local select_sql = "select url,pic from tb_content where status ='1' and category_id="..id.." order by sort_order";
res = db:query(select_sql); --执行sql
local responsejson = cjson.encode(res); -- 将mysql返回的数据转换成json
red:set("content_"..id,responsejson); -- 存到Redis中
ngx.say(responsejson); -- 输出
db:close() -- 关闭mysql连接
else
cache_ngx:set('content_cache_'..id, rescontent, 10*60); -- 把Redis中的数据写到Nginx缓存中,设置过期时间
ngx.say(rescontent) -- 输出
end
red:close() -- 关闭Redis连接
else
ngx.say(contentCache) -- 输出
end

配置Nginx

现在需要配置一下nginx,让它能够执行该脚本。编辑上面提到的nginx.conf文件,在http.server中添加图中内容



上面一行的意思是有read_content的请求就执行该lua文件。重新加载一下文件

 cd /usr/local/openresty/nginx/sbin		# 切换到nginx下的sbin目录中
./nginx -s road # 重新加载文件

最后测试一下:



可以看到,数据正确加载成功了。我在做这个的时候,有一个数据库字段写错了,然后一直不出结果。所以,小伙伴们一定要注意别写错了。

Nginx限流

Nginx限流的方式有两种,一种是控制速率,另一种是控制并发量。

控制速率

控制速率就是限制访问Nginx的数量,如果数量超过限制,就直接拒绝请求,不去处理。



首先我们需要进行一个限流的配置,编辑上面提到的nginx的配置文件,在http里面添加以下内容:

#限流设置
#binary_remote_addr 是一种key,表示基于 remote_addr(客户端IP) 来做限流,binary_ 的目的是压缩内存占用量。
#zone:定义共享内存区来存储访问信息, contentRateLimit:10m 表示一个大小为10M,名字为contentRateLimit的内存区域。1M能存储16000 IP地址的访问信息,10M可以存储16W IP地址访问信息。
#rate 用于设置最大访问速率,rate=10r/s 表示每秒最多处理10个请求。Nginx 实际上以毫秒为粒度来跟踪请求信息,因此 10r/s 实际上是限制:每100毫秒处理一个请求。这意味着,自上一个请求处理完后,若后续100毫秒内又有请求到达,
limit_req_zone $binary_remote_addr zone=contentRateLimit:10m rate=2r/s;



配置完之后我们还需要使用限流配置,在nginx的配置文件中,在http.server.location中使用限流配置

#burst相当于队列,若rate=2r/s同时有4个请求到达,Nginx 会处理第一个请求,剩余3个请求将放入队列,然后每隔500ms从队列中获取一个请求进行处理。若请求数大于4,将拒绝处理多余的请求,直接返回503
#nodelay,配合burst使用,并发处理不延迟,不按(1s/rate)秒/个的速率处理,等到完成之后,按照正常的速率处理
limit_req zone=contentRateLimit burst=4 nodelay; #使用限流配置



最后不要忘了重新加载文件

 cd /usr/local/openresty/nginx/sbin		# 切换到nginx下的sbin目录中
./nginx -s road # 重新加载文件

控制并发量

控制并发量就是限制一个ip对服务器的连接数。首先我们需要配置一下,编辑nginx.conf文件,在http下添加如下配置

#根据IP地址来限制,存储内存大小10M,配置名为perip,大小为1m
limit_conn_zone $binary_remote_addr zone=perip:10m;
#根据IP地址来限制,存储内存大小10M,配置名为perserver,大小为1m
limit_conn_zone $server_name zone=perserver:10m;

配置完成之后,我们需要让某一个location使用这个配置,这里,我们让/brand使用这个配置,在nginx.conf中的http.server.location /brand中添加以下内容

limit_conn perip 10;   #设置单个客户端ip与服务器的连接数为10.
limit_conn perserver 100; #限制与服务器的总连接数为100
#表示这个请求给180主机处理,因为程序运行在主机上,不在虚拟机上
proxy_pass http://192.168.31.180:18081



最后,重新加载一下文件即可。

 cd /usr/local/openresty/nginx/sbin		# 切换到nginx下的sbin目录中
./nginx -s road # 重新加载文件

Canal环境搭建

介绍

Canal可以用来监控数据库数据的变化,从而获得新增数据,或者修改的数据。当数据库发生增删改的时候,会产生一个日志文件,Canal通过读取日志文件,知道哪些数据发生了变化。在这里,我们将更新的数据给到Canal微服务中,然后微服务把数据写到Redis里。

开启binlog模式以及创建MySQL用户

Canal是基于mysql的主从模式实现的,模拟了mysql slave的交互协议,把自己伪装成mysql slave,向mysql master发送dump请求,mysql master收到dump请求,开始推送binary log给slave(也就是canal)canal解析binary log对象(原始为byte流)。所以,MySQL必须得开启binlog模式

docker exec -it mysql /bin/bash		# 进入到mysql中
cd /etc/mysql/mysql.conf.d #切换到 mysql.conf.d文件夹中
vi mysqld.cnf # 编辑 mysqld.cnf文件



因为Canal需要访问数据库,所以我们给它安排个账号,用root账户不太安全。打开Navicat或者直接命令行,运行

-- 用户名是canal,%表示能在任意机器上登录,密码是123456
-- SELECT查询权限,REPLICATION SLAVE, REPLICATION CLIENT主从复制权限,
-- SUPER ON *.* TO 'canal'@'%':用户canal拥有任意数据库,任意表的这些权限
-- FLUSH PRIVILEGES:刷新权限
create user canal@'%' IDENTIFIED by '123456';
GRANT SELECT, REPLICATION SLAVE, REPLICATION CLIENT,SUPER ON *.* TO 'canal'@'%';
FLUSH PRIVILEGES;

最后重启一下MySQL容器

docker restart mysql

安装和配置Canal容器

  • 下载镜像
docker pull docker.io/canal/canal-server
  • 安装Canal
docker run -p 11111:11111 --name canal -d docker.io/canal/canal-server  #11111:11111:端口映射

安装完成之后还需要进行配置,

docker exec -it canal /bin/bash		# 进入到canal容器中
cd canal-server/conf # 切换到配置文件所在的目录



进入到canal.properties中看看,里面配置了Canal的id,端口等信息



再来看看instance.properties,这个文件配置了数据库相关的信息



配置完成后,设置开机启动,并记得重启canal。

docker update --restart=always canal
docker restart canal

Canal微服务搭建

首先,在changgou-service下创建一个Module叫changgou-service-canal作为我们的微服务工程。创建完成后就可以添加所需的依赖了,但是我们添加的依赖包Maven仓库里面没有,需要手动导入,视频中文件我在资料里面没找到,然后我在网上找了一个,小伙伴们如果需要的话可以点击下载。下载解压后,打开里面的starter-canal目录,在这个目录下打开控制台,使用mvn install命令进行安装,过程可能有点慢,耐心等待即可。



安装完成后就可以导入这个依赖了。

<dependencies>
<!--canal依赖-->
<dependency>
<groupId>com.xpand</groupId>
<artifactId>starter-canal</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>

微服务怎么能少了启动类和配置文件

@SpringBootApplication(exclude={DataSourceAutoConfiguration.class})
@EnableEurekaClient
@EnableCanalClient
public class CanalApplication { public static void main(String[] args) {
SpringApplication.run(CanalApplication.class,args);
}
}
server:
port: 18083
spring:
application:
name: canal
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:7001/eureka
instance:
prefer-ip-address: true
feign:
hystrix:
enabled: true
#hystrix 配置
hystrix:
command:
default:
execution:
timeout:
#如果enabled设置为false,则请求超时交给ribbon控制
enabled: true
isolation:
strategy: SEMAPHORE
#canal配置
canal:
client:
instances:
example:
host: 192.168.31.200
port: 11111

启动一下,看看有没有问题



哎呀,出问题了。这个问题搞得我一夜都没睡好觉,非常难受,折腾了很长时间。最后把Canal卸了重装,终于搞定了。虚拟机里面有Canal,我就没装直接改改配置就用了,可能是之前哪个地方的配置有问题吧,所以还是得自己装一遍。

再启动一次



终于好了!Canal微服务搭建成功!

广告同步

搭建微服务



如上图,每次执行广告操作的时候,MySQL会记录操作日志,然后将操作日志发送给canal,canal将操作记录发送给canal微服务,canal微服务根据修改的分类ID调用content微服务查询分类对应的所有广告,canal微服务再将所有广告存入到Redis缓存。

首先,我们需要搭建一个广告微服务,在changgou-service-api中创建一个Module叫changgou-service-content-api作为API工程,然后在com.robod.content.pojo包中准备添加两个JavaBean:Content.java和ContentCategory.java



然后在changgou-service下创建一个changgou-service-content工程作为广告微服务。添加所需的依赖:

<dependencies>
<dependency>
<groupId>com.changgou</groupId>
<artifactId>changgou-common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.changgou</groupId>
<artifactId>changgou-service-content-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>

最后,添加配置文件和启动类

server:
port: 18084
spring:
application:
name: content
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://192.168.31.200:3306/changgou_content?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
username: root
password: root
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:7001/eureka
instance:
prefer-ip-address: true
feign:
hystrix:
enabled: true
mybatis:
configuration:
map-underscore-to-camel-case: true #开启驼峰功能 #hystrix 配置
hystrix:
command:
default:
execution:
timeout:
#如果enabled设置为false,则请求超时交给ribbon控制
enabled: true
isolation:
strategy: SEMAPHORE logging:
level:
com: debug # 不加这个MyBatis Log插件不打印sql语句
@SpringBootApplication
@EnableEurekaClient
@MapperScan(basePackages = {"com.robod.mapper"})
public class ContentApplication { public static void main(String[] args) {
SpringApplication.run(ContentApplication.class);
}
}

启动一下

广告查询实现

这一步需要完成的功能就是根据广告的分类id去查询出对应的广告集合,所以,我们添加一个叫findByCategoryId的方法去实现这个功能,在各层中实现一下。

/**
* 根据分类的ID 获取该分类下的所有的广告的列表
* Controller层 ContentController.java
*/
@GetMapping(value = "/list/category/{id}")
public Result<List<Content>> findByCategoryId(@PathVariable long id){
List<Content> contents = contentService.findByCategoryId(id);
return new Result<>(true,StatusCode.OK,"成功查询出所有的广告",contents);
}
-----------------------------------------------------------------------------
//Service层 ContentServiceImpl.java
@Override
public List<Content> findByCategoryId(long id) {
return contentMapper.findByCategoryId(id);
}
-------------------------------------------------------------------------------
// Dao层 ContentMapper.java
@Select("select * from tb_content where category_id = #{id} and status = 1")
List<Content> findByCategoryId(long id);

因为我们需要在Canal微服务中调用广告微服务中的方法,所以在changgou-service-content-api工程中添加feign

@FeignClient(name="content")	//指定微服务的名字
@RequestMapping(value = "/content")
public interface ContentFeign { /**
* 根据分类ID查询所有广告
* @param id
* @return
*/
@GetMapping(value = "/list/category/{id}")
Result<List<Content>> findByCategoryId(@PathVariable long id);
}

广告同步实现

既然是将数据同步到Redis,那么就需要配置一下Redis,在Canal微服务中修改application.yml配置文件,添加redis配置



接下来,在启动类中开启feign,修改CanalApplication,添加@EnableFeignClients注解

//要先在changgou-servie-canal中添加changgou-service-content-api的依赖
@EnableFeignClients(basePackages = {"com.robod.content.feign"})

最后,在com.robod.canal包中添加一个监听类CanalDataEventListener去监听数据变化并将变化的数据写到Redis中。

/**
* @author Robod
* @date 2020/7/14 10:47
* 实现MySQL数据监听
*/
@CanalEventListener
public class CanalDataEventListener {
private final ContentFeign contentFeign;
private final StringRedisTemplate stringRedisTemplate; public CanalDataEventListener(ContentFeign contentFeign, StringRedisTemplate stringRedisTemplate) {
this.contentFeign = contentFeign;
this.stringRedisTemplate = stringRedisTemplate;
} /**
* 监听数据变化,将数据写到Redis中
* @param eventType
* @param rowData
*/
@ListenPoint(
destination = "example",
schema = "changgou_content",
table = {"tb_content","tb_content_category"},
eventType = {
CanalEntry.EventType.INSERT,
CanalEntry.EventType.UPDATE,
CanalEntry.EventType.DELETE}
)
public void onEventListener(CanalEntry.EventType eventType, CanalEntry.RowData rowData) {
String categoryId = getColumnValue(eventType,rowData);
List<Content> contents = contentFeign.findByCategoryId(Long.parseLong(categoryId)).getData();
stringRedisTemplate.boundValueOps("content_"+categoryId).set(JSON.toJSONString(contents));
} private String getColumnValue(CanalEntry.EventType eventType, CanalEntry.RowData rowData) {
if (eventType == CanalEntry.EventType.UPDATE || eventType == CanalEntry.EventType.INSERT) {
for (CanalEntry.Column column : rowData.getAfterColumnsList()) {
if ("category_id".equalsIgnoreCase(column.getName())) {
return column.getValue();
}
}
}
if (eventType == CanalEntry.EventType.DELETE) {
for (CanalEntry.Column column : rowData.getBeforeColumnsList()) {
if ("category_id".equalsIgnoreCase(column.getName())) {
return column.getValue();
}
}
}
return "";
} }

来测试一下



OK!数据库中和Redis中数据同步了。

总结

这篇文章介绍了Lua、OpenResty以及Canal,并实现了广告的缓存与同步功能。操作都不难,但是坑非常多,本来以为很快就能完成,结果花了好几天才完成,所以做这个一定要细心。

如果我的文章对你有些帮助,不要忘了点赞,收藏,转发,关注。要是有什么好的意见欢迎在下方留言。让我们下期再见!

畅购商城(四):Lua、OpenResty、Canal实现广告缓存与同步的更多相关文章

  1. 畅购商城(五):Elasticsearch实现商品搜索

    好好学习,天天向上 本文已收录至我的Github仓库DayDayUP:github.com/RobodLee/DayDayUP,欢迎Star,更多文章请前往:目录导航 畅购商城(一):环境搭建 畅购商 ...

  2. 畅购商城(二):分布式文件系统FastDFS

    好好学习,天天向上 本文已收录至我的Github仓库DayDayUP:github.com/RobodLee/DayDayUP,欢迎Star,更多文章请前往:目录导航 畅购商城(一):环境搭建 畅购商 ...

  3. 畅购商城(八):微服务网关和JWT令牌

    好好学习,天天向上 本文已收录至我的Github仓库DayDayUP:github.com/RobodLee/DayDayUP,欢迎Star,更多文章请前往:目录导航 畅购商城(一):环境搭建 畅购商 ...

  4. 畅购商城(九):Spring Security Oauth2

    好好学习,天天向上 本文已收录至我的Github仓库DayDayUP:github.com/RobodLee/DayDayUP,欢迎Star,更多文章请前往:目录导航 畅购商城(一):环境搭建 畅购商 ...

  5. 畅购商城(七):Thymeleaf实现静态页

    好好学习,天天向上 本文已收录至我的Github仓库DayDayUP:github.com/RobodLee/DayDayUP,欢迎Star,更多文章请前往:目录导航 Thymeleaf简单入门 什么 ...

  6. 用Nginx+Lua(OpenResty)开发高性能Web应用

    在互联网公司,Nginx可以说是标配组件,但是主要场景还是负载均衡.反向代理.代理缓存.限流等场景:而把Nginx作为一个Web容器使用的还不是那么广泛.Nginx的高性能是大家公认的,而Nginx开 ...

  7. Nginx+Lua(OpenResty)开发高性能Web应用

    使用Nginx+Lua(OpenResty)开发高性能Web应用 博客分类: 跟我学Nginx+Lua开发 架构 ngx_luaopenresty 在互联网公司,Nginx可以说是标配组件,但是主要场 ...

  8. 使用Nginx+Lua(OpenResty)开发高性能Web应用

    摘自(http://jinnianshilongnian.iteye.com/blog/2280928) 在互联网公司,Nginx可以说是标配组件,但是主要场景还是负载均衡.反向代理.代理缓存.限流等 ...

  9. 安装Nginx+Lua+OpenResty开发环境配置全过程实例

    安装Nginx+Lua+OpenResty开发环境配置全过程实例 OpenResty由Nginx核心加很多第三方模块组成,默认集成了Lua开发环境,使得Nginx可以作为一个Web Server使用. ...

随机推荐

  1. HDU 3686 Traffic Real Time Query System (图论)

    HDU 3686 Traffic Real Time Query System 题目大意 给一个N个点M条边的无向图,然后有Q个询问X,Y,问第X边到第Y边必需要经过的点有多少个. solution ...

  2. HDU 5963 朋友 (找规律,思维)

    HDU 5963 朋友 题目大意 B君在围观一群男生和一群女生玩游戏,具体来说游戏是这样的: 给出一棵n个节点的树,这棵树的每条边有一个权值,这个权值只可能是0或1. 在一局游戏开始时,会确定一个节点 ...

  3. mac篇---mac安装jupyter

    1.Jupyter搭建 pip install --user jupyter 如果是在python3中,则用如下命令: pip3 install --user jupyter 如下图所示: 2. Ju ...

  4. When Lambo with Howdoo

    原文链接:https://howdoo.io/when-lambo/ 为了庆祝即将推出的革命性新社交媒体平台Howdoo以及我们令人惊喜的合作伙伴关系和社区,我们正在发起一项竞赛,以最终回答“When ...

  5. 赋值,逻辑,运算符, 控制流程之if 判断

    赋值运算 (1). 增量运算 age += 1 # age = age + 1 print(age) age -= 10 # age = age - 10 (2).交叉赋值 x = 111 y = 2 ...

  6. 上海python14期第一次周考

    上海python14期第一次周考 1 介绍 满分50分 考试范围: Python语法 数据类型 流程控制 考试时间: 周五下午3.00点-晚6:00 2 基础题(38分) 什么是编程语言?什么是语言? ...

  7. ## Java基础(二):变量类型

    Java 变量类型 一.局部变量:类的方法中的变量 局部变量声明在方法.构造方法或者语句块中: 局部变量在方法.构造方语句块中被执行的时候创建,当他们执行完成后,变量被销毁 访问修饰符不能用于局部变量 ...

  8. Scala 基础(十五):Scala 模式匹配(三)

    1 变量声明中的模式 match中每一个case都可以单独提取出来,意思是一样的. 应用案例 val (x, y) = (1, 2) val (q, r) = BigInt(10) /% 3 //说明 ...

  9. java 面向对象(八):面向对象的特征一:封装性

    面向对象的特征一:封装与隐藏1.为什么要引入封装性?我们程序设计追求“高内聚,低耦合”.高内聚 :类的内部数据操作细节自己完成,不允许外部干涉:低耦合 :仅对外暴露少量的方法用于使用. 隐藏对象内部的 ...

  10. 数据可视化之分析篇(四)PowerBI分析模型:产品关联度分析

    https://zhuanlan.zhihu.com/p/64510355 逛超市的时候,面对货架上琳琅满目的商品,你会觉得这些商品的摆放,或者不同品类的货架分布是随机排列的吗,当然不是. 应该都听说 ...