持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第3天,点击查看活动详情
前言
由于时间关系,这次期末作孽只能做一个非常简单的玩意,由于期末作业可以在阶段作品的基础上进行优化,于是我有个大胆的想法,为什么我不阔以直接把SpringBoot 构建的单体项目拆分为微服务架构,这个也是一个升级嘛,顺便复习一下微服务。
本次微服务拆分的案例是:嘿从零开始基于SpringBoot 打造在线聊天室(4.4W字最长博文)
(PS:主要是为了对付期末作业,如果没事的话,别这样乱拆)
这个是基于vue + springBoot 整合mybatis websocket 做的在线聊天室(这里直接使用Map来代替redis,所以直需要mysql做存储)
由于我们只是对后端进行技术升级,所以,我们前端基本上不用动,甚至连接口都不用换。这充分体现了前后端分离的第一个好处呀。那第二个好处咧?当然是基于vue更加方便去嫖组件呀!
所以页面还是这个样子: 这个样子 and this:
项目创建
废话不多说,我们重新创建我们的项目。这部分内容可以参考这两篇博客,我这里就不复述了。
SpringCloud基本微服务构建(Eureka+GateWay)
我们这里先创建出四个module, 分别是登录,注册,Eureka,GateWay。
这里呢,我把登录和注册拆分为两个模块了,也就是这样。
之所以这样拆呢其实也很简单,如果后面有微信登录,注册,qq邮箱等等第三方验证登录的时候,业务肯定很复杂,所以这两个模块还是需要拆分的。
但是在这里的话,又有个问题,因为登录和注册在很多方面又是相互耦合的,例如User。你登录的时候需要User实体,同样注册也需要呀,如果拆分,那么可能就需要每一个模块都需要User,例如在这里:
那么这里的话如果不优化,那么就会出现代码重复。
那么如何优化呢,其实也很简单,我们单独把User服务再拆分出来,也就是这样:
不过,这里为了省事,我就不拆了,毕竟这玩意我就是闹着玩,没办法交个期末作业,而且我这样做压根就不算优化。
登录/注册拆分
这里忘了说了,我这里拆的话还是每一个服务在共用同一个数据库,懒得再拆表了。
注册模块
注册模块的话比较好拆分 因为用到的东西很少。
就是做一个简单的插入
Controller代码
@Controller
@ResponseBody
public class Register {
@Autowired
RegisterMessage registerMessage;
@Autowired
UserService userService;
@PostMapping("/register")
public RegisterMessage Register(@RequestBody Map<String, Object> userMap) throws Exception {
String account = (String) userMap.get("account");
String username = (String)userMap.get("username");
String password = (String) userMap.get("password");
if(account!=null && password!=null){
userService.addUser(account,username,password);
registerMessage.setFlag(1);
}else {
registerMessage.setFlag(-1);
}
return registerMessage;
}
}
复制代码
Server代码
@Service
public class UserService {
@Autowired
UserMapper userMapper;
public User selectUserById(Integer id){return userMapper.selectUserById(id);}
public User selectUserByAccount(String account){
return userMapper.selectUserByAccount(account);
}
public User selectUserByAccountAndPassword(String account,String password){
return userMapper.selectUserByAccountAndPassword(account,password);
}
@Transactional(rollbackFor = Exception.class)
public int addUser(String account,String username,String password) throws Exception {
// 发生异常回滚
int flag = 1;
try {
userMapper.AddUser(account, username, password);
}catch (Exception e){
flag = -1;
throw new Exception("用户添加异常");
}
return flag;
}
}
复制代码
就这两个核心的。
登录模块
这个登录模块就稍微复杂了一点,因为还有个token生成,并且登录后进入聊天室也是在这里做的,所以,功能复杂一点。
对比原来的项目,就少了个聊天模块
然后这里保留的话,也是保留了比较多的东西的
拦截器保留
持久层保留
这部分的话也是,其实就是保留User和Friend,看过原来的单体项目的应该知道,这个friend其实室没事用的,本来要做的,但是没去做,以后再说吧。
服务层
之后的话,服务层和注册的不太一样,不过也是原来单体架构里面的服务层一样的
@Service
public class UserService {
@Autowired
UserMapper userMapper;
public User selectUserById(Integer id){return userMapper.selectUserById(id);}
public User selectUserByAccount(String account){
return userMapper.selectUserByAccount(account);
}
public User selectUserByAccountAndPassword(String account,String password){
return userMapper.selectUserByAccountAndPassword(account,password);
}
@Transactional(rollbackFor = Exception.class)
public int addUser(String account,String username,String password) throws Exception {
// 发生异常回滚
int flag = 1;
try {
userMapper.AddUser(account, username, password);
}catch (Exception e){
flag = -1;
throw new Exception("用户添加异常");
}
return flag;
}
}
复制代码
至于工具类,这个就保留了两个
网关GateWay配置
做完这些之后的话,还需要再配置一下网关,主要是为了做跨域。 两个方案。
一个是配置文件:
globalcors: # 全局的跨域处理
add-to-simple-url-handler-mapping: true # 解决options请求被拦截问题
corsConfigurations:
'[/**]':
allowedOrigins: # 允许哪些网站的跨域请求
- "http://localhost:8090"
allowedMethods: # 允许的跨域ajax的请求方式
- "GET"
- "POST"
- "DELETE"
- "PUT"
- "OPTIONS"
allowedHeaders: "*" # 允许在请求中携带的头信息
allowCredentials: true # 是否允许携带cookie
maxAge: 360000 # 这次跨域检测的有效期
复制代码
还有一个,自然就是配置类
@Configuration
public class CorsConfig {
@Bean
public CorsWebFilter corsFilter() {
CorsConfiguration config = new CorsConfiguration();
config.addAllowedMethod("*");//允许所有请求头
config.addAllowedOrigin("*");//允许所有请求方法,例如get,post等
config.addAllowedHeader("*");//允许所有的请求来源
config.setMaxAge(360000L);
config.setAllowCredentials(true);//允许携带cookie
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(new PathPatternParser());
source.registerCorsConfiguration("/**", config);//对所有经过网关的请求都生效
return new CorsWebFilter(source);
}
}
复制代码
前端设置
这里做了之后我们前端还要再设置一下,让请求走网关。
我的网关的完整配置如下:
server:
port: 8000
spring:
application:
name: gateway
cloud:
gateway:
discovery:
locator:
enabled: true
globalcors: # 全局的跨域处理
add-to-simple-url-handler-mapping: true # 解决options请求被拦截问题
corsConfigurations:
'[/**]':
allowedOrigins: # 允许哪些网站的跨域请求
- "http://localhost:8090"
allowedMethods: # 允许的跨域ajax的请求方式
- "GET"
- "POST"
- "DELETE"
- "PUT"
- "OPTIONS"
allowedHeaders: "*" # 允许在请求中携带的头信息
allowCredentials: true # 是否允许携带cookie
maxAge: 360000 # 这次跨域检测的有效期
routes:
- id: loginclient
uri: lb://loginclient
predicates:
- Path=/loginClient/**
filters:
- StripPrefix=1
- id: registerclient
uri: lb://registerclient
predicates:
- Path=/registerClient/**
filters:
- StripPrefix=1
eureka:
client:
service-url: # eureka的地址信息
defaultZone: http://127.0.0.1:10086/eureka
复制代码
然后再我的前端的话也是原来做了跨域处理的,但是GateWay的话需要重新设置一下,主要是让GateWay通过一下Axios请求
然后在我的登录,注册模块就是这样的:
到这里的话我们的登录注册功能其实就改造完成了。接下来是聊天改造。
聊天拆分
这部分也是分两个部分,一个是聊天的信息加载,还有一个是,websocket 网络聊天。
聊天拆分的话,这里我们将引入RabbitMQ,用来做聊天信息的持久化,原来是,你发了我就存,这样做可以,但是qps多了就炸了,所以引入消息队列。当然这里的话我们还是使用mysql来做存储,实际上我们应该使用mangodb的,但是作业要求是mysql,而且mysql也可以勉强完成工作,大不了SpringBoot 那里再开启缓存嘛,降低一下查询压力(只要没有发送新消息) 而且这里还能再拆分,那就是在线聊天人员统计,管理这一块,不过不能再搞了,因为容易被锤~
当然最重要的原因是:
一个SpringBoot 起来就烧掉我起码100MB 的内存。技术除了牛逼之外,我觉得省钱也是很重要的。 (这也是为什么要搞个好点的电脑搞开发,不然你连idea可能都养不起)
ok,天也不早了,这个咱们下回分解。(端午都不去玩,我还是人嘛!!!)