SpringBoot - 分布式

1.分布式应用

在分布式系统中,国内常用zookeeper + dubbo组合,而SpringBoot推荐使用全栈的Spring,SpringBoot + SpringCloud

分布式系统:

  • 单一应用架构

    当网站流量很小时,只需一个应用,将所有功能都部署在一起,以减少部署节点和成本。此时,用于简化增删改查工作量的数据访问框架(ORM)是关键

  • 垂直应用架构

    当访问量逐渐增大,单一应用增加机器带来的加速度越来越小,将应用拆成互不相干的几个应用,以提升效率。此时,用于加速前端页面开发的Web框架(MVC)是关键

  • 分布式应用架构

    当垂直应用越来越多,应用之间交互不可避免,将核心业务抽取出来,作为独立的服务,逐渐形成稳定的服务中心,使前端应用能更快速的响应多变的市场需求。此时,用于提高业务复用及整合的分布式服务框架(RPC)是关键

  • 流动计算架构

    当服务越来越多,容量的评估,小服务资源的浪费等问题逐渐显现,此时需增加一个调度中心基于访问压力实时管理集群容量,提高集群利用率。此时,用于提高机器利用率的资源调度和治理中心(SOA)是关键

2.Zookeeper和Dubbo

Zookeeper

ZOokeeper是一个分布式的,开放源码的分布式应用程序协调服务。它是一个为分布式应用提供一致性服务的软件,提供的功能包括:配置维护、域名服务、分布式同步、组服务等

Dubbo

Dubbo是Alibaba开源的分布式服务框架,它最大的特点是按照分层的方式来架构,使用这种方式可以使各个层之间解耦合(或者最大限度地松耦合)。从服务模型的角度来看,Dubbo采用的是一种非常简单的模型,要么是提供方提供服务,要么是消费放消费服务,所以基于这一点可以抽象出服务提供方(Provider)和服务消费方(Consumer)两个角色

Dubbo架构图:

3.SpringCloud

SpringCLoud

SpringCloud是一个分布式的整体解决方案。SpringCloud为开发者提供了在分布式系统(配置管理,服务发现,熔断,路由,微代理,控制总线,一次性token,全局锁,leader选举,分布式session,集群状态)中快速构建的工具,使用SpringCloud的开发者可以快速的启动服务或构建应用、同时能够快速和云平台资源进行对接

SpringCloud分布式开发五大常用组件

  • 服务发现 —— Netflix Eureka
  • 客服端负载均衡 —— Netflix Ribbon
  • 断路器 —— Netflix Hystrix
  • 服务网关 —— Netflix Zuul
  • 分布式配置 —— Spring Cloud Config

使用Zookeeper + Dubbo作为分布式

环境准备

  1. Docker下载zookeeper镜像并启动容器

    docker pull zookeeper
    docker run --name zk -p 2181:2181 --restart always -d zookeeper
    
  2. 创建两个项目provider-ticket和comsumer-user,引入依赖如下

    <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            
            <dependency>
                <groupId>org.apache.dubbo</groupId>
                <artifactId>dubbo-spring-boot-starter</artifactId>
                <version>2.7.1</version>
            </dependency>
           <!--引入dubbo的starter后还要引入dubbo的依赖,否则会找不到dubbo的@Service注解-->
            <dependency>
                <groupId>org.apache.dubbo</groupId>
                <artifactId>dubbo</artifactId>
                <version>2.7.1</version>
            </dependency>
            <!--必须去除zookeeper的依赖,否则会报错-->
            <dependency>
                <groupId>com.github.sgroschupf</groupId>
                <artifactId>zkclient</artifactId>
                <version>0.1</version>
                <exclusions>
                    <exclusion>
                        <groupId>org.apache.zookeeper</groupId>
                        <artifactId>zookeeper</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
            
            <dependency>
                <groupId>org.apache.curator</groupId>
                <artifactId>curator-recipes</artifactId>
                <version>2.12.0</version>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
    </dependencies>
    
  3. 配置provider-ticket的application.properties

    dubbo.application.name=provider-ticket
    #注册中心的地址
    dubbo.registry.address=zookeeper://IP:2181
    #dubbo扫描的包
    dubbo.scan.base-packages=com.chenpeng.ticket.service
    
  4. 配置comsumer-user的application.properties

    dubbo.application.name=consumer-user
    dubbo.registry.address=zookeeper://IP:2181
    

测试运行

  1. 在provider-ticket中创建service层接口及其实现类

    public interface TicketService {
    
        public String getTicket();
    }
    
    import org.apache.dubbo.config.annotation.Service;
    import org.springframework.stereotype.Component;
    
    @Component
    @Service//将服务发布出去(注意需要使用dubbo的@Service)
    public class TicketServiceImpl implements TicketService {
        @Override
        public String getTicket() {
            return "《厉害了,我的国》!";
        }
    }
    
  2. 在comsumer-user中创建TicketService接口,注意包名路径必须与provider-ticket保持一致

  3. 在comsumer-user中创建service层类

    @Service
    public class UserService {
    
        @Reference//该注解用来引用服务
        TicketService ticketService;
    
        public void hello(){
            String ticket = ticketService.getTicket();
            System.out.println("电影票:"+ticket);
        }
    }
    
  4. 测试,注意测试时provider-ticket需要一直开着

    @Autowired
      UserService userService;
    
      @Test
      public void contextLoads() {
        userService.hello();
      }
    

    控制台输出

    电影票:《厉害了,我的国》!
    

使用SpringCloud作为分布式

环境准备

  1. 创建三个项目eureka-server、provider-ticket和comsumer-user

    eureka-server中引入依赖如下:

    <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-test</artifactId>
                <scope>test</scope>
            </dependency>
    </dependencies>
    

    provider-ticket和comsumer-user引入依赖如下:

    <dependencies>
            <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.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
    </dependencies>
    
  2. 配置eureka-server中的application.yml,启动类上添加@EnableEurekaServer注解启用注册中心

    server:
      port: 8765 
    eureka:
      instance:
        hostname: eureka-server #eureka实例的主机名
      client:
        register-with-eureka: false #不把自己注册到eureka上
        fetch-registry: false #不从eureka上获取服务的注册信息
        service-url:
          defaultZone: http://localhost:8765/eureka/
    
  3. 配置provider-ticket中的application.yml

    server:
      port: 8081
    spring:
      application:
        name: provider-ticket
    eureka:
      instance:
        prefer-ip-address: true #注册服务时使用服务的IP地址
      client:
        service-url:
          defaultZone: http://localhost:8765/eureka/
    
  4. 配置consumer-user中的application.yml

    server:
      port: 8888
    spring:
      application:
        name: consumer-user
    eureka:
      instance:
        prefer-ip-address: true #注册服务时使用服务的IP地址
      client:
        service-url:
          defaultZone: http://localhost:8765/eureka/
    

测试运行

  1. 在provider-ticket中创建service层类

    @Service
    public class TicketService {
    
        public String getTicket(){
            return "《厉害了,我的国》";
        }
    }
    
  2. 在provider-ticket中创建controller层类

    @RestController
    public class TicketController {
    
        @Autowired
        TicketService ticketService;
    
        @GetMapping("/ticket")
        public String getTicket(){
            return ticketService.getTicket();
        }
    }
    
  3. 启动eureka-server和provider-ticket,浏览器访问http://localhost:8765,发现provider-ticket已经注册进来了

  4. 在consumer-user中启动类中添加

    @LoadBalanced//采用负载均衡机制,即多个实例平均调用
    @Bean
    public RestTemplate restTemplate(){//用来发送http请求
        return new RestTemplate();
    }
    
  5. 在consumer-user中创建controller层类

    @RestController
    public class UserController {
    
        @Autowired
        RestTemplate restTemplate;
    
        @GetMapping("/buy")
        public String buy(String name){
            String s = restTemplate.getForObject("http://PROVIDER-TICKET/ticket", String.class);
            return name+"买了"+s;
        }
    }
    
  6. 启动consumer-user,浏览器访问http://localhost:8888/buy?name=cc,页面显示

posted @ 2020-04-14 17:51  codeDD  阅读(133)  评论(0编辑  收藏  举报