SpringCloud学习

Eureka服务注册与发现

搭建父工程
父工程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>com.cc</groupId>
    <artifactId>springcloud</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>pom</packaging>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.4.RELEASE</version>
        <relativePath/>
    </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>Hoxton.SR1</spring-cloud.version>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <modules>
        <module>eureka-server</module>
        <module>eureka-client</module>
    </modules>

</project>

创建子工程Module,命名eureka-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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.cc</groupId>
        <artifactId>springcloud</artifactId>
        <version>1.0-SNAPSHOT</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <groupId>com.cc</groupId>
    <artifactId>eureka-server</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>eureka-server</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

配置application.properties

server.port=9000
#提交IP信息
eureka.instance.prefer-ip-address=true
eureka.instance.hostname=localhost
#设置这两个防止注册自己
eureka.client.register-with-eureka=false
eureka.client.fetch-registry=false
eureka.client.service-url.defaultZone=http://${eureka.instance.hostname}:${server.port}/eureka/

启动类上加上@EnableEurekaServer注解开启服务

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

启动项目访问http://localhost:9000
在这里插入图片描述
配置Eureka客户端

<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.cc</groupId>
        <artifactId>springcloud</artifactId>
        <version>1.0-SNAPSHOT</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <artifactId>eureka-client</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>eureka-client</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-netflix-eureka-client -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
            <version>2.2.1.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

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

application.properties

#服务注册的地址
#eureka.instance.prefer-ip-address=true
eureka.client.service-url.defaultZone=http://127.0.0.1:9000/eureka
server.port=8001
spring.application.name=eureka-client

启动类

@SpringBootApplication
@EnableEurekaClient
public class EurekaClientApplication {
    public static void main(String[] args) {
        SpringApplication.run(EurekaClientApplication.class, args);
    }
}

访问http://localhost:9000
在这里插入图片描述
写一个controller类

@RestController
public class HiController {
    @Value("${server.port}")
    String port;
    @GetMapping("hi")
    public String hi(){
        return "hi"+port;
    }
}

访问http://localhost:8001/hi,显示hi80001

Eureka Server集群
创建application.properties

spring.profiles.active=

application-1.properties

server.port=9001
spring.application.name=eureka-server
eureka.instance.hostname=ek1
eureka.client.service-url.defaultZone=http://ek2:9002/eureka/,http://ek3:9003/eureka/

application-2.properties

server.port=9002
spring.application.name=eureka-server
eureka.instance.hostname=ek2
eureka.client.service-url.defaultZone=http://ek1:9001/eureka/,http://ek3:9003/eureka/

application-3.properties

server.port=9003
spring.application.name=eureka-server
eureka.instance.hostname=ek3
eureka.client.service-url.defaultZone=http://ek1:9001/eureka/,http://ek2:9002/eureka/

ek1,ek2,ek3对应127.0.0.1地址的别名
在C:\Windows\System32\drivers\etc\hosts修改
在这里插入图片描述
IDEA中设置可以启动多个服务
在这里插入图片描述
启动三个服务
启动前修改
application.properties

spring.profiles.active=1
spring.profiles.active=2
spring.profiles.active=3

访问http://ek1:9001
在这里插入图片描述

在这里插入图片描述
启动客户端
修改application.properties

#服务注册的地址
#eureka.instance.prefer-ip-address=true
eureka.client.service-url.defaultZone=http://127.0.0.1:**9001**/eureka
server.port=8001
spring.application.name=eureka-client

启动服务访问http://ek1:9001
在这里插入图片描述
访问http://ek2:9002同样可以发现服务
在这里插入图片描述
Ribbon实现负载均衡
在上面的基础上启动两个服务提供方(EUREK-CLIENT)端口为8001,8002
创建服务的消费方ribbon-client
pom.xml

 <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.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
</dependency>

添加application.properties配置

spring.application.name=ribbon-client
server.port=7000
eureka.client.service-url.defaultZone=http://localhost:9000/eureka/

添加配置类开启负载算法

@Configuration
public class RibbonConfig {
    @Bean
    @LoadBalanced
    RestTemplate restTemplate(){
        return new RestTemplate();
    }
}

创建service消费eureka-client

@Service
public class RibbonService {
    @Autowired
    RestTemplate restTemplate;
    public String hi(){
        return restTemplate.getForObject("http://eureka-client/hi",String.class);
    }
}

创建控制类

@RestController
public class RibbonController {
    @Autowired
    RibbonService ribbonService;
    @GetMapping("hi")
    public String hi(){
        return ribbonService.hi();
    }
}

启动类添加@EnableEurekaClient注解。启动项目,访问http://localhost/7000/hi
hi8001
hi8002
发现在这两个之间相互跳转,因而实现了负载均衡

Feign使用
在上面项目的基础上新创建一个Module,取名为feign-client
pom

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

application.properties

spring.application.name=feign-client
server.port=8800
eureka.client.service-url.defaultZone=http://localhost:9000/eureka/

启动类加上@EnableFeignClients注解

@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
public class FeignClientApplication {
    public static void main(String[] args) {
        SpringApplication.run(FeignClientApplication.class, args);
    }
}

配置一个FeignConfig类,注入一个Retryer的Bean,Feign在远程调用失败后会进行重试

@Configuration
public class FeignConfig {
    @Bean
    public Retryer feignRetryer(){
        return new Retryer.Default(100,SECONDS.toMillis(1),5);
    }

配置一个EurekaClientFeign接口进行远程调用服务

@FeignClient(value = "eureka-client",configuration = FeignConfig.class)
public interface EurekaClientFeign {
    @GetMapping("hi")
    String hi();
}

配置一个service消费服务

@Service
public class Hiservice {
    @Autowired
    EurekaClientFeign eurekaClientFeign;
    public String hi(){
        return eurekaClientFeign.hi();
    }
}

配置一个controller暴露接口

@RestController
public class hicontroller {
    @Autowired
    Hiservice hiservice;
    @GetMapping("hi")
    public String hi(){
        return hiservice.hi();
    }
}

启动eureka-service,启动两个eureka-client,端口为8001,8002,启动feign-client
在这里插入图片描述
访问http://localhost:8800/hi
hi8001
hi8002
在这两个轮流出现,说明feign具有负载均衡能力。

熔断器Hystrix
分布式系统中,不免出现服务的故障,一个服务的故障,导致依赖于它的其他微服务可能出现线程阻塞,最终可能导致系统的瘫痪,为了提高系统的容错能力,Hystrix孕育而生。有两种使用Hystrix的场景,RestTemplate和Ribbon作为消费时使用Hystrix,Feign作为服务消费时使用Hystrix.
RestTemplate和Ribbon上使用Hystrix
在之前的基础上改造项目,在ribbon-client中添加如下依赖:

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

启动类添加@EnableHystrix注解开启熔断功能
改造RibbonService类

@Service
public class RibbonService {
    @Autowired
    RestTemplate restTemplate;
    @HystrixCommand(fallbackMethod = "hiError")
    public String hi(){
        return restTemplate.getForObject("http://eureka-client/hi",String.class);
    }

    public String hiError(){
        return "hi,sorry error!";
    }
}

@HystrixCommand(fallbackMethod=“hiError”)注解是服务不可用时回退处理hiError方法。
依次启动eureka-server,eureka-client,ribbon-client,访问http://localhost:7000/hi,显示
hi8002
关闭eureka-client服务,再访问http://localhost:7000/hi,显示
hi,sorry error!

Feign中使用Hystrix
Feign中的起步依赖已经引入了Hystrix的依赖,所以不再导入依赖,再application.properties中开启Hystrix的功能

feign.hystrix.enabled=true

修改EurekaClientFeign接口的代码,在@FeignClient注解中加入回滚的类,该类必须实现被@FeignClient修饰的接口,最后需要注入IoC容器中。

@FeignClient(value = "eureka-client",configuration = FeignConfig.class,fallback = HiHystrix.class)
public interface EurekaClientFeign {
    @GetMapping("hi")
    String hi();
}
@Component
public class HiHystrix implements EurekaClientFeign{
    @Override
    public String hi(){
        return "hi,sorry,error!";
    }
}

启动eureka-server,eureka-client,feign-client,访问http://localhost:8800/hi,显示
hi8002
关闭eureka-client,再访问http://localhost:8800/hi,显示
hi,sorry,error!

使用Hystrix Dashboard监控熔断器的状态
在RestTemplate中使用Hystrix Dashboard
在ribbon-client工程中添加如下pom依赖

<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>

在启动类中添加@EnableHystrixDashboard开启 Hystrix Dashboard的功能
在application.properties中添加如下代码暴露所有端口

management.endpoints.web.exposure.include=*

依次启动eureka-server,eureka-client,ribbon-client,
浏览器先访问http://localhost:7000/hi
再访问http://localhost:7000/actuator/hystrix.stream
在这里插入图片描述
再访问http://localhost:7000/hystrix
在这里插入图片描述

在上面填写http://localhost:7000/actuator/hystrix.stream, 2000, cc点击"monitor"进入
在这里插入图片描述
上图显示数据的各项指标

Feign中使用Hystrix Dashboard
在ribbon-client工程的pom中添加如下依赖,因为Feign自带的Hystrix的依赖不是起步依赖,所以还需加入spring-cloud-starter-netflix-hystrix

<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>

在启动类添加@EnalbeHystrixDashboard注解开启HystrixDashboard的功能。
application.properties中添加:

management.endpoints.web.exposure.include=*

在浏览器访问如之前一样。
使用Turbine聚合监控
新创建一个Module工程,取名monitor-client
pom依赖

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>
<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-turbine</artifactId>
</dependency>

application.properties

spring.application.name=service-turbine
server.port=8888

turbine.combine-host-port=true
turbine.app-config=ribbon-client,feign-client
turbine.cluster-name-expression=new String("default")
turbine.aggregator.cluster-config=default

eureka.client.service-url.defaultZone=http://localhost:9000/eureka

启动类上添加@EnableTurbine注解
依次启动eureka-client,eureka-client,ribbon-client,feign-client,monitor-client,访问http://localhost:7000/hi,http://localhost:8800/hi,再访问http://localhost:7000/hystrix,http://localhost:8800/hystrix的任意一个,在监控流中输入:http://localhost:8888/turbine.stream,2000,cc,点击"monitor"进入
在这里插入图片描述
这页面就可以看见bibbon-client,feign-client的HystrixDashboard的数据。


Spring Cloud Zuul路由网关

Zuul作为微服务系统的网关组件,用于构建边界服务,致力于动态路由、过滤、监控、弹性伸缩和安全。

在这里插入图片描述

在之前的项目上创建一Module命名zuul-client

pom依赖

<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-zuul</artifactId>
</dependency>

启动类添加@EnableEurekaClient和@EnableZuulProxy注解

@SpringBootApplication
@EnableEurekaClient
@EnableZuulProxy
public class ZuulClientApplication {
    public static void main(String[] args) {
        SpringApplication.run(ZuulClientApplication.class, args);
    }

}

application.yml

server:
  port: 5000
eureka:
  client:
    service-url:
      defaultZone: http://localhost:9000/eureka/
spring:
  application:
    name: service-zuul

zuul:
  routes:
    hiapi:
      path: /hiapi/**
      service-id: eurka-client
    ribbonapi:
      path: /ribbonapi/**
      service-id: ribbon-client
    feignapi:
      path: /feignapi/**
      service-id: feign-client

依次启动eureka-server,eureka-client(2次),ribbon-client,feign-client,zuul-client,访问http://localhost:5000/hiapi/hi,浏览器交替显示

hi8001

hi8002

可见Zuul在路由转发做了负载均衡,多次http://localhost5000/ribbonapi/hi,http://localhost5000/feignapi/hi,访问类似


​ 如果不需要Ribbon做负载均衡,修改application.yml(实际开发中不可取)

zuul:
	routes:
		hiapi: /hiapi/**
		url: http://localhost:8001

重新启动zuul-service,访问http://localhost:5000/hiapi/hi,浏览器只显示

hi8001

而http://localhost5000/ribbonapi/hi,http://localhost5000/feignapi/hi能正常的负载均衡,因为在内部之前实现了负载均衡

如何像指定Url,且想负载均衡,那么需要自己维护一张注册表

application.yml修改

zuul:
  routes:
    hiapi:
	  path: /hiapi/**
	  serviceId: hiapi-vl
ribbon:
  eureka:
    enabled: false
hiapi-vl:
  ribbon:
    listOfServers: http://localhost:8001,http://localhost:8002   //listOfServers会出现cannot find

重新启动zuul-service,http://localhost:5000/hiapi/hi,确实能负载均衡,

但http://localhost5000/ribbonapi/hi,http://localhost5000/feignapi/hi不能正常访问了,出现了bug,总之,这部分没有必要这么做。


在Zuul上配置API接口的版本号

给服务API接口加前缀,例如:http://localhost:5000/v1/hiapi/hi,这时候序配置appplication.yml

zuul.prefix: /v1

在Zuul上配置熔断器

Zuul中集成了Hystrix依赖,只需要实现FallbackProvider接口即可

@Component
public class MyFallbackProvider implements FallbackProvider {
    @Override
    public String getRoute() {
        return "*";  //*代表为所有的服务都添加熔断功能,成功。但“eureka-client"为单个服务添加熔断,我尝试失败了,为解决。
    }
    @Override
    public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
        return new ClientHttpResponse() {
            @Override
            public HttpStatus getStatusCode() throws IOException {
                return HttpStatus.OK;
            }
            @Override
            public int getRawStatusCode() throws IOException {
                return 200;
            }
            @Override
            public String getStatusText() throws IOException {
                return "OK";
            }
            @Override
            public void close() {

            }
            @Override
            public InputStream getBody() throws IOException {
                return new ByteArrayInputStream("Oops!error,i'm the fallback".getBytes());
            }
            @Override
            public HttpHeaders getHeaders() {
                HttpHeaders httpHeaders = new HttpHeaders();
                httpHeaders.setContentType(MediaType.APPLICATION_JSON);
                return httpHeaders;
            }
        };
    }
}

重新启动zuul-client工程,关闭掉所有eureka-client,访问http://localhost:5000/v1/hiapi/hi显示:

Oops!error,i’m the fallback


在Zuul中使用过滤器

继承ZuulFilter类,实现其中方法即可,如本案例,检查请求参数中是否传入了token参数。

@Component
public class MyFilter extends ZuulFilter {
    private static Logger log= LoggerFactory.getLogger(MyFilter.class);
    @Override
    public String filterType() {
        return PRE_TYPE;
    }
    @Override
    public int filterOrder() {
        return 0;
    }
    @Override
    public boolean shouldFilter() {
        return true;
    }
    @Override
    public Object run()  {
        RequestContext currentContext = RequestContext.getCurrentContext();
        HttpServletRequest request=currentContext.getRequest();
        String token = request.getParameter("token");
        if(token==null){
            log.warn("token is empty");
            currentContext.setSendZuulResponse(false);
            currentContext.setResponseStatusCode(401);
            try {
                currentContext.getResponse().getWriter().write("token is empty");
            }catch (Exception e){
                return null;
            }
        }
        log.info("ok");
        return null;
    }
}

重启zuul-client,访问http://localhost:5000/v1/hiapi/hi,显示

token is empty

访问http://localhost:5000/v1/hiapi/hi?token=sfsdf,显示

hi8001


服务网关(Spring Cloud Gateway)

服务网关是Spring Cloud官方推出的第二代网关框架,用于代替第一代网关Zuul,它建立在Spring Framework5之上,使用非阻塞模式,性能上优于Zuul,几乎实现了Netfix Zuul的全部功能。服务网关的核心组件也有路由和过滤器,不同在于多了一个断言(Predicate),用于判断请求交给哪个Gateway Web Hanler处理。如图

在这里插入图片描述

断言来自于Java8的接口

在这里插入图片描述

1.After路由断言工厂

在之前的项目上新建一个Module工程,取名gateway-client

pom

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

application.yml

server:
  port: 9002

spring:
  cloud:
    gateway:
      routes:
        - id: getway9003
          uri: http://localhost:7000
          predicates:
            - After=2017-01-20T17:42:47.789-07:00[America/Denver]

当请求在这个时间之后,请求会被转发到http://localhost:7000,启动项目工程eureka-service,eurka-client(2个),ribbon-client,gateway-client,访问http://localhost:9002/hi,会显示http://localhost:7000/hi返回的类容。与时间断言的还有Before,Between路由断言工厂。

2.Header断言工厂

application.yml

server:
  port: 9002

spring:
  cloud:
    gateway:
      routes:
        - id: getway9003
          uri: http://localhost:7000
          predicates:
            - Header=X-Request-Id, \d+

请求需要传入Header 的键为Request-Id,Header的数值为数字时才能匹配断言,重启项目,使用curl执行以下命令:

curl -H 'X-Request-Id:1' localhost:9002/hi

如果没有带X-Request-Id的Header的键,或者Header不为数字时,返回404

Postman请求如图

在这里插入图片描述

3.Cookie路由断言

server:
  port: 9002

spring:
  cloud:
    gateway:
      routes:
        - id: getway9003
          uri: http://localhost:7000
          predicates:
            - Cookie=name,cc

curl命令:

curl -H 'Cookie:name=cc' localhost:9002/hi

在这里插入图片描述

4.Host路由断言工厂

server:
  port: 9002

spring:
  cloud:
    gateway:
      routes:
        - id: getway9003
          uri: http://localhost:7000
          predicates:
            - Host=**.cc.com

curl可以这样:

curl -H 'Host:www.cc.com' localhost:9002/hi

5.Method路由断言工厂

该路由需要请求参数、如GET、POST、PUT、DELETED等

server:
  port: 9002

spring:
  cloud:
    gateway:
      routes:
        - id: getway9003
          uri: http://localhost:7000
          predicates:
            - Method=GET
curl localhost:9002/hi    成功
curl -XPOST localhost:9002/hi   失败404

6.Path路由断言工厂

server:
  port: 9002

spring:
  cloud:
    gateway:
      routes:
        - id: getway9003
          uri: https://blog.csdn.net
          predicates:
            - Path=/weixin_40911000/article/details/{segment}
curl localhost:9002/weixin_40911000/article/details/107523591

7.Query路由断言工厂

server:
  port: 9002

spring:
  cloud:
    gateway:
      routes:
        - id: getway9003
          uri: http://localhost:7000
          predicates:
            - Query=name,ab.  //请求带有两个参数,也可以带有一个参数如:- Query=name
curl localhost:9002/hi?name=abc  //请求带2个参数的    curl localhost:9002/hi?name=sdsg  这个会失败
curl localhost:9002/hi?name=sdsg //请求带1个参数的

Gateway中的过滤器

服务网关内置的过滤器工厂

过滤器工厂作用参数
AddRequestHeader为原始请求添加HeaderHeader的名称及值
AddRequestParameter为原始请求添加请求参数参数名称及值
AddResponseHeader为原始响应添加HeaderHeader的名称及值
DedupeResponseHeader剔除响应头中重复的值需要去重的Header名称及去重策略
Hystrix为路由引入Hystrix的断路器保护HystrixCommand的名称
FallbackHeaders为fallbackUri的请求头中添加具体的异常信息Header的名称
PrefixPath为原始请求路径添加前缀前缀路径
PreserveHostHeader为请求添加一个preserveHostHeader=true的属性,路由过滤器会检查该属性以决定是否要发送原始的Host
RequestRateLimiter用于对请求限流,限流算法为令牌桶keyResolver、rateLimiter、statusCode、denyEmptyKey、emptyKeyStatus
RedirectTo将原始请求重定向到指定的URLhttp状态码及重定向的url
RemoveHopByHopHeadersFilter为原始请求删除IETF组织规定的一系列Header默认就会启用,可以通过配置指定仅删除哪些Header
RemoveRequestHeader为原始请求删除某个HeaderHeader名称
RemoveResponseHeader为原始响应删除某个HeaderHeader名称
RewritePath重写原始的请求路径原始路径正则表达式以及重写后路径的正则表达式
RewriteResponseHeader重写原始响应中的某个HeaderHeader名称,值的正则表达式,重写后的值
SaveSession在转发请求之前,强制执行WebSession::save操作
secureHeaders为原始响应添加一系列起安全作用的响应头无,支持修改这些安全响应头的值
SetPath修改原始的请求路径修改后的路径
SetResponseHeader修改原始响应中某个Header的值Header名称,修改后的值
SetStatus修改原始响应的状态码HTTP 状态码,可以是数字,也可以是字符串
StripPrefix用于截断原始请求的路径使用数字表示要截断的路径的数量
Retry针对不同的响应进行重试retries、statuses、methods、series
RequestSize设置允许接收最大请求包的大小。如果请求包大小超过设置的值,则返回 413 Payload Too Large请求包大小,单位为字节,默认值为5M
ModifyRequestBody在转发请求之前修改原始请求体内容修改后的请求体内容
ModifyResponseBody修改原始响应体的内容修改后的响应体内容
Default为所有路由添加过滤器过滤器工厂名称及值

1.AddRequestHeader过滤器工厂

在之前的项目中修改application.yml

server:
  port: 9002
spring:
  cloud:
    gateway:
      routes:
        - id: add_request_header_route
          uri: http://httpbin.org:80
          filters:
            - AddRequestHeader=X-Request-Foo, Bar
          predicates:
            - After=2017-01-20T17:42:47.789-07:00[America/Denver]

启动工程,访问http://localhost:9002/get,显示从http://httpbin.org:80/get得到的请求:

{
  "args": {}, 
  "headers": {
    "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9", 
    "Accept-Encoding": "gzip, deflate, br", 
    "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8", 
    "Content-Length": "0", 
    "Forwarded": "proto=http;host=\"localhost:9002\";for=\"0:0:0:0:0:0:0:1:58513\"", 
    "Host": "httpbin.org", 
    "Sec-Fetch-Dest": "document", 
    "Sec-Fetch-Mode": "navigate", 
    "Sec-Fetch-Site": "none", 
    "Sec-Fetch-User": "?1", 
    "Upgrade-Insecure-Requests": "1", 
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.61 Safari/537.36", 
    "X-Amzn-Trace-Id": "Root=1-5f1fe576-d773ec00bf8d2dd2dc0d5c0a", 
    "X-Forwarded-Host": "localhost:9002", 
    "X-Request-Foo": "Bar"
  }, 
  "origin": "0:0:0:0:0:0:0:1, 219.152.5.50", 
  "url": "http://localhost:9002/get"
}

从结果来看,确实在请求头中加入了X-Request-Foo请求头。

2.ReqwritePath过滤工厂

Nginx的强大功能一就是重写路径,服务网关也默认提供了这样的功能,而Zuul没有这个功能

修改application.yml

server:
  port: 9002
spring:
  cloud:
    gateway:
      routes:
        - id: reqrite_path_route
          uri: https://blog.csdn.net
          filters:
            - RewritePath=/foo/(?<segment>.*), /$\{segment}
          predicates:
            - Path=/foo/**

所有以/foo/**开始的路路径都会命中配置的路由,并执行过滤器的逻辑,本案例将/foo/(?.*)重写为{segment},如访问http://localhost:9002/foo/weixin_40911000,页面将转发到https://blog.csdn.net/weixin_40911000页面,如访问http://localhost:9002/foo/weixin_40911000/bac,页面报错404,因为不存在https://blog.csdn.net/weixin_40911000/abc页面。

3.自定义过滤器

服务网关内置了19种强大的过滤工厂,当然根据需要也可以自定义过滤器。需要实现GatewayFilter和Ordered这两个接口。现在写一个请求耗时的RequestTimeFilter.

public class RequestTimeFilter implements GatewayFilter, Ordered {
    private  static final Log log= LogFactory.getLog(GatewayFilter.class);
    private  static final  String REQUEST_TIME_BEGIN="requestTimeBegin";
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        exchange.getAttributes().put(REQUEST_TIME_BEGIN,System.currentTimeMillis());
        return chain.filter(exchange).then(
                Mono.fromRunnable(() -> {
                    Long starterTime=exchange.getAttribute(REQUEST_TIME_BEGIN);
                    if (starterTime !=null){
                        log.info(exchange.getRequest().getURI().getRawPath()+":"+(System.currentTimeMillis()-starterTime)+"ms");
                    }
                })
                );
    }

    @Override
    public int getOrder() {
        return 0;
    }
}

将过滤器注册到路由中

@Configuration
public class MyRouteLocator {
    @Bean
    public RouteLocator customerRouteLocator(RouteLocatorBuilder builder){
        return  builder.routes()
                .route(r->r.path("/get/**")
                .filters(f ->f.filter(new RequestTimeFilter())
                .addResponseHeader("X-Response-Default-Foo","Default-Bar"))
                .uri("http://httpbin.org:80")
                .order(0)
                .id("customer_filter_router")
                )
                .build();
    }
}

启动项目访问http://localhost:9002/get控制台打印

2020-07-28 18:37:23.777  INFO 4100 --- [ctor-http-nio-2] o.s.cloud.gateway.filter.GatewayFilter   : /get:599ms

4.全局过滤器

服务网关根据作用范围分为网关过滤器(GatewayFilter)和全局过滤器(GlobalFilter).

(1)GatewayFilter,通过spring.cloud.routes.filters配置在具体路由下,只作用在当前的路由上;或通过spring.cloud.default-filters配置在全局中,作用在所有路由上。

(2)GlobalFilter:不需要配置,作用在所欲路由上,系统初始化时加载。
在这里插入图片描述

上图每一个GlobalFilter都作用在每一个路由上,能够满足大多数的需求。如需定制需实现GlobalFilter,Ordered接口,如定制一个校验请求参数是否含有"token"

public class TokenFilter implements GlobalFilter, Ordered {
    Logger logger= LoggerFactory.getLogger(TokenFilter.class);
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        String token= exchange.getRequest().getQueryParams().getFirst("token");
        if(token == null || token.isEmpty()){
            logger.info("token is empty..");
            exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
            return exchange.getResponse().setComplete();
        }
        return chain.filter(exchange);
    }

    @Override
    public int getOrder() {
        return -100;
    }
}
 @Bean
    public TokenFilter tokenFilter(){
        return new TokenFilter();
    }

启动项目访问:http://localhost:9002/get,控制台:

2020-07-28 19:02:24.523  INFO 13464 --- [ctor-http-nio-3] com.cc.gatewayclient.filter.TokenFilter  : token is empty..

日志显示参数没有传入"token"

限流

高并发系统中往往需要做限流,一方面为了防止流量突发使服务过载,另一方面为了防止流量攻击。常见的限流维度有IP限流,Url限流,访问频次限流。常见的限流算法有:计数器算法、漏桶算法、令牌桶算法。在服务网关过滤器中可以自行实现这三种,,但服务网关官方只提供了RequestRateLimiterGatewayFilterFactory这个类,使用Redis和lua脚本实现令牌桶算法进行限流。

案例:在之前的gateway-client中演示:

pom

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>

application.yml

server:
  port: 9002
spring:
  cloud:
    gateway:
      routes:
        - id: limit_route
          uri: http://httpbin.org:80
          predicates:
            - After=2017-01-20T17:42:47.789-07:00[America/Denver]
          filters:
            - name: RequestRateLimiter
              args:
                key-resolver: '#{@hostAddrKeyResolver}'
                redis-rate-limiter.replenishRate: 1
                ridis-rate-limiter.burstCapacity: 3
  application:
    name: gateway-limiter

  redis:
    host: localhost
    port: 6379
    database: 0

KeyResolver需要实现resolve方法

public class HostAddrKeyResolver  implements KeyResolver {
    @Override
    public Mono<String> resolve(ServerWebExchange exchange) {
        return Mono.just(exchange.getRequest().getRemoteAddress().getAddress().getHostAddress());
    }
}

如根据Url限流

return Mono.just(exchange.getRequest().getURI().getPath());
return Mono.just(exchange.getRequest().getQueryParams().getFirst("user"));  //根据用户的维度限流

注入容器

 @Bean
    public HostAddrKeyResolver hostAddrKeyResolver(){
        return new HostAddrKeyResolver();
    }

用Jemter进行测试,配置10thread去循环localhost:9002/get,从测试结果可以看到有的成功,有的失败。

服务化

简短说就是通过统一端口访问各个微服务。

pom

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

application.yml

server:
  port: 9002
spring:
  application:
    name: gateway-service
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true
          lower-case-service-id: true
eureka:
  client:
    service-url:
      defaultZone:
        http://localhost:9000/eureka/

启动eureka-server、eureka-client、gateway-client

访问http://localhost:9002/eureka-client/hi 显示:hi8002

上述例子向gateway-client发送请求,必须带上eureka-client这个前缀,才能转发到eureka-client上,转发之前会将eureka-client去掉。

也可以自定义服务名:

server:
  port: 9002
spring:
  application:
    name: gateway-service
  cloud:
    gateway:
      discovery:
        locator:
          enabled: false
          lower-case-service-id: true
      routes:
        - id: eureka-client
          uri: lb://EUREKA-CLIENT
          predicates:
            - Path=/demo/**
          filters:
            - StripPrefix=1
eureka:
  client:
    service-url:
      defaultZone:
        http://localhost:9000/eureka/

spring.cloud.gateway.discovery.locator.enabled设置为false,那么之前的http://localhost:9002/eureka-client/hi就不能正常访问了,

StripPrefix过滤器在转发之前将/demo去掉,访问http://localhost:9002/demo/hi 就能正常转发到eureka-client上:显示:hi8002


服务注册与发现Consul

1.下载Consul安装包,本案例下载windows版本,解压,只有一个consul.exe,在该路径上,在路径上输入cmd进入dos模式,使用consul agent -dev 启动。访问http://localhost:8500

在这里插入图片描述

Consul常见执行命令:

consul agent -dev 运行一个consul agent

consul join IP 加入集群

consul members 列出集群中的members

2.新创建一个微服务consul-provider

pom

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>

application.yml

server:
  port: 8001
spring:
  application:
    name: consul-provider
  cloud:
    consul:
      host: localhost
      port: 8500
      discovery:
        service-name: consul-provider

启动类:

@SpringBootApplication
@EnableDiscoveryClient
public class ConsulProviderApplication {

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

写一个服务:

@RestController
public class HiController {
    @Value("${server.port}")
    String port;
    @GetMapping("/hi")
    public String home(){
        return "hi"+",my port:"+port;
    }
}

访问:http://locahost:8500 可以在注册中心发现注册的服务consul-provider。访问http://locahost:8001/hi也能正常访问。

3.0创建消费服务:

新建一个服务:consul-consumer

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
   <groupId>org.springframework.cloud</groupId>
   <artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>
<dependency>
   <groupId>org.springframework.cloud</groupId>
   <artifactId>spring-cloud-starter-consul-config</artifactId>
</dependency>
server:
  port: 8002
spring:
  application:
    name: consul-consumer
  cloud:
    consul:
      host: localhost
      port: 8500
      discovery:
        service-name: consul-consumer
    loadbalancer:           //这个不要忘记加,不然访问会出错
      ribbon:
        enabled: false
@FeignClient(value = "consul-provider")
public interface ClientFeign {
    @GetMapping("/hi")
    String home() ;
}

@Service
public class HiService {
    @Autowired
    ClientFeign clientFeign;
    @GetMapping(value = "/hi")
    public String hi(){
        return clientFeign.home();
    }
}
@RestController
public class HiController {
    @Autowired
    HiService hiService;
    @GetMapping("/hi")
    public String sayHi(){
        return hiService.hi();
    }
}
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class ConsulConsumerApplication {

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

启动项目consul-provider,consul-consumer,访问http://localhost:8002/hi,如果服务提供者做了集群,默认也做了集群访问:默认为轮询。

4.Spring Cloud Consul Config做服务配置中心。

在上面的consul-provider上修改:在pom中添加如下依赖

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

在application.yml中使用一下配置:

spring:
  profiles:
    active: dev

添加bootstrap.yml

spring:
  application:
    name: consul-provider
  cloud:
    consul:
      host: localhost
      port: 8500
      discovery:
        service-name: consul-provider

      config:
        enabled: true   //开启config
        format: yaml	//可以为yaml或properties
        prefix: config  //配置的基本目录,比如config
        profile-separator: ':'   //配置分割符,默认为","
        data-key: data   //为应用配置的Key名字,值为整个应用配置的字符串

在consul的Key/Value中配置

在这里插入图片描述

新建一个controller

@RestController
public class FooBarController {
    @Value("${foo.bar}")
    String fooBar;
    @GetMapping("/foo")
    public String getFooBar(){
        return fooBar;
    }
}

启动项目可以发现端口为8888,访问http://localhost:8888/foo,显示 bar,说明已成功从Consul的配置中读取foo.bar的配置。

5.动态刷新配置

Spring Cloud Consul Config支持动态刷新,在需要动态刷新的类上添加@RefreScope

@RestController
@RefreshScope
public class FooBarController {
    @Value("${foo.bar}")
    String fooBar;
    @GetMapping("/foo")
    public String getFooBar(){
        return fooBar;
    }
}

重启项目,在consul配置中心重新配置foo.bar如:

foo:
  bar:bar1
server:
  port:8888

访问http://localhost:8888/foo,显示bar1。动态刷新成功。


SpringCloudConfig

1.ConfigServer读取本地文件

新建一个SpringCloud工程,父工程pom

<groupId>com.cc</groupId>
    <artifactId>springcloud1</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>pom</packaging>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.4.RELEASE</version>
        <relativePath/>
    </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>Hoxton.SR1</spring-cloud.version>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

新建一个Module工程,取名config-server

pom文件代码

<parent>
        <artifactId>springcloud1</artifactId>
        <groupId>com.cc</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>config-server</artifactId>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-config-server</artifactId>
        </dependency>
    </dependencies>
@SpringBootApplication
@EnableConfigServer
public class ConfigServerApplication {
    public static void main(String[] args) {
        SpringApplication.run(ConfigServerApplication.class,args);
    }
}
server:
  port: 8769
spring:
  application:
    name: config-server
  profiles:
    active: native
  cloud:
    config:
      server:
        native:
          search-locations: classpath:/shared       #读取配置的路径为classpath下的shared目录

在shared目录下新建一个config-client-dev.yml,注意:config-client和读取配置文件工程的spring.application.name的名字相同即可。

server:
  port: 8762
foo: foo version 1

新建一个Module工程,取名config-client

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-config</artifactId>
    </dependency>
</dependencies>

新建配置文件bootstrap.yml,bootsrap.yml具有优于application.yml的执行顺序

spring:
  application:
    name: config-client
  cloud:
    config:
      uri: http://localhost:8769
      fail-fast: true
  profiles:
    active: dev
@SpringBootApplication
@RestController
public class ConfigClientApplication {
    public static void main(String[] args) {
        SpringApplication.run(ConfigClientApplication.class,args);
    }
    @Value("${foo}")
    String foo;
    @RequestMapping(value = "/foo")
    public String hi(){
        return foo;
    }
}

访问http://localhost:8762/foo,显示

foo version 1

可见读取配置文件成功。


2.Config Server从远程仓库Git中读取配置文件

修改config-server的application.yml

server:
  port: 8769
spring:
  application:
    name: config-server
  cloud:
    config:
      server:
        git:
          uri: https://github.com/cheng290/respo
          search-paths: blob
          username: 
          password: 
      label: master

uri为远程仓库的地址,search-paths为远程仓库的文件夹地址,username和password为Git仓库的登录名和密码,label为git仓库的分支名,本利从master读取。将config-client-dev.yml上传到远程仓库,上传地址为https://github.com/cheng290/respo.启动项目config-se/server,config-client,访问http://localhost:8762/foo,显示

foo version 1

可见配置远程仓库成功。


3.构建高可用的Config Server

当微服务很多时,可以考虑将Config Server做成一个为服务,并将其集群。

3.1构建Eureka Server

新建一个eureka-server工程,作为注册中心。

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
server:
  port: 8761
eureka:
  client:
    register-with-eureka: false
    fetch-registry: false
    service-url:
      defaultZone: http://localhost:${server.port}/eureka/
@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {
    public static void main(String[] args) {
        SpringApplication.run(EurekaServerApplication.class,args);
    }
}

3.2改造config Server

添加如下pom

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency
@SpringBootApplication
@EnableConfigServer
@EnableEurekaClient
public class ConfigServerApplication {
    public static void main(String[] args) {
        SpringApplication.run(ConfigServerApplication.class,args);
    }
}

application.yml添加

eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka/

3.3改造Config Client

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
@SpringBootApplication
@RestController
@EnableEurekaClient
public class ConfigClientApplication {
    public static void main(String[] args) {
        SpringApplication.run(ConfigClientApplication.class,args);
    }
    @Value("${foo}")
    String foo;
    @RequestMapping(value = "/foo")
    public String hi(){
        return foo;
    }
}

bootstrap.yml

spring:
  application:
    name: config-client
  cloud:
    config:
      fail-fast: true
      discovery:
        enabled: true
        service-id: config-server
  profiles:
    active: dev
server:
  port: 8762
eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka/

依次启动eureka-server,config-server,config-client,访问http://localhost:8762/foo,显示foo version 1配置成功。

搭建高可用的Config Server,将Config Server多启动几个,如端口为8769,8768,8767,就构成了集群。多次启动confgi-client,从控制台可以看到,服务会分别从这几个端口的微服务中读取配置文件。


Spring Cloud Bus刷新配置

在config-client工程中修改

pom添加如下依赖

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

bootstrap.yml:注意从远程仓库读取配置时,为bootstrap.yml,如果写为application.yml报错

spring:
  application:
    name: config-client
  cloud:
    config:
      fail-fast: true
      discovery:
        enabled: true
        service-id: config-server
  profiles:
    active: dev
  rabbitmq:
    host: localhost
    port: 5672
    username: guest
    password: guest
server:
  port: 8762
eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka/
management:
  endpoints:
    web:
      exposure:
        include: "*"
@SpringBootApplication
@RestController
@EnableEurekaClient
@RefreshScope          //添加的注解
public class ConfigClientApplication {
    public static void main(String[] args) {
        SpringApplication.run(ConfigClientApplication.class,args);
    }
    @Value("${foo}")
    String foo;
    @RequestMapping(value = "/foo")
    public String hi(){
        return foo;
    }
}

启动eureka-server,config-server,config-client,访问http://localhsot:8762/foo,显示:foo version 1,去远程仓库修改配置,foo的值该为:foo version 222,使用postman发送一个post请求,http://localhost:8762/actuator/bus-refresh.访问http://localhsot:8762/foo显示:foo version 222.


将配置文件配置在MYSQL数据库中

当我们需要二次开发对配置进行展示或做管控功能时,将配置存储在关系型数据库中会更便捷。

在之前的config-server上做改造

pom

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>

application.yml

server:
  port: 8769
spring:
  application:
    name: config-server
  profiles:
    active: jdbc
  cloud:
    config:
      label: master
      server:
        jdbc:
          sql: select key1,value1 from config_properties where application=? and profile=? and label=?
  datasource:
    url: jdbc:mysql://localhost:3306/runoob?useUnicode=true&characterEncoding=utf8&characterSetResults=utf8&serverTimezone=GMT%2B8
    username: root
    password: 123456
    driver-class-name: com.mysql.cj.jdbc.Driver
eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka/

创建数据表

CREATE TABLE `config_properties` (
  `id` int(10) NOT NULL AUTO_INCREMENT,
  `key1` varchar(255) DEFAULT NULL,
  `value1` varchar(255) DEFAULT NULL,
  `application` varchar(255) DEFAULT NULL,
  `profile` varchar(255) DEFAULT NULL,
  `label` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;

插入数据

在这里插入图片描述

其中config-client中的的端口为8083,访问http://localhsot:8083/foo显示:version

注意这种方式,在config-client中必须配置端口号,而其他配置方式读取服务端的可以不用配置。

服务链路追踪Spring Cloud Sleuth

​ 链路追踪去跟进一个请求到底有哪些服务的参与,参与的顺序有是怎么样的,从而达到每个请求的步骤清新可见,出现问题能快速定位的目的。目前常见的有Google的Dapper,Twitter的Zipkin,以及阿里的Eagleeye,本案例采用Zipkin。

1.启动Zipkin Server

​ curl -sSL https://zipkin.io/quickstart.sh | bash -s

​ java -jar zipkin.jar

​ 第一条命令使用Gitbash下载Zipkin的jar包,第二条命令是启动jar包,默认端口为9411。

2.构建微服务提供者sleuth-provider

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-zipkin</artifactId>
</dependency>
server:
  port: 8763
spring:
  application:
    name: sleuth-provider
  sleuth:
    web:
      client:
        enabled: true
    sampler:
      probability: 1.0  #将采样比例设置为1.0,也就是全部需要,默认为。0.1
  zipkin:
    base-url: http://localhost:9411/
eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka/
@RestController
public class HiController {
    @Autowired
    Tracer tracer;
    @Value("${server.port}")
    String port;
    @GetMapping("/hi")
    public String home( String name){
        tracer.currentSpan().tag("name","cc");
        return "hi"+name+",i am port:"+port;
    }
}
@SpringBootApplication
@EnableEurekaClient
public class SleuthProviderApplication {
    public static void main(String[] args) {
        SpringApplication.run(SleuthProviderApplication.class,args);
    }
}

3.构建微服务消费者sleuth-consumer

<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-openfeign</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-zipkin</artifactId>
</dependency>
server:
  port: 8765
spring:
  application:
    name: sleuth-consumer
  sleuth:
    web:
      client:
        enabled: true
    sampler:
      probability: 1.0  #将采样比例设置为1.0,也就是全部需要,默认为。0.1
  zipkin:
    base-url: http://localhost:9411/

eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka/
@FeignClient(value = "sleuth-provider")
public interface EurekaClientFeign {
    @GetMapping("/hi")
    String home1(@RequestParam(value = "name") String name);  //注意:有参数的时候@RequestParam这个注解一定不能少
}
@Service
public class HiService {
    @Autowired
    EurekaClientFeign eurekaClientFeign;
    public String sayHi(String name){
        return eurekaClientFeign.home1(name);
    }
}
@RestController
public class HiController {
    @Autowired
    HiService hiService;
    @GetMapping("/hi")
    public String sayHi(@RequestParam(defaultValue = "cc",required = false)String name){
        return hiService.sayHi(name);
    }
}
@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients    //在使用Feign时,这个注解不要忘添加
public class SleuthConsumerApplication {
    public static void main(String[] args) {
        SpringApplication.run(SleuthConsumerApplication.class,args);
    }
}

启动eureka-server,sleuth-provider,sleuth-consumer,和zipkin.jar包,访问http://loclhost:8762/hi,再访问http://localhost:9411

在这里插入图片描述

4.在链路数据中添加自定义的数据

再本案例中添加操作人的代码:

@RestController
public class HiController {
    @Autowired
    Tracer tracer;
    @Value("${server.port}")
    String port;
    @GetMapping("/hi")
    public String home( String name){
        tracer.currentSpan().tag("name","cc");   //添加操作的人
        return "hi"+name+",i am port:"+port;
    }
}

5.使用RabbitMQ传输数据

上例中zipkin-server接收数据是通过HTTP的方式,本案例采用RabbitMQ来传递。

下载安装rabbitMQ这里不再说,安装启动好之后,使用一下命令来启动Zipkin的服务:

rabbit_address=localhost java -jar zipkin.jar

在sleuth-privider,sleuth-consumer中添加如下依赖

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-stream-binder-rabbit</artifactId>
</dependency>

在这两个服务中,去掉spring.zikin.base-url的配置,添加如下代码

rabbitmq:
  host: localhost
  username: guest
  password: guest
  port: 5672

重新启动服务,同样可以达到这种使用zipkin的效果。

6.在ELasticSearch中存储链路中的数据

当Zipkin Server重启时,之前的链路数据全部消失,它是将数据存在在内存中的。使用数据能解决这种问题,支持MYSQL,ElasticSearch,Cassandra数据库,本案例采用ELasticSearch,在高并发的情况下也比较适用。

读者自行安装ElasticSearch和Kinbana(图形化web工具),ElasticSearch的默认端口为9200,Kibana的默认端口为5601

启动这两个服务,在使用一下命令启动zipkin server:

storage_type=elasticsearch es_hosts=http://localhost:9200 es_index=zipkin java -jar zipkin.jar

访问http://localhost:5601新建一个z*的查询。

在这里插入图片描述

重新启动zipkin访问http://localhost:9411,能看到数据任然存在。

微服务监控Spring Boot Admin

1.使用Spring Boot Admin监控Spring Boot应用

1.1新建一个admin-server服务

Caused by: java.lang.StackOverflowError: null

org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘adminHandlerMapping’ defined in class path resource [de/codecentric/boot/admin/server/config/AdminServerWebConfiguration$ServletRestApiConfirguation.class]: Invocation of init method failed; nested exception is java.lang.StackOverflowError

注意:以上这个错误一定要注意spirng-boot-admin-starter-server的版本与springboot对应,不然报内存溢出问题。本案例采用2.2.4

<dependency>
    <groupId>de.codecentric</groupId>
    <artifactId>spring-boot-admin-starter-server</artifactId>
    <version>2.2.4</version>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
server:
  port: 8769
spring:
  application:
    name: admin-server
@SpringBootApplication
@EnableAdminServer
public class AdminServerApplication {
    public static void main(String[] args) {
        SpringApplication.run(AdminServerApplication.class,args);
    }
}

1.2创建一个Spring Boot Admin Client工程

<dependency>
    <groupId>de.codecentric</groupId>
    <artifactId>spring-boot-admin-starter-client</artifactId>
    <version>2.2.4</version>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
server:
  port: 8768
spring:
  application:
    name: admin-client
  boot:
    admin:
      client:
        url: http://localhost:8769
management:
  endpoints:
    web:
      exposure:
        include: '*'
  endpoint:
    health:
      show-details: always
@SpringBootApplication
public class AdminClientApplication {
    public static void main(String[] args) {
        SpringApplication.run(AdminClientApplication.class,args);
    }
}

依次启动admin-server,admin-client两个工程,访问http://localhost:8769,可以在监控端看见很多用用的信息。

在这里插入图片描述

在这里插入图片描述

2.使用Spring Boot Admin 监控Spring Cloud 为服务

2.1创建Admin Server

<dependency>
    <groupId>de.codecentric</groupId>
    <artifactId>spring-boot-admin-starter-server</artifactId>
    <version>2.2.4</version>
</dependency>
<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.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
server:
  port: 8769
spring:
  application:
    name: admin-server
  security:
    user:
      name: admin
      password: admin
eureka:
  client:
    registry-fetch-interval-seconds: 5
    service-url:
      defaultZone: ${EUREKA_SERVICE_URL:http://localhost:9000}/eureka/
  instance:
    lease-renewal-interval-in-seconds: 10
#    health-check-url: /actuator/health
management:
  endpoint:
    health:
      show-details: always
  endpoints:
    web:
      exposure:
        include: "*"
@SpringBootApplication
@EnableAdminServer
@EnableEurekaClient
public class AdminServerApplication {
    public static void main(String[] args) {
        SpringApplication.run(AdminServerApplication.class,args);
    }
}

2.2创建Amdin Client

<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.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
server:
  port: 8768
spring:
  application:
    name: admin-client
eureka:
  instance:
    lease-renewal-interval-in-seconds: 10
#    health-check-url-path: /actuator/heath
  client:
    registry-fetch-interval-seconds: 5
    service-url:
      defaultZone: ${EUREKA_SERVICE_URL:http://localhost:9000}/eureka/
management:
  endpoints:
    web:
      exposure:
        include: '*'
  endpoint:
    health:
      show-details: always
@SpringBootApplication
@EnableEurekaClient
public class AdminClientApplication {
    public static void main(String[] args) {
        SpringApplication.run(AdminClientApplication.class,args);
    }
}

依次启动eureka-client,admin-server,admin-client,访问http://localhost:8769

在这里插入图片描述

2.3Spring Boot admin 集成Security组件

在admin-server的基础上改造,添加如下依赖

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

在之前的application.yml中添加如下配置

spring:
  security:
    user:
      name: admin
      password: admin
eureka:
  instance:
    metadata-map:
      user.name: ${spring.security.user.name}
      user.password: ${spring.security.user.password}
@Configuration
public class SecuritySecureConfig extends WebSecurityConfigurerAdapter {
    private final String adminContextPath;
    public SecuritySecureConfig(AdminServerProperties adminServerProperties){
        this.adminContextPath=adminServerProperties.getContextPath();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        SavedRequestAwareAuthenticationSuccessHandler successHandler=new SavedRequestAwareAuthenticationSuccessHandler();
        successHandler.setTargetUrlParameter("redirectTo");
        http.authorizeRequests()
                .antMatchers(adminContextPath+"/assets/**").permitAll()  //给静态资源css/img都加允许访问的权限,
                .antMatchers(adminContextPath+"/login").permitAll()
                .anyRequest().authenticated()
                .and()
                .formLogin().loginPage(adminContextPath+"/login").successHandler(successHandler)
                .and()
                .logout().logoutUrl(adminContextPath+"/logout")
                .and()
                .httpBasic()  //这些静态资源部不支持跨域禁用掉
                .and()
                .csrf()
                .disable();
    }
}

2.4Spring Boot Admin 集成Mail

当服务部监控或者下线了,可以通过邮箱发送信息。

在admin-server中添加如下

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-mail</artifactId>
</dependency>
server:
  port: 8769
spring:
  application:
    name: admin-server
  security:
    user:
      name: admin
      password: admin
  mail:
    host: smtp.qq.com
    username: 123@qq.com
    password: 

  boot:
    admin:
      notify:
        mail:
          to: 456@126.com
          from: 123@qq.com
eureka:
  client:
    registry-fetch-interval-seconds: 5
    service-url:
      defaultZone: http://localhost:9000/eureka/
  instance:
    lease-renewal-interval-in-seconds: 10
    metadata-map:
      user.name: admin
      user.password: admin
management:
  endpoint:
    health:
      show-details: always
  endpoints:
    web:
      exposure:
        include: "*"

启动服务,再关闭掉admin-client下线,会发送邮件

在这里插入图片描述

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
Spring Cloud是一个用于构建分布式系统的开源框架。它提供了一系列的工具和组件,帮助开发者快速构建和部署云原生应用。如果你想学习Spring Cloud,以下是一些步骤和资源可以帮助你入门: 1. 了解Spring框架:在学习Spring Cloud之前,建议先掌握Spring框架的基础知识,包括Spring Boot和Spring MVC等。 2. 学习微服务架构:Spring Cloud是基于微服务架构的,所以学习微服务的概念和原理非常重要。了解什么是微服务、微服务架构的优势和挑战以及常用的微服务组件。 3. 官方文档:Spring Cloud有详细的官方文档,包含了各个组件的使用说明和示例代码。你可以从官方文档开始学习,逐步掌握各个组件的功能和用法。 4. 示例项目:通过参考一些示例项目可以更好地理解和学习Spring Cloud。你可以在GitHub上搜索Spring Cloud相关的示例项目,如Spring Cloud Samples等。 5. 教程和博客:有许多优秀的教程和博客文章可以帮助你学习Spring Cloud。你可以搜索一些知名的技术博客或者在线学习平台,如CSDN、博客园、Spring官方博客等。 6. 练手项目:选择一个小型的练手项目,尝试使用Spring Cloud构建一个简单的微服务应用。通过实践来加深对Spring Cloud的理解和掌握。 记住,学习Spring Cloud需要一定的时间和实践,持续学习和实践是非常重要的。希望以上的建议对你有帮助!如果你还有其他问题,可以随时提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值