SpringCloud组件学习

模块说明

​ 一个媒资模块,用于查询所有的媒资信息;

​ 一个订单模块,调用媒资模块中的接口,查询媒资信息;

基础工程搭建

  1. 创建父工程,pom.xml
<!-- ⽗⼯程打包⽅式为pom -->
<packaging>pom</packaging>
<!-- spring boot ⽗启动器依赖 -->
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.1.6.RELEASE</version>
</parent>
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>Greenwich.RELEASE</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>
<dependencies>
    <!-- web依赖 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!-- ⽇志依赖 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-logging</artifactId>
    </dependency>
    <!-- 测试依赖 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
    <!-- lombok⼯具 -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.4</version>
    </dependency>
    <!-- Actuator可以帮助你监控和管理Spring Boot应⽤ -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    <!-- 热部署 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
        <optional>true</optional>
    </dependency>
    <!-- 引⼊Jaxb,开始 -->
    <dependency>
        <groupId>com.sun.xml.bind</groupId>
        <artifactId>jaxb-core</artifactId>
        <version>2.2.11</version>
    </dependency>
    <dependency>
        <groupId>javax.xml.bind</groupId>
        <artifactId>jaxb-api</artifactId>
    </dependency>
    <dependency>
        <groupId>com.sun.xml.bind</groupId>
        <artifactId>jaxb-impl</artifactId>
        <version>2.2.11</version>
    </dependency>
    <dependency>
        <groupId>org.glassfish.jaxb</groupId>
        <artifactId>jaxb-runtime</artifactId>
        <version>2.2.10-b140310.1920</version>
    </dependency>
    <dependency>
        <groupId>javax.activation</groupId>
        <artifactId>activation</artifactId>
        <version>1.1.1</version>
    </dependency>
    <!-- 引⼊Jaxb,结束 -->
    <!-- 引入spring cloud common模块 -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-commons</artifactId>
    </dependency>
</dependencies>
<build>
    <plugins>
        <!-- 编译插件 -->
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <configuration>
                <source>11</source>
                <target>11</target>
                <encoding>UTF-8</encoding>
            </configuration>
        </plugin>
        <!-- 打包插件 -->
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>
  1. 创建New Module,service_common,pom.xml
<dependencies>
    <!--Spring Data Jpa-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <scope>runtime</scope>
    </dependency>
</dependencies>
  1. 建立实体类
package com.lx.pojo;

import lombok.Data;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import java.io.Serializable;

@Data
@Entity
@Table(name = "media")
public class Media implements Serializable {

    private static final long serialVersionUID = 4355137647179605524L;

    @Id
    private Integer id;
    private String name;
}
  1. 创建New Module,service_media,pom.xml
<dependencies>
    <dependency>
        <groupId>com.lx</groupId>
        <artifactId>service_common</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>
</dependencies>
  1. 创建dao层
package com.lx.dao;

import com.lx.pojo.Media;
import org.springframework.data.jpa.repository.JpaRepository;

public interface IMediaDao extends JpaRepository<Media, Integer> {
}

  1. 创建service层和实现类
package com.lx.service;

import com.lx.pojo.Media;

public interface IMediaService {

    Media findOneById(Integer id);
}
package com.lx.service.impl;

import com.lx.dao.IMediaDao;
import com.lx.pojo.Media;
import com.lx.service.IMediaService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class MediaServiceImpl implements IMediaService {

    @Autowired
    private IMediaDao mediaDao;

    @Override
    public Media findOneById(Integer id) {
        return mediaDao.findById(id).get();
    }
}
  1. 创建controller层,port参数用于后面测试访问的哪个端口服务
package com.lx.controller;

import com.lx.service.IMediaService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
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("/media")
public class MediaController {

    @Value("${server.port}")
    private Integer port;

    @Autowired
    private IMediaService mediaService;

    @GetMapping("/findMeidaById/{id}")
    public String findMeidaById(@PathVariable Integer id) {
        return mediaService.findOneById(id).getName() + " ---->>>> " + port;
    }
}
  1. 创建application.yml
server:
  port: 8080
  servlet:
    context-path: /

spring:
  application:
    name: service-media
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/sc_test?useUnicode=true&characterEncoding=utf8
    username: root
    password: 123456
  jpa:
    database: MySQL
    show-sql: true
    hibernate:
      naming:
        # 避免将驼峰命名转换为下划线命名
        physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
  1. 创建启动类
package com.lx;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@EntityScan("com.lx.pojo")
public class MediaApplication {

    public static void main(String[] args) {
        SpringApplication.run(MediaApplication.class, args);
    }
}
  1. 启动服务,测试

    http://localhost:8080/media/findMeidaById/1

  2. 创建New Module,service_order,controller层

package com.lx.controller;

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;

@RestController
@RequestMapping("/order")
public class OrderController {

    @Autowired
    private RestTemplate restTemplate;

    @GetMapping("/getMediaNameById/{id}")
    public String getMediaNameById(@PathVariable Integer id) {
        String url = "http://localhost:8080/media/findMeidaById/" + id;
        String name = restTemplate.getForObject(url, String.class);
        return name;
    }
}
  1. 创建启动类
package com.lx;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

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

    @Bean
    public RestTemplate getRestTemplate() {
        return new RestTemplate();
    }
}
  1. 创建application.yml
server:
  port: 8090
  servlet:
    context-path: /
    
spring:
  application:
    name: service-order
  1. 启动服务,测试

    http://localhost:8090/order/getMediaNameById/1

    至此,基础工程搭建完毕,下面用SpringCloud组件改造工程。

工程改造

Eureka注册中心

  1. 创建New Module,eureke_server,pom.xml
<dependencies>
    <!--Eureka server依赖-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
    </dependency>
</dependencies>
  1. 创建application.yml

    eureka.server.use-read-only-response-cache和eureka.server.response-cache-update-interval-ms:针对eureka发现慢的设置

    eureka.client.service-url.defaultZone:在集群模式下应该指向其他eureka server,如果有多个,逗号拼接

    eureka.client.register-with-eureka和eureka.client.fetch-registry:在集群模式下可以改成true

server:
  port: 8761

spring:
  application:
    name: eureka-server

eureka:
  server:
    use-read-only-response-cache: false
    response-cache-update-interval-ms: 0
  instance:
    hostname: eurekaServer
  client:
    service-url:
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
    register-with-eureka: false
    fetch-registry: false
  1. 创建启动类
package com.lx;

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

@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {

    public static void main(String[] args) {
        SpringApplication.run(EurekaServerApplication.class, args);
    }
}
  1. 启动服务,测试

    http://localhost:8761/

  2. 可以把eureka_server复制一份,改下端口:8762,搭建高可用集群

媒资微服务注册到Eureka Server
  1. pom.xml
<!-- eureka client 客户端依赖引入 -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
  1. application.yml

    eureka.client.registryFetchIntervalSeconds:拉取Eureka Server实例信息到本地,默认30秒,解决快速发现新服务

    eureka.instance.metadata-map:自定义Eureka元数据

eureka:
  client:
    registryFetchIntervalSeconds: 3
    service-url:
      defaultZone: http://eurekaServer:8761/eureka
  instance:
    prefer-ip-address: true
    instance-id: ${spring.cloud.client.ip-address}:${spring.application.name}:${server.port}:@project.version@
    metadata-map:
      cluster: cl1
      region: rn1
  1. 启动类

    增加@EnableDiscoveryClient,可以使用@EnableEurekaClient,功能一样,从通用性考虑,使用@EnableDiscoveryClient,从SpringCloud的Edgware版本开始,不加注解也可以,但是建议加上

@EnableDiscoveryClient
  1. 重启server_media,查看eureka server,可以看到server_media已经注册到eureka server
  2. 复制一份server_media,修改端口:8081,搭建集群
订单微服务注册到Eureka Server
  1. pom.xml
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
  1. application.yml
eureka:
  client:
  	registryFetchIntervalSeconds: 3
    service-url:
      defaultZone: http://eurekaServer:8761/eureka
  instance:
    prefer-ip-address: true
    instance-id: ${spring.cloud.client.ip-address}:${spring.application.name}:${server.port}:@project.version@
  1. 启动类
@EnableDiscoveryClient
  1. controller改造
package com.lx.controller;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
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;
import java.util.Map;

@Slf4j
@RestController
@RequestMapping("/order")
public class OrderController {

    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private DiscoveryClient discoveryClient;

    @GetMapping("/getMediaNameById/{id}")
    public String getMediaNameById(@PathVariable Integer id) {
        List<ServiceInstance> instances = discoveryClient.getInstances("service_media");
        ServiceInstance serviceInstance = instances.get(0);
        String host = serviceInstance.getHost();
        int port = serviceInstance.getPort();
        // 自定义元数据获取
        for(Map.Entry<String, String> entry : serviceInstance.getMetadata().entrySet()) {
            log.info(entry.getKey() + "  -->> " + entry.getValue());
        }
        String url = "http://" + host + ":" + port + "/media/findMeidaById/" + id;
        String name = restTemplate.getForObject(url, String.class);
        return name;
    }
}
  1. 重启server_order,查看eureka server,可以看到server_order已经注册到eureka server
  2. 复制一份server_order,修改端口:8091,搭建集群

Feign远程调用

Feign = RestTemplate + Ribbon + Hystrix,所以整合直接用Feign,以后有机会在单独整合Bibbon、Hystrix

订单微服务引入Feign依赖
  1. pom.xml
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
  1. 启动类
package com.lx;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;

@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class OrderApplication {

    public static void main(String[] args) {
        SpringApplication.run(OrderApplication.class, args);
    }
}
  1. 创建Feign接口和failback实现类

    @PathVariable(“id”) 的value必须 设置,否则会抛异常

    @FeignClient中value的值不能用下划线,坑

package com.lx.service;

import com.lx.service.impl.MediaFailback;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;

@FeignClient(value = "service-media", fallback = MediaFailback.class, path = "/media")
public interface IMediaServiceFeignClient {

    @GetMapping("/findMeidaById/{id}")
    public String findMeidaById(@PathVariable("id") Integer id);
}
package com.lx.service.impl;

import com.lx.service.IMediaServiceFeignClient;
import org.springframework.stereotype.Component;

@Component
public class MediaFailback implements IMediaServiceFeignClient {

    @Override
    public String findMeidaById(Integer id) {
        return "==============>>>>>>>>>进入failback       " + id;
    }
}
  1. controller改造
package com.lx.controller;

import com.lx.service.IMediaServiceFeignClient;
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("/order")
public class OrderController {

    @Autowired
    private IMediaServiceFeignClient mediaServiceFeignClient;

    @GetMapping("/getMediaNameById/{id}")
    public String getMediaNameById(@PathVariable Integer id) {
        String name = mediaServiceFeignClient.findMeidaById(id);
        return name;
    }
}
Feign对Bibbon的支持
  1. application.yml

    增加service_media,针对被调用方微服务生效,不加就是全局生效

    service_media.ribbon.ReadTimeout:Feign的超时时间

    负载均衡策略
    RoundRobinRule:轮询策略
    RandomRule:随机策略
    RetryRule:重试策略
    BestAvailableRule:最⼩连接数策略
    AvailabilityFilteringRule:可⽤过滤策略
    ZoneAvoidanceRule:区域权衡策略(默认策略)
service_media:
  ribbon:
    ConnectTimeout: 2000
    ReadTimeout: 3000
    OkToRetryOnAllOperations: true
    MaxAutoRetries: 0
    MaxAutoRetriesNextServer: 0
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RoundRobinRule
Feign对Hystrix的支持
  1. application.yml

    timeoutInMilliseconds:Hystrix的超时时间

    如果设置了多个超时时间,熔断的时候是根据两个时间的最小值来进行的,进入回退降级逻辑

feign:
  hystrix:
    enabled: true
    
hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 5000
Feign对请求压缩和响应压缩的支持
  1. application.yml
feign:
  compression:
    request:
      enabled: true
      mime-types: text/html,application/xml,application/json
      min-request-size: 2048
    response:
      enabled: true
Feign的日志级别设置
  1. application.yml
logging:
  level:
    com.lx.service.IResumeServiceFeignClient: debug
  1. 增加配置类

    Feign的⽇志级别(Feign请求过程信息)

    NONE:默认的,不显示任何⽇志----性能最好

    BASIC:仅记录请求⽅法、URL、响应状态码以及执⾏时间----⽣产问题追踪

    HEADERS:在BASIC级别的基础上,记录请求和响应的header

    FULL:记录请求和响应的header、body和元数据----适⽤于开发及测试环境定位问题

package com.lx.config;

import feign.Logger;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class FeignConfig {

    @Bean
    Logger.Level feignLevel() {
        return Logger.Level.FULL;
    }
}
  1. 启动服务,测试

GateWay网关组件

GateWay是Spring Cloud的一个全新项目,是取代Netflix Zuul的,是基于Spring5.0 + SpringBoot2.0 + WebFlux(异步非阻塞模型)等技术开发,性能高于Zuul,官方测试,GateWAy是Zuul的1.6倍。

  1. 创建New Module,gateway_server,pom.xml

    GateWay不需要Web模块,它引入WebFlux(类似SpringMVC),所以建立New Module的时候,parent选择none

<!--spring boot 父启动器依赖-->
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.1.6.RELEASE</version>
</parent>

<dependencies>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-commons</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>
    <!--GateWay 网关-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-gateway</artifactId>
    </dependency>
    <!--引入webflux-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-webflux</artifactId>
    </dependency>
    <!--日志依赖-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-logging</artifactId>
    </dependency>
    <!--测试依赖-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
    <!--lombok工具-->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.4</version>
        <scope>provided</scope>
    </dependency>

    <!--引入Jaxb,开始-->
    <dependency>
        <groupId>com.sun.xml.bind</groupId>
        <artifactId>jaxb-core</artifactId>
        <version>2.2.11</version>
    </dependency>
    <dependency>
        <groupId>javax.xml.bind</groupId>
        <artifactId>jaxb-api</artifactId>
    </dependency>
    <dependency>
        <groupId>com.sun.xml.bind</groupId>
        <artifactId>jaxb-impl</artifactId>
        <version>2.2.11</version>
    </dependency>
    <dependency>
        <groupId>org.glassfish.jaxb</groupId>
        <artifactId>jaxb-runtime</artifactId>
        <version>2.2.10-b140310.1920</version>
    </dependency>
    <dependency>
        <groupId>javax.activation</groupId>
        <artifactId>activation</artifactId>
        <version>1.1.1</version>
    </dependency>
    <!--引入Jaxb,结束-->

    <!-- Actuator可以帮助你监控和管理Spring Boot应用-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    <!--热部署-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
        <optional>true</optional>
    </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>
</dependencies>

<dependencyManagement>
    <!--spring cloud依赖版本管理-->
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>Greenwich.RELEASE</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

<build>
    <plugins>
        <!--编译插件-->
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <configuration>
                <source>11</source>
                <target>11</target>
                <encoding>utf-8</encoding>
            </configuration>
        </plugin>
        <!--打包插件-->
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>
  1. application.yml

    动态路由设置,uri以lb://开头(lb代表从注册中心获取服务),后面是需要转发到的服务名称

    GateWay内置了很多predicates功能,实现了各种路由匹配规则

    时间点后匹配- After=2017-01-20T17:42:47.789-07:00[America/Denver]
    时间点前匹配- Before=2017-01-20T17:42:47.789-07:00[America/Denver]
    时间区间匹配- Between=2017-01-20T17:42:47.789-07:00[America/Denver], 2017-01-21T17:42:47.789-07:00[America/Denver]
    指定Cookie正则匹配指定值- Cookie=chocolate, ch.p
    指定Header正则匹配指定值- Header=X-Request-Id, \d+
    请求Host匹配指定值- Host=.somehost.org,.anotherhost.org
    请求Method匹配指定请求⽅式- Method=GET,POST
    请求路径正则匹配- Path=/red/{segment},/blue/{segment}
    请求包含某参数- Query=green
    请求包含某参数并且参数值匹配正则表达式- Query=red, gree.
    远程地址匹配- RemoteAddr=192.168.1.1/24

    StripPrefix:可以去掉meida之后转发

server:
  port: 9002

eureka:
  client:
    serviceUrl:
      defaultZone: http://eurekaServer:8761/eureka/
  instance:
    prefer-ip-address: true
    instance-id: ${spring.cloud.client.ip-address}:${spring.application.name}:${server.port}:@project.version@

spring:
  application:
    name: gateway-server
  cloud:
    gateway:
      routes:
        - id: service-order-router
          uri: lb:/service-order
          predicates:
            - Path=/order/**
        - id: service-media-router
          uri: lb://service-media
          predicates:
            - Path=/media/**
          filters:
            - StripPrefix=1
  1. 启动类
package com.lx;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

@SpringBootApplication
@EnableDiscoveryClient
public class GateWayApplication {

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

  1. 启动服务,测试

    订单测试:http://localhost:9002/order/getMediaNameById/1

    媒资测试:http://localhost:9002/media/media/findMeidaById/1

  2. 自定义全局过滤器实现IP访问限制

    过滤器类型影响范围
    GateWayFilter应⽤到单个路由路由上
    GlobalFilter应⽤到所有的路由上
package com.lx.filter;

import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.util.ArrayList;
import java.util.List;

@Component
public class BlackListFilter implements GlobalFilter, Ordered {

    // 模拟黑名单列表,正常去数据库或缓存中查询
    private static List<String> blackList = new ArrayList<>();

    static {
        blackList.add("0:0:0:0:0:0:0:1");   // 模拟本机地址
    }

    /**
     * 过滤器核心方法
     * @param exchange  封装了request和response对象的上下文
     * @param chain     网关过滤器链(包含全局过滤器和单路过滤器)
     * @return
     */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // 思路: 获取客户端ip,判断是否在黑名单中,在的话就拒绝访问,不在的话就放行
        // 从上下文中取出request和response对象
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();
        // 从request对象中获取客户端ip
        String clientIp = request.getRemoteAddress().getHostString();
        // 拿着clientIp去黑名单中查询,存在的话就拒绝访问
        if(blackList.contains(clientIp)) {
            // 拒绝访问,返回
            response.setStatusCode(HttpStatus.UNAUTHORIZED);        //  状态码
            System.out.println("===============>>>>>IP : " + clientIp + " 在黑名单中,将被拒绝访问!");
            String data = "Request be denied!";
            DataBuffer wrap = response.bufferFactory().wrap(data.getBytes());
            return response.writeWith(Mono.just(wrap));
        }
        // 合法请求, 放行,执行后续的过滤器
        return chain.filter(exchange);
    }

    /**
     * 返回值表示当前过滤器的顺序(优先级),数值越小,优先级越高
     * @return
     */
    @Override
    public int getOrder() {
        return 0;
    }
}
  1. GateWay高可用

    复制一份gateway-server,修改端口:9003,在GateWay上游使用Nginx进行负载转发。

# 配置多个GateWay实例
upstream gateway {
    server 127.0.0.1:9002;
    server 127.0.0.1:9003;
}

location / {
	proxy_pass http://gateway;
}

Config分布式配置中心 + Bus消息总线 + RabbitMq

  1. 建立New Module,config_server,pom.xml
<dependencies>
    <!--eureka client 客户端依赖引入-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>
    <!--config配置中心服务端-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-config-server</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-bus-amqp</artifactId>
    </dependency>
</dependencies>
  1. 启动类
package com.lx;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.config.server.EnableConfigServer;

@SpringBootApplication
@EnableDiscoveryClient
@EnableConfigServer
public class ConfigServerApplication {

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

  1. application.yml

    Spring Cloud Bus支持Kafka,RabbitMq,Config + Bus结合实现配置信息的自动刷新。

server:
  port: 9006

eureka:
  client:
    service-url:
      defaultZone: http:/eurekaServer:8761/eureka
  instance:
    prefer-ip-address: true
    instance-id: ${spring.cloud.client.ip-address}:${spring.application.name}:${server.port}:@project.version@

spring:
  application:
    name: config-server
  cloud:
    config:
      server:
        git:
          uri: https://github.com/username/test.git
          username: username
          password: password
          search-paths:
            - test
      label: master
  rabbitmq:
    host: 127.0.0.1
    port: 5672
    username: guest
    password: guest

management:
  endpoints:
    web:
      exposure:
        include: "*"
  endpoint:
    health:
      show-details: always
  1. 启动服务,访问git下master下的配置文件

    http://localhost:9006/master/service-media-dev.yml

  2. 在媒资微服务上构建client端,pom.xml

<!-- config配置中⼼服务端 -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-config-client</artifactId>
</dependency>
<!-- Config配置手动刷新 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-actuator</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>
  1. application.yml改名为bootstrap.xml

    bootstrap.xml是系统级别的,优先级比application.yml高,应用启动时会检查这个配置文件,在这个配置文件中指定配置中心的服务地址,会自动拉取所有应用配置并且启用。

spring:
  cloud:
    config:
      name: service-media
      profile: dev
      label: master
      uri: http://localhost:9006
  rabbitmq:
    host: 127.0.0.1
    port: 5672
    username: guest
    password: guest
  1. 增加config测试controller
package com.lx.controller;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/config")
@RefreshScope
public class ConfigController {

    @Value("${alert.message}")
    private String alertMessage;
    @Value("${mysql.url}")
    private String mySqlUrl;

    @GetMapping("/viewConfig")
    public String viewConfig() {
        return "lxMessage ====>>>   " + alertMessage + "<br />mySqlUrl    =====>>>>    " + mySqlUrl;
    }
}

  1. 重启媒资微服务,测试链接

    http://localhost:8080/config/viewConfig

  2. 测试自动刷新,向配置中心发送post请求,用Postman测试

    全部刷新:http://localhost:9006/actuator/bus-refresh

    指定刷新:http://localhost:9006/actuator/bus-refresh/server-media:8080

Sleuth + Zipkin分布式链路追踪

链路追踪的本质就是日志记录,把Sleuth的数据信息发送给Zipkin进行聚合,利用Zipkin存储并展示数据。

核心概念:Trace、TraceID、Span、SpanID、事件

CS :client send/start 客户端/消费者发出⼀个请求,描述的是⼀个span开始

SR: server received/start 服务端/⽣产者接收请求 SR-CS属于请求发送的⽹络延迟

SS: server send/fifinish 服务端/⽣产者发送应答 SS-SR属于服务端消耗时间

CR:client received/fifinished 客户端/消费者接收应答 CR-SS表示回复需要的时间(响应的⽹络延迟)

  1. 每一个需要被追踪的微服务都引入依赖
<!--链路追踪-->
<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>
  1. 每一个需要被追踪的微服务application.yml
logging:
  level:
    org.springframework.web.servlet.DispatcherServlet: debug
    org.springframework.cloud.sleuth: debug

重启服务后,请求到来时,在控制台就可以看到Sleuth输出的日志

  1. 创建New Module,zipkin_server,pom.xml
<dependencies>
    <!--zipkin-server的依赖坐标-->
    <dependency>
        <groupId>io.zipkin.java</groupId>
        <artifactId>zipkin-server</artifactId>
        <version>2.12.3</version>
        <exclusions>
            <!--排除掉log4j2的传递依赖,避免和springboot依赖的⽇志组件冲突-->
            <exclusion>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-log4j2</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
    <!--zipkin-server ui界⾯依赖坐标-->
    <dependency>
        <groupId>io.zipkin.java</groupId>
        <artifactId>zipkin-autoconfigure-ui</artifactId>
        <version>2.12.3</version>
    </dependency>
    <dependency>
        <groupId>io.zipkin.java</groupId>
        <artifactId>zipkin-autoconfigure-storage-mysql</artifactId>
        <version>2.12.3</version>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
    </dependency>
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid-spring-boot-starter</artifactId>
        <version>1.1.10</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-tx</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
    </dependency>
</dependencies>
  1. 启动类
package com.lx;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import zipkin2.server.internal.EnableZipkinServer;
import javax.sql.DataSource;

@SpringBootApplication
@EnableZipkinServer
public class ZipkinServerApplication {

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

    @Bean
    public PlatformTransactionManager transactionManager(DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }
}
  1. application.yml

    zipkin可以持久化到mysql

server:
  port: 9411

management:
  metrics:
    web:
      server:
        auto-time-requests: false

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/zipkin?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowMultiQueries=true
    username: root
    password: 123456
    druid:
      initialSize: 10
      minIdle: 10
      maxActive: 30
      maxWait: 50000

zipkin:
  storage:
    type: mysql
  1. 对被追踪的微服务application.yml增加

    spring.zipkin.sender.type:web / kafka / rabbit

    spring.sleuth.sampler.probability:采样率,1代表100%,默认0.1,代表10%

spring:
  zipkin:
    base-url: http://127.0.0.1:9411
    sender:
      type: web
  sleuth:
    sampler:
      probability: 1
  1. zipkin持久化到mysql,需要创建数据库zipkin,语句官方有提供

    https://github.com/openzipkin/zipkin/blob/master/zipkin-storage/mysql-v1/src/main/resources/mysql.sql

  2. 启动服务,测试,先请求order微服务,然后在zipkin中查看

    查看链路追踪:http://localhost:9411

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值