Spring Cloud:第八章:项目搭建测试

SpringCloud测试

1. 项目架构

1.1. 简单约定

为了项目名称不要太长,把SpringCloudMicroService缩写为SCMS。

1.2. 工程名称

SCMS-Parent 各个模块的聚合工程同时也是父工程
SCMS-API 公共API工程模块
SCMS-Provider-Dept8001 服务提供者,通过8001端口访问
SCMS-Consummer-Dept80 服务消费者,通过80端口访问

1.3. 聚合、依赖、调用关系

在这里插入图片描述

2. SCMS-Parent创建过程

2.1. 创建Maven工程

2.2. 编辑pom.xml

<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<maven.compiler.source>1.8</maven.compiler.source>
		<maven.compiler.target>1.8</maven.compiler.target>
		<junit.version>4.12</junit.version>
		<log4j.version>1.2.17</log4j.version>
	</properties>

	<dependencyManagement>
		<dependencies>
			<dependency>
				<groupId>org.springframework.cloud</groupId>
				<artifactId>spring-cloud-dependencies</artifactId>
				<version>Dalston.SR1</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
			<dependency>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-dependencies</artifactId>
				<version>1.5.9.RELEASE</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
			<dependency>
				<groupId>mysql</groupId>
				<artifactId>mysql-connector-java</artifactId>
				<version>5.0.4</version>
			</dependency>
			<dependency>
				<groupId>com.alibaba</groupId>
				<artifactId>druid</artifactId>
				<version>1.0.31</version>
			</dependency>
			<dependency>
				<groupId>org.mybatis.spring.boot</groupId>
				<artifactId>mybatis-spring-boot-starter</artifactId>
				<version>1.3.0</version>
			</dependency>
			<dependency>
				<groupId>ch.qos.logback</groupId>
				<artifactId>logback-core</artifactId>
				<version>1.2.3</version>
			</dependency>
			<dependency>
				<groupId>junit</groupId>
				<artifactId>junit</artifactId>
				<version>${junit.version}</version>
				<scope>test</scope>
			</dependency>
			<dependency>
				<groupId>log4j</groupId>
				<artifactId>log4j</artifactId>
				<version>${log4j.version}</version>
			</dependency>
		</dependencies>
	</dependencyManagement>

3. SCMS-API创建过程

3.1. 创建Maven模块

在SCMS-Parent工程上点右键,然后new

3.2. 创建Dept实体类

包名:com.atguigu.cloud.entities

public class Dept implements Serializable{

	private static final long serialVersionUID = 1L;
	
	private Integer deptNo;
	private String deptName;
	private String dbSource;
	……

4. SCMS-Provider-Dept8001创建过程

4.1. 创建Maven模块

在SCMS-Parent工程上点右键,然后new

4.2. 编辑pom.xml

	<dependency>
		<groupId>mysql</groupId>
		<artifactId>mysql-connector-java</artifactId>
	</dependency>
	<dependency>
		<groupId>com.alibaba</groupId>
		<artifactId>druid</artifactId>
	</dependency>
	<dependency>
		<groupId>ch.qos.logback</groupId>
		<artifactId>logback-core</artifactId>
	</dependency>
	<dependency>
		<groupId>org.mybatis.spring.boot</groupId>
		<artifactId>mybatis-spring-boot-starter</artifactId>
	</dependency>
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-jetty</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>
	<!-- 修改后立即生效,热部署 -->
	<dependency>
		<groupId>org.springframework</groupId>
		<artifactId>springloaded</artifactId>
	</dependency>
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-devtools</artifactId>
	</dependency>

4.3. 依赖SCMS-API的操作过程

4.4. 创建数据库和数据库表

DROP DATABASE IF EXISTS `cloud_db_one`;
CREATE DATABASE `cloud_db_one` CHARACTER SET utf8 COLLATE utf8_bin;
USE cloud_db_one;
DROP TABLE IF EXISTS `dept`;
CREATE TABLE `dept` (
  `dept_no` int(11) NOT NULL AUTO_INCREMENT,
  `dept_name` varchar(500) DEFAULT NULL,
  `db_source` varchar(500) DEFAULT NULL,
  PRIMARY KEY (`dept_no`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of dept
-- ----------------------------
INSERT INTO `dept` VALUES ('1', 'IT', DATABASE());
INSERT INTO `dept` VALUES ('2', 'HR', DATABASE());
INSERT INTO `dept` VALUES ('3', 'MIS', DATABASE());
INSERT INTO `dept` VALUES ('4', 'FUN', DATABASE());
INSERT INTO `dept` VALUES ('5', 'WM', DATABASE());
INSERT INTO `dept` VALUES ('6', 'EI', DATABASE());
INSERT INTO `dept` VALUES ('7', 'UU', DATABASE());

4.5. 整合MyBatis

4.5.1. 第一步:创建DeptMapper接口

包名:com.atguigu.springcloud.mapper
package com.atguigu.springcloud.mapper;

import java.util.List;

import org.apache.ibatis.annotations.Mapper;

import com.atguigu.cloud.entities.Dept;

@Mapper//千万记得加这个注解!!!
public interface DeptMapper {
	
	Dept findById(Integer deptNo);
	
	List<Dept> findAll();
	
	boolean addDept(Dept dept);

}

4.5.2. 第二步:创建DeptMapper.xml

位置:

<?xml version="1.0" encoding="UTF-8" ?>
<select id="findById" resultType="Dept" parameterType="Integer">
	select dept_no deptNo,dept_name deptName,db_source dbSource from dept where dept_no=#{deptNo};
</select>
<select id="findAll" resultType="Dept">
	select dept_no deptNo,dept_name deptName,db_source dbSource from dept;
</select>
<insert id="addDept" parameterType="Dept">
	INSERT INTO dept(dept_name,db_source) VALUES(#{deptName},DATABASE());
</insert>

4.5.3. 第三步:创建mybatis.cfg.xml

位置:

<?xml version="1.0" encoding="UTF-8" ?>
<configuration>

	<settings>
		<setting name="cacheEnabled" value="true" />
	</settings>

</configuration>

4.5.4. 第四步:创建application.yml

位置:
server:
port: 8001
mybatis:
config-location: classpath:mybatis/mybatis.cfg.xml
type-aliases-package: com.atguigu.cloud.entities
mapper-locations:

  • classpath:mybatis/mapper/**/*.xml

spring:
application:
name: Atguigu-SCMS-Dept
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: org.gjt.mm.mysql.Driver
url: jdbc:mysql://localhost:3306/cloud_db_one
username: root
password: root
dbcp2:
min-idle: 5
initial-size: 5
max-total: 5
max-wait-millis: 200
spring.application.name配置的是这个微服务的名字,将来在注册中心中显示的和消费端引用的都是这个名字。
4.6. Service层
4.6.1. 接口
package com.atguigu.springcloud.service;

import java.util.List;

import com.atguigu.cloud.entities.Dept;

public interface DeptService {
Dept findById(Integer deptNo);

List<Dept> findAll();

boolean addDept(Dept dept);

}
4.6.2. 实现类
package com.atguigu.springcloud.service.impl;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.atguigu.cloud.entities.Dept;
import com.atguigu.springcloud.mapper.DeptMapper;
import com.atguigu.springcloud.service.DeptService;

@Service
public class DeptServiceImpl implements DeptService {

@Autowired
private DeptMapper deptMapper;

@Override
public Dept findById(Integer deptNo) {
	return deptMapper.findById(deptNo);
}

@Override
public List<Dept> findAll() {
	return deptMapper.findAll();
}

@Override
public boolean addDept(Dept dept) {
	return deptMapper.addDept(dept);
}

}

4.7. Controller层
package com.atguigu.springcloud.controller;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import com.atguigu.cloud.entities.Dept;
import com.atguigu.springcloud.service.DeptService;

@RestController//相当于@Controller+@ResponseBody
public class DeptController {

@Autowired
private DeptService deptService;

@RequestMapping(value="/dept/add", method=RequestMethod.POST)
public boolean add(@RequestBody Dept dept) {
	
	return deptService.addDept(dept);
}

@RequestMapping(value="/dept/get/{deptNo}", method=RequestMethod.GET)
public Dept get(@PathVariable("deptNo") Integer deptNo) {
	return deptService.findById(deptNo);
}

@RequestMapping(value="/dept/get/all", method=RequestMethod.GET)
public List<Dept> getAll() {
	return deptService.findAll();
}

}

4.8. 主程序
package com.atguigu.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class SCMSProviderDept8001 {

public static void main(String[] args) {
	SpringApplication.run(SCMSProviderDept8001.class, args);
}

}
4.9. 测试
以Spring Boot App的方式运行工程
通过浏览器访问http://localhost:8001/dept/get/all
5. SCMS-Consummer-Dept80创建过程
5.1. 创建Maven模块
在SCMS-Parent工程上点右键,然后new

5.2. 编辑pom.xml

org.springframework.boot
spring-boot-starter-web


org.springframework
springloaded


org.springframework.boot
spring-boot-devtools


com.atguigu.scms
SCMS-API
0.0.1-SNAPSHOT

5.3. 创建application.yml

server:
port: 80
5.4. 创建ConfigBean类
package com.atguigu.cloud.config.beans;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

@Configuration
public class ConfigBean {

@Bean
public RestTemplate getRestTemplate() {
	return new RestTemplate();
}

}
相当于

5.5. 创建DeptControllerConsummer类
package com.atguigu.cloud.controller;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import com.atguigu.cloud.entities.Dept;

@RestController
public class DeptControllerConsummer {

private static final String REST_URL_PREFIX = "http://localhost:8001";

@Autowired
private RestTemplate restTemplate;

@RequestMapping("/consummer/dept/add")
public boolean add(Dept dept) {
	return restTemplate.postForObject(REST_URL_PREFIX+"/dept/add", dept, Boolean.class);
}

@RequestMapping("/consummer/dept/get/{deptNo}")
public Dept get(@PathVariable("deptNo") Integer deptNo) {
	return restTemplate.getForObject(REST_URL_PREFIX+"/dept/get/"+deptNo, Dept.class);
}

@SuppressWarnings("unchecked")
@RequestMapping("/consummer/dept/get/all")
public List<Dept> getAll() {
	return restTemplate.getForObject(REST_URL_PREFIX+"/dept/get/all", List.class);
}

}

5.6. 主程序
5.7. 测试
以Spring Boot App方式运行程序
访问http://localhost/consummer/dept/get/all
6. 遗留问题
Consummer通过http://localhost:8001来访问Provider中提供的服务需要知道IP地址和端口号,但是Provider微服务是有名字的,如果能够通过名字代替IP地址和端口号就能够简单很多,该如何做到呢?
我们下面要学习的Eureka注册中心可以解决这个问题。

SpringCloud 单机版Eureka

  1. 简介
    Eureka相当于我们熟悉的Zookeeper,用来注册我们的微服务。

  2. 创建SCMS-Eureka-7000工程模块
    2.1. 创建Maven模块

2.2. 编辑pom.xml

org.springframework.cloud spring-cloud-starter-eureka-server org.springframework springloaded org.springframework.boot spring-boot-devtools

2.3. 创建application.yml
server:
port: 7000
eureka:
instance:
hostname: localhost #eureka服务端的实例名称
client:
register-with-eureka: false #false表示不向注册中心注册自己。
fetch-registry: false #false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
service-url:
defaultZone: http:// e u r e k a . i n s t a n c e . h o s t n a m e : {eureka.instance.hostname}: eureka.instance.hostname:{server.port}/eureka/ #指定客户端访问Eureka服务端的URL地址

2.4. 主程序
@SpringBootApplication
@EnableEurekaServer //把当前微服务标记为Eureka注册中心,接受其他微服务的注册
public class SpringCloudEurekaApp {

public static void main(String[] args) {
	SpringApplication.run(SpringCloudEurekaApp.class, args);
}

}

2.5. 测试
以Spring Boot App方式运行工程
访问http://localhost:7000

  1. 将SCMS-Provider-Dept8001注册进Eureka
    3.1. 编辑pom.xml


    org.springframework.cloud
    spring-cloud-starter-eureka


    org.springframework.cloud
    spring-cloud-starter-config

    3.2. 编辑application.yml
    eureka:
    client:
    service-url:
    defaultZone: http://localhost:7000/eureka
    参照:defaultZone: http:// e u r e k a . i n s t a n c e . h o s t n a m e : {eureka.instance.hostname}: eureka.instance.hostname:{server.port}/eureka/
    3.3. 修改SCMS-Provider-Dept8001主程序
    @SpringBootApplication
    @EnableEurekaClient //该微服务将参照application.yml中的配置注册到Eureka
    public class SCMSProviderDept8001 {

    public static void main(String[] args) {
    SpringApplication.run(SCMSProviderDept8001.class, args);
    }

}

3.4. 测试
在Eureka微服务已经启动的前提下,启动SCMS-Provider-Dept8001。

  1. 在SCMS-Consummer-Dept80中通过Eureka中注册的服务器名访问微服务
    4.1. 编辑pom.xml

    org.springframework.cloud
    spring-cloud-starter-eureka


    org.springframework.cloud
    spring-cloud-starter-ribbon


    org.springframework.cloud
    spring-cloud-starter-config

4.2. 编辑application.yml
eureka:
client:
register-with-eureka: false #当前是微服务的调用端、消费端,不进行注册
service-url:
defaultZone: http://localhost:7000/eureka
4.3. 修改SCMS-Consummer-Dept80主程序
@SpringBootApplication
@EnableEurekaClient
public class SCMSConsummer80 {

public static void main(String[] args) {
	SpringApplication.run(SCMSConsummer80.class, args);
}

}
4.4. 修改ConfigBean
@Configuration
public class ConfigBean {

@Bean
@LoadBalanced
public RestTemplate getRestTemplate() {
	return new RestTemplate();
}

}
这里其实已经用到了Ribbon,这是SpringCloud技术栈中的负载均衡工具,后面会讲到,现在你先知道Eureka需要和Ribbon配合起来才能实现服务名替代效果。
4.5. 用微服务名替代IP地址加端口号
//private static final String REST_URL_PREFIX = “http://localhost:8001”;
private static final String REST_URL_PREFIX = “http://ATGUIGU-SCMS-DEPT”;

SpringCloud 集群版Eureka

  1. 集群方案
    SCMS-Eureka-7000
    通过域名www.eureka7000.com访问
    SCMS-Eureka-7001
    通过域名www.eureka7001.com访问
    SCMS-Eureka-7002
    通过域名www.eureka7002.com访问
  2. 操作步骤
    2.1. 配置域名

2.2. 创建SCMS-Eureka-7001和SCMS-Eureka-7002工程
类似于创建SCMS-Eureka-7000
2.3. 编辑pom.xml


org.springframework.cloud
spring-cloud-starter-eureka-server



org.springframework
springloaded


org.springframework.boot
spring-boot-devtools

2.4. 编辑application.yml
2.4.1. 7000

server:
port: 7000
eureka:
instance:
hostname: localhost #eureka服务端的实例名称
client:
register-with-eureka: false #false表示不向注册中心注册自己。
fetch-registry: false #false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
service-url:
defaultZone: http://www.eureka7001.com:7001/eureka/,http://www.eureka7002.com:7002/eureka/ #指定客户端访问Eureka服务端的URL地址
2.4.2. 7001
配置7000和7002的地址
2.4.3. 7002
配置7000和7001的地址
2.5. 测试
分别以Spring Boot App方式运行三个工程,访问其中一个
http://www.eureka7000.com:7000/

  1. 访问集群
    3.1. 编辑SCMS-Provider-Dept8001的yml文件
    defaultZone: http://www.eureka7000.com:7000/eureka,http://www.eureka7001.com:7001/eureka,http://www.eureka7002.com:7002/eureka

3.2. 启动SCMS-Provider-Dept8001
3.3. 查看服务注册情况

SpringCloud Ribbon

  1. 简介
    SpringCloud技术栈中的Nginx。但是Ribbon和Nginx有一个重大区别:Ribbon是一个客户端负载均衡解决方案。
    按照我们前面例子的架构组成,Ribbon需要配置在SCMS-Consummer-Dept80工程中(其实我们已经配置好了)。
    Ribbon在工作时分成两步:
    第一步:先确定Eureka集群中的一个EurekaServer,它优先选择在同一个区域内负载较少的。
    第二步:再根据用户指定的策略,在从EurekaServer取到的服务注册列表中选择一个微服务地址。
    Ribbon提供了多种负载均衡策略:比如轮询、随机和根据响应时间加权。
  2. 搭建Provider集群
    2.1. 创建另外两个数据库
    DROP DATABASE IF EXISTS cloud_db_two;
    CREATE DATABASE cloud_db_two CHARACTER SET utf8 COLLATE utf8_bin;
    USE cloud_db_two;
    DROP TABLE IF EXISTS dept;
    CREATE TABLE dept (
    dept_no int(11) NOT NULL AUTO_INCREMENT,
    dept_name varchar(500) DEFAULT NULL,
    db_source varchar(500) DEFAULT NULL,
    PRIMARY KEY (dept_no)
    ) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8;

– Records of dept


INSERT INTO dept VALUES (‘1’, ‘IT’, DATABASE());
INSERT INTO dept VALUES (‘2’, ‘HR’, DATABASE());
INSERT INTO dept VALUES (‘3’, ‘MIS’, DATABASE());
INSERT INTO dept VALUES (‘4’, ‘FUN’, DATABASE());
INSERT INTO dept VALUES (‘5’, ‘WM’, DATABASE());
INSERT INTO dept VALUES (‘6’, ‘EI’, DATABASE());
INSERT INTO dept VALUES (‘7’, ‘UU’, DATABASE());
DROP DATABASE IF EXISTS cloud_db_three;
CREATE DATABASE cloud_db_three CHARACTER SET utf8 COLLATE utf8_bin;
USE cloud_db_three;
DROP TABLE IF EXISTS dept;
CREATE TABLE dept (
dept_no int(11) NOT NULL AUTO_INCREMENT,
dept_name varchar(500) DEFAULT NULL,
db_source varchar(500) DEFAULT NULL,
PRIMARY KEY (dept_no)
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8;


– Records of dept


INSERT INTO dept VALUES (‘1’, ‘IT’, DATABASE());
INSERT INTO dept VALUES (‘2’, ‘HR’, DATABASE());
INSERT INTO dept VALUES (‘3’, ‘MIS’, DATABASE());
INSERT INTO dept VALUES (‘4’, ‘FUN’, DATABASE());
INSERT INTO dept VALUES (‘5’, ‘WM’, DATABASE());
INSERT INTO dept VALUES (‘6’, ‘EI’, DATABASE());
INSERT INTO dept VALUES (‘7’, ‘UU’, DATABASE());

2.2. 新建8002和8003工程
SCMS-Provider-Dept8002
SCMS-Provider-Dept8003
pom.xml文件内容和8001一致,Java代码也一致
2.3. 修改8002和8003的yml文件
从8001那里把application.yml复制过来,然后修改下面配置项
server.port: 8002
spring.datasource.url: jdbc:mysql://localhost:3306/cloud_db_two

server.port: 8003
spring.datasource.url: jdbc:mysql://localhost:3306/cloud_db_three

注意:8001和8002以及8003要想成为一个集群,分担负载,那么对外暴露的微服务名称必须一致。也就是说,spring.application.name配置项要一致。这里我们都是复制过来的,不修改就可以了。
2.4. 启动Provider集群并测试
http://localhost:8001/dept/get/all
http://localhost:8002/dept/get/all
http://localhost:8003/dept/get/all
3. 负载均衡测试
我们前面为了Consummer80工程能够通过微服务名称调用微服务其实已经完成Ribbon的配置了,这里再重申一下Ribbon的配置:
3.1. 确认pom.xml文件

org.springframework.cloud
spring-cloud-starter-eureka


org.springframework.cloud
spring-cloud-starter-ribbon


org.springframework.cloud
spring-cloud-starter-config

3.2. 确认ConfigBean中getRestTemplate()方法注解
@Configuration
public class ConfigBean {

@Bean
@LoadBalanced
public RestTemplate getRestTemplate() {
	return new RestTemplate();
}

}

3.3. 访问Consummer80工程
http://localhost/consummer/dept/get/all
如果看到不同请求访问时看到的数据库名数据不一样那么说明负载均衡配置成功。

SpringCloud Feign

  1. 简介
    Feign是对Ribbon的进一步封装。微服务模块中加入了Feign后将同时具备Ribbon和RestTemplate的功能,进一步简化了代码。
  2. 修改SCMS-API工程
    2.1. 加入Feign依赖

    org.springframework.cloud
    spring-cloud-starter-feign

2.2. 创建DeptClientService接口
package com.atguigu.cloud.service;

import java.util.List;

import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import com.atguigu.cloud.entities.Dept;

//指定要调用的微服务名称
@FeignClient(value=“ATGUIGU-SCMS-DEPT”)
public interface DeptClientService {

@RequestMapping(value="/dept/add", method=RequestMethod.POST)
public boolean add(Dept dept);

@RequestMapping(value="/dept/get/{deptNo}", method=RequestMethod.GET)
public Dept get(@PathVariable("deptNo") Integer deptNo);

@RequestMapping(value="/dept/get/all", method=RequestMethod.GET)
public List<Dept> getAll();

}
3. 创建SCMS-Consummer-Feign工程
SCMS-Consummer-Feign工程是SCMS-Consummer-Dept80的升级版,所以有些配置相似。
3.1. 创建Maven模块

pom.xml文件和SCMS-Consummer-Dept80工程一致。
3.2. 创建Controller
这时不再需要RestTemplate了,Feign中也内置了Ribbon。
package com.atguigu.cloud.controller;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import com.atguigu.cloud.entities.Dept;
import com.atguigu.cloud.service.DeptClientService;

@RestController
public class FeignController {

@Autowired
private DeptClientService feignService;

@RequestMapping(value="/consummer/feign/dept/add",method=RequestMethod.POST)
public boolean add(Dept dept) {
	return feignService.add(dept);
}

@RequestMapping(value="/consummer/feign/dept/get/{deptNo}",method=RequestMethod.GET)
public Dept getById(@PathVariable("deptNo") Integer deptNo) {
	return feignService.get(deptNo);
}

@RequestMapping(value="/consummer/feign/dept/get/all", method=RequestMethod.GET)
public List<Dept> getList() {
	return feignService.getAll();
}

}

3.3. 创建主程序
package com.atguigu.cloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.feign.EnableFeignClients;

@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
public class SCMSConsummerFeigh {

public static void main(String[] args) {
	SpringApplication.run(SCMSConsummerFeigh.class, args);
}

}

  1. 测试Feign程序
    需要启动的微服务有:
    SCMS-Eureka-7000
    SCMS-Eureka-7001
    SCMS-Eureka-7002
    SCMS-Provider-Dept8001
    SCMS-Provider-Dept8002
    SCMS-Provider-Dept8003
    SCMS-Consummer-Feign

SpringCloud Hystrix

  1. 简介
    1.1. 分布式系统面临的问题

在微服务架构体系下,服务间的调用错综复杂,交织成一张大网。如果其中某个节点突然无法正常工作,则访问它的众多服务都会被卡住,进而有更多服务被卡住,系统中的线程、CPU、内存等资源有可能被迅速耗尽,最终整个服务体系崩溃。
我们管这样的现象叫服务雪崩。

1.2. Hytrix介绍
Hystrix是一个用于处理分布式系统的延迟和容错的开源库,在分布式系统里,许多依赖不可避免的会调用失败,比如超时、异常等,Hystrix能够保证在一个依赖出问题的情况下,不会导致整体服务失败,避免级联故障,以提高分布式系统的弹性。

“断路器”本身是一种开关装置,当某个服务单元发生故障之后,通过断路器的故障监控(类似熔断保险丝),向调用方返回一个符合预期的、可处理的备选响应(FallBack),而不是长时间的等待或者抛出调用方无法处理的异常,这样就保证了服务调用方的线程不会被长时间、不必要地占用,从而避免了故障在分布式系统中的蔓延,乃至雪崩。
Hytrix能够提供服务降级、服务熔断、服务限流、接近实时的监控等方面的功能。
2. 服务熔断机制
熔断机制是应对雪崩效应的一种微服务链路保护机制。
当扇出链路的某个微服务不可用或者响应时间太长时,会进行服务的降级,进而熔断该节点微服务的调用,快速响应错误信息。当检测到该节点微服务调用响应正常后恢复调用链路。在SpringCloud框架里熔断机制通过Hystrix实现。Hystrix会监控微服务间调用的状况,当失败的调用到一定阈值,缺省是5秒内20次调用失败就会启动熔断机制。熔断机制的注解是@HystrixCommand。
2.1. 创建SCMS-Provider-Hystrix-8004工程

2.2. 编辑pom.xml
在SCMS-Provider-Dept8001基础上添加Hystrix依赖

org.springframework.cloud
spring-cloud-starter-hystrix

2.3. 编辑yml文件
在SCMS-Provider-Dept8001基础上修改端口号和应用名称
server:
port: 8004
spring:
application:
name: Atguigu-SCMS-Hystrix-Dept
2.4. Java代码
把SCMS-Provider-Dept8001的Java代码复制过来。
2.4.1. 修改DeptController
我们发现@HystrixCommand注解的fallbackMethod属性指定的方法入参和返回值都和原方法一致。这就保证了原方法出现问题时可以由fallbackMethod方法来接替,从而返回一个可以让调用者继续执行其他任务的响应信息。
package com.atguigu.springcloud.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import com.atguigu.cloud.entities.Dept;
import com.atguigu.springcloud.service.DeptService;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;

@RestController
public class DeptController {

@Autowired
private DeptService deptService;

@RequestMapping(value="/dept/get/{deptNo}", method=RequestMethod.GET)
@HystrixCommand(fallbackMethod="hystrixGet")
public Dept get(@PathVariable("deptNo") Integer deptNo) {
	
	Dept dept = deptService.findById(deptNo);
	
	if(dept == null) {
		throw new RuntimeException("未找到和"+deptNo+"对应的信息!");
	}
	
	return dept;
}

public Dept hystrixGet(@PathVariable("deptNo") Integer deptNo) {
	
	return new Dept(deptNo, "与"+deptNo+"对应的Dept信息未找到!", "对应数据库信息未找到!");
}

}

2.4.2. 修改主程序
@SpringBootApplication
@EnableEurekaClient
@EnableCircuitBreaker //启用对Hystrix熔断机制的支持
public class SCMSProviderHystrixDept {

public static void main(String[] args) {
	SpringApplication.run(SCMSProviderHystrixDept.class, args);
}

}

2.5. 测试
这时启动Eureka集群和SCMS-Provider-Hystrix-8004工程即可。
http://localhost:8004/dept/get/3
{“deptNo”:3,“deptName”:“MIS”,“dbSource”:“cloud_db_three”}

http://localhost:8004/dept/get/344
{“deptNo”:344,“deptName”:“与”+deptNo+“对应的Dept信息未找到!”,“dbSource”:“对应数据库信息未找到!”}
3. 服务降级机制
3.1. 简介
服务降级处理是在客户端(Consummer端)实现完成的,与服务端(Provider端)没有关系。当某个Consummer访问一个Provider却迟迟得不到响应时执行预先设定好的一个解决方案,而不是一直等待。
3.2. 修改SCMS-API工程
3.2.1. 创建一个工厂类实现FallbackFactory接口
package com.atguigu.cloud.service.factory;

import java.util.List;

import org.springframework.stereotype.Component;

import com.atguigu.cloud.entities.Dept;
import com.atguigu.cloud.service.DeptClientService;

import feign.hystrix.FallbackFactory;

@Component //非常容易忘!!!
public class DeptClientServiceFallBackFactory implements FallbackFactory {

@Override
public DeptClientService create(Throwable throwable) {
	
	return new DeptClientService() {
		
		@Override
		public Dept get(Integer deptNo) {
			return new Dept(deptNo, "与"+deptNo+"对应的信息未找到[Consummer]", "数据库不存在");
		}
		
		@Override
		public List<Dept> getAll() {
			return null;
		}
		
		@Override
		public boolean add(Dept dept) {
			// TODO Auto-generated method stub
			return false;
		}
	};
	
}

}

3.2.2. 修改DeptClientService接口的@FeignClient注解
@FeignClient(
value=“ATGUIGU-SCMS-DEPT”,
fallbackFactory=DeptClientServiceFallBackFactory.class)
public interface DeptClientService {

3.3. 修改SCMS-Consummer-Feign工程的yml文件
添加:
feign:
hystrix:
enabled: true
3.4. 测试
启动Eureka集群
启动一个没有加熔断机制的Provider
启动Feign Consummer

先测试正常访问,然后关掉Provider后再访问
4. 服务监控
4.1. 新建Maven模块

4.2. 编辑pom.xml

org.springframework.boot
spring-boot-starter-web



org.springframework.cloud
spring-cloud-starter-hystrix


org.springframework.cloud
spring-cloud-starter-hystrix-dashboard

4.3. 创建yml文件
server:
port: 9001
4.4. 主程序
@SpringBootApplication
@EnableHystrixDashboard
public class SpringCloudHytrixDashBord {

public static void main(String[] args) {
	SpringApplication.run(SpringCloudHytrixDashBord.class, args);
}

}

yml 配置
server:
port: 9001
4.5. 给被监控的微服务添加依赖

org.springframework.boot
spring-boot-starter-actuator

注意:被监控的微服务也需要配置好熔断机制才可以。目前我们的工程中SCMS-Provider-Hystrix-8004满足这个要求。
4.6. 查看监控信息
4.6.1. 监控首页
http://localhost:9001/hystrix

4.6.2. 直接查看监控数据
http://localhost:8004/hystrix.stream
8004是被监控的微服务端口

4.6.3. 图形化界面方式查看监控数据

Delay:该参数用来控制服务器上轮询监控信息的延迟时间,默认为2000毫秒,可以通过配置该属性来降低客户端的网络和CPU消耗。

Title:该参数对应了头部标题Hystrix Stream之后的内容,默认会使用具体监控实例的URL,可以通过配置该信息来展示更合适的标题。

4.6.4. 参数说明

4.6.5. 复杂监控效果示例

SpringCloud Zuul

  1. 简介
    不同的微服务一般有不同的网络地址,而外部的客户端可能需要调用多个服务的接口才能完成一个业务需求。比如一个电影购票的手机APP,可能会调用电影分类微服务,用户微服务,支付微服务等。如果客户端直接和微服务进行通信,会存在以下问题:
     客户端会多次请求不同微服务,增加客户端的复杂性
     存在跨域请求,在一定场景下处理相对复杂
     认证复杂,每一个服务都需要独立认证
     难以重构,随着项目的迭代,可能需要重新划分微服务,如果客户端直接和微服务通信,那么重构会难以实施
     某些微服务可能使用了其他协议,直接访问有一定困难
    Zuul包含了对请求的路由和过滤两个最主要的功能:
    其中路由功能负责将外部请求转发到具体的微服务实例上,是实现外部访问统一入口的基础而过滤器功能则负责对请求的处理过程进行干预,是实现请求校验、服务聚合等功能的基础。
    Zuul和Eureka进行整合,将Zuul自身注册为Eureka服务治理下的应用,同时从Eureka中获得其他微服务的消息,也即以后的访问微服务都是通过Zuul跳转后获得。
    总体来说,Zuul提供了代理、路由和过滤的功能。
  2. 创建SCMS-Zuul-9002工程
    2.1. 创建Maven模块

2.2. 编辑pom.xml


org.springframework.cloud
spring-cloud-starter-zuul


org.springframework.cloud
spring-cloud-starter-eureka



org.springframework.boot
spring-boot-starter-actuator



org.springframework.cloud
spring-cloud-starter-hystrix


org.springframework.cloud
spring-cloud-starter-config



org.springframework.boot
spring-boot-starter-jetty


org.springframework.boot
spring-boot-starter-web


org.springframework.boot
spring-boot-starter-test



org.springframework
springloaded


org.springframework.boot
spring-boot-devtools

2.3. 编辑yml文件
server:
port: 9002
spring:
application:
name: scms-zuul-gateway //网关在eurka上注册的服务名;
eureka:
client:
service-url:
defaultZone: http://www.eureka7000.com:7000/eureka,http://www.eureka7001.com:7001/eureka,http://www.eureka7002.com:7002/eureka
2.4. 主程序
@SpringBootApplication
@EnableZuulProxy
public class SpringCloudZuulApp {

public static void main(String[] args) {
	SpringApplication.run(SpringCloudZuulApp.class, args);
}

}
2.5. 初步测试
启动Eureka集群
启动SCMS-Provider-Hystrix-8004供访问测试
启动SCMS-Zuul-9002
查看Eureka上注册的服务

不用路由直接访问http://localhost:8004/dept/get/3
通过路由访问
http://localhost:9002/atguigu-scms-hystrix-dept/dept/get/3
3. 路由访问映射规则
3.1. 隐藏微服务名称
在Zuul工程的application.yml中配置:
zuul:
routes:
atguigu.serviceId: atguigu-scms-hystrix-dept (atguigu可以省略直接写serviceId)
atguigu.path: /atguigu-demo/**
访问方式http://localhost:9002/atguigu-demo/dept/get/2
3.2. 原真实服务名忽略
经过上面的配置后,既可以使用别名访问,又可以使用原服务名访问。
例如http://localhost:9003/atguigu-scms-hystrix-dept/dept/get/2
如果希望只能通过别名访问,可以做这样的配置:
zuul:
ignored-services: atguigu-scms-hystrix-dept
routes:
atguigu.serviceId: atguigu-scms-hystrix-dept
atguigu.path: /atguigu-demo/**
如果希望屏蔽所有微服务原名称可以使用“
zuul:
ignored-services: "
"
routes:
atguigu.serviceId: atguigu-scms-hystrix-dept
atguigu.path: /atguigu-demo/**
3.3. 统一设置公共前缀
zuul:
prefix: /good
ignored-services: “*”
routes:
atguigu.serviceId: atguigu-scms-hystrix-dept
atguigu.path: /atguigu-demo/**
设置后所有微服务名前面都需要再加上前缀才可以访问

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值