二十五、SpringBoot與分散式(Zookeeper和Dubbo、Spring Boot和Spring Cloud)
一、分散式應用
在分散式系統中,國內常用zookeeper+dubbo組合,而SpringBoot推薦使用全棧的Spring,SpringBoot+SpringCloud。
分散式系統:
- 單一應用架構
當網站流量很小時,只需一個應用,將所有功能都部署在一起,以減少部署節點和成本。此時,用於簡化增刪改查工作量的資料訪問框架(ORM)是關鍵。
- 垂直應用架構
當訪問量逐漸增大,單一應用增加機器帶來的加速度越來越小,將應用拆成互不相干的幾個應用,以提升效率。此時,用於加速前端頁面開發的Web框架(MVC)是關鍵。
- 分散式服務架構
當垂直應用越來越多,應用之間互動不可避免,將核心業務抽取出來,作為獨立的服務,逐漸形成穩定的服務中心,使前端應用能更快速的響應多變的市場需求。此時, 用於提高業務複用及整合的分散式服務框架(RPC)是關鍵。
- 流動計算架構
當服務越來越多,容量的評估,小服務資源的浪費等問題逐漸顯現,此時需增加一個排程中心基於訪問壓力實時管理叢集容量,提高叢集利用率。此時,用於提高機器利用率的資源排程和治理中心(SOA)是關鍵。
二、Zookeeper和Dubbo
- ZooKeeper
ZooKeeper 是一個分散式的,開放原始碼的分散式應用程式協調服務。它是 一個為分散式應用提供一致性服務的軟體,提供的功能包括:配置維護、 域名服務、分散式同步、組服務等。
- Dubbo
Dubbo是Alibaba開源的分散式服務框架,它最大的特點是按照分層的方式來架構,使用這種方式可以使各個層之間解耦合(或者最大限度地鬆耦合)。從服務模型的角度來看,Dubbo採用的是一種非常簡單的模型,要麼是提供方提供服務,要麼是消費方消費服務,所以基於這一點可以抽象出服務提供方(Provider)和服務消費方(Consumer)兩個角色。
1、將服務提供者註冊到註冊中心
- 1.引入dubbo和zkclient相關依賴
<!-- 引入dubbo --> <dependency> <groupId>com.alibaba.boot</groupId> <artifactId>dubbo-spring-boot-starter</artifactId> <version>0.1.0</version> </dependency> <!-- 引入zookeeoer的客戶端工具 --> <dependency> <groupId>com.github.sgroschupf</groupId> <artifactId>zkclient</artifactId> <version>0.1</version> </dependency>
- 2.配置dubbo的掃描包和註冊中心地址
dubbo.application.name=provider-ticket
dubbo.registry.address=zookeeper://192.168.3.226:2181
dubbo.scan.base-packages=com.atguigu.ticket.service
- 3.使用@Service 釋出服務
package com.atguigu.ticket.service;
public interface TicketService {
public String getTicket();
}
package com.atguigu.ticket.service;
import com.alibaba.dubbo.config.annotation.Service;
import org.springframework.stereotype.Component;
@Component
@Service //將服務釋出出去
public class TicketServiceImpl implements TicketService{
@Override
public String getTicket() {
return "《hahaha》";
}
}
2、將服務消費者註冊到註冊中心
- 1.引入dubbo和zkclient相關依賴
<!-- 引入dubbo -->
<dependency>
<groupId>com.alibaba.boot</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>0.2.0</version>
</dependency>
<!-- 引入zookeeoer的客戶端工具 -->
<dependency>
<groupId>com.github.sgroschupf</groupId>
<artifactId>zkclient</artifactId>
<version>0.1</version>
</dependency>
- 2.配置dubbo的掃描包和註冊中心地址
dubbo.application.name=consumer-user
dubbo.registry.address=zookeeper://192.168.3.227:2182
- 3.引用服務
package com.atguigu.ticket.service;
public interface TicketService {
public String getTicket();
}
package com.atguigu.user.service;
import com.alibaba.dubbo.config.annotation.Reference;
import com.atguigu.ticket.service.TicketService;
import org.springframework.stereotype.Service;
@Service
public class UserService {
@Reference
TicketService ticketService;
public void hello(){
String ticket = ticketService.getTicket();
System.out.println("買到票拉"+ticket);
}
}
3、遇到一個問題沒有解決:'消費方'註解引用'提供方'的介面時報空指標
java.lang.NullPointerException
at com.atguigu.user.service.UserService.hello(UserService.java:13)
at com.atguigu.user.ConsumerUserApplicationTests.contextLoads(ConsumerUserApplicationTests.java:18)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.springframework.test.context.junit4.statements.RunBeforeTestExecutionCallbacks.evaluate(RunBeforeTestExecutionCallbacks.java:74)
at org.springframework.test.context.junit4.statements.RunAfterTestExecutionCallbacks.evaluate(RunAfterTestExecutionCallbacks.java:84)
at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75)
at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)
at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:251)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:97)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:190)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
三、Spring Boot和Spring Cloud
1、Spring Cloud
Spring Cloud是一個分散式的整體解決方案。Spring Cloud 為開發者提供了在分散式系統(配 置管理,服務發現,熔斷,路由,微代理,控制匯流排,一次性token,全域性瑣,leader選舉,分 布式session,叢集狀態)中快速構建的工具,使用Spring Cloud的開發者可以快速的啟動服務或構建應用、同時能夠快速和雲平臺資源進行對接。
SpringCloud分散式開發五大常用元件
- 服務發現——Netflix Eureka
- 客服端負載均衡——Netflix Ribbon
- 斷路器——Netflix Hystrix
- 服務閘道器——Netflix Zuul
- 分散式配置——Spring Cloud Config
2、微服務
3、Spring Cloud 入門
-
1.建立註冊中心
/** * 1、引入eureka * 2、配置Eureka資訊 * 3、@EnableEurekaServer //啟用註冊中心的功能 * */
pom.xml
<!-- 引入eureka -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
application.yml
# 配置Eureka資訊
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/
EurekaServerApplication
//啟用註冊中心的功能
@EnableEurekaServer
@SpringBootApplication
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class, args);
}
}
- 2.建立provider
/** * 1、引入eureka-client * 2、配置Eureka資訊 * 3、寫service和control並執行,將服務註冊進註冊中心 * */
pom.xml
<!-- 引入eureka-client -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!-- 引入web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
application.yml
server:
port: 8002
spring:
application:
name: provider-ticket
eureka:
instance:
prefer-ip-address: true #註冊服務的時候使用服務的ip地址
client:
service-url:
defaultZone: http://localhost:8761/eureka
Service
@Service
public class TicketService {
public String getTicket(){
System.out.println("8002");
return "<鬥牛>";
}
}
Controller
@RestController
public class TicketController {
@Autowired
TicketService ticketService;
@GetMapping("/ticket")
public String getTicket(){
return ticketService.getTicket();
}
}
執行測試結果
- 3.建立consumer
/** * 1、引入eureka-client * 2、配置Eureka資訊 * 3、@EnableDiscoveryClient //開啟發現服務功能 * 4、使用RestTemplate 傳送http請求的 * 5、@LoadBalanced //使用負載均衡機制 * */
pom.xml
<!-- 引入eureka-client -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!-- 引入web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
application.yml
server:
port: 8200
spring:
application:
name: consumer-user
eureka:
instance:
prefer-ip-address: true #註冊服務的時候使用服務的ip地址
client:
service-url:
defaultZone: http://localhost:8761/eureka
ConsumerUserApplication
@EnableDiscoveryClient //開啟發現服務功能
@SpringBootApplication
public class ConsumerUserApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerUserApplication.class, args);
}
//傳送http請求的
@LoadBalanced //使用負載均衡機制
@Bean
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
Controller
@RestController
public class UserController {
@Autowired
RestTemplate restTemplate;
@GetMapping("/buy")
public String buyTicket(String name){
String s = restTemplate.getForObject("http://PROVIDER-TICKET/ticket", String.class);
return "購買力"+name+s;
}
}
執行測試結果
- 4.provider多例項測試
1.將不同埠號的provider-ticket打包放在service資料夾中
2.在兩個終端視窗啟動兩個服務
Amy:~ Amy$ cd /Users/Amy/Downloads/service
Amy:service Amy$ java -jar provider-ticket-0.0.1-8001SNAPSHOT.jar
Amy:~ Amy$ cd /Users/Amy/Downloads/service
Amy:service Amy$ java -jar provider-ticket-0.0.1-8002SNAPSHOT.jar
3.啟動ConsumerUserApplication
4.執行ConsumerUser的buy方法(多次執行),啟用了負載均衡機制