猫眼后端springcloud

猫眼调试过程当中的问题

image.png
查看exphp

第二章

数据传递示意图
image.png
框架构建
image.png
不上传.mvn文件
此处去掉
image.png
image.png
添加backend_common模块后
parent项目添加以下
image.png
backend_common添加以下
image.png
backend_common的配置pom文件添加以下东西
image.png
这是为了打包后能到resources下的xml的配置文件(好比Mapper文件)
要安装mybatis helper以及generateallsetter这两款插件
前者能够提示mapper中的方法对应xml文件的方法
后者能够alt+enter自动提示生成set代码语句html

lombok用法示例
image.png
image.png
cleanup自动生成try catch自动关闭流java

第四章

父级包必定要按照pom打包
image.pngmysql

image.png
consumer消费示例
restTemplate(http请求工具)
image.png
eurekaClient
image.png
image.pnglinux

image.png
eureka的面试点
image.png
没有一个同时达到C A P
AP好比redis,它的一致性就不是很强
CA 数据库场景和分布式数据库场景
CP 好比mysql数据库
image.png
zookeeper好比在某个节点获取数据的时候,在操做结束以前,你都不可能在其余节点获取改数据
eureka保证的是可用性,客户端从服务端注册表中拉去信息的时候有30s的延迟nginx

第五章

影片相关的表结构
image.pngweb

第六章

在没有使用Feign的状况下调用film模块的信息
`
// 播放厅对应的影片数据, 影片冗余数据, 缓存里有一份
private MoocHallFilmInfoT describeFilmInfo(String filmId) throws CommonServiceException{面试

// GET REGISTER
   ServiceInstance choose = eurekaClient.choose("film-service");
   // 组织调用参数
   String hostname = choose.getHost();
   int port = choose.getPort();

   String uri = "/films/"+filmId;

   String url = "http://"+hostname+":"+port + uri;

   // 经过restTemplate调用影片服务
   JSONObject baseResponseVO = restTemplate.getForObject(url, JSONObject.class);

   // 解析返回值
   JSONObject dataJson = baseResponseVO.getJSONObject("data");

   // 组织参数
   MoocHallFilmInfoT hallFilmInfo = new MoocHallFilmInfoT();

// "filmId":"1",
// "filmName":"我不是药神",
// "filmLength":"132",
// "filmCats":"喜剧,剧情",
// "actors":"程勇,曹斌,吕受益,刘思慧",
// "imgAddress":"films/238e2dc36beae55a71cabfc14069fe78236351.jpg",redis

hallFilmInfo.setFilmId(dataJson.getIntValue("filmId"));
   hallFilmInfo.setFilmName(dataJson.getString("filmName"));
   hallFilmInfo.setFilmLength(dataJson.getString("filmLength"));
   hallFilmInfo.setFilmCats(dataJson.getString("filmCats"));
   hallFilmInfo.setActors(dataJson.getString("actors"));
   hallFilmInfo.setImgAddress(dataJson.getString("imgAddress"));

   return hallFilmInfo;

}
`算法

第七章ribbon

image.png

image.png
建立多环境
image.png
image.png
image.png
启动Eureka和三个provider
整合Eureka和ribbon
例子中provide为三个节点
第一种方式
image.png
第二种方式
image.png
image.png

负载均衡算法

image.png
image.png
image.png
自定义负载规则
image.png
image.png
IRule的源码
image.png
image.png
自定义IRule使服务挂掉
image.png
IPing
image.png
image.png
image.png
image.png
image.png

ribbon的参数

image.png
image.png
image.png

第八章

Hystrix属于高可用的功能
不能帮助实现任何业务

8-2 Hystrix入门

image.png
image.png
image.png
image.png

8-3 Hystrix架构图

官方的架构图
image.png
思惟导图
image.png

8-4 Hystrix入门

建立测试工程

8-5 Hystrix Command的创建

image.png
构建CommandTest
`

@Test
public void executeTest(){
    long beginTime = System.currentTimeMillis();

    CommandDemo commandDemo = new CommandDemo("execute");

    // 同步执行Command
    String result = commandDemo.execute();

    long endTime = System.currentTimeMillis();
    System.out.println("result="+result+" , speeding="+(endTime-beginTime));
}

`
构建CommandDemo

package com.imooc.hystrix.show.command;

import com.netflix.hystrix.*;
import lombok.Data;

/**
 * @author : jiangzh
 * @program : com.imooc.hystrix.show.command
 * @description :
 **/
@Data
public class CommandDemo extends HystrixCommand<String> {

    private String name;

    public CommandDemo(String name){
        super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("CommandHelloWorld")
        );

        this.name = name;
    }

    /**
    * @Description: 
    * @Param:
    * @return: java.lang.String
    * @Author: jiangzh
    */
    // 单次请求调用的业务方法
    @Override
    protected String run() throws Exception {
        String result = "CommandHelloWorld name : "+ name;


        System.err.println(result+" , currentThread-"+Thread.currentThread().getName());

        return result;
    }

}

Hystrix若是command直接执行run方法的话,则直接进入第6步

8-6 Hystrix Queue的演示

构建CommandTest

@Test
    public void queueTest() throws ExecutionException, InterruptedException {
        long beginTime = System.currentTimeMillis();

        CommandDemo commandDemo = new CommandDemo("queue");

        Future<String> queue = commandDemo.queue();

        long endTime = System.currentTimeMillis();

        System.out.println("future end , speeding="+(endTime-beginTime));

        long endTime2 = System.currentTimeMillis();

        System.out.println("result="+queue.get()+" , speeding="+(endTime2-beginTime));

    }

构建CommandDemo

package com.imooc.hystrix.show.command;

import com.netflix.hystrix.*;
import lombok.Data;

/**
 * @author : jiangzh
 * @program : com.imooc.hystrix.show.command
 * @description :
 **/
@Data
public class CommandDemo extends HystrixCommand<String> {

    private String name;

    public CommandDemo(String name){
        super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("CommandHelloWorld")
        );

        this.name = name;
    }

    /**
    * @Description: 
    * @Param:
    * @return: java.lang.String
    * @Author: jiangzh
    */
    // 单次请求调用的业务方法
    @Override
    protected String run() throws Exception {
        String result = "CommandHelloWorld name : "+ name;

        Thread.sleep(800l);
        System.err.println(result+" , currentThread-"+Thread.currentThread().getName());

        return result;
    }

}

8-7 Observe的两种形式的演示

//非阻塞式调用 必须有耗时的操做,不然主进程退出后,天然也退出了

@Test
    public void observeTest(){
        long beginTime = System.currentTimeMillis();

        CommandDemo commandDemo = new CommandDemo("observe");

        Observable<String> observe = commandDemo.observe();

        // 阻塞式调用
        String result = observe.toBlocking().single();

        long endTime = System.currentTimeMillis();
        System.out.println("result="+result+" , speeding="+(endTime-beginTime));


        // 非阻塞式调用
        observe.subscribe(new Subscriber<String>() {
            @Override
            public void onCompleted() {
                System.err.println("observe , onCompleted");
            }

            @Override
            public void onError(Throwable throwable) {
                System.err.println("observe , onError - throwable="+throwable);
            }

            @Override
            public void onNext(String result) {
                long endTime = System.currentTimeMillis();
                System.err.println("observe , onNext result="+result+" speend:"+(endTime - beginTime));
            }
        });
    }

8-8 ToObserve的两种形式的演示

1.必须实例化两个CommandDemo
2.若想非阻塞的状况执行,必须sleep

@Test
    public void toObserveTest() throws InterruptedException {
        long beginTime = System.currentTimeMillis();

        CommandDemo commandDemo1 = new CommandDemo("toObservable1");

        Observable<String> toObservable1 = commandDemo1.toObservable();

        // 阻塞式调用
        String result = toObservable1.toBlocking().single();

        long endTime = System.currentTimeMillis();
        System.out.println("result="+result+" , speeding="+(endTime-beginTime));

        CommandDemo commandDemo2 = new CommandDemo("toObservable2");
        Observable<String> toObservable2 = commandDemo2.toObservable();
        // 非阻塞式调用
        toObservable2.subscribe(new Subscriber<String>() {
                @Override
                public void onCompleted() {
                System.err.println("toObservable , onCompleted");
            }

            @Override
            public void onError(Throwable throwable) {
                System.err.println("toObservable , onError - throwable="+throwable);
            }

            @Override
            public void onNext(String result) {
                long endTime = System.currentTimeMillis();
                System.err.println("toObservable , onNext result="+result+" speend:"+(endTime - beginTime));
            }
        });

        Thread.sleep(2000l);
    }

8-9 四种执行方式区别讲解

image.png
observe是先执行command的run方法返回res,而后执行加载Subscriber
ToObserver是先加载Subscriber后执行command的run方法,因此在第二个例子中,若是不进行休眠的话,OnNext中是打印不出来结果的
image.png
现实用的最多的是Command queue

8-10 ObserveableCommand演示

/**
 * @author : jiangzh
 * @program : com.imooc.hystrix.show.command
 * @description :
 **/
@Data
public class ObserveCommandDemo extends HystrixObservableCommand<String> {

    private String name;

    public ObserveCommandDemo(String name){
        super(Setter
                .withGroupKey(HystrixCommandGroupKey.Factory.asKey("ObserveCommandDemo"))
                .andCommandKey(HystrixCommandKey.Factory.asKey("ObserveCommandKey")));
        this.name = name;
    }

    @Override
    protected Observable<String> construct() {

        System.err.println("current Thread: "+Thread.currentThread().getName());

        return Observable.create(new Observable.OnSubscribe<String>() {
            @Override
            public void call(Subscriber<? super String> subscriber) {
                // 业务处理
                subscriber.onNext("action 1 , name="+name);
                subscriber.onNext("action 2 , name="+name);
                subscriber.onNext("action 3 , name="+name);

                // 业务处理结束
                subscriber.onCompleted();
            }
        }).subscribeOn(Schedulers.io());
    }
}
/**
 * @author : jiangzh
 * @program : com.imooc.hystrix.show.command
 * @description :
 **/
public class ObserveCommandTest {

    @Test
    public void observeTest() throws InterruptedException {
        long beginTime = System.currentTimeMillis();

        ObserveCommandDemo commandDemo = new ObserveCommandDemo("ObserveCommandTest-observe");

        Observable<String> observe = commandDemo.observe();

        // 阻塞式调用
//        String result = observe.toBlocking().single();
//
//        long endTime = System.currentTimeMillis();
//        System.out.println("result="+result+" , speeding="+(endTime-beginTime));


        // 非阻塞式调用
        observe.subscribe(new Subscriber<String>() {
            @Override
            public void onCompleted() {
                System.err.println("ObserveCommandTest-observe , onCompleted");
            }

            @Override
            public void onError(Throwable throwable) {
                System.err.println("ObserveCommandTest-observe , onError - throwable="+throwable);
            }

            @Override
            public void onNext(String result) {
                long endTime = System.currentTimeMillis();
                System.err.println("ObserveCommandTest-observe , onNext result="+result+" speend:"+(endTime - beginTime));
            }
        });

        Thread.sleep(1000l);
    }

}

8-11 两种命令的区别

image.png
Command不是主线程执行的,ObservableCommand使用的是主线程

8-12 GroupKey和CommandKey

image.png
image.png
image.png
image.png

8-13 请求缓存

image.png

8-14 请求缓存演示

8-15 请求合并介绍

8-16 请求合并对象建立

package com.imooc.hystrix.show.command;

import com.netflix.hystrix.*;
import org.assertj.core.util.Lists;

import java.util.Collection;
import java.util.Iterator;
import java.util.List;

/**
 * @author : jiangzh
 * @program : com.imooc.hystrix.show.command
 * @description : 请求合并处理对象
 **/
public class CommandCollapser extends HystrixCollapser<List<String>, String , Integer> {

    private Integer id;

    public CommandCollapser(Integer id){
        super(Setter
                .withCollapserKey(HystrixCollapserKey.Factory.asKey("CommandCollapser"))
                .andCollapserPropertiesDefaults(
                        HystrixCollapserProperties.defaultSetter()
                        .withTimerDelayInMilliseconds(1000)
                )
        );
        this.id = id;
    }
    /**
    * @Description: 获取请求参数
    * @Param: []
    * @return: java.lang.Integer
    * @Author: jiangzh
    */
    @Override
    public Integer getRequestArgument() {
        return id;
    }

    /**
    * @Description: 批量业务处理
    * @Param: [collection]
    * @return: com.netflix.hystrix.HystrixCommand<java.util.List<java.lang.String>>
    * @Author: jiangzh
    */
    @Override
    protected HystrixCommand<List<String>> createCommand(Collection<CollapsedRequest<String, Integer>> collection) {
        return new BatchCommand(collection);
    }

    /**
    * @Description: 批量处理结果与请求业务之间映射关系处理
    * @Param: [strings, collection]
    * @return: void
    * @Author: jiangzh
    */
    @Override
    protected void mapResponseToRequests(List<String> strings, Collection<CollapsedRequest<String, Integer>> collection) {
        int counts = 0;
        Iterator<HystrixCollapser.CollapsedRequest<String, Integer>> iterator = collection.iterator();
        while (iterator.hasNext()) {
            HystrixCollapser.CollapsedRequest<String, Integer> response = iterator.next();

            String result = strings.get(counts++);

            response.setResponse(result);
        }
    }
}


class BatchCommand extends HystrixCommand<List<String>>{

    private Collection<HystrixCollapser.CollapsedRequest<String, Integer>> collection;

    public BatchCommand(Collection<HystrixCollapser.CollapsedRequest<String, Integer>> collection){
        super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("BatchCommand")));
        this.collection = collection;
    }

    @Override
    protected List<String> run() throws Exception {
        System.err.println("currentThread : "+Thread.currentThread().getName());
        List<String> result = Lists.newArrayList();

        Iterator<HystrixCollapser.CollapsedRequest<String, Integer>> iterator = collection.iterator();
        while (iterator.hasNext()) {
            HystrixCollapser.CollapsedRequest<String, Integer> request = iterator.next();

            Integer reqParam = request.getArgument();

            // 具体业务逻辑
            result.add("Mooc req: "+ reqParam);
        }

        return result;
    }
}

8-17 请求合并演示

public class CollapserUnit {

    @Test
    public void collapserTest() throws ExecutionException, InterruptedException {
        HystrixRequestContext context = HystrixRequestContext.initializeContext();

        // 构建请求 -> 主要优化点,多个服务调用的屡次HTTP请求合并
        // 缺点:不多有机会对同一个服务进行屡次HTTP调用,同时还要足够的"近"
        CommandCollapser c1 = new CommandCollapser(1);
        CommandCollapser c2 = new CommandCollapser(2);
        CommandCollapser c3 = new CommandCollapser(3);
        CommandCollapser c4 = new CommandCollapser(4);

        // 获取结果, 足够的近 -> 10ms
        Future<String> q1 = c1.queue();
        Future<String> q2 = c2.queue();
        Future<String> q3 = c3.queue();
        Future<String> q4 = c4.queue();

        String r1 = q1.get();
        String r2 = q2.get();
        String r3 = q3.get();
        String r4 = q4.get();

        // 打印
        System.err.println(r1+" , "+r2+" , "+r3+" , "+r4);

        context.close();
    }


}

由于足够近因此合并为两次请求
image.png
增长请求间隔
image.png
image.png
设置不超过一秒的合并
image.png
image.png

8-18 Hystrix的隔离术

image.png
命名
image.png
image.png
信号量隔离就是一个排队的过程,能够理解为限流

image.png
image.png
image.png
image.png

8-19 Hystrix的隔离演示和差别的讲解

image.png
此时程序不会另起线程,而是在主线程中执行
image.png
image.png
image.png
image.png

8-20 Hystrix线程隔离参数解析和演示

8-21 Hystrix信号量隔离参数解析和演示

image.png

image.png

8-22 Hystrix快速失败和降级

8-23 Hystrix熔断器介绍 (10:39)

image.png

8-24 Hystrix熔断器演示 (11:21)

image.png

8-25 Hystrix两种应用场景介绍 (04:08)

image.png

8-26 Hystrix环境集成

8-27 Hystrix演示及fallback使用介绍 (13:40)

HystrixBadRequestException触发的异常

8-28 Hystrix监控讲解与演示 (09:51)

image.png

8-29 实战技巧:如何设置线程池 (07:48)

最重要的一点
image.png
2000rps 100台机器,平均20个rps每台。加上一部分冗余的值(0.3+0.8倍)
队列长度设置成线程池长度的0.5-1倍

8-30 Hystrix章节总结 (04:23)

第一步

HystrixCommand默认线程隔离
HystrixObservableCommand默认信号量隔离,同时以此能够执行多个命令

第二步

检查有没有缓存,请求合并,必定开启Hystrix上线文,请求足够的近

第三步

检查断路器是否开启,开启的话直接fallback
不然检查信号量和线程池

第四步

执行run和construct 失败的走fallback
有个特殊的状况是若是抛出的HystrixBadRequestException,不会触发fallback而直接抛出异常
若是执行成功,超时也会fallback
若是fallback成功的话会返回,失败的话抛出异常

9 Feign 最好的httpClient

9-1 Feign自我介绍 (09:26)

image.png
image.png
image.png

9-2 Feign环境准备 (08:22)

1 添加依赖 启动文件开启器注解
2 consumer中建立接口api
image.png
3 comsumer中建立控制器方法
image.png
image.png
image.png

9-3 Feign演示及Feign注解解析 (08:22)

image.png

9-4 Feign之HTTP注解介绍 (07:06)

在服务提供方providerController
image.png
consumer providerApi
image.png
consume controller
image.png
注意事项
在provider接口中@RequestParam必定要加上
image.png

9-5 HTTP注解演示及注意事项讲解 (03:57)

url进行测试
image.png
image.png

9-6 FeignClient参数讲解 (04:02)

image.png
image.png
image.png

9-7 FeignClient参数讲解之Primary (09:13)

image.png
经过动态代理的方式生成实现类
//若是接口指定实现类,则接口primary为false,实现类加注解Primary
image.png
image.png

9-8 Feign特性之Configuration (14:11)

image.png
image.png

image.png
image.png

9-9 Feign整合Ribbon (07:00)

image.png
image.png

9-10 Feign整合Hystrix (14:42)

image.png
Feign只要name改为服务名,则自动接入ribbon
Feign.Hystrix.enabled = true 整合Hystrix

image.png
方式1 实现降级
image.png
方式2 实现feign.hystrix.FallbackFactory工厂的方式
image.png
image.png

9-11 Feign项目调优-HTTPClient (06:54)

image.png
image.png
image.png
能提升QPS

9-12 Feign项目调优---解压缩 (05:20)

image.png
image.png

9-13 Feign实战技巧之继承讲解 (04:47)

image.png
不要采用多继承

9-14 Feign实战改造基础环境构建 (06:27)

9-15 Feign继承特性实现及开发技巧 (09:52)

9-16 Feign实战开发技巧讲解实现 (08:11)

建立api模块,并开启feign

image.png
其余的服务模块依赖该api模块

建立公共返回对象和查询接口

package com.mooc.meetingfilm.apis.film;

import com.mooc.meetingfilm.apis.film.vo.DescribeFilmRespVO;
import com.mooc.meetingfilm.utils.common.vo.BaseResponseVO;
import com.mooc.meetingfilm.utils.exception.CommonServiceException;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

/**
 * @author : jiangzh
 * @program : com.mooc.meetingfilm.apis.film
 * @description : Film提供的公共接口服务
 **/
public interface FilmFeignApis {

    /**
    * @Description: 对外暴露的接口服务
    * @Param: [filmId]
    * @return: com.mooc.meetingfilm.utils.common.vo.BaseResponseVO
    * @Author: jiangzh
    */
    @RequestMapping(value = "/films/{filmId}", method = RequestMethod.GET)
    BaseResponseVO<DescribeFilmRespVO> describeFilmById(@PathVariable("filmId") String filmId) throws CommonServiceException;

}
package com.mooc.meetingfilm.apis.film.vo;

import lombok.Data;

/**
 * @author : jiangzh
 * @program : com.mooc.meetingfilm.film.controller.vo
 * @description : 根据主键获取影片信息对象
 **/
@Data
public class DescribeFilmRespVO {

    private String filmId;
    private String filmName;
    private String filmLength;
    private String filmCats;
    private String actors;
    private String imgAddress;
    private String subAddress;


}

其余引用该api接口的处理

实现api的接口
package com.mooc.meetingfilm.hall.apis;

import com.mooc.meetingfilm.apis.film.FilmFeignApis;
import org.springframework.cloud.openfeign.FeignClient;

/**
 * @author : jiangzh
 * @program : com.mooc.meetingfilm.hall.apis
 * @description : film提供的接口服务
 **/
@FeignClient(name = "film-service")
public interface FilmFeignApi extends FilmFeignApis {

}
引入api下的返回对象
启动类下添加注解
package com.mooc.meetingfilm.hall;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.ComponentScan;

@ComponentScan(basePackages = {"com.mooc.meetingfilm"})
@MapperScan(basePackages = {"com.mooc.meetingfilm.hall.dao"})
@EnableFeignClients
@EnableDiscoveryClient
@SpringBootApplication
public class BackendHallApplication {

    public static void main(String[] args) {
        SpringApplication.run(BackendHallApplication.class, args);
    }

}
具体调用
@Resource
    private FilmFeignApi filmFeignApi;
    
        BaseResponseVO<DescribeFilmRespVO> baseResponseVO = filmFeignApi.describeFilmById(filmId);
        DescribeFilmRespVO filmResult = baseResponseVO.getData();
        if(filmResult ==null || ToolUtils.strIsNull(filmResult.getFilmId())){
            throw new CommonServiceException(404,"抱歉,未能找到对应的电影信息,filmId : "+filmId);
        }

9-17 Feign内容总结----如何体现出高逼格 (06:33)

10 ZUUL微服务网关

10-1 Zuul章节介绍 (01:14)

10-2 Zuul自我介绍 (04:57)

image.png
image.png

10-3 Zuul基础环境构建 (03:54)

10-4 Zuul基础使用演示 (09:58)

配置路由

10-5 Zuul ServiceId访问支持 (01:53)

image.png
不由止的状况下能够经过服务名代替配置的路径
image.png

10-6 Zuul请求表达式详解 (06:21)

image.png
或者省略其中的横杠
image.png

10-7 Zuul核心之Filter介绍 (08:24)

image.png

10-8 Zuul核心之自定义Filter (10:53)

10-9 Zuul核心之预约义Filter讲解及源码解析思路介绍 (06:15)

image.png
image.png
image.png

10-10 Zuul面试点之Zuul版本差别 (04:37)

ZUUL 典型的servlet加filter作的
image.png
阻塞式线程占用资源多,
一个请求进来,一个线程处理,阻塞式 并发量没有高
ZUUL2 NIO模型
image.png
image.png

10-11 Zuul面试点之Hystrix整合 (03:24)

10-12 Zuul面试点之Hystrix降级处理 (04:58)

10-13 Zuul面试点之Cookie和特殊头信息处理 (06:48)

10-14 Zuul与Meetingfilm整合 (06:15)

10-15 Zuul知识点梳理 (04:43)

11 微服务安全

11-1 服务安全章节介绍 (02:06)

image.png

11-2 JWT介绍 (11:27)

image.png
image.png

11-3 JWT颁发流程讲解 (07:27)

image.png

11-4 JWT验证开发演示 (10:43)

11-5 JWT验证演示 (05:34)

在zuul中加入JWTFilter验证token

package com.mooc.meetingfilm.apigwzuul.filters;

import com.alibaba.fastjson.JSONObject;
import com.mooc.meetingfilm.utils.common.vo.BaseResponseVO;
import com.mooc.meetingfilm.utils.properties.JwtProperties;
import com.mooc.meetingfilm.utils.util.JwtTokenUtil;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import io.jsonwebtoken.JwtException;
import lombok.extern.slf4j.Slf4j;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Enumeration;

/**
 * @author : jiangzh
 * @program : com.mooc.meetingfilm.apigwzuul.filters
 * @description :
 **/
@Slf4j
public class JWTFilter extends ZuulFilter {
    /**
    * @Description: Filter类型
    * @Param: []
    * @return: java.lang.String
    * @Author: jiangzh
    */
    @Override
    public String filterType() {
        return "pre";
    }

    /**
    * @Description: filter的执行顺序
    * @Param: []
    * @return: int
    * @Author: jiangzh
    */
    @Override
    public int filterOrder() {
        return 0;
    }

    /**
    * @Description: 是否要拦截
    * @Param: []
    * @return: boolean
    * @Author: jiangzh
    */
    @Override
    public boolean shouldFilter() {
        return true;
    }

    /**
    * @Description: Filter的具体业务逻辑
    * @Param: []
    * @return: java.lang.Object
    * @Author: jiangzh
    */
    @Override
    public Object run() throws ZuulException {
        // JWT工具类
        JwtTokenUtil jwtTokenUtil = new JwtTokenUtil();
        JwtProperties jwtProperties = JwtProperties.getJwtProperties();

        // ThreadLocal
        RequestContext ctx = RequestContext.getCurrentContext();
        // 获取当前请求和返回值
        HttpServletRequest request = ctx.getRequest();
        HttpServletResponse response = ctx.getResponse();

        // 提早设置请求继续,若是失败则修改此内容
        ctx.setSendZuulResponse(true);
        ctx.setResponseStatusCode(200);

        // 判断是不是登录,若是是登录则不验证JWT
        if (request.getServletPath().endsWith("/" + jwtProperties.getAuthPath())) {
            return null;
        }

        // 一、验证Token有效性 -> 用户是否登陆过
        final String requestHeader = request.getHeader(jwtProperties.getHeader());
        String authToken = null;
        // Bearer header.payload.sign
        if (requestHeader != null && requestHeader.startsWith("Bearer ")) {
            authToken = requestHeader.substring(7);

            //验证token是否过时,包含了验证jwt是否正确
            try {
                boolean flag = jwtTokenUtil.isTokenExpired(authToken);
                if (flag) {
                    renderJson(ctx , response, BaseResponseVO.noLogin());
                }else{
                    // 二、解析出JWT中的payload -> userid - randomkey
                    String randomkey = jwtTokenUtil.getMd5KeyFromToken(authToken);
                    String userId = jwtTokenUtil.getUsernameFromToken(authToken);
                    // 三、是否须要验签,以及验签的算法

                    // 四、判断userid是否有效
                    // TODO
                }
            } catch (JwtException e) {
                //有异常就是token解析失败
                renderJson(ctx ,response, BaseResponseVO.noLogin());
            }
        } else {
            //header没有带Bearer字段
            renderJson(ctx ,response, BaseResponseVO.noLogin());
        }

        return null;
    }


    /**
     * 渲染json对象
     */
    public static void renderJson(RequestContext ctx, HttpServletResponse response, Object jsonObject) {
        // 设置终止请求
        response.setHeader("Content-Type", "application/json;charset=UTF-8");
        ctx.setSendZuulResponse(false);
        ctx.setResponseBody(JSONObject.toJSONString(jsonObject));
    }

}
package com.mooc.meetingfilm.apigwzuul.config;

import com.mooc.meetingfilm.apigwzuul.filters.JWTFilter;
import com.mooc.meetingfilm.apigwzuul.filters.MyFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @author : jiangzh
 * @program : com.mooc.meetingfilm.apigwzuul.config
 * @description :
 **/
@Configuration
public class ZuulConfig {

    @Bean
    public MyFilter initMyFilter(){
        return new MyFilter();
    }

    @Bean
    public JWTFilter initJWTFilter(){
        return new JWTFilter();
    }
}

11-6 CORS跨域资源共享解决 (11:55)

image.png

11-7 Eureka Server安全问题介绍 (02:29)

image.png

11-8 Eureka Server整合SpringSecurity

image.png
Eureka下配置
SpringSecurityConfig配置类 排除CSRF的影响

package com.mooc.meetingfilm.eureka.conf;


import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

/**
 * @author : jiangzh
 * @program : com.mooc.meetingfilm.eureka.conf
 * @description : SpringSecurity配置
 **/
@EnableWebSecurity
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {

    /**
     * @Description: 对eureka注册的URL不进行CSRF防护
     * @Param: [http]
     * @return: void
     * @Author: jiangzh
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().ignoringAntMatchers("/eureka/**");
        super.configure(http);
    }

}

解决安全问题 (10:07)
依赖包:

<dependency>

<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-security</artifactId>

</dependency>

Eureka Server配置:
spring:
security:

user:
  name: jiangzh
  password: jiangzh123
  roles: SUPERUSER

Eureka URL修改成:
http://jiangzh:jiangzh123@localhost:8761/eureka/

TestNG自动化测试

12-1 微服务自动化测试介绍 (02:08)

12-2 自动化测试重要性 (06:12)

12-3 TestNG框架介绍及环境搭建 (07:55)

image.png

12-4 TestNG常见注解演示及讲解 (07:43)

image.png
注解test修改
image.png
这几种注解的执行顺序
若是有两个test的执行方法的话
image.png

12-5 TestNG测试报告生成 (10:56)

image.png

12-6 TestNG整合业务测试 (08:41)

建立可执行的xml文件
image.png
建立生成测试报告的模板ExtentTestNGIReporterListener.java
image.png

12-7 TestNG测试影片新增 (13:19)

12-8 TestNG测试影片列表 (07:16)

12-9 TestNG动态数据自动化测试 (06:43)

package com.mooc.meetingfilm.testng.films;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.mooc.meetingfilm.testng.common.RestUtils;
import com.mooc.meetingfilm.utils.common.vo.BaseResponseVO;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate;
import org.testng.Assert;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;

import java.util.List;

/**
 * @author : jiangzh
 * @program : com.mooc.meetingfilm.testng.films
 * @description :
 **/
@Slf4j
public class FilmsTest {


    @Test
    public void addFilm(){
        String url = "http://localhost:8401/films/film:add";
        FilmSavedReqVO filmSavedReqVO = new FilmSavedReqVO();
        filmSavedReqVO.setFilmStatus("1");
        filmSavedReqVO.setFilmName("SpringCloud Jiangzh讲的课程");
        filmSavedReqVO.setFilmEnName("SpringCloud");
        filmSavedReqVO.setMainImgAddress("/imgs/main.jpg");
        filmSavedReqVO.setFilmScore("10.0");
        filmSavedReqVO.setFilmScorers("123456");
        filmSavedReqVO.setPreSaleNum("50000");
        filmSavedReqVO.setBoxOffice("90000");
        filmSavedReqVO.setFilmTypeId("1");
        filmSavedReqVO.setFilmSourceId("1");
        filmSavedReqVO.setFilmCatIds("1");
        filmSavedReqVO.setAreaId("1");
        filmSavedReqVO.setDateId("1");
        filmSavedReqVO.setFilmTime("2025-12-11");
        filmSavedReqVO.setDirectorId("1");
        filmSavedReqVO.setActIds("1,2");
        filmSavedReqVO.setRoleNames("管理员,实习");
        filmSavedReqVO.setFilmLength("20");
        filmSavedReqVO.setBiography("SpringCloud Jiangzh讲的课程");
        filmSavedReqVO.setFilmImgs("/imgs/1.jpg,/imgs/2.jpg,/imgs/3.jpg,/imgs/4.jpg,/imgs/5.jpg");

        RestTemplate restTemplate = RestUtils.getRestTemplate();
        ResponseEntity<BaseResponseVO> baseresponse
                = restTemplate.postForEntity(url, filmSavedReqVO, BaseResponseVO.class);
        log.info("addFilm baseresponse : {}", baseresponse);
        // 验证返回值的Code是否是200
        BaseResponseVO body = baseresponse.getBody();
        Integer code = new Integer(200);

        // 第一道拦截
        Assert.assertEquals(code, body.getCode());
    }


    //dataProvider有几回,下边测试方法就会执行几回,而且将数据经过该测试方法当作参数传入
    @Test(dataProvider = "filmsDataProvider")
    public void films(String filmsName, int expectCounts) {
        String uri = "http://localhost:8401/films";
        RestTemplate restTemplate = RestUtils.getRestTemplate();
        String response
                = restTemplate.getForObject(uri, String.class);
        log.info("response : {}", response);
        JSONObject result = JSONObject.parseObject(response);
        // 数量是否大于1

        // 名字与插入的内容是否相同
        JSONObject data = result.getJSONObject("data");
        JSONArray films = data.getJSONArray("films");

        // 成功计数器
        int count = 0;

        List<DescribeFilmsRespVO> describeFilmsRespVOS = films.toJavaList(DescribeFilmsRespVO.class);
        for(DescribeFilmsRespVO vo : describeFilmsRespVOS){
            if(vo.getFilmEnName().equals(filmsName)){
                count ++ ;
            }
        }

        log.info("count : {}", count);
        //判断count和expectCount
        Assert.assertEquals(count, expectCounts);
    }

    //注入动态数据
    @DataProvider(name = "filmsDataProvider")
    public Object[][] filmsDataProvider(){
        Object[][] objects = new Object[][]{
                {"SpringCloud", 1},//filmName,expectCount~~~~
                {"SpringCloud2", 0}
        };

        return objects;
    }


    @Data
    public static class DescribeFilmsRespVO{

        private String filmId;
        private String filmStatus;
        private String filmName;
        private String filmEnName;
        private String filmScore;
        private String preSaleNum;
        private String boxOffice;
        private String filmTime;
        private String filmLength;
        private String mainImg;

    }

    @Data
    public static class FilmSavedReqVO{
        private String filmStatus;
        private String filmName;
        private String filmEnName;
        private String mainImgAddress;
        private String filmScore;
        private String filmScorers;
        private String preSaleNum;
        private String boxOffice;
        private String filmTypeId;
        private String filmSourceId;
        private String filmCatIds;
        private String areaId;
        private String dateId;
        private String filmTime;
        private String directorId;
        private String actIds;      // actIds与RoleNames是否是能在数量上对应上
        private String roleNames;
        private String filmLength;
        private String biography;
        private String filmImgs;
    }

}

13 Docker入门

13-1 Docker章节介绍 (01:33)

13-2 Docker自我介绍 (07:33)

image.png

13-3 Window下Docker环境安装 (05:09)

必定要作的设置改为linux的环境
image.png
设置镜像加速
image.png

13-4 Docker基础概念介绍 (04:33)

13-5 Dockerfile讲解及演示 (13:38)

image.png
这次的基础镜像是centos
Dockerfile详解

#基础镜像
FROM centos:7.1.1503

MAINTAINER jiangzheng "coding-jiangzh@qq.com"

#定义环境变量 编码utf8
ENV LANG zh_CN.utf-8
#定义帐户
USER root

#######################################################
#建立文件夹
RUN mkdir -p /home/jiangzh/env /home/jiangzh/workspace /home/jiangzh/bin

#复制java并解压
ADD ./jdk-8u181-linux-x64.tar.gz /home/jiangzh/env/jdk
#复制Eureka.jar
COPY ./backend-eureka-server.jar /home/jiangzh/workspace/
#复制启动执行sh
COPY ./entrypoint.sh /home/jiangzh/bin/

#######################################################
#定义jdk的环境变量
ENV JAVA_HOME /home/jiangzh/env/jdk/jdk1.8.0_181

#初始化后进入的目录
WORKDIR /home/jiangzh

#对外暴露的端口
EXPOSE 8761

#把JAVA_HOME的配置文件加到path下才能生效
ENV PATH /home/jiangzh:$JAVA_HOME/bin:$PATH
#添加.sh的可执行权限
RUN chmod a+x bin/*.sh
#在docker启动的时候执行的命令,表示相对目录
ENTRYPOINT ["bin/entrypoint.sh"]

entrypoint.sh

#!/bin/sh

#定义环境变量
export SHELL_BASE=/home/jiangzh/sbin

#进入工做目录
cd /home/jiangzh/workspace

## start eureka service
nohup java  -Dfile.encoding="UTF-8" -jar /home/jiangzh/workspace/backend-eureka-server.jar &

#死循环防止docker这个进程退出
for (( ; ; ))
do
  sleep 5
done

13-6 Docker常见命令介绍及使用演示 (10:01)

docker操做必须是root帐户或者同等权限

#Docker构建须要寻找到dockerfile
docker build -t meetingfilm-backend:1.0 .

#查看全部的Docker 镜像列表
docker images

#启动Docker容器
前置准备: meetingfilm-backend:1.0的镜像
docker run -itd -p 8761:8761 meetingfilm-backend:1.0

#查看已经运行的Docker容器列表
docker ps -a

#中止docker容器
docker stop <CONTAINER ID>

image.png

13-7 Docker基本使用总结 (02:52)

image.png
dockerfile就是一个文件,构建镜像前必须有dockerfile

14 微服务部署

14-8 项目总体结构图

image.png

image.png

14-9 docker安装mysql

#拉取msyql镜像
docker pull mysql:5.7

#查看镜像是否存在
docker images | grep mysql

#运行msyql容器
docker run -itd --name jiangzh_mysql -p 3306:3306 -e MYSQL_ROOT_PASSWORD=123456 mysql:5.7

#进入mysql容器
docker exec -it jiangzh_mysql /bin/bash

#登陆mysql
mysql -uroot -p123456

#修改mysql远程访问权限
GRANT ALL ON *.* TO 'root'@'%';

#刷新权限flush privileges

#修改加密方式
ALTER USER 'root'@'localhost' IDENTIFIED BY 'password' PASSWORD EXPIRE NEVER;

ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '123456';

#再次刷新权限
flush privileges;

14-10 docker安装nginx

下载Nginx

sudo docker pull nginx

image.png

查看Nginx下载是否完成

sudo docker images |grep nginx

image.png

建立必要目录

sudo mkdir -p /opt/install/nginx/conf
sudo mkdir -p /opt/install/nginx/conf/vhost
sudo mkdir -p /opt/install/nginx/logs

三个目录随便写,主要做用以下:

  • conf目录放置Nginx默认配置文件
  • conf/vhost目录放置Nginx引入配置文件【子配置文件】
  • logs目录放置Nginx日志文件

建立Nginx配置文件

在/opt/install/nginx/conf目录中建立nginx.conf,做为Nginx默认配置文件,大概内容以下:

user  root;
worker_processes  1;
error_log /var/log/nginx/error.log;
pid /run/nginx.pid;

include /usr/share/nginx/modules/*.conf;

events {
    worker_connections  1024;
}


http {
    include       /etc/nginx/mime.types;
    include       /etc/nginx/vhost/*.conf;
    
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    server {
        listen       80;
        server_name localhost;


        location / {
            root html;
            index index.html index.htm;
        }

        error_page    500 502 503 504 /50x.html;
        location = /50x.html    {
                root    html;
        }

    }

}

建立一个测试引入配置文件【子配置文件】

建立一个引入配置文件,来测试include是否可用,目录写在/opt/install/nginx/conf/vhost

server {

    listen 80;

    autoindex on;

    server_name jiangzh.jd.com;

    access_log /var/log/nginx/access.log combined;

    index index.html index.htm index.jsp index.php;
    
    if ( $query_string ~* ".*[;'<>].*" ){
        return 404;
    }

    location / {

        proxy_pass https://www.jd.com;

        if ($request_method = 'OPTIONS') {
            add_header 'Access-Control-Allow-Origin' '*';
            add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
            add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization';
            add_header 'Access-Control-Max-Age' 1728000;
            add_header 'Content-Type' 'text/plain; charset=utf-8';
            add_header 'Content-Length' 0;
            
            return 204;
        }
        
        if ($request_method = 'POST') {
            add_header 'Access-Control-Allow-Origin' '*';
            add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
            add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization';
            add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';
        }
        
        if ($request_method = 'GET') {
            add_header 'Access-Control-Allow-Origin' '*';
            add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
            add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization';
            add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';
        }
    }

}

启动Nginx容器

sudo docker run -itd --name jiangzh-nginx -p 80:80 
-v /opt/install/nginx/conf/nginx.conf:/etc/nginx/nginx.conf 
-v /opt/install/nginx/logs:/var/log/nginx 
-v /opt/install/nginx/conf/vhost:/etc/nginx/vhost nginx

查看Nginx容器启动是否成功

sudo docker ps -a

image.png

将本地建立的几个目录在启动时候挂载在Nginx的Docker容器上,达到外部配置文件引入的目标,命令以下:

14-11多环境的解决方案

image.png

14-12

构建镜像

将文件夹上传到linux服务器
cd 到console目录
image.png
docker build -t film-console:1.0 .

执行建立容器脚本

sh start.sh

进入容器

docker -exec -it film-console bash

查看进程

image.png

15 Spring Cloud GateWay

16 项目上线

日志输出配置
https://blog.csdn.net/lhl1124...

相关文章
相关标签/搜索