第十四章-SpringBoot 与分布式
Dubbo 简介
分布式应用,国内常用组合 Zookeeper + Dubbo
SpringBoot 使用 SpringCloud
Zookeeper 分布式应用程序协调服务
Dubbo Alibaba 开源的分布式服务框架,服务提供方 Provider+ 服务消费方 Consumer
安装 zookeeper
docker pull zookeeper # EXPOSE 2181 2888 3888 8080 docker run --name zk01 -p 2181:2181 --restart always -d zookeeper
创建一个空工程,两个 spring-web 模块
provider-ticket consumer-ticket
1、将服务提供者注册到注册中心
2、引入 dubbo 和 zkclient 依赖
3、配置 dubbo 的扫描包和注册中心地址
4、使用@Service 发布服务
https://github.com/alibaba/dubbo-spring-boot-starter
provider-ticket 提供者
引入依赖 pom.xml
<dependency> <groupId>com.alibaba.spring.boot</groupId> <artifactId>dubbo-spring-boot-starter</artifactId> <version>2.0.0</version> </dependency> <dependency> <groupId>com.101tec</groupId> <artifactId>zkclient</artifactId> <version>0.9</version> </dependency>
配置 application.properties
spring.dubbo.application.name=privoder-ticket spring.dubbo.server=true #注册中心地址 spring.dubbo.registry.address=zookeeper://127.0.0.1:2181 spring.dubbo.scan=com.example.ticket.service
服务接口
package com.example.ticket.service; public interface TicketService { public String getTicket(); }
发布服务实现
package com.example.ticket.service.impl; import com.alibaba.dubbo.config.annotation.Service; import com.example.ticket.service.TicketService; import org.springframework.stereotype.Component; @Component @Service // 发布服务 public class TicketServiceImpl implements TicketService { @Override public String getTicket() { return "门票"; } }
开启配置可用
package com.example.ticket; import com.alibaba.dubbo.spring.boot.annotation.EnableDubboConfiguration; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @EnableDubboConfiguration @SpringBootApplication public class ProviderTicketApplication { public static void main(String[] args) { SpringApplication.run(ProviderTicketApplication.class, args); } }
consumer-ticket 消费者
引入依赖
<dependency> <groupId>com.alibaba.spring.boot</groupId> <artifactId>dubbo-spring-boot-starter</artifactId> <version>2.0.0</version> </dependency> <dependency> <groupId>com.101tec</groupId> <artifactId>zkclient</artifactId> <version>0.9</version> </dependency>
配置 application.properties
spring.dubbo.application.name=consumer-ticket #注册中心地址 spring.dubbo.registry.address=zookeeper://127.0.0.1:2181
暴露接口
package com.example.ticket.service; public interface TicketService { public String getTicket(); }
用户服务接口
package com.example.ticket.service; public interface UserService { public String getTicket(); }
用户服务实现
package com.example.ticket.impl; import com.alibaba.dubbo.config.annotation.Reference; import com.example.ticket.service.TicketService; import com.example.ticket.service.UserService; import org.springframework.stereotype.Service; @Service public class UserServiceImpl implements UserService { @Reference TicketService ticketService; @Override public String getTicket() { return ticketService.getTicket(); } }
开启配置可用
package com.example.ticket; import com.alibaba.dubbo.spring.boot.annotation.EnableDubboConfiguration; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @EnableDubboConfiguration @SpringBootApplication public class ConsumerTicketApplication { public static void main(String[] args) { SpringApplication.run(ConsumerTicketApplication.class, args); } }
测试
package com.example.ticket; import com.example.ticket.service.UserService; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; @SpringBootTest class ConsumerTicketApplicationTests { @Autowired UserService userService; @Test void contextLoads() { String ticket = userService.getTicket(); System.out.println(ticket); } }
SpringCloud-Eureka 注册中心
SpringCloud 是一个分布式的整体解决方案
5 大常用组件
- 服务发现 Netflix Eureka
- 客户端负载均衡 Netflix Ribbon
- 断路器 Netflix Hystrix
- 服务网关 Netflix Zuul
- 分布式配置 Spring Cloud Config
pom.xml
<?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.3.2.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.example</groupId> <artifactId>eureka-demo</artifactId> <version>0.0.1-SNAPSHOT</version> <name>eureka-demo</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> <spring-cloud.version>Hoxton.SR6</spring-cloud.version> </properties> <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> <exclusions> <exclusion> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> </exclusion> </exclusions> </dependency> </dependencies> <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> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
application.properties
server.port=8761 #eureka主机名 eureka.instance.hostname=eureka-server # 不把自己注册在eureka上 eureka.client.register-with-eureka=false # 不从eureka上获取服务注册信息 eureka.client.fetch-registry=false eureka.client.service-url.defaultZone=http://localhost:8761/eureka/
package com.example.eurekademo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer; /** * 注册中心 * http://localhost:8761/ */ @EnableEurekaServer // 启用注册中心 @SpringBootApplication public class EurekaDemoApplication { public static void main(String[] args) { SpringApplication.run(EurekaDemoApplication.class, args); } }
启用注册中心:http://localhost:8761/
服务注册(服务提供者)
pom.xml
<?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.3.2.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.example</groupId> <artifactId>provider-demo</artifactId> <version>0.0.1-SNAPSHOT</version> <name>provider-demo</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> <spring-cloud.version>Hoxton.SR6</spring-cloud.version> </properties> <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> <exclusions> <exclusion> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> </exclusion> </exclusions> </dependency> </dependencies> <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> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
application.properties
server.port=8002 spring.application.name=provider-demo #注册服务的时候使用ip地址 eureka.instance.prefer-ip-address=true eureka.client.service-url.defaultZone=http://localhost:8761/eureka/
package com.example.providerdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; /** * 服务提供者 */ @SpringBootApplication public class ProviderDemoApplication { public static void main(String[] args) { SpringApplication.run(ProviderDemoApplication.class, args); } }
服务
package com.example.providerdemo.service; import org.springframework.stereotype.Service; @Service public class TicketService { public String getTicket(){ return "电影票8001"; // return "电影票8002"; } }
接口
package com.example.providerdemo.controller; import com.example.providerdemo.service.TicketService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class TicketController { @Autowired TicketService ticketService; @GetMapping("/ticket") public String getTicket(){ return ticketService.getTicket(); } }
服务发现&服务消费者
pom.xml
<?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.3.2.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.example</groupId> <artifactId>consumer-demo</artifactId> <version>0.0.1-SNAPSHOT</version> <name>consumer-demo</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> <spring-cloud.version>Hoxton.SR6</spring-cloud.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <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-test</artifactId> <scope>test</scope> <exclusions> <exclusion> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> </exclusion> </exclusions> </dependency> </dependencies> <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> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
application.properties
server.port=8200 spring.application.name=consumer-demo #注册服务的时候使用ip地址 eureka.instance.prefer-ip-address=true eureka.client.service-url.defaultZone=http://localhost:8761/eureka/
package com.example.consumerdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.context.annotation.Bean; import org.springframework.web.client.RestTemplate; /** * 服务消费者 */ @EnableDiscoveryClient // 开启服务发现 @SpringBootApplication public class ConsumerDemoApplication { public static void main(String[] args) { SpringApplication.run(ConsumerDemoApplication.class, args); } @LoadBalanced // 开启负载均衡机制 @Bean public RestTemplate restTemplate(){ return new RestTemplate(); } }
package com.example.consumerdemo.controller; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; @RestController public class UserController { @Autowired RestTemplate restTemplate; @GetMapping("buy") public String getTicket(){ String ticket = restTemplate.getForObject("http://PROVIDER-DEMO/ticket", String.class); return "buy" + ticket; } }