spring cloud gateway

API网关是一个服务器,是系统的唯一入口。从面向对象设计的角度看,它与外观模式类似。API网关方式的核心要点,是所有的客户端和消费端都通过统一的网关接入微服务,在网关层处理所有的非业务功能,通常网关提供REST/HTTP的访问API。

网关一般具备以下功能:
性能:API高可用,负载均衡,容错机制。
安全:权限身份认证、脱敏,流量清洗,后端签名(保证全链路可信调用),黑名单(非法调用的限制)。
日志:日志记录(spainid,traceid)一旦涉及分布式,全链路跟踪必不可少。
缓存:数据缓存。
监控:记录请求响应数据,api耗时分析,性能监控。
限流:流量控制,错峰流控,可以定义多种限流规则。
灰度:线上灰度部署,可以减小风险。
路由:动态路由规则。
过滤:例如鉴权之后构造头部,增加请求头、增加请求参数、增加响应头和断路器等。

API Gateway 常见的选型有基于 Openresty 的 Kong、基于 Go 的 Tyk 和基于 Java 的 Zuul、Spring Cloud Gateway。Nginx适合做门户网关,是作为整个全局的网关,是对外的,处于最外层的;而Gateway更像是业务网关,主要用来对应不同的客户端提供服务的,用于聚合业务的。各个微服务独立部署,职责单一,对外提供服务的时候需要有一个东西把业务聚合起来。Gateway是用Java写的,易于扩展和维护,Gateway网关可以实现熔断、重试等功能,这是Nginx不具备的。

面试题
https://www.kuibuke.com/exam
https://www.cnblogs.com/goody9807/p/6425399.html

Java集合

并发集合

Java5介绍了并发集合,大部分位于java.util.concurrent并发包中。

  • ConcurrentHashMap
  • CopyOnWriteArrayList:目的是高效并发读取。很多应用场景中,要求读操作尽可能快,写操作可以相对慢。CopyOnWriteArrayList中利用CopyOnWrite机制优化到写入不会阻塞读取
  • ConcurrentLinkedQueue:通过CAS操作和锁分离,是高并发环境中性能最好的队列。
  • BlockingQueue:线程间数据共享阻塞队列接口,它有不同的实现。BlockingQueue让从队列中获取消息的服务线程在队列空时等待,当有消息后再将线程唤醒。主要操作它的put和take方法。
  • ConcurrentSkipListMap:随机数据结构,跳表。跳表的内部维护了分层的多个链表,使用锁分离机制,查询时间复杂度是O(lgn)。

ConcurrentHashMap

ConcurrentHashMap,不仅提供线程安全,还用锁分离和内部分区等现代技术提高了可扩展性。把实际map分割(segmentation)成若干部分,默认值为16,使的多线程减小争用。但其size()等方法可能需要全局锁而取得所有段的锁,性能反而比HashMap低。

(低并发)线程安全容器:

  • Collections.synchronizedMap()
  • Collections.synchronizedList()
  • Vector
  • Hashtable:大小增加到一定的时候,性能会急剧下降,因为迭代时整个map需要被锁定很长的时间。

BlockingQueue

ArrayBlockingQueue

LinkedBlockingQueue:通过takeLock、putLock两把锁实现了读数据和写数据的分离,实现了对独占锁分离。

 

PriorityQueue 保证最高或者最低优先级的的元素总是在队列头部,当遍历一个 PriorityQueue 时,没有任何顺序保证。
LinkedHashMap 维持的顺序是元素插入的顺序。LinkedHashMap 课保证遍历顺序是元素插入的顺序。
集合的排序:可以使用有序集合,如 TreeSet 或 TreeMap,或者使用有顺序的的集合,如List,然后通过 Collections.sort() 来排序。
打印数组:数组没有实现 toString() 方法,可以使用 Arrays.toString() 和 Arrays.deepToString() 方法来转为字符串再打印数组。
Java 中的 LinkedList 是单向链表还是双向链表。在 Eclipse,可以使用快捷键 Ctrl + T,直接在编辑器中打开该类检查源码。
Java 中的 TreeMap 是使用红黑树实现的。
在 Java 7 中,ArrayList 的默认大小是 10 个元素。Java 7 中 ArrayList 和 HashMap 类的代码片段:

// from ArrayList.java JDK 1.7
private static final int DEFAULT_CAPACITY = 10;

//from HashMap.java JDK 7
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16

遍历 ArrayList 时移除一个元素,不是使用ArrayList 的 remove() ,而是使用Iterator 的 remove()方法,则不会出现 ConcurrentModificationException 异常。
想使用 Java 中增强的for-each 循环来遍历,只需要实现 Iterable 接口。或者实现 Collection 接口,默认就具有该属性。
Vector、Hashtable等是同步集合类,用同步方法来实现了线程安全, 而ArrayList、HashMap等不是线程安全的。

Java集合继承结构
集合继承结构

HashMap

由数组加链表实现

默认大小是16个元素(必须是2的幂),因为可以通过按位与计算余数,比求模更快。

Hashtable 与 HashMap

a) Hashtable 是 JDK 1 遗留下来的类,而 HashMap 是后来增加的。
b)Hashtable 是同步的,比较慢,但 HashMap 没有同步策略,所以会更快。
c)Hashtable 不允许有个空的 key,但是 HashMap 允许出现一个 null key。
大多数情况下,只要不涉及线程安全问题,Map基本都可以使用HashMap。HashMap的是无序的,允许将null用作键和值,非线程安全。HashMap不能保证元素的顺序,HashMap能够将键设为null,也可以将值设为null。
Hashtable,(注意大小写:不是HashTable),Hashtable不能将键和值设为null,否则运行时会报空指针异常错误,Hashtable是线程安全的。
HashSet 的内部采用 HashMap来实现。HashSet 允许有一个null key。

LinkedHashMap

LinkedHashMap增加了时间和空间上的开销,通过维护一个运行于所有条目的双向链表,LinkedHashMap保证了元素迭代的顺序。该迭代顺序可以是插入顺序或者是访问顺序。LinkedHashMap是有序、Key和Value都允许空、非线程安全的。LinkedHashMap可以认为是HashMap+LinkedList,即它既使用HashMap操作数据结构,又使用LinkedList维护插入元素的先后顺序。LinkedList每次访问一个元素(get或put),被访问的元素都会被提到最后面。
LinkedHashMap可以应用来实现LRUCache,LRU即Least Recently Used 最近最少使用。当缓存满了,优先淘汰最不常访问的数据。

LinkedHashMap相关问题:
java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to xxx。
rpc远程调用在底层还是使用的HTTPClient,所以在传递参数的时候,必定要有个顺序,不然服务层在接的时候就出问题了,所以它才会转为LinkedHashMap。spring 有一个类叫ModelMap,继承了linkedhashMap,即 public class ModelMap extends LinkedHashMap,所以一个接口返回的结果就可以直接用ModelMap来接。注意ModelMap是没有泛型的,不管返回的结果是什么类型的map,泛型是多复杂的map,都可以直接new一个Modelmap,用它来接返回的结果。

Java 中使用 Collections 的最佳实践

a)使用正确的集合类。如果不需要同步列表,使用 ArrayList 而不是 Vector。
b)优先使用并发集合。不是对集合进行同步并,发集合提供更好的可扩展性。
c)使用接口表示和访问集合。如使用List存储 ArrayList,使用 Map 存储 HashMap 。
d)使用集合的时候使用泛型。
e)使用迭代器来循环集合。

线程安全的集合

喂,SHE
喂(V,Vector)
S(Stack)
H(Hashtable)
E(Enumeration)

生产者消费者模型

在现实中许多线程问题都属于生产者消费者模型。较低级的解决方式是用wait和notify,更好的办法是用Semaphore 或者 BlockingQueue

生产者消费者模型,准确地说应该是“生产者-消费者-仓储”模型,有以下几点需要明确:
1、生产者仅仅在仓储未满时候生产,仓满则停止生产。
2、消费者仅仅在仓储有产品时候才能消费,仓空则等待。
3、当消费者发现仓储没产品可消费时候会通知生产者生产。
4、生产者在生产出可消费产品时候,应该通知等待的消费者去消费。

生产者消费者问题是研究多线程程序时绕不开的经典问题之一,它描述是有一块缓冲区作为仓库,生产者可以将产品放入仓库,消费者则可以从仓库中取走产品。解决生产者/消费者问题的常用方法是采用某种机制保护生产者和消费者之间的同步,常用的同步方法是采用信号或加锁机制,保证资源在任意时刻至多被一个线程访问。

如下代码示例,使用了BlockingQueue阻塞队列来线程间共享数据:

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;

/**
 * java多线程模拟生产者消费者问题
 * 
 * ProducerConsumerApplication是主类,Producer生产者,Consumer消费者,Product产品,Storage仓库
 */
public class ProducerConsumerApplication {
    public static void main(String[] args) {
        ProducerConsumerApplication pc = new ProducerConsumerApplication();
        Storage s = pc.new Storage();
        Producer p1 = pc.new Producer("生产者1", s);
        Producer p2 = pc.new Producer("生产者2", s);
        Consumer c1 = pc.new Consumer("消费者1", s);
        Consumer c2 = pc.new Consumer("消费者2", s);
        Consumer c3 = pc.new Consumer("消费者3", s);

        ExecutorService service = Executors.newCachedThreadPool();
        service.submit(p1);
        service.submit(p2);
        service.submit(c1);
        service.submit(c2);
        service.submit(c3);
    }

    /**
     * 消费者
     */
    class Consumer implements Runnable {
        private String name;
        private Storage s = null;

        public Consumer(String name, Storage s) {
            this.name = name;
            this.s = s;
        }

        public void run() {
            try {
                while (true) {
                    System.out.println(name + "准备消费产品.");
                    Product product = s.pop();
                    System.out.println(name + "已消费(" + product.toString() + ").");
                    Thread.sleep(500);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 生产者
     */
    class Producer implements Runnable {
        private String name;
        private Storage s = null;

        public Producer(String name, Storage s) {
            this.name = name;
            this.s = s;
        }

        public void run() {
            try {
                while (true) {
                    Product product = new Product((int) (Math.random() * 10000)); // 产生0~9999随机整数
                    System.out.println(name + "准备生产(" + product.toString() + ").");
                    s.push(product);
                    System.out.println(name + "已生产(" + product.toString() + ").");
                    Thread.sleep(500);
                }
            } catch (InterruptedException e1) {
                e1.printStackTrace();
            }
        }
    }

    /**
     * 仓库,用来存放产品
     */
    public class Storage {
        BlockingQueue queues = new LinkedBlockingQueue(10);

        /**
         * 生产
         */
        public void push(Product p) throws InterruptedException {
            queues.put(p);
        }

        /**
         * 消费
         */
        public Product pop() throws InterruptedException {
            return queues.take();
        }
    }

    /**
     * 产品
     */
    public class Product {
        private int id;

        public Product(int id) {
            this.id = id;
        }

        public String toString() {
            // 重写toString方法
            return "产品:" + this.id;
        }
    }
}

数组转List

List list=java.util.Arrays.asList(arr);

Swagger

Swagger

WebService提供了WSDL来描述提供的WS调用接口,还提供了其它工具来生成WS客户端代码。REST则可以使用Swagger规则来描述Restful接口,以及通过Swagger UI来测试Restful接口。

Swagger2是通过在接口上提供一系统注解来描述接口,而Swagger3则是通过一个swagger规范的json文件来描述。

Swagger使用指南
https://my.oschina.net/dlam/blog/808315
swagger2常用注解说明
https://blog.csdn.net/u014231523/article/details/76522486

Spring boot项目,使用maven打包包含本地jar
http://www.jianshu.com/p/4f88837945c9

swagger2markup

1.单独启动要输出文档的swagger服务。
2.在@Test中编写Swagger2MarkupConverter部分代码,或在pom.xml中配置。
3.运行mvn test,生成.adoc文件。
4.在Maven视图中,运行Plugins下的asciidoctor:process-asciidoc,生成html文件。
http://blog.didispace.com/swagger2markup-asciidoc/

Swagger 2.9.2

出现Unable to infer base url,java.lang.NumberFormatException: For input string: “”错误,解决方法:在@ApiImplicitParam中增加 example参数值。
并且 2.9.2版本界面风格也发生了变化,需要点击 Try it out,再执行 Execute。

注解

@ApiIgnore 或 @ApiParam(hidden = true) 加在参数前,可以使用参数不被显示在swagger ui中。

带附件的接口

只在附件参数前加@ApiParam注解,或者只添加@RequestBody注解,但没有字段注释。

@ApiOperation(value = "注册用户", notes = "")
@ApiImplicitParams({
        @ApiImplicitParam(name = "userName", value = "用户名", paramType = "query", dataType = "string", required = false),
        @ApiImplicitParam(name = "mobilePhone", value = "手机号", paramType = "query", dataType = "string", required = true)
})
@RequestMapping(value = "/registUser", method = RequestMethod.POST)
ResponseProto registUser(String mobilePhone, String userName,@ApiParam(value = "头像", required = true) MultipartFile image) {
}

进程管理工具

pm2

pm2 = P (rocess) M (anager)2,是可以用于生产环境的Nodejs的进程管理工具,它内置一个负载均衡,可以保证服务不会中断一直在线,并且提供0秒reload功能,还有其他一系列进程管理、监控功能。

pm2的安装和使用

npm install -g pm2
pm2 start app.js
pm2 startup, pm2 save 两条命令,用来保证服务器启动时,pm2管理的程序自动运行。
pm2 reload all
pm2 restart [id]
pm2 logs [id]
pm2 delete [id]
pm2 list
pm2 show [id] 或者 # pm2 info [id]  #查看进程详细信息

更多pm2常用的命令用法介绍
http://i5ting.github.io/node-deploy-practice/
http://www.111cn.net/sys/linux/120062.htm
http://www.pangjian.info/2016/12/02/deploy-nodejs-pm2-1/?utm_medium=referral

可以通过自定义启动文件,来管理jar包。创建xx.json,示例内容如下:

{
    "name": "cogrowth-api",
    "script": "java",
    "args": [
        "-Xms64m",
        "-Xmx512m",
        "-Dspring.profiles.active=pub",
        "-jar",
        "cogrowth-api-1.0.jar",
    ],
    "exec_interpreter": "",
    "exec_mode": "fork"
}
pm2 start xx.json

exec_interpreter: NodeJs解析器,本文不适用
exec_mode: 执行模式[cluster|fork]这个针对NodeJs应用的配置,非NodeJs应用统一fork
日志路径在~/.pm2/logs,stdout和stderr被分开存放,程序中的所有stdout和stderr都被收集方便查错。

Java开发库

Guava

Guava是一种基于开源的Java库,其中包含谷歌正在由他们很多项目使用的很多核心库。这个库是为了方便编码,并减少编码错误。这个库提供用于集合,缓存,支持原语,并发性,常见注解,字符串处理,I/O和验证的实用方法。

生成PDF

freemarker

使用freemarker从模板+数据,生成html文件。

Configuration cfg = new Configuration(Configuration.VERSION_2_3_28);
        try {
            cfg.setClassForTemplateLoading(this.getClass(), "/static/");
            Template template=cfg.getTemplate("content.html");
            PrintWriter pw = new PrintWriter(new File(destPath));
            template.process(params, pw);
            pw.close();//关闭流
        } catch (IOException e) {
            e.printStackTrace();
        } catch (TemplateException e) {
            e.printStackTrace();
        }

生成Docker镜像的Dockerfile

FROM kenkoooo/ubuntu-openjdk8

RUN apt-get update \
    && apt-get install -y \
        curl \
        xfonts-base \
        xfonts-75dpi \
    && apt-get clean

#安装wkhtmltox(0.12.5版本没有生成目录,改用wkhtmltox_0.12.6版本)
#可以在https://builds.wkhtmltopdf.org/0.12.6-dev/中寻找对应系统的版本
RUN curl "https://builds.wkhtmltopdf.org/0.12.6-dev/wkhtmltox_0.12.6-0.20180618.3.dev.e6d6f54.xenial_amd64.deb" -L -o "wkhtmltopdf.deb"
RUN dpkg -i ./wkhtmltopdf.deb
RUN apt-get install -f
RUN rm -rf wkhtmlto*
ENV LANG C.UTF-8

#安装chrome-headless
RUN apt-get install --assume-yes libxss1 libappindicator1 libindicator7
RUN wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
RUN apt install --assume-yes ./google-chrome*.deb
RUN rm -rf google-chrome*

#安装chrome-driver
#可以在线下载chromedriver_linux64.zip
#RUN wget https://chromedriver.storage.googleapis.com/73.0.3683.68/chromedriver_linux64.zip
#也可以拷贝下载好的chromedriver_linux64.zip
COPY chromedriver_linux64.zip .
RUN unzip chromedriver_linux64.zip
RUN cp chromedriver /usr/local/bin/chromedriver
RUN chmod 755 /usr/local/bin/chromedriver
RUN rm -rf chromedriver_linux64*

CPU架构

iPhone CPU架构

架构对应的设备:

 armv6 设备: iPhone, iPhone2, iPhone3G, 第一代、第二代 iPod Touch
 armv7 设备: iPhone3GS, iPhone4, iPhone4S;iPad, iPad2, iPad3(The New iPad), iPad mini;iPod Touch 3G, iPod Touch4
 armv7s设备: iPhone5, iPhone5C, iPad4(iPad with Retina Display)
 arm64 设备: iPhone5S, iPad Air, iPad mini2(iPad mini with Retina Display)

Android CPU架构

ABI:ApplicationBinary Interface 应用程序二进制接口,定义了二进制文件(尤其是.so文件),如何运行在相应的系统平台上,从使用的指令集、内存对齐到可用的系统函数库。
EABI:Embedded application binary interface 嵌入式应用二进制接口。

armeabi: 第5代、第6代的ARM处理器,是针对所有的arm cpu,只要支持arm的指令集就可以,早期的手机用的比较多。v7a,v8a是针对arm后来出的指令集版本制定的cpu如符合arm7指令集的cpu。
armeabi-v7a: 第7代及以上的 32位ARM 处理器,是针对有浮点运算或高级扩展功能的arm v7 cpu,2011年15月以后的生产的大部分Android设备都使用它.
arm64-v8a: 第8代、64位ARM处理器,很少设备,三星 Galaxy S6是其中之一。
x86: 平板、模拟器用得比较多。
x86_64: 64位的平板。

项目包含总结

如果项目只包含了 armeabi,那么在所有Android设备都可以运行;
如果项目只包含了 armeabi-v7a,除armeabi架构的设备外都可以运行;
如果项目只包含了 x86,那么armeabi架构和armeabi-v7a的Android设备是无法运行的; 
如果同时包含了 armeabi, armeabi-v7a和x86,所有设备都可以运行,程序在运行的时候去加载不同平台对应的so,这是较为完美的一种解决方案,同时也会导致包变大。

例如,如果只想支持armeabi-v7a,需要在gradle中配置

    defaultConfig {
        ndk {
            abiFilters "armeabi-v7a"
        }
    }

默认情况下,打包后会自动生成armeabi 到 x86的所有文件夹。这就有可能导致一些x86的设备因为在x86文件夹下找不到so文件,而使应用崩溃。

Spring Cloud

Spring官方在spring boot的基础上,对Netflix开源组件进一步封装,提供了一个名为Spring Cloud的开源的一套云应用开发工具,一个非常优秀的服务治理框架。另一个非常优秀的服务治理框架 Dubbo。
Spring Cloud专注于提供良好的开箱即用经验的典型用例和可扩展性机制覆盖。

Netflix公司和Pivotal公司

Netflix是一家视频网站,该网站上的美剧应该是最火的。Netflix的微服务大规模的应用,在技术上毫无保留的把一整套微服务架构核心技术栈开源了出来,叫做Netflix OSS。

Pivotal把Netflix开源的一整套核心技术产品线做了一系列的封装,就变成了Spring Cloud,Spring Cloud到现在为止不只有Netflix提供的方案可以集成,但其中Netflix是最成熟的。

Spring Cloud微服务架构

在Spring Cloud微服务系统中,一种常见的负载均衡方式是:客户端的请求首先经过负载均衡(zuul、Ngnix),再到达服务网关(zuul集群),然后再到具体的服务。服务统一注册到高可用的服务注册中心集群,服务的所有的配置文件由配置服务管理,配置服务的配置文件放在git仓库,方便开发人员随时改配置。

Spring Cloud微服务架构中的几个基础服务治理组件:

  • 服务注册中心:Eureka Server
  • 注册服务:Eureka Client
  • 发现服务:Discovery Client
  • 负载均衡:Load Balance、RestTemplate、Ribbon,Feign
  • 断路器:Hystrix,另外Feign自带断路器功能。断路打开后,可以避免连锁故障,fallback方法可以直接返回一个固定值。Turbine可以汇聚监控信息,并提供给Hystrix Dashboard集中展示和监控。
  • 分布式配置中心:Spring Cloud Config,它支持配置服务放在配置服务的内存中(即本地),也支持放在远程Git仓库中。两个角色,config server config client。
  • 消息总线:Spring Cloud Bus,将分布式节点加入消息总线,利用轻量的消息代理(message broker 经纪人)使得应用程序可以高效地解耦通信过程。在众多的开源消息中间件产品中,当前版本的spring cloud bus仅支持两款中间件产品:rabbitmq和kafka两个binder,也可以自己写binder扩展。
  • Api gateway:微服务场景下,将细粒度的服务组合起来提供一个粗粒度的服务,所有请求都导入一个统一的入口,那么整个服务只需要暴露一个api,对外屏蔽了服务端的实现细节,也减少了客户端与服务器的网络调用次数。与业务关系并不大的通用处理逻辑可以从Api gateway中剥离出来,Api gateway仅仅负责服务的编排与结果的组装。这样对一些后端微服务进行了代理,避免了各个后端服务独立管理CORS和验证问题。
  • 路由网关转发和过滤器:zuul,zuul还能过滤做一些安全验证,zuul默认和Ribbon结合实现了负载均衡的功能。Zuul是Netflix 提供的一个开源组件,是致力于在云平台上提供动态路由,监控,弹性,安全等边缘服务的框架。zuul静态路由配置规则:
    分布式系统中未使用注册服务时,service1的名称可更换,下面的配置中.path中的**会被添加到.url的后面。若不配置.url,可直接使用service1作为请求路径。

    zuul.routes.service1.url=http://localhost:8002/storage/
    zuul.routes.service1.path=/proxy/**
    
  • 动态路由
    动态路由需要达到配置持久化和动态刷新的效果。即不仅要能满足从spring的配置文件properties加载路由信息,还需要从数据库加载配置。另外一点是,在容器启动后,能动态刷新内存中的路由信息,达到不停机维护路由信息的效果。
  • 数据监控,Spring Boot Actuator 提供了强大的应用自省功能,提供了丰富的 Endpoints 的信息,覆盖 Spring Boot 应用程序运行的方方面面。这些指标都是以 JSON 接口数据的方式呈现。而使用 Spring Boot Admin 可以实现这些 JSON 接口数据的界面展现,结合可视化的 Spring Boot Admin 管理界面,一切显得如此“高大上”。
  • 链路追踪:Sleuth。

Ribbon、Feign与Nginx

Feign与Ribbon区别之一是调用方式不同。Ribbon需要自己构建http请求使用RestTemplate发送给其它服务,步骤繁琐。Feign则是在Ribbon的基础上进行了改进,采用接口的方式将需要调用的其他服务的方法定义成抽象方法。Feign 是一个声明web服务客户端,Feign 是一个使用起来更加方便的HTTP 客戶端,使用起来就像是调用自身工程的方法。Feign包含了ribben,feign是远程调用的,ribbon是做负载均衡。

Nginx是服务端负载均衡,即请求由 nginx 服务器端进行转发。负载均衡、反向代理,代理后端服务器。隐藏真实地址,防火墙,不能外网直接访问,安全性较高。

Ribbon是客户端负载均衡, 是从 eureka 注册中心服务器端上获取服务注册信息列表,缓存到本地,然后在本地实现轮询负载均衡策略,即在客户端实现负载均衡。

应用场景的区别:Nginx 适合于服务器端实现负载均衡,比如 Tomcat 。Ribbon 适合与在微服务中 RPC 远程调用实现本地服务负载均衡,比如 Dubbo、SpringCloud 中都是采用本地负载均衡。

Cloud与Boot与微服务的关系

微服务是一种架构的理念,提出了微服务的设计原则,从理论为具体的技术落地提供了指导思想。
Spring Boot是一套快速配置脚手架,可以基于Spring Boot快速开发单个微服务。Spring Cloud是一个基于Spring Boot实现的服务治理工具包。Spring Boot专注于快速、方便集成的单个微服务个体,Spring Cloud关注全局的服务治理框架。Spring Boot/Cloud是微服务实践的最佳落地方案。

Cloud版本变化

  • Spring Cloud Finchley 在2018年 06 月 19 日正式发布,这次的重大发布主要带来了以下 4 项重大更新。
    (1)新增 Spring Cloud Gateway 组件
    Spring Cloud Gateway 是一个基于 Spring Webflux 和响应式 Netty 的下一代 API 网关,用来替换 Spring Cloud Netflix Zuul。它提供了更加简单的动态路由,以及针对每个路由的过滤器(如地址重写、断路器、添加/删除请求头、限流和安全等)。
    (2)新增 Spring Cloud Function 组件
    Spring Cloud Function 的主要功能如下:
    通过一系列函数推进业务逻辑的实现;
    将业务逻辑的开发生命周期从任何特定运行目标中分离,以便相同的代码可以作为一个 Web 端点、一个流处理器或一个任务来运行;
    支持一个跨 serverless providers 的统一编程模型,并拥有独立运行的能力(本地或 PaaS 平台);
    支持在 serverless providers 上面启用 Spring Boot 特性,如自动配置、依赖注入、指标等;
    (3)兼容 Spring Boot 2.0.x
    Finchley 版本是基于 Spring Boot 2.0.x 构建的,官方建议不要与 Spring Boot 1.5.x 及之前的版本一起工作。
    (4)最低支持 JDK 1.8
    JDK 门槛提高了,1.8 毕竟是现在的主流。
  • Camden。
  • Dalston将于 2018 年 12 月结束生命周期。
  • Edgware伴随着 Spring Boot 1.5.x 的结束而结束生命周期。

参考:
《Spring Cloud微服务实战》
http://blog.didispace.com/
http://blog.csdn.net/forezp/article/details/70148833
http://blog.csdn.net/u010066934/article/details/54232622

Spring Cloud 文档
http://cloud.spring.io/spring-cloud-static/spring-cloud.html#_features

PiggyMetrics微服务编译运行解析
http://blog.csdn.net/nihaomanihao11/article/details/73822681
http://blog.csdn.net/rickiyeat/article/details/60792925

中小型互联网公司微服务实践-经验和教训
http://www.ityouknow.com/springcloud/2017/10/19/micro-service-practice.html

Spring Cloud各个微服务之间用HTTP通信的效率的讨论

Spring cloud的http从几个方面可以优化:

1、换成okhttp3。
2、应用中不要做异步传输,防止异步等待,如果遇到异步场景,一定要利用好消息队列和缓存。
3、如果http1.1,有些场景利用keep-alive,减少连接损耗。
4、可以考虑尝试HTTP/2。替换原有的Jackson序列化为更快的或者更适合的序列化方式(例如fastJson,kryo);还有http连接池替换实现等都会有可观的性能提升。

一般场景下spring cloud的性能足以满足大多数需求,最终要看服务拆分是否合理和业务场景的实际需求。
rpc固然速度比http快,但是稳定性、数据传输的可靠性、熔断保护、更方便的链路监控和追踪,这时候HTTP的优势就体现了。
http传输内容上比rpc多了几十字节的头,对于一般都是几k到几十上百k的数据,几十个字节的头影响不大。
如果使用feign,会默认加keepalive也是长链接,就多了http的20字节报文头,但是开发效率却提升了几个档次。
目前很多大型项目多语言共存,http是最通用的协议,可以很好地解决跨语言跨平台兼容性。比如Dubbo协议,无法简单地实现跨语言。http交互只是协议比较重,但不会慢太多,追求单机极致性能的确可以考虑换协议。
微服务的一个重要理念就是水平扩展,慢这个问题通过业务设计还有多实例部署可以很好地提速。http协议很成熟,并且特点熟知:1.支持客户/服务器模式。2.简单快速。3.灵活。4.无连接。5.无状态。也是一个速度与开发成本还有兼容性相互妥协兼顾的结果。

https://www.zhihu.com/question/270355472

spring cloud与dubbo性能对比
http://www.cnblogs.com/chen110xi/p/6349580.html

Eureka配置

eureka.client.register-with-eureka: 表示是否将自己注册到Eureka Server, 默认为true。 如果当前应用就是Eureka Server,则设为 false。
eureka.client.fetch-registry: 表示是否从Eureka Server获取注册信息,默认为true。如果这是一个单点的 Eureka Server,不需要同步其他节点的数据,可以设为false。
eureka.client.serviceUrl.defaultZone:在client端可以指定多个eureka服务,用逗号分隔。
https://www.jianshu.com/p/67fa6bbdbadf

在eureka中注册的服务能互通的前提是各服务间的网络本来就是通的。
主机服务程序在eureka中默认使用哈希值代替ip来作为识别码,可以配置eureka.instance.preferIpAddress=true来指定使用IP来作为识别码。

是纯正的 servlet 应用,需部署到web容器中。

Hystrix

使用feign时断路器不起作用,检查在引入了openfeign依赖后,不要再引入ribbon、hystrix。

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>2.1.0.RELEASE</version>
</dependency>

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
<version>2.1.0.RELEASE</version>
</dependency>

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>

gateway 和 serucity

Spring Cloud Gateway是Spring Cloud官方推出的第二代网关框架,取代Zuul网关。网关作为流量的,在微服务系统中有着非常作用,网关常见的功能有路由转发、权限校验、限流控制等作用。
Spring Security致力于为Java应用提供认证和授权管理。它是一个强大的,高度自定义的认证和访问控制框架,这句话包括两个关键词:Authentication(认证)和 Authorization(授权,也叫访问控制)。

Mysql

Mysql存储引擎

MyISAM

MyISAM:官方提供的存储引擎,基于传统的ISAM类型,ISAM是Indexed Sequential Access Method (有索引的顺序访问方法) 的缩写,它是存储记录和文件的标准方法。不是事务安全的,不支持外键。

  • 查询效率高,执行大量的查询MyISAM比较适合。
  • 不支持事务,回滚将造成不完全回滚,不具有原子性。
  • 不支持外键
  • 只支持表级锁,不支持行锁定,这样同一个表上的读锁和写锁是互斥的,MyISAM并发读写时如果等待队列中既有读请求又有写请求,默认写请求的优先级高,即使读请求先到,所以MyISAM不适合于有大量查询和修改并存的情况,那样查询进程会长时间阻塞。因为MyISAM是锁表,所以某项读操作比较耗时会使其他写进程饿死。在操作myisam表时,select,update,delete,insert语句都会给表自动加锁,如果加锁以后的表满足insert并发的情况下,可以在表的尾部插入新的数据。表锁开销小,加锁快,不会出现死锁,粒度大,锁冲突概率高,并发效率低。
  • 支持全文搜索,全文索引是指对char、varchar和text中的每个词(停用词除外)建立倒排序索引。MyISAM的全文索引其实没啥用,因为它不支持中文分词,必须由使用者分词后加入空格再写到数据表里,而且少于4个汉字的词会和停用词一样被忽略掉。

DELETE 表时,先drop表,然后重建表
MyISAM 表被存放在三个文件:frm 文件存放表格定义,数据文件是MYD (MYData) ,索引文件是MYI (MYIndex)引伸。
跨平台很难直接拷贝
MyISAM中可以使AUTO_INCREMENT类型字段建立联合索引
表格可以被压缩。
MyISAM支持GIS数据,InnoDB不支持。GIS数据是指空间数据对象:Point,Line,Polygon,Surface等。
MyISAM保存表的具体行数,在查询不带where时,直接返回保存的行数。所以没有where的count(*)使用MyISAM要比InnoDB快得多。因为MyISAM内置了一个计数器,count(*)时它直接从计数器中读;而InnoDB必须扫描全表。所以在InnoDB上执行count(*)时一般要伴随where,且where中要包含主键以外的索引列。这里特别强调“主键以外”,是因为InnoDB中primary index是和raw data存放在一起的,而secondary index则是单独存放,然后有个指针指向primary key。所以只是count(*)的话使用secondary index扫描更快,而primary key则主要在扫描索引同时要返回raw data时的作用较大。

InnoDB

InnoDB,支持外键、行锁、事务是他的最大特点。如果有大量的update和insert,建议使用InnoDB,特别是针对多个并发和QPS较高的情况。5.5版本后成为默认存储引擎。

  • 支持ACID事务,InnoDB的AUTOCOMMIT默认是打开的,即每条SQL语句会默认被封装成一个事务自动提交,这样会影响速度,所以最好是把多条SQL语句显示放在begin和commit之间,组成一个事务去提交。
  • 支持外键
  • 支持行级锁

update、insert、delete自动加隐式排它锁,或者select for update显示排它锁。不加锁读取

在Mysql5.6版本开始支持FullText类型的索引,5.7.6版本支持了中文索引。
DELETE 表时,是一行一行的删除
InnoDB 把数据和索引存放在表空间里面
跨平台可直接拷贝使用
InnoDB中必须包含AUTO_INCREMENT类型字段的索引
表格很难被压缩
InnoDB的主键范围更大,最大是MyISAM的2倍
innodb属于索引组织表,有两种存储方式,共享表空间存储和多表空间存储,两种存储方式的表结构和myisam一样,以表名开头,扩展名是.frm。如果使用共享表空间,那么所有表的数据文件和索引文件都保存在一个表空间里,一个表空间可以有多个文件,通过innodb_data_file_path和innodb_data_home_dir参数设置共享表空间的位置和名字,一般共享表空间的名字叫ibdata1-n。如果使用多表空间,那么每个表都有一个表空间文件用于存储每个表的数据和索引,文件名以表名开头,以.ibd为扩展名。

TokuDB

第三方开发的存储引擎,写速度快,

支持数据压缩存储,适合访问频率低的数据,历史数据归档。不适合大量读取

支持在线添加索引不影响读写操作,

3.对比选择:
MyISAM相对简单,在效率上要优于InnoDB。从效率上来看,如果系统读多写少,对原子性要求低,那么MyISAM最好的选择,且MyISAM恢复速度快,可直接用备份覆盖恢复。如果系统读少写多,尤其是高并发写入,InnoDB就是首选了。

MyISAM与InnoDB两者之间区别与选择,详细总结,性能对比
https://www.cnblogs.com/y-rong/p/8110596.html

数据库锁级别

  • 表级锁:开销小,加锁快,不会出现死锁,粒度大,锁冲突概率高,并发效率低。
  • 行级锁:开锁大,加锁慢,可能出现死锁,粒度小,锁冲突概率低,并发效率高。
  • 共享锁:读锁。其它事务可读不可写。
  • 排它锁:写锁。其它事务不可读不可写。

存储过程与函数

在Mysql服务器中运行,减少网络数据传输。

  • 存储过程实现复杂数据库操作,是独立的sql语句调用。与数据库实现绑定,降低了程序的可移植性。
  • 函数实现针对性强的功能,例如按特殊策略求和。用户定义函数不能修改数据库全局状态。

Mysql8.0新特性

  • 默认字符集修改为了utf-8
  • 增加了隐式索引,隐藏后的索引不会被查询优化器使用,这个特性可用了性能调试。
  • 通用表表达式,复杂查询中的嵌入语句表达更清晰。
  • 窗口函数,与集合函数类似,可实现新的查询方式。不会将多行查询结果合并,不需要group by

Mysql索引

需要额外磁盘空间来保存索引,插入更新删除会增加额外的开销,索引适合读多写少的场景。

索引类型:

  • 唯一索引:值可为NULL。
  • 主键索引:不允许出现空值。
  • 普通索引
  • 联合索引:最左原则即查询条件中的字段必须符合在索引字段中从左开始连续
  • 全文索引:只能在char、varchar、text类型字段上使用。

索引实现:

  • B-Tree:mysql中普遍使用B+Tree索引,但在实现方式上又分为聚簇索引和非聚簇索引。
  • R-Tree
  • Hash
  • FullText:倒排索引,关键字与文档关系。

聚簇索引和非聚簇索引

  • 聚簇索引:指主索引文件和数据文件为同一份文件,聚簇索引主要用在Innodb存储引擎中。在该索引实现方式中B+Tree的叶子节点上的data就是数据本身,key为主键。
  • 非聚簇索引:指B+Tree的叶子节点上的data并不是数据本身,而是数据存放的地址。主索引和辅助索引没啥区别,只是主索引中的key一定得是唯一的。主要用在MyISAM存储引擎中。非聚簇索引比聚簇索引多了一次读取数据的IO操作,所以查找性能上会差。

utf8mb4

MySQL在5.5.3之后增加了utf8mb4的编码,mb4就是most bytes 4的意思,专门用来兼容四字节的unicode。
utf8mb4是utf8的超集,为了节省空间,一般情况下使用utf8就够了。

int(11)

int(n)中,n表示查询显示时的最小宽度,即最小的字符数。若不够最小长度,则前面补0。

自定义函数

转换三点分版本号为纯数字,以便于比较版本大小:

CREATE DEFINER=`root`@`%` FUNCTION `formatVersion3`(version varchar(11)) RETURNS varchar(9) CHARSET latin1
BEGIN
	declare result VARCHAR(9);
	set result = CONCAT(LPAD(SUBSTRING_INDEX(SUBSTRING_INDEX(version, '.', 1), '.', -1), 3, '0'),
			    LPAD(SUBSTRING_INDEX(SUBSTRING_INDEX(version, '.', 2), '.', -1), 3, '0'),
			    LPAD(SUBSTRING_INDEX(SUBSTRING_INDEX(version, '.', 3), '.', -1), 3, '0'));
	RETURN result;
END