首先我们知道springcloud是一个微服务框架,按照官方文档的说法,springcloud提供了一些开箱即用的功能:

1 分布式/版本化配置

2 服务的注册与发现

3 路由

4 服务到服务之间调用

5 负载均衡

6 断路器

7 分布式消息

本文我们讲解的组件注册中心用的是阿里的nacos(关于nacos的安装可自行百度),网关用的就是springcloudgateway,负载均衡ribbon,断路器hystrix,客户端调用目标是openfeign

一 先看服务的注册

 

      服务的注册核心的类就是spring-cloud-commons下的两个类

org.springframework.cloud.client.serviceregistry.ServiceRegistry

org.springframework.cloud.client.serviceregistry.AbstractAutoServiceRegistration

ServiceRegistry是一个接口,接口中定义了如何向注册中心注册服务

package org.springframework.cloud.client.serviceregistry;

/**
* Contract to register and deregister instances with a Service Registry.
*
* @author Spencer Gibb
* @since 1.2.0
*/
public interface ServiceRegistry<R extends Registration> { /**
* Registers the registration. A registration typically has information about
* an instance, such as its hostname and port.
* @param registration The registration.
*/
void register(R registration); /**
* Deregisters the registration.
* @param registration
*/
void deregister(R registration); /**
* Closes the ServiceRegistry. This is a lifecycle method.
*/
void close(); /**
* Sets the status of the registration. The status values are determined
* by the individual implementations.
*
* @see org.springframework.cloud.client.serviceregistry.endpoint.ServiceRegistryEndpoint
* @param registration The registration to update.
* @param status The status to set.
*/
void setStatus(R registration, String status); /**
* Gets the status of a particular registration.
*
* @see org.springframework.cloud.client.serviceregistry.endpoint.ServiceRegistryEndpoint
* @param registration The registration to query.
* @param <T> The type of the status.
* @return The status of the registration.
*/
<T> T getStatus(R registration);
}

AbstractAutoServiceRegistration则是springcloud向注册中心注册服务的启动逻辑

package org.springframework.cloud.client.serviceregistry;

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.web.context.ConfigurableWebServerApplicationContext;
import org.springframework.boot.web.context.WebServerInitializedEvent;
import org.springframework.cloud.client.discovery.ManagementServerPortUtils;
import org.springframework.cloud.client.discovery.event.InstancePreRegisteredEvent;
import org.springframework.cloud.client.discovery.event.InstanceRegisteredEvent;
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 {@link ServiceRegistry}
* implementations.
*
* TODO: Document the lifecycle.
*
* @param <R> Registration type passed to the {@link ServiceRegistry}.
*
* @author Spencer Gibb
*/
public abstract class AbstractAutoServiceRegistration<R extends Registration>
implements AutoServiceRegistration, ApplicationContextAware, ApplicationListener<WebServerInitializedEvent> {
private static final Log logger = LogFactory
.getLog(AbstractAutoServiceRegistration.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); 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 ApplicationContext getContext() {
return context;
} @Override
@SuppressWarnings("deprecation")
public void onApplicationEvent(WebServerInitializedEvent event) {
bind(event);
} @Deprecated
public void bind(WebServerInitializedEvent event) {
ApplicationContext context = event.getApplicationContext();
if (context instanceof ConfigurableWebServerApplicationContext) {
if ("management".equals(
((ConfigurableWebServerApplicationContext) context).getServerNamespace())) {
return;
}
}
this.port.compareAndSet(0, event.getWebServer().getPort());
this.start();
} @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;
} public boolean isAutoStartup() {
return this.autoStartup;
} public void start() {
if (!isEnabled()) {
if (logger.isDebugEnabled()) {
logger.debug("Discovery Lifecycle disabled. Not starting");
}
return;
} // only initialize if nonSecurePort is greater than 0 and it isn't already running
// because of containerPortInitializer below
if (!this.running.get()) {
this.context.publishEvent(new InstancePreRegisteredEvent(this, getRegistration()));
register();
if (shouldRegisterManagement()) {
registerManagement();
}
this.context.publishEvent(
new InstanceRegisteredEvent<>(this, getConfiguration()));
this.running.compareAndSet(false, true);
} } /**
* @return Whether the management service should be registered with the
* {@link ServiceRegistry}.
*/
protected boolean shouldRegisterManagement() {
if (this.properties == null || this.properties.isRegisterManagement()) {
return getManagementPort() != null
&& ManagementServerPortUtils.isDifferent(this.context);
}
return false;
} /**
* @return The object used to configure the registration.
*/
@Deprecated
protected abstract Object getConfiguration(); /**
* @return True, if this 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");
} @PreDestroy
public void destroy() {
stop();
} public boolean isRunning() {
return this.running.get();
} protected AtomicBoolean getRunning() {
return running;
} public int getOrder() {
return this.order;
} public int getPhase() {
return 0;
} protected ServiceRegistry<R> getServiceRegistry() {
return this.serviceRegistry;
} protected abstract R getRegistration(); protected abstract R getManagementRegistration(); /**
* Register the local service with the {@link ServiceRegistry}.
*/
protected void register() {
this.serviceRegistry.register(getRegistration());
} /**
* Register the local management service with the {@link ServiceRegistry}.
*/
protected void registerManagement() {
R registration = getManagementRegistration();
if (registration != null) {
this.serviceRegistry.register(registration);
}
} /**
* De-register the local service with the {@link ServiceRegistry}.
*/
protected void deregister() {
this.serviceRegistry.deregister(getRegistration());
} /**
* De-register the local management service with the {@link ServiceRegistry}.
*/
protected void deregisterManagement() {
R registration = getManagementRegistration();
if (registration != null) {
this.serviceRegistry.deregister(registration);
}
} public void stop() {
if (this.getRunning().compareAndSet(true, false) && isEnabled()) {
deregister();
if (shouldRegisterManagement()) {
deregisterManagement();
}
this.serviceRegistry.close();
}
}
}

从代码可以看出该类实现了ApplicationListener来监听WebServerInitializedEvent,最终调用register方法来注册服务,register方法就是通过ServiceRegistry接口的实现,比如阿里nacos的实现类com.alibaba.cloud.nacos.registry.NacosServiceRegistry

/*
* Copyright (C) 2018 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 com.alibaba.cloud.nacos.registry; import java.util.List; import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.client.serviceregistry.Registration;
import org.springframework.cloud.client.serviceregistry.ServiceRegistry;
import org.springframework.util.StringUtils; import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
import com.alibaba.nacos.api.naming.NamingService;
import com.alibaba.nacos.api.naming.pojo.Instance; /**
* @author xiaojing
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
public class NacosServiceRegistry implements ServiceRegistry<Registration> { private static final Logger log = LoggerFactory.getLogger(NacosServiceRegistry.class); private final NacosDiscoveryProperties nacosDiscoveryProperties; private final NamingService namingService; public NacosServiceRegistry(NacosDiscoveryProperties nacosDiscoveryProperties) {
this.nacosDiscoveryProperties = nacosDiscoveryProperties;
this.namingService = nacosDiscoveryProperties.namingServiceInstance();
} @Override
public void register(Registration registration) { if (StringUtils.isEmpty(registration.getServiceId())) {
log.warn("No service to register for nacos client...");
return;
} String serviceId = registration.getServiceId(); Instance instance = getNacosInstanceFromRegistration(registration); try {
namingService.registerInstance(serviceId, instance);
log.info("nacos registry, {} {}:{} register finished", serviceId,
instance.getIp(), instance.getPort());
}
catch (Exception e) {
log.error("nacos registry, {} register failed...{},", serviceId,
registration.toString(), e);
}
} @Override
public void deregister(Registration registration) { log.info("De-registering from Nacos Server now..."); if (StringUtils.isEmpty(registration.getServiceId())) {
log.warn("No dom to de-register for nacos client...");
return;
} NamingService namingService = nacosDiscoveryProperties.namingServiceInstance();
String serviceId = registration.getServiceId(); try {
namingService.deregisterInstance(serviceId, registration.getHost(),
registration.getPort(), nacosDiscoveryProperties.getClusterName());
}
catch (Exception e) {
log.error("ERR_NACOS_DEREGISTER, de-register failed...{},",
registration.toString(), e);
} log.info("De-registration finished.");
} @Override
public void close() { } @Override
public void setStatus(Registration registration, String status) { if (!status.equalsIgnoreCase("UP") && !status.equalsIgnoreCase("DOWN")) {
log.warn("can't support status {},please choose UP or DOWN", status);
return;
} String serviceId = registration.getServiceId(); Instance instance = getNacosInstanceFromRegistration(registration); if (status.equalsIgnoreCase("DOWN")) {
instance.setEnabled(false);
}
else {
instance.setEnabled(true);
} try {
nacosDiscoveryProperties.namingMaintainServiceInstance()
.updateInstance(serviceId, instance);
}
catch (Exception e) {
throw new RuntimeException("update nacos instance status fail", e);
} } @Override
public Object getStatus(Registration registration) { String serviceName = registration.getServiceId();
try {
List<Instance> instances = nacosDiscoveryProperties.namingServiceInstance()
.getAllInstances(serviceName);
for (Instance instance : instances) {
if (instance.getIp().equalsIgnoreCase(nacosDiscoveryProperties.getIp())
&& instance.getPort() == nacosDiscoveryProperties.getPort())
return instance.isEnabled() ? "UP" : "DOWN";
}
}
catch (Exception e) {
log.error("get all instance of {} error,", serviceName, e);
}
return null;
} private Instance getNacosInstanceFromRegistration(Registration registration) {
Instance instance = new Instance();
instance.setIp(registration.getHost());
instance.setPort(registration.getPort());
instance.setWeight(nacosDiscoveryProperties.getWeight());
instance.setClusterName(nacosDiscoveryProperties.getClusterName());
instance.setMetadata(registration.getMetadata());
return instance;
} }

我们可以看到NacosServiceRegistry.register方法最终会调用NamingService.registerInstance

 @Override
public void registerInstance(String serviceName, String groupName, Instance instance) throws NacosException { if (instance.isEphemeral()) {
BeatInfo beatInfo = new BeatInfo();
beatInfo.setServiceName(NamingUtils.getGroupedName(serviceName, groupName));
beatInfo.setIp(instance.getIp());
beatInfo.setPort(instance.getPort());
beatInfo.setCluster(instance.getClusterName());
beatInfo.setWeight(instance.getWeight());
beatInfo.setMetadata(instance.getMetadata());
beatInfo.setScheduled(false);
long instanceInterval = instance.getInstanceHeartBeatInterval();
beatInfo.setPeriod(instanceInterval == 0 ? DEFAULT_HEART_BEAT_INTERVAL : instanceInterval); beatReactor.addBeatInfo(NamingUtils.getGroupedName(serviceName, groupName), beatInfo);
} serverProxy.registerService(NamingUtils.getGroupedName(serviceName, groupName), groupName, instance);
}

该方法判断isEphemeral,是true的话会向beatReactor新增一个当前service的心跳任务,作用就是通过定时任务向注册中心发一个心跳请求,表示服务仍然存活,这个定时任务的间隔时间默认为5秒,默认值定义在com.alibaba.nacos.api.common.Constants里面,我们可以自定义该配置,spring.cloud.nacos.discovery.heart-beat-interval=1000,这个时间如果太长可能服务已经出问题了,但是注册中心无法感知,导致请求还会打到该节点,太短的话可能会对注册中心造成压力

最终向注册中心注册服务就是发起一个简单的http请求,具体细节大家可以看源代码

二 再看springcloud的feign配置加载

     feign的启用通过注解EnableFeignClients,该注解会通过import FeignClientsRegistrar来加载feign的配置,其中FeignClientsRegistrar是对接口ImportBeanDefinitionRegistrar的一个实现,该接口相当于自定义对某一包下bean的自定义装配,比如这里的FeignClientsRegistrar就是对注解中basePackages下的feign clientbean的自定义装配。

其它关于对feign的配置加载,可参考我的另一篇文章 https://www.cnblogs.com/minjay/p/15746223.html

三 ribbon的配置加载 

      ribbon是负载均衡工具,在ribbon之前spring首先会先配置LoadBalancerAutoConfiguration,如下代码

/*
* Copyright 2013-2017 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.netflix.ribbon; import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.ArrayList;
import java.util.List; import com.netflix.client.IClient;
import com.netflix.client.http.HttpRequest;
import com.netflix.ribbon.Ribbon; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.AllNestedConditions;
import org.springframework.boot.autoconfigure.condition.AnyNestedCondition;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.client.actuator.HasFeatures;
import org.springframework.cloud.client.loadbalancer.AsyncLoadBalancerAutoConfiguration;
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryFactory;
import org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.cloud.client.loadbalancer.RestTemplateCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.AsyncRestTemplate;
import org.springframework.web.client.RestTemplate; /**
* Auto configuration for Ribbon (client side load balancing).
*
* @author Spencer Gibb
* @author Dave Syer
* @author Biju Kunjummen
*/
@Configuration
@Conditional(RibbonAutoConfiguration.RibbonClassesConditions.class)
@RibbonClients
@AutoConfigureAfter(name = "org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration")
@AutoConfigureBefore({LoadBalancerAutoConfiguration.class, AsyncLoadBalancerAutoConfiguration.class})
@EnableConfigurationProperties({RibbonEagerLoadProperties.class, ServerIntrospectorProperties.class})
public class RibbonAutoConfiguration { @Autowired(required = false)
private List<RibbonClientSpecification> configurations = new ArrayList<>(); @Autowired
private RibbonEagerLoadProperties ribbonEagerLoadProperties; @Bean
public HasFeatures ribbonFeature() {
return HasFeatures.namedFeature("Ribbon", Ribbon.class);
} @Bean
public SpringClientFactory springClientFactory() {
SpringClientFactory factory = new SpringClientFactory();
factory.setConfigurations(this.configurations);
return factory;
} @Bean
@ConditionalOnMissingBean(LoadBalancerClient.class)
public LoadBalancerClient loadBalancerClient() {
return new RibbonLoadBalancerClient(springClientFactory());
} @Bean
@ConditionalOnClass(name = "org.springframework.retry.support.RetryTemplate")
@ConditionalOnMissingBean
public LoadBalancedRetryFactory loadBalancedRetryPolicyFactory(final SpringClientFactory clientFactory) {
return new RibbonLoadBalancedRetryFactory(clientFactory);
} @Bean
@ConditionalOnMissingBean
public PropertiesFactory propertiesFactory() {
return new PropertiesFactory();
} @Bean
@ConditionalOnProperty(value = "ribbon.eager-load.enabled")
public RibbonApplicationContextInitializer ribbonApplicationContextInitializer() {
return new RibbonApplicationContextInitializer(springClientFactory(),
ribbonEagerLoadProperties.getClients());
} @Configuration
@ConditionalOnClass(HttpRequest.class)
@ConditionalOnRibbonRestClient
protected static class RibbonClientHttpRequestFactoryConfiguration { @Autowired
private SpringClientFactory springClientFactory; @Bean
public RestTemplateCustomizer restTemplateCustomizer(
final RibbonClientHttpRequestFactory ribbonClientHttpRequestFactory) {
return restTemplate -> restTemplate.setRequestFactory(ribbonClientHttpRequestFactory);
} @Bean
public RibbonClientHttpRequestFactory ribbonClientHttpRequestFactory() {
return new RibbonClientHttpRequestFactory(this.springClientFactory);
}
} //TODO: support for autoconfiguring restemplate to use apache http client or okhttp @Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnRibbonRestClientCondition.class)
@interface ConditionalOnRibbonRestClient { } private static class OnRibbonRestClientCondition extends AnyNestedCondition {
public OnRibbonRestClientCondition() {
super(ConfigurationPhase.REGISTER_BEAN);
} @Deprecated //remove in Edgware"
@ConditionalOnProperty("ribbon.http.client.enabled")
static class ZuulProperty {} @ConditionalOnProperty("ribbon.restclient.enabled")
static class RibbonProperty {}
} /**
* {@link AllNestedConditions} that checks that either multiple classes are present
*/
static class RibbonClassesConditions extends AllNestedConditions { RibbonClassesConditions() {
super(ConfigurationPhase.PARSE_CONFIGURATION);
} @ConditionalOnClass(IClient.class)
static class IClientPresent { } @ConditionalOnClass(RestTemplate.class)
static class RestTemplatePresent { } @ConditionalOnClass(AsyncRestTemplate.class)
static class AsyncRestTemplatePresent { } @ConditionalOnClass(Ribbon.class)
static class RibbonPresent { } }
}

LoadBalancerAutoConfiguration会自动帮我们注入支持负载均衡的RestTemplate以及配置重试retry相关。

加载完LoadBalancerAutoConfiguration后会自动配置RibbonAutoConfiguration,其中很核心的类就是装载了bean LoadBalancerClient为RibbonLoadBalancerClient,LoadBalancerClient接口是spring自己定义的一个接口,但是我发现这个接口的实现在整个调用过程中都用不到,还望哪位大佬能指导下这个实现机制在什么时候会用的到

还有一个核心配置就是SpringClientFactory,这个类的作用就是在请求时根据你的serviceId去加载相关配置,比如连接超时和读取超时

     

从服务间的一次调用分析整个springcloud的调用过程(一)的更多相关文章

  1. 从服务间的一次调用分析整个springcloud的调用过程(二)

    先看示例代码 @RestController @RequestMapping("/students") public class StudentController { @Auto ...

  2. Android服务之PackageManagerService启动源码分析

    了解了Android系统的启动过程的读者应该知道,Android的所有Java服务都是通过SystemServer进程启动的,并且驻留在SystemServer进程中.SystemServer进程在启 ...

  3. spring boot / cloud (十四) 微服务间远程服务调用的认证和鉴权的思考和设计,以及restFul风格的url匹配拦截方法

    spring boot / cloud (十四) 微服务间远程服务调用的认证和鉴权的思考和设计,以及restFul风格的url匹配拦截方法 前言 本篇接着<spring boot / cloud ...

  4. Asp.Net Core使用SignalR进行服务间调用

    网上查询过很多关于ASP.NET core使用SignalR的简单例子,但是大部分都是简易聊天功能,今天心血来潮就搞了个使用SignalR进行服务间调用的简单DEMO. 至于SignalR是什么我就不 ...

  5. SpringCloud服务间调用

    SpringCloud服务间的调用有两种方式:RestTemplate和FeignClient.不管是什么方式,他都是通过REST接口调用服务的http接口,参数和结果默认都是通过jackson序列化 ...

  6. SpringCloud初体验:三、Feign 服务间调用(FeignClient)、负载均衡(Ribbon)、容错/降级处理(Hystrix)

    FeignOpenFeign Feign是一种声明式.模板化的HTTP客户端. 看了解释过后,可以理解为他是一种 客户端 配置实现的策略,它实现 服务间调用(FeignClient).负载均衡(Rib ...

  7. spring boot项目使用swagger-codegen生成服务间调用的jar包

    swagger-codegen的github:https://github.com/swagger-api/swagger-codegen 需要的环境:jdk > 1.7   maven > ...

  8. SpringCloud实现服务间调用(RestTemplate方式)

    上一篇文章<SpringCloud搭建注册中心与服务注册>介绍了注册中心的搭建和服务的注册,本文将介绍下服务消费者调用服务提供者的过程. 本文目录 一.服务调用流程二.服务提供者三.服务消 ...

  9. SpringCloud 服务间互相调用 @FeignClient注解

    SpringCloud搭建各种微服务之后,服务间通常存在相互调用的需求,SpringCloud提供了@FeignClient 注解非常优雅的解决了这个问题 首先,保证几个服务都在一个Eureka中注册 ...

随机推荐

  1. Pytest_在jenkins中使用allure报告(13)

    一.安装allure插件 点击jenkins管理-->插件管理 点击Available,在搜索框中输入allure并安装 二.配置构建命令 三.构建配置allure插件 点击构建后置操作 pat ...

  2. webSocket 前端 js 加入 心跳机制 的基本写法

    1前言 websocket 一般 每隔 90 秒无操作则会自动断开  ,需要加入一个心跳机制 来防止 自断 2. 实验过程 (1)设定一个jsp 或html 文件都行 ,加入元素 (2)js 源码 , ...

  3. POJ prime distance

    https://oj.shiyancang.cn/Problem/781.html 素数距离,数据范围21亿,如果用素数筛存,并且进行做的话,按照x/lnx计算会是一个非常恐怖的复杂度.确定要做什么, ...

  4. 关于 Intel CPU 和Iris Xe Graphics的报告问题

    关于 Intel CPU 和Iris Xe Graphics的报告问题 有些用户报告了一些技术问题,这里有更多的信息和如何解决. Intel 11th CPU & Iris Xe Graphi ...

  5. 内核内存分配器SLAB和SLUB

    内核分配器的功能 在操作系统管理的虚拟内存中,用于内存管理的最小单位是页,大多数传统的架构是4KB.由于进程每次申请分配4KB是不现实的,比如分配几个字节或几十个字节,这时需要中间机制来管理页面的微型 ...

  6. 【经验总结-markdown】markdown字体和颜色设置

    字体设置 关键词为face <font face = "黑体">我是黑体</font> <font face = "宋体"> ...

  7. 【初体验】macos下android ndk交叉编译hello world,并拷贝到android手机上执行

    1.机器上以前安装了java 1.8(貌似android ndk不需要java) 2. 下载android ndk,版本是android-ndk-r14b (比较奇怪,我下载了最新的android-n ...

  8. 一种Django多租户解决方案

    什么是多租户? 多租户技术或称多重租赁技术,简称SaaS,是一种软件架构技术,是实现如何在多用户环境下(此处的多用户一般是面向企业用户)共用相同的系统或程序组件,并且可确保各用户间数据的隔离性. 多租 ...

  9. web服务组件

  10. 【webpack4.0】---dev.config.js基本配置(六)

    一.开发环境配置准备 1.创建dev.config.js文件 用来配置开发环境的代码 2.安装webpack-merge cnpm install webpack-merge -D 用来合并webpa ...