柚子快报邀请码778899分享:Dubbo+Zookeeper
如果产品服务的业务量不是很大,业务又不是特别繁杂,功能模块的拆分颗粒度就不需要太细,也就没必要上微服务。因为微服务的运维治理复杂度及服务器资源开销代价是难以想象的。
本编简述下如何使用Dubbo+Zookeeper即可实现传统的分布式服务,并且支持高并发高可用场景下的动态扩容。
有关Dubbo的介绍,就无需多言了,它是一款高性能、轻量级的开源Java RPC框架(Remote Procedure Call Protocol 远程过程调用协议),它提供了三大核心能力:面向接口的远程方法调用,智能容错和负载均衡,以及服务自动注册和发现。
准备工作:
搭建springboot脚手架并成功运行,可参考历史分享springboot+mybatis 启动Zookeeper服务(作为Dubbo的服务注册协调中心)(搭建配置ZK服务,后续会在运维章节另行讲述)
1. maven添加Dubbo及ZK依赖
2.Dubbo配置
2.1 yml
#dubbodubbo: application: name: demo-user registry: address: zookeeper://192.168.2.7:2181?backup=192.168.2.8:2181,192.168.2.9:2181 file: /dubbo/demo-user/cache protocol: zookeeper check: false protocol: name: dubbo port: 28102 scan: base-packages: com.demo.user.service.dubbo consumer: check: false timeout: 10000 retries: 0 #ZK locklock: zk-servers: 192.168.2.7:2181,192.168.2.8:2181,192.168.2.9:2181
2.2 开启dubbo服务注解扫描
import com.alibaba.dubbo.config.spring.context.annotation.EnableDubbo;import org.springframework.boot.autoconfigure.SpringBootApplication;@EnableDubbo@SpringBootApplicationpublic class DemoUserApplication { public static void main(String[] args) { SpringApplication.run(DemoUserApplication.class, args); }}
3.Dubbo调用
3.1 dubbo service provider
import lombok.extern.slf4j.Slf4j;import com.alibaba.dubbo.config.annotation.Service;@Slf4j@Service // 注意该@Service注解是alibaba包下的,不要错引用了Spring包下的public class UserDubboServiceImpl implements UserDubboService {}
3.2 dubbo service consumer
import com.alibaba.dubbo.config.annotation.Reference;import lombok.extern.slf4j.Slf4j;import org.springframework.stereotype.Service;@Slf4j@Servicepublic class OrderServiceImpl extends BaseServiceImpl
4.ZK分布式锁
4.1 zk lock 配置
import lombok.extern.slf4j.Slf4j;import org.apache.curator.framework.CuratorFramework;import org.apache.curator.framework.CuratorFrameworkFactory;import org.apache.curator.framework.recipes.locks.InterProcessSemaphoreMutex;import org.apache.curator.framework.state.ConnectionState;import org.apache.curator.framework.state.ConnectionStateListener;import org.apache.curator.retry.ExponentialBackoffRetry;import org.springframework.beans.factory.InitializingBean;import org.springframework.beans.factory.annotation.Value;import org.springframework.context.annotation.Configuration;import java.util.concurrent.TimeUnit;/** * 分布式锁: * 注意该锁不能重入,即不可有嵌套锁*/@Slf4j@Configuration // 注意该组件需被springboot扫描注册成beanpublic class DistributedLock implements InitializingBean { private static CuratorFramework curator = null; private static ThreadLocal
/**
* bean添加到spring容器后,在完成bean实例化后才会执行该方法
*/ @Override
public void afterPropertiesSet() { if (curator == null) { curator = CuratorFrameworkFactory.builder().sessionTimeoutMs(30000).connectionTimeoutMs(30000) .retryPolicy(new ExponentialBackoffRetry(1000, Integer.MAX_VALUE)).connectString(zkServers).build(); curator.getConnectionStateListenable().addListener(new ZKListener()); curator.start(); } } /** * 不等待,遇锁直接退出 * * @param key * @return */ public static boolean acquire(String key) { try { InterProcessSemaphoreMutex lock = new InterProcessSemaphoreMutex(DistributedLock.curator, basePath + key); lock.acquire(); localLock.set(lock); } catch (Exception e) { log.warn("DistributedLock acquire error", e); return false; } return true; } /** * 排队等待 * * @param key * @param timeout * @return */ public static boolean acquire(String key, long timeout) { try { InterProcessSemaphoreMutex lock = new InterProcessSemaphoreMutex(DistributedLock.curator, basePath + key); lock.acquire(timeout, TimeUnit.SECONDS); localLock.set(lock); } catch (Exception e) { log.warn("DistributedLock acquire error", e); return false; } return true; } /** * 排队等待 * * @param key * @param timeout * @return */ public static boolean acquire(String key, long timeout, TimeUnit unit) { try { InterProcessSemaphoreMutex lock = new InterProcessSemaphoreMutex(DistributedLock.curator, basePath + key); lock.acquire(timeout, unit); localLock.set(lock); } catch (Exception e) { log.warn("DistributedLock acquire error", e); return false; } return true; } /** * 释放锁 */ public static void release() { try { InterProcessSemaphoreMutex lock = localLock.get(); if (lock != null && lock.isAcquiredInThisProcess()) { lock.release(); } } catch (Exception e) { log.warn("DistributedLock release error", e); } } /** * zk监听器 */ public class ZKListener implements ConnectionStateListener { @Override public void stateChanged(CuratorFramework client, ConnectionState state) { if (ConnectionState.LOST.equals(state)) { // 连接丢失 log.warn("==ZK LOCK== DistributedLock lost session with zookeeper"); } else if (ConnectionState.CONNECTED.equals(state)) { // 连接新建 log.warn("==ZK LOCK== DistributedLock connected with zookeeper"); } else if (ConnectionState.RECONNECTED.equals(state)) { // 重新连接 log.warn("==ZK LOCK== DistributedLock reconnected with zookeeper"); } } } /** * 分布式锁业务类型 */ public enum LockType { PRODUCT_INVENTORY }}
4.2 zk lock 使用
// 加锁, String lockKey = DistributedLock.LockType.PRODUCT_INVENTORY + "/" + productId; try{ if(DistributedLock.acquire(lockKey, 3)){ // todo something 抢购排队下单,扣减商品库存 } }catch(Exception e){ log.error("userId = {}, couponId = {}, 下单失败:{}", userId, productId, e.getMessage()); throw new Exception("下单失败", e); }finally{ DistributedLock.release(); } // service 方法中抛出异常,回滚事务 throw new CommonException("下单失败");
5.CAP理论
一个分布式系统最多只能同时满足一致性(Consistency)、可用性(Availability)和分区容错性(Partition tolerance)这三项中的两项,也即不可能三角CAP理论。
既然是分布式服务,自然需要具备分区容错性。除此之外,用户更多的是需要服务的可用性,至于一致性方面,目前常用的折中解决方案是,选择最终一致性。
以上关于后端服务常用的框架技术及一些相关中间件都已整理完毕,后续会更新基于SpringCloud Alibaba的微服务有关章节。
柚子快报邀请码778899分享:Dubbo+Zookeeper
推荐阅读
发表评论