热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

etcd使用经历

etcd是一个开源的、分布式的键值对数据存储系统,提供共享配置、服务的注册和发现。etcd与zookeeper相比算是轻量级系统,两者的一致性协议也一样

  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+.*/@Deprecatedpublic 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);}@Overridepublic 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 &#61; LoggerFactory.getLogger(EtcdNettyClient.class);// default etcd portprivate static final int DEFAULT_PORT &#61; 2379;private static final String ENV_ETCD4J_ENDPOINT &#61; "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** &#64;param sslContext SSL context if connecting with SSL. Null if not connecting with SSL.* &#64;param uri to connect to*/public EtcdNettyClient(final SslContext sslContext, final URI... uri) {this(new EtcdNettyConfig(), sslContext, uri);}/*** Constructor** &#64;param securityContext security context.* &#64;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** &#64;param config for netty* &#64;param sslContext SSL context if connecting with SSL. Null if not connecting with SSL.* &#64;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** &#64;param config for netty* &#64;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** &#64;param config for netty* &#64;param securityContext security context (ssl, authentication)* &#64;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 &#61; 0;this.config &#61; config.clone();this.securityContext &#61; securityContext.clone();this.uris &#61; uris;this.eventLoopGroup &#61; config.getEventLoopGroup();this.bootstrap &#61; 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() {&#64;Overridepublic void initChannel(SocketChannel ch) throws Exception {ChannelPipeline p &#61; 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** &#64;return the current bootstrap*/protected Bootstrap getBootstrap() {return bootstrap;}/*** Send a request and get a future.** &#64;param etcdRequest Etcd Request to send* &#64;return Promise for the request.*/public EtcdResponsePromise send(final EtcdRequest etcdRequest) throws IOException {ConnectionState connectionState &#61; new ConnectionState(uris, lastWorkingUriIndex);if (etcdRequest.getPromise() &#61;&#61; null) {etcdRequest.setPromise(new EtcdResponsePromise(etcdRequest.getRetryPolicy(),connectionState,new RetryHandler() {&#64;Overridepublic void doRetry(ConnectionState connectionState) throws IOException {connect(etcdRequest, connectionState);}}));}connect(etcdRequest, connectionState);return etcdRequest.getPromise();}/*** Connect to server** &#64;param etcdRequest to request with* &#64;param Type of response* &#64;throws IOException if request could not be sent.*/&#64;SuppressWarnings("unchecked")protected void connect(final EtcdRequest etcdRequest) throws IOException {this.connect(etcdRequest, etcdRequest.getPromise().getConnectionState());}/*** Connect to server** &#64;param etcdRequest to request with* &#64;param connectionState for retries* &#64;param Type of response* &#64;throws IOException if request could not be sent.*/&#64;SuppressWarnings("unchecked")protected void connect(final EtcdRequest 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 &#61; URI.create(etcdRequest.getUrl());if (requestUri.getHost() !&#61; null && requestUri.getPort() > -1) {uri &#61; requestUri;} else if (connectionState.uris.length &#61;&#61; 0 && System.getenv(ENV_ETCD4J_ENDPOINT) !&#61; null) {// read uri from environment variableString endpoint_uri &#61; System.getenv(ENV_ETCD4J_ENDPOINT);if(logger.isDebugEnabled()) {logger.debug("Will use environment variable {} as uri with value {}", ENV_ETCD4J_ENDPOINT, endpoint_uri);}uri &#61; URI.create(endpoint_uri);} else {uri &#61; connectionState.uris[connectionState.uriIndex];}// Start the connection attempt.final ChannelFuture connectFuture &#61; bootstrap.connect(connectAddress(uri));etcdRequest.getPromise().getConnectionState().loop &#61; connectFuture.channel().eventLoop();etcdRequest.getPromise().attachNettyPromise(connectFuture.channel().eventLoop().newPromise());connectFuture.addListener(new GenericFutureListener() {&#64;Overridepublic void operationComplete(final ChannelFuture f) throws Exception {if (!f.isSuccess()) {final Throwable cause &#61; 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 promisesif (etcdRequest.getPromise().getNettyPromise().isCancelled()) {f.channel().close();etcdRequest.getPromise().getNettyPromise().setFailure(new CancellationException());return;}final Promise listenedToPromise &#61; etcdRequest.getPromise().getNettyPromise();// Close channel when promise is satisfied or cancelled laterlistenedToPromise.addListener(new GenericFutureListener>() {&#64;Overridepublic void operationComplete(Future future) throws Exception {// Only close if it was not redirected to new promiseif (etcdRequest.getPromise().getNettyPromise() &#61;&#61; listenedToPromise) {f.channel().close();}}});if (logger.isDebugEnabled()) {logger.debug("Connected to {} ({})", f.channel().remoteAddress().toString(), connectionState.uriIndex);}lastWorkingUriIndex &#61; connectionState.uriIndex;modifyPipeLine(etcdRequest, f.channel().pipeline());createAndSendHttpRequest(uri, etcdRequest.getUrl(), etcdRequest, f.channel()).addListener(new ChannelFutureListener() {&#64;Overridepublic 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() {&#64;Overridepublic 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** &#64;param req to process* &#64;param pipeline to modify* &#64;param Type of Response*/private void modifyPipeLine(final EtcdRequest req, final ChannelPipeline pipeline) {final EtcdResponseHandler handler &#61; new EtcdResponseHandler<>(this, req);if (req.hasTimeout()) {pipeline.addFirst(new ReadTimeoutHandler(req.getTimeout(), req.getTimeoutUnit()));}pipeline.addLast(handler);pipeline.addLast(new ChannelHandlerAdapter() {&#64;Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {handler.retried(true);req.getPromise().handleRetry(cause);}});}/*** Get HttpRequest belonging to etcdRequest** &#64;param server server for http request* &#64;param uri to send request to* &#64;param etcdRequest to send* &#64;param channel to send request on* &#64;param Response type* &#64;return HttpRequest* &#64;throws Exception when creating or sending HTTP request fails*/private ChannelFuture createAndSendHttpRequest(URI server, String uri, EtcdRequest etcdRequest, Channel channel) throws Exception {HttpRequest httpRequest &#61; 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() &#43; ":" &#43; server.getPort());} else {httpRequest.headers().add(HttpHeaderNames.HOST, this.config.getHostName());}HttpPostRequestEncoder bodyRequestEncoder &#61; null;Map keyValuePairs &#61; etcdRequest.getRequestParams();if (keyValuePairs !&#61; null && !keyValuePairs.isEmpty()) {HttpMethod etcdRequestMethod &#61; etcdRequest.getMethod();if (etcdRequestMethod &#61;&#61; HttpMethod.POST || etcdRequestMethod &#61;&#61; HttpMethod.PUT) {bodyRequestEncoder &#61; new HttpPostRequestEncoder(httpRequest, false);for (Map.Entry entry : keyValuePairs.entrySet()) {bodyRequestEncoder.addBodyAttribute(entry.getKey(), entry.getValue());}httpRequest &#61; bodyRequestEncoder.finalizeRequest();} else {QueryStringEncoder encoder &#61; new QueryStringEncoder(uri);for (Map.Entry entry : keyValuePairs.entrySet()) {encoder.addParam(entry.getKey() , entry.getValue());}httpRequest.setUri(encoder.toString());}}etcdRequest.setHttpRequest(httpRequest);ChannelFuture future &#61; channel.write(httpRequest);if (bodyRequestEncoder !&#61; null && bodyRequestEncoder.isChunked()) {future &#61; channel.write(bodyRequestEncoder);}channel.flush();return future;}/*** Close netty*/&#64;Overridepublic 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() &#61;&#61; -1 ? DEFAULT_PORT : uri.getPort());}private class HttpBasicAuthHandler extends ChannelOutboundHandlerAdapter {&#64;Overridepublic 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 &#61; Base64.encode(Unpooled.copiedBuffer(securityContext.username() &#43; ":" &#43; securityContext.password(),CharsetUtil.UTF_8)).toString(CharsetUtil.UTF_8);request.headers().add(HttpHeaderNames.AUTHORIZATION, "Basic " &#43; auth);}}
}

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

 

转:https://www.cnblogs.com/wuxun1997/p/8137753.html



推荐阅读
  • 个人学习使用:谨慎参考1Client类importcom.thoughtworks.gauge.Step;importcom.thoughtworks.gauge.T ... [详细]
  • 本文介绍了Web学习历程记录中关于Tomcat的基本概念和配置。首先解释了Web静态Web资源和动态Web资源的概念,以及C/S架构和B/S架构的区别。然后介绍了常见的Web服务器,包括Weblogic、WebSphere和Tomcat。接着详细讲解了Tomcat的虚拟主机、web应用和虚拟路径映射的概念和配置过程。最后简要介绍了http协议的作用。本文内容详实,适合初学者了解Tomcat的基础知识。 ... [详细]
  • Java太阳系小游戏分析和源码详解
    本文介绍了一个基于Java的太阳系小游戏的分析和源码详解。通过对面向对象的知识的学习和实践,作者实现了太阳系各行星绕太阳转的效果。文章详细介绍了游戏的设计思路和源码结构,包括工具类、常量、图片加载、面板等。通过这个小游戏的制作,读者可以巩固和应用所学的知识,如类的继承、方法的重载与重写、多态和封装等。 ... [详细]
  • 网络请求模块选择——axios框架的基本使用和封装
    本文介绍了选择网络请求模块axios的原因,以及axios框架的基本使用和封装方法。包括发送并发请求的演示,全局配置的设置,创建axios实例的方法,拦截器的使用,以及如何封装和请求响应劫持等内容。 ... [详细]
  • 本文介绍了RxJava在Android开发中的广泛应用以及其在事件总线(Event Bus)实现中的使用方法。RxJava是一种基于观察者模式的异步java库,可以提高开发效率、降低维护成本。通过RxJava,开发者可以实现事件的异步处理和链式操作。对于已经具备RxJava基础的开发者来说,本文将详细介绍如何利用RxJava实现事件总线,并提供了使用建议。 ... [详细]
  • 本文介绍了JavaScript进化到TypeScript的历史和背景,解释了TypeScript相对于JavaScript的优势和特点。作者分享了自己对TypeScript的观察和认识,并提到了在项目开发中使用TypeScript的好处。最后,作者表示对TypeScript进行尝试和探索的态度。 ... [详细]
  • php7 curl_init(),php7.3curl_init获取301、302跳转后的数据
    最近在做一个蜘蛛项目,发现在抓取数据时,有时会碰到301的页面,原本写的curl_init函数php7-远程获取api接口或网页内容&#x ... [详细]
  • centos php部署到nginx 404_NodeJS项目部署到阿里云ECS服务器全程详解
    本文转载自:http:www.kovli.com20170919ecs-deploy作者:Kovli本文详细介绍如何部署NodeJS项目到阿里云ECS上, ... [详细]
  • 使用nodejs爬取b站番剧数据,计算最佳追番推荐
    本文介绍了如何使用nodejs爬取b站番剧数据,并通过计算得出最佳追番推荐。通过调用相关接口获取番剧数据和评分数据,以及使用相应的算法进行计算。该方法可以帮助用户找到适合自己的番剧进行观看。 ... [详细]
  • Linux重启网络命令实例及关机和重启示例教程
    本文介绍了Linux系统中重启网络命令的实例,以及使用不同方式关机和重启系统的示例教程。包括使用图形界面和控制台访问系统的方法,以及使用shutdown命令进行系统关机和重启的句法和用法。 ... [详细]
  • 开发笔记:加密&json&StringIO模块&BytesIO模块
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了加密&json&StringIO模块&BytesIO模块相关的知识,希望对你有一定的参考价值。一、加密加密 ... [详细]
  • 本文介绍了OC学习笔记中的@property和@synthesize,包括属性的定义和合成的使用方法。通过示例代码详细讲解了@property和@synthesize的作用和用法。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • 解决nginx启动报错epoll_wait() reported that client prematurely closed connection的方法
    本文介绍了解决nginx启动报错epoll_wait() reported that client prematurely closed connection的方法,包括检查location配置是否正确、pass_proxy是否需要加“/”等。同时,还介绍了修改nginx的error.log日志级别为debug,以便查看详细日志信息。 ... [详细]
  • Spring常用注解(绝对经典),全靠这份Java知识点PDF大全
    本文介绍了Spring常用注解和注入bean的注解,包括@Bean、@Autowired、@Inject等,同时提供了一个Java知识点PDF大全的资源链接。其中详细介绍了ColorFactoryBean的使用,以及@Autowired和@Inject的区别和用法。此外,还提到了@Required属性的配置和使用。 ... [详细]
author-avatar
201153蜡笔小新
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有