当前位置:   article > 正文

编程不良人, springCloud项目新知识点_编程不良人 elasticsearch

编程不良人 elasticsearch

1. 格式化RedisTemplate保存的对象为json格式

// An highlighted block
@Configuration
public class RedisTemplateConfig {
    @Autowired
    public RedisTemplateConfig(RedisTemplate redisTemplate) {
        //1.创建jackson序列化方式
        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(
                Object.class);
        //2..创建object mapper
        ObjectMapper objectMapper = new ObjectMapper();
        //3.允许访问对象中所有属性
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        //4.转换json过程中保存类的信息
        objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL,
                JsonTypeInfo.As.WRAPPER_ARRAY);
        jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
        //5.设置value的序列化规则和 key的序列化规则   //key String hashkey:String hashvalue: json序列化
        StringRedisSerializer stringKeySerializer = new StringRedisSerializer();
        redisTemplate.setKeySerializer(stringKeySerializer);
        //6.jackson2JsonRedisSerializer就是JSON序列号规则,
        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
        //6.5 设置hash类型key value 序列化方式
        redisTemplate.setHashKeySerializer(stringKeySerializer);
        redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
        //7工厂创建redisTemplate对象之后在进行配置
        redisTemplate.afterPropertiesSet();
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

2. 创建objecMapper配置类

/**
 * ObjectMapper配置类
 */
@Configuration
public class BeanConfig {

    @Bean
    public ObjectMapper getObjectMapper() {
        return new ObjectMapper();
    }

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

3. 属性赋值, 业务key

        /**
         * 已登录用户信息接口,
         * 使用业务key保存数据, 使用属性复制
         */
        @GetMapping("/admin-user")
        public AdminDTO admin(String token) {
            Admin admin = (Admin) redisTemplate.opsForValue()
                    .get(RedisPrefix.TOKEN_KEY + token);
            AdminDTO adminDTO = new AdminDTO();
            // 1.属性复制
            BeanUtils.copyProperties(admin, adminDTO);
            return adminDTO;
        }


	// 业务key
	public interface RedisPrefix {
	    /**代表用户认证token key*/
	    String TOKEN_KEY = "TOKEN:";
	
	    /**
	     * 点赞数量业务key
	     */
	    String VIDEO_LIKE_COUNT_PREFIX = "VIDEO_LIKE_COUNT:";
	
	    // 是否点赞业务key
	    String USER_LIKED = "USER_LIKED:";
	
	    /**
	     * 播放次数业务key
	     */
	    String VIDEO_PLAYED_COUNT_PREFIX = "VIDEO_PLAYED_COUNT:";
	
	    /**
	     * 用户不喜欢业务key
	     */
	    String USER_DISLIKE_PREFIX = "USER_DISLIKE:";
	
	    String SESSION = "session:";
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40

4. 自定义注解, 获取redis中的token

// 自定义接口, 运行时有效, 加载方法上,
// 根据request对象,获取redis中的token注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface RequiredToken {
}




/**
 * 自定义拦截器, 拦截所有带有自定义注解的方法请求
 */
@Configuration
public class MvcConfig implements WebMvcConfigurer {

    @Autowired  // 注入自定义拦截器
    private TokenInterceptors tokenInterceptors;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {

        // 拦截所有带有自定义注解的方法请求
        registry.addInterceptor(tokenInterceptors)
                .addPathPatterns("/**");
    }
}




/**
 * 自定义拦截器,保存用户信息和token
 */
@Component
@Slf4j
public class TokenInterceptors implements HandlerInterceptor {

    @Autowired
    private StringRedisTemplate redisTemplate;
    @Autowired
    private ObjectMapper objectMapper;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        // 判断方法上是否有自定义注解  RequiredToken
        boolean requiredToken = ((HandlerMethod) handler).getMethod().isAnnotationPresent(RequiredToken.class);

        if (requiredToken) {
            // 1 获取token信息
            String token = request.getParameter("token");
            log.info("拦截器获取的token信息为{}", token);
            // 2设置保存的业务key
            String tokenKey = "session_" + token;
            // 3 根据token获取用户信息
            String userStr = redisTemplate.opsForValue().get(tokenKey);
            User user = objectMapper.readValue(userStr, User.class);

            // 获取不到用户信息
            if (user == null) throw new RuntimeException("无效的token");

            // 4 保存到请求上下文中
            request.setAttribute("token", token);
            request.setAttribute("user", user);
        }

        return true;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70

在这里插入图片描述

5. 自定义过滤器配置, 设置需要token才可以访问的接口

/**
 * 自定义过滤器配置, 设置需要token才可以访问的接口
 */
@Component
public class TokenGatewayFilterFactory extends AbstractGatewayFilterFactory<TokenGatewayFilterFactory.Config> {


    @Autowired
    private StringRedisTemplate redisTemplate;

    public TokenGatewayFilterFactory() {
        super(Config.class);
    }

    /**
     * @param config 就是本类中Config的对象
     * @return
     */
    @Override
    public GatewayFilter apply(Config config) {
        return new GatewayFilter() {
            /**
             * 在网关中配置 -Token就可以使用了
             * @param exchange 交换机,包装的request,response
             * @param chain     放行功能
             * @return
             */
            @Override
            public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

                // 当requiredToken为True的时候才认证
                if (config.requiredToken) {

                    if (exchange.getRequest().getQueryParams().get(0) == null) throw new RuntimeException("非法异常!");

                    // 获取token信息
                    String token = exchange.getRequest().getQueryParams().get("token").get(0);

                    // 判断token是否存在,不存在直接抛出异常
                    if (!redisTemplate.hasKey(RedisPreFix.TOKEN_KEY + token)) throw new RuntimeException("不合法的令牌!");
                }

                return chain.filter(exchange);
            }
        };
    }

    /**
     * 用来配置将使用filter时指定值赋值给Config中哪个属性, 多个参数时按顺序返回
     */
    @Override
    public List<String> shortcutFieldOrder() {
        return Arrays.asList("requiredToken", "name");
    }

    /**
     * 内部类,指定配置中的token需要的参数
     */
    public static class Config {
        // 默认为false
        private boolean requiredToken;

        public boolean isRequiredToken() {
            return requiredToken;
        }

        public void setRequiredToken(boolean requiredToken) {
            this.requiredToken = requiredToken;
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
// 全局路由配置
server:
  port: 9999
spring:
  application:
    name: API-GATEWAY
  cloud:
    nacos:
      server-addr: 192.168.216.128:8848
    gateway:
      routes: #断言 用来配置路由规则 id uri path

        #admins router
        - id: admins_router
          uri: lb://API-ADMINS #负载均衡写法
          predicates:
            #采用这种路径断言办法,请求进入网关断言,但是不能已这种方式发送给controller,因为没有此路径的请求
            - Path=/admin/demos,/admin/tokens,/admin/admin-user,/admin/tokens/**
          filters:
            - StripPrefix=1 #去掉请求前缀的filter =写int类型 1代表去掉1级前缀 2代表去掉两级

        #categary router
        - id: categary_router
          uri: lb://API-CATEGARYS
          predicates:
            - Path=/categary/**
          filters:
            - StripPrefix=1
            - Token=true
        #users router
        - id: users_router
          uri: lb://API-USERS
          predicates:
            - Path=/admin/users/**
          filters:
            - StripPrefix=1
            - Token=true
        #videos router
        - id: videos_router
          uri: lb://API-VIDEOS
          predicates:
            - Path=/admin/videos/**
          filters:
            - StripPrefix=1
            - Token=true

      #跨域配置
      globalcors:
        cors-configurations:
          '[/**]':
            allowedOrigins: "*"
            allowedMethods: "*"
            allowedHeaders: "*"

  #配置redis用来做token验证的
  redis:
    port: 6379
    host: 192.168.216.128
    database: 0
    password: 123456
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60

6.全局异常处理

/**
 * 全局异常配置, 实现ErrorWebExceptionHandler接口
 */
@Configuration
public class GlobalExceptionConfiguration implements ErrorWebExceptionHandler {

    @Override //参数1: request response   ex:出现异常时异常对象
    public Mono<Void> handle(ServerWebExchange exchange, Throwable ex) {
        Map<String, String> result = new HashMap<>();

        //1.获取响应对象
        ServerHttpResponse response = exchange.getResponse();

        //2.response是否结束  多个异常处理时候
        if (response.isCommitted()) {
            return Mono.error(ex);
        }

        //2.设置响应头类型
        response.getHeaders().setContentType(MediaType.APPLICATION_JSON);

        //3.设置响应状态吗
        if (ex instanceof IllegalTokenException) {
            response.setStatusCode(HttpStatus.FORBIDDEN);
        } else {
            response.setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR);
        }
        //4.设置响应内容
        return response
                .writeWith(Mono.fromSupplier(() -> {
                    DataBufferFactory bufferFactory = response.bufferFactory();
                    result.put("msg", ex.getMessage());
                    ObjectMapper objectMapper = new ObjectMapper();
                    try {
                        return bufferFactory.wrap(objectMapper.writeValueAsBytes(result));
                    } catch (JsonProcessingException e) {
                        e.printStackTrace();
                        return null;
                    }
                }));
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42

7. 用户上传接口, 使用MQ异步上传

    /**
     * 用户文件上传
     *
     * @param file        上传文件
     * @param video       视频对象需要的参数
     * @param category_id 分类id
     * @param request     token验证获取用户信息
     * @return
     */
    @PostMapping("/user/videos")
    @RequiredToken
    public Video publishVideos(MultipartFile file, Video video,
                               Integer category_id, HttpServletRequest request) throws IOException {
        // 1.获取文件原始名称 getOriginalFilename()
        String originalFilename = file.getOriginalFilename();

        // 2 获取文件名称后缀, .mp4, 没有点 添加上
        String ext = FilenameUtils.getExtension(originalFilename);

        // 3 生成uuid
        String uuidFileName = UUID.randomUUID().toString().replace("-", "");

        // 4 生成uuid文件名名称  如 20220714.MP4
        String newFileName = uuidFileName + "" + ext;

        // 5 上传阿里云oss, 获取路径
        String url = OSSUtils.upload(file.getInputStream(), "videos", newFileName);
        log.info("上传成功返回的路径为:{}", url);

        // 使用阿里云截取某一帧作为封面
        String cover = url
                + "?x-oss-process=video/snapshot,t_30000,f_jpg,w_0,h_0,m_fast,ar_auto";
        log.info("阿里云oss根据ur1截取视频封面: {}", cover);

        // 获取用户信息
        User user = (User) request.getAttribute("user");
        video.setUid(user.getId());

        // 6 设置视频信息
        video.setLink(url);
        video.setCover(cover);
        video.setCategoryId(category_id);

        // 7 调用video微服务保存数据库信息
        Video videoResult = videosClient.publish(video);
        log.info("保存成功之后返回的视频信息:{}", videoResult);
        return videoResult;
    }



VideoClient层代码, 转发到消费者代码

    /** controller
     * 添加一条视频
     * @param video
     * @return
     */
    @PostMapping("publish")
    public Video publish(@RequestBody Video video) {
        log.info("接收到的video对象为:{}", video);
        return videoService.insert(video);
    }



Service层, mq对象
@Override
    public Video insert(Video video) {
        // 更新数据
        video.setCreatedAt(new Date());
        video.setUpdatedAt(new Date());
        video.setLikes(0L);
        // video.setCategory(categoryClient.findById(video.getCategoryId()));
        // 保存到数据库
        videoMapper.insert(video);

        // 将video对象转为videoVo对象,
        VideoVo videoVO = this.getVideoVO(video);

        // 使用mq异步处理,提升系统响应, 将视频信息写入es索引库
        rabbitTemplate.convertAndSend("videos", "", 
                JSONUtils.writeJSON(videoVO));
        return video;
    }




服务消费者代码, 在search微服务里面, 同时保存到el中, 方便搜索
/**
 * 服务消费者
 */
@Component
@Slf4j
public class VideoConsumer {

    @Autowired
    @Qualifier("elasticsearchClient")
    private RestHighLevelClient restHighLevelClient;
    @Autowired
    private ObjectMapper objectMapper;



    @RabbitListener(bindings = @QueueBinding(
            value = @Queue,   // 绑定队列
            exchange = @Exchange(name = "videos", type = "fanout") // 绑定交换机, 默认交换机
    ))
    public void receive(String message) throws IOException {
        log.info("消费者MQ接收到的video信息为:{}", message);
        // 1 将mq中的 json转为对象
        VideoVo videoVo = objectMapper.readValue(message, VideoVo.class);

        // 2 创建ES中索引请求对象,  参数1 操作索引, 参数2: 索引类型, 参数3: 文当id
        IndexRequest indexRequest = new IndexRequest("video", "video", videoVo.getId().toString());

        // 3 设置ES文档的内容
        indexRequest.source(message, XContentType.JSON);

        // 4 执行索引操作
        IndexResponse indexResponse = restHighLevelClient.index(indexRequest, RequestOptions.DEFAULT);
        log.info(" video录入ES中的信息为:{}", indexResponse);
    }


}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128

7. 分布式搜索Elasticsearch

/**
 * ES配置类, 不用配置文件中配置
 */
@Configuration
public class RestClientConfig extends AbstractElasticsearchConfiguration {

    @Bean
    @Override
    public RestHighLevelClient elasticsearchClient() {
        ClientConfiguration clientConfiguration = ClientConfiguration.builder()
                .connectedTo("localhost:9200") // 端口号
                .build();
        return RestClients.create(clientConfiguration).rest();
    }
}




@Service
public class VideoServiceSearchImpl implements VideoSearchService {

    @Qualifier("elasticsearchClient")
    @Autowired
    private RestHighLevelClient restHighLevelClient;
    @Autowired
    private ObjectMapper objectMapper;

    /**
     * 根据ES分页搜索
     *
     * @param q
     * @param page
     * @param rows
     * @return
     */
    @Override
    public Map<String, Object> videos(String q, Integer page, Integer rows) {
        HashMap<String, Object> map = new HashMap<>();

        // 1. 计算分页
        int start = (page - 1) * rows;
        // 2.创建索引对象
        SearchRequest request = new SearchRequest();

        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder
                .from(start).size(rows)     // 分页查询
                .query(QueryBuilders.termQuery("title", q));  // 根据name字段分词搜索
        // 3 设置搜索索引, 搜索类型
        request.indices("video").types("video").source(searchSourceBuilder);

        SearchResponse searchResponse = null;

        //执行搜索
        try {
            searchResponse = restHighLevelClient.search(request, RequestOptions.DEFAULT);

            // 获取条数
            Long totalHits = searchResponse.getHits().getTotalHits().value;

            // 创建VO集合
            ArrayList<VideoVo> videoVos = new ArrayList<>();

            if (totalHits > 0) {
                map.put("total_count", totalHits);

                // 获取数组对象
                SearchHit[] hits = searchResponse.getHits().getHits();

                // 遍历结果,保存到集合中
                for (SearchHit hit : hits) {
                    String videoVOJSON = hit.getSourceAsString();
                    VideoVo videoVo = objectMapper.readValue(videoVOJSON, VideoVo.class);
                    videoVo.setId(Integer.parseInt(hit.getId()));
                    videoVos.add(videoVo);
                }

            }

            map.put("items", videoVos);
            return map;

        } catch (IOException e) {
            e.printStackTrace();
        }
        return map;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小丑西瓜9/article/detail/199712
推荐阅读
相关标签
  

闽ICP备14008679号