微服务架构:Spring-Cloud 组件与集群

标签: Spring Cloud  项目架构

Spring-Cloud项目的搭建

因为spring-cloud是基于spring-boot项目来的,所以我们项目得是一个spring-boot项目,至于spring-boot项目,
这里我们先不讨论,这里要注意的一个点是spring-cloud的版本与spring-boot的版本要对应下图:

 

spring-boot

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.0.2.RELEASE</version>
</parent>

spring-cloud:

 

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>Finchley.SR2</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement> 

eureka是什么?

eurekaNetflflix的子模块之一,也是一个核心的模块,eureka里有2个组件,一个是EurekaServer(一个独立的项
) 这个是用于定位服务以实现中间层服务器的负载平衡和故障转移,另一个便是EurekaClient(我们的微服务)
它是用于与Server交互的,可以使得交互变得非常简单:只需要通过服务标识符即可拿到服务。
spring-cloud的关系:
 
Spring Cloud 封装了 Netflix公司开发的 Eureka 模块来实现服务注册和发现(可以对比Zookeeper)
Eureka 采用了 C-S 的设计架构。Eureka Server 作为服务注册功能的服务器,它是服务注册中心。
而系统中的其他微服务,使用 Eureka 的客户端连接到 Eureka Server并维持心跳连接。这样系统的维护人员就可
以通过 Eureka Server 来监控系统中各个微服务是否正常运行。SpringCloud 的一些其他模块(比如Zuul)就可以
通过 Eureka Server 来发现系统中的其他微服务,并执行相关的逻辑。
如何使用?

spring-cloud项目里面加入依赖:
单机版 eureka 服务端依赖和配置+启动类
 
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
    <version>2.0.2.RELEASE</version>
</dependency>

 

server:
  port: 8081
eureka:
  server:
   enable-self-preservation: false  #关闭自我保护机制
   eviction-interval-timer-in-ms: 4000 #设置清理间隔(单位:毫秒 默认是60*1000)
  instance:
    hostname: eureka-server8001 #修改的本机hosts,可忽略
  client:
    registerWithEureka: false #不把自己作为一个客户端注册到自己身上
    fetchRegistry: false  #不需要从服务端获取注册信息(因为在这里自己就是服务端,而且已经禁用自己注册了)
    serviceUrl:
     defaultZone: http://${eureka.instance.home}:${eureka.port}/eureka #可填写自己具体的local host + port

    

@SpringBootApplication
@EnableEurekaServer
public class EurekaServer8001 {
    public static void main(String[] args) {
        SpringApplication.run(EurekaServer8001.class);
    }
}

eureka 客户端

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    <version>2.0.2.RELEASE</version>
</dependency>
server:
  port: 8082
eureka:
  client:
    serviceUrl:
      defaultZone: http://${eureka.instance.home}:${eureka.port}/eureka 
#可填写自己具体的local host + port
#eureka服务端提供的注册地址 参考服务端配置的这个路径
  instance:
    instance-id: order8082 #此实例注册到eureka服务端的唯一的实例ID
    prefer-ip-address: true #是否显示IP地址
    leaseRenewalIntervalInSeconds: 10 #eureka客户需要多长时间发送心跳给eureka服务器,表明它仍然活着,默认为30 秒 (与下面配置的单位都是秒)
    leaseExpirationDurationInSeconds: 30 #Eureka服务器在接收到实例的最后一次发出的心跳后,需要等待多久才可以将此实例删除,默认为90秒

spring:
  application:
    name: order-server #此实例注册到eureka服务端的name
@SpringBootApplication
@EnableEurekaClient
public class OrderApplication6001 {
    public static void main(String[] args) {
        SpringApplication.run(OrderApplication6001.class);
    }
}

集群 eureka 服务端依赖和配置+启动类

 这是我自己的集群环境和修改本机的hosts文件

server:
  port: 8001
eureka:
  server:
   enable-self-preservation: false  #关闭自我保护机制
   eviction-interval-timer-in-ms: 4000 #设置清理间隔(单位:毫秒 默认是60*1000)
  instance:
    hostname: eureka-server8001
  client:
    registerWithEureka: false #不把自己作为一个客户端注册到自己身上
    fetchRegistry: false  #不需要从服务端获取注册信息(因为在这里自己就是服务端,而且已经禁用自己注册了)
    serviceUrl:
     defaultZone: http://eureka-server8002:8002/eureka,http://eureka-server8003:8003/eureka,http://eureka-server8004:8004/eureka,http://eureka-server8005:8005/eureka

启动 eureka 集群后的主页面

红色说明没有本机eureka信息是因为eureka的server会把自己的注册信息与其他的server同步, 所以这里我们不需要注册到自己身上,因为另外两台服务器会配置本台服务器。

客户端 的注册配置(一部分)注册到所有 服务端机器

defaultZone: http://eureka-server8001:8001/eureka/,http://eureka-server8002:8002/eureka/,http://eureka-server8003:8003/eureka/,http://eureka-server8004:8004/eureka/,http://eureka-server8005:8005/eureka/ 
我们这里只截取了要改动的那一部分。 就是 原来是注册到那一个地址上面,现在是要写五个eureka注册地址,但
是不是代表他会注册五次,因为我们eureka server的注册信息是同步的,这里只需要注册一次就可以了,但是为
什么要写五个地址呢。因为这样就可以做到高可用的配置:打个比方有5台服务器。但是突然宕机了一台, 但是其
4台还健在,依然可以注册我们的服务,换句话来讲, 只要有一台服务还建在,那么就可以注册服务,这里 需要
理解一下。
这里效果图就不发了, 和之前单机的没什么两样,只是你服务随便注册到哪个eureka server上其他的eureka
server上都有该服务的注册信息。
ribbon 和 feign 组件
 
首先先看下ribbon 和 fegin 的定义

 

Spring Cloud Ribbon是基于Netflix Ribbon实现的一套客户端 负载均衡的工具。
简单的说,RibbonNetflix发布的开源项目,主要功能是提供客户端的软件负载均衡算法,将Netflix的中间层服
务连接在一起。Ribbon客户端组件提供一系列完善的配置项如连接超时,重试等。简单的说,就是在配置文件中
列出Load Balancer(简称LB)后面所有的机器,Ribbon会自动的帮助你基于某种规则(如简单轮询,随机连接
等)去连接这些机器。我们也很容易使用Ribbon实现自定义的负载均衡算法。
 

 

Feign是一个声明式WebService客户端。使用Feign能让编写Web Service客户端更加简单, 它的使用方法是定义一
个接口,然后在上面添加注解,同时也支持JAX-RS标准的注解。Feign也支持可拔插式的编码器和解码器。Spring
CloudFeign进行了封装,使其支持了Spring MVC标准注解和HttpMessageConvertersFeign可以与Eureka
Ribbon组合使用以支持负载均衡。
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
    <version>2.0.2.RELEASE</version>
</dependency>

下面通过两张图看下服务端的负载均衡和客户端的负载均衡有什么区别

服务端的负载均衡以Nginx 为例  客户端发起请求后通过算法分配到具体的某一台服务器

客户端的负载均衡以ribbon 为例 客户端先通过算法得到需要分配的某一台服务器再发起请求

我们先看下IRule 的实现者的负载均衡策略

我们按照客户端负载均衡的图片看下代码

order6001/order6002

//控制器
@RestController
public class OrderController {

    @RequestMapping("/getOrder")
    @ResponseBody
    public Object getOrder(){
        Map<String,String> map = new HashMap<>();
        map.put("/getOrder","port::order6001");
        return map;
    }
}
//启动类
@SpringBootApplication
@EnableEurekaClient
public class OrderApplication6001 {
    public static void main(String[] args) {
        SpringApplication.run(OrderApplication6001.class);
    }
}

application.yml 参考上面的客户端的集群环境

shopping-cart3001/shopping-cart3002/shopping-cart3003
//IRule 负载均衡策略配置
@Configuration
public class OrderRuleConfig {

    @Bean
    public IRule iRule(){
        return new RoundRobinRule();
    }
}
//控制器
@RestController
public class ShoppingCartController {

    @Autowired
    private ShoppingToOrderFeginClient shoppingToOrderFeginClient;

    @RequestMapping("/getShoppingCart")
    public Object getShoppingCart(){
        Map<String,String> map = new HashMap<>();
        map.put("/getShoppingCart","port::shopping-cart::3001");
        return map;
    }
    //使用Fegin 客户端 跨系统shopping调用order服务
    @RequestMapping("/ShoppingToOrderFeginClient")
    public Object getShoppingToOrderFeginClient(){
        return shoppingToOrderFeginClient.getOrderFeginClient();
    }


}
//Fegin 客户端配置
@FeignClient(name = "ORDER-SERVER")
public interface ShoppingToOrderFeginClient {
    // 类似使用  dubbo 在Order系统中暴露服务提供调用
    @RequestMapping("/getOrder")
    public Object getOrderFeginClient();
}
//启动类
@SpringBootApplication
@EnableEurekaClient
@RibbonClients({
        @RibbonClient(name = "ORDER-SERVER", configuration = OrderRuleConfig.class)
})
@EnableFeignClients
public class ShoppingCartApplication3001 {
    public static void main(String[] args) {
        SpringApplication.run(ShoppingCartApplication3001.class);
    }
}
user8000
@Configuration
public class OrderRuleConfig {

    @Bean
    public IRule iRule(){
        return new RoundRobinRule();
    }
}
@Configuration
public class ShoppingRuleConfig {

    @Bean
    public IRule iRule(){
        return new RoundRobinRule();
    }
}
@RestController
public class UserController {

    @Autowired
    private OrderFeginClient orderFeginClient;

    @Autowired
    private ShoppingFeginClient shoppingFeginClient;

    @RequestMapping("/getUserToOrder")// user 调用 order 服务 
    @ResponseBody
    public Object getUserToOrder(){
        return orderFeginClient.getOrderFeginClient();
    }

    @RequestMapping("/getUserToShoppingCart")// user 调用 ShoppingCart 服务
    @ResponseBody
    public Object getUserToShoppingCart(){
        return shoppingFeginClient.getShoppingCartFeginClient();
    }

    @RequestMapping("/getUserToShoppingCartToOrder") // user 调用 ShoppingCart 服务 ,ShoppingCart 再调用 Order服务
    @ResponseBody
    public Object getShoppingToOrderFeginClient(){
        return shoppingFeginClient.getShoppingToOrderFeginClient();
    }

}
@FeignClient(name = "ORDER-SERVER")
public interface OrderFeginClient {

    @RequestMapping("/getOrder")
    public Object getOrderFeginClient();
}
@FeignClient(name = "SHOPPING-SERVER")
public interface ShoppingFeginClient {

    @RequestMapping("/getShoppingCart")
    public Object getShoppingCartFeginClient();

    @RequestMapping("/ShoppingToOrderFeginClient")
    public Object getShoppingToOrderFeginClient();
}
@SpringBootApplication
@EnableEurekaClient
@RibbonClients({
        @RibbonClient(name = "ORDER-SERVER", configuration = OrderRuleConfig.class),
        @RibbonClient(name = "SHOPPING-SERVER", configuration = ShoppingRuleConfig.class)
})
@EnableFeignClients
public class UserApplication8000 {
    public static void main(String[] args) {
        SpringApplication.run(UserApplication8000.class);
    }
}

eureka主界面

测试访问,由于负载均衡策略设置的为轮询,所以结果是循环切换
http://localhost:8000/getUserToOrder
{"/getOrder":"port::order6001"}/{"/getOrder":"port::order6002"}

http://localhost:8000/getUserToShoppingCart

{"/getShoppingCart":"port::shopping-cart::3001"}/

{"/getShoppingCart":"port::shopping-cart::3002"}/

{"/getShoppingCart":"port::shopping-cart::3003"}

 

Hystrix 断路器 组件

hystrix是什么?
Hystrix是一个用于处理分布式系统的延迟和容错的开源库,在分布式系统里,许多依赖不可避免的会调用失败,比
如超时、异常等,Hystrix能够保证在一个依赖出问题的情况下,不会导致整体服务失败,避免级联故障,以提高分
布式系统的弹性。
断路器本身是一种开关装置,当某个服务单元发生故障之后,通过断路器的故障监控(类似熔断保险丝),向调
用方返回一个符合预期的、可处理的备选响应(FallBack),而不是长时间的等待或者抛出调用方无法处理的异
常,这样就保证了服务调用方的线程不会被长时间、不必要地占用,从而避免了故障在分布式系统中的蔓延,乃至
雪崩。
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
    <version>2.0.2.RELEASE</version>
</dependency>

如果通过 nginx负载均衡算法 分配到的那一台服务器刚好404了怎么办呢,难道一直卡着吗,后面的所有请求都在浪费系统资源,最终导致系统崩溃

在多系统相互调用之间a->b->c->d,如果b服务404了 ,是不是也卡住了 ,后面的所有请求也都在浪费系统资源,最终导致系统崩溃

Hystrix 降级配置
@RequestMapping("/getUserToOrder")
@ResponseBody
@HystrixCommand(fallbackMethod = "getUserToOrderFallback")
public Object getUserToOrder(){
    return orderFeginClient.getOrderFeginClient();
}

public Object getUserToOrderFallback(){
    return "断路器生效,系统正在维护中";
}

启动类添加@EnableHystrix 注解

当调用getUserToOrder方法时该服务发生故障 那么Hystrix可以做到降级的作用 会执行getUserToOrderFallback方法,可以快速的响应

Hystrix 熔断配置 application.yml 

hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 20000
      circuitBreaker:
        requestVolumeThreshold: 5 #当前线程请求超过5次 熔断配置 半开失败一次后尝试调用,全开失败五次进入fallbackMethod方法
#        sleepWindowInMilliseconds : 5000   时间

Hystrix 限流配置

hystrix通过线程池的方式来管理你的微服务调用,他默认是一个线程池(10大小) 管理你的所有微服务,你可以
给某个微服务开辟新的线程池:
threadPoolKey 就是在线程池唯一标识, hystrix 会拿你这个标识去计数,看线程占用是否超过了, 超过了就会直
接降级该次调用
比如, 这里coreSize给他值为5 那么假设你这个方法调用时间是3s执行完, 那么在3s内如果有超过2个请求进来的
话, 剩下的请求则全部降级
 
@RequestMapping("/getUserToOrder")
@ResponseBody
@HystrixCommand(fallbackMethod = "getUserToOrderFallback"/*降级配置或者实现具体fegin接口*/,threadPoolKey = "order6001",
threadPoolProperties = {@HystrixProperty(name = "coreSize",value = "5")}/*限流配置 最多5个线程 其余fallback*/)
public Object getUserToOrder(){
    return orderFeginClient.getOrderFeginClient();
}

配置通用的fallbackMethod 方法 即 实现 fegin 接口即可 eg:

@FeignClient(name = "ORDER-SERVER",fallback = OrderFeginClientImpl.class)
public interface OrderFeginClient {

    @RequestMapping("/getOrder")
    public Object getOrderFeginClient();
}
@Service
public class OrderFeginClientImpl implements OrderFeginClient {
    @Override
    public Object getOrderFeginClient() {
        return "断路器生效,系统正在维护中";
    }
}
feign整合hystrix:
feign 默认是支持hystrix的, 但是在Spring - cloud Dalston 版本之后就默认关闭了, 因为不一定业务需求要用的
到,
所以现在要使用首先得打开他,在yml文件加上如下配置:
feign:
  hystrix:
    enabled: true
zuul 网关组件
Zuul包含了对请求的路由和过滤两个最主要的功能:
其中路由功能负责将外部请求转发到具体的微服务实例上,是实现外部访问统一入口的基础而过滤器功能则负责对
请求的处理过程进行干预,是实现请求校验、服务聚合等功能的基础.
ZuulEureka进行整合,将Zuul自身注册为Eureka服务治理下的应用,同时从Eureka中获得其他微服务的消息,
也即以后的访问微服务都是通过Zuul跳转后获得。
注意:Zuul服务最终还是会注册进Eureka,也属于一个客户端
 
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    <version>2.0.2.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
    <version>2.0.2.RELEASE</version>
</dependency>

启动类添加 @EnableZuulProxy 注解

zuul/zuul-server9001/zuul-server9002 application.yml 部分 其余和集群中的客户端配置一样
zuul-application.yml 部分
spring:
  application:
    name: zuul #此实例注册到eureka服务端的name

zuul:
  prefix: /api # 添加前缀
  ignored-services: "*"#user-server # 如果是 "*" 就是所有
  routes:
    user:
      serviceId: zuul-server
      path: /zuul/**
zuul-server9001/zuul-server9002 application.yml 部分
spring:
  application:
    name: zuul-server #此实例注册到eureka服务端的name

zuul:
#  prefix: /api # 添加前缀
  ignored-services: "*"#user-server # 如果是 "*" 就是所有
  routes:
    user:
      serviceId: user-server
      path: /user/**
user8000服务的控制器
@RequestMapping("/test")
@ResponseBody
public Object getTest(){
    // 原始访问路径 http://localhost:8000/test
    // application.yml
    //     spring:application:name: user-server #此实例注册到eureka服务端的name
    // 路由访问路径 http://localhost:8080/user-server/test
    // 设置路由规则后的访问路径
    //     http://localhost:8080/user/test
    // 设置zuul集群后的访问路径 
    //     http://localhost:8080/api/zuul/user/test
    /**
     * zuul:
     *    prefix: /api # 添加前缀
     *   ignored-services: user-server # 如果是 "*" 就是过滤所有
     *   routes:
     *     user:
     *       serviceId: user-server
     *       path: /user/**
     */

    return "路由测试访问";
}

zuul 的过滤器

过滤器(fifilter)zuul的核心组件 zuul大部分功能都是通过过滤器来实现的。
zuul中定义了4种标准过滤器类型,这 些过滤器类型对应于请求的典型生命周期。
PRE:这种过滤器在请求被路由之前调用。可利用这种过滤器实现身份 验证、在 集群中选择请求的微服务、记录调试信息等。
ROUTING:这种过滤器将请求路由到微服务。这种过滤器 用于构建发送给微服 务的请求,并使用 Apache HttpCIient Netfilx Ribbon请求微服务
POST:这种过滤器在路由到微服务以后执行。这种过滤器可用来为响应添加标准 的 HTTP Header、收集统计信息和指标、将响应从微服务发送给客户端等。
ERROR:在其他阶段发生错误时执行该过滤器。
如果要编写一个过滤器,则需继承ZuulFilter类 实现其中方法:
 
@Component
public class LogFilter extends ZuulFilter {
    @Override
    public String filterType() {
        return FilterConstants.ROUTE_TYPE;
    }
    @Override
    public int filterOrder() {
        return FilterConstants.PRE_DECORATION_FILTER_ORDER;
    }
    @Override
    public boolean shouldFilter() {
        return true;
    }
    @Override
    public Object run() throws ZuulException {
        RequestContext currentContext = RequestContext.getCurrentContext();
        HttpServletRequest request = currentContext.getRequest();
        String remoteAddr = request.getRemoteAddr();
        System.out.println("访问者IP"+remoteAddr+"访问地址:"+request.getRequestURI());
        return null;
    }
}
由代码可知,自定义的 zuul Filter需实现以下几个方法。
filterType:返回过滤器的类型。有 pre route post error等几种取值,分别对应上文的几种过滤器。
详细可以参考 com.netflix.zuul.ZuulFilter.filterType()中的注释。
filter0rder:返回一个 int值来指定过滤器的执行顺序,不同的过滤器允许返回相同的数字。
shouldFilter:返回一个 boolean值来判断该过滤器是否要执行, true表示执行, false表示不执行。
run:过滤器的具体逻辑。
禁用zuul过滤器 Spring Cloud默认为Zuul编写并启用了一些过滤器,例如DebugFilter FormBodyWrapperFilter
等,这些过滤器都存放在spring-cloud-netflix-core这个jar包 里,一些场景下,想要禁用掉部分过滤器,该怎么办
呢? 只需在application.yml里设置zuul...disable=true 例如,要禁用上面我们写的过滤器,这样配置就行了:
zuul.LogFilter.pre.disable=true

eureka 集群主界面

zuul 容错与回退直接看官网的demo

class MyFallbackProvider implements FallbackProvider {
    @Override
    public String getRoute() {
        //制定为哪个微服务提供回退(这里写微服务名 写*代表所有微服务)
        return "*";
    }
    //此方法需要返回一个ClientHttpResponse对象 ClientHttpResponse是一个接口,具体的回退逻辑要实现此接口
    //route:出错的微服务名 cause:出错的异常对象
    @Override
    public ClientHttpResponse fallbackResponse(String route, final Throwable cause) {
        //这里可以判断根据不同的异常来做不同的处理, 也可以不判断
        //完了之后调用response方法并根据异常类型传入HttpStatus
        if (cause instanceof HystrixTimeoutException) {
            return response(HttpStatus.GATEWAY_TIMEOUT);
        } else {
            return response(HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }
    private ClientHttpResponse response(final HttpStatus status) {
    //这里返回一个ClientHttpResponse对象 并实现其中的方法,关于回退逻辑的详细,便在下面的方法中
        return new ClientHttpResponse() {
            @Override
            public HttpStatus getStatusCode() throws IOException {
                //返回一个HttpStatus对象 这个对象是个枚举对象, 里面包含了一个status code 和 reasonPhrase信息
                return status;
            }
            @Override
            public int getRawStatusCode() throws IOException {
                //返回statuscode 比如 404500
                return status.value();
            }
            @Override
            public String getStatusText() throws IOException {
                //返回一个HttpStatus对象的reasonPhrase信息
                return status.getReasonPhrase();
            }
            @Override
            public void close() {
                //close的时候调用的方法, 讲白了就是当降级信息全部响应完了之后调用的方法
            }
            @Override
            public InputStream getBody() throws IOException {
                //吧降级信息响应回前端
                return new ByteArrayInputStream("降级信息".getBytes());
            }
            @Override
            public HttpHeaders getHeaders() {
                //需要对响应报头设置的话可以在此设置
                HttpHeaders headers = new HttpHeaders();
                headers.setContentType(MediaType.APPLICATION_JSON);
                return headers;
            }
        };
    }
}

eureka 集群主界面

 最终调用图

HystrixDashbord 监控中心

版权声明:本文为qq_38108719原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_38108719/article/details/104887686

智能推荐

26_Python基础_继承

面向对象三大特性: 封装 根据 职责 将 属性 和 方法 封装 到一个抽象的 类 中 继承 实现代码的重用, 相同的代码不需要重复的编写 多态 不同的对象调用相同的方法,  产生不同的执行结果,  增加代码的灵活度 1.  单继承 1.1 概念 继承的概念:&...

循环

与任何程序设计语言一样Java利用条件语句与循环结构确定流程控制,一下总结一下Java中的循环语句: while do while for switch 对于golang来说: switch非常灵活。从第一个expr为true的case开始执行,如果case带有fallthrough,程序会继续执行下一条case,不会再判断下一条case的expr,如果之后的case都有fallthrough,d...

1638 统计只差一个字符的子串数目(动态规划)

1. 问题描述: 给你两个字符串 s 和 t ,请你找出 s 中的非空子串的数目,这些子串满足替换一个不同字符以后,是 t 串的子串。换言之,请你找到 s 和 t 串中恰好只有一个字符不同的子字符串对的数目。比方说, "computer" 和 "computation"...

websocket基本原理

HTTP中一个request只能有一个response。而且这个response也是被动的,不能主动发起 因此过去的服务端推送信息是通过客户端不停的轮询实现的 websocket是双向通信协议,提供了服务端主动推送信息的能力 需要客户端(浏览器)和服务端同时支持 如果经过代理的话,还需要代理支持,否则有些代理在长时间无通信时会自动切断连接 因此WS为了保证连接不被断掉,会发心跳 WebSocket...

mybatis+ehcache二级缓存

导入jar包 mapper.xml文件开启二级缓存 pojo类实现序列化接口 配置ehcache.xml 测试...

猜你喜欢

python+opencv实现图像拼接

任务 拍摄两张图片去除相同部分,拼接在一起 原图 结果 步骤 读取两张图片 使用sift检测关键点及描述因子 匹配关键点 处理并保存关键点 得到变换矩阵 图像变换并拼接 代码实现 扩展 这里对右边图像进行变换,右边变得模糊,可以修改代码对左边图像变换 这里只有两张图片拼接,可以封装实现多张图片拼接 可以修改代码实现上下图片的拼接...

python_sklearn机器学习算法系列之AdaBoost------人脸识别(PCA,决策树)

          注:在读本文之前建议读一下之前的一片文章python_sklearn机器学习算法系列之PCA(主成分分析)------人脸识别(k-NearestNeighbor,KNN)         本文主要目的是通过一个简单的小...

memmove函数与memcpy函数的模拟实现

memmove函数和memcpy函数都是在内存复制任意类型的,但是它俩也有区别。当源区域和目标区域有重复的,memmove函数会复制缓冲区重叠的部分,而memcpy相反,会报出未知错误。 下面给出两个函数的实现 首先,memmove函数。 实现的基本原理如下图。 具体代码如下: memcpy函数的实现很简单,就直接给出源代码了...

SpringFramework核心 - IOC容器的实现 - 总结

1. 概述 把Spring技术内幕第一章和第二章过了一遍,也做了一些笔记, 对IOC容器的实现有了一定皮毛理解,现在跟着源码再过一遍总结一下IOC容器的初始化,Bean的初始化的过程,做一下总结 ① IOC容器和简单工厂模式 在开始之前,先想想我们平时是怎么使用IOC容器为我们管理Bean的,假设我们要把下面的User类交给IOC容器管理 我们不想关心如何创建一个User对象实例的,仅仅在需要他的...