SpringCloud

一、环境搭建

创建Maven父项目

1.依赖版本管理
<properties>
    <java.version>1.8</java.version>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
    <encoding>UTF-8</encoding>
    <spring-boot.version>2.3.7.RELEASE</spring-boot.version>
    <spring-cloud.version>Hoxton.SR1</spring-cloud.version>
    <mybatis-plus.version>3.4.2</mybatis-plus.version>
    <driuid.version>1.2.3</driuid.version>
    <log4j.version>1.2.17</log4j.version>
    <lombok.version>1.18.16</lombok.version>
    <mysql.version>8.0.12</mysql.version>
    <junit.version>4.13.2</junit.version>
</properties>
2.依赖管理

注意:父项目使用依赖管理后,子项目并不会直接继承父项目的依赖,而是当引入相同依赖时不再需要依赖版本,而会使用父项目的依赖版本。

<dependencyManagement>
    <dependencies>
        <!--springBoot-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-dependencies</artifactId>
            <version>${spring-boot.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
        <!--springCloud-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>${spring-cloud.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
        <!--mysql数据库驱动-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>${mysql.version}</version>
            <scope>runtime</scope>
        </dependency>
        <!--druid数据源-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>${driuid.version}</version>
        </dependency>
        <!--mybatis-plus-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>${mybatis-plus.version}</version>
        </dependency>
        <!--lombok-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>${lombok.version}</version>
            <optional>true</optional>
        </dependency>
        <!--log4j-->
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>${log4j.version}</version>
        </dependency>
        <!--日志配置-->
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-core</artifactId>
            <version>1.2.3</version>
        </dependency>
        <!--junit测试-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>${junit.version}</version>
        </dependency>
    </dependencies>
</dependencyManagement>

二、Eureka(集群/服务注册/消费)

Eureka是spring cloud中的一个负责服务注册与发现的组件。

1.集群搭建

1.导入依赖
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
    <version>2.2.5.RELEASE</version>
</dependency>
2.环境配置

一个Eureka集群包含7001和7002等许多服务,在这个集群中,7001指向其他所有服务,7002也指向其他所有服务。

server:
  port: 7001
eureka:
  instance:
    hostname: eureka7001.com
  client:
    #此客户端是否获取eureka服务器注册表上的注册信息,默认为true
    fetch-registry: false
    #实例是否在eureka服务器上注册自己的信息以供其他服务发现,默认为true
    register-with-eureka: false
    service-url:
      #单机
#      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
      #绑定其他服务器,实现集群
      defaultZone: http://eureka7002.com:7002/eureka/
#---------------------------------------------------------------------      
server:
  port: 7002
eureka:
  instance:
    hostname: eureka7002.com
  client:
    #此客户端是否获取eureka服务器注册表上的注册信息,默认为true
    fetch-registry: false
    #实例是否在eureka服务器上注册自己的信息以供其他服务发现,默认为true
    register-with-eureka: false
    service-url:
      #单机
#      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
        #集群
        defaultZone: http://eureka7001.com:7001/eureka/      
3.开启服务注册中心

在主启动类上添加注解

@EnableEurekaServer

2.服务注册(服务生产者)

1.导入依赖
<dependencies>
    <dependency>
        <groupId>com.zhoupiyao</groupId>
        <artifactId>springcloud-api</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </dependency>
    <!--mysql驱动-->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
    </dependency>
    <!--mybatis-plus-->
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
    </dependency>
    <!--druid数据源-->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
  <!--springBoot启动器-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-jetty</artifactId>
    </dependency>
  <!--热部署-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
        <optional>true</optional>
        <scope>true</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
  <!--Eureka客户端/服务注册端-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        <version>2.2.5.RELEASE</version>
    </dependency>
</dependencies>
2.配置
server:
  port: 8001
mybatis-plus:
  type-aliases-package: com.zhoupiyao.pojo
  mapper-locations: mapper/*.xml
spring:
  application:
    name: springcloud-provider
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    url: jdbc:mysql://localhost:3306/dbtest01?useSSL=false&charaterEncoding=UTF-8&serverTimezone=Asia/Shanghai
    username: root
    password: 123
eureka:
  client:
    service-url:
      defaultZone:  http://eureka7001.com:7001/eureka/ ,http://eureka7002.com:7002/eureka/
  instance:
    #该实例注册到服务中心的唯一ID
    instance-id: springcloud-provider-8001
    #服务注册中心实例的主机名
    hostname: localhost

info:
#服务注册者信息
  app.name: zhoupiyao
  company.name: 周皮妖
3.开启服务注册
@EnableEurekaClient
//开启服务发现
@EnableDiscoveryClient
@MapperScan("com.zhoupiyao.dao")
4.服务详情
@RestController
public class BlogController {
    @Autowired
    private BlogService blogService;
    @Autowired
    private DiscoveryClient client;
    @RequestMapping("/blog/getOne/{id}")
    public Blog getBlog(@PathVariable("id") Long id){
        return blogService.getById(id);
    }
    @RequestMapping("/blog/getAll")
    public List<Blog> getBlogs(){
        return blogService.list();
    }
    @GetMapping("/blog/add")
    public boolean addBlog(Blog blog){
        return blogService.save(blog);
    }
    @RequestMapping("/blog/decovery")
    public Object decovery(){
        List<String> services = client.getServices();
        services.forEach(System.out::println);
        List<ServiceInstance> instances = client.getInstances("SPRINGCLOUD-PROVIDER");
        for (ServiceInstance instance : instances) {
            System.out.println("主机:" + instance.getHost() + "\t" +
                            "端口:" + instance.getPort() + "\t" +
                            "实例ID:" + instance.getInstanceId() + "\t" +
                            "实例URi:" + instance.getUri());
        }
        return this.client;
    }

3.远程调用服务(服务消费者)

1.导入依赖
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-devtools</artifactId>
    <optional>true</optional>
    <scope>true</scope>
</dependency>
<!--Eureka客户端/服务消费端-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    <version>2.2.5.RELEASE</version>
</dependency>
2.配置
server:
  port: 80
eureka:
  client:
  //关闭注册自己,默认开启
    register-with-eureka: true
    service-url:
      defaultZone:  http://eureka7001.com:7001/eureka/ ,http://eureka7002.com:7002/eureka/
3.开启服务
@EnableEurekaClient
4.编写配置类

编写配置类ConfigBean来产生RestTemplate用于远程调用服务

@Configuration
public class ConfigBean {
    @Bean
    //开启Ribbon负载均衡
    //@LoadBalanced
    public RestTemplate getRestTemplate(){
        return new RestTemplate();
    }
5.调用服务
@RestController
public class BlogController {
  
    @Autowired
    RestTemplate restTemplate;
//    private final static String PROVIDER_URL_PREFIX = "http://localhost:8001/blog/";
    private final static String PROVIDER_URL_PREFIX = "http://SPRINGCLOUD-PROVIDER/blog/";
    @RequestMapping("/blog/getAll")
    public List<Blog> getAll(){
        return restTemplate.getForObject(PROVIDER_URL_PREFIX+"getAll", List.class);
    }
    @RequestMapping("/blog/getOne/{id}")
    public Blog getBlog(@PathVariable("id") Long id){
        return restTemplate.getForObject(PROVIDER_URL_PREFIX+"getOne/"+id, Blog.class);
    }
    @PostMapping("/blog/add")
    public boolean addBlog(@RequestBody Blog blog){
        return restTemplate.postForObject(PROVIDER_URL_PREFIX+"add/",blog,Boolean.class);
    }
}

三、Ribbon(负载均衡)

Ribbon是一个基于HTTP和TCP的客户端负载均衡器,当我们将Ribbon和Eureka一起使用时,Ribbon会到Eureka注册中心去获取服务端列表,然后进行轮询访问以到达负载均衡的作用,客户端负载均衡也需要心跳机制去维护服务端清单的有效性,当然这个过程需要配合服务注册中心一起完成。

1.导入依赖

<!--ribbon负载均衡-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
    <version>2.2.5.RELEASE</version>
</dependency>

2.负载均衡策略

命名 策略类 描述
随机策略 RandomRule 随机选择server
轮询策略 RoundRobinRule 轮询选择, 轮询index,选择index对应位置的Server;
重试策略 RetryRule 对选定的负载均衡策略机上重试机制,在一个配置时间段内当选择Server不成功,则一直尝试使用subRule的方式选择一个可用的server;
最低并发策略 BestAvailableRule 逐个考察server,如果server断路器打开,则忽略,再选择其中并发链接最低的server
可用过滤策略 AvailabilityFilteringRule 过滤掉一直失败并被标记为circuit tripped的server,过滤掉那些高并发链接的server(active connections超过配置的阈值)或者使用一个AvailabilityPredicate来包含过滤server的逻辑,其实就就是检查status里记录的各个Server的运行状态;
响应时间加权重策略 ResponseTimeWeightedRule 根据server的响应时间分配权重,响应时间越长,权重越低,被选择到的概率也就越低。响应时间越短,权重越高,被选中的概率越高,这个策略很贴切,综合了各种因素,比如:网络,磁盘,io等,都直接影响响应时间
区域权重策略 ZoneAvoidanceRule 综合判断server所在区域的性能,和server的可用性,轮询选择server并且判断一个AWS Zone的运行性能是否可用,剔除不可用的Zone中的所有server

3.配置均衡策略(配置类方法)

注意:该类不能放在主应用程序上下文@ComponentScan所扫描的包中,否则配置将会被所有Ribbon Client共享。

@Configuration
public class MyRule {
    @Bean
    public IRule myrule(){
        //自定义均衡策略
        //return new DIIYRule();。
        //指定使用的负载均衡策略
        return new RandomRule;
    }
}

自定义均衡策略--在RandomRule基础上进行了修改

public class DIIYRule extends AbstractLoadBalancerRule {

    private int total= 0 ;
    private int currentIndex = 0;
    public DIIYRule() {
    }
    public Server choose(ILoadBalancer lb, Object key) {

        if (lb == null) {
            return null;
        } else {
            Server server = null;
            while(server == null) {
                if (Thread.interrupted()) {
                    return null;
                }
                List<Server> upList = lb.getReachableServers();
                List<Server> allList = lb.getAllServers();
                int serverCount = allList.size();
                if (serverCount == 0) {
                    return null;
                }
                //自定义调用规则:
                //按顺序每个服务调用5次,当所有服务均被调用5次后,
                // 重新从第1个服务开始继续按当前规则循环调用
                //======================================================
                if (total < 5){
                    server = (Server)upList.get(currentIndex);
                    total++;
                }else {
                    total = 0;
                    currentIndex++;
                    if (currentIndex > upList.size() - 1){
                        currentIndex = 0;
                    }
                    server = (Server)upList.get(currentIndex);
                }
                //======================================================
//                int index = this.chooseRandomInt(serverCount);
//                server = (Server)upList.get(index);
                if (server == null) {
                    Thread.yield();
                } else {
                    if (server.isAlive()) {
                        return server;
                    }
                    server = null;
                    Thread.yield();
                }
            }
            return server;
        }
    }
    protected int chooseRandomInt(int serverCount) {
        return ThreadLocalRandom.current().nextInt(serverCount);
    }

    public Server choose(Object key) {
        return this.choose(this.getLoadBalancer(), key);
    }
    public void initWithNiwsConfig(IClientConfig clientConfig) {
    }
}

4.配置均衡策略(配置文件方法)

#这个是微服务的id
SPRINGCLOUD-PROVIDER:
  ribbon:
    #均衡策略  
    #NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule #配置规则 随机
    #NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RoundRobinRule #配置规则 轮询
    #NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RetryRule #配置规则 重试
    #NFLoadBalancerRuleClassName: com.netflix.loadbalancer.WeightedResponseTimeRule #配置规则 响应时间权重
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.BestAvailableRule #配置规则 最空闲连接策略

5.启动负载均衡服务

@RibbonClient(name = "SPRINGCLOUD-PROVIDER",configuration = MyRule.class)

四、Feign(接口方式调用服务)

Feign底层是使用了ribbon作为负载均衡的客户端,而ribbon的负载均衡也是依赖于eureka 获得各个服务的地址,所以要引入eureka-client。

1.导入依赖

<!--Feign-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
    <version>2.2.5.RELEASE</version>
</dependency>

2.接口Service

@FeignClient(name = "SPRINGCLOUD-PROVIDER")
public interface ClientService {
    @RequestMapping("/blog/getOne/{id}")
    Blog getBlog(@PathVariable("id") Long id);

    @RequestMapping("/blog/getAll")
    List<Blog> getAll();

    @PostMapping("/blog/add")
    boolean add(Blog blog);
}

3.消费者注入service

@RestController
public class BlogController {
    @Autowired
    private ClientService clientService;

    @RequestMapping("/blog/getAll")
    public List<Blog> getAll(){
        return clientService.getAll();
    }
    @RequestMapping("/blog/getOne/{id}")
    public Blog getBlog(@PathVariable("id") Long id){
        return clientService.getBlog(id);
    }
    @PostMapping("/blog/add")
    public boolean addBlog(@RequestBody Blog blog){
        return clientService.add(blog);
    }
}

4.开启Feign

@EnableFeignClients(basePackages = "com.zhoupiyao")

五、Hystrix

Hystrix是一个用于处理分布式系统的延迟和容错的开源库,在分布式系统中,许多依赖不可避免的会调用失败,超时、异常等,Hystrix能够保证在一个依赖出问题的情况下,不会导致整体服务失败,避免级联故障,提高分布式系统的弹性

1.服务熔断

服务熔断一般是某个服务(下游服务)故障引起。

服务降级处理是在服务端实现完成的。当某个目标服务调用慢或者有大量超时,会向调用方法返回一个符合预期的、可处理的备选响应(FallBack),而不是长时间的等待或者抛出无法处理的异常造成请求阻塞,保证了服务调用方的线程不会被长时间占用,避免故障在分布式系统中蔓延,乃至雪崩。

1.导入依赖
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
    <version>2.2.5.RELEASE</version>
</dependency>
2.熔断备选方案
//绑定备选方案
@HystrixCommand(fallbackMethod = "hystrix_getBlog")
    @RequestMapping("/blog/getOne/{id}")
    public Blog getBlog(@PathVariable("id") Long id){
        Blog blog = blogService.getById(id);
        if (blog == null){
            throw new RuntimeException("找不到对应的博客!");
        }
        return blog;
    }
    //备选方案:当绑定此服务的服务发生异常或超时等待,此方法被执行
    public Blog hystrix_getBlog(@PathVariable("id") Long id){
        return new Blog().setId(id)
                .setTitle("hystrix_调用了备选方案")
                .setViews(0)
                .setDbname("没有找到对应数据库")
                .setAuthor("没有找到相应作者");
    }
3.开启Hystrix
@EnableCircuitBreaker
4.服务熔断效果

服务正常调用

当查询不到用户时,服务抛出异常,Hystrix为了使整体服务不宕机,从而调用备用服务,该服务将错误信息返回。

2.服务降级

服务降级一般是从整体负荷考虑。

服务降级处理是在客户端实现完成的,与服务端没有关系。整体资源快不够了,忍痛将某些服务单元先关掉,关闭后还要返回一些可处理的备选方法,待渡过难关,再开启回来,

1.客户端创建回调类

ClientServiceFallbackFactory

package com.zhoupiyao.service;

import com.zhoupiyao.pojo.Blog;
import feign.hystrix.FallbackFactory;
import org.springframework.stereotype.Component;

import java.util.List;
@Component
public class ClientServiceFallbackFactory implements FallbackFactory {
    @Override
    public ClientService create(Throwable throwable) {
        return new ClientService() {
            @Override
            public Blog getBlog(Long id) {
                return new Blog()
                            .setTitle("服务已关闭,这是客户端的降级服务!");
            }
            @Override
            public List<Blog> getAll() {
                return null;
            }

            @Override
            public boolean add(Blog blog) {
                return false;
            }
        };
    }
}
2.接口类绑定

绑定注册服务以及失败回调类

@FeignClient(name = "SPRINGCLOUD-PROVIDER",fallbackFactory = ClientServiceFallbackFactory.class)
3.服务降级效果

当服务(整个注册的服务)关闭后,客服端调用注册中心方法时由于获取不到就会进行服务降级调用客户端原先定义好的回调方法。

3.DashBoard流监控

注意:DashBoard监控的是添加了服务熔断的服务。

1.导入依赖
<!--Hystrix-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
    <version>2.2.5.RELEASE</version>
</dependency>
<!--dashboard-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId> spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
    <version>2.2.5.RELEASE</version>
</dependency>

3.DashBoard流监控

注意:DashBoard监控的是添加了服务熔断的服务。

1.导入依赖
<!--Hystrix-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
    <version>2.2.5.RELEASE</version>
</dependency>
<!--dashboard-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId> spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
    <version>2.2.5.RELEASE</version>
</dependency>
2.开启DashBoard
@EnableHystrixDashboard
3.监控绑定服务

进入:http://localhost:9001/hystrix

image

3.DashBoard效果

监控请求

image

posted @ 2021-04-22 17:40  一只小周皮妖🐯  阅读(118)  评论(0)    收藏  举报