客户端应用
待补充
典型应用场景
分布式锁
非公平锁
执行流程如下:
如上实现方式在并发问题比较严重的情况下,性能会下降的比较厉害,主要原因是,所有的连接 都在对同一个节点进行监听,当服务器检测到删除事件时,要通知所有的连接,所有的连接同时收到事件,再次并发竞争,这就是羊群效应。使用公平锁可以避免羊群效应。
公平锁
执行流程如下:
公平锁和非公平锁都是互斥锁,同一时间只能有一个请求占用,如果是大量的并发上来,所有的请求都得加锁,性能是会急剧下降的。因此使用共享锁,可以在一定程度上解决这个问题。
//以下是curator官网上的公平锁示例
//client是zookeeper客户端连接,lockPath是分布式锁对应zookeeper的节点
InterProcessMutex lock = new InterProcessMutex(client, lockPath);
//maxWait, waitUnit两个参数可以对获取锁的时间做限制,lock.acquire()则是会一直等待直到获得锁
if ( lock.acquire(maxWait, waitUnit) )
{
try
{
// do some work inside of the critical section here
}
finally
{
lock.release();
}
}
共享锁
共享锁主要需要解决以下两个问题:
①读写不一致
②双写不一致问题
执行流程如下:
共享锁在公平锁的基础上,将锁分为读锁和写锁,读锁需要会被在他前面的写锁堵塞,并且需要监听前面的一些写锁,写锁则本质上就是一个公平锁,会被前面的读锁与写锁堵塞,并且需要对前面的一个锁监听。
//读锁示例
//client是zookeeper客户端连接,lockPath是分布式锁对应zookeeper的节点
InterProcessReadWriteLock lock = new InterProcessReadWriteLock(client, lockPath);
InterProcessMutex readLock = lock.readLock();
//maxWait, waitUnit两个参数可以对获取锁的时间做限制,lock.acquire()则是会一直等待直到获得锁
if ( readLock.acquire(maxWait, waitUnit) )
{
try
{
// do some work inside of the critical section here
}
finally
{
readLock.release();
}
}
//写锁示例
//client是zookeeper客户端连接,lockPath是分布式锁对应zookeeper的节点
InterProcessReadWriteLock lock = new InterProcessReadWriteLock(client, lockPath);
InterProcessMutex writeLock = lock.writeLock();
//maxWait, waitUnit两个参数可以对获取锁的时间做限制,lock.acquire()则是会一直等待直到获得锁
if ( writeLock.acquire(maxWait, waitUnit) )
{
try
{
// do some work inside of the critical section here
}
finally
{
writeLock.release();
}
}
Leader选举
Zookeeper可以进行对多个连接的客户端通过一定的规则选举出一个客户端来执行特定的代码,该特性可以用来执行Redis预热
//示例代码
public class LeaderSelectorDemo {
private static final String CONNECT_STR="192.168.109.200:2181";
private static RetryPolicy retryPolicy=new ExponentialBackoffRetry( 5*1000, 10 );
private static CuratorFramework curatorFramework;
private static CountDownLatch countDownLatch = new CountDownLatch(1);
public static void main(String[] args) throws InterruptedException {
String appName = System.getProperty("appName");
CuratorFramework curatorFramework = CuratorFrameworkFactory.newClient(CONNECT_STR, retryPolicy);
LeaderSelectorDemo.curatorFramework = curatorFramework;
curatorFramework.start();
LeaderSelectorListener listener = new LeaderSelectorListenerAdapter()
{
public void takeLeadership(CuratorFramework client) throws Exception
{
System.out.println(" I' m leader now . i'm , "+appName);
//睡眠15秒钟后结束,结束后zookeeper进行Leader选举,如果不想循环选举,可以让该线程不结束
TimeUnit.SECONDS.sleep(15);
}
};
//多个客户端争夺的节点Leader,可以用来作为Leader选举的分组依据
LeaderSelector selector = new LeaderSelector(curatorFramework, "/cachePreHeat_leader", listener);
selector.autoRequeue(); // not required, but this is behavior that you will probably expect
selector.start();
countDownLatch.await();
}
}
注册中心
场景分析
① 在分布式服务体系结构比较简单的场景下,Order-Service 需要调用 User-Service,我们直接将外部服务的ip以及端口配置在服务配置文件中
② 在外部服务是一个集群时,可以使用Nginx进行反向代理
③ 对于下图对应的较为复杂的分布式服务之间的调用,虽然使用Nginx可以实现,但是不好维护,每次增减服务时,都需要人工对Nginx甚至Nginx集群进行人工配置,这样不仅耗费人力,而且容易出错,此时,可以使用Zookeeper将各个服务信息进行保存,各个服务对其他服务的集群信息进行监听,如果其他服务的集群信息有所修改时,便能即时更新到本地缓存的各大服务集群信息,从而更好的实现远程服务集群信息维护
使用案例
被调用方
① 创建工程,引入依赖
<?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.5.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>product-center</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>product-center</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Hoxton.SR8</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-zookeeper-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</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>
② 设置配置文件
spring.application.name=product-center
#zookeeper 连接地址
spring.cloud.zookeeper.connect-string=ip:2181
#将本服务注册到zookeeper
spring.cloud.zookeeper.discovery.register=true
spring.cloud.zookeeper.session-timeout=30000
③ 编写远程接口
@SpringBootApplication
@RestController
public class ProductCenterApplication {
@Value("${server.port}")
private String port;
@Value( "${spring.application.name}" )
private String name;
@GetMapping("/getInfo")
public String getServerPortAndName(){
return this.name +" : "+ this.port;
}
public static void main(String[] args) {
SpringApplication.run(ProductCenterApplication.class, args);
}
}
调用方
① 创建工程,引入依赖
<?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.5.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>user-center</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>user-center</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Hoxton.SR8</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-zookeeper-discovery</artifactId>
</dependency>
<!--<dependency>-->
<!--<groupId>org.springframework.cloud</groupId>-->
<!--<artifactId>spring-cloud-starter-openfeign</artifactId>-->
<!--</dependency>-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</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>
② 设置配置文件
spring.application.name=user-center
#zookeeper 连接地址
spring.cloud.zookeeper.connect-string=ip:2181
③ 配置RestTemplate
@SpringBootApplication
public class UserCenterApplication {
public static void main(String[] args) {
SpringApplication.run(UserCenterApplication.class, args);
}
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
RestTemplate restTemplate = new RestTemplate();
return restTemplate;
}
}
④ 远程调用
@RestController
public class TestController {
@Autowired
private RestTemplate restTemplate;
@Autowired
private LoadBalancerClient loadBalancerClient;
@GetMapping("/test")
public String test() {
return this.restTemplate.getForObject("http://product-center/getInfo", String.class);
}
@GetMapping("/lb")
public String getLb(){
ServiceInstance choose = loadBalancerClient.choose("product-center");
String serviceId = choose.getServiceId();
int port = choose.getPort();
return serviceId + " : "+port;
}
}
运行测试
① 启动两个product-center 实例
② 启动一个user-center 实例并访问http://localhost:8080/test