1. 程式人生 > >Dubbo原始碼學習--Redis註冊中心(三)

Dubbo原始碼學習--Redis註冊中心(三)

基於 Redis 1 實現的註冊中心 2

/user-guide/images/dubbo-redis-registry.jpg

使用 Redis 的 Key/Map 結構儲存資料結構:

  • 主 Key 為服務名和型別
  • Map 中的 Key 為 URL 地址
  • Map 中的 Value 為過期時間,用於判斷髒資料,髒資料由監控中心刪除 3

使用 Redis 的 Publish/Subscribe 事件通知資料變更:

  • 通過事件的值區分事件型別:registerunregistersubscribeunsubscribe
  • 普通消費者直接訂閱指定服務提供者的 Key,只會收到指定服務的 registerunregister 事件
  • 監控中心通過 psubscribe 功能訂閱 /dubbo/*,會收到所有服務的所有變更事件

呼叫過程:

  1. 服務提供方啟動時,向 Key:/dubbo/com.foo.BarService/providers 下,添加當前提供者的地址
  2. 並向 Channel:/dubbo/com.foo.BarService/providers 傳送 register 事件
  3. 服務消費方啟動時,從 Channel:/dubbo/com.foo.BarService/providers 訂閱 register 和 unregister 事件
  4. 並向 Key:/dubbo/com.foo.BarService/providers 下,添加當前消費者的地址
  5. 服務消費方收到 register 和 unregister 事件後,從 Key:/dubbo/com.foo.BarService/providers
     下獲取提供者地址列表
  6. 服務監控中心啟動時,從 Channel:/dubbo/* 訂閱 register 和 unregister,以及 subscribeunsubsribe事件
  7. 服務監控中心收到 register 和 unregister 事件後,從 Key:/dubbo/com.foo.BarService/providers下獲取提供者地址列表
  8. 服務監控中心收到 subscribe 和 unsubsribe 事件後,從 Key:/dubbo/com.foo.BarService/consumers 下獲取消費者地址列表

配置

<dubbo:registry address="redis://10.20.153.10:6379"
/>

<dubbo:registry address="redis://10.20.153.10:6379?backup=10.20.153.11:6379,10.20.153.12:6379" />

<dubbo:registry protocol="redis" address="10.20.153.10:6379" />

<dubbo:registry protocol="redis" address="10.20.153.10:6379,10.20.153.11:6379,10.20.153.12:6379" />

選項

  • 可通過 <dubbo:registry group="dubbo" /> 設定 redis 中 key 的字首,預設為 dubbo
  • 可通過 <dubbo:registry cluster="replicate" /> 設定 redis 叢集策略,預設為 failover
    • failover: 只寫入和讀取任意一臺,失敗時重試另一臺,需要伺服器端自行配置資料同步
    • replicate: 在客戶端同時寫入所有伺服器,只讀取單臺,伺服器端不需要同步,註冊中心叢集增大,效能壓力也會更大

可靠性宣告

阿里內部並沒有採用 Redis 做為註冊中心,而是使用自己實現的基於資料庫的註冊中心,即:Redis 註冊中心並沒有在阿里內部長時間執行的可靠性保障,此 Redis 橋接實現只為開源版本提供,其可靠性依賴於 Redis 本身的可靠性。

Redis註冊中心實現了RedisRegistryFactory工廠,用來生成Registry的實現類RedisRegistry。

public class RedisRegistryFactory extends AbstractRegistryFactory {

    @Override
    protected Registry createRegistry(URL url) {
        return new RedisRegistry(url);
    }
}
RedisRegistry中主要實現瞭如下介面:
(1)doRegister(URL url);//向註冊中心註冊服務
(2)doUnregister(URL url);//取消服務註冊
(3)doSubscribe(URL url, NotifyListener listener);//向註冊中心監聽服務

(4)doUnsubscribe(URL url, NotifyListener listener);//取消監聽註冊中心服務

public class RedisRegistry extends FailbackRegistry {

    private static final Logger logger = LoggerFactory.getLogger(RedisRegistry.class);

    private static final int DEFAULT_REDIS_PORT = 6379;

    private final static String DEFAULT_ROOT = "dubbo";

    private final ScheduledExecutorService expireExecutor = Executors.newScheduledThreadPool(1, new NamedThreadFactory("DubboRegistryExpireTimer", true));

    private final ScheduledFuture<?> expireFuture;

    private final String root;

    private final Map<String, JedisPool> jedisPools = new ConcurrentHashMap<String, JedisPool>();

    private final ConcurrentMap<String, Notifier> notifiers = new ConcurrentHashMap<String, Notifier>();

    private final int reconnectPeriod;

    private final int expirePeriod;

    private volatile boolean admin = false;

    private boolean replicate;

	//建構函式中主要完成了redis連線相關的操作
    public RedisRegistry(URL url) {
        super(url);
        if (url.isAnyHost()) {
            throw new IllegalStateException("registry address == null");
        }
        GenericObjectPoolConfig config = new GenericObjectPoolConfig();
        config.setTestOnBorrow(url.getParameter("test.on.borrow", true));
        config.setTestOnReturn(url.getParameter("test.on.return", false));
        config.setTestWhileIdle(url.getParameter("test.while.idle", false));
        if (url.getParameter("max.idle", 0) > 0)
            config.setMaxIdle(url.getParameter("max.idle", 0));
        if (url.getParameter("min.idle", 0) > 0)
            config.setMinIdle(url.getParameter("min.idle", 0));
        if (url.getParameter("max.active", 0) > 0)
            config.setMaxTotal(url.getParameter("max.active", 0));
        if (url.getParameter("max.total", 0) > 0)
            config.setMaxTotal(url.getParameter("max.total", 0));
        if (url.getParameter("max.wait", url.getParameter("timeout", 0)) > 0)
            config.setMaxWaitMillis(url.getParameter("max.wait", url.getParameter("timeout", 0)));
        if (url.getParameter("num.tests.per.eviction.run", 0) > 0)
            config.setNumTestsPerEvictionRun(url.getParameter("num.tests.per.eviction.run", 0));
        if (url.getParameter("time.between.eviction.runs.millis", 0) > 0)
            config.setTimeBetweenEvictionRunsMillis(url.getParameter("time.between.eviction.runs.millis", 0));
        if (url.getParameter("min.evictable.idle.time.millis", 0) > 0)
            config.setMinEvictableIdleTimeMillis(url.getParameter("min.evictable.idle.time.millis", 0));

        String cluster = url.getParameter("cluster", "failover");
        if (!"failover".equals(cluster) && !"replicate".equals(cluster)) {
            throw new IllegalArgumentException("Unsupported redis cluster: " + cluster + ". The redis cluster only supported failover or replicate.");
        }
        replicate = "replicate".equals(cluster);

        List<String> addresses = new ArrayList<String>();
        addresses.add(url.getAddress());
        String[] backups = url.getParameter(Constants.BACKUP_KEY, new String[0]);
        if (backups != null && backups.length > 0) {
            addresses.addAll(Arrays.asList(backups));
        }

        String password = url.getPassword();
        for (String address : addresses) {
            int i = address.indexOf(':');
            String host;
            int port;
            if (i > 0) {
                host = address.substring(0, i);
                port = Integer.parseInt(address.substring(i + 1));
            } else {
                host = address;
                port = DEFAULT_REDIS_PORT;
            }
            if (StringUtils.isEmpty(password)) {
                this.jedisPools.put(address, new JedisPool(config, host, port,
                        url.getParameter(Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT)));
            } else {
                this.jedisPools.put(address, new JedisPool(config, host, port,
                        url.getParameter(Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT), password));
            }
        }

        this.reconnectPeriod = url.getParameter(Constants.REGISTRY_RECONNECT_PERIOD_KEY, Constants.DEFAULT_REGISTRY_RECONNECT_PERIOD);
        String group = url.getParameter(Constants.GROUP_KEY, DEFAULT_ROOT);
        if (!group.startsWith(Constants.PATH_SEPARATOR)) {
            group = Constants.PATH_SEPARATOR + group;
        }
        if (!group.endsWith(Constants.PATH_SEPARATOR)) {
            group = group + Constants.PATH_SEPARATOR;
        }
        this.root = group;

        this.expirePeriod = url.getParameter(Constants.SESSION_TIMEOUT_KEY, Constants.DEFAULT_SESSION_TIMEOUT);
        this.expireFuture = expireExecutor.scheduleWithFixedDelay(new Runnable() {
            public void run() {
                try {
                    deferExpired(); // Extend the expiration time
                } catch (Throwable t) { // Defensive fault tolerance
                    logger.error("Unexpected exception occur at defer expire time, cause: " + t.getMessage(), t);
                }
            }
        }, expirePeriod / 2, expirePeriod / 2, TimeUnit.MILLISECONDS);
    }

    private void deferExpired() {
        for (Map.Entry<String, JedisPool> entry : jedisPools.entrySet()) {
            JedisPool jedisPool = entry.getValue();
            try {
                Jedis jedis = jedisPool.getResource();
                try {
                    for (URL url : new HashSet<URL>(getRegistered())) {
                        if (url.getParameter(Constants.DYNAMIC_KEY, true)) {
                            String key = toCategoryPath(url);
                            if (jedis.hset(key, url.toFullString(), String.valueOf(System.currentTimeMillis() + expirePeriod)) == 1) {
                                jedis.publish(key, Constants.REGISTER);
                            }
                        }
                    }
                    if (admin) {
                        clean(jedis);
                    }
                    if (!replicate) {
                        break;//  If the server side has synchronized data, just write a single machine
                    }
                } finally {
                    jedis.close();
                }
            } catch (Throwable t) {
                logger.warn("Failed to write provider heartbeat to redis registry. registry: " + entry.getKey() + ", cause: " + t.getMessage(), t);
            }
        }
    }

    // The monitoring center is responsible for deleting outdated dirty data
    private void clean(Jedis jedis) {
        Set<String> keys = jedis.keys(root + Constants.ANY_VALUE);
        if (keys != null && !keys.isEmpty()) {
            for (String key : keys) {
                Map<String, String> values = jedis.hgetAll(key);
                if (values != null && values.size() > 0) {
                    boolean delete = false;
                    long now = System.currentTimeMillis();
                    for (Map.Entry<String, String> entry : values.entrySet()) {
                        URL url = URL.valueOf(entry.getKey());
                        if (url.getParameter(Constants.DYNAMIC_KEY, true)) {
                            long expire = Long.parseLong(entry.getValue());
                            if (expire < now) {
                                jedis.hdel(key, entry.getKey());
                                delete = true;
                                if (logger.isWarnEnabled()) {
                                    logger.warn("Delete expired key: " + key + " -> value: " + entry.getKey() + ", expire: " + new Date(expire) + ", now: " + new Date(now));
                                }
                            }
                        }
                    }
                    if (delete) {
                        jedis.publish(key, Constants.UNREGISTER);
                    }
                }
            }
        }
    }

    public boolean isAvailable() {
        for (JedisPool jedisPool : jedisPools.values()) {
            try {
                Jedis jedis = jedisPool.getResource();
                try {
                    if (jedis.isConnected()) {
                        return true; // At least one single machine is available.
                    }
                } finally {
                    jedis.close();
                }
            } catch (Throwable t) {
            }
        }
        return false;
    }

    @Override
    public void destroy() {
        super.destroy();
        try {
            expireFuture.cancel(true);
        } catch (Throwable t) {
            logger.warn(t.getMessage(), t);
        }
        try {
            for (Notifier notifier : notifiers.values()) {
                notifier.shutdown();
            }
        } catch (Throwable t) {
            logger.warn(t.getMessage(), t);
        }
        for (Map.Entry<String, JedisPool> entry : jedisPools.entrySet()) {
            JedisPool jedisPool = entry.getValue();
            try {
                jedisPool.destroy();
            } catch (Throwable t) {
                logger.warn("Failed to destroy the redis registry client. registry: " + entry.getKey() + ", cause: " + t.getMessage(), t);
            }
        }
    }

	//註冊地址到redis中主 Key 為服務名和型別
	//Map 中的 Key 為 URL 地址,Map 中的 Value 為過期時間,用於判斷髒資料,髒資料由監控中心刪除
    @Override
    public void doRegister(URL url) {
        String key = toCategoryPath(url);
        String value = url.toFullString();
        String expire = String.valueOf(System.currentTimeMillis() + expirePeriod);
        boolean success = false;
        RpcException exception = null;
        for (Map.Entry<String, JedisPool> entry : jedisPools.entrySet()) {
            JedisPool jedisPool = entry.getValue();
            try {
                Jedis jedis = jedisPool.getResource();
                try {
                    jedis.hset(key, value, expire);
                    jedis.publish(key, Constants.REGISTER);
                    success = true;
                    if (!replicate) {
                        break; //  If the server side has synchronized data, just write a single machine
                    }
                } finally {
                    jedis.close();
                }
            } catch (Throwable t) {
                exception = new RpcException("Failed to register service to redis registry. registry: " + entry.getKey() + ", service: " + url + ", cause: " + t.getMessage(), t);
            }
        }
        if (exception != null) {
            if (success) {
                logger.warn(exception.getMessage(), exception);
            } else {
                throw exception;
            }
        }
    }

	//取消註冊,刪除key值併發送通知
    @Override
    public void doUnregister(URL url) {
        String key = toCategoryPath(url);
        String value = url.toFullString();
        RpcException exception = null;
        boolean success = false;
        for (Map.Entry<String, JedisPool> entry : jedisPools.entrySet()) {
            JedisPool jedisPool = entry.getValue();
            try {
                Jedis jedis = jedisPool.getResource();
                try {
                    jedis.hdel(key, value);
                    jedis.publish(key, Constants.UNREGISTER);
                    success = true;
                    if (!replicate) {
                        break; //  If the server side has synchronized data, just write a single machine
                    }
                } finally {
                    jedis.close();
                }
            } catch (Throwable t) {
                exception = new RpcException("Failed to unregister service to redis registry. registry: " + entry.getKey() + ", service: " + url + ", cause: " + t.getMessage(), t);
            }
        }
        if (exception != null) {
            if (success) {
                logger.warn(exception.getMessage(), exception);
            } else {
                throw exception;
            }
        }
    }

	//訂閱
    @Override
    public void doSubscribe(final URL url, final NotifyListener listener) {
        String service = toServicePath(url);
        Notifier notifier = notifiers.get(service);
        if (notifier == null) {
			//開啟執行緒來訂閱通知資料
            Notifier newNotifier = new Notifier(service);
            notifiers.putIfAbsent(service, newNotifier);
            notifier = notifiers.get(service);
            if (notifier == newNotifier) {
                notifier.start();
            }
        }
        boolean success = false;
        RpcException exception = null;
        for (Map.Entry<String, JedisPool> entry : jedisPools.entrySet()) {
            JedisPool jedisPool = entry.getValue();
            try {
                Jedis jedis = jedisPool.getResource();
                try {
                    if (service.endsWith(Constants.ANY_VALUE)) {
                        admin = true;
                        Set<String> keys = jedis.keys(service);
                        if (keys != null && !keys.isEmpty()) {
                            Map<String, Set<String>> serviceKeys = new HashMap<String, Set<String>>();
                            for (String key : keys) {
                                String serviceKey = toServicePath(key);
                                Set<String> sk = serviceKeys.get(serviceKey);
                                if (sk == null) {
                                    sk = new HashSet<String>();
                                    serviceKeys.put(serviceKey, sk);
                                }
                                sk.add(key);
                            }
                            for (Set<String> sk : serviceKeys.values()) {
                                doNotify(jedis, sk, url, Arrays.asList(listener));
                            }
                        }
                    } else {
                        doNotify(jedis, jedis.keys(service + Constants.PATH_SEPARATOR + Constants.ANY_VALUE), url, Arrays.asList(listener));
                    }
                    success = true;
                    break; // Just read one server's data
                } finally {
                    jedis.close();
                }
            } catch (Throwable t) { // Try the next server
                exception = new RpcException("Failed to subscribe service from redis registry. registry: " + entry.getKey() + ", service: " + url + ", cause: " + t.getMessage(), t);
            }
        }
        if (exception != null) {
            if (success) {
                logger.warn(exception.getMessage(), exception);
            } else {
                throw exception;
            }
        }
    }

    @Override
    public void doUnsubscribe(URL url, NotifyListener listener) {
    }

    private void doNotify(Jedis jedis, String key) {
        for (Map.Entry<URL, Set<NotifyListener>> entry : new HashMap<URL, Set<NotifyListener>>(getSubscribed()).entrySet()) {
            doNotify(jedis, Arrays.asList(key), entry.getKey(), new HashSet<NotifyListener>(entry.getValue()));
        }
    }

    private void doNotify(Jedis jedis, Collection<String> keys, URL url, Collection<NotifyListener> listeners) {
        if (keys == null || keys.isEmpty()
                || listeners == null || listeners.isEmpty()) {
            return;
        }
        long now = System.currentTimeMillis();
        List<URL> result = new ArrayList<URL>();
        List<String> categories = Arrays.asList(url.getParameter(Constants.CATEGORY_KEY, new String[0]));
        String consumerService = url.getServiceInterface();
        for (String key : keys) {
            if (!Constants.ANY_VALUE.equals(consumerService)) {
                String prvoiderService = toServiceName(key);
                if (!prvoiderService.equals(consumerService)) {
                    continue;
                }
            }
            String category = toCategoryName(key);
            if (!categories.contains(Constants.ANY_VALUE) && !categories.contains(category)) {
                continue;
            }
            List<URL> urls = new ArrayList<URL>();
            Map<String, String> values = jedis.hgetAll(key);
            if (values != null && values.size() > 0) {
                for (Map.Entry<String, String> entry : values.entrySet()) {
                    URL u = URL.valueOf(entry.getKey());
                    if (!u.getParameter(Constants.DYNAMIC_KEY, true)
                            || Long.parseLong(entry.getValue()) >= now) {
                        if (UrlUtils.isMatch(url, u)) {
                            urls.add(u);
                        }
                    }
                }
            }
            if (urls.isEmpty()) {
                urls.add(url.setProtocol(Constants.EMPTY_PROTOCOL)
                        .setAddress(Constants.ANYHOST_VALUE)
                        .setPath(toServiceName(key))
                        .addParameter(Constants.CATEGORY_KEY, category));
            }
            result.addAll(urls);
            if (logger.isInfoEnabled()) {
                logger.info("redis notify: " + key + " = " + urls);
            }
        }
        if (result == null || result.isEmpty()) {
            return;
        }
        for (NotifyListener listener : listeners) {
            notify(url, listener, result);
        }
    }

    private String toServiceName(String categoryPath) {
        String servicePath = toServicePath(categoryPath);
        return servicePath.startsWith(root) ? servicePath.substring(root.length()) : servicePath;
    }

    private String toCategoryName(String categoryPath) {
        int i = categoryPath.lastIndexOf(Constants.PATH_SEPARATOR);
        return i > 0 ? categoryPath.substring(i + 1) : categoryPath;
    }

    private String toServicePath(String categoryPath) {
        int i;
        if (categoryPath.startsWith(root)) {
            i = categoryPath.indexOf(Constants.PATH_SEPARATOR, root.length());
        } else {
            i = categoryPath.indexOf(Constants.PATH_SEPARATOR);
        }
        return i > 0 ? categoryPath.substring(0, i) : categoryPath;
    }

    private String toServicePath(URL url) {
        return root + url.getServiceInterface();
    }

    private String toCategoryPath(URL url) {
        return toServicePath(url) + Constants.PATH_SEPARATOR + url.getParameter(Constants.CATEGORY_KEY, Constants.DEFAULT_CATEGORY);
    }

	//建立訊息訂閱執行緒,當服務未多個的時候建立的執行緒也是多個,比較消耗資源
    private class NotifySub extends JedisPubSub {

        private final JedisPool jedisPool;

        public NotifySub(JedisPool jedisPool) {
            this.jedisPool = jedisPool;
        }

		//呼叫回撥函式
        @Override
        public void onMessage(String key, String msg) {
            if (logger.isInfoEnabled()) {
                logger.info("redis event: " + key + " = " + msg);
            }
            if (msg.equals(Constants.REGISTER)
                    || msg.equals(Constants.UNREGISTER)) {
                try {
                    Jedis jedis = jedisPool.getResource();
                    try {
						//更新本地記憶體中的通知資料
                        doNotify(jedis, key);
                    } finally {
                        jedis.close();
                    }
                } catch (Throwable t) { // TODO Notification failure does not restore mechanism guarantee
                    logger.error(t.getMessage(), t);
                }
            }
        }

        @Override
        public void onPMessage(String pattern, String key, String msg) {
            onMessage(key, msg);
        }

        @Override
        public void onSubscribe(String key, int num) {
        }

        @Override
        public void onPSubscribe(String pattern, int num) {
        }

        @Override
        public void onUnsubscribe(String key, int num) {
        }

        @Override
        public void onPUnsubscribe(String pattern, int num) {
        }

    }
	//接收通知執行緒
    private class Notifier extends Thread {

        private final String service;
        private final AtomicInteger connectSkip = new AtomicInteger();
        private final AtomicInteger connectSkiped = new AtomicInteger();
        private final Random random = new Random();
        private volatile Jedis jedis;
        private volatile boolean first = true;
        private volatile boolean running = true;
        private volatile int connectRandom;

        public Notifier(String service) {
            super.setDaemon(true);
            super.setName("DubboRedisSubscribe");
            this.service = service;
        }

        private void resetSkip() {
            connectSkip.set(0);
            connectSkiped.set(0);
            connectRandom = 0;
        }

        private boolean isSkip() {
            int skip = connectSkip.get(); // Growth of skipping times
            if (skip >= 10) { // If the number of skipping times increases by more than 10, take the random number
                if (connectRandom == 0) {
                    connectRandom = random.nextInt(10);
                }
                skip = 10 + connectRandom;
            }
            if (connectSkiped.getAndIncrement() < skip) { // Check the number of skipping times
                return true;
            }
            connectSkip.incrementAndGet();
            connectSkiped.set(0);
            connectRandom = 0;
            return false;
        }
		//開啟執行緒監聽通知資料
        @Override
        public void run() {
            while (running) {
                try {
                    if (!isSkip()) {
                        try {
                            for (Map.Entry<String, JedisPool> entry : jedisPools.entrySet()) {
                                JedisPool jedisPool = entry.getValue();
                                try {
                                    jedis = jedisPool.getResource();
                                    try {
                                        if (service.endsWith(Constants.ANY_VALUE)) {
                                            if (!first) {
                                                first = false;
                                                Set<String> keys = jedis.keys(service);
                                                if (keys != null && !keys.isEmpty()) {
                                                    for (String s : keys) {
                                                        doNotify(jedis, s);
                                                    }
                                                }
                                                resetSkip();
                                            }
                                            jedis.psubscribe(new NotifySub(jedisPool), service); // blocking
                                        } else {
                                            if (!first) {
                                                first = false;
                                                doNotify(jedis, service);
                                                resetSkip();
                                            }
                                            jedis.psubscribe(new NotifySub(jedisPool), service + Constants.PATH_SEPARATOR + Constants.ANY_VALUE); // blocking
                                        }
                                        break;
                                    } finally {
                                        jedis.close();
                                    }
                                } catch (Throwable t) { // Retry another server
                                    logger.warn("Failed to subscribe service from redis registry. registry: " + entry.getKey() + ", cause: " + t.getMessage(), t);
                                    // If you only have a single redis, you need to take a rest to avoid overtaking a lot of CPU resources
                                    sleep(reconnectPeriod);
                                }
                            }
                        } catch (Throwable t) {
                            logger.error(t.getMessage(), t);
                            sleep(reconnectPeriod);
                        }
                    }
                } catch (Throwable t) {
                    logger.error(t.getMessage(), t);
                }
            }
        }

        public void shutdown() {
            try {
                running = false;
                jedis.disconnect();
            } catch (Throwable t) {
                logger.warn(t.getMessage(), t);
            }
        }

    }

}

相關推薦

Dubbo原始碼學習--Redis註冊中心

基於 Redis 1 實現的註冊中心 2。使用 Redis 的 Key/Map 結構儲存資料結構:主 Key 為服務名和型別Map 中的 Key 為 URL 地址Map 中的 Value 為過期時間,用於判斷髒資料,髒資料由監控中心刪除 3使用 Redis 的 Publish

淘淘商城系列—— 安裝zookeeper註冊中心

Zookeeper的介紹 註冊中心負責服務地址的註冊與查詢,相當於目錄服務,服務提供者和消費者只在啟動時與註冊中心互動,註冊中心不轉發請求,壓力較小。使用dubbo-2.3.3以上版本,官方建議使用zookeeper作為註冊中心。 Zookeeper是Apacahe Hadoop的子專案

spring原始碼學習筆記-初始化-BeanFactory

轉自http://www.sandzhang.com/blog/2011/04/05/spring-study-notes-initialization-3/ refresh()方法中在上篇obtainFreshBeanFactory()方法建立了beanfactory物

Dubbo原始碼學習--RoundRobinLoadBalance負載均衡

RoundRobin LoadBalance輪循,按公約後的權重設定輪循比率。存在慢的提供者累積請求的問題,比如:第二臺機器很慢,但沒掛,當請求調到第二臺時就卡在那,久而久之,所有請求都卡在調到第二臺上。1)獲取輪詢key  服務名+方法名獲取可供呼叫的invokers個數l

Golang原始碼學習:排程邏輯工作執行緒的執行流程與排程迴圈

本文內容主要分為三部分: 1. main goroutine 的排程執行 2. 非 main goroutine 的退出流程 3. 工作執行緒的執行流程與排程迴圈。 ## main goroutine 的排程執行 runtime·rt0_go中在呼叫完runtime.newproc建立main gorouti

Dubbo原始碼學習--註冊中心

    目前Dubbo官方提供的註冊中心有Multicast、Zookeeper、Redis和Simple註冊中心,官方推薦使用Zookeeper作為生產環境的註冊中心。    Dubbo官方也提供了擴充套件機制,開發人員可以根據自己的需要遵守一定的擴充套件規範開發自己的註冊

Redis學習筆記--Redis客戶端

本機 -c trace 圖形 tro cli family 毫秒 ati 1.Redis客戶端 1.1 Redis自帶的客戶端   (1)啟動   啟動客戶端命令:[root@kwredis bin]# ./redis-cli -h 127.0.0.1 -p 6379

dubbo入門學習註冊中心 zookeeper入門

一、Dubbo支援的註冊中心 1. Zookeeper   1.1 優點:支援網路叢集   1.2 缺點:穩定性受限於Zookeeper 2. Redis   2.1 優點:效能高.   2.2 缺點:對伺服器環境要求較高. 3. Multicast   3.1 優點:面中心化,不需要額外安裝

spring-cloud學習筆記Eureka註冊中心修改成IP顯示

修改配置類 eureka: instance: #使用IP訪問註冊中心 prefer-ip-address: true #在註冊中心status的時候顯示的格式,這裡是 ip:埠 instance-id: ${spring.cloud.c

從零學習遊戲伺服器開發 CSBattleMgr服務原始碼研究

如上圖所示,這篇文章我們將介紹CSBattleMgr的情況,但是我們不會去研究這個伺服器的特別細節的東西(這些細節我們將在後面的文章中介紹)。閱讀一個未知的專案原始碼如果我們開始就糾結於各種細節,那麼我們最終會陷入“橫看成嶺側成峰,遠近高低各不同”的尷尬境界,浪費時間不說,可

結合redis設計與實現的redis原始碼學習-1-記憶體分配zmalloc

在進入公司後的第一個任務就是使用redis的快取功能實現伺服器的雲託管功能,在瞭解了大致需求後,依靠之前對redis的瞭解封裝了常用的redis命令,並使用單例的連線池來維護與redis的連線,使用連線池來獲取redis的連線物件,依靠這些功能基本可以實現要求的

Spring-cloud微服務實戰【】:eureka註冊中心

  回憶一下,在上一篇文章中,我們建立了兩個springboot專案,並且在consumer專案中通過restTemplate進行HTTP通訊,成功訪問到了producer提供的介面,思考一下這樣的實現方式有什麼問題?   1.consumer必須知道producer的IP,才能呼叫對方的HTTP介面,並且在

spring boot 登錄註冊 demo -- 前後端傳遞

lin 表單提交 www col log ref rec put 內容 前端頁面通過thymeleaf渲染     <dependency> <groupId>org.springframework.boot</gro

Oracle學習系類篇

eat procedure body str 分組 錯誤 style mage 可見 1. 存儲過程 CREATE OR REPLACE PROCEDURE SP_NAME( PM_NAME [IN/OUT/IN OUT] PM_TYPE...)

小白學習之Code First

數據庫 change chang chan inf 模型 code test nbsp 上下文Context類中的base構造器的幾個方法重置(1、無參 2、database name 3 、 連接字符串) 無參:如果基類base方法中無參,code first將會以 :{

機器學習算法整理決策樹

outlook spa com width 選擇 clas .com img 衡量標準 決策樹的訓練與測試 如何切分特征(選擇節點) 衡量標準-熵 信息增益 決策樹構造實例 信息增益:表示特

Redis筆記整理:進階操作與高級部分

數據庫 NoSQL Redis [TOC] Redis筆記整理(三):進階操作與高級部分 Redis發布訂閱 Redis發布訂閱(pub/sub)是一種消息通信模式:發送者(pub)發送消息,訂閱者(sub)接收消息。 Redis客戶端可以訂閱任意數量的頻道。 下圖展示了頻道channel1,以

Redis詳解

redis codis twemproxy redis集群 redis-trib.rb 一、Redis集群介紹 Clustering:redis 3.0之後進入生產環境分布式數據庫,通過分片機制來進行數據分布,clustering 內的每個節點,僅有數據庫的一部分數據;去中心化的集群:re

Python學習過程筆記整理

font pytho def 駝峰 python學習 erl -s 函數參數 python 函數 -函數使用   -函數需要先定義,定義不會執行函數   -使用函數,俗稱調用 -定義函數   -格式:def 函數名稱(參數):,函數名稱不能用大駝峰,參數可以沒有 -調用函數

廖雪峰網站—學習python基礎知識

style 字符串 知識 code ron sar sof 轉換 () 一、判斷 1、條件判斷 age = 18 if age >= 18: print(‘your are is‘, age) print(‘adult‘) ag