etcd是一个开源的、分布式的键值对数据存储系统,提供共享配置、服务的注册和发现。etcd与zookeeper相比算是轻量级系统,两者的一致性协议也一样,etcd的raft比zookeeper的paxos简单。关于windows版本的etcd服务端和nodejs浏览器下载和安装见etcd服务端和客户端安装

  我们用etcd,就需要etcd客户端,这里用的是java客户端etcd4j。etcd客户端通过http发送get、put、post、delete等操作到服务端执行对目录信息的增删查改。etcd应用于微服务架构中的角色是服务注册中心,通过对接口调用信息的添加和查询,提供服务的注册和发现能力。

  下面结合etcd4j的主要功能类EtcdClient,我们看看etcd的一些操作:

 * Copyright (c) 2015, Jurriaan Mous and contributors as indicated by the @author tags.
package mousio.etcd4j; import io.netty.handler.ssl.SslContext;
import mousio.client.retry.RetryPolicy;
import mousio.client.retry.RetryWithExponentialBackOff;
import mousio.etcd4j.requests.*;
import mousio.etcd4j.responses.EtcdAuthenticationException;
import mousio.etcd4j.responses.EtcdException;
import mousio.etcd4j.responses.EtcdHealthResponse;
import mousio.etcd4j.responses.EtcdMembersResponse;
import mousio.etcd4j.responses.EtcdSelfStatsResponse;
import mousio.etcd4j.responses.EtcdStoreStatsResponse;
import mousio.etcd4j.responses.EtcdVersionResponse;
import mousio.etcd4j.responses.EtcdLeaderStatsResponse;
import mousio.etcd4j.transport.EtcdClientImpl;
import mousio.etcd4j.transport.EtcdNettyClient; import java.io.Closeable;
import java.io.IOException;
import java.net.URI;
import java.util.concurrent.TimeoutException; /**
* Etcd client.
*/
public class EtcdClient implements Closeable {
private final EtcdClientImpl client;
private RetryPolicy retryHandler; /**
* Constructor
*
* @param baseUri URI to create connection on
*/
public EtcdClient(URI... baseUri) {
this(EtcdSecurityContext.NONE, baseUri);
} /**
* Constructor
*
* @param username username
* @param password password
* @param baseUri URI to create connection on
*/
public EtcdClient(String username, String password, URI... baseUri) {
this(EtcdSecurityContext.withCredential(username, password), baseUri);
} /**
* Constructor
*
* @param sslContext context for Ssl connections
* @param username username
* @param password password
* @param baseUri URI to create connection on
*/
public EtcdClient(SslContext sslContext, String username, String password, URI... baseUri) {
this(new EtcdSecurityContext(sslContext, username, password), baseUri);
} /**
* Constructor
*
* @param sslContext context for Ssl connections
* @param baseUri URI to create connection on
*/
public EtcdClient(SslContext sslContext, URI... baseUri) {
this(EtcdSecurityContext.withSslContext(sslContext), baseUri);
} /**
* Constructor
*
* @param securityContext context for security
* @param baseUri URI to create connection on
*/
public EtcdClient(EtcdSecurityContext securityContext, URI... baseUri) {
this(new EtcdNettyClient(
securityContext,
(baseUri.length == 0)
? new URI[] { URI.create("https://127.0.0.1:4001") }
: baseUri
));
} /**
* Create a client with a custom implementation
*
* @param etcdClientImpl to create client with.
*/
public EtcdClient(EtcdClientImpl etcdClientImpl) {
this.client = etcdClientImpl;
this.retryHandler = RetryWithExponentialBackOff.DEFAULT;
} /**
* Get the version of the Etcd server
*
* @return version as String
* @deprecated use version() when using etcd 2.1+.
*/
@Deprecated
public String getVersion() {
try {
return new EtcdOldVersionRequest(this.client, retryHandler).send().get();
} catch (IOException | EtcdException | EtcdAuthenticationException | TimeoutException e) {
return null;
}
} /**
* Get the version of the Etcd server
*
* @return version
*/
public EtcdVersionResponse version() {
try {
return new EtcdVersionRequest(this.client, retryHandler).send().get();
} catch (IOException | EtcdException | EtcdAuthenticationException | TimeoutException e) {
return null;
}
} /**
* Get the Self Statistics of Etcd
*
* @return EtcdSelfStatsResponse
*/
public EtcdSelfStatsResponse getSelfStats() {
try {
return new EtcdSelfStatsRequest(this.client, retryHandler).send().get();
} catch (IOException | EtcdException | EtcdAuthenticationException | TimeoutException e) {
return null;
}
} /**
* Get the Leader Statistics of Etcd
*
* @return EtcdLeaderStatsResponse
*/
public EtcdLeaderStatsResponse getLeaderStats() {
try {
return new EtcdLeaderStatsRequest(this.client, retryHandler).send().get();
} catch (IOException | EtcdException | EtcdAuthenticationException | TimeoutException e) {
return null;
}
} /**
* Get the Store Statistics of Etcd
*
* @return vEtcdStoreStatsResponse
*/
public EtcdStoreStatsResponse getStoreStats() {
try {
return new EtcdStoreStatsRequest(this.client, retryHandler).send().get();
} catch (IOException | EtcdException | EtcdAuthenticationException | TimeoutException e) {
return null;
}
} /**
* Get the Members of Etcd
*
* @return vEtcdMembersResponse
*/
public EtcdMembersResponse getMembers() {
try {
return new EtcdMembersRequest(this.client,retryHandler).send().get();
} catch (IOException | EtcdException | EtcdAuthenticationException | TimeoutException e) {
return null;
}
} /**
* Get the Members of Etcd
*
* @return vEtcdMembersResponse
*/
public EtcdHealthResponse getHealth() {
try {
return new EtcdHealthRequest(this.client,retryHandler).send().get();
} catch (IOException | EtcdException | EtcdAuthenticationException | TimeoutException e) {
return null;
}
} /**
* Put a key with a value
*
* @param key to put
* @param value to put on key
* @return EtcdKeysRequest
*/
public EtcdKeyPutRequest put(String key, String value) {
return new EtcdKeyPutRequest(client, key, retryHandler).value(value);
} /**
* Refresh a key with new ttl
* (without notifying watchers when using etcd 2.3+)
*
* @param key to refresh
* @param ttl to update key with
* @return EtcdKeysRequest
*/
public EtcdKeyPutRequest refresh(String key, Integer ttl) {
return new EtcdKeyPutRequest(client, key, retryHandler).refresh(ttl);
} /**
* Create a dir
*
* @param dir to create
* @return EtcdKeysRequest
*/
public EtcdKeyPutRequest putDir(String dir) {
return new EtcdKeyPutRequest(client, dir, retryHandler).isDir();
} /**
* Post a value to a key for in-order keys.
*
* @param key to post to
* @param value to post
* @return EtcdKeysRequest
*/
public EtcdKeyPostRequest post(String key, String value) {
return new EtcdKeyPostRequest(client, key, retryHandler).value(value);
} /**
* Deletes a key
*
* @param key to delete
* @return EtcdKeysRequest
*/
public EtcdKeyDeleteRequest delete(String key) {
return new EtcdKeyDeleteRequest(client, key, retryHandler);
} /**
* Deletes a directory
*
* @param dir to delete
* @return EtcdKeysRequest
*/
public EtcdKeyDeleteRequest deleteDir(String dir) {
return new EtcdKeyDeleteRequest(client, dir, retryHandler).dir();
} /**
* Get by key
*
* @param key to get
* @return EtcdKeysRequest
*/
public EtcdKeyGetRequest get(String key) {
return new EtcdKeyGetRequest(client, key, retryHandler);
} /**
* Get directory
*
* @param dir to get
* @return EtcdKeysGetRequest
*/
public EtcdKeyGetRequest getDir(String dir) {
return new EtcdKeyGetRequest(client, dir, retryHandler).dir();
} /**
* Get all keys
*
* @return EtcdKeysRequest
*/
public EtcdKeyGetRequest getAll() {
return new EtcdKeyGetRequest(client, retryHandler);
} @Override
public void close() throws IOException {
if (client != null) {
client.close();
}
} /**
* Set the retry handler. Default is an exponential back-off with start of 20ms.
*
* @param retryHandler to set
* @return this instance
*/
public EtcdClient setRetryHandler(RetryPolicy retryHandler) {
this.retryHandler = retryHandler;
return this;
}
}

  这个类提供能etcd连接的方法,也就是构造器方法,提供了对etcd的操作方法(get、put、post、putDir、delete、deleteDir等),还提供了重试策略(参见RetryPolicy)。我们跟一下EtcdClient(URI... baseUri),进入EtcdClient(EtcdSecurityContext securityContext, URI... baseUri),实例化EtcdNettyClient:

 * Copyright (c) 2015, Jurriaan Mous and contributors as indicated by the @author tags.
package mousio.etcd4j.transport; import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioDatagramChannel;
import io.netty.handler.codec.base64.Base64;
import io.netty.handler.codec.http.*;
import io.netty.handler.codec.http.multipart.HttpPostRequestEncoder;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslHandler;
import io.netty.handler.stream.ChunkedWriteHandler;
import io.netty.handler.timeout.ReadTimeoutHandler;
import io.netty.resolver.dns.DnsAddressResolverGroup;
import io.netty.resolver.dns.DnsServerAddresses;
import io.netty.util.CharsetUtil;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;
import io.netty.util.concurrent.Promise;
import mousio.client.ConnectionState;
import mousio.client.retry.RetryHandler;
import mousio.etcd4j.EtcdSecurityContext;
import mousio.etcd4j.promises.EtcdResponsePromise;
import mousio.etcd4j.requests.EtcdRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.URI;
import java.nio.channels.ClosedChannelException;
import java.util.Map;
import java.util.concurrent.CancellationException; /**
* @author Jurriaan Mous
* @author Luca Burgazzoli
*
* Netty client for the requests and responses
*/
public class EtcdNettyClient implements EtcdClientImpl {
private static final Logger logger = LoggerFactory.getLogger(EtcdNettyClient.class); // default etcd port
private static final int DEFAULT_PORT = 2379;
private static final String ENV_ETCD4J_ENDPOINT = "ETCD4J_ENDPOINT";
private final EventLoopGroup eventLoopGroup;
private final URI[] uris; private final Bootstrap bootstrap;
//private final String hostName;
private final EtcdNettyConfig config;
private final EtcdSecurityContext securityContext; protected volatile int lastWorkingUriIndex; /**
* Constructor
*
* @param sslContext SSL context if connecting with SSL. Null if not connecting with SSL.
* @param uri to connect to
*/
public EtcdNettyClient(final SslContext sslContext, final URI... uri) {
this(new EtcdNettyConfig(), sslContext, uri);
} /**
* Constructor
*
* @param securityContext security context.
* @param uri to connect to
*/
public EtcdNettyClient(final EtcdSecurityContext securityContext, final URI... uri) {
this(new EtcdNettyConfig(), securityContext, uri);
} /**
* Constructor with custom eventloop group and timeout
*
* @param config for netty
* @param sslContext SSL context if connecting with SSL. Null if not connecting with SSL.
* @param uris to connect to
*/
public EtcdNettyClient(final EtcdNettyConfig config,
final SslContext sslContext, final URI... uris) {
this(config, new EtcdSecurityContext(sslContext), uris);
} /**
* Constructor with custom eventloop group and timeout
*
* @param config for netty
* @param uris to connect to
*/
public EtcdNettyClient(final EtcdNettyConfig config, final URI... uris) {
this(config, EtcdSecurityContext.NONE, uris);
} /**
* Constructor with custom eventloop group and timeout
*
* @param config for netty
* @param securityContext security context (ssl, authentication)
* @param uris to connect to
*/
public EtcdNettyClient(final EtcdNettyConfig config,
final EtcdSecurityContext securityContext, final URI... uris) {
logger.info("Setting up Etcd4j Netty client"); this.lastWorkingUriIndex = 0;
this.config = config.clone();
this.securityContext = securityContext.clone();
this.uris = uris;
this.eventLoopGroup = config.getEventLoopGroup();
this.bootstrap = new Bootstrap()
.group(eventLoopGroup)
.channel(config.getSocketChannelClass())
.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)
.option(ChannelOption.TCP_NODELAY, true)
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, config.getConnectTimeout())
.resolver(new DnsAddressResolverGroup(
NioDatagramChannel.class,
DnsServerAddresses.defaultAddresses()))
.handler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline p = ch.pipeline();
if (securityContext.hasNettySsl()) {
p.addLast(securityContext.nettySslContext().newHandler(ch.alloc()));
} else if (securityContext.hasSsl()) {
p.addLast(new SslHandler(securityContext.sslContext().createSSLEngine()));
}
p.addLast("codec", new HttpClientCodec());
p.addLast("auth", new HttpBasicAuthHandler());
p.addLast("chunkedWriter", new ChunkedWriteHandler());
p.addLast("aggregate", new HttpObjectAggregator(config.getMaxFrameSize()));
}
});
} /**
* For tests
*
* @return the current bootstrap
*/
protected Bootstrap getBootstrap() {
return bootstrap;
} /**
* Send a request and get a future.
*
* @param etcdRequest Etcd Request to send
* @return Promise for the request.
*/
public <R> EtcdResponsePromise<R> send(final EtcdRequest<R> etcdRequest) throws IOException {
ConnectionState connectionState = new ConnectionState(uris, lastWorkingUriIndex); if (etcdRequest.getPromise() == null) {
etcdRequest.setPromise(new EtcdResponsePromise<R>(
etcdRequest.getRetryPolicy(),
connectionState,
new RetryHandler() {
@Override
public void doRetry(ConnectionState connectionState) throws IOException {
connect(etcdRequest, connectionState);
}
}));
} connect(etcdRequest, connectionState); return etcdRequest.getPromise();
} /**
* Connect to server
*
* @param etcdRequest to request with
* @param <R> Type of response
* @throws IOException if request could not be sent.
*/
@SuppressWarnings("unchecked")
protected <R> void connect(final EtcdRequest<R> etcdRequest) throws IOException {
this.connect(etcdRequest, etcdRequest.getPromise().getConnectionState());
} /**
* Connect to server
*
* @param etcdRequest to request with
* @param connectionState for retries
* @param <R> Type of response
* @throws IOException if request could not be sent.
*/
@SuppressWarnings("unchecked")
protected <R> void connect(final EtcdRequest<R> etcdRequest, final ConnectionState connectionState) throws IOException {
if(eventLoopGroup.isShuttingDown() || eventLoopGroup.isShutdown() || eventLoopGroup.isTerminated()){
etcdRequest.getPromise().getNettyPromise().cancel(true);
logger.debug("Retry canceled because of closed etcd client");
return;
} final URI uri; // when we are called from a redirect, the url in the request may also
// contain host and port!
URI requestUri = URI.create(etcdRequest.getUrl());
if (requestUri.getHost() != null && requestUri.getPort() > -1) {
uri = requestUri;
} else if (connectionState.uris.length == 0 && System.getenv(ENV_ETCD4J_ENDPOINT) != null) {
// read uri from environment variable
String endpoint_uri = System.getenv(ENV_ETCD4J_ENDPOINT);
if(logger.isDebugEnabled()) {
logger.debug("Will use environment variable {} as uri with value {}", ENV_ETCD4J_ENDPOINT, endpoint_uri);
}
uri = URI.create(endpoint_uri);
} else {
uri = connectionState.uris[connectionState.uriIndex];
} // Start the connection attempt.
final ChannelFuture connectFuture = bootstrap.connect(connectAddress(uri));
etcdRequest.getPromise().getConnectionState().loop = connectFuture.channel().eventLoop();
etcdRequest.getPromise().attachNettyPromise(connectFuture.channel().eventLoop().<R>newPromise()); connectFuture.addListener(new GenericFutureListener<ChannelFuture>() {
@Override
public void operationComplete(final ChannelFuture f) throws Exception {
if (!f.isSuccess()) {
final Throwable cause = f.cause();
if (logger.isDebugEnabled()) {
logger.debug("Connection failed to {}, cause {}", connectionState.uris[connectionState.uriIndex], cause);
} if (cause instanceof ClosedChannelException || cause instanceof IllegalStateException) {
etcdRequest.getPromise().cancel(new CancellationException("Channel closed"));
} else {
etcdRequest.getPromise().handleRetry(f.cause());
} return;
} // Handle already cancelled promises
if (etcdRequest.getPromise().getNettyPromise().isCancelled()) {
f.channel().close();
etcdRequest.getPromise().getNettyPromise().setFailure(new CancellationException());
return;
} final Promise listenedToPromise = etcdRequest.getPromise().getNettyPromise(); // Close channel when promise is satisfied or cancelled later
listenedToPromise.addListener(new GenericFutureListener<Future<?>>() {
@Override
public void operationComplete(Future<?> future) throws Exception {
// Only close if it was not redirected to new promise
if (etcdRequest.getPromise().getNettyPromise() == listenedToPromise) {
f.channel().close();
}
}
}); if (logger.isDebugEnabled()) {
logger.debug("Connected to {} ({})", f.channel().remoteAddress().toString(), connectionState.uriIndex);
} lastWorkingUriIndex = connectionState.uriIndex; modifyPipeLine(etcdRequest, f.channel().pipeline()); createAndSendHttpRequest(uri, etcdRequest.getUrl(), etcdRequest, f.channel())
.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
if (!future.isSuccess()) {
etcdRequest.getPromise().setException(future.cause());
if (!f.channel().eventLoop().inEventLoop()) {
f.channel().eventLoop().shutdownGracefully();
} f.channel().close();
}
}
}); f.channel().closeFuture().addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
if (logger.isDebugEnabled()) {
logger.debug("Connection closed for request {} on uri {} ",
etcdRequest.getMethod().name(),
etcdRequest.getUri());
}
}
});
}
});
} /**
* Modify the pipeline for the request
*
* @param req to process
* @param pipeline to modify
* @param <R> Type of Response
*/
private <R> void modifyPipeLine(final EtcdRequest<R> req, final ChannelPipeline pipeline) {
final EtcdResponseHandler<R> handler = new EtcdResponseHandler<>(this, req); if (req.hasTimeout()) {
pipeline.addFirst(new ReadTimeoutHandler(req.getTimeout(), req.getTimeoutUnit()));
} pipeline.addLast(handler);
pipeline.addLast(new ChannelHandlerAdapter() {
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
handler.retried(true);
req.getPromise().handleRetry(cause);
}
});
} /**
* Get HttpRequest belonging to etcdRequest
*
* @param server server for http request
* @param uri to send request to
* @param etcdRequest to send
* @param channel to send request on
* @param <R> Response type
* @return HttpRequest
* @throws Exception when creating or sending HTTP request fails
*/
private <R> ChannelFuture createAndSendHttpRequest(URI server, String uri, EtcdRequest<R> etcdRequest, Channel channel) throws Exception {
HttpRequest httpRequest = new DefaultHttpRequest(HttpVersion.HTTP_1_1, etcdRequest.getMethod(), uri);
httpRequest.headers().add(HttpHeaderNames.CONNECTION, "keep-alive");
if(!this.config.hasHostName()) {
httpRequest.headers().add(HttpHeaderNames.HOST, server.getHost() + ":" + server.getPort());
} else {
httpRequest.headers().add(HttpHeaderNames.HOST, this.config.getHostName());
} HttpPostRequestEncoder bodyRequestEncoder = null;
Map<String, String> keyValuePairs = etcdRequest.getRequestParams();
if (keyValuePairs != null && !keyValuePairs.isEmpty()) {
HttpMethod etcdRequestMethod = etcdRequest.getMethod();
if (etcdRequestMethod == HttpMethod.POST || etcdRequestMethod == HttpMethod.PUT) {
bodyRequestEncoder = new HttpPostRequestEncoder(httpRequest, false);
for (Map.Entry<String, String> entry : keyValuePairs.entrySet()) {
bodyRequestEncoder.addBodyAttribute(entry.getKey(), entry.getValue());
} httpRequest = bodyRequestEncoder.finalizeRequest();
} else {
QueryStringEncoder encoder = new QueryStringEncoder(uri);
for (Map.Entry<String, String> entry : keyValuePairs.entrySet()) {
encoder.addParam(entry.getKey() , entry.getValue());
} httpRequest.setUri(encoder.toString());
}
} etcdRequest.setHttpRequest(httpRequest);
ChannelFuture future = channel.write(httpRequest);
if (bodyRequestEncoder != null && bodyRequestEncoder.isChunked()) {
future = channel.write(bodyRequestEncoder);
}
channel.flush();
return future;
} /**
* Close netty
*/
@Override
public void close() {
logger.info("Shutting down Etcd4j Netty client"); if (config.isManagedEventLoopGroup()) {
logger.debug("Shutting down Netty Loop");
eventLoopGroup.shutdownGracefully();
}
} private InetSocketAddress connectAddress(URI uri) {
return InetSocketAddress.createUnresolved(uri.getHost(), uri.getPort() == -1 ? DEFAULT_PORT : uri.getPort());
} private class HttpBasicAuthHandler extends ChannelOutboundHandlerAdapter {
@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
if (securityContext.hasCredentials() && msg instanceof HttpRequest) {
addBasicAuthHeader((HttpRequest)msg);
} ctx.write(msg, promise);
} private void addBasicAuthHeader(HttpRequest request) {
final String auth = Base64.encode(
Unpooled.copiedBuffer(
securityContext.username() + ":" + securityContext.password(),
CharsetUtil.UTF_8)
).toString(CharsetUtil.UTF_8); request.headers().add(HttpHeaderNames.AUTHORIZATION, "Basic " + auth);
}
}
}

  进入EtcdNettyClient(final EtcdSecurityContext securityContext, final URI... uri),再进入public EtcdNettyClient(final EtcdNettyConfig config,
                         final EtcdSecurityContext securityContext, final URI... uris),我们发现etcd集成了netty框架。

etcd使用经历的更多相关文章

  1. Etcd学习(一)安装和.NETclient測试

    Etcd是一个比較新的分布式协调框架,由CoreOS的开发团队开发,如今才仅仅到0.4.6版本号,还没公布1.0版本号 我看了一下GitHub上作者们的提交记录,如今应该还在如火如荼的开发以及改动Bu ...

  2. Etcd安全配置之Basic Auth认证

    <中小团队落地配置中心详解>文章中我们介绍了如何基于Etcd+Confd构建配置中心,最后提到Etcd的安全问题时说了可以使用账号密码认证以达到安全访问的目的,究竟该如何开启认证以及怎么设 ...

  3. 开发的服务集群部署方案,以etcd为基础(java)

    当前有很多服务集群部署,但是对于我们自己开发的服务系统怎么样能够解决部署问题,对大家很麻烦和笨重. 首先,我想说对于我们国内,小公司小系统比较多.大型系统毕竟少数,向阿里云看齐的不多.其实所谓的需要集 ...

  4. 从零开始入门 K8s | 手把手带你理解 etcd

    作者 | 曾凡松(逐灵) 阿里云容器平台高级技术专家 本文整理自<CNCF x Alibaba 云原生技术公开课>第 16 讲. 导读:etcd 是用于共享配置和服务发现的分布式.一致性的 ...

  5. [转帖]从零开始入门 K8s | 手把手带你理解 etcd

    从零开始入门 K8s | 手把手带你理解 etcd https://zhuanlan.zhihu.com/p/96721097 导读:etcd 是用于共享配置和服务发现的分布式.一致性的 KV 存储系 ...

  6. 探索etcd,Zookeeper和Consul一致键值数据存储的性能

    这篇博文是探索三个分布式.一致性键值数据存储软件性能的系列文章中的第一篇:etcd.Zookeeper和Consul,由etcd团队所写,可以让我们全面地了解如何评估三个分布式一致存储软件的性能.翻译 ...

  7. 第16 章 : 深入理解 etcd:基于原理解析

    深入理解 etcd:基于原理解析 本文将主要分享以下三方面的内容: 第一部分,会为大家介绍 etcd 项目发展的整个历程,从诞生至今 etcd 经历的那些重要的时刻: 第二部分,会为大家介绍 etcd ...

  8. 一篇文章带你搞懂 etcd 3.5 的核心特性

    作者 唐聪,腾讯云资深工程师,极客时间专栏<etcd实战课>作者,etcd活跃贡献者,主要负责腾讯云大规模k8s/etcd平台.有状态服务容器化.在离线混部等产品研发设计工作. etcd ...

  9. etcd学习(9)-etcd中的存储实现

    etcd中的存储实现 前言 V3和V2版本的对比 MVCC treeIndex 原理 MVCC 更新 key MVCC 查询 key MVCC 删除 key 压缩 周期性压缩 版本号压缩 boltdb ...

随机推荐

  1. USACO 简易题解(蒟蒻的题解)

    蒟蒻难得可以去比赛,GDOI也快到了,还是认真刷题(不会告诉你之前都在颓废),KPM 神犇既然都推荐刷USACO, 辣就刷刷. 现在蒟蒻还没刷完,太蒟刷得太慢,so 写了的搞个简易题解(没代码,反正N ...

  2. ACM-ICPC北京站总结

    失踪人口回归.... 第一次ACM比赛还是比较紧张的,总体来说发挥还是有一点失常. day1热身赛 一共四道去年和前年的北京站的题目....似乎都是银牌题及以下难度.半个小时我们就完成了嘴巴AK,然而 ...

  3. HTTP Status 500 - com.opensymphony.xwork2.ActionSupport.toAddPage()

    使用struts2过程中碰到以下错误 HTTP Status 500 - com.opensymphony.xwork2.ActionSupport.toAddPage() type Exceptio ...

  4. Build_Release.bat

    Build_Release.bat @echo off pushd "%~dp0" set tag=Release set PATH="C:\Program Files ...

  5. N!含有多少个 2/5质因子

    编程之美127页,N!中含有质因数2的个数 = [N/2] + [N/4] + [N/8] + [N/16] + ..... 要理解上式,先看 编程之美126页,N!中含有质因数5的个数Z 举例:N ...

  6. Oracle DBLink连接数过多的问题(Ora-02020)

    前不久开发人员编译存储时报ORA -02020 错,如下是解决方案步骤.   报错全信息: Error:OR A -04052在查: 找远程对象 NIP.PB_PERADDRESSLIST@DB_NI ...

  7. 【scala】异常处理

    Scala 的异常处理和其它语言比如 Java 类似. 抛出异常 Scala 抛出异常的方法和 Java一样,使用 throw 方法 throw new IllegalArgumentExceptio ...

  8. jQ通过cookie记住用户名

    总结要点: 一.封装的一个cookie函数: 二.获取input的value值:$("input[name=user]").val() 三.设置input的value值:$(&qu ...

  9. 【2018年全国多校算法寒假训练营练习比赛(第五场)-E】情人节的电灯泡(二维树状数组单点更新+区间查询)

    试题链接:https://www.nowcoder.com/acm/contest/77/E 题目描述 情人节到了,小芳和小明手牵手,打算过一个完美的情人节,但是小刚偏偏也来了,当了一个明晃晃的电灯泡 ...

  10. Mac下webpack安装

    最近开始接触构建工具webpack,公司电脑是 windows,而我自己的呢是mac.本来以为在自己电脑安装很简单,但是出了点问题,所以写出来分享下. 这里用npm的方式安装,首先你要安装node.j ...