进行交互,前端可以使用stomp.js类库进行交互,消息一STOMP协议格式进行传输,这样就规定了消息传输格式。
消息进入后端以后,可以将消息与实现STOMP格式的代理器进行整合。
这是为了消息统一管理,进行机器扩容时,可进行负载均衡部署
使用spring websocket:
使用spring websocket,是因为他提供了STOMP的传输自协议的同时,还提供了StockJS的支持。
当然,除此之外,spring websocket还提供了权限整合的功能,还有自带天生与spring家族等相关框架进行无缝整合。
应用场景
应用背景
2016年,在公司与同事一起讨论和开发了公司内部的客服系统,由于前端技能的不足,很多通讯方面的问题,无法亲自调试前端来解决问题。
因为公司技术架构体系以前后端分离为主,故前端无法协助后端调试,后端无法协助前端调试
在加上websocket为公司刚启用的协议,了解的人不多,导致前后端调试问题重重。
一年后的今天,我打算将前端重温,自己来调试一下前后端,来发掘一下之前联调的问题.
当然,前端,我只是考虑stomp.js和sockt.js的使用。
代码阶段设计
角色
- 客服
- 客户
登录用户状态
- 上线
- 下线
分配策略
- 用户登陆后,应该根据用户角色进行分配
关系保存策略
- 应该提供关系型保存策略: 考虑内存式策略(可用于测试),redis式策略
备注:优先应该考虑实现Inmemory策略,用于测试,让关系保存策略与存储平台无关
通讯层设计
- 归类topic的广播设计(通讯方式:1-n)
- 归类queue的单点设计(通讯方式:1-1)
代码实现
角色
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import java.util.Collection;
public enum Role {
CUSTOMER_SERVICE,
CUSTOMER;
public static boolean isCustomer(User user) {
Collection<GrantedAuthority> authorities = user.getAuthorities();
SimpleGrantedAuthority customerGrantedAuthority = new SimpleGrantedAuthority("ROLE_" + Role.CUSTOMER.name());
return authorities.contains(customerGrantedAuthority);
}
public static boolean isCustomerService(User user) {
Collection<GrantedAuthority> authorities = user.getAuthorities();
SimpleGrantedAuthority customerServiceGrantedAuthority = new SimpleGrantedAuthority("ROLE_" + Role.CUSTOMER_SERVICE.name());
return authorities.contains(customerServiceGrantedAuthority);
}
}
代码中User对象,为安全对象,即 spring中org.springframework.security.core.userdetails.User,为UserDetails的实现类。
User对象中,保存了用户授权后的很多基础权限信息,和用户信息。
如下:
public interface UserDetails extends Serializable {
Collection<? extends GrantedAuthority> getAuthorities();
String getPassword();
String getUsername();
boolean isAccountNonExpired();
boolean isAccountNonLocked();
boolean isCredentialsNonExpired();
boolean isEnabled();
}
方法 #isCustomer 和 #isCustomerService 用来判断用户当前是否是顾客或者是客服。
登录用户状态
public interface StatesManager {
enum StatesManagerEnum{
ON_LINE,
OFF_LINE
}
void changeState(User user , StatesManagerEnum statesManagerEnum);
StatesManagerEnum currentState(User user);
}
设计登录状态时,应存在登录状态管理相关的状态管理器,此管理器只负责更改用户状态和获取用户状态相关操作。
并不涉及其他关联逻辑,这样的代码划分,更有助于面向接口编程的扩展性
分配策略
public interface DistributionUsers {
void distribution(User user);
}
分配角色接口设计,只关注传入的用户,并不关注此用户是客服或者用户,具体需要如何去做,由具体的分配策略来决定。
关系保存策略
public interface RelationHandler {
void saveRelation(User customerService,User customer);
List<User> listCustomers(User customerService);
void deleteRelation(User customerService,User customer);
void saveCustomerService(User customerService);
List<User> listCustomerService();
User getCustomerService(User customer);
boolean exist(User user);
User availableNextCustomerService();
}
关系保存策略,亦是只关注关系保存相关,并不在乎于保存到哪个存储介质中。
实现类由Inmemory还是redis还是mysql,它并不专注。
但是,此处需要注意,对于这种关系保存策略,开发测试时,并不涉及高可用,可将Inmemory先做出来用于测试。
开发功能同时,相关同事再来开发其他介质存储的策