avatar

SpringBoot与分布式

分布式应用

分布式应用(distributed application)指的是应用程序分布在不同计算机上,通过网络来共同完成一项任务的工作方式。
为什么需要分布式:

  • 单一应用架构
    当网站流量很小时,只需一个应用,将所有功能都部署在一起,以减少部署节点和成本。此时,用于简化增删改查工作量的数据访问框架(ORM)是关键。
  • 垂直应用架构
    当访问量逐渐增大,单一应用增加机器带来的加速度越来越小,将应用拆成互不相干的几个应用,以提升效率。此时,用于加速前端页面开发的Web框架(MVC)是关键。
  • 分布式服务架构
    当垂直应用越来越多,应用之间交互不可避免,将核心业务抽取出来,作为独立的服务,逐渐形成稳定的服务中心,使前端应用能更快速的响应多变的市场需求。此时,用于提高业务复用及整合的分布式服务框架(RPC)是关键。
    • 当服务越来越多,容量的评估,小服务资源的浪费等问题逐渐显现,此时需增加一个调度中心基于访问压力实时管理集群容量,提高集群利用率。此时,用于提高机器利用率的资源调度和治理中心(SOA)是关键。

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

Zookeeper和Dubbo

概述

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

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

1、当服务启动得时候 容器(Container)就会启动一个Provider
2、将自身得服务发布到注册中心ZooKeeper(ZooKeeper非常强大不止有注册中心这一种功能)
3、客户端订阅注册中心
4、客户端通过注册中心得知服务位置以后,调用远程服务
5、Monitor会监控整个流程。

整合springboot

我们创建两个项目,一个充当Provider,一个充当Consumer。
我们在Provider中编写服务,并且将服务注册到zookeeper中,Consumer订阅zookeeper,使用服务。

在pom文件中添加依赖

依赖在provider和consumer都要加入

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.6.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.zenshin</groupId>
<artifactId>provider-ticket</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>provider-ticket</name>
<description>Demo project for Spring Boot</description>

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

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--加入dubbo的启动器-->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>2.7.3</version>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--加入dubbo-->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo</artifactId>
<version>2.7.3</version>
</dependency>
<!--zookeeper依赖的curator框架-->
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>4.0.1</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>2.8.0</version>
</dependency>
<!--加入zookeeper依赖-->
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.13</version>
<type>pom</type>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--操作zookeeper客户端-->
<dependency>
<groupId>com.101tec</groupId>
<artifactId>zkclient</artifactId>
<version>0.10</version>
</dependency>

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

比较简单的,我们可以直接使用dubbo的启动器和客户端即可实现

<!-- 导入dubbo与springboot整合启动器 -->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>2.7.6</version>
</dependency>
<!-- 导入zookeeper客户端 -->
<dependency>
<groupId>com.101tec</groupId>
<artifactId>zkclient</artifactId>
<version>0.10</version>
</dependency>
<!-- 导入zookeeper客户端所需依赖:curator框架 -->
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>4.3.0</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>4.3.0</version>
</dependency>

添加Provider项目配置文件

#应用项目名
dubbo.application.name=provider-ticket
#zookeeper地址
dubbo.registry.address=zookeeper://192.168.1.8:2181
#扫描哪个包下的地址
dubbo.scan.base-packages=com.zenshin.providerticket.service

生产者Provider代码编写

@EnableDubbo
可以在指定的包名下(通过 dubbo.scan.base-packages)扫描 Dubbo 的服务提供者(以 @Service 标注)以及 Dubbo 的服务消费者(以 Reference 标注)。
@Service:
表示服务的具体实现,被注解的类会被dubbo扫描

//路径为:com.zenshin.providerticket.service.TicketService
public interface TicketService {
public String getTicket();
}
@EnableDubbo //开启对dubbo支持
@Component//加到Springboot的容器中
@Service //标记此类,表示服务的具体实现,将发布出去,这个service不是springboot的而是dubbo的
public class TicketServiceImpl implements TicketService{
@Override
public String getTicket() {
return "《厉害了,我的国》";
}
}

添加Consumer项目配置文件

#配置应用名
dubbo.application.name=consumer-user
#注册中心地址
dubbo.registry.address=zookeeper://192.168.1.8:2181

消费者Consumer代码编写

编写与分布式服务类相同的接口(不必实现),并保证包结构相同,可以直接把生产者包直接拷贝过来,只保留接口
这是简单使用,如果想要真正使用的话,直接把接口打包一下引用即可。

//路径为:com.zenshin.providerticket.service.TicketService,与生产者的接口保持一致
public interface TicketService {
public String getTicket();
}

然后我们进行编写调用接口的类 UserService
@Reference 可以定义在类中的一个字段、方法上,表示一个服务的引用。通常 @Reference 定义在一个字段上

@Service
public class UserService {
@Reference//这里是全类名匹配的,在注册中心找这个全类名,谁注册过就去找谁
TicketService ticketService;
public void hello()
{
String ticket = ticketService.getTicket();
System.out.println("买到票了"+ticket);
}
}

我们在测试类中对消费者进行测试,注意在测试的时候,我们的生产者必须运行着,因为生产者是真正的功能提供者

@SpringBootTest
class ConsumerUserApplicationTests {
@Autowired
UserService userService;

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

运行后,我们就可以在控制台看到输出:买到票了《厉害了,我的国》

dubbo注解详解
dubbo与zookeeper整合案例

Spring Cloud

概述

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

  • SpringCloud分布式开发五大常用组件
    • 服务发现——Netflix Eureka
    • 客户端负载均衡——Netflix Ribbon
    • 断路器——Netflix Hystrix
    • 服务网关——Netflix Zuul
    • 分布式配置——Spring Cloud Config

入门尝鲜

SpringBoot后面更多的是整合案例,应该学习相应的知识结合使用,这里只是简单介绍,做一个小实例
1、首先我们创建一个功能,作为我们的注册中心——利用Eureka实现(使用spring初始化器选择springcloud eureka server模块)
2、创建一个服务提供者项目(选择eureka-client模块:用来服务注册)
3、创建一个服务消费者项目(选择eureka-client模块:用来调用服务)

Eureka注册中心

  • pom依赖
    <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
    </dependency>
    <dependencyManagement>
    <dependencies>
    <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-dependencies</artifactId>
    <version>${spring-cloud.version}</version>
    <type>pom</type>
    <scope>import</scope>
    </dependency>
    </dependencies>
    </dependencyManagement>
  • 配置文件
    server:
    port: 8761
    eureka:
    instance:
    hostname: eureka-server # eureka实例的主机名
    client:
    register-with-eureka: false # 不将自己注册到eureka上,不是高可用不需要
    fetch-registry: false # 不从eureka上来获取服务的注册信息,因为这个本身就是注册中心,不需要获取
    service-url:
    defaultZone: http://localhost:8761/eureka/ # 注册中心服务注册的地址
  • 需要在主程序入口添加一个注解@EnableEurekaServer
    @EnableEurekaServer
    @SpringBootApplication
    public class EurekaServerApplication {

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

    }
  • 浏览器访问http://localhost:8761/,可以看到界面化操作

服务注册

  • pom依赖
    <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>
    <!--通过http暴露服务-->
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependencyManagement>
    <dependencies>
    <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-dependencies</artifactId>
    <version>${spring-cloud.version}</version>
    <type>pom</type>
    <scope>import</scope>
    </dependency>
    </dependencies>
    </dependencyManagement>
  • 编写一个service层的TicketService类
    @Service
    public class TicketService {
    public String getTicket()
    {
    return "《厉害了,我的国》";
    }
    }
    编写一个controller,将我们的服务暴露出来,因为SpringCloud在整合微服务的时候,是通过http来进行通信的,所以我们需要编写一个controller
    @RestController
    public class TicketController {

    @Autowired
    TicketService ticketService;

    @GetMapping("/ticket")
    public String getTicket()
    {
    return ticketService.getTicket();
    }
    }
  • 生产者配置
    server:
    port: 8001
    spring:
    application:
    name: provider-ticket

    eureka:
    instance:
    prefer-ip-address: true #注册服务的时候使用ip进行注册
    client:
    service-url:
    defaultZone: http://localhost:8761/eureka/ # 注册中心服务注册的地址
    运行起来以后,我们就会发现,在eureka上注册了一个实例
    我们来来注册多个应用:
    修改不同的端口,利用maven的打包工具打成一个包,然后多次启动,将服务注册到eureka中

消费者

消费者的pom文件与生产者相同

  • 配置文件
    spring:
    application:
    name: consumer-user
    server:
    port: 8200

    eureka:
    instance:
    prefer-ip-address: true #注册服务的时候使用ip进行注册
    client:
    service-url:
    defaultZone: http://localhost:8761/eureka/ # 注册中心服务注册的地址
  • 程序主入口加入注解
    @EnableDiscoveryClient //开启发现服务功能
    @SpringBootApplication
    public class ConsumerUserApplication {

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

    @LoadBalanced//发送请求的时候加上负载均衡机制
    @Bean
    public RestTemplate restTemplate()
    {
    return new RestTemplate();
    }
    }
    往容器中注入一个RestTemplate类,这个类用发送Rest请求,@LoadBalanced开启负载均衡,如果创建不同端口的provider应用,则访问会被均衡到各个应用,@EnableDiscoveryClient开启发现服务
  • 编写一个controller
    @RestController
    public class UserController {
    @Autowired
    RestTemplate restTemplate;

    @GetMapping("/buy")
    public String buyTicker(String name)
    {
    String template = restTemplate.getForObject("http://PROVIDER-TICKET/ticket", String.class);//发送服务的url写容器名字就可以。
    return name+"购买了"+template;
    }
    }
    运行以后我们可以往浏览器中输入: http://localhost:8200/buy?name=zhangsan发请求,则会响应
    浏览器中会出现如下结果:
    zhangsan购买了《厉害了,我的国》

我们可以看一下启动的两个生产者的控制台,都会有调用,因为开启的负载均衡。
这里是只是简单的尝试,更多的可以在Springcloud中进行学习

文章作者: zenshin
文章链接: https://zlh.giserhub.com/2020/04/29/springboot/distributed/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 zenshin's blog
打赏
  • 微信
    微信
  • 支付宝
    支付宝

评论