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作为分布式
环境准备
-
Docker下载zookeeper镜像并启动容器
docker pull zookeeper docker run --name zk -p 2181:2181 --restart always -d zookeeper
-
创建两个项目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>
-
配置provider-ticket的application.properties
dubbo.application.name=provider-ticket #注册中心的地址 dubbo.registry.address=zookeeper://IP:2181 #dubbo扫描的包 dubbo.scan.base-packages=com.chenpeng.ticket.service
-
配置comsumer-user的application.properties
dubbo.application.name=consumer-user dubbo.registry.address=zookeeper://IP:2181
测试运行
-
在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 "《厉害了,我的国》!"; } }
-
在comsumer-user中创建TicketService接口,注意包名路径必须与provider-ticket保持一致
-
在comsumer-user中创建service层类
@Service public class UserService { @Reference//该注解用来引用服务 TicketService ticketService; public void hello(){ String ticket = ticketService.getTicket(); System.out.println("电影票:"+ticket); } }
-
测试,注意测试时provider-ticket需要一直开着
@Autowired UserService userService; @Test public void contextLoads() { userService.hello(); }
控制台输出
电影票:《厉害了,我的国》!
使用SpringCloud作为分布式
环境准备
-
创建三个项目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>
-
配置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/
-
配置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/
-
配置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/
测试运行
-
在provider-ticket中创建service层类
@Service public class TicketService { public String getTicket(){ return "《厉害了,我的国》"; } }
-
在provider-ticket中创建controller层类
@RestController public class TicketController { @Autowired TicketService ticketService; @GetMapping("/ticket") public String getTicket(){ return ticketService.getTicket(); } }
-
启动eureka-server和provider-ticket,浏览器访问http://localhost:8765,发现provider-ticket已经注册进来了
-
在consumer-user中启动类中添加
@LoadBalanced//采用负载均衡机制,即多个实例平均调用 @Bean public RestTemplate restTemplate(){//用来发送http请求 return new RestTemplate(); }
-
在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; } }
-
启动consumer-user,浏览器访问http://localhost:8888/buy?name=cc,页面显示