SpringCloud简单应用

一、微服务架构

  1. 什么是分布式?
    不同模块部署在不同服务器上。
    作用:分布式解决网站高并发带来问题。
  2. 什么是集群?
    多台服务器部署相同应用构成一个集群。
    作用:通过负载均衡设备共同对外提供服务。
  3. 什么是RPC?
    RPC 的全称是 Remote Procedure Call 是一种进程间通信方式。
    它允许程序调用另一个地址空间(通常是共享网络的另一台机器上)的过程或函数,而不用程序员显式编码这个远程调用的细节。即无论是调用本地接口/服务的还是远程的接口/服务,本质上编写的调用代码基本相同。
    比如两台服务器A,B,一个应用部署在A服务器上,想要调用B服务器上应用提供的函数或者方法,由于不在一个内存空间,不能直接调用,这时候需要通过就可以应用RPC框架的实现来解决。
    1. restful、soap、rpc理解:
      1. RESTful是一种架构设计风格,提供了设计原则和约束条件,而不是架构。而满足这些约束条件和原则的应用程序或设计就是 RESTful架构或服务。
      2. SOAP,简单对象访问协议是一种数据交换协议规范,
        是一种轻量的、简单的、基于XML的协议的规范。SOAP协议和HTTP协议一样,都是底层的通信协议,只是请求包的格式不同而已,SOAP包是XML格式的。
        SOAP的消息是基于xml并封装成了符合http协议,因此,它符合任何路由器、 防火墙或代理服务器的要求。
        soap可以使用任何语言来完成,只要发送正确的soap请求即可,基于soap的服务可以在任何平台无需修改即可正常使用。
      3. RPC就是从一台机器(客户端)上通过参数传递的方式调用另一台机器(服务器)上的一个函数或方法(可以统称为服务)并得到返回的结果。
        RPC 会隐藏底层的通讯细节(不需要直接处理Socket通讯或Http通讯)
        RPC 是一个请求响应模型。客户端发起请求,服务器返回响应(类似于Http的工作方式)
        RPC 在使用形式上像调用本地函数(或方法)一样去调用远程的函数(或方法)。
    2. 几种比较典型的RPC的实现和调用框架 :
      1. RMI实现,利用java.rmi包实现,基于Java远程方法协议(Java Remote Method Protocol) 和java的原生序列化。
      2. Hessian,是一个轻量级的remoting onhttp工具,使用简单的方法提供了RMI的功能。 基于HTTP协议,采用二进制编解码。
      3. thrift是一种可伸缩的跨语言服务的软件框架。thrift允许你定义一个描述文件,描述数据类型和服务接口。依据该文件,编译器方便地生成RPC客户端和服务器通信代码。
      4. Dubbo是阿里巴巴公司开源的一个高性能优秀的高性能、轻量级的开源Java RPC框架,使得应用可通过高性能的 RPC 实现服务的输出和输入功能,可以和Spring框架无缝集成,它提供了三大核心能力:面向接口的远程方法调用,智能容错和负载均衡,以及服务自动注册和发现。
      5. SpringCloud 为开发人员提供了快速构建分布式系统的一些工具,包括配置管理、服务发现、断路器、路由、微代理、事件总线、全局锁、决策竞选、分布式会话等等。
  4. 什么是SOA?
    业务系统分解为多个组件,让每个组件都独立提供离散,自治,可复用的服务能力。
    通过服务的组合和编排来实现上层的业务流程。
    作用:简化维护,降低整体风险,伸缩灵活。
  5. 什么是微服务?
    架构设计概念,各服务间隔离(分布式也是隔离),自治(分布式依赖整体组合)其它特性(单一职责,边界,异步通信,独立部署)是分布式概念的更严格执行。
    作用:各服务可独立应用,组合服务也可系统应用(巨石应用[monolith]的简化实现策略-平台思想)
  6. 使用RPC http技术实现简单会员与订单系统通讯:
    1. 创建会员项目SpringCloud-Member:
      <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>1.5.2.RELEASE</version>
      </parent>
      
      <dependencies>
       <!-- spring boot test -->
       <dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-starter-web</artifactId>
       </dependency>
      </dependencies>
      
      <build>
       <plugins>
           <plugin>
               <groupId>org.springframework.boot</groupId>
               <artifactId>spring-boot-maven-plugin</artifactId>
           </plugin>
       </plugins>
      </build>
      
      package chauncy.contorller;
      
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.web.bind.annotation.RequestMapping;
      import org.springframework.web.bind.annotation.RestController;
      
      import chauncy.entity.UserEntity;
      import chauncy.service.UserServcie;
      
      @RestController
      @RequestMapping("/userApi")
      public class UserApiController {
      	
      	@Autowired
      	private UserServcie userService;
      	
      	@RequestMapping("/getUser")
      	public UserEntity getUser(Integer userId){
      		return userService.getUser(userId);
      	}
      }
      
      package chauncy.service;
      
      import java.util.List;
      
      import chauncy.entity.UserEntity;
      
      
      public interface UserServcie {
      	
      	public List<UserEntity> getAllUser();
      	
      	public UserEntity getUser(Integer id);
      }
      
      package chauncy.service.impl;
      
      import java.util.List;
      
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.stereotype.Service;
      
      import chauncy.dao.UserDao;
      import chauncy.entity.UserEntity;
      import chauncy.service.UserServcie;
      
      @Service
      public class UserServiceImpl implements UserServcie{
      	
      	@Autowired
      	private UserDao userDao;
      	
      	@Override
      	public List<UserEntity> getAllUser(){
      		return userDao.getAllUser();
      	}
      	
      	@Override
      	public UserEntity getUser(Integer id){
      		List<UserEntity> userList=getAllUser();
      		for(UserEntity user : userList){
      			if(user.getId() == id){
      				return user;
      			}
      		}
      		return null;
      	}
      }
      
      package chauncy.dao;
      
      import java.util.ArrayList;
      import java.util.List;
      
      import org.springframework.stereotype.Component;
      
      import chauncy.entity.UserEntity;
      
      @Component
      public class UserDao {
      	
      	public List<UserEntity>  getAllUser(){
      		List<UserEntity> userList=new ArrayList<UserEntity>();
      		for (int i = 0; i <= 20; i++) {
      			userList.add(new UserEntity(i, "name:"+i));
      		}
      		return userList;
      	}
      }
      
      package chauncy.entity;
      
      public class UserEntity {
      	
      	private Integer id;
      	private String name;
      	
      	public UserEntity(Integer id, String name) {
      		super();
      		this.id = id;
      		this.name = name;
      	}
      	
      	public UserEntity() {
      		super();
      	}
      
      	public Integer getId() {
      		return id;
      	}
      	public void setId(Integer id) {
      		this.id = id;
      	}
      	public String getName() {
      		return name;
      	}
      	public void setName(String name) {
      		this.name = name;
      	}
      }
      
      package chauncy;
      
      import org.springframework.boot.SpringApplication;
      import org.springframework.boot.autoconfigure.SpringBootApplication;
      
      @SpringBootApplication
      public class App {
      	
      	public static void main(String[] args){
      		SpringApplication.run(App.class, args);
      	}
      }
      
      server:
          context-path: /member
      
    2. 创建订单项目SpringCloud-Order:
      <parent>
      	<groupId>org.springframework.boot</groupId>
      	<artifactId>spring-boot-starter-parent</artifactId>
      	<version>1.5.2.RELEASE</version>
      </parent>
      
      <dependencies>
      	<!-- spring boot test -->
      	<dependency>
      		<groupId>org.springframework.boot</groupId>
      		<artifactId>spring-boot-starter-web</artifactId>
      	</dependency>
      	<dependency>
      		<groupId>org.apache.httpcomponents</groupId>
      		<artifactId>httpclient</artifactId>
      		<version>4.5.10</version>
      	</dependency>
      </dependencies>
      
      <build>
      	<plugins>
      		<plugin>
      			<groupId>org.springframework.boot</groupId>
      			<artifactId>spring-boot-maven-plugin</artifactId>
      		</plugin>
      	</plugins>
      </build>
      
      package chauncy.controller;
      
      import java.io.IOException;
      
      import org.apache.http.HttpEntity;
      import org.apache.http.client.ClientProtocolException;
      import org.apache.http.client.methods.CloseableHttpResponse;
      import org.apache.http.client.methods.HttpGet;
      import org.apache.http.impl.client.CloseableHttpClient;
      import org.apache.http.impl.client.HttpClients;
      import org.apache.http.util.EntityUtils;
      import org.springframework.web.bind.annotation.RequestMapping;
      import org.springframework.web.bind.annotation.RestController;
      
      @RestController
      public class OrderController {
      	
      	@RequestMapping("/getOrderUserId")
      	public String getOrderUserId(Integer userId) {
      		String result = get("http://www.chauncywang.com:8080/member/userApi/getUser?userId="+userId);
      		return result;
      	}
      	
      	/**
      	 * 发送 get请求
      	 */
      	public String get(String url) {
      		CloseableHttpClient httpclient = HttpClients.createDefault();
      		try {
      			// 创建httpget.
      			HttpGet httpget = new HttpGet(url);
      			System.out.println("executing request " + httpget.getURI());
      			// 执行get请求.
      			CloseableHttpResponse response = httpclient.execute(httpget);
      			try {
      				// 获取响应实体
      				HttpEntity entity = response.getEntity();
      				System.out.println("--------------------------------------");
      				// 打印响应状态
      				System.out.println(response.getStatusLine());
      				if (entity != null) {
      					// 打印响应内容长度
      					System.out.println("Response content length: " + entity.getContentLength());
      					String result = EntityUtils.toString(entity);
      					// 打印响应内容
      					System.out.println("Response content: " + result);
      					return result;
      				}
      				System.out.println("------------------------------------");
      			} finally {
      				response.close();
      			}
      		} catch (ClientProtocolException e) {
      			e.printStackTrace();
      		} catch (IOException e) {
      			e.printStackTrace();
      		} finally {
      			// 关闭连接,释放资源
      			try {
      				httpclient.close();
      			} catch (IOException e) {
      				e.printStackTrace();
      			}
      		}
      		return null;
      	}
      }
      
      package chauncy;
      
      import org.springframework.boot.SpringApplication;
      import org.springframework.boot.autoconfigure.SpringBootApplication;
      
      @SpringBootApplication
      public class App {
      
      	public static void main(String[] args) {
      		SpringApplication.run(App.class, args);
      	}
      }
      
      server:
          context-path: /order
          port: 8081
      

二、SpringCloud

SpringCloud 为开发人员提供了快速构建分布式系统的一些工具,包括配置管理、服务发现、断路器、路由、微代理、事件总线、全局锁、决策竞选、分布式会话等等。它运行环境简单,可以在开发人员的电脑上跑。另外说明spring cloud是基于Springboot的,所以需要开发中对Springboot有一定的了解。
SpringCloud底层是基于HttpClient的,只不过做了一些封装。

三、服务的注册与发现(Eureka)

Eureka是SpringCloud内置的注册中心,其它可以做注册中心的有:Zookeeper、Redis等。

  1. 服务注册:
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.2.RELEASE</version>
        <relativePath /> <!-- lookup parent from repository -->
    </parent>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>
    
    <dependencies>
        <!--eureka server -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka-server</artifactId>
        </dependency>
        <!-- spring boot test -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Dalston.RC1</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
    <repositories>
        <repository>
            <id>spring-milestones</id>
            <name>Spring Milestones</name>
            <url>https://repo.spring.io/milestone</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
    </repositories>
    
    package chauncy.app;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
    
    @SpringBootApplication
    @EnableEurekaServer
    public class EurekaServer {
    	public static void main(String[] args) {
    		SpringApplication.run(EurekaServer.class, args);
    	}
    }
    
    server:
      port: 8888
    eureka:
      instance:
        hostname: localhost
      client:
        registerWithEureka: false #是否将自身注册
        fetchRegistry: false #如果为true,启动时报警
        serviceUrl:
          defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
    
  2. 服务提供者:
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.2.RELEASE</version>
        <relativePath /> <!-- lookup parent from repository -->
    </parent>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Dalston.RC1</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
    <repositories>
        <repository>
            <id>spring-milestones</id>
            <name>Spring Milestones</name>
            <url>https://repo.spring.io/milestone</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
    </repositories>
    
    package chauncy.controller;
    
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    public class EurekaClientController {
    
    	@Value("${server.port}")
    	String port;
    
    	@RequestMapping("/client")
    	public String client(@RequestParam String name) {
    		return "name:" + name + ",from port:" + port;
    	}
    
    }
    
    package chauncy;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
    
    @SpringBootApplication
    @EnableEurekaClient
    public class ServerApplication {
    	
    	public static void main(String[] args) {
    		SpringApplication.run(ServerApplication.class, args);
    	}
    	
    }
    
    eureka:
      client:
        serviceUrl:
          defaultZone: http://www.chauncywang.com:8888/eureka/
    server:
      port: 8999
    spring:
      application:
        name: SpringCloud-EurekaClient
    

四、服务消费者(rest+ribbon)

在微服务架构中,业务都会被拆分成一个独立的服务,服务与服务的通讯是基于http restful的。Spring cloud有两种服务调用方式,一种是ribbon+restTemplate,另一种是feign。

  1. 什么是ribbon?
    ribbon是一个负载均衡客户端,可以很好的控制http和tcp的一些行为。Feign默认集成了ribbon。
  2. 使用ribbon+restTemplate搭建消费者模型:
    <parent>
    	<groupId>org.springframework.boot</groupId>
    	<artifactId>spring-boot-starter-parent</artifactId>
    	<version>1.5.2.RELEASE</version>
    	<relativePath /> <!-- lookup parent from repository -->
    </parent>
    
    <properties>
    	<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    	<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    	<java.version>1.8</java.version>
    </properties>
    
    <dependencies>
    	<dependency>
    		<groupId>org.springframework.cloud</groupId>
    		<artifactId>spring-cloud-starter-eureka</artifactId>
    	</dependency>
    	<dependency>
    		<groupId>org.springframework.cloud</groupId>
    		<artifactId>spring-cloud-starter-ribbon</artifactId>
    	</dependency>
    	<dependency>
    		<groupId>org.springframework.boot</groupId>
    		<artifactId>spring-boot-starter-web</artifactId>
    	</dependency>
    
    	<dependency>
    		<groupId>org.springframework.boot</groupId>
    		<artifactId>spring-boot-starter-test</artifactId>
    		<scope>test</scope>
    	</dependency>
    </dependencies>
    
    <dependencyManagement>
    	<dependencies>
    		<dependency>
    			<groupId>org.springframework.cloud</groupId>
    			<artifactId>spring-cloud-dependencies</artifactId>
    			<version>Dalston.RC1</version>
    			<type>pom</type>
    			<scope>import</scope>
    		</dependency>
    	</dependencies>
    </dependencyManagement>
    
    <build>
    	<plugins>
    		<plugin>
    			<groupId>org.springframework.boot</groupId>
    			<artifactId>spring-boot-maven-plugin</artifactId>
    		</plugin>
    	</plugins>
    </build>
    
    <repositories>
    	<repository>
    		<id>spring-milestones</id>
    		<name>Spring Milestones</name>
    		<url>https://repo.spring.io/milestone</url>
    		<snapshots>
    			<enabled>false</enabled>
    		</snapshots>
    	</repository>
    </repositories>
    
    package chauncy.controller;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    import org.springframework.web.bind.annotation.RestController;
    
    import chauncy.service.RibbonService;
    
    @RestController
    @RequestMapping("/ribbon")
    public class RibbonController {
    	
    	@Autowired
    	RibbonService ribbonService;
    	
    	@RequestMapping("/index")
    	public String index(@RequestParam String name){
    		return ribbonService.indexLoad(name);
    	}
    }
    
    package chauncy.service;
    
    public interface RibbonService {
    	
    	public String indexLoad(String name);
    }
    
    package chauncy.service.impl;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    import org.springframework.web.client.RestTemplate;
    
    import chauncy.service.RibbonService;
    
    @Service
    public class RibbonServiceImpl implements RibbonService{
    	
    	@Autowired
    	RestTemplate restTemplate;
    	
    	public String indexLoad(String name) {
    		return restTemplate.getForObject("http://SPRINGCLOUD-EUREKACLIENT/client?name="+name, String.class);
    	}
    
    }
    
    package chauncy;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.client.loadbalancer.LoadBalanced;
    import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
    import org.springframework.context.annotation.Bean;
    import org.springframework.web.client.RestTemplate;
    
    @SpringBootApplication
    @EnableEurekaClient
    public class App {
    	
    	public static void main(String[] args) {
    		SpringApplication.run(App.class, args);
    	}
    	
    	@Bean
    	@LoadBalanced
    	RestTemplate restTemplate() {
    		return new RestTemplate();
    	}
    
    }
    
    eureka:
      client:
        serviceUrl:
          defaultZone: http://www.chauncywang.com:8888/eureka/
    server:
      port: 8666
    spring:
      application:
        name: SpringCloud-Ribbon
    

五、服务消费者(Feign)

  1. 什么是Feign?
    Feign是一个声明式的伪Http客户端,它使得写Http客户端变得更简单。使用Feign,只需要创建一个接口并注解。它具有可插拔的注解特性,可使用Feign 注解和JAX-RS注解。Feign支持可插拔的编码器和解码器。Feign默认集成了Ribbon,并和Eureka结合,默认实现了负载均衡的效果。
    简而言之:
    1. Feign 采用的是基于接口的注解。
    2. Feign 整合了ribbon。
  2. 使用Feign搭建消费者模型:
    <parent>
    	<groupId>org.springframework.boot</groupId>
    	<artifactId>spring-boot-starter-parent</artifactId>
    	<version>1.5.2.RELEASE</version>
    	<relativePath /> <!-- lookup parent from repository -->
    </parent>
    
    <properties>
    	<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    	<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    	<java.version>1.8</java.version>
    </properties>
    
    <dependencies>
    	<dependency>
    		<groupId>org.springframework.cloud</groupId>
    		<artifactId>spring-cloud-starter-eureka</artifactId>
    	</dependency>
    	<dependency>
    		<groupId>org.springframework.cloud</groupId>
    		<artifactId>spring-cloud-starter-feign</artifactId>
    	</dependency>
    	<dependency>
    		<groupId>org.springframework.boot</groupId>
    		<artifactId>spring-boot-starter-web</artifactId>
    	</dependency>
    
    	<dependency>
    		<groupId>org.springframework.boot</groupId>
    		<artifactId>spring-boot-starter-test</artifactId>
    		<scope>test</scope>
    	</dependency>
    </dependencies>
    
    <dependencyManagement>
    	<dependencies>
    		<dependency>
    			<groupId>org.springframework.cloud</groupId>
    			<artifactId>spring-cloud-dependencies</artifactId>
    			<version>Dalston.RC1</version>
    			<type>pom</type>
    			<scope>import</scope>
    		</dependency>
    	</dependencies>
    </dependencyManagement>
    
    <build>
    	<plugins>
    		<plugin>
    			<groupId>org.springframework.boot</groupId>
    			<artifactId>spring-boot-maven-plugin</artifactId>
    		</plugin>
    	</plugins>
    </build>
    
    <repositories>
    	<repository>
    		<id>spring-milestones</id>
    		<name>Spring Milestones</name>
    		<url>https://repo.spring.io/milestone</url>
    		<snapshots>
    			<enabled>false</enabled>
    		</snapshots>
    	</repository>
    </repositories>
    
    package chuancy.controller;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import chuancy.service.FeignService;
    
    @RestController
    public class IndexController {
    	
    	@Autowired
    	private FeignService feignService;
    	
    	@RequestMapping("/index")
    	public String index(String name){
    		return feignService.client(name);
    	}
    }
    
    package chuancy.service;
    
    import org.springframework.cloud.netflix.feign.FeignClient;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    
    @FeignClient("SpringCloud-EurekaClient")
    public interface FeignService {
    	
    	@RequestMapping("/client")
    	public String client(@RequestParam("name")String name);
    }
    
    package chuancy;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
    import org.springframework.cloud.netflix.feign.EnableFeignClients;
    
    @SpringBootApplication
    @EnableFeignClients
    @EnableDiscoveryClient
    public class App {
    	
    	public static void main(String[] args) {
    		SpringApplication.run(App.class, args);
    	}
    }
    
    eureka:
      client:
        serviceUrl:
          defaultZone: http://www.chauncywang.com:8888/eureka/
    server:
      port: 8777
    spring:
      application:
        name: SpringCloud-Feign
    

六、Hystrix断路器

在微服务架构中,根据业务来拆分成一个个的服务,服务与服务之间可以相互调用(RPC),在Spring Cloud可以用RestTemplate+Ribbon和Feign来调用。为了保证其高可用,单个服务通常会集群部署。由于网络原因或者自身的原因,服务并不能保证100%可用,如果单个服务出现问题,调用这个服务就会出现线程阻塞,此时若有大量的请求涌入,Servlet容器的线程资源会被消耗完毕,导致服务瘫痪。服务与服务之间的依赖性,故障会传播,会对整个微服务系统造成灾难性的严重后果,这就是服务故障的“雪崩”效应。为了解决这个问题,业界提出了断路器模型。

  1. 什么是Hystrix?
    Netflix开源了Hystrix组件,实现了断路器模式,SpringCloud对这一组件进行了整合。 在微服务架构中,一个请求需要调用多个服务是非常常见的,如下图:
    在这里插入图片描述
    较底层的服务如果出现故障,会导致连锁故障。当对特定的服务的调用的不可用达到一个阀值(Hystric 是5秒20次) 断路器将会被打开。
    在这里插入图片描述
    断路打开后,可用避免连锁故障,fallback方法可以直接返回一个固定值。
  2. 在ribbon使用断路器:
    改造SpringCloud-Ribbon项目
    1. 引入Hystrix依赖:
      <dependency>
          <groupId>org.springframework.cloud</groupId>
          <artifactId>spring-cloud-starter-hystrix</artifactId>
      </dependency>
      
    2. 改造service和启动类:
      package chauncy.service.impl;
      
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.stereotype.Service;
      import org.springframework.web.client.RestTemplate;
      
      import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
      
      import chauncy.service.RibbonService;
      
      @Service
      public class RibbonServiceImpl implements RibbonService{
      	
      	@Autowired
      	RestTemplate restTemplate;
      	
      	@HystrixCommand(fallbackMethod="error")
      	public String indexLoad(String name) {
      		return restTemplate.getForObject("http://SPRINGCLOUD-EUREKACLIENT/client?name="+name, String.class);
      	}
      	
      	public String error(String name){
      		return "参数为:"+name+",调用SPRINGCLOUD-EUREKACLIENT失败!";
      	}
      }
      
      package chauncy;
      
      import org.springframework.boot.SpringApplication;
      import org.springframework.boot.autoconfigure.SpringBootApplication;
      import org.springframework.cloud.client.loadbalancer.LoadBalanced;
      import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
      import org.springframework.cloud.netflix.hystrix.EnableHystrix;
      import org.springframework.context.annotation.Bean;
      import org.springframework.web.client.RestTemplate;
      
      @SpringBootApplication
      @EnableEurekaClient
      @EnableHystrix
      public class App {
      	
      	public static void main(String[] args) {
      		SpringApplication.run(App.class, args);
      	}
      	
      	@Bean
      	@LoadBalanced
      	RestTemplate restTemplate() {
      		return new RestTemplate();
      	}
      
      }
      
  3. 在Feign中使用断路器:
    Feign是自带断路器的,在D版本的Spring Cloud中,它没有默认打开。需要在配置文件中配置打开它,在配置文件加以下代码:
    feign.hystrix.enabled=true
    基于SpringCloud-Feign工程进行改造,只需要在FeignClient的FeignService接口的注解中加上fallback的指定类就行了:
    package chuancy.service;
    
    import org.springframework.cloud.netflix.feign.FeignClient;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    
    @FeignClient(value="SpringCloud-EurekaClient",fallback=FeignServiceHystrix.class)
    public interface FeignService {
    	
    	@RequestMapping("/client")
    	public String client(@RequestParam("name")String name);
    }
    
    FeignServiceHystrix需要实现FeignService接口,并注入到Ioc容器中,代码如下:
    package chuancy.service;
    
    import org.springframework.stereotype.Component;
    
    @Component
    public class FeignServiceHystrix implements FeignService{
    
    	@Override
    	public String client(String name) {
    		return "feign-->name:"+name+"系统错误,调用接口失败!";
    	}
    
    }
    
    在配置文件application.yml中增加feign.hystrix.enabled=true:
    eureka:
      client:
        serviceUrl:
          defaultZone: http://www.chauncywang.com:8888/eureka/
    server:
      port: 8777
    spring:
      application:
        name: SpringCloud-Feign
    feign:
      hystrix:
        enabled: true
    
  4. Hystrix Dashboard (断路器:Hystrix 仪表盘)
    基于SpringCloud-Ribbon改造,SpringCloud-Feign的改造相同。
    首选在pom.xml引入起步依赖:
    <dependency>
    	<groupId>org.springframework.boot</groupId>
    	<artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    
    <dependency>
    	<groupId>org.springframework.cloud</groupId>
    	<artifactId>spring-cloud-starter-hystrix-dashboard</artifactId>
    </dependency>
    
    在主程序启动类中加入@EnableHystrixDashboard注解,开启hystrixDashboard:
    package chauncy;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.client.loadbalancer.LoadBalanced;
    import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
    import org.springframework.cloud.netflix.hystrix.EnableHystrix;
    import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;
    import org.springframework.context.annotation.Bean;
    import org.springframework.web.client.RestTemplate;
    
    @SpringBootApplication
    @EnableEurekaClient
    @EnableHystrix
    @EnableHystrixDashboard
    public class App {
    	
    	public static void main(String[] args) {
    		SpringApplication.run(App.class, args);
    	}
    	
    	@Bean
    	@LoadBalanced
    	RestTemplate restTemplate() {
    		return new RestTemplate();
    	}
    
    }
    
    打开浏览器:访问http://localhost:8666/hystrix,界面如下: 在这里插入图片描述
    点击monitor stream,进入下一个界面,访问:
    http://localhost:8666/ribbon/index?name=chauncy
    此时会出现监控界面:
    在这里插入图片描述

七、使用Zuul构建API Gateway

  1. 什么是API Gateway?
    在Spring Cloud微服务系统中,一种常见的负载均衡方式是,客户端的请求首先经过负载均衡(zuul、Ngnix),再到达服务网关(zuul集群),然后再到具体的服务。
  2. 什么是Zuul?
    1. 定义:
      英:Routing in an integral part of a microservice architecture. For example, / may be mapped to your web application, /api/users is mapped to the user service and /api/shop is mapped to the shop service. Zuul is a JVM based router and server side load balancer by Netflix.
      译:路由在微服务架构的一个组成部分。 例如,/可以映射到您的Web应用程序,/ api / users映射到用户服务,并且/ api / shop映射到商店服务。 Zuul是Netflix的基于JVM的路由器和服务器端负载均衡器。
    2. 其功能包括:
      1. 验证
      2. 见解
      3. 压力测试
      4. 金丝雀测试
      5. 动态路由
      6. 服务迁移
      7. 减载
      8. 安全
      9. 静态响应处理
      10. 主动/主动流量管理
    3. Zuul的规则引擎允许规则和过滤器基本上用任何JVM语言编写,内置支持Java和Groovy。
  3. 搭建SpringCloud-Zuul工程:
    pom.xml:
    <parent>
    	<groupId>org.springframework.boot</groupId>
    	<artifactId>spring-boot-starter-parent</artifactId>
    	<version>1.5.2.RELEASE</version>
    	<relativePath /> <!-- lookup parent from repository -->
    </parent>
    
    <properties>
    	<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    	<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    	<java.version>1.8</java.version>
    </properties>
    
    <dependencies>
    	<dependency>
    		<groupId>org.springframework.cloud</groupId>
    		<artifactId>spring-cloud-starter-eureka</artifactId>
    	</dependency>
    	<dependency>
    		<groupId>org.springframework.cloud</groupId>
    		<artifactId>spring-cloud-starter-zuul</artifactId>
    	</dependency>
    	<dependency>
    		<groupId>org.springframework.boot</groupId>
    		<artifactId>spring-boot-starter-web</artifactId>
    	</dependency>
    
    	<dependency>
    		<groupId>org.springframework.boot</groupId>
    		<artifactId>spring-boot-starter-test</artifactId>
    		<scope>test</scope>
    	</dependency>
    </dependencies>
    
    <dependencyManagement>
    	<dependencies>
    		<dependency>
    			<groupId>org.springframework.cloud</groupId>
    			<artifactId>spring-cloud-dependencies</artifactId>
    			<version>Dalston.RC1</version>
    			<type>pom</type>
    			<scope>import</scope>
    		</dependency>
    	</dependencies>
    </dependencyManagement>
    
    <build>
    	<plugins>
    		<plugin>
    			<groupId>org.springframework.boot</groupId>
    			<artifactId>spring-boot-maven-plugin</artifactId>
    		</plugin>
    	</plugins>
    </build>
    
    <repositories>
    	<repository>
    		<id>spring-milestones</id>
    		<name>Spring Milestones</name>
    		<url>https://repo.spring.io/milestone</url>
    		<snapshots>
    			<enabled>false</enabled>
    		</snapshots>
    	</repository>
    </repositories>
    
    启动类App.java:
    package chauncy;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
    import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
    
    @SpringBootApplication
    @EnableEurekaClient
    @EnableZuulProxy
    public class App {
    	
    	public static void main(String[] args) {
    		SpringApplication.run(App.class, args);
    	}
    }
    
    配置文件application.yml:
    eureka:
      client:
        serviceUrl:
          defaultZone: http://www.chauncywang.com:8888/eureka/
    server:
      port: 8678
    spring:
      application:
        name: SpringCloud-Zuul
    zuul:
      routes:
        api-a:
         path: /api-a/**
         service-id: SpringCloud-Ribbon
        api-b:
         path: /api-b/**
         service-id: SpringCloud-Feign
    
  4. 服务过滤:
    zuul不仅只是路由,并且还能过滤,做一些安全验证。
    在上面搭建的工程中引入代码:
    package chauncy.filter;
    
    import javax.servlet.http.HttpServletRequest;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.stereotype.Component;
    
    import com.netflix.zuul.ZuulFilter;
    import com.netflix.zuul.context.RequestContext;
    
    /**   
     * @classDesc: 功能描述(使用Zuul实现服务过滤)  
     * @author: ChauncyWang  
     * @version: 1.0  
     */  
    @Component
    public class MyFilter extends ZuulFilter {
    
    	private static Logger log = LoggerFactory.getLogger(MyFilter.class);
    	
    	/**
    	 * filterType:返回一个字符串代表过滤器的类型,在zuul中定义了四种不同生命周期的过滤器类型,具体如下: 
    	 * 1.pre:路由之前。
    	 * 2.routing:路由之时。
    	 * 3.post: 路由之后。
    	 * 4.error:发送错误调用。
    	 */
    	@Override
    	public String filterType() {
    		return "pre";
    	}
    	
    	/**
    	 * filterOrder:过滤的顺序。
    	 */
    	@Override
    	public int filterOrder() {
    		return 0;
    	}
    	
    	/**
    	 * shouldFilter:这里可以写逻辑判断,是否要过滤。代码采用return true;表永远过滤。
    	 */
    	@Override
    	public boolean shouldFilter() {
    		return true;
    	}
    	
    	/**
    	 * run:过滤器的具体逻辑。可用很复杂,包括查sql、nosql去判断该请求到底有没有权限访问。
    	 */
    	@Override
    	public Object run() {
    		RequestContext ctx = RequestContext.getCurrentContext();
    		HttpServletRequest request = ctx.getRequest();
    		log.info(String.format("%s >>> %s", request.getMethod(), request.getRequestURL().toString()));
    		Object accessToken = request.getParameter("token");
    		if (accessToken == null) {
    			log.warn("token is empty");
    			ctx.setSendZuulResponse(false);
    			ctx.setResponseStatusCode(401);
    			try {
    				ctx.getResponse().getWriter().write("token is empty");
    			} catch (Exception e) {
    			}
    
    			return null;
    		}
    		log.info("ok");
    		return null;
    	}
    }
    
    引入后,重启工程访问:http://localhost:8678/api-a/ribbon/index?name=chauncy,显示:token is empty。

八、分布式配置中心

分布式配置中心(Spring Cloud Config),在分布式系统中,由于服务数量巨多,为了方便服务配置文件统一管理,实时更新,所以需要分布式配置中心组件。在Spring Cloud中,有分布式配置中心组件spring cloud config ,它支持配置服务放在配置服务的内存中(即本地),也支持放在远程Git仓库中。在spring cloud config 组件中,分两个角色,一是config server,二是config client。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值