貓眼後端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...

相關文章
相關標籤/搜索