springcloud(Fdeign+Zuul)入门教程(二)

10 篇文章 0 订阅

1.回顾代码

1.1整体架构

在这里插入图片描述

1.2父工程

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>cn.itcast</groupId>
    <artifactId>cloud-demo</artifactId>
    <packaging>pom</packaging>
    <version>1.0-SNAPSHOT</version>
    <modules>
        <module>user-service</module>
        <module>consumer-demo</module>
        <module>eureka-server</module>
    </modules>


    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.4.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
        <spring-cloud.version>Finchley.SR1</spring-cloud.version>
        <mapper.starter.version>2.0.3</mapper.starter.version>
        <mysql.version>5.1.32</mysql.version>
        <pageHelper.starter.version>1.2.5</pageHelper.starter.version>
    </properties>
    <!-- dependency管理器中所有子工程都有该依赖-->
    <dependencyManagement>
        <dependencies>
            <!--springcloud版本依赖项目-->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <!--通用mapper启动器 -->
            <dependency>
                <groupId>tk.mybatis</groupId>
                <artifactId>mapper-spring-boot-starter</artifactId>
                <version>${mapper.starter.version}</version>
            </dependency>
            <!-- mysql驱动-->
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>${mysql.version}</version>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

1.3user-service

在这里插入图片描述
pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>cloud-demo</artifactId>
        <groupId>cn.itcast</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>user-service</artifactId>

    <dependencies>
        <!-- Spring工程的web启动器-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- mysql-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>tk.mybatis</groupId>
            <artifactId>mapper-spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
    </dependencies>


</project>

cn.itcast.UserApplication

package cn.itcast;


import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import tk.mybatis.spring.annotation.MapperScan;

@EnableDiscoveryClient
@SpringBootApplication
@MapperScan("cn.itcast.user.mapper")
public class UserApplication {
    public static void main(String[] args) {
        SpringApplication.run(UserApplication.class);
    }

}

cn.itcast.pojo.User

package cn.itcast.user.pojo;

import lombok.Data;
import tk.mybatis.mapper.annotation.KeySql;

import javax.persistence.Id;
import javax.persistence.Table;
import java.util.Date;

@Data
@Table(name="tb_user")
public class User {
    //id
    @Id
    @KeySql(useGeneratedKeys = true)
    private Long id;
    //用户名
//    @Column(name = "username")
    private String username;
    //密码
    private String password;
    private String phone;
    private Date created;

}

cn.itcast.mapper.UserMapper

package cn.itcast.user.mapper;

import cn.itcast.user.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import tk.mybatis.mapper.common.Mapper;

public interface UserMapper extends Mapper<User> {
}

cn.itcast.service.UserService

package cn.itcast.user.service;

import cn.itcast.user.mapper.UserMapper;
import cn.itcast.user.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class UserService {
    @Autowired
    private UserMapper userMapper;
    public User queryById(Long id){
        /*try {
            Thread.sleep(2000l);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }*/
        return userMapper.selectByPrimaryKey(id);
    }

}

cn.itcast.controller.UserController

package cn.itcast.user.controller;

import cn.itcast.user.pojo.User;
import cn.itcast.user.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("user")
public class Controller {
    @Autowired
    private UserService userService;

    //@PathVariable是spring3.0的一个新功能:接收请求路径中占位符的值
    @GetMapping("/{id}")
    public User queryById(@PathVariable("id") Long id){
        return userService.queryById(id);
    }
}

application.yml

server:
  port: 8081
spring:
  application:
    name: user-service
  datasource:
      driver-class-name: com.mysql.jdbc.Driver
      url: jdbc:mysql://localhost/yum6
      username: root
      password: 123456
mybatis:
  type-aliases-package: cn.itcast.user.pojo

eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:10086/eureka,http://127.0.0.1:10087/eureka
  instance:
    prefer-ip-address: true
    ip-address: 127.0.0.1
    #每隔30s发送一次心跳
    lease-renewal-interval-in-seconds: 30
    #最小过期时长,每隔30秒发一次心跳,如果隔了90s还没有发送心跳,就挂了
    lease-expiration-duration-in-seconds: 90

1.4consumer-server

在这里插入图片描述
pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>cloud-demo</artifactId>
        <groupId>cn.itcast</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>consumer-demo</artifactId>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
        </dependency>
    </dependencies>
</project>

cn.itcast.ConsumerApplication

package cn.itcast;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.SpringCloudApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
//启动类
//@EnableCircuitBreaker
//@EnableDiscoveryClient
//@SpringBootApplication
@SpringCloudApplication
public class ConsumerApplication {
    //需要调用,所以需要这个对象
    @Bean
    @LoadBalanced
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }
    public static void main(String[] args) {
        SpringApplication.run(ConsumerApplication.class,args);
    }
}

cn.itcast.consumer.pojo.user

package cn.itcast.consumer.pojo;

import lombok.Data;
import java.util.Date;

@Data
public class User {
    //id
    private Long id;
    //用户名
//    @Column(name = "username")
    private String username;
    //密码
    private String password;
    private String phone;
    private Date created;

}

cn.itcast.consumer.web.ConsumerController

package cn.itcast.consumer.web;

import com.netflix.hystrix.contrib.javanica.annotation.DefaultProperties;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import java.util.List;

@RestController
@RequestMapping("consumer")
//该类中所有方法默认失败时调用方法
@DefaultProperties(defaultFallback = "queryByIdFallback")
public class ConsumerController {
    @Autowired
    private RestTemplate restTemplate;

    @GetMapping("{id}")
    public String queryById(@PathVariable("id") Long id){
        //user-service是服务id
        String url = "http://user-service/user/"+id;
        //返回的对象转成json字符串
        String user = restTemplate.getForObject(url, String.class);
        return user;
    }
   /* //集群+失败处理=最终方案
    //启用降级和线程隔离,一旦失败,会去找@DefaultProperties中配置的方法
    @HystrixCommand(commandProperties = {
            @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "3000"),
            //熔断器关闭后,失败次数
            @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold",value = "10"),
            //熔断器关闭后,休眠时间
            @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds",value = "10000"),

    })
    @GetMapping("{id}")
    public String queryById(@PathVariable("id") Long id){
        //模拟熔断环境测试,id为偶数即请求失败,为奇数就成功
        if(id%2 == 0){
            throw new RuntimeException("");
        }
        //user-service是服务id
        String url = "http://user-service/user/"+id;
        //返回的对象转成json字符串
        String user = restTemplate.getForObject(url, String.class);
        return user;
    }*/

    public String queryByIdFallback(){
        return "不好意思,服务器太拥挤了!";
    }
  /*  //方法一:
    //集群+失败处理
    //失败容错的指令,即成功时使用该方法,失败时调用注解中参数指的方法。要求失败和成功时的两个方法返回值和参数完全一样
    @HystrixCommand(fallbackMethod = "queryByIdFallback" )
    @GetMapping("{id}")
    public String queryById(@PathVariable("id") Long id){
        //user-service是服务id
        String url = "http://user-service/user/"+id;
        //返回的对象转成json字符串
        String user = restTemplate.getForObject(url, String.class);
        return user;
    }*/

    /*//最终方案
    @GetMapping("{id}")
    public User queryById(@PathVariable("id") Long id){
        //user-service是服务id
        String url = "http://user-service/user/"+id;
        User user = restTemplate.getForObject(url, User.class);
        return user;
    }*/

   /* 方法一:
    @Autowired
    private RibbonLoadBalancerClient client;

    //修改后
    @GetMapping("{id}")
    public User queryById(@PathVariable("id") Long id){

        //根据服务id获取实例,该方法帮我们实现了负载均衡,默认是轮询。
        ServiceInstance instance = client.choose("user-service");
          ...
        User user = restTemplate.getForObject(baseUrl, User.class);
        return user;
    }
    */

/*    修改前
    @Autowired
    private DiscoveryClient discoveryClient;
    @GetMapping("{id}")
    public User queryById(@PathVariable("id") Long id){
        //根据服务id获取实例
        List<ServiceInstance> instances = discoveryClient.getInstances("user-service");
        // 因为只有一个UserService,因此我们直接get(0)获取
        ServiceInstance instance = instances.get(0);
        //从实例中取出ip和端口
        String url = instance.getHost();
        int port = instance.getPort();
        String baseUrl = "http://"+instance.getHost() + ":" + instance.getPort()+"/user/"+id;
        System.out.println(baseUrl);
        User user = restTemplate.getForObject(baseUrl, User.class);
        return user;
    }*/


}

application.yml

server:
  port: 8088
spring:
  application:
    name: consumer-service
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:10086/eureka,http://127.0.0.1:10087/eureka
   #要不要拉取服务
    fetch-registry: true
    #拉取周期
    registry-fetch-interval-seconds: 3
user-service:
  ribbon:
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
           timeoutInMilliseconds: 3000

1.5eureka-server

在这里插入图片描述
pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>cloud-demo</artifactId>
        <groupId>cn.itcast</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>eureka-server</artifactId>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>
    </dependencies>
</project>

cn.itcast.EurekaServer

package cn.itcast;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

@EnableEurekaServer
@SpringBootApplication
public class EurekaServer {
    public static void main(String[] args) {
        SpringApplication.run(EurekaServer.class);
    }
}

application.yml

server:
  port: 10086
#服务名
spring:
  application:
    name: eureka-server
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:10086/eureka
    register-with-eureka: false
  instance:
    prefer-ip-address: true
    ip-address: 127.0.0.1
  server:
    eviction-interval-timer-in-ms: 300000
    enable-self-preservation: false

2.Feign

由于调用者中的url是:String url = “http://user-service/user/”+id;其中写死了user。
feign可以把正常的Rest请求隐藏起来,让别人看不到,让用户以为是本地请求。伪装成类似SpringMVC的Controller一样。
在这里插入图片描述
分析:
想要写这些代码,需要知道四个信息

  • 请求url
  • 请求参数
  • 请求类型
  • 返回结果
    在这里插入图片描述
    这上述这段代码中都有这四个信息。
    feign利用SpringMVC的注解来取识别请求的信息,从而帮助你自动进行远程调用,而不需要自己去写。

2.1快速入门

2.1.1引入依赖

consumer-service的pom.xml中加入依赖:

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

早期版本不叫openfeign,而是feign。

2.1.2注入注解启用feign

在ConsumerApplication中加入注解@EnableFeignClients

package cn.itcast;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.SpringCloudApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
//启动类
//@EnableCircuitBreaker
//@EnableDiscoveryClient
//@SpringBootApplication
@EnableFeignClients
@SpringCloudApplication
public class ConsumerApplication {
    //需要调用,所以需要这个对象
    @Bean
    @LoadBalanced
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }
    public static void main(String[] args) {
        SpringApplication.run(ConsumerApplication.class,args);
    }
}

2.1.3配置

不需要配置,他是通过springMVC的注解得知那些信息。

2.1.4编写代码

包:cn.itcast.consumer.client
接口:UserClient

package cn.itcast.consumer.client;

import cn.itcast.consumer.pojo.User;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
@FeignClient("user-service")
public interface UserClient {
    @GetMapping("user/{id}")
    User queryById(@PathVariable("id")Long id);
    
}

2.1.5修改ConsumerController

package cn.itcast.consumer.web;

import cn.itcast.consumer.client.UserClient;
import cn.itcast.consumer.pojo.User;
import com.netflix.hystrix.contrib.javanica.annotation.DefaultProperties;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import java.util.List;

@RestController
@RequestMapping("consumer")
//该类中所有方法默认失败时调用方法
@DefaultProperties(defaultFallback = "queryByIdFallback")
public class ConsumerController {
    @Autowired
    private UserClient userClient;
    //集群+熔断+feign
    @GetMapping("{id}")
    public User queryById(@PathVariable("id") Long id){
        return userClient.queryById(id);
    }
    public String queryByIdFallback(){
        return "不好意思,服务器太拥挤了!";
    }

2.1负载均衡

feign底层已经依赖的ribbon,使用方法和之间讲解的一样。

超时时长

l连接超时时长默认是1s。
读取超时时长也默认是1s。

ribbon:
  ConnectionTimeOut: 500
  ReadTimeOut: 100

2.2hystix支持

feign底层已经依赖的ribbon、hystrix。但是他没有走spring的熔断,所以使用方法跟之前不太一样。
hystri在feign中默认是关闭的,开启需要配置。

2.1.1开启

feign:
  hystrix:
    enabled: true

2.1.2注解

在启动类上加上@SpringCloudApplication

package cn.itcast;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.SpringCloudApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
//启动类
//@EnableCircuitBreaker
//@EnableDiscoveryClient
//@SpringBootApplication
@EnableFeignClients
@SpringCloudApplication
public class ConsumerApplication {
    //需要调用,所以需要这个对象
    @Bean
    @LoadBalanced
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }
    public static void main(String[] args) {
        SpringApplication.run(ConsumerApplication.class,args);
    }
}

2.1.3在接口上加上熔断,并写一个实现类去实现它

熔断需要自己去配,需要写一个类来做对于feign的熔断配置,这个类必须实现写的UserClient。

package cn.itcast.consumer.client;

        import cn.itcast.consumer.pojo.User;
        import org.springframework.cloud.openfeign.FeignClient;
        import org.springframework.web.bind.annotation.GetMapping;
        import org.springframework.web.bind.annotation.PathVariable;
@FeignClient(value = "user-service",fallback = UserClientFallBack.class)
public interface UserClient {
    @GetMapping("user/{id}")
    User queryById(@PathVariable("id")Long id);

}

注意:
这段代码中最重要的就是
@FeignClient(value = “user-service”,fallback = UserClientFallBack.class)
表示熔断器,其中请求失败,熔断器关闭进入休眠时间时,请求访问进入fallback指代的参数方法,执行该方法。

package cn.itcast.consumer.client;

import cn.itcast.consumer.pojo.User;
import org.springframework.stereotype.Component;

//熔断接口的实现类,在这里面写熔断逻辑,并将其注解到Spring里
@Component
class UserClientFallBack implements UserClient {
    @Override
    public User queryById(Long id) {
        User users = new User();
        users.setUsername("未知用户!");
        return users;
    }
}

注意:
该方法就是请求失败,熔断器关闭进入休眠时间时,请求访问时执行该方法。

测试:
在这里插入图片描述

2.4请求压缩

适应于请求比较大,比如文件的上传下载请求。
通过以下参数开启请求与相应的压缩功能:
feign:
compression:
request:
enable:true
response:
enable:true
同时,我们也可以对请求的数据类型,以及触发压缩的大小下限进行设置:
feign:
compression:
request:
enable:true
mime-type: text/html,application/xml,application/json #设置压缩的数据类型
min-request-size:2048 #设置触发压缩的大小下限

3.Zuul网关

3.1简介

在微服务架构中,Zuul就是守门的大Boss。
在这里插入图片描述

3.2Zuul加入后的架构

在这里插入图片描述
不管是来自客户端的请求还是服务内部调用,一切对服务的请求都会经过Zuul这个网关,然后再由网关来实现鉴权、动态路由等操作。Zuul就是我们服务的统一入口。
要想保证服务不挂,还要限流:限制用户的流量。

3.3快速入门

3.3.1新建工程

在这里插入图片描述

3.3.2加入依赖

<dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
        </dependency>
    </dependencies>

3.3.3编写启动类并加上注解

包:cn.itcast
类:GatewayApplication
注解:@EnableZuulProxy

package cn.itcast;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;

@EnableZuulProxy
@SpringBootApplication
public class GatewayApplication {
    public static void main(String[] args) {
        SpringApplication.run(GatewayApplication.class);

    }
}

3.3.4编写配置

zuul要有路由转发功能,就必须确切的知道每个请求所对应的是那个微服务。即路由规则,需要人为配置。

server:
  port: 10010
zuul:
  #路由规则,是map结构,key就是路由id,只要不重复就可以,value
  routes:
    hehe:
      #匹配路径
      path: /user-service/**
      #匹配路径映射的实际路径
      url: http://127.0.0.1:8081

path是访问路径中的匹配路径,只要路径中有匹配路径就映射到url这个实际路径。但是要注意,匹配路径只是用来匹配的,url才是实际路径,所以访问路径中完整路径为匹配路径+所要访问的controller路径。

3.3.5测试

在这里插入图片描述
上面出现一个问题就是映射路径写死了。
所以将来如果一个服务是集群,就出现问题。
所以在配置的时候,不应该是面向url的配置,应该是面向服务的配置。

3.4面向服务的

优化

3.4.1加入依赖

<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

完整的pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>cloud-demo</artifactId>
        <groupId>cn.itcast</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>gateway</artifactId>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
    </dependencies>
</project>

3.4.2修改配置

server:
  port: 10010
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1/10086/eureka
zuul:
  #路由规则,是map结构,key就是路由id,只要不重复就可以,value
  routes:
  #路由id
    user-service:
      #匹配路径
      path: /user-service/**
      #映射到服务id
      serviceId: user-service

当用户请求到匹配路径,会映射到serviceid对应的服务,但是这个服务没有具体的ip地址,其底层就会调用eureka去拉取服务列表,利用负载均衡算法,动态的获取服务的ip地址。
在zuul的底层已经引入了Ribbon。所以已经实现了负载均衡。

3.4.3测试

在这里插入图片描述

3.4.4默认路由规则

简化配置
key=服务id,value=映射路径

server:
  port: 10010
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1/10086/eureka
zuul:
  #路由规则,是map结构,key就是路由id,只要不重复就可以,value
  routes:
    user-service: /user-service/**

在这里插入图片描述

3.4.5路由前缀

在这里插入图片描述
在配置文件中并没有配置consumer-service,但是还是可以访问。

因为该配置方法其实zuul已经默认配置了,它通过eureka,拉取服务,将eureka上的所有服务都默认配置成该规则。所以不需要配置也可以实现路由转发。
但是默认服务存在问题:

  • 他将所有服务都暴露了。
  • 服务匹配url过于复杂。
  • 匹配路径和真实路径重复,eg:/user/user/9,user重复。

解决前两个问题的办法:

zuul:
  routes:
    #默认匹配url是/user-service/**,简化匹配路径url。
    user-service: /user/**
    #配置不想暴露的服务
  ignored-services:
    - consumer-service

此时再访问:
在这里插入图片描述
解决这三个问题的办法:

zuul:
  routes:
    user-service:
      #匹配路径
      path: /user/**
      #映射到服务id
      serviceId: user-service
      strip-prefix: false
  #配置不想暴露的服务
  ignored-services:
    - consumer-service

区分全局配置去除前缀和在配置单个服务时的去除前缀。

server:
  port: 10010
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:10086/eureka
spring:
  application:
    name: gateway
zuul:
  routes:
    #默认匹配url是/user-service/**,简化匹配路径url。
    user-service:
      path: /user/**
      servicedId: user-service
        #去除前缀,此时只针对于user-service
      strip-prefix: false
  #全局配置不想暴露的服务
  ignored-services:
    - consumer-service
  prefix: /api
  #全局配置去除前缀,此时针对的是prefix,不加该配置的话,访问就应该是、spi/匹配路径/真实url
  strip-prefix: false


在这里插入图片描述

3.5过滤器

zuul作为网关的其中一个重要功能,就是实现请求的鉴权。而这个动作我们往往是通过zuul提供的过滤器来实现的。

3.5.1ZuulFilter

在这里插入图片描述
ZuulFilter是过滤器的顶级父类,其中最重要的方法:

		//过滤器类型:pre-请求在被路由之前执行;routing-在路由请求时调用;post-在routing和error过滤器之后调用;error-处理请求时发生错误调用。
        public String filterType() ;
        //过滤器优先级。数字越小优先级越高。
        public int filterOrder();
         //要不要过滤
        public boolean shouldFilter();
        //过滤逻辑,过滤器的具体业务逻辑。
        public Object run() throws ZuulException;

3.5.2过滤器执行生命周期

在这里插入图片描述
正常流程:
请求到达首先会经过pre类型过滤器,而后到达routin类型,进行路由,请求就到达真正的服务提供者,执行请求,返回结果后,会到达post过滤器,而后返回相应。

异常流程:

  • 整个过程中,pre或者routing过滤器出现异常,都会直接进入error过滤器,再error处理完毕后,会将请求交给post过滤器,最后返回给用户。
  • 如果是error过滤器自己出现异常,最终会进入post过滤器,而后返回。
  • 如果是post过滤器出现异常,会跳转到error过滤器,但是与pre和routing不同时,请求不会再到达post过滤器了。

3.5.3使用场景

  • 请求鉴权:一般放在pre类型,如果发现没有访问权限,就直接拦截了。
  • 异常处理:一般会放在error类型和post类型过滤器中结合来处理。
  • 服务调用时长统计:pre和post结合使用。

3.5.4自定义过滤器

模拟一个登陆的校验。
基本逻辑:如果请求中有access-token参数,则认为请求有效,放行。

<dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
</dependency>
package cn.itcast.filter;

import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.apache.commons.lang.StringUtils;
import org.apache.http.HttpStatus;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import javax.servlet.http.HttpServletRequest;

public class LoginFilter extends ZuulFilter {
    @Override
    public String filterType() {
        return FilterConstants.PRE_TYPE;
    }

    @Override
    public int filterOrder() {
        return FilterConstants.PRE_DECORATION_FILTER_ORDER -1;
    }

    @Override
    public boolean shouldFilter() {
        return true;
    }

    @Override
    public Object run() throws ZuulException {
        //获取请求上下文
        RequestContext rxt = RequestContext.getCurrentContext();
        //获取request
        HttpServletRequest request = rxt.getRequest();
        //获取请求参数access-token
        String token = request.getParameter("access-token");
        //判断是否存在
        if(StringUtils.isBlank(token)){
            //不存在,未登陆,就拦截
            rxt.setSendZuulResponse((false));
            //状态码403
            rxt.setResponseStatusCode(HttpStatus.SC_FORBIDDEN);
        }
        return null;
    }
}

3.4负载均衡和熔断

Zuul中默认已经继承了Ribbon负载均衡和Hystrix熔断机制,但是所有的超时策略都是走的默认值,比如熔断超时时间只有1.5s,就很容触发了,因为我们建议手动配置。

hystrix:
  command:
    default:
      execution:
        islocation:
          thread:
            timeoutInMilliseconds: 6000
ribbon:
  ConnectionTimeout: 500
  ReadTimeout: 2000

Ribbon的超时时长真实值是(read+connection)*2,必须小于hystrix的超时时长。

3.5Zuul的高可用

启动多个Zuul服务就可以了,他会自动高可用。
当启动多个时,它会自动注册到Eureka,形成集群。如果是服务内部访问,你访问Zuul,自动负载均衡,没问题。但是Zuul更多是外部访问,用户他们无法通过Eureka进行负载均衡。此时,我们会使用其他网关,来对Zuul进行代理,比如Nginx。

spring-cloud-config:统一配置中心,自动去git拉取最新的配置,缓存。使用Git的Webhook钩子,去通知配置中心,说配置发生了变化没配置中心会通过消息总线去通知所有的微服务,更新配置。
spring-cloud-bus:消息配置总线。
spring-cloud-stream:消息通信
spring-cloud-hystrix-dashboard:容错统计,形成图形化界面
spring-cloud-sleuth:链路追踪,结合Zipkin。
他是一个独立的微服务。config-server。可以把配置都提交到git中,其中公用的部分

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值