谷粒商城分布式高级篇2
检索服务
导入页面到search目录下面,编辑host文件加上最后一行
nginx修改:
server_name *.gulimall.com;
导入页面到search目录下面,编辑host文件加上最后一行
nginx修改:
server_name *.gulimall.com;
动词,相当于mysql的insert
名词,相当于mysql的databse
在index索引中,可以定义一个或者多个类型
类似于mysql的table,每一种类型的数据放在一起
文档是json格式
拉取镜像image
docker pull mysql
背景:
做一个提供API接口调用的平台,用户可以开通接口调用权限。用户可以使用接口,并且每次调用会进行统计。管理员可以发布接口、下线接口、接入接口,以及可视化接口的调用情况、数据。
@Override
public ResponseResult login(LoginDto loginDto) {
//nomal login need phone and password
if (!StringUtils.isAnyBlank(loginDto.getPhone(), loginDto.getPassword())) {
ApUser apUser = this.lambdaQuery()
.eq(ApUser::getPhone, loginDto.getPhone())
.one();
if (apUser == null) {
return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST, "用户不存在");
}
String salt = apUser.getSalt();
String password = loginDto.getPassword();
String encryptPassword = DigestUtils.md5DigestAsHex((password + salt).getBytes());
if (!encryptPassword.equals(apUser.getPassword())) {
return ResponseResult.errorResult(AppHttpCodeEnum.LOGIN_PASSWORD_ERROR);
}
//正确,返回jwt信息
HashMap<String, Object> map = new HashMap<>();
String token = AppJwtUtil.getToken(apUser.getId().longValue());
map.put("token", token);
apUser.setPassword("");
apUser.setSalt("");
map.put("user", apUser);
return ResponseResult.okResult(map);
}
HashMap<String, Object> map = new HashMap<>();
String token = AppJwtUtil.getToken(0L);
map.put("token", token);
return ResponseResult.okResult(map);
}
tb_blog表 :
create table tb_blog
(
id bigint unsigned auto_increment comment '主键'
primary key,
shop_id bigint not null comment '商户id',
user_id bigint unsigned not null comment '用户id',
title varchar(255) collate utf8mb4_unicode_ci not null comment '标题',
images varchar(2048) not null comment '探店的照片,最多9张,多张以","隔开',
content varchar(2048) collate utf8mb4_unicode_ci not null comment '探店的文字描述',
liked int unsigned default '0' null comment '点赞数量',
comments int unsigned null comment '评论数量',
create_time timestamp default CURRENT_TIMESTAMP not null comment '创建时间',
update_time timestamp default CURRENT_TIMESTAMP not null on update CURRENT_TIMESTAMP comment '更新时间'
)
collate = utf8mb4_general_ci
row_format = COMPACT;
GEO就是Geolocation的简写形式,代表地理坐标。Redis在3.2版本中加入了对GEO的支持,允许存储地理坐标信息,帮助我们根据经纬度来检索数据。常见的命令有:
分布式锁:满足分布式系统或集群模式下多进程可见并且互斥的锁。
分布式锁的核心思想就是让大家都使用同一把锁,只要大家使用的是同一把锁,那么我们就能锁住线程,不让线程进行,让程序串行执行,这就是分布式锁的核心思路
常见的分布式锁有三种
Mysql:mysql本身就带有锁机制,但是由于mysql性能本身一般,所以采用分布式锁的情况下,其实使用mysql作为分布式锁比较少
Redis:redis作为分布式锁是非常常见的一种使用方式,现在企业级开发中基本都使用redis或者zookeeper作为分布式锁,利用setnx这个方法,如果插入key成功,则表示获得到了锁,如果有人插入成功,其他人插入失败则表示无法获得到锁,利用这套逻辑来实现分布式锁
Zookeeper:zookeeper也是企业级开发中较好的一个实现分布式锁的方案。
模拟1000个用户同时发送请求,也就是需要使用jmeter进行压力测试,需要编写代码获取1000个用户的token
生成1000个登录用户并生成token
@SpringBootTest
class HmDianPingApplicationTests {
@Resource
private StringRedisTemplate stringRedisTemplate;
@Resource
private IUserService userService;
@Test
@Transactional
public void insertUser() {
final String filePath = "src/main/resources/user.txt";
final int count = 1000;
BufferedWriter writer;
try {
writer = new BufferedWriter(new FileWriter(filePath));
for (int i = 0; i < count; i++) {
String phone = "13" + RandomUtil.randomNumbers(9);
String token = this.login(phone);
writer.write(token);
writer.newLine();
}
writer.close();
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
System.out.println("生成用户token完毕");
}
}
public String login(String phone) {
User user = new User();
user.setPhone(phone);
user.setNickName(USER_NICK_NAME_PREFIX + RandomUtil.randomString(10));
userService.save(user);
//生成token
String token = UUID.randomUUID().toString();
//将User对象转为HashMap存储
UserDTO userDTO = BeanUtil.copyProperties(user, UserDTO.class);
Map<String, Object> userMap = BeanUtil.beanToMap(userDTO, new HashMap<>(),
CopyOptions.create()
.setIgnoreNullValue(true)
.setFieldValueEditor((fieldName, fieldvalue) -> fieldvalue.toString())
);
//存储
String tokenKey = LOGIN_USER_KEY + token;
stringRedisTemplate.opsForHash().putAll(tokenKey, userMap);
//设置有效期
stringRedisTemplate.expire(tokenKey, LOGIN_USER_TTL, TimeUnit.MINUTES);
return token;
}
}
为什么要使用缓存?
缓存数据存储于代码中,而代码运行在内存中,内存的读写性能远高于磁盘,缓存可以大大降低用户访问并发量带来的服务器读写压力
查询数据库之前先查询缓存,如果缓存数据存在,则直接从缓存中返回,如果缓存数据不存在,再查询数据库,然后将数据存入redis。