今天来聊一聊分布式框架。介绍一下ZooKeeper与Dubbo。
下载链接:ZooKeeper
- 二进制文件,下载这个 apache-zookeeper-3.5.5-bin.tar.gz
- 源码,要研究源码,可以下这个 apache-zookeeper-3.5.5.tar.gz
Mac下使用ZooKeeper(单点模式)
- 修改zookeeper/conf下的zoo_sample.cfg文件,将zoo_sample.cfg重命名为zoo.cfg
- cd到当前目录 终端下执行
zkServer start
- 查看状态
zkServer status
- 连接到zk,
zkCli -server localhost
下面是zk中的一些命令,日常工作基本不会用到。有查看节点,新增节点,修改节点,删除节点。
ZooKeeper中的角色
角色 | 描述 |
---|---|
领导者(leader) | 负责投票的发起和决议,更新系统状态 |
学习者(learner)-跟随者(follower) | 接收客户端请求并向客户端返回结果,在选主过程中参与投票 |
学习者(learner)-观察者(observer) | 可以接收客户端连接,将请求转发给leader节点,但是不参与投票过程, 只同步leader状态,observer是为了扩展系统,提高读取速度 |
客户端(client) | 请求发起方 |
Dubbo
节点角色说明
节点 | 角色说明 |
---|---|
Provider | 暴露服务的服务提供方 |
Consumer | 调用远程服务的服务消费方 |
Registry | 服务注册与发现的注册中心 |
Monitor | 统计服务的调用次数和调用时间的监控中心 |
Container | 服务运行容器 |
调用关系说明
- 服务容器负责启动,加载,运行服务提供者。
- 服务提供者在启动时,向注册中心注册自己提供的服务。
- 服务消费者在启动时,向注册中心订阅自己所需的服务。
- 注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者。
- 服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用。
- 服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心。
现在SpringBoot都更新到2.0版本了,就用注解配置
吧。不用xml了…
嗯,简单一点,不用数据库了
- 配置好Swagger,全局异常拦截器
-
添加api模块
-
最后的结构
gradle:build.gradle
allprojects {
apply plugin: 'java'
apply plugin: 'idea'
apply plugin: 'maven'
apply plugin: 'eclipse'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
apply plugin: "io.franzbecker.gradle-lombok"
group = 'com.ler.dubbo'
version = '1.0.0-SNAPSHOT'
sourceCompatibility = '1.8'
jar.enabled = true
configurations {
developmentOnly
runtimeClasspath {
extendsFrom developmentOnly
}
compileOnly {
extendsFrom annotationProcessor
}
}
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
//dubbo zk
compile group: 'com.alibaba', name: 'dubbo', version: '2.6.1'
compile(group: 'com.101tec', name: 'zkclient', version: '0.10') {
exclude group: 'log4j', module: 'log4j'
exclude group: 'org.slf4j', module: 'slf4j-api'
exclude group: 'org.slf4j', module: 'slf4j-log4j12'
}
compile('org.apache.curator:curator-recipes:4.0.1') {
exclude group: 'org.apache.zookeeper', module: 'zookeeper'
}
}
}
buildscript {
ext {
springBootVersion = '2.0.2.RELEASE'
}
repositories {
maven {
url "https://plugins.gradle.org/m2/"
}
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
classpath "io.franzbecker:gradle-lombok:1.14"
}
}
dependencies {
compile project(":api")
implementation 'org.springframework.boot:spring-boot-starter-web'
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc'
compile group: 'com.alibaba', name: 'fastjson', version: '1.2.55'
compile group: 'org.apache.commons', name: 'commons-lang3', version: '3.8.1'
compile group: 'io.springfox', name: 'springfox-swagger2', version: '2.9.2'
compile group: 'io.springfox', name: 'springfox-swagger-ui', version: '2.9.2'
}
因为没有数据库,所以application.properties
配置简单了很多。 为了防止冲突,只有一个端口配置
server.port=8081
swagger 配置
package com.ler.dubbo.demo.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
/**
* @author lww
*/
@Configuration
@EnableSwagger2
public class SwaggerConfig implements WebMvcConfigurer {
@Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.forCodeGeneration(true)
.select()
.apis(RequestHandlerSelectors.basePackage("com.ler.dubbo.demo.controller"))
.build()
.apiInfo(apiInfo());
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder().title("Dubbo Demo").description("接口文档").version("1.0.0-SNAPSHOT").build();
}
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/**")
.addResourceLocations("classpath:/static/");
registry.addResourceHandler("swagger-ui.html")
.addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/");
}
}
DubboConfiguration
package com.ler.dubbo.demo.config;
import com.alibaba.dubbo.config.ApplicationConfig;
import com.alibaba.dubbo.config.ConsumerConfig;
import com.alibaba.dubbo.config.ProtocolConfig;
import com.alibaba.dubbo.config.RegistryConfig;
import com.alibaba.dubbo.config.spring.context.annotation.DubboComponentScan;
import java.util.HashMap;
import java.util.Map;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author lww
* @date 2019-08-17 15:37
*/
@Configuration
//扫描生产者service
@DubboComponentScan(basePackages = "com.ler.dubbo.demo.service.impl")
public class DubboConfiguration {
/**
* 当前应用配置
*/
@Bean
public ApplicationConfig applicationConfig() {
ApplicationConfig applicationConfig = new ApplicationConfig();
//应用名字
applicationConfig.setName("dubbo-demo");
Map<String, String> parameters = new HashMap<>(16);
//qos打开,一台机上多个项目时,可能会造成端口冲突
parameters.put("qos.enable", "false");
applicationConfig.setParameters(parameters);
return applicationConfig;
}
/**
* 连接注册中心配置
*/
@Bean
public RegistryConfig registryConfig() {
RegistryConfig registryConfig = new RegistryConfig();
//协议
registryConfig.setProtocol("zookeeper");
//zk地址
registryConfig.setAddress("127.0.0.1:2181");
//缓存存放路径
registryConfig.setFile("dubbo.cache");
//whether to export service on the register center
registryConfig.setRegister(true);
registryConfig.setClient("curator");
return registryConfig;
}
/**
* 服务提供者协议配置
*/
@Bean
public ProtocolConfig protocolConfig() {
ProtocolConfig protocolConfig = new ProtocolConfig();
//协议名字 还有其他的,感兴趣的可以去搜一下看看 rmi协议,hessian协议,thrift协议,http协议
protocolConfig.setName("dubbo");
//service port,-1 随机端口
protocolConfig.setPort(-1);
//thread pool size (fixed size)
protocolConfig.setThreads(100);
return protocolConfig;
}
/**
* 消费者统一设置
*/
@Bean
public ConsumerConfig consumerConfig() {
ConsumerConfig consumerConfig = new ConsumerConfig();
//超时时间
consumerConfig.setTimeout(3000);
//启动检查
consumerConfig.setCheck(false);
//懒加载
consumerConfig.setLazy(true);
return consumerConfig;
}
}
生产者
- Domain
package com.ler.dubbo.api.domain;
import java.io.Serializable;
import lombok.Data;
/**
* @author lww
* @date 2019-08-17 15:42
*/
@Data
public class User implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
private Integer age;
}
- Service
package com.ler.dubbo.api.service;
import com.ler.dubbo.api.domain.User;
/**
* @author lww
* @date 2019-08-17 15:41
*/
public interface UserService {
User getUser();
}
- ServiceImpl
package com.ler.dubbo.demo.service.impl;
import com.alibaba.dubbo.config.annotation.Service;
import com.ler.dubbo.api.domain.User;
import com.ler.dubbo.api.service.UserService;
/**
* @author lww
* @date 2019-08-17 15:53
*/
//Dubbo的Service注解,此处的timeout优先级更高
@Service(timeout = 5000)
//@Component 可以使用Spring的@Component注解,来让Spring来管理。因为测试Dubbo不用这个注解
public class UserServiceImpl implements UserService {
@Override
public User getUser() {
User user = new User();
user.setName("Hello Dubbo!");
user.setAge(123);
try {
Thread.sleep(4000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return user;
}
}
消费者
- Service
package com.ler.dubbo.demo.service;
import com.ler.dubbo.api.domain.User;
public interface LocalUserService {
User getUser();
}
- ServiceImpl
package com.ler.dubbo.demo.service.impl;
import com.alibaba.dubbo.config.annotation.Reference;
import com.ler.dubbo.api.domain.User;
import com.ler.dubbo.api.service.UserService;
import com.ler.dubbo.demo.service.LocalUserService;
import org.springframework.stereotype.Service;
/**
* @author lww
* @date 2019-08-17 16:53
*/
//Spring的Service注解
@Service
public class LocalUserServiceImpl implements LocalUserService {
//dubbo注解
@Reference
private UserService userService;
@Override
public User getUser() {
User user = userService.getUser();
return user;
}
}
- Controller
package com.ler.dubbo.demo.controller;
import com.ler.dubbo.api.domain.User;
import com.ler.dubbo.demo.result.HttpResult;
import com.ler.dubbo.demo.service.LocalUserService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import javax.annotation.Resource;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author lww
* @date 2019-08-17 16:55
*/
@Api(value = "/", description = "用户相关")
@RequestMapping("/")
@RestController
public class UserController {
@Resource
private LocalUserService localUserService;
@ApiOperation("获取用户")
@GetMapping("/user")
public HttpResult getUser() {
User user = localUserService.getUser();
return HttpResult.success(user);
}
}
- 超时时间统一设置为3秒,睡眠4秒
-
在方法上设置为5秒,修改后要重启,才能看到效果
-
响应正常
总结
下面是项目代码 项目源码