注册中心

在分布式架构中注册中心起到了管理各种服务功能包括服务的注册、发现、熔断、负载、降级等功能,在分布式架构中起到了不可替代的作用。常见的注册中心有eureka,zookeeper等等,在springcloud中,它封装了Netflix公司开发的Eureka模块来实现服务的注册与发现,简单的来说注册中心里会存放着我们的ip、端口、业务,如果是只是存储我们可以想到很多,数据库,文件,内存,redis都是可以的存的。那么今天小编这里就把redis当成注册中心来实现。

配置与分析

在springcloud中我们创建一个业务模块去找注册中心时,一般会@EnableDiscoveryClient、@EnableEurekaClient、@EnableEurekaServer,不过@EnableEurekaClient和@EnableEurekaServer都是Eureka给我提供的。这里我们就要SpringCloud给我们提供的@EnableDiscoveryClient,点击进入@EnableDiscoveryClient源码中。

/*
* Copyright 2013-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/ package org.springframework.cloud.client.discovery; import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; import org.springframework.context.annotation.Import; /**
* Annotation to enable a DiscoveryClient implementation.
* @author Spencer Gibb
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(EnableDiscoveryClientImportSelector.class)
public @interface EnableDiscoveryClient { /**
* If true, the ServiceRegistry will automatically register the local server.
*/
boolean autoRegister() default true;
}

我们可以看到autoRegister()方法,我们可以看上面的注释,如果为true,将把本地服务注册到ServiceRegistry中。ServiceRegistry可以理解为将服务注册到注册中心的一个通道,那么我们想把服务注册到注册中心你个方法就必须返回true。

可以看到注解上的注解@Import(EnableDiscoveryClientImportSelector.class)

这个注解引入了这个类,那我们再进入这个类

/*
* Copyright 2013-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/ package org.springframework.cloud.client.discovery; import org.springframework.boot.bind.RelaxedPropertyResolver;
import org.springframework.cloud.commons.util.SpringFactoryImportSelector;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.annotation.Order;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.Environment;
import org.springframework.core.env.MapPropertySource;
import org.springframework.core.type.AnnotationMetadata; import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List; /**
* @author Spencer Gibb
*/
@Order(Ordered.LOWEST_PRECEDENCE - 100)
public class EnableDiscoveryClientImportSelector
extends SpringFactoryImportSelector<EnableDiscoveryClient> { @Override
public String[] selectImports(AnnotationMetadata metadata) {
String[] imports = super.selectImports(metadata); AnnotationAttributes attributes = AnnotationAttributes.fromMap(
metadata.getAnnotationAttributes(getAnnotationClass().getName(), true)); boolean autoRegister = attributes.getBoolean("autoRegister"); if (autoRegister) {
List<String> importsList = new ArrayList<>(Arrays.asList(imports));
importsList.add("org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationConfiguration");
imports = importsList.toArray(new String[0]);
} else {
Environment env = getEnvironment();
if(ConfigurableEnvironment.class.isInstance(env)) {
ConfigurableEnvironment configEnv = (ConfigurableEnvironment)env;
LinkedHashMap<String, Object> map = new LinkedHashMap<>();
map.put("spring.cloud.service-registry.auto-registration.enabled", false);
MapPropertySource propertySource = new MapPropertySource(
"springCloudDiscoveryClient", map);
configEnv.getPropertySources().addLast(propertySource);
} } return imports;
} @Override
protected boolean isEnabled() {
return new RelaxedPropertyResolver(getEnvironment()).getProperty(
"spring.cloud.discovery.enabled", Boolean.class, Boolean.TRUE);
} @Override
protected boolean hasDefaultFactory() {
return true;
} }

我们可以看到selectImports方法里的判断,autoRegister的判断,当我autoRegister()为true时才会执行List的添加,添加的内容我们可以发现Lis添加的内容,只不过是个配置类,不过我们顺藤摸瓜就可以找到serviceregistry的包,我们可以发现包下有许多包是做配置的,那么我们找到最核心的类AbstractAutoServiceRegistration类找到他的父类AbstractDiscoveryLifecycle,在这个类中我们找到抽象register()方法,看一下上面的注释我们就可以发现这个才是用来注册的方法,接下我们找到重写的start()方法可以看到register()方法在这里被调用。

AbstractAutoServiceRegistration源码

package org.springframework.cloud.client.serviceregistry;

import org.springframework.cloud.client.discovery.AbstractDiscoveryLifecycle;

/**
* Lifecycle methods that may be useful and common to {@link ServiceRegistry} implementations.
*
* TODO: document the lifecycle
*
* @param <R> registration type passed to the {@link ServiceRegistry}.
*
* @author Spencer Gibb
*/
@SuppressWarnings("deprecation")
public abstract class AbstractAutoServiceRegistration<R extends Registration> extends AbstractDiscoveryLifecycle implements AutoServiceRegistration { private final ServiceRegistry<R> serviceRegistry;
private AutoServiceRegistrationProperties properties; @Deprecated
protected AbstractAutoServiceRegistration(ServiceRegistry<R> serviceRegistry) {
this.serviceRegistry = serviceRegistry;
} protected AbstractAutoServiceRegistration(ServiceRegistry<R> serviceRegistry, AutoServiceRegistrationProperties properties) {
this.serviceRegistry = serviceRegistry;
this.properties = properties;
} protected ServiceRegistry<R> getServiceRegistry() {
return this.serviceRegistry;
} protected abstract R getRegistration(); protected abstract R getManagementRegistration(); /**
* Register the local service with the {@link ServiceRegistry}
*/
@Override
protected void register() {
this.serviceRegistry.register(getRegistration());
} /**
* Register the local management service with the {@link ServiceRegistry}
*/
@Override
protected void registerManagement() {
R registration = getManagementRegistration();
if (registration != null) {
this.serviceRegistry.register(registration);
}
} /**
* De-register the local service with the {@link ServiceRegistry}
*/
@Override
protected void deregister() {
this.serviceRegistry.deregister(getRegistration());
} /**
* De-register the local management service with the {@link ServiceRegistry}
*/
@Override
protected void deregisterManagement() {
R registration = getManagementRegistration();
if (registration != null) {
this.serviceRegistry.deregister(registration);
}
} @Override
public void stop() {
if (this.getRunning().compareAndSet(true, false) && isEnabled()) {
deregister();
if (shouldRegisterManagement()) {
deregisterManagement();
}
this.serviceRegistry.close();
}
} @Override
protected boolean shouldRegisterManagement() {
if (this.properties == null || this.properties.isRegisterManagement()) {
return super.shouldRegisterManagement();
}
return false;
}
}

AbstractDiscoveryLifecycle源码

/*
* Copyright 2013-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/ package org.springframework.cloud.client.discovery; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger; import javax.annotation.PreDestroy; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeansException;
import org.springframework.boot.context.embedded.EmbeddedServletContainerInitializedEvent;
import org.springframework.cloud.client.discovery.event.InstanceRegisteredEvent;
import org.springframework.cloud.client.serviceregistry.ServiceRegistry;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationListener;
import org.springframework.core.env.Environment; /**
* Lifecycle methods that may be useful and common to various DiscoveryClient implementations.
*
* @deprecated use {@link org.springframework.cloud.client.serviceregistry.AbstractAutoServiceRegistration} instead. This class will be removed in the next release train.
*
* @author Spencer Gibb
*/
@Deprecated
public abstract class AbstractDiscoveryLifecycle implements DiscoveryLifecycle,
ApplicationContextAware, ApplicationListener<EmbeddedServletContainerInitializedEvent> { private static final Log logger = LogFactory.getLog(AbstractDiscoveryLifecycle.class); private boolean autoStartup = true; private AtomicBoolean running = new AtomicBoolean(false); private int order = 0; private ApplicationContext context; private Environment environment; private AtomicInteger port = new AtomicInteger(0); protected ApplicationContext getContext() {
return context;
} @Override
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
this.context = applicationContext;
this.environment = this.context.getEnvironment();
} @Deprecated
protected Environment getEnvironment() {
return environment;
} @Deprecated
protected AtomicInteger getPort() {
return port;
} @Override
public boolean isAutoStartup() {
return this.autoStartup;
} @Override
public void stop(Runnable callback) {
try {
stop();
} catch (Exception e) {
logger.error("A problem occurred attempting to stop discovery lifecycle", e);
}
callback.run();
} @Override
public void start() {
if (!isEnabled()) {
if (logger.isDebugEnabled()) {
logger.debug("Discovery Lifecycle disabled. Not starting");
}
return;
} // only set the port if the nonSecurePort is 0 and this.port != 0
if (this.port.get() != 0 && getConfiguredPort() == 0) {
setConfiguredPort(this.port.get());
}
// only initialize if nonSecurePort is greater than 0 and it isn't already running
// because of containerPortInitializer below
if (!this.running.get() && getConfiguredPort() > 0) {
register();
if (shouldRegisterManagement()) {
registerManagement();
}
this.context.publishEvent(new InstanceRegisteredEvent<>(this,
getConfiguration()));
this.running.compareAndSet(false, true);
}
} @Deprecated
protected abstract int getConfiguredPort();
@Deprecated
protected abstract void setConfiguredPort(int port); /**
* @return if the management service should be registered with the {@link ServiceRegistry}
*/
protected boolean shouldRegisterManagement() {
return getManagementPort() != null && ManagementServerPortUtils.isDifferent(this.context);
} /**
* @return the object used to configure the registration
*/
@Deprecated
protected abstract Object getConfiguration(); /**
* Register the local service with the DiscoveryClient
*/
protected abstract void register(); /**
* Register the local management service with the DiscoveryClient
*/
protected void registerManagement() {
} /**
* De-register the local service with the DiscoveryClient
*/
protected abstract void deregister(); /**
* De-register the local management service with the DiscoveryClient
*/
protected void deregisterManagement() {
} /**
* @return true, if the {@link DiscoveryLifecycle} is enabled
*/
protected abstract boolean isEnabled(); /**
* @return the serviceId of the Management Service
*/
@Deprecated
protected String getManagementServiceId() {
// TODO: configurable management suffix
return this.context.getId() + ":management";
} /**
* @return the service name of the Management Service
*/
@Deprecated
protected String getManagementServiceName() {
// TODO: configurable management suffix
return getAppName() + ":management";
} /**
* @return the management server port
*/
@Deprecated
protected Integer getManagementPort() {
return ManagementServerPortUtils.getPort(this.context);
} /**
* @return the app name, currently the spring.application.name property
*/
@Deprecated
protected String getAppName() {
return this.environment.getProperty("spring.application.name", "application");
} @Override
public void stop() {
if (this.running.compareAndSet(true, false) && isEnabled()) {
deregister();
if (shouldRegisterManagement()) {
deregisterManagement();
}
}
} @PreDestroy
public void destroy() {
stop();
} @Override
public boolean isRunning() {
return this.running.get();
} protected AtomicBoolean getRunning() {
return running;
} @Override
public int getOrder() {
return this.order;
} @Override
public int getPhase() {
return 0;
} @Override
@Deprecated
public void onApplicationEvent(EmbeddedServletContainerInitializedEvent event) {
// TODO: take SSL into account
// Don't register the management port as THE port
if (!"management".equals(event.getApplicationContext().getNamespace())) {
this.port.compareAndSet(0, event.getEmbeddedServletContainer().getPort());
this.start();
}
}
}

回到AbstractAutoServiceRegistration,我们可以参考AbstractAutoServiceRegistration中重新的regiter()方法调用了serviceRegistry的register()方法,那么我们进入register()方法参考一下注释我们可以发现传入的参数类,主要传入我们的ip 端口 业务等等,那么方法的参数是它的一个方法,那么我们重写他AbstractAutoServiceRegistration类看一下这个方法我们需要返回一个什么类进去。

package com.rk.ytl.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.client.serviceregistry.AbstractAutoServiceRegistration;
import org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationProperties;
import org.springframework.cloud.client.serviceregistry.Registration;
import org.springframework.cloud.client.serviceregistry.ServiceRegistry;
import org.springframework.context.annotation.Configuration; /**
* @author 杨天乐
* @date 2018/4/20 16:26
*/
@Configuration
public class RedisAutoServiceRegistration extends AbstractAutoServiceRegistration { @Autowired
private MyServiceInstance myServiceInstance; @Autowired
private AutoServiceRegistrationProperties autoServiceRegistrationProperties; @Value("${server.port}")
private Integer port; protected RedisAutoServiceRegistration(ServiceRegistry serviceRegistry) {
super(serviceRegistry);
} @Override
protected Registration getRegistration() {
return myServiceInstance;
} @Override
protected Registration getManagementRegistration() {
return null;
} @Override
protected int getConfiguredPort() {
return port;
} @Override
protected void setConfiguredPort(int port) { } @Override
protected Object getConfiguration() {
return null;
} @Override
protected boolean isEnabled() {
return autoServiceRegistrationProperties.isEnabled();
}
}

这个是我写好的,我们重写完之后我们可以看到返回了一个Registration对象,那么可以根据源码找到Registration对象继承了ServiceInstance,我们看一下源码

/*
* Copyright 2013-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/ package org.springframework.cloud.client; import java.net.URI;
import java.util.Map; /**
* Represents an instance of a Service in a Discovery System
* @author Spencer Gibb
*/
public interface ServiceInstance { /**
* @return the service id as registered.
*/
String getServiceId(); /**
* @return the hostname of the registered ServiceInstance
*/
String getHost(); /**
* @return the port of the registered ServiceInstance
*/
int getPort(); /**
* @return if the port of the registered ServiceInstance is https or not
*/
boolean isSecure(); /**
* @return the service uri address
*/
URI getUri(); /**
* @return the key value pair metadata associated with the service instance
*/
Map<String, String> getMetadata();
}

(注意:这里一定要把isEnable()方法设置为true,不然是不可以的,至于为什么,大家可以参考AbstractDiscoveryLifecycle的start()方法如果不为true是执行不了register()方法的)

端口,ip,业务都在这里,那么我们就是找他了,再写一个类去实现它。

package com.rk.ytl.config;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.client.serviceregistry.Registration;
import org.springframework.stereotype.Component; import java.net.InetAddress;
import java.net.URI;
import java.net.UnknownHostException;
import java.util.Map; /**
* @author 杨天乐
* @date 2018/4/20 16:29
*/
@Component
public class MyServiceInstance implements Registration { @Value("${spring.application.name}")
private String applicationName; @Value("${server.port}")
private Integer port; @Override
public String getServiceId() {
return applicationName;
} @Override
public String getHost() {
String host = "";
try {
InetAddress inetAddress = InetAddress.getLocalHost();
host =inetAddress.getHostAddress();
} catch (UnknownHostException e) {
e.printStackTrace();
}
return host;
} @Override
public int getPort() {
return port;
} @Override
public boolean isSecure() {
return false;
} @Override
public URI getUri() {
return null;
} @Override
public Map<String, String> getMetadata() {
return null;
}
}

然后把他注入进来就可以了。参数完成了那么我们回头看是哪个类的register()方法是serviceRegistry类的,那么上面说到了serviceRegistry类是一个把服务中到服务中心的通道,那么我们就可以自己实现通过

serviceRegistry通向redis的通道。

package com.rk.ytl.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.serviceregistry.Registration;
import org.springframework.cloud.client.serviceregistry.ServiceRegistry;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component; /**
* @author 杨天乐
* @date 2018/4/20 17:02
*/
@Component
public class MyServiceRegistry implements ServiceRegistry { @Autowired
private StringRedisTemplate stringRedisTemplate; @Override
public void register(Registration registration) {
ValueOperations<String, String> redis = stringRedisTemplate.opsForValue();
String hostname= registration.getHost()+":"+registration.getPort();
String serviceId = registration.getServiceId();
redis.set(serviceId,hostname);
} @Override
public void deregister(Registration registration) {
stringRedisTemplate.delete(registration.getServiceId());
} @Override
public void close() { } @Override
public void setStatus(Registration registration, String status) { } @Override
public Object getStatus(Registration registration) {
return null;
}
}

接下来配置一个启动类就可以了

package com.rk.ytl;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient; /**
* @author 杨天乐
* @date 2018/4/20 16:12
*/
@SpringBootApplication
@EnableDiscoveryClient
public class RedisRegisterApplication { public static void main(String[] args) {
SpringApplication.run(RedisRegisterApplication.class,args);
}
}

yml配置:

spring:
redis:
host: redis地址
application:
name: redis-register-center
server:
port: 9090

运行后我们在redis里就可以看见我们的本机的ip端口

springcloud源码分析(一)之采用redis实现注册中心的更多相关文章

  1. [源码分析] OpenTracing之跟踪Redis

    [源码分析] OpenTracing之跟踪Redis 目录 [源码分析] OpenTracing之跟踪Redis 0x00 摘要 0x01 总体逻辑 1.1 相关概念 1.2 埋点插件 1.3 总体逻 ...

  2. 插件开发之360 DroidPlugin源码分析(五)Service预注册占坑

    请尊重分享成果,转载请注明出处: http://blog.csdn.net/hejjunlin/article/details/52264977 在了解系统的activity,service,broa ...

  3. 插件开发之360 DroidPlugin源码分析(四)Activity预注册占坑

    请尊重分享成果,转载请注明出处: http://blog.csdn.net/hejjunlin/article/details/52258434 在了解系统的activity,service,broa ...

  4. Dubbo(三):深入理解Dubbo源码之如何将服务发布到注册中心

    一.前言 前面有说到Dubbo的服务发现机制,也就是SPI,那既然Dubbo内部实现了更加强大的服务发现机制,现在我们就来一起看看Dubbo在发现服务后需要做什么才能将服务注册到注册中心中. 二.Du ...

  5. Spring源码分析(六)解析和注册BeanDefinitions

    摘要:本文结合<Spring源码深度解析>来分析Spring 5.0.6版本的源代码.若有描述错误之处,欢迎指正. 当把文件转换为Document后,接下来的提取及注册bean就是我们的重 ...

  6. motan源码分析一:服务发布及注册

    motan是新浪微博开源的服务治理框架,具体介绍请看:http://tech.sina.com.cn/i/2016-05-10/doc-ifxryhhh1869879.shtml. 本系列的文章将分析 ...

  7. Redis 内存管理 源码分析

    要想了解redis底层的内存管理是如何进行的,直接看源码绝对是一个很好的选择 下面是我添加了详细注释的源码,需要注意的是,为了便于源码分析,我把redis为了弥补平台差异的那部分代码删了,只需要知道有 ...

  8. Redis 专栏(使用介绍、源码分析、常见问题...)

    一.介绍相关 说Redis : 介绍Redis特性,使用场景,使用Jedis操作Redis等. 二.源码分析 1. 数据结构 Redis源码分析(sds):Redis自己封装的C语言字符串类型. Re ...

  9. Fabric2.2中的Raft共识模块源码分析

    引言 Hyperledger Fabric是当前比较流行的一种联盟链系统,它隶属于Linux基金会在2015年创建的超级账本项目且是这个项目最重要的一个子项目.目前,与Hyperledger的另外几个 ...

随机推荐

  1. 第05组 Alpha冲刺(3/4)

    第05组 Alpha冲刺(3/4) 队名:天码行空 组长博客连接 作业博客连接 团队燃尽图(共享): GitHub当日代码/文档签入记录展示(共享): 组员情况: 组员1:卢欢(组长) 过去两天完成了 ...

  2. MySQL select from where multiple conditions

    Maybe one of the most used MySQL commands is SELECT, that is the way to stract the information from ...

  3. java(三)基础类型之间的转换

    自动类型转换:容量小的类型自动转换成为容量大的数据类型,数据类型按容量大小排序为: 有多种类型的数据混合运算时,系统首先自动将所有数据转换成容量最大的那种数据类型,然后在进行运算: byte.shor ...

  4. 当接口请求体里的日期格式跟web页面日期格式不一致时,该如何处理呢?

    首先引入Unix纪元时间戳的概念:即格林威治时间(GMT,Greenwich Mean Time)1970年1月1日00:00:00,到当前时间的秒数.单位为秒(s). 那么当前时间的Unix纪元时间 ...

  5. ASP.NET Core Identity 的示例

    1. appsettings.json { "ConnectionStrings": { "DefaultConnection": "Server=( ...

  6. Oracle:Redhat 7.4+Oracle Rac 11.2.0.4 执行root.sh报错处理

    一.报错信息 二.原因分析 因为RHEL 7使用systemd而不是initd运行进程和重启进程,而root.sh通过传统的initd运行ohasd进程 三.解决办法 在RHEL 7中ohasd需要被 ...

  7. PHP mysqli_kill MySQLi 函数

    mysqli_kill - 让服务器杀掉一个 MySQL 线程 语法:mysqli_kill ( mysqli $link , int $processid ) 本函数可以用来让服务器杀掉 proce ...

  8. Xamarin.Forms iOS 真机测试 打包

    等着打包过程中记录一下如何打一个debug包到真机上测试的流程1. 需要在XCode中创建一个新的项目,选择iOS==>Single View App,点击Next 2. 在新的弹框中需要App ...

  9. MSSQL添加外键

    alter table 需要建立外键的表 with check/nocheck add constraint 外键名字 foreign key (需要建立外键的字段名) references 外键表( ...

  10. python测试mysql写入性能完整实例

    这篇文章主要介绍了python测试mysql写入性能完整实例,具有一定借鉴价值,需要的朋友可以参考下 本文主要研究的是python测试mysql写入性能,分享了一则完整代码,具体介绍如下. 测试环境: ...