SpringCloud

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-N9juMG4q-1615811544821)(Pictures/1587880250279.png)]

文章目录

一、SpringCloud介绍


1.1 微服务架构

微服务架构的提出者:马丁福勒

https://martinfowler.com/articles/microservices.html

简而言之,微服务架构样式[1]是一种将单个应用程序开发为一组小服务的方法,每个小服务都在自己的进程中运行并与轻量级机制(通常是HTTP资源API)进行通信。这些服务围绕业务功能构建,并且可以由全自动部署机制独立部署。这些服务的集中管理几乎没有,它可以用不同的编程语言编写并使用不同的数据存储技术。

1、 微服务架构只是一个样式,一个风格。

2、 将一个完整的项目,拆分成多个模块去分别开发。

3、 每一个模块都是单独的运行在自己的容器中。

4、 每一个模块都是需要相互通讯的。 Http,RPC,MQ。

5、 每一个模块之间是没有依赖关系的,单独的部署。

6、 可以使用多种语言去开发不同的模块。

7、 使用MySQL数据库,Redis,ES去存储数据,也可以使用多个MySQL数据库。

总结:将复杂臃肿的单体应用进行细粒度的划分,每个拆分出来的服务各自打包部署。

1.2 SpringCloud介绍
  • SpringCloud是微服务架构落地的一套技术栈。

  • SpringCloud中的大多数技术都是基于Netflix公司的技术进行二次研发。

  • SpringCloud的中文社区网站:http://springcloud.cn/

  • SpringCloud的中文网:http://springcloud.cc/

  • 八个技术点:

    • Eureka - 服务的注册与发现
    • Robbin - 服务之间的负载均衡
    • Feign - 服务之间的通讯
    • Hystrix - 服务的线程隔离以及断路器
    • Zuul - 服务网关
    • Stream - 实现MQ的使用
    • Config - 动态配置
    • Sleuth - 服务追踪

二、服务的注册与发现-Eureka【重点


2.1 引言

Eureka就是帮助我们维护所有服务的信息,以便服务之间的相互调用

Eureka
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-98H9C7l8-1615811546295)(Pictures/1587887319057.png)]
2.2 Eureka的快速入门
2.2.1 创建EurekaServer

创建一个父工程,并且在父工程中指定SpringCloud的版本,并且将packaing修改为pom jar war

<packaging>pom</packaging>

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>Greenwich.SR5</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

创建eureka的server,创建SpringBoot工程,并且导入依赖,在启动类中添加注解,编写yml文件

导入依赖

<dependencies>
    <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>
</dependencies>

启动类添加注解

@SpringBootApplication
@EnableEurekaServer
public class EurekaApplication {

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

}

编写yml配置文件

server:
  port: 8761      # 端口号

eureka:
  instance:
    hostname: localhost   # localhost
  client:
    # 当前的eureka服务是单机版的
    registerWithEureka: false
    fetchRegistry: false
    serviceUrl:
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
2.2.2 创建EurekaClient

创建Maven工程,修改为SpringBoot

导入依赖

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

在启动类上添加注解

@SpringBootApplication
@EnableEurekaClient
public class CustomerApplication {

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

}

编写配置文件

# 指定Eureka服务地址
eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka

#指定服务的名称
spring:
  application:
    name: CUSTOMER

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-t848hdQg-1615811544823)(Pictures/1587912052479.png)]

2.2.3 测试Eureka

创建了一个Search搜索模块,并且注册到Eureka

使用到EurekaClient的对象去获取服务信息

@Autowired
private EurekaClient eurekaClient;

正常RestTemplate调用即可

@GetMapping("/customer")
public String customer(){
    //1. 通过eurekaClient获取到SEARCH服务的信息
    InstanceInfo info = eurekaClient.getNextServerFromEureka("SEARCH", false);

    //2. 获取到访问的地址
    String url = info.getHomePageUrl();
    System.out.println(url);//http://localhost:8082/search

    //3. 通过restTemplate访问
    String result = restTemplate.getForObject(url + "/search", String.class);

    //4. 返回
    return result;
}
2.3 Eureka的安全性

实现Eureka认证

导入依赖

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

编写配置类

默认情况下,当 spring security 要求用户向应用程序发送请求时,都必须携带一个有效的 csrf 令牌,而 Eureka 客户端通常不会拥有有效的跨站点请求(CSRF)令牌,此时 Eureka Server 端应该对 eureka 的请求路径放行。

CSRF(Cross-site request forgery) 是指跨站请求伪造,是 web 常见的攻击之一,从 Spring Security 4.0 开始,默认情况下 security 会启用 CSRF 保护,以防止 CSRF 攻击应用程序。

@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // 忽略掉/eureka/**
        http.csrf().ignoringAntMatchers("/eureka/**");
        super.configure(http);
    }
}

编写配置文件

# 指定用户名和密码
spring:
  security:
    user:
      name: root
      password: root

其他服务想注册到Eureka上需要添加用户名和密码

eureka:
  client:
    service-url:
      defaultZone: http://用户名:密码@localhost:8761/eureka
2.4 Eureka的高可用

如果程序的正在运行,突然Eureka宕机了。

  • 如果调用方访问过一次被调用方了,Eureka的宕机不会影响到功能。

  • 如果调用方没有访问过被调用方,Eureka的宕机就会造成当前功能不可用。

搭建Eureka高可用

准备多台Eureka

采用了复制的方式,删除iml和target文件,并且修改pom.xml中的项目名称,再给父工程添加一个module

让服务注册到多台Eureka

eureka:
  client:
    service-url:
      defaultZone: http://root:root@localhost:8761/eureka,http://root:root@localhost:8762/eureka

让多台Eureka之间相互通讯

eureka:
  client:
    registerWithEureka: true      # 注册到Eureka上
    fetchRegistry: true           # 从Eureka拉取信息
    serviceUrl:
      defaultZone: http://root:root@localhost:8762/eureka/
2.5 Eureka的细节

EurekaClient启动时,将自己的信息注册到EurekaServer上,EurekaSever就会存储上EurekaClient的注册信息。

当EurekaClient调用服务时,本地没有注册信息的缓存时,去EurekaServer中去获取注册信息。

EurekaClient会通过心跳的方式去和EurekaServer进行连接。(默认30sEurekaClient会发送一次心跳请求,如果超过了90s还没有发送心跳信息的话,EurekaServer就认为你宕机了,将当前EurekaClient从注册表中移除)

eureka:
  instance:
    lease-renewal-interval-in-seconds: 30      #心跳的间隔
    lease-expiration-duration-in-seconds: 90    # 多久没发送,就认为你宕机了

EurekaClient会每隔30s去EurekaServer中去更新本地的注册表

eureka:
  client:
    registry-fetch-interval-seconds: 30 # 每隔多久去拉取服务注册信息

Eureka的自我保护机制,统计15分钟内,如果一个服务的心跳发送比例低于85%,EurekaServer就会开启自我保护机制

  • 不会从EurekaServer中去移除长时间没有收到心跳的服务。
  • EurekaServer还是可以正常提供服务的。
  • 网络比较稳定时,EurekaServer才会开始将自己的信息被其他节点同步过去
eureka:
  server:
    enable-self-preservation: true  # 开启自我保护机制

CAP定理,C - 一致性,A-可用性,P-分区容错性,这三个特性在分布式环境下,只能满足2个,而且分区容错性(分区容错性是指系统能够容忍节点之间的网络通信的故障)在分布式环境下,是必须要满足的,只能在AC之间进行权衡。

(一)一致性(C)

在分布式系统中的所有数据备份,在同一时刻是否同样的值。(等同于所有节点访问同一份最新的数据副本)

(二)可用性(A)

在集群中一部分节点故障后,集群整体是否还能响应客户端的读写请求。(对数据更新具备高可用性)

(三)分区容错性(P)

以实际效果而言,分区相当于对通信的时限要求。系统如果不能在时限内达成数据一致性,就意味着发生了分区的情况,必须就当前操作在C和A之间做出选择。

总结:

以上可以知道分区容错性(P)主要代表网络波动产生的错误,这是不可避免的,且这个三个模式不可兼得,所以目前就只有两种模式:CP和AP模式。

其中CP表示遵循一致性原则,但不能保证高可用性,其中zookeeper作为注册中心就是采用CP模式,因为zookeeper有过半节点不可以的话整个zookeeper将不可用。

AP表示遵循于可用性原则,例如Eureka作为注册中心用的是AP模式,因为其为去中心化,采用你中有我我中有你的相互注册方式,只要集群中有一个节点可以使用,整个eureka服务就是可用的,但可能会出现短暂的数据不一致问题。

三、服务间的负载均衡-Ribbon【重点


3.1 引言

Robbin是帮助我们实现服务和服务负载均衡,Robbin属于客户端负载均衡

客户端负载均衡:customer客户模块,将2个Search模块信息全部拉取到本地的缓存,在customer中自己做一个负载均衡的策略,选中某一个服务。

服务端负载均衡:在注册中心中,直接根据你指定的负载均衡策略,帮你选中一个指定的服务信息,并返回。

Robbin
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JJ8mchnR-1615811546297)(Pictures/1587977965492.png)]
3.2 Ribbon的快速入门

启动两个search模块 vm options设置值为-Dserver.port=8082

在customer导入robbin依赖

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

配置整合RestTemplate和Robbin

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

在customer中去访问search

@GetMapping("/customer")
public String customer(){
    String result = restTemplate.getForObject("http://SEARCH/search", String.class);

    //4. 返回
    return result;
}
3.3 Ribbon配置负载均衡策略

负载均衡策略

  • RandomRule:随机策略
  • RoundRobbinRule:轮询策略
  • WeightedResponseTimeRule:默认会采用轮询的策略,后续会根据服务的响应时间,自动给你分配权重
  • BestAvailableRule:根据被调用方并发数最小的去分配

采用注解的形式,所有的服务都采用这个负载均衡策略

@Bean
public IRule robbinRule(){
    return new RandomRule();
}

配置文件去指定负载均衡的策略(推荐)

# 指定具体服务的负载均衡策略
SEARCH:      # 编写服务名称
  ribbon:
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.WeightedResponseTimeRule  # 具体负载均衡使用的类

四、服务间的调用-Feign【重点


4.1 引言

Feign可以帮助我们实现面向接口编程,就直接调用其他的服务,简化开发。

4.2 Feign的快速入门

导入依赖

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

添加一个注解

@EnableFeignClients

创建一个接口,并且和Search模块做映射

@FeignClient("SEARCH")   // 指定服务名称
public interface SearchClient {
    
    // value -> 目标服务的请求路径,method -> 映射请求方式
    @RequestMapping(value = "/search",method = RequestMethod.GET)
    String search();

}

测试使用

@Autowired
private SearchClient searchClient;

@GetMapping("/customer")
public String customer(){
    String result = searchClient.search();
    return result;
}

fegin底层使用了ribbon作为负载均衡

SEARCH:
  ribbon:
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
4.3 Feign的传递参数方式

注意事项

  • 如果你传递的参数,比较复杂时,默认会采用POST的请求方式。
  • 传递单个参数时,推荐使用@PathVariable,如果传递的单个参数比较多,这里也可以采用@RequestParam,不要省略value属性
  • 传递对象信息时,统一采用json的方式,添加@RequestBody
  • Client接口必须采用@RequestMapping

在Search模块下准备三个接口

 @RequestMapping("/search/{id}")
    public Customer findById(@PathVariable Integer id){

        return new Customer(id,"zhangsan",19);
    }

    @RequestMapping("/getCustomer")
    public Customer getCustomer(Integer id, String name){
        return new Customer(id,name,30);
    }

    @RequestMapping("/add")
    public Customer add(Customer customer){
        return customer;
    }

封装Customer模块下的Controller

@GetMapping("/customer/{id}")
public Customer findById(@PathVariable Integer id){
    return searchClient.findById(id);
}

@GetMapping("/getCustomer")
public Customer getCustomer(@RequestParam Integer id, @RequestParam String name){
    return searchClient.getCustomer(id,name);
}

@GetMapping("/save")            // 会自动转换为POST请求  405
public Customer save(Customer customer){
    return searchClient.save(customer);
}

Customer模块再封装Client接口

@RequestMapping(value = "/search/{id}",method = RequestMethod.GET)
Customer findById(@PathVariable(value = "id") Integer id);

@RequestMapping(value = "/getCustomer",method = RequestMethod.GET)
Customer getCustomer(@RequestParam(value = "id") Integer id, @RequestParam(value = "name") String name);

@RequestMapping(value = "/save",method = RequestMethod.POST)
Customer save(@RequestBody Customer customer);

测试

4.4 Feign的Fallback

Fallback可以帮助我们在使用Feign去调用另外一个服务时,如果出现了问题,走服务降级,返回一个错误数据,避免功能因为一个服务出现问题,全部失效。

4.4.1 FallBack方式

创建一个POJO类,实现Client接口。

@Component
public class SearchClientFallBack implements SearchClient {
    @Override
    public String search() {
        return "出现问题啦!!!";
    }

    @Override
    public Customer findById(Integer id) {
        return null;
    }

    @Override
    public Customer getCustomer(Integer id, String name) {
        return null;
    }

    @Override
    public Customer save(Customer customer) {
        return null;
    }
}

修改CLient接口中的注解,添加一个属性。

@FeignClient(value = "SEARCH",fallback = SearchClientFallBack.class)

添加一个配置文件。

# feign和hystrix组件整合
feign:
  hystrix:
    enabled: true
4.4.1 FallBackFactory方式

调用方无法知道具体的错误信息是什么,通过FallBackFactory的方式去实现这个功能

FallBackFactory基于Fallback

创建一个POJO类,实现FallBackFactory

@Component
public class SearchClientFallBackFactory implements FallbackFactory<SearchClient> {

    @Autowired
    private SearchClientFallBack searchClientFallBack;

    @Override
    public SearchClient create(Throwable throwable) {
        throwable.printStackTrace();
        return searchClientFallBack;
    }
}

修改Client接口中的属性

@FeignClient(value = "SEARCH",fallbackFactory = SearchClientFallBackFactory.class)

五、服务的隔离及断路器-Hystrix【重点


5.1 引言
Hystrix
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-90OpZtj4-1615811546298)(Pictures/1588044427589.png)]
5.2 降级机制实现

导入依赖

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

添加一个注解

@EnableCircuitBreaker

在CustomerController中针对某一个接口去编写他的降级方法

@GetMapping("/customer/{id}")
@HystrixCommand(fallbackMethod = "findByIdFallBack")
public Customer findById(@PathVariable Integer id){
    int i = 1/0;
    return searchClient.findById(id);
}

// findById的降级方法  方法的描述要和接口一致
public Customer findByIdFallBack(Integer id){
    return new Customer(-1,"",0);
}

在接口上添加注解

@HystrixCommand(fallbackMethod = "findByIdFallBack")

5、 测试一下

效果
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xAT05Irv-1615811546300)(Pictures/1588048924123.png)]
5.3 线程隔离

在一个基于微服务的应用程序中,您通常需要调用多个微服务完成一个特定任务。这些调用默认是使用相同的线程来执行调用的,这些线程Java容器为处理所有请求预留的。在高服务器请求的情况下,一个性能较低的服务会“霸占”java容器中绝大多数线程,而其它性能正常的服务的请求则需要等待线程资源的释放。最后,整个java容器会崩溃。

  • Hystrix的线程池(默认),当用户请求服务A和服务I的时候,tomcat的线程(图中蓝色箭头标注)会将请求的任务交给服务A和服务I的内部线程池里面的线程(图中橘色箭头标注)来执行,tomcat的线程就可以去干别的事情去了,当服务A和服务I自己线程池里面的线程执行完任务之后,就会将调用的结果返回给tomcat的线程,从而实现资源的隔离,当有大量并发的时候,服务内部的线程池的数量就决定了整个服务的并发度,例如服务A的线程池大小为10个,当同时有12请求时,只会允许10个任务在执行,其他的任务被放在线程池队列中,或者是直接走降级服务,此时,如果服务A挂了,就不会造成大量的tomcat线程被服务A拖死,服务I依然能够提供服务。整个系统不会受太大的影响。

  • 信号量,信号量的资源隔离只是起到一个开关的作用,例如,服务X的信号量大小为10,那么同时只允许10个tomcat的线程(此处是tomcat的线程,而不是服务X的独立线程池里面的线程)来访问服务X,其他的请求就会被拒绝,从而达到限流保护的作用。

Hystrix的线程池的配置(具体配置的name属性要查看HystrixCommandProperties类)

配置信息namevalue
线程隔离策略execution.isolation.strategyTHREAD
指定超时时间execution.isolation.thread.timeoutInMilliseconds1000
是否开启超时时间配置execution.timeout.enabledtrue
超时之后是否中断线程execution.isolation.thread.interruptOnTimeouttrue
取消任务后是否中断线程execution.isolation.thread.interruptOnCancelfalse

代码实现

@GetMapping("/customer/{id}")
@HystrixCommand(fallbackMethod = "findByIdFallBack",commandProperties = {
        @HystrixProperty(name = "execution.isolation.strategy",value = "THREAD"),
        @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "3000")
})
public Customer findById(@PathVariable Integer id) throws InterruptedException {
    System.out.println(Thread.currentThread().getName());
    Thread.sleep(3000);
    return searchClient.findById(id);
}

信号量的配置信息

配置信息namevalue
线程隔离策略execution.isolation.strategySEMAPHORE
指定信号量的最大并发请求数execution.isolation.semaphore.maxConcurrentRequests10

代码实现

@GetMapping("/customer/{id}")
@HystrixCommand(fallbackMethod = "findByIdFallBack",commandProperties = {
        @HystrixProperty(name = "execution.isolation.strategy",value = "SEMAPHORE")
})
public Customer findById(@PathVariable Integer id) throws InterruptedException {
    System.out.println(Thread.currentThread().getName());
    return searchClient.findById(id);
}
5.4 断路器
5.4.1 断路器介绍

马丁福勒断路器论文:https://martinfowler.com/bliki/CircuitBreaker.html

在调用指定服务时,如果说这个服务的失败率达到你输入的一个阈值,将断路器从closed状态,转变为open状态,指定服务时无法被访问的,如果你访问就直接走fallback方法,在一定的时间内,open状态会再次转变为half open状态,允许一个请求发送到我的指定服务,如果成功,转变为closed,如果失败,服务再次转变为open状态,会再次循环到half open,直到断路器回到一个closed状态。

断路器
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vlVJb7Lk-1615811546301)(Pictures/1588075354295.png)]
5.4.2 配置断路器的监控界面

导入依赖

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

在启动类中添加注解

@EnableHystrixDashboard

配置一个Servlet路径,指定上Hystrix的Servlet

@WebServlet("/hystrix.stream")
public class HystrixServlet extends HystrixMetricsStreamServlet {
}

//------------------------------------------------------------
// 在启动类上,添加扫描Servlet的注解
@ServletComponentScan("com.qf.servlet")

测试直接访问http://host:port/hystrix

监控界面
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZkxLP2t8-1615811546302)(Pictures/1588078651357.png)]

在当前位置输入映射好的servlet路径

检测效果
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1Ry8NO5y-1615811546303)(Pictures/1588078701738.png)]
5.4.3 配置断路器的属性

断路器的属性(默认10s秒之内请求数)

配置信息namevalue
断路器的开关circuitBreaker.enabledtrue
失败阈值的总请求数circuitBreaker.requestVolumeThreshold20
请求总数失败率达到%多少时circuitBreaker.errorThresholdPercentage50
断路器open状态后,多少秒是拒绝请求的circuitBreaker.sleepWindowInMilliseconds5000
强制让服务拒绝请求circuitBreaker.forceOpenfalse
强制让服务接收请求circuitBreaker.forceClosedfalse

具体配置方式

@GetMapping("/customer/{id}")
@HystrixCommand(fallbackMethod = "findByIdFallBack",commandProperties = {
    @HystrixProperty(name = "circuitBreaker.enabled",value = "true"),
    @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold",value = "10"),
    @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage",value = "70"),
    @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds",value = "5000")
})
public Customer findById(@PathVariable Integer id) throws InterruptedException {
        System.out.println(Thread.currentThre![请添加图片描述](https://img-blog.csdnimg.cn/20210315203351382.png)
ad().getName());
        if(id==1){
            int i = 1/0;
        }
//        Thread.sleep(3000);
//        int i=1/0;
        return searchClient.findById(id);
    }
5.5 请求缓存
5.5.1 请求缓存介绍
  • 请求缓存的生命周期是一次请求

  • 请求缓存是缓存当前线程中的一个方法,将方法参数作为key,方法的返回结果作为value

  • 在一次请求中,目标方法被调用过一次,以后就都会被缓存。

请求缓存
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vEbMU4I6-1615811546304)(Pictures/1588127678866.png)]
5.5.2 请求缓存的实现

创建一个Service,在Service中调用Search服务。

@Service
public class CustomerService {

    @Autowired
    private SearchClient searchClient;


    @CacheResult
    @HystrixCommand(commandKey = "findById")
    public Customer findById(@CacheKey Integer id) throws InterruptedException {
        return searchClient.findById(id);
    }

    @CacheRemove(commandKey = "findById")
    @HystrixCommand
    public void clearFindById(@CacheKey Integer id){
        System.out.println("findById被清空");
    }

}

使用请求缓存的注解

@CacheResult:帮助我们缓存当前方法的返回结果(必须@HystrixCommand配合使用)
@CacheRemove:帮助我们清除某一个缓存信息(基于commandKey)
@CacheKey:指定哪个方法参数作为缓存的标识

修改Search模块的返回结果

return new Customer(1,"张三",(int)(Math.random() * 100000));

编写Filter,去构建HystrixRequestContext

@WebFilter("/*")
public class HystrixRequestContextInitFilter implements Filter {

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HystrixRequestContext.initializeContext();
        filterChain.doFilter(servletRequest,servletResponse);
    }
}

Customer项目启动类加上扫描filter包 @ServletComponentScan({“com.qf.customer.servlet”,“com.qf.customer.filter”})

修改Controller

public Customer findById(@PathVariable Integer id) throws InterruptedException {
    System.out.println(customerService.findById(id));
    System.out.println(customerService.findById(id));
    customerService.clearFindById(id);
    System.out.println(customerService.findById(id));
    System.out.println(customerService.findById(id));
    return searchClient.findById(id);
}

测试结果

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uboeNc77-1615811544825)(Pictures/1588133189886.png)]

六、服务的网关-Zuul【重点


6.1 引言
  • 客户端维护大量的ip和port信息,直接访问指定服务
  • 认证和授权操作,需要在每一个模块中都添加认证和授权的操作
  • 项目的迭代,服务要拆分,服务要合并,需要客户端进行大量的变化
  • 统一的把安全性校验都放在Zuul中
  • Zuul=route+filter
zuul
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ch8IRKa3-1615811546305)(Pictures/1588145514669.png)]
6.2 Zuul的快速入门

创建Maven项目,修改为SpringBoot

导入依赖

<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

编写配置文件

# 指定Eureka服务地址
eureka:
  client:
    service-url:
      defaultZone: http://root:root@localhost:8761/eureka,http://root:root@localhost:8762/eureka

#指定服务的名称
spring:
  application:
    name: ZUUL

server:
  port: 80

直接测试

测试效果
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AhywvSV7-1615811546306)(Pictures/1588148029538.png)]
6.3 Zuul常用配置信息
6.3.1 Zuul的监控界面

导入依赖 actuator是监控系统健康情况的工具

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

编写配置文件

# 查看zuul的监控界面(开发时,配置为*,上线,不要配置)
management:
  endpoints:
    web:
      exposure:
        include: "*"

直接访问

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sYDUrHKi-1615811544827)(Pictures/1588157803975.png)]

6.3.2 忽略服务配置
# zuul的配置
zuul:
  # 基于服务名忽略服务,无法查看 ,如果要忽略全部的默认服务,ignored-services可以配置"*",但对自定义服务的配置,是无法忽略的
  # ignored-patterns 对自定义服务的配置,是可以忽略的
  ignored-services: eureka
  # 监控界面依然可以查看,在访问的时候,404,地址的忽略方法会忽略自定义的服务配置
  ignored-patterns: /**/search/**

测试,访问http://localhost/search/search/2,会报404

6.3.3 自定义服务配置
# zuul的配置
zuul:
  # 指定自定义服务(方式一 , key(服务名):value(路径))
#  routes:
#    search: /ss/**
#    customer: /cc/**
  # 指定自定义服务(方式二)
  routes:
    kehu:   # 自定义名称
      path: /ccc/**     # 映射的路径
      serviceId: customer   # 服务名称
6.3.4 灰度发布

在Zuul的启动类中添加一个配置类

@Bean
public PatternServiceRouteMapper serviceRouteMapper() {
    return new PatternServiceRouteMapper(
        "(?<name>^.+)-(?<version>v.+$)",
        "${version}/${name}");
}

在Customer模块中的application.yml文档编写如下配置

version: v1

#指定服务的名称
spring:
  application:
    name: CUSTOMER-${version}

再复制一个Customer服务,VM options=-Dversion=v2 -Dserver.port=9001

修改Zuul的配置

# zuul的配置
zuul:
  # 基于服务名忽略服务,无法查看  , 如果需要用到-v的方式,一定要忽略掉
  # ignored-services: "*"

CustomerController代码

    @Value("${version}")
    private String version;


    @GetMapping("/version")
    public String version() throws InterruptedException {
        return version;
    }

测试

测试效果
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Dy2ZcK5g-1615811546307)(Pictures/1588165064217.png)]
6.4 Zuul的过滤器执行流程 routes filter

客户端请求发送到Zuul服务上,首先通过PreFilter链,如果正常放行,会把请求再次转发给RoutingFilter,请求转发到一个指定的服务,在指定的服务响应一个结果之后,再次走一个PostFilter的过滤器链,最终再将响应信息交给客户端。

过滤器
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GOExV7KE-1615811546307)(Pictures/1588172828199.png)]
6.5 Zuul过滤器入门

创建POJO类,继承ZuulFilter抽象类

@Component
public class TestZuulFilter 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 {
    System.out.println("prefix过滤器执行~~~");
    return null;
}

测试

效果
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bntXNiL2-1615811546308)(Pictures/1588175034889.png)]
6.6 PreFilter实现token校验

准备访问路径,请求参数传递token

http://localhost/v1/customer/version?token=123

创建AuthenticationFilter

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

    @Override
    public int filterOrder() {
        return PRE_DECORATION_FILTER_ORDER - 2;
    }

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

    @Override
    public Object run() throws ZuulException {
        //..
    }
    
}

在run方法中编写具体的业务逻辑代码

@Override
public Object run() throws ZuulException {
    //1. 获取Request对象
    RequestContext requestContext = RequestContext.getCurrentContext();
    HttpServletRequest request = requestContext.getRequest();

    //2. 获取token参数
    String token = request.getParameter("token");

    //3. 对比token
    if(token == null || !"123".equalsIgnoreCase(token)) {
        //4. token校验失败,直接响应数据
        requestContext.setSendZuulResponse(false);
        requestContext.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value());
    }
    return null;
}

测试

效果
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-W8l88WVa-1615811546309)(Pictures/1588177367016.png)]
6.7 Zuul的降级

创建POJO类,实现接口FallbackProvider

Zuul的fallback容错处理逻辑,只针对timeout异常处理,当请求被Zuul路由后,只要服务有返回(包括异常),都不会触发Zuul的fallback容错逻辑。

@Component
public class ZuulFallBack implements FallbackProvider {}

重写两个方法

@Override
public String getRoute() {
    return "*";   // 代表指定全部出现问题的服务,都走这个降级方法
}

@Override
public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
    System.out.println("降级的服务:" + route);
    cause.printStackTrace();

    return new ClientHttpResponse() {
        @Override
        public HttpStatus getStatusCode() throws IOException {
            // 指定具体的HttpStatus
            return HttpStatus.INTERNAL_SERVER_ERROR;
        }

        @Override
        public int getRawStatusCode() throws IOException {
            // 返回的状态码
            return HttpStatus.INTERNAL_SERVER_ERROR.value();
        }

        @Override
        public String getStatusText() throws IOException {
            // 指定错误信息
            return HttpStatus.INTERNAL_SERVER_ERROR.getReasonPhrase();
        }

        @Override
        public void close() {

        }

        @Override
        public InputStream getBody() throws IOException {
            // 给用户响应的信息
            String msg = "当前服务:" + route + "出现问题!!!";
            return new ByteArrayInputStream(msg.getBytes());
        }

        @Override
        public HttpHeaders getHeaders() {
            // 指定响应头信息
            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.APPLICATION_JSON);
            return headers;
        }
    };
}

测试

效果
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dti0dPNZ-1615811546309)(Pictures/1588180538336.png)]
6.8 Zuul动态路由

创建一个过滤器,执行顺序最好放在Pre过滤器的最后面

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

    @Override
    public int filterOrder() {
        return FilterConstants.PRE_DECORATION_FILTER_ORDER+2;
    }

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

    @Override
    public Object run() throws ZuulException {
        RequestContext requestContext = RequestContext.getCurrentContext();
        HttpServletRequest request = requestContext.getRequest();

        String redisKey = request.getParameter("redisKey");
        if(redisKey!=null && "customer".equals(redisKey)){
            //如果携带的redisKey是customer,则跳转到以下地址
            // http://localhost:8080/customer
            requestContext.put(FilterConstants.SERVICE_ID_KEY,"customer-v1");
            requestContext.put(FilterConstants.REQUEST_URI_KEY,"/customer");
        }else if(redisKey!=null && "search".equals(redisKey)){
            // http://localhost:8081/search/1
            requestContext.put(FilterConstants.SERVICE_ID_KEY,"search");
            requestContext.put(FilterConstants.REQUEST_URI_KEY,"/search/1");
        }
        return null;
    }
}

测试

效果
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YKw4Fv2d-1615811546310)(Pictures/1588185307172.png)]

七、多语言支持-Sidecar


7.1 引言

在SpringCloud的项目中,需要接入一些非Java的程序,第三方接口,无法接入eureka,hystrix,feign等等组件。启动一个代理的微服务,代理微服务去和非Java的程序或第三方接口交流,通过代理的微服务去接入SpringCloud的相关组件。

sidecar
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-o0QgPM1o-1615811546311)(Pictures/1588234895867.png)]
7.2 Sidecar实现
7.2.1 创建一个第三方的服务,创建一个SpringBoot工程 other_serivce

在application.properties文件中配置项目的端口号

server.port=7001

编写Controller

@RestController
public class TestController {

    @GetMapping("/list")
    public String list(){
        return "other-service:list";
    }
}
7.2.2 创建maven工程06_sidecar,修改为SpringBoot

导入依赖

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

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

添加注解,访注解为复合注解,这个注解包括@EnableCircuitBreaker,@EnableDiscoveryClient,和@EnableZuulProxy

@EnableSidecar

编写配置文件

server:
  port: 81

# 指定Eureka服务地址
eureka:
  client:
    service-url:
      defaultZone: http://root:root@localhost:8761/eureka,http://root:root@localhost:8762/eureka
# 指定服务名称
spring:
  application:
    name: sidecar

# 指定代理的第三方服务
sidecar:
  port: 7001
7.3.3 Customer项目中添加Feign和Controller

Feign的编写

@FeignClient("SIDECAR")
public interface OtherSeriviceFeign {

    @GetMapping("/list")
    String list();
}

Controller中的编写

    @Autowired
    private OtherSeriviceFeign otherSeriviceFeign;

    @GetMapping("/list")
    public String list(){
        return otherSeriviceFeign.list();
    }

通过customer通过feign调用第三方服务

效果
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gdcQ2LOM-1615811546311)(Pictures/1588237130846.png)]

八、服务间消息传递-Stream


8.1 引言

Stream就是在消息队列的基础上,对其进行封装,让咱们更方便的去操作MQ消息队列。

效果
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9NGo6Hsu-1615811546312)(Pictures/1588245420362.png)]
8.2 Stream快速入门

启动RabbitMQ

在Search项目中添加消费者-导入依赖

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

消费者-配置文件

spring:
  # 连接RabbitMQ
  rabbitmq:
    host: 192.168.206.142
    port: 5672
    username: test
    password: 123
    virtual-host: /test

创建消息通道绑定的接口 @Input接口表示作为消息的消费者,监听的是myMessage的交换机。Spring会自动帮我们创建队列并绑定到交换机上。

public interface StreamClient {
    @Input("myMessage")
    SubscribableChannel input();
}

监听类来监听队列,获取消息。@EnableBinding注解绑定定义了 @Input注解的接口,以此实现对消息通道(Channel)的绑定。@StreamListener用在方法上,用来监听指定的交换机,因为会默认创建一个队列绑定到交换机,所以监听交换机实际上就是监听队列

@Component
@EnableBinding(StreamClient.class)
public class StreamReceiver {
    
    @StreamListener("myMessage")
    public void msg(Object msg){
        System.out.println("接收到消息: " + msg);
    }
}

生产者-导入依赖

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

生产者-配置文件

spring:
  # 连接RabbitMQ
  rabbitmq:
    host: 192.168.199.109
    port: 5672
    username: test
    password: test
    virtual-host: /test

创建消息通道绑定的接口 @Output注解表示作为消息的生产者,会将消息发送到名称为myMessage的交换机

public interface StreamClient {
    @Output("myMessage")
    MessageChannel output();
}

启动类添加注解 ,绑定定义了 @Output注解的接口,以此实现对消息通道(Channel)的绑定

@EnableBinding(StreamClient.class)

Controller的实现

@RestController
public class MessageController {

    @Autowired
    private StreamClient streamClient;

    @GetMapping("/send")
    public String send(){
        streamClient.output().send(MessageBuilder.withPayload("hello,Stream!!").build());
        return "消息发送成功";
    }
}
8.3 Stream重复消费问题

在消费方Search项目中需要添加一个配置,指定消费者组

指定消费者组后,只会有一个消费者接收和消费。所以之前会出现重复消费的问题,是由于默认情况下,任何消费者都会产生一个匿名消费组,所以每个消费者实例都会有自己的消费组,从而当有消息发送的时候,就形成了广播的模式。

spring:
  cloud:
    stream:
      bindings:
        myMessage:				# 队列名称
          group: customer      # 消费者组
8.4 Stream的消费者手动ack

在Search项目编写配置

spring:
  cloud:
    stream:
      # 实现手动ACK
      rabbit:
        bindings:
          myMessage:
            consumer:
              acknowledgeMode: MANUAL

Search项目修改消费端方法

@StreamListener("myMessage")
public void msg(Object msg,
                @Header(name = AmqpHeaders.CHANNEL) Channel channel,
                @Header(name = AmqpHeaders.DELIVERY_TAG) Long deliveryTag) throws IOException {
    System.out.println("接收到消息: " + msg);
    channel.basicAck(deliveryTag,false);
}

九、服务的动态配置-Config【重点


9.1 引言
  • 配置文件分散在不同的项目中,不方便维护。

  • 配置文件的安全问题。

  • 修改完配置文件,无法立即生效。

config
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-w394SpK8-1615811546313)(Pictures/1588257620824.png)]
9.2 搭建Config-Server

创建Maven工程,修改为SpringBoot

导入依赖

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

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

添加注解

@EnableConfigServer

编写配置文件(Git的操作)

spring:
  cloud:
    config:
      server:
        git:
          basedir: D:\basedir    # 本地仓库的地址
          username: chenzetao666    #  远程仓库用户名
          password: czt136204   #   远程仓库密码
          uri: https://gitee.com/chenzetao666/config_test.git       # 远程仓库地址

测试(http://localhost:port/{label}/{application}-{profile}.yml)

效果
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2gPWzDBo-1615811546313)(Pictures/1588260114740.png)]
9.3 搭建Config-Client

导入依赖

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

编写配置文件

# 指定Eureka服务地址
eureka:
  client:
    service-url:
      defaultZone: http://root:root@localhost:8761/eureka,http://root:root@localhost:8762/eureka

#指定服务的名称
spring:
  application:
    name: CUSTOMER-${version}
  cloud:
    config:
      discovery:
        enabled: true
        service-id: CONFIG
      profile: dev

version: v1


# CONFIG -> CUSTOMER-v1-dev.yml

修改配置名称

修改为bootstrap.yml

测试发布消息到RabbitMQ

效果
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NmGlKpCT-1615811546314)(Pictures/1588263245773.png)]
9.4 实现动态配置
9.4.1 实现原理
实现原理
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NOunA2vP-1615811546314)(Pictures/1588268131944.png)]
9.4.2 服务连接RabbitMQ

Config项目和Customer项目均要导入依赖

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

Config项目编写配置文件连接RabbitMQ信息

spring:
  rabbitmq:
    host: 192.168.206.142
    port: 5672
    username: test
    password: 123
    virtual-host: /test
9.4.3 实现手动刷新

Config项目和Customer项目均要导入依赖

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

Config项目和Customer项目均要编写配置文件,yml设置开放访问地址,默认情况下都不允许访问

management:
  endpoints:
    web:
      exposure:
        include: "*"

为customer添加一个controller,不要忘记加@RefreshScope注解,不要手动刷新无效

@RestController
@RefreshScope
public class CustomerController {

    @Value("${env}")
    private String env;

    @GetMapping("/env")
    public String env(){
        return env;
    }
}

测试,发送请求http://localhost:8888/actuator/bus-refresh,注意是post请求

1. CONFIG在Gitee修改之后,自动拉取最新的配置信息。
2. 其他模块需要更新的话,手动发送一个请求http://ip:port/actuator/bus-refresh,不重启项目,即可获取最新的配置信息
9.5.4 内网穿透

为了能让git webhooks访问本地的http://ip:port/actuator/bus-refresh地址,需要把localhost:8888的地址改为外网地址,因此需要使用内网穿透。内网穿透官网https://natapp.cn/

注册登录

购买一个免费的隧道。

配置隧道
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8uIxHkVX-1615811546315)(Pictures/1588317871876.png)]

下载客户端,并复制config.ini文件,在文件中指定authtoken

netapp软件
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-I7kDGBlG-1615811546315)(Pictures/1588317944258.png)]

启动exe文件,并测试使用域名访问config接口

效果
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jmiiQzsW-1615811546316)(Pictures/1588317929937.png)]
9.5.5 实现自动刷新

配置Gitee中的WebHooks

配置Gitee中的WebHooks
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-80BFcbZe-1615811546316)(Pictures/1588319227917.png)]

给Config添加一个过滤器,注意要在启动类中放行filter

@ServletComponentScan("com.qf.filter")

测试

测试
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kgJBGQx2-1615811546317)(Pictures/1588319255162.png)]

十、服务的追踪-Sleuth【重点


10.1 引言

随着业务越来越复杂,系统也随之进行各种拆分,特别是随着微服务架构和容器技术的兴起,看似简单的一个应用,后台可能有几十个甚至几百个服务在支撑;一个前端的请求可能需要多次的服务调用最后才能完成;当请求变慢或者不可用时,我们无法得知是哪个后台服务引起的,这时就需要解决如何快速定位服务故障点,服务的追踪就能很好的解决这样的问题。

  • Sleuth可以获得到整个服务链路的信息。

  • Zipkin通过图形化界面去看到信息。

  • Sleuth将日志信息存储到数据库中。

Sleuth
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DIzIHxQm-1615811546317)(Pictures/1588325099243.png)]
10.2 Sleuth的使用

在customer和search工程导入依赖

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

在customer和search工程编写配置文件,配置日志输出级别

logging:
  level:
    org.springframework.web.servlet.DispatcherServlet: DEBUG

测试

日志信息
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TAoLN8RL-1615811546317)(Pictures/1588336398785.png)]
SEARCH:服务名称
e9c:总链路id (traceId)
f07:当前服务的链路id (spanId)
false:不会将当前的日志信息,输出其他系统中

traceId相同,表示当前的一个请求。spanId不同,表示经过不同的服务进行处理。同一个请求,traceId只有一个,但spanId却可以有多个。如果重新发送一个请求,则traceId会改变。
10.3 Zipkin的使用

通过Docker-compose安装zipkin图形画界面 zipkin官网 https://zipkin.io/

version: "3.1"
services:
  zipkin:
   image: daocloud.io/daocloud/zipkin:latest
   restart: always
   container_name: zipkin
   ports:
     - 9411:9411

导入依赖,把Customer项目和Search项目中的spring-cloud-starter-sleuth改为spring-cloud-starter-zipkin

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

Customer项目和Search项目编写配置文件

#指定服务的名称
spring:
  sleuth:
    sampler:
      probability: 1   # 百分之多少的sleuth信息需要输出到zipkin中
  zipkin:
    base-url: http://192.168.206.142:9411/  # 指定zipkin的地址
    sender:
      type: web   #将trace数据通过http发送给zipkin

测试

测试
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-v3lgiA1e-1615811546318)(Pictures/1588341969002.png)]
10.4 整合RabbitMQ

导入RabbitMQ依赖

修改配置文件

spring:
  zipkin:
    sender:
      type: rabbit

修改Zipkin的信息

version: "3.1"
services:
  zipkin:
   image: daocloud.io/daocloud/zipkin:latest
   restart: always
   container_name: zipkin
   ports:
     - 9411:9411
   environment:
     - RABBIT_ADDRESSES=192.168.206.142:5672
     - RABBIT_USER=test
     - RABBIT_PASSWORD=123
     - RABBIT_VIRTUAL_HOST=/test

测试

测试
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PnvEgVsG-1615811546318)(Pictures/1588348586217.png)]
10.5 Zipkin存储数据到ES

重新修改zipkin的yml文件

version: "3.1"
services:
  zipkin:
   image: daocloud.io/daocloud/zipkin:latest
   restart: always
   container_name: zipkin
   ports:
     - 9411:9411
   environment:
     - RABBIT_ADDRESSES=192.168.199.109:5672
     - RABBIT_USER=test
     - RABBIT_PASSWORD=test
     - RABBIT_VIRTUAL_HOST=/test
     - STORAGE_TYPE=elasticsearch
     - ES_HOSTS=http://192.168.206.142:9200

十一、完整SpringCloud架构图【重点


完整架构图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Sck04V9t-1615811546319)(Pictures/1588351313922.png)]

ceId会改变。




#### 10.3 Zipkin的使用

> 通过Docker-compose安装zipkin图形画界面  zipkin官网 https://zipkin.io/

```yml
version: "3.1"
services:
  zipkin:
   image: daocloud.io/daocloud/zipkin:latest
   restart: always
   container_name: zipkin
   ports:
     - 9411:9411

导入依赖,把Customer项目和Search项目中的spring-cloud-starter-sleuth改为spring-cloud-starter-zipkin

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

Customer项目和Search项目编写配置文件

#指定服务的名称
spring:
  sleuth:
    sampler:
      probability: 1   # 百分之多少的sleuth信息需要输出到zipkin中
  zipkin:
    base-url: http://192.168.206.142:9411/  # 指定zipkin的地址
    sender:
      type: web   #将trace数据通过http发送给zipkin

测试

测试
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jXkghXcZ-1615811546319)(Pictures/1588341969002.png)]
10.4 整合RabbitMQ

导入RabbitMQ依赖

修改配置文件

spring:
  zipkin:
    sender:
      type: rabbit

修改Zipkin的信息

version: "3.1"
services:
  zipkin:
   image: daocloud.io/daocloud/zipkin:latest
   restart: always
   container_name: zipkin
   ports:
     - 9411:9411
   environment:
     - RABBIT_ADDRESSES=192.168.206.142:5672
     - RABBIT_USER=test
     - RABBIT_PASSWORD=123
     - RABBIT_VIRTUAL_HOST=/test

测试

测试
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0Msre4DN-1615811546320)(Pictures/1588348586217.png)]
10.5 Zipkin存储数据到ES

重新修改zipkin的yml文件

version: "3.1"
services:
  zipkin:
   image: daocloud.io/daocloud/zipkin:latest
   restart: always
   container_name: zipkin
   ports:
     - 9411:9411
   environment:
     - RABBIT_ADDRESSES=192.168.199.109:5672
     - RABBIT_USER=test
     - RABBIT_PASSWORD=test
     - RABBIT_VIRTUAL_HOST=/test
     - STORAGE_TYPE=elasticsearch
     - ES_HOSTS=http://192.168.206.142:9200

十一、完整SpringCloud架构图【重点


完整架构图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yFJ61V5J-1615811546321)(Pictures/1588351313922.png)]

请添加图片描述

Spring Cloud是一个用于构建分布式系统的开发工具集合。它提供了一些常用的组件和框架,包括服务注册和发现、负载均衡、断路器、分布式配置等等。在使用Spring Cloud时,有一些常见的错误和注意事项需要注意。 首先,关于Spring Boot和Spring Cloud版本对应错误。在使用Spring Cloud时,需要确保Spring Boot和Spring Cloud的版本兼容。不同版本之间可能存在依赖冲突或不兼容的情况,因此需要根据官方文档或者相关文档来选择合适的版本。 另外,Spring Cloud Config是一个用于集中管理和动态获取配置的工具。它支持从Git、SVN或本地文件系统中获取配置文件,并提供了服务器和客户端支持。你可以通过官方使用说明文档了解更多关于Spring Cloud Config的详细信息。 此外,关于选择使用Nacos还是Eureka作为服务注册和发现组件的问题。Nacos是一个功能更强大的服务注册和发现组件,它整合了Spring Cloud Eureka、Spring Cloud Config和Spring Cloud Bus的功能。使用Nacos可以实现配置的中心动态刷新,而不需要为配置中心新增集群或使用消息队列。另一方面,Eureka是Spring Cloud原生全家桶的一部分,相对来说更加稳定一些。选择使用哪个组件需要根据具体的需求和项目特点来决定。 综上所述,Spring Cloud是一个用于构建分布式系统的开发工具集合,它提供了一些常用的组件和框架。在使用Spring Cloud时,需要注意Spring Boot和Spring Cloud版本的兼容性,并可以使用Spring Cloud Config来动态获取配置。同时,可以选择使用Nacos或Eureka作为服务注册和发现组件,具体选择需要根据项目需求来决定。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值