文章目录
架构图
shop-parent(后端父项目)
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>cn.wolfcode</groupId>
<artifactId>shop-parent</artifactId>
<version>1.0.0</version>
<modules>
<module>api-gateway</module>
<module>shop-provider</module>
<module>shop-provider-api</module>
<module>shop-uaa</module>
<module>websocket-server</module>
<module>shop-common</module>
<module>canal-client</module>
</modules>
<packaging>pom</packaging>
<!--父工程-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.2.RELEASE</version>
</parent>
<!--依赖版本的锁定-->
<properties>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<spring-cloud.version>Hoxton.SR8</spring-cloud.version>
<spring-cloud-alibaba.version>2.2.3.RELEASE</spring-cloud-alibaba.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring-cloud-alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
</resources>
</build>
</project>
shop-common(公共项目)
- 被其他微服务所继承
- 骨架图
pom.xml
<artifactId>shop-common</artifactId>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<scope>provided</scope>
</dependency>
<!--redis依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.38</version>
</dependency>
</dependencies>
CommonConstants
- 微服务公用常量
public class CommonConstants {
public static final String TOKEN_NAME = "token";
public static final String REAL_IP = "X-REAL-IP";
public static final String FEIGN_REQUEST_KEY= "FEIGN_REQUEST";
public static final String FEIGN_REQUEST_TRUE= "1"; //微服务之间调用
public static final String FEIGN_REQUEST_FALSE= "0";//网关调用
}
UserInfo(用户对象)
- 微服务公用的用户对象
@Setter@Getter
public class UserInfo implements Serializable {
private Long phone;
private String nickName;
private String heead;
private String birthDay;
private String info;
private String loginIp;
}
BusinessException(自定义异常)
@Setter
@Getter
public class BusinessException extends RuntimeException {
private CodeMsg codeMsg;
public BusinessException(CodeMsg codeMsg){
this.codeMsg = codeMsg;
}
}
CommonControllerAdvice(统一异常处理)
- 其他服务继承这个类,然后类上贴上注解
@ControllerAdvice
即可使用
public class CommonControllerAdvice {
@ExceptionHandler(BusinessException.class)
@ResponseBody
public Result handleBusinessException(BusinessException ex){
return Result.error(ex.getCodeMsg());
}
@ExceptionHandler(Exception.class)
@ResponseBody
public Result handleDefaultException(Exception ex){
ex.printStackTrace();//在控制台打印错误消息.
return Result.defaultError();
}
}
RequireLogin(登陆注解)
- 贴了此注解的接口,必须登录才能访问(在拦截部分设置此效果)
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequireLogin {
}
FeignRequestInterceptor(微服务间调用标识)
/**
* 在feign调用的时候底层还是RestTemplate进行调用,在调用前 可以对RestTemplate进行处理
*/
public class FeignRequestInterceptor implements RequestInterceptor {
@Override
public void apply(RequestTemplate template) {
template.header(CommonConstants.FEIGN_REQUEST_KEY,CommonConstants.FEIGN_REQUEST_TRUE);
}
}
RequireLoginInterceptor(拦截需登陆才能访问)
- 与以上
@RequireLogin
注解使用,贴了此注解的接口,必须登录状态才可以访问
public class RequireLoginInterceptor implements HandlerInterceptor {
private StringRedisTemplate redisTemplate;
public RequireLoginInterceptor(StringRedisTemplate redisTemplate){
this.redisTemplate = redisTemplate;
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if(handler instanceof HandlerMethod){
HandlerMethod handlerMethod = (HandlerMethod) handler;
String feignRequest = request.getHeader(CommonConstants.FEIGN_REQUEST_KEY);
if(!StringUtils.isEmpty(feignRequest) && CommonConstants.FEIGN_REQUEST_FALSE.equals(feignRequest) && handlerMethod.getMethodAnnotation(RequireLogin.class)!=null){
// 设置响应类型
response.setContentType("application/json;charset=utf-8");
// 获取用户token信息
String token = request.getHeader(CommonConstants.TOKEN_NAME);
// 判断token是否为空
if(StringUtils.isEmpty(token)){
response.getWriter().write(JSON.toJSONString(Result.error(CommonCodeMsg.TOKEN_INVALID)));
return false;
}
UserInfo userInfo = JSON.parseObject(redisTemplate.opsForValue().get(CommonRedisKey.USER_TOKEN.getRealKey(token)),UserInfo.class);
// token 可能伪造的 或者token过期了
if(userInfo==null){
response.getWriter().write(JSON.toJSONString(Result.error(CommonCodeMsg.TOKEN_INVALID)));
return false;
}
// 获取用户的真实ip信息
String ip = request.getHeader(CommonConstants.REAL_IP);
// 确保ip一致
if(!userInfo.getLoginIp().equals(ip)){
response.getWriter().write(JSON.toJSONString(Result.error(CommonCodeMsg.LOGIN_IP_CHANGE)));
return false;
}
}
}
return true;
}
}
CodeMsg(反馈信息)
- 被其他服务继承,然后定义各自服务的各自的常量信息
- 主要作为参数传给result对象,返回给前端的是result对象
@Setter
@Getter
@NoArgsConstructor
@AllArgsConstructor
public class CodeMsg implements Serializable {
private Integer code;
private String msg;
}
- 演示其他服务继承此类(做参考,每个服务不一样,有些不用定义)
CommonCodeMsg(公用反馈信息)
public class CommonCodeMsg extends CodeMsg {
private CommonCodeMsg(Integer code, String msg){
super(code,msg);
}
public static final CommonCodeMsg ILLEGAL_OPERATION = new CommonCodeMsg(-1,"非法操作");
public static final CommonCodeMsg TOKEN_INVALID = new CommonCodeMsg(-2,"登录超时,请重新登录");
public static final CommonCodeMsg LOGIN_IP_CHANGE = new CommonCodeMsg(-3,"登录IP发生改变,请重新登录");
}
Result(返回给前端的封装类–重点)
- 传入
CodeMsg
类型对象(各个服务都继承CodeMsg,所以各个服务不一样的定义),代表操作错误
@Setter
@Getter
public class Result<T> implements Serializable {
public static final int SUCCESS_CODE = 200;//成功码.
public static final String SUCCESS_MESSAGE = "操作成功";//成功信息.
public static final int ERROR_CODE = 500000;//错误码.
public static final String ERROR_MESSAGE = "系统异常";//错误信息.
private int code;
private String msg;
private T data;
public Result(){}
private Result(int code, String msg, T data){
this.code = code;
this.msg = msg;
this.data = data;
}
public static <T> Result<T> success(T data){
return new Result(SUCCESS_CODE,SUCCESS_MESSAGE,data);
}
public static <T> Result<T> success(String msg, T data){
return new Result(SUCCESS_CODE,msg,data);
}
public static Result error(CodeMsg codeMsg){
return new Result(codeMsg.getCode(),codeMsg.getMsg(),null);
}
public static Result defaultError(){
return new Result(ERROR_CODE,ERROR_MESSAGE,null);
}
public boolean hasError(){
//状态吗!=200 说明有错误.
return this.code!=SUCCESS_CODE;
}
}
CommonRedisKey(公用枚举管理key)
@Getter
public enum CommonRedisKey {
USER_TOKEN("userToken:",TimeUnit.MINUTES,30);
CommonRedisKey(String prefix, TimeUnit unit, int expireTime){
this.prefix = prefix;
this.unit = unit;
this.expireTime = expireTime;
}
public String getRealKey(String key){
return this.prefix+key;
}
private String prefix;
private TimeUnit unit;
private int expireTime;
}
api-gateway(网关项目)
启动类
@SpringBootApplication
@EnableDiscoveryClient
public class ApiGatewayApplication {
public static void main(String[] args) {
SpringApplication.run(ApiGatewayApplication.class,args);
}
}
pom.xml
<artifactId>api-gateway</artifactId>
<dependencies>
<!--gateway网关-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!--nacos客户端-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-spring-cloud-gateway-adapter</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<!--redis依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>cn.wolfcode</groupId>
<artifactId>shop-common</artifactId>
<version>1.0.0</version>
</dependency>
</dependencies>
api-gateway-dev.yaml
- 配置中心上的
server:
port: 9000
spring:
cloud:
gateway:
discovery:
locator:
enabled: true # 让gateway可以发现nacos中的微服务
routes:
- id: uaa_route
uri: lb://uaa-service # lb指的是从nacos中按照名称获取微服务,并遵循负载均 衡策略
predicates:
- Path=/uaa/**
filters:
- StripPrefix=1
- id: product_route
uri: lb://product-service
predicates:
- Path=/product/**
filters:
- StripPrefix=1
- id: seckill_route
uri: lb://seckill-service
predicates:
- Path=/seckill/**
filters:
- StripPrefix=1
- id: ws_route
uri: lb://websocket-service
predicates:
- Path=/ws/**
filters:
- StripPrefix=1
bootstrap.yml
- idea项目上的
spring:
application:
name: api-gateway
cloud:
nacos:
config:
server-addr: 192.168.8.12:8848 #nacos中心地址
file-extension: yaml # 配置文件格式
shared-configs:
- data-id: redis-config-dev.yaml
- data-id: nacos-discovery-config-dev.yaml
profiles:
active: dev # 环境标识
- 网关
- 骨架图
CorsConfig
- 处理跨域问题
@Configuration
public class CorsConfig {
@Bean
public CorsWebFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(new PathPatternParser());
CorsConfiguration config = new CorsConfiguration();
// 允许cookies跨域
config.setAllowCredentials(true);
// #允许向该服务器提交请求的URI,*表示全部允许,在SpringMVC中,如果设成*,会自动转成当前请求头中的Origin
config.addAllowedOrigin("*");
// #允许访问的头信息,*表示全部
config.addAllowedHeader("*");
// 预检请求的缓存时间(秒),即在这个时间段里,对于相同的跨域请求不会再预检了
config.setMaxAge(18000L);
// 允许提交请求的方法,*表示全部允许
config.addAllowedMethod("*");
source.registerCorsConfiguration("/**", config);
return new CorsWebFilter(source);
}
}
GatewayConfiguration
- 处理限流问题(参考演示)
- 有些配置的值则是对应配置中心配置文件中所设置的id值
@Configuration
public class GatewayConfiguration {
private final List<ViewResolver> viewResolvers;
private final ServerCodecConfigurer serverCodecConfigurer;
public GatewayConfiguration(ObjectProvider<List<ViewResolver>> viewResolversProvider,
ServerCodecConfigurer serverCodecConfigurer) {
this.viewResolvers = viewResolversProvider.getIfAvailable(Collections::emptyList);
this.serverCodecConfigurer = serverCodecConfigurer;
}
// 配置限流的异常处理器
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler() {
// Register the block exception handler for Spring Cloud Gateway.
return new SentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer);
}
// 初始化一个限流的过滤器
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public GlobalFilter sentinelGatewayFilter() {
return new SentinelGatewayFilter();
}
@PostConstruct
public void initBlockHandlers() {
BlockRequestHandler blockRequestHandler = new BlockRequestHandler() {
public Mono<ServerResponse> handleRequest(ServerWebExchange serverWebExchange, Throwable throwable) {
Map map = new HashMap<>();
map.put("code", 0);
map.put("message", "接口被限流了");
return ServerResponse.status(HttpStatus.OK).
contentType(MediaType.APPLICATION_JSON).
body(BodyInserters.fromValue(map));
}
};
GatewayCallbackManager.setBlockHandler(blockRequestHandler);
}
@PostConstruct
private void initCustomizedApis() {
Set<ApiDefinition> definitions = new HashSet<>();
ApiDefinition api1 = new ApiDefinition("order_api1")
.setPredicateItems(new HashSet<ApiPredicateItem>() {{
add(new ApiPathPredicateItem().setPattern("/order-serv/api1/**").
setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_PREFIX));
}});
definitions.add(api1);
GatewayApiDefinitionManager.loadApiDefinitions(definitions);
}
//增加对商品微服务的 限流
@PostConstruct
private void initGatewayRules() {
Set<GatewayFlowRule> rules = new HashSet<>();
/* rules.add(new GatewayFlowRule("product_route")
.setCount(3)
.setIntervalSec(1)
);
rules.add(new GatewayFlowRule("order_api1").
setCount(1).
setIntervalSec(1));*/
GatewayRuleManager.loadRules(rules);
}
}
CommonFilter
- 配置全局过滤(做参考)
- 前置过滤将用户的请求头加入真实ip,和定义它是属于网关访问的请求标识
/**
* 定义全局过滤器,功能如下:
* 1.把客户端真实IP通过请求同的方式传递给微服务
* 2.在请求头中添加FEIGN_REQUEST的请求头,值为0,标记请求不是Feign调用,而是客户端调用
* 3.刷新Token的有效时间
*/
@Component
public class CommonFilter implements GlobalFilter {
@Autowired
private StringRedisTemplate redisTemplate;
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
/**
* pre拦截逻辑
* 在请求去到微服务之前,做了两个处理
* 1.把客户端真实IP通过请求同的方式传递给微服务
* 2.在请求头中添加FEIGN_REQUEST的请求头,值为0,标记请求不是Feign调用,而是客户端调用
*/
ServerHttpRequest request = exchange.getRequest().mutate().
header(CommonConstants.REAL_IP,exchange.getRequest().getRemoteAddress().getHostString()).
header(CommonConstants.FEIGN_REQUEST_KEY,CommonConstants.FEIGN_REQUEST_FALSE).
build();
return chain.filter(exchange.mutate().request(request).build()).then(Mono.fromRunnable(()->{
/**
* post拦截逻辑
* 在请求执行完微服务之后,需要刷新token在redis的时间
* 判断token不为空 && Redis还存在这个token对于的key,这时候需要延长Redis中对应key的有效时间.
*/
String token,redisKey;
if(!StringUtils.isEmpty(token =exchange.getRequest().getHeaders().getFirst(CommonConstants.TOKEN_NAME))
&& redisTemplate.hasKey(redisKey = CommonRedisKey.USER_TOKEN.getRealKey(token))){
redisTemplate.expire(redisKey, CommonRedisKey.USER_TOKEN.getExpireTime(), CommonRedisKey.USER_TOKEN.getUnit());
}
}));
}
}
canal-client(数据同步项目)
直接跳转至:使用canal同步数据库
shop-provider(管理服务公共依赖)
- 被处理业务的微服务所继承
pom.xml
<artifactId>shop-provider</artifactId>
<packaging>pom</packaging>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--nacos客户端-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<dependency>
<groupId>cn.wolfcode</groupId>
<artifactId>shop-common</artifactId>
<version>1.0.0</version>
</dependency>
</dependencies>
业务微服务所用的pom.xml(参考)
<dependencies>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.10</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.2.0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!--redis依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>cn.wolfcode</groupId>
<artifactId>seckill-api</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-spring-boot-starter</artifactId>
<version>2.0.4</version>
</dependency>
<dependency>
<groupId>cn.wolfcode</groupId>
<artifactId>pay-api</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>cn.wolfcode</groupId>
<artifactId>intergral-api</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>cn.wolfcode</groupId>
<artifactId>product-api</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
<version>2.2.2.RELEASE</version>
<exclusions>
<exclusion>
<groupId>io.seata</groupId>
<artifactId>seata-spring-boot-starter</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-spring-boot-starter</artifactId>
<version>1.3.0</version>
</dependency>
</dependencies>
shop-provider-api(管理api公用的依赖)
- 继承它的微服务,其实就是放一些逻辑微服务需要用到的
实体类
和redis枚举类
和vo对象
等信息,被逻辑微服务所继承 - api项目继承此公用api项目,则无需在pom.xml再添加依赖
pom.xml
<artifactId>shop-provider-api</artifactId>
<packaging>pom</packaging>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
shop-uaa(用户项目)
- 参考骨架图
启动类
@SpringBootApplication
@EnableDiscoveryClient
@MapperScan(basePackages = "xxx.xxx.mapper")
public class UaaApplication {
public static void main(String[] args) {
SpringApplication.run(UaaApplication.class,args);
}
}
websocket-server(消息通知项目)
具体请看:websocket使用
- 骨架图演示
pom.xml
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--nacos客户端-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.38</version>
</dependency>
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-spring-boot-starter</artifactId>
<version>2.0.4</version>
</dependency>
</dependencies>
bootstrap.yml
spring:
application:
name: websocket-service
cloud:
nacos:
config:
server-addr: 192.168.8.12:8848 #nacos中心地址
file-extension: yaml # 配置文件格式
shared-configs:
- data-id: rocketmq-config-dev.yaml
- data-id: nacos-discovery-config-dev.yaml
profiles:
active: dev # 环境标识
websocket-service-dev.yaml
server:
port: 8091
rocketmq:
producer:
group: websocket-group
config部分
@Configuration
public class WebSocketConfig {
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
@Setter
@Getter
@ServerEndpoint("/{uuid}")
@Component
public class WebSocketServer {
private Session session;
public static ConcurrentHashMap<String,WebSocketServer> clients = new ConcurrentHashMap<>();
@OnOpen
public void onOpen(Session session, @PathParam( "uuid") String uuid){
System.out.println("客户端连接===>"+uuid);
this.session = session;
clients.put(uuid,this);
}
@OnClose
public void onClose(@PathParam( "uuid") String uuid){
clients.remove(uuid);
}
@OnError
public void onError(Throwable error) {
error.printStackTrace();
}
}
mq部分(参考演示)
public class MQConstant {
//订单结果
public static final String ORDER_RESULT_TOPIC = "ORDER_RESULT_TOPIC";
}
@Setter
@Getter
public class OrderMQResult implements Serializable {
private Integer time;//秒杀场次
private Long seckillId;//秒杀商品id
private String orderNo;//订单编号
private String msg;//提示消息
private Integer code;//状态码
private String token;//用户token
}
- 以下这个是重点~~~~
- 通过token标识唯一客户端,然后给此客户端发送信息
@Component
@Slf4j
@RocketMQMessageListener(consumerGroup = "MQOrderResultListener",
topic = MQConstant.ORDER_RESULT_TOPIC)
public class MQOrderResultListenner implements RocketMQListener<OrderMQResult> {
@SneakyThrows
@Override
public void onMessage(OrderMQResult orderMQResult) {
log.info("进入到通知队列中..................");
// 通知客户端
try {
Thread.sleep(1000);
}catch (Exception e){
e.printStackTrace();
}
String token = orderMQResult.getToken();
WebSocketServer webSocketServer = WebSocketServer.clients.get(token);
if (webSocketServer!=null){
webSocketServer.getSession().getBasicRemote().sendText(JSON.toJSONString(orderMQResult));
log.info("成功通知用户..................");
}
}
}