springcloud搭建标配配置(参考)

架构图

在这里插入图片描述

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("成功通知用户..................");
        }
    }
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值