责任链模式 

责任链模式在Dubbo中发挥的作用举足轻重,就像是Dubbo框架的骨架。Dubbo的调用链组织是用责任链模式串连起来的。

责任链中的每个节点实现Filter接口,然后由ProtocolFilterWrapper,将所有Filter串连起来。

Dubbo的许多功能都是通过Filter扩展实现的,比如监控、日志、缓存、安全、telnet以及RPC本身都是。

如果把Dubbo比作一列火车,责任链就像是火车的各车厢,每个车厢的功能不同。

如果需要加入新的功能,增加车厢就可以了,非常容易扩展。

最经典的实现链式Filter代码。采用匿名内部类来实现,一定要DEBUG进去看看。

 private static <T> Invoker<T> buildInvokerChain(final Invoker<T> invoker, String key, String group) {

       Invoker<T> last = invoker;

       List<Filter> filters = ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(invoker.getUrl(), key, group);

       if (filters.size() > 0) {

           for (int i = filters.size() - 1; i >= 0; i --) {

               final Filter filter = filters.get(i);

               final Invoker<T> next = last;

               last = new Invoker<T>() {

                   public Class<T> getInterface() {

                       return invoker.getInterface();

                   }

                   public URL getUrl() {

                       return invoker.getUrl();

                   }

                   public boolean isAvailable() {

                       return invoker.isAvailable();

                   }

                   public Result invoke(Invocation invocation) throws RpcException {

                       return filter.invoke(next, invocation);

                   }

                   public void destroy() {

                       invoker.destroy();

                   }

                   @Override

                   public String toString() {

                       return invoker.toString();

                   }

               };

           }

       }

       return last;

   }

 

至少有2个典型案例:

  • dubbo filter链式调用

  • dubbo handler链式调用

Dubbo的Filter类似于 serlvet filter.可以搞一些非业务的工作,如限流,超时,访问日志记录,trace等。 dubbo服务,进行refer或export时,会build filter. 采用了匿名机制。

public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {

       if (Constants.REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) {

           return protocol.export(invoker);

       }

       return protocol.export(buildInvokerChain(invoker, Constants.SERVICE_FILTER_KEY, Constants.PROVIDER));

   }

   public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {

       if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {

           return protocol.refer(type, url);

       }

       return buildInvokerChain(protocol.refer(type, url), Constants.REFERENCE_FILTER_KEY, Constants.CONSUMER);

   }

   public void destroy() {

       protocol.destroy();

   }

   private static <T> Invoker<T> buildInvokerChain(final Invoker<T> invoker, String key, String group) {

       Invoker<T> last = invoker;

       List<Filter> filters = ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(invoker.getUrl(), key, group);

       if (filters.size() > 0) {

           for (int i = filters.size() - 1; i >= 0; i --) {

               final Filter filter = filters.get(i);

               final Invoker<T> next = last;

               last = new Invoker<T>() {

                   public Class<T> getInterface() {

                       return invoker.getInterface();

                   }

                   public URL getUrl() {

                       return invoker.getUrl();

                   }

                   public boolean isAvailable() {

                       return invoker.isAvailable();

                   }

                   public Result invoke(Invocation invocation) throws RpcException {

                       return filter.invoke(next, invocation);

                   }

                   public void destroy() {

                       invoker.destroy();

                   }

                   @Override

                   public String toString() {

                       return invoker.toString();

                   }

               };

           }

       }

       return last;

   }

  

dubbo handler采用的也是链式模式。 链式模型是通信系统中的经典模式,也叫做pipeline模式。 应用数据通过协议层,传输层,序列化后,和管道非常类似,在每一层,都会进行相应的业务处理,然后传到下一层

protected ChannelHandler wrapInternal(ChannelHandler handler, URL url) {

       return new MultiMessageHandler(new HeartbeatHandler(((Dispatcher)ExtensionLoader.getExtensionLoader(Dispatcher.class).getAdaptiveExtension()).dispatch(handler, url)));

   }

最里层那个hanlder也是一层一层套的handler.分别是DecodeHandler,HeaderExchangeHandler,DubboProtocol(handler)

这几个handler关系比较复杂

观察者模式

Dubbo中使用观察者模式最典型的例子是RegistryService

消费者在初始化的时候回调用subscribe方法,注册一个观察者,如果观察者引用的服务地址列表发生改变,就会通过NotifyListener通知消费者。

此外,Dubbo的InvokerListenerExporterListener 也实现了观察者模式,只要实现该接口,并注册,

就可以接收到consumer端调用refer和provider端调用export的通知。Dubbo的注册/订阅模型和观察者模式就是天生一对。

节点export或refer的时候,都会订阅感兴趣的节点。

public <T> Exporter<T> export(Invoker<T> originInvoker) throws RpcException {

       final RegistryProtocol.ExporterChangeableWrapper<T> exporter = this.doLocalExport(originInvoker);

       final Registry registry = this.getRegistry(originInvoker);

       final URL registedProviderUrl = this.getRegistedProviderUrl(originInvoker);

       registry.register(registedProviderUrl);

       final URL overrideSubscribeUrl = this.getSubscribedOverrideUrl(registedProviderUrl);

       final RegistryProtocol.OverrideListener overrideSubscribeListener = new RegistryProtocol.OverrideListener(overrideSubscribeUrl);

       this.overrideListeners.put(overrideSubscribeUrl, overrideSubscribeListener);

       registry.subscribe(overrideSubscribeUrl, overrideSubscribeListener);

}

private <T> Invoker<T> doRefer(Cluster cluster, Registry registry, Class<T> type, URL url) {

   RegistryDirectory<T> directory = new RegistryDirectory(type, url);

   directory.setRegistry(registry);

   directory.setProtocol(this.protocol);

   URL subscribeUrl = new URL("consumer", NetUtils.getLocalHost(), 0, type.getName(), directory.getUrl().getParameters());

   if (!"*".equals(url.getServiceInterface()) && url.getParameter("register", true)) {

       registry.register(subscribeUrl.addParameters(new String[]{"category", "consumers", "check", String.valueOf(false)}));

   }

   directory.subscribe(subscribeUrl.addParameter("category", "providers,configurators,routers"));

   return cluster.join(directory);

}

public void subscribe(URL url) {

   this.setConsumerUrl(url);

   this.registry.subscribe(url, this);

}

  

上面,订阅节点信息的时候,把自己的this引用传进去了。这样,当节点有变化的时候,会通过this上下文,修改Invoker列表。

ChildListener zkListener = (ChildListener)listeners.get(listener);

                   if (zkListener == null) {

                       listeners.putIfAbsent(listener, new ChildListener() {

                           public void childChanged(String parentPath, List<String> currentChilds) {

                               ZookeeperRegistry.this.notify(url, listener, ZookeeperRegistry.this.toUrlsWithEmpty(url, parentPath, currentChilds));

                           }

                       });

                       zkListener = (ChildListener)listeners.get(listener);

 }

  

一路进来,刷新invoker.注意,invoker会产生竟态条件,所以需要加锁。

 public synchronized void notify(List<URL> urls) {

       List<URL> invokerUrls = new ArrayList();

       List<URL> routerUrls = new ArrayList();

       List<URL> configuratorUrls = new ArrayList();

       Iterator i$ = urls.iterator();

       while(true) {

           while(true) {

               while(i$.hasNext()) {

                   URL url = (URL)i$.next();

                   String protocol = url.getProtocol();

                   String category = url.getParameter("category", "providers");

               this.refreshInvoker(invokerUrls);

               return;

               }

           }

   }

  

修饰器模式

Dubbo中还大量用到了修饰器模式。比如ProtocolFilterWrapper类是对Protocol类的修饰。在export和refer方法中,配合责任链模式,

把Filter组装成责任链,实现对Protocol功能的修饰。其他还有ProtocolListenerWrapper、 ListenerInvokerWrapperInvokerWrapper等。

个人感觉,修饰器模式是一把双刃剑,一方面用它可以方便地扩展类的功能,而且对用户无感,

但另一方面,过多地使用修饰器模式不利于理解,因为一个类可能经过层层修饰,最终的行为已经和原始行为偏离较大。

工厂方法模式

CacheFactory的实现采用的是工厂方法模式。CacheFactory接口定义getCache方法,

然后定义一个AbstractCacheFactory抽象类实现CacheFactory

并将实际创建cache的createCache方法分离出来,并设置为抽象方法。这样具体cache的创建工作就留给具体的子类去完成。

插件机制

Dubbo本身的功能基本都够用了,但是Dubbo没有固步自封,而是平等的对待第三方,用户可以定制自己的插件,对Dubbo功能进行扩展。 Dubbo通过SPI机制,实现插件机制。 机制如下:

  • DUBBO框架预留了接口,具体的实现,由插件实现

  • SPI注解,通过SPI注解,以及约定的配置文件,完成实现接口的映射关系

  • 插件配置放在目录”META-INF/dubbo/internal“下面

  • 配置文件的格式是”KEY=VALUE“格式

  • KEY是SPI注解上面的值,VALUE是对应的插件实现类

loadExtensionClasses会从约定好的目录下载加载类。

   private Map<String, Class<?>> loadExtensionClasses() {

       SPI defaultAnnotation = (SPI)this.type.getAnnotation(SPI.class);

       String value = defaultAnnotation.value();

       Map<String, Class<?>> extensionClasses = new HashMap();

       this.loadFile(extensionClasses, "META-INF/dubbo/internal/");

       return extensionClasses;

   }

一般情况下,使用默认的即可,如果需要使用自定义的插件,可以通过URL传递。 例如,负载均均衡测试,通过URL指定,如果未指定,则使用默认的随机负载均衡策略。

loadbalance = (LoadBalance)ExtensionLoader.getExtensionLoader(LoadBalance.class).getExtension(((Invoker)invokers.get(0)).getUrl().getMethodParameter(invocation.getMethodName(), "loadbalance", "random"));

上述加载类的过程非常复杂,这次简单过一下

抽象工厂模式

ProxyFactory及其子类是Dubbo中使用抽象工厂模式的典型例子。

ProxyFactory提供两个方法,分别用来生产ProxyInvoker

(这两个方法签名看起来有些矛盾,因为getProxy方法需要传入一个Invoker对象,而getInvoker方法需要传入一个Proxy对象,看起来会形成循环依赖,但其实两个方式使用的场景不一样)。

AbstractProxyFactory实现了ProxyFactory接口,作为具体实现类的抽象父类。

然后定义了JdkProxyFactoryJavassistProxyFactory两个具体类,分别用来生产基于jdk代理机制和基于javassist代理机制的ProxyInvoker

适配器模式

为了让用户根据自己的需求选择日志组件,Dubbo自定义了自己的Logger接口,并为常见的日志组件(包括jcl, jdk, log4j, slf4j)提供相应的适配器。

并且利用简单工厂模式提供一个LoggerFactory,客户可以创建抽象的Dubbo自定义Logger,而无需关心实际使用的日志组件类型。

在LoggerFactory初始化时,客户通过设置系统变量的方式选择自己所用的日志组件,这样提供了很大的灵活性。

至少有3个经典案例

  • Transport 完成Server 和Client接口功能

  • CoderAdapter完成encode和decode功能 NettyCodecAdapter

  • @Adaptive 

@SPI("netty")

public interface Transporter {

   @Adaptive({"server", "transporter"})

   Server bind(URL var1, ChannelHandler var2) throws RemotingException;

   @Adaptive({"client", "transporter"})

   Client connect(URL var1, ChannelHandler var2) throws RemotingException;

}

下面就是给类动态的增加功能。

private static Wrapper makeWrapper(Class<?> c)

   {

       if( c.isPrimitive() )

           throw new IllegalArgumentException("Can not create wrapper for primitive type: " + c);

       String name = c.getName();

       ClassLoader cl = ClassHelper.getClassLoader(c);

       StringBuilder c1 = new StringBuilder("public void setPropertyValue(Object o, String n, Object v){ ");

       StringBuilder c2 = new StringBuilder("public Object getPropertyValue(Object o, String n){ ");

       StringBuilder c3 = new StringBuilder("public Object invokeMethod(Object o, String n, Class[] p, Object[] v) throws " + InvocationTargetException.class.getName() + "{ ");

       c1.append(name).append(" w; try{ w = ((").append(name).append(")$1); }catch(Throwable e){ throw new IllegalArgumentException(e); }");

       c2.append(name).append(" w; try{ w = ((").append(name).append(")$1); }catch(Throwable e){ throw new IllegalArgumentException(e); }");

       c3.append(name).append(" w; try{ w = ((").append(name).append(")$1); }catch(Throwable e){ throw new IllegalArgumentException(e); }");

       Map<String, Class<?>> pts = new HashMap<String, Class<?>>(); // <property name, property types>

       Map<String, Method> ms = new LinkedHashMap<String, Method>(); // <method desc, Method instance>

       List<String> mns = new ArrayList<String>(); // method names.

       List<String> dmns = new ArrayList<String>(); // declaring method names.

       // get all public field.

       for( Field f : c.getFields() )

       {

           String fn = f.getName();

           Class<?> ft = f.getType();

           if( Modifier.isStatic(f.getModifiers()) || Modifier.isTransient(f.getModifiers()) )

               continue;

           c1.append(" if( $2.equals(\"").append(fn).append("\") ){ w.").append(fn).append("=").append(arg(ft, "$3")).append("; return; }");

           c2.append(" if( $2.equals(\"").append(fn).append("\") ){ return ($w)w.").append(fn).append("; }");

           pts.put(fn, ft);

       }

       Method[] methods = c.getMethods();

       // get all public method.

       boolean hasMethod = hasMethods(methods);

       if( hasMethod ){

           c3.append(" try{");

       }

       for( Method m : methods )

       {

           if( m.getDeclaringClass() == Object.class ) //ignore Object's method.

               continue;

           String mn = m.getName();

           c3.append(" if( \"").append(mn).append("\".equals( $2 ) ");

           int len = m.getParameterTypes().length;

           c3.append(" && ").append(" $3.length == ").append(len);

           boolean override = false;

           for( Method m2 : methods ) {

               if (m != m2 && m.getName().equals(m2.getName())) {

                   override = true;

                   break;

               }

           }

           if (override) {

               if (len > 0) {

                   for (int l = 0; l < len; l ++) {

                       c3.append(" && ").append(" $3[").append(l).append("].getName().equals(\"")

                           .append(m.getParameterTypes()[l].getName()).append("\")");

                   }

               }

           }

           c3.append(" ) { ");

           if( m.getReturnType() == Void.TYPE )

               c3.append(" w.").append(mn).append('(').append(args(m.getParameterTypes(), "$4")).append(");").append(" return null;");

           else

               c3.append(" return ($w)w.").append(mn).append('(').append(args(m.getParameterTypes(), "$4")).append(");");

           c3.append(" }");

           mns.add(mn);

           if( m.getDeclaringClass() == c )

               dmns.add(mn);

           ms.put(ReflectUtils.getDesc(m), m);

       }

       if( hasMethod ){

           c3.append(" } catch(Throwable e) { " );

           c3.append("     throw new java.lang.reflect.InvocationTargetException(e); " );

           c3.append(" }");

       }

       c3.append(" throw new " + NoSuchMethodException.class.getName() + "(\"Not found method \\\"\"+$2+\"\\\" in class " + c.getName() + ".\"); }");

       // deal with get/set method.

       Matcher matcher;

       for( Map.Entry<String,Method> entry : ms.entrySet() )

       {

           String md = entry.getKey();

           Method method = (Method)entry.getValue();

           if( ( matcher = ReflectUtils.GETTER_METHOD_DESC_PATTERN.matcher(md) ).matches() )

           {

               String pn = propertyName(matcher.group(1));

               c2.append(" if( $2.equals(\"").append(pn).append("\") ){ return ($w)w.").append(method.getName()).append("(); }");

               pts.put(pn, method.getReturnType());

           }

           else if( ( matcher = ReflectUtils.IS_HAS_CAN_METHOD_DESC_PATTERN.matcher(md) ).matches() )

           {

               String pn = propertyName(matcher.group(1));

               c2.append(" if( $2.equals(\"").append(pn).append("\") ){ return ($w)w.").append(method.getName()).append("(); }");

               pts.put(pn, method.getReturnType());

           }

           else if( ( matcher = ReflectUtils.SETTER_METHOD_DESC_PATTERN.matcher(md) ).matches() )

           {

               Class<?> pt = method.getParameterTypes()[0];

               String pn = propertyName(matcher.group(1));

               c1.append(" if( $2.equals(\"").append(pn).append("\") ){ w.").append(method.getName()).append("(").append(arg(pt,"$3")).append("); return; }");

               pts.put(pn, pt);

           }

       }

       c1.append(" throw new " + NoSuchPropertyException.class.getName() + "(\"Not found property \\\"\"+$2+\"\\\" filed or setter method in class " + c.getName() + ".\"); }");

       c2.append(" throw new " + NoSuchPropertyException.class.getName() + "(\"Not found property \\\"\"+$2+\"\\\" filed or setter method in class " + c.getName() + ".\"); }");

       // make class

       long id = WRAPPER_CLASS_COUNTER.getAndIncrement();

       ClassGenerator cc = ClassGenerator.newInstance(cl);

       cc.setClassName( ( Modifier.isPublic(c.getModifiers()) ? Wrapper.class.getName() : c.getName() + "$sw" ) + id );

       cc.setSuperClass(Wrapper.class);

       cc.addDefaultConstructor();

       cc.addField("public static String[] pns;"); // property name array.

       cc.addField("public static " + Map.class.getName() + " pts;"); // property type map.

       cc.addField("public static String[] mns;"); // all method name array.

       cc.addField("public static String[] dmns;"); // declared method name array.

       for(int i=0,len=ms.size();i<len;i++)

           cc.addField("public static Class[] mts" + i + ";");

       cc.addMethod("public String[] getPropertyNames(){ return pns; }");

       cc.addMethod("public boolean hasProperty(String n){ return pts.containsKey($1); }");

       cc.addMethod("public Class getPropertyType(String n){ return (Class)pts.get($1); }");

       cc.addMethod("public String[] getMethodNames(){ return mns; }");

       cc.addMethod("public String[] getDeclaredMethodNames(){ return dmns; }");

       cc.addMethod(c1.toString());

       cc.addMethod(c2.toString());

       cc.addMethod(c3.toString());

       try

       {

           Class<?> wc = cc.toClass();

           // setup static field.

           wc.getField("pts").set(null, pts);

           wc.getField("pns").set(null, pts.keySet().toArray(new String[0]));

           wc.getField("mns").set(null, mns.toArray(new String[0]));

           wc.getField("dmns").set(null, dmns.toArray(new String[0]));

           int ix = 0;

           for( Method m : ms.values() )

               wc.getField("mts" + ix++).set(null, m.getParameterTypes());

           return (Wrapper)wc.newInstance();

       }

       catch(RuntimeException e)

       {

           throw e;

       }

       catch(Throwable e)

       {

           throw new RuntimeException(e.getMessage(), e);

       }

       finally

       {

           cc.release();

           ms.clear();

           mns.clear();

           dmns.clear();

       }

   }

  

代理模式

Dubbo consumer使用Proxy类创建远程服务的本地代理,本地代理实现和远程服务一样的接口,并且屏蔽了网络通信的细节,使得用户在使用本地代理的时候,感觉和使用本地服务一样。

public class JavassistProxyFactory extends AbstractProxyFactory {

   @SuppressWarnings("unchecked")

   public <T> T getProxy(Invoker<T> invoker, Class<?>[] interfaces) {

       return (T) Proxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker));

   }

   public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) {

       // TODO Wrapper类不能正确处理带$的类名

       final Wrapper wrapper = Wrapper.getWrapper(proxy.getClass().getName().indexOf('$') < 0 ? proxy.getClass() : type);

       return new AbstractProxyInvoker<T>(proxy, type, url) {

           @Override

           protected Object doInvoke(T proxy, String methodName,

                                     Class<?>[] parameterTypes,

                                     Object[] arguments) throws Throwable {

               return wrapper.invokeMethod(proxy, methodName, parameterTypes, arguments);

           }

       };

   }

}

第三课 Dubbo设计中的设计模式的更多相关文章

  1. 第二课 Dubbo设计的架构设计

    总体架构 Dubbo的总体架构,如图所示: Dubbo框架设计一共划分了10个层,而最上面的Service层是留给实际想要使用Dubbo开发分布式服务的开发者实现业务逻辑的接口层.图中左边淡蓝背景的为 ...

  2. ionic新手教程第三课-在项目中使用requirejs分离controller文件和server文件

    继上篇教程中提到的,我们新建一个简单的tabs类型的Ionic项目. 依据文件夹文件我们知道,系统自己主动创建了一个controller文件和server文件,而且把全部的控制器和服务都写到这两个文件 ...

  3. AD设计中,三种大面积覆铜的区别

    在AD设计中,主要有三种大面积覆铜方式,分别是Fill(铜皮) Polygon Pour(灌铜)和Plane(平面层),这三种方式刚开始的时候没有细细区分,现在分别应用了一下, 总结如下,欢迎指正 F ...

  4. 精通Dubbo——dubbo2.0源码中的设计模式与SPI介绍

    Dubbo源码包介绍当我们从github把Dubbo源码下载下来之后有如下源码包   下面来说明每个包的作用,以便我们有目的的阅读代码 dubbo-admin dubbo管理平台源码包,用来管理dub ...

  5. C#软件设计——小话设计模式原则之:依赖倒置原则DIP

    前言:很久之前就想动笔总结下关于软件设计的一些原则,或者说是设计模式的一些原则,奈何被各种bootstrap组件所吸引,一直抽不开身.群里面有朋友问博主是否改行做前端了,呵呵,其实博主是想做“全战”, ...

  6. C#软件设计——小话设计模式原则之:单一职责原则SRP

    前言:上篇C#软件设计——小话设计模式原则之:依赖倒置原则DIP简单介绍了下依赖倒置的由来以及使用,中间插了两篇WebApi的文章,这篇还是回归正题,继续来写写设计模式另一个重要的原则:单一职责原则. ...

  7. C#软件设计——小话设计模式原则之:开闭原则OCP

    前言:这篇继续来看看开闭原则.废话少说,直接入正题. 软件设计原则系列文章索引 C#软件设计——小话设计模式原则之:依赖倒置原则DIP C#软件设计——小话设计模式原则之:单一职责原则SRP C#软件 ...

  8. (转载)JDK中的设计模式

    写的很好,学习道路更轻松一些 原文地址:http://blog.csdn.net/gtuu0123/article/details/6114197 JDK中设计模式 分类: Java相关 设计模式 2 ...

  9. 访何红辉:谈谈Android源码中的设计模式

    最近Android 6.0版本的源代码开放下载,刚好分析Android源码的技术书籍<Android源码设计模式解析与实战>上市,我们邀请到它的作者何红辉,来谈谈Android源码中的设计 ...

随机推荐

  1. 鸿蒙内核源码分析(调度队列篇) | 内核有多少个调度队列 | 百篇博客分析OpenHarmony源码 | v6.05

    百篇博客系列篇.本篇为: v06.xx 鸿蒙内核源码分析(调度队列篇) | 内核有多少个调度队列 | 51.c.h .o 任务管理相关篇为: v03.xx 鸿蒙内核源码分析(时钟任务篇) | 触发调度 ...

  2. 深入浅出WPF-01.WPF缘起

    WPF缘起 自2012年起,根据公司需要,开始进入界面开发工作.公司是医疗器械行业,很多操作都是基于PC进行的,所以桌面应用开发尤为重要.原有项目都是基于MFC进行的开发,而且是VC6.0的技术,维护 ...

  3. 一、Ansible基础之入门篇

    目录 1. Ansible基础 1.1 介绍 1.2 工作原理 1.3 如何安装 1.3.1 先决条件 1.3.2 安装Ansible 1.4 管理节点与被管理节点建立SSH信任关系 1.5 快速入门 ...

  4. 实现一个简单的侧边导航Winform程序框架

    目录 简介 实现导航面板 实现方法 使用方法 实现标题栏 窗体拖拽及最大化 自定义窗体按钮 标题显示 按钮设置 实现状态栏 整体使用 参考文章 简介 每次新项目都要想着界面怎么设计好,但想来想去上位机 ...

  5. JVM学习笔记——GC算法

    GC 算法 GC 即 Garbage Collection 垃圾回收.JVM 中的 GC 99%发生在堆中,而 Java 堆中采用的垃圾回收机制为分代收集算法.即将堆分为新生代和老年代,根据不同的区域 ...

  6. asp.net core使用identity+jwt保护你的webapi(二)——获取jwt token

    前言 上一篇已经介绍了identity在web api中的基本配置,本篇来完成用户的注册,登录,获取jwt token. 开始 开始之前先配置一下jwt相关服务. 配置JWT 首先NuGet安装包: ...

  7. Conda 创建和删除虚拟环境

    1.检验当前conda的版本 conda -V C:\Users>conda -V conda 4.10.1 2.conda 常用的命令 查看已有的虚拟环境 C:\Users>conda ...

  8. 题解 [BJOI2019]奥术神杖

    题目传送门 题目大意 给出一个残缺的字符串,每个位置都 \(\in[0,9]\).有 \(m\) 中贡献,即 \(s,k\),表示该字符串中没出现一次 \(s\),贡献便乘上 \(k\).最后对贡献求 ...

  9. CF911G Mass Change Queries(线段树+暴力)

    cf机子真的快. 其实这个题的维护的信息还是很巧妙的. 首先,观察到题目中涉及到,区间修改这个操作,然后最后只查询一次,我们不妨用线段树来维护这个过程. 但是貌似直接维护每个位置的值可能不太好维护. ...

  10. 2021.3.3--vj补题

    题目 C - C CodeForces - 1166C The legend of the foundation of Vectorland talks of two integers xx and  ...