SpringCloud微服务搭建

SpringCloud微服务搭建

具体关于SpringCloud介绍请参考其他文章,下面主要讲解SpringCloud搭建微服务的步骤,其中用到了Eurake、Ribbon、Feign、Hystrix、Zuul、Config技术。
本次案例主要分为以下几个微服务

  • 提供者服务用于操作user表,进行增删改查;
  • 消费者服务用于请求提供者服务,进行增删改查;
  • 公共模块微服务,用于提供其他微服务共同需要的功能;

本次案例用的工具版本如下
eclipse : Mars
JDK : 1.8
SpringCloud : Dalston
SpringBoot : 1.5.9

一、建立一个总工程

首先建立一个microservice-parent总工程,总工程中并不做什么业务逻辑,总工程主要定义一个POM文件,将后续各个微服公用的一些jar包在总工程的pom中进行导入。为方便起见,建立一个working set用于归类所有的微服务,命名为springcloudset。然后建立总工程
New -> Maven -> Maven Project
在这里插入图片描述
注意,Packaging模式要选择pom模式,不要选择jar模式。
POM文件如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

	<modelVersion>4.0.0</modelVersion>
	<groupId>com.lzj.springcloud</groupId>
	<artifactId>microservice-parent</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>pom</packaging>

	<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>
		<lombok.version>1.16.18</lombok.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.1.46</version>
			</dependency>
			<dependency>
			    <groupId>com.alibaba</groupId>
			    <artifactId>druid</artifactId>
			    <version>1.1.0</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>
</project>

二、建立公共微服

然后建立一个microservice-com的公共模块,用于提供后续微服需要的公共的东西。创建好公共模块,其它微服务需要的话,不用再创建,直接引用该模块即可。
1、下面在总工程下,即microservice-parent下创建第一个微服务microservice-com模块。
New -> Maven -> Maven Module
这里写图片描述

这里写图片描述
注意,Packaging不要选择pom模式,而应该选择ja模式,后续的微服都是如此。
pom.xml内容为:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<!-- 继承父工程 -->
	<parent>
		<groupId>com.lzj.springcloud</groupId>
		<artifactId>microservice-parent</artifactId>
		<version>0.0.1-SNAPSHOT</version>
	</parent>
	<!-- 当前工程的名字 -->
	<artifactId>microservice-com</artifactId>
	<dependencies><!-- 当前Module需要用到的jar包,按自己需求添加,如果父类已经包含了,可以不用写版本号 -->
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
		</dependency>
	</dependencies>
</project>
  •  

2、然后在该模块中建立一个User的bean,以供其它微服务进行调用。

package com.lzj.springcloud.entity;
public class User {
	private int id;
	private String name;
	private int age;
	
	public User() {
		super();
	}

	public User(int id, String name, int age) {
		super();
		this.id = id;
		this.name = name;
		this.age = age;
	}

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	@Override
	public String toString() {
		return "User [id=" + id + ", name=" + name + ", age=" + age + "]";
	}
	
}

3、创建完公共微服后,执行mvn clean install进行装载。
创建完microservice-com模块后,在microservice-parent父工程中的pom.xml文件中增加了

	<modules>
		<module>microservice-com</module>
	</modules>

microservice-com工程目录如下;
这里写图片描述
三、建立microservice-provider微服务

1、与建立microservice-com服务类似,建立一个microservice-provider微服务
2、该服务用于提供直接操作数据库user表。首先在pom中配置依赖的包,操作数据库需要的数据库驱动、数据源等。

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>com.lzj.springcloud</groupId>
		<artifactId>microservice-parent</artifactId>
		<version>0.0.1-SNAPSHOT</version>
	</parent>

	<artifactId>microservice-provider</artifactId>

	<dependencies>
		<dependency>
			<groupId>com.lzj.springcloud</groupId>
			<artifactId>microservice-com</artifactId>
			<version>${project.version}</version>
		</dependency>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
		</dependency>
		<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>
		</dependency>
		<!-- 修改后立即生效,热部署 -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>springloaded</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-devtools</artifactId>
		</dependency>
	</dependencies>

</project>

在microservice-provider微服找那个需要引用microservice-com微服中User bean,所以在pom文件中引入了microservice-com的依赖。

		<dependency>
			<groupId>com.lzj.springcloud</groupId>
			<artifactId>microservice-com</artifactId>
			<version>${project.version}</version>
		</dependency>

3、在microservice-provider需要操作数据库,在application.yml中配置mybatis和数据源如下:

server: 
  port: 8002

mybatis: 
  config-location: "classpath:mybatis/mybatis.cfg.xml"        # mybatis配置文件所在路径
  mapper-locations: 
    - "classpath:mybatis/mapper/**/*.xml"                     # mapper映射文件
  type-aliases-package: com.lzj.springcloud.entity            # 别名类所在包

spring: 
  application: 
    name: microservicecloud-provider                          #微服务的名字
  datasource: 
    driver-class-name: org.gjt.mm.mysql.Driver                # mysql驱动包      
    type: com.alibaba.druid.pool.DruidDataSource              # 当前数据源操作类型
    url: "jdbc:mysql://localhost:3306/lzj"                    # 数据库名称
    username: root
    password: lzjlzj
    dbcp2: 
      initial-size: 5                                         # 初始化连接数
      max-total: 5                                            # 最大连接数
      max-wait-millis: 200                                    # 等待连接获取的最大超时时间
      min-idle: 5                                             # 数据库连接池的最小维持连接数

4、在src/main/resources目录下创建mybatis文件夹后新建mybatis.cfg.xml文件,内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>

	<!-- 开启二级缓存 -->
	<settings> 
    	<setting name="cacheEnabled" value="true"/>  
	</settings> 

</configuration>

5、创建Dao接口,用于操作user表的接口

package com.lzj.springcloud.dao;
import java.util.List;
import org.apache.ibatis.annotations.Mapper;
import com.lzj.springcloud.entity.User;

@Mapper
public interface UserDao {

	public boolean addUser(User user);
	
	public User getUser(int id);
	
	public List<User> getUsers();
}

6、在src/main/resources/mybatis目录下创建mapper目录,并在mapper目录下创建UserMapper.xml文件,内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.lzj.springcloud.dao.UserDao">

    <select id="getUser" resultType="User" parameterType="int">
        select * from user where ID=#{id}
    </select>
    
    <select id="getUsers" resultType="User">
    	select * from user
    </select>
    
    <insert id="addUser" parameterType="User">
    	insert into user(NAME, AGE) values(#{name}, #{age})
    </insert>
    
</mapper>

7、创建UserService服务接口

package com.lzj.springcloud.service;
import java.util.List;
import com.lzj.springcloud.entity.User;
public interface UserService {
	
	public boolean addUser(User user);
	
	public User getUser(int id);
	
	public List<User> getUsers();
}

8、创建UserServiceImpl接口的实现

package com.lzj.springcloud.service.impl;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.lzj.springcloud.dao.UserDao;
import com.lzj.springcloud.entity.User;
import com.lzj.springcloud.service.UserService;

@Service
public class UserServiceImpl implements UserService {

	@Autowired
	private UserDao userDao;
	
	@Override
	public boolean addUser(User user) {
		boolean flag;
		flag = userDao.addUser(user);
		return flag;
	}

	@Override
	public User getUser(int id) {
		User user = userDao.getUser(id);
		return user;
	}

	@Override
	public List<User> getUsers() {
		List<User> users = userDao.getUsers();
		return users;
	}

}

9、创建Controller层,用于相应REST请求

package com.lzj.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.lzj.springcloud.entity.User;
import com.lzj.springcloud.service.UserService;

@RestController
public class UserController {

	@Autowired
	private UserService service;
	
	@RequestMapping(value="/add", method=RequestMethod.POST)
	public boolean addUser(@RequestBody User user){
		boolean flag = service.addUser(user);
		return flag;
	}
	
	@RequestMapping(value="/get/{id}", method=RequestMethod.GET)
	public User getUser(@PathVariable("id") int id){
		User user = service.getUser(id);
		return user;
	}
	
	@RequestMapping(value="/getUser/list", method=RequestMethod.GET)
	public List<User> getUsers(){
		List<User> users = service.getUsers();
		return users;
	}
}

10、创建microservice-provider的启动类

package com.lzj.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class ProviderApplication {

	public static void main(String[] args) {

		SpringApplication.run(ProviderApplication.class, args);
	}

}

11、测试microservice-provider微服
启动microservice-provider服务,然后在浏览器中发送请求 http://localhost:8002/getUser/list,相应结果如下
这里写图片描述
创建并配置完microservice-provider后,在microservice-parent的pom的文件中,module标签如下

	<modules>
		<module>microservice-com</module>
		<module>microservice-provider</module>
		<module>microservice-consumer</module>
	</modules>

microservice-provider工程目录如下
这里写图片描述

四、建立microservice-consumer微服务

microservice-consumer服务用于请求microservice-provicer
创建与microservice-com类似,在microservice-parent下创建microservice-consumer微服。
1、pom配置如下:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <parent>
    <groupId>com.lzj.springcloud</groupId>
    <artifactId>microservice-parent</artifactId>
    <version>0.0.1-SNAPSHOT</version>
  </parent>
  <artifactId>microservice-consumer</artifactId>
  
	<dependencies>
		<!-- 依赖microservice-com模块 -->
		<dependency>
			<groupId>com.lzj.springcloud</groupId>
			<artifactId>microservice-com</artifactId>
			<version>${project.version}</version>
		</dependency>
				<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<!-- 修改后立即生效,热部署 -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>springloaded</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-devtools</artifactId>
		</dependency>
	</dependencies>
</project>

热部署即为,工程每次修改完后,会重新部署。

2、application.yml配置文件如下:
配置访问端口

server: 
  port: 8003

3、配置RestTemplate的bean
RestTemplate用于模拟发送REST的客户端请求

package com.lzj.springcloud.configbean;
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();
	}
}

3、配置controller层

package com.lzj.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.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import com.lzj.springcloud.entity.User;

@RestController
public class UserConsumerController {
	private static String REST_URL_PREFIX = "http://localhost:8002";
	
	@Autowired
	private RestTemplate restTemplate;
	
	@RequestMapping(value="/consumer/add")
	public boolean addUser(User user){
		Boolean flag = restTemplate.postForObject(REST_URL_PREFIX + "/add", user, Boolean.class);
		return flag;
	}
	
	@RequestMapping(value="/consumer/get/{id}")
	public User get(@PathVariable("id") int id){
		User user = restTemplate.getForObject(REST_URL_PREFIX + "/get" + "/id", User.class);
		return user;
	}
	
	@SuppressWarnings({ "unchecked", "rawtypes" })
	@RequestMapping(value="/consumer/list")
	public List<User> getList(){
		List list = restTemplate.getForObject(REST_URL_PREFIX + "/getUser/list", List.class);
		return list;
	}
}

4、创建启动类

package com.lzj.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class ConsumerApplication {

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

}

5、测试
在浏览器中发送 http://localhost:8003/consumer/add?name=lzj5&age=35
相应如下:
这里写图片描述

发送请求后,在数据库user表中插入了一条数据
这里写图片描述
至此需要的微服务已经全部创建完毕,在microservice-parent的pom文件中可以看到总工程下有三个微服务。

 	<modules>
		<module>microservice-com</module>
		<module>microservice-provider</module>
		<module>microservice-consumer</module>
	</modules>

五、建立microservice-eurake1微服务

eureka微服务用于注册和发现服务。
首先建立一个依赖于microservice-parent的microservice-eurake1工程,与上面建立方法一样。
1、pom文件配置如下:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>com.lzj.springcloud</groupId>
		<artifactId>microservice-parent</artifactId>
		<version>0.0.1-SNAPSHOT</version>
	</parent>
	
	<artifactId>microservice-eurake1</artifactId>
  
	<dependencies>
		<!--eureka-server服务端 -->
		<dependency>
    		<groupId>org.springframework.cloud</groupId>
    		<artifactId>spring-cloud-netflix-eureka-server</artifactId>
		</dependency>
		<!-- 修改后立即生效,热部署 -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>springloaded</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-devtools</artifactId>
		</dependency>
	</dependencies>
  
</project>

如果出现“Failed to read artifact descriptor for org.springframework.cloud:spring-cloud-st”问题,请参考解决“Failed to read artifact descriptor for org.springframework.cloud:spring-cloud-st”

2、application.yml文件配置如下

eureka:
  instance:
    hostname: localhost
  client:
    register-with-eureka: false
    fetch-registry: false
    service-url:
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
      
server:
  port: 9001

3、创建启动类

package com.lzj.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

@SpringBootApplication
@EnableEurekaServer
public class EurekaApplication {

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

}

注意,一定不要忘记@EnableEurekaServer注解,该注解用于激活eureka的服务端。
现在可以启动microservice-eureka微服务了
这里写图片描述
正确创建eureka微服务后,启动页面如上图所示,只是还没有微服务注册进来。

5、把microservice-provider微服务注册进microservice-eureka服务中

  • 在microservice-provider微服务中的pom文件加入下面依赖
		<!-- 将微服务microservice-provider侧注册进eureka -->
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-eureka</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-config</artifactId>
		</dependency>		

microservice-provider中的pom文件如下:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>com.lzj.springcloud</groupId>
		<artifactId>microservice-parent</artifactId>
		<version>0.0.1-SNAPSHOT</version>
	</parent>

	<artifactId>microservice-provider</artifactId>

	<dependencies>
		<dependency>
			<groupId>com.lzj.springcloud</groupId>
			<artifactId>microservice-com</artifactId>
			<version>${project.version}</version>
		</dependency>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
		</dependency>
		<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>
		</dependency>
		<!-- 将微服务microservice-provider侧注册进eureka -->
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-eureka</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-config</artifactId>
		</dependency>		
		<!-- 修改后立即生效,热部署 -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>springloaded</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-devtools</artifactId>
		</dependency>
	</dependencies>

</project>
  • 在microservice-provider微服务中的application.yml文件中加入如下配置
    表示把microservice-provider微服务注册进http://localhost:9001/eureka指示的服务中,即microservice-eureka微服务中。
 eureka:
  client:
    service-url:
      defaultZone: http://localhost:9001/eureka

microservice-provider中application.yml配置文件如下:

server: 
  port: 8002

mybatis: 
  config-location: "classpath:mybatis/mybatis.cfg.xml"        # mybatis配置文件所在路径
  mapper-locations: 
    - "classpath:mybatis/mapper/**/*.xml"                     # mapper映射文件
  type-aliases-package: com.lzj.springcloud.entity            # 别名类所在包


spring: 
  application: 
    name: microservicecloud-provider                          #微服务的名字
  datasource: 
    driver-class-name: org.gjt.mm.mysql.Driver                # mysql驱动包      
    type: com.alibaba.druid.pool.DruidDataSource              # 当前数据源操作类型
    url: "jdbc:mysql://localhost:3306/lzj"                    # 数据库名称
    username: root
    password: lzjlzj
    dbcp2: 
      initial-size: 5                                         # 初始化连接数
      max-total: 5                                            # 最大连接数
      max-wait-millis: 200                                    # 等待连接获取的最大超时时间
      min-idle: 5                                             # 数据库连接池的最小维持连接数

eureka:
  client:
    service-url:
      defaultZone: http://localhost:9001/eureka
  • 在microservice-provider微服务中的启动类上加入@EnableEurekaClient注解。表示microservice-provider微服务启动时就启动eureka的客户端,该客户端自动的把microservice-provider服务注册进microservice-eureka1中。
package com.lzj.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

@SpringBootApplication
@EnableEurekaClient  //本服务启动后会自动注册进eureka服务中
public class ProviderApplication {

	public static void main(String[] args) {

		SpringApplication.run(ProviderApplication.class, args);
	}

}

6、测试microservice-provider服务已经注册进了microservice-eureka1服务。
启动microservice-eureka1微服务,然后再启动microservice-provider微服务,
在浏览器中请求http://localhost:9001/,反馈网页如下:
这里写图片描述
可见网页中Application那一栏多了一个MICROSERVICECLOUD-PROVIDER服务,此处显示的服务名字是在microservice-provider服务中的application.yml文件中配置的

spring: 
  application: 
    name: microservicecloud-provider                          #微服务的名字

为追求完美者,下面完善注册服务的信息,如果只是想达到功能,7、8、9可以不用看。
7、修改注册服务的主机名称
我们看到在上述的测试中,注册进eureka中的MICROSERVICE-PROVIDER服务,在状态一栏里面显示的是主机名称:服务名称的形式,主机默认是我本人主机名称,如下图所示。
这里写图片描述

下面修改microservicecloud-provider中的application.yml文件,关于eureka的部分改为如下:

eureka:
  client:
    service-url:
      defaultZone: http://localhost:9001/eureka
  instance:
    instance-id: microservicecloud-provider8002

重启服务,状态栏中显示的即为配置的instance-id名称。

对应关系如下
这里写图片描述

8、eureka中显示注册微服务的ip信息
当鼠标放在注册微服务的链接上,左下角显示注册微服务的ip信息,如下所示
这里写图片描述
左下角显示了microservice-provider微服务的ip信息,要想达到此效果,只需要microservice-provider服务中application.yml中eureka的配置改为如下

eureka:
  client:
    service-url:
      defaultZone: http://localhost:9001/eureka
  instance:
    instance-id: microservicecloud-provider8002               #自定义服务名称信息
    prefer-ip-address: true                                   #访问路径可以显示IP地址

9、点击注册微服务链接,显示微服务的info信息
这里写图片描述
当点击上图划红线的链接处,显示下面erro信息
这里写图片描述
下面定制当点击链接时,显示指定信息
首先在microservice-parent的pom中增加<build>的配置,pom如下所示

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

	<modelVersion>4.0.0</modelVersion>
	<groupId>com.lzj.springcloud</groupId>
	<artifactId>microservice-parent</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>pom</packaging>

	<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>
		<lombok.version>1.16.18</lombok.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.1.46</version>
			</dependency>
			<dependency>
			    <groupId>com.alibaba</groupId>
			    <artifactId>druid</artifactId>
			    <version>1.1.0</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>
	
	<build>
		<finalName>microservicecloud</finalName>
		<resources>
			<resource>
				<directory>src/main/resources</directory>
				<filtering>true</filtering>
			</resource>
		</resources>
		<plugins>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-resources-plugin</artifactId>
				<configuration>
					<delimiters>
						<delimit>$</delimit>
					</delimiters>
				</configuration>
			</plugin>
		</plugins>
	</build>	
	
	<modules>
		<module>microservice-com</module>
		<module>microservice-provider</module>
		<module>microservice-consumer</module>
		<module>microservice-eurake1</module>
	</modules>
</project>

然后再microservice-provider的服务中增加如下依赖

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-actuator</artifactId>
		</dependency>	

最后在microservice-provider中application.yml中配置显示的info信息

server: 
  port: 8002

mybatis: 
  config-location: "classpath:mybatis/mybatis.cfg.xml"        # mybatis配置文件所在路径
  mapper-locations: 
    - "classpath:mybatis/mapper/**/*.xml"                     # mapper映射文件
  type-aliases-package: com.lzj.springcloud.entity            # 别名类所在包


spring: 
  application: 
    name: microservicecloud-provider                          #微服务的名字
  datasource: 
    driver-class-name: org.gjt.mm.mysql.Driver                # mysql驱动包      
    type: com.alibaba.druid.pool.DruidDataSource              # 当前数据源操作类型
    url: "jdbc:mysql://localhost:3306/lzj"                    # 数据库名称
    username: root
    password: lzjlzj
    dbcp2: 
      initial-size: 5                                         # 初始化连接数
      max-total: 5                                            # 最大连接数
      max-wait-millis: 200                                    # 等待连接获取的最大超时时间
      min-idle: 5                                             # 数据库连接池的最小维持连接数

eureka:
  client:
    service-url:
      defaultZone: http://localhost:9001/eureka
  instance:
    instance-id: microservicecloud-provider8002               #自定义服务名称信息
    prefer-ip-address: true                                   #访问路径可以显示IP地址
    
info: 
  app.name: microservicecloud-provider
  company.name: www.lzj.com
  build.artifactId: $project.artifactId$
  build.version: $project.version$

重启microservice-eurake1和microservicecloud-provider微服务,点击注册的微服务microservicecloud-provider的链接,显示如下页面
这里写图片描述

10、eureka的自我保护
过段时间,微服务没有用时,会出现下面红色的警告信息,即为eureka的自我保护。
这里写图片描述

默认情况下,如果EurekaServer在一定时间内没有接收到某个微服务实例的心跳,EurekaServer将会注销该实例(默认90秒)。但是当网络分区故障发生时,微服务与EurekaServer之间无法正常通信,以上行为可能变得非常危险了——因为微服务本身其实是健康的,此时本不应该注销这个微服务。Eureka通过“自我保护模式”来解决这个问题——当EurekaServer节点在短时间内丢失过多客户端时(可能发生了网络分区故障),那么这个节点就会进入自我保护模式。一旦进入该模式,EurekaServer就会保护服务注册表中的信息,不再删除服务注册表中的数据(也就是不会注销任何微服务)。当网络故障恢复后,该Eureka Server节点会自动退出自我保护模式。

在自我保护模式中,Eureka Server会保护服务注册表中的信息,不再注销任何服务实例。当它收到的心跳数重新恢复到阈值以上时,该Eureka Server节点就会自动退出自我保护模式。它的设计哲学就是宁可保留错误的服务注册信息,也不盲目注销任何可能健康的服务实例。

综上,自我保护模式是一种应对网络异常的安全保护措施。它的架构哲学是宁可同时保留所有微服务(健康的微服务和不健康的微服务都会保留),也不盲目注销任何健康的微服务。使用自我保护模式,可以让Eureka集群更加的健壮、稳定。

在Spring Cloud中,可以使用eureka.server.enable-self-preservation = false 禁用自我保护模式。

六、建立eureka集群

第五步只是建立了一个eureka的微服务,如果当这个微服务down掉了,那么其它微服务就不能被注册和发现,整个系统就会down掉,所以下面建立多个eureka微服务,配置eureka集群,需要注册的微服务要注册到所有的eureka的微服务中,即注册到整个集群上,当一个eureka的微服务挂掉了,其它的eureka微服可以继续工作。
首先修改host文件,添加127.0.0.1的多个域名映射,方便后面模拟根据多个地址进行注册服务。
分别复制microservice-eurake1工程为microservice-eurake2和microservice-eurake3,
把microservice-eurake2服务中pom文件中的artifactId改为(因为之前该微服是复制过来的,如果是创建过来的就不用修改)<artifactId>microservice-eurake2</artifactId>
把microservice-eurake3服务中pom文件中的artifactId改为<artifactId>microservice-eurake3</artifactId>
修改microservice-eureka1的application.yml文件为

 eureka:
  instance:
    hostname: localhost
  client:
    register-with-eureka: false
    fetch-registry: false
    service-url:
      #defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/        #设置与Eureka Server交互的地址查询服务和注册服务都需要依赖这个地址。
      defaultZone: http://eureka9003.com:9003/eureka/,http://eureka9002.com:9002/eureka/
server:
  port: 9001

修改microservice-eureka2的application.yml文件为

eureka:
  instance:
    hostname: localhost
  client:
    register-with-eureka: false
    fetch-registry: false
    service-url:
      #defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
      defaultZone: http://eureka9001.com:9001/eureka/,http://eureka9003.com:9003/eureka/
server:
  port: 9002

修改microservice-eureka3的application.yml文件为

eureka:
  instance:
    hostname: localhost
  client:
    register-with-eureka: false
    fetch-registry: false
    service-url:
      #defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
      defaultZone: http://eureka9001.com:9001/eureka/,http://eureka9002.com:9002/eureka/
      
server:
  port: 9003

然后修改microservice-provider微服务application.yml中的defaultZone配置,把该微服务同时注册到三个eureka微服务中,即eureka集群中。

server: 
  port: 8002

mybatis: 
  config-location: "classpath:mybatis/mybatis.cfg.xml"        # mybatis配置文件所在路径
  mapper-locations: 
    - "classpath:mybatis/mapper/**/*.xml"                     # mapper映射文件
  type-aliases-package: com.lzj.springcloud.entity            # 别名类所在包


spring: 
  application: 
    name: microservicecloud-provider                          #微服务的名字
  datasource: 
    driver-class-name: org.gjt.mm.mysql.Driver                # mysql驱动包      
    type: com.alibaba.druid.pool.DruidDataSource              # 当前数据源操作类型
    url: "jdbc:mysql://localhost:3306/lzj"                    # 数据库名称
    username: root
    password: lzjlzj
    dbcp2: 
      initial-size: 5                                         # 初始化连接数
      max-total: 5                                            # 最大连接数
      max-wait-millis: 200                                    # 等待连接获取的最大超时时间
      min-idle: 5                                             # 数据库连接池的最小维持连接数

eureka:
  client:
    service-url:
      #defaultZone: http://localhost:9001/eureka
      defaultZone: http://eureka9001.com:9001/eureka/,http://eureka9002.com:9002/eureka/,http://eureka9003.com:9003/eureka/
  instance:
    instance-id: microservicecloud-provider8002               #自定义服务名称信息
    prefer-ip-address: true                                   #访问路径可以显示IP地址
    
info: 
  app.name: microservicecloud-provider
  company.name: www.lzj.com
  build.artifactId: $project.artifactId$
  build.version: $project.version$

测试:运行microservice-eurake1、microservice-eurake2、microservice-eurake3、microservice-provider
从浏览器中访问eureka1的管理端http://localhost:9001/
这里写图片描述
可以看出,两个划横线的为microservice-eurake1的备份,microservice-provider为注册上来的服务。eureka的3个微服务组成了一个服务注册的集群,只有有一个能工作,能保证业务的执行。

七、Ribbon负载均衡

Ribbon的负载均衡是应用于客户端的,即调用一方的,在本案例中就是应用于microservice-consumer微服务的,下面就对microservice-consumer微服务使用负载均衡。
1、microservice-consumer进行Ribbon配置
下面进行修改microservice-consumer微服务的配置
pom文件中增加Ribbon需要的依赖

		<!-- Ribbon相关 -->
		<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.cloud</groupId>
			<artifactId>spring-cloud-starter-config</artifactId>
		</dependency>

修改application.yml文件,增加eureka的服务注册功能,修改后的配置如下

server: 
  port: 8003

eureka:
  client:
    register-with-eureka: false
    service-url: 
      defaultZone: http://eureka9003.com:9003/eureka/,http://eureka9002.com:9002/eureka/,http://eureka9001.com:9001/eureka/

负载均衡实际是根据RestTemplate根据均衡算法进行调度不同地址上的同一个微服务的部署。所以修改ConfigBean,在RestTemplate上加@LoadBalanced注解。

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

把microservice-consumer也注册到eureka服务中,需要在启动类上加@EnableEurekaClient注解

@SpringBootApplication
@EnableEurekaClient
public class ConsumerApplication {

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

}

修改controller层,不再通过地址和端口调取其它微服的应用,而是根据微服务的名来调取应用。修改后如下

@RestController
public class UserConsumerController {
//	private static String REST_URL_PREFIX = "http://localhost:8002";
	/*直接根据微服务名调用,而不再是根据地址和端口了,运用了eureka的发现功能*/
	private static String REST_URL_PREFIX = "http://microservicecloud-provider";
	
	@Autowired
	private RestTemplate restTemplate;
	
	@RequestMapping(value="/consumer/add")
	public boolean addUser(User user){
		Boolean flag = restTemplate.postForObject(REST_URL_PREFIX + "/add", user, Boolean.class);
		return flag;
	}
	
	@RequestMapping(value="/consumer/get/{id}")
	public User get(@PathVariable("id") int id){
		User user = restTemplate.getForObject(REST_URL_PREFIX + "/get/" + id, User.class);
		return user;
	}
	
	@SuppressWarnings({ "unchecked", "rawtypes" })
	@RequestMapping(value="/consumer/list")
	public List<User> getList(){
		List list = restTemplate.getForObject(REST_URL_PREFIX + "/getUser/list", List.class);
		return list;
	}
}

只是修改了private static String REST_URL_PREFIX = “http://microservicecloud-provider”;

测试:修改上述配置后,下面进行测试,启动microservice-eurake1、microservice-eurake2、microservice-eurake3微服务,然后启动microservice-provider提供者微服务,最后启动带Ribbon负载均衡配置的消费者(客户端)微服务microservice-consumer。从浏览器中发送请求http://localhost:8003/consumer/list,响应页面如下
这里写图片描述

说明可以从消费端通过微服务名找到提供者微服务,然后进行调用。

2、Ribbon负载均衡
上面在消费端microservice-consumer配置好了Ribbon,提供者微服务目前只有一个。为减小提供者微服务的压力,现在再部署两个提供者微服务,当客户端发送请求时,由三个微服务中的一个随机的响应请求。
复制microservice-provider工程生成microservice-provider2和microservice-provider3
修改microservice-provider2的启动类为ProviderApplication2
修改microservice-provider3的启动类为ProviderApplication3
修改microservice-provider2的application.yml配置为

server: 
  port: 8003

mybatis: 
  config-location: "classpath:mybatis/mybatis.cfg.xml"        # mybatis配置文件所在路径
  mapper-locations: 
    - "classpath:mybatis/mapper/**/*.xml"                     # mapper映射文件
  type-aliases-package: com.lzj.springcloud.entity            # 别名类所在包


spring: 
  application: 
    name: microservicecloud-provider                          #微服务的名字
  datasource: 
    driver-class-name: org.gjt.mm.mysql.Driver                # mysql驱动包      
    type: com.alibaba.druid.pool.DruidDataSource              # 当前数据源操作类型
    url: "jdbc:mysql://localhost:3306/lzj"                    # 数据库名称
    username: root
    password: lzjlzj
    dbcp2: 
      initial-size: 5                                         # 初始化连接数
      max-total: 5                                            # 最大连接数
      max-wait-millis: 200                                    # 等待连接获取的最大超时时间
      min-idle: 5                                             # 数据库连接池的最小维持连接数

eureka:
  client:
    service-url:
      #defaultZone: http://localhost:9001/eureka
      defaultZone: http://eureka9001.com:9001/eureka/,http://eureka9002.com:9002/eureka/,http://eureka9003.com:9003/eureka/
  instance:
    instance-id: microservicecloud-provider8003               #自定义服务名称信息
    prefer-ip-address: true                                   #访问路径可以显示IP地址
    
info: 
  app.name: microservicecloud-provider
  company.name: www.lzj.com
  build.artifactId: $project.artifactId$
  build.version: $project.version$

修改microservice-provider3的配置为

只是分别修改了server.port和eureka.instance.instance-id。

为方便观察哪一个提供者微服务响应的客户端请求,在提供者微服中分别打印两条日志,
microservice-provider微服务中UserServiceImpl类中getUser方法修改为如下:

	@Override
	public User getUser(int id) {
		User user = userDao.getUser(id);
		System.out.println("microservice-provider微服务在响应客户端请求……");
		System.out.println("user : " + user);
		return user;
	}

microservice-provider2微服务中UserServiceImpl类中getUser方法修改为如下:

	@Override
	public User getUser(int id) {
		User user = userDao.getUser(id);
		System.out.println("microservice-provider2微服务在响应客户端请求……");
		System.out.println("user : " + user);
		return user;
	}

microservice-provider3微服务中UserServiceImpl类中getUser方法修改为如下:

	@Override
	public User getUser(int id) {
		User user = userDao.getUser(id);
		System.out.println("microservice-provider3微服务在响应客户端请求……");
		System.out.println("user : " + user);
		return user;
	}

测试:启动3个eureka的微服务集群,然后启动3个上述提供者微服务,最后启动消费者微服务microservice-consumer,分别从前端发起http://localhost:7001/consumer/get/{id}多笔请求,id用数字代替,发现分别在三个提供者服务的console中输出如下内容:

microservice-provider3微服务在响应客户端请求……
user : User [id=1, name=lzj1, age=20]

microservice-provider3微服务在响应客户端请求……
user : User [id=2, name=lzj2, age=24]

microservice-provider微服务在响应客户端请求……
user : User [id=4, name=lzj4, age=30]

microservice-provider2微服务在响应客户端请求……
user : User [id=3, name=lzj3, age=26]
……

3个提供者微服务随机的响应客户端请求。

3、通过Ribbon的核心组件IRule定义查找消费端调用提供端微服务的策略
如没有指定轮询策略,默认是消费端随机调用提供端微服的策略,下面指定轮询调用策略。只需要在microservice-consumer中的ConfigBean类添加如下声明:

	@Bean
	public IRule myRule(){
		return new RoundRobinRule(); 	//轮询策略
	}

重新启动3个eureka微服务和3个提供者微服务,最后启动消费者微服务,重新测试,可知消费者微服务是轮询调用提供者的3个微服务的。

八、Feign负载均衡

Feign是一个声明式WebService客户端。使用Feign能让编写Web Service客户端更加简单。Feign是对Ribbon的包装,Feign集成了Ribbon。
前面在使用Ribbon+RestTemplate时,利用RestTemplate对http请求的封装处理,形成了一套模版化的调用方法。但是在实际开发中,由于对服务依赖的调用可能不止一处,往往一个接口会被多处调用,所以通常都会针对每个微服务自行封装一些客户端类来包装这些依赖服务的调用。所以,Feign在此基础上做了进一步封装,由他来帮助我们定义和实现依赖服务接口的定义。在Feign的实现下,我们只需创建一个接口并使用注解的方式来配置它,即可完成对服务提供方的接口绑定,简化了使用Spring cloud Ribbon时,自动封装服务调用客户端的开发量。
Feign既然是对Ribbon的包装,那么Feign也是用在客户端的,即消费端的。下面建立集成Feign的消费端
复制microservice-consumer工程为microservice-consumer-feign
修改microservice-consumer-feign启动类的名字为FeignConsumerApplication;
microservice-consumer-feign的pom文件中增加对Feign的依赖:

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

创建ConsumerService接口,用于包装microservicecloud-provider微服务,以后要调用microservicecloud-provider服务中的方法,只需要调用接口中对应的方法即可:

package com.lzj.springcloud.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.lzj.springcloud.entity.User;

/*以后调用microservicecloud-provider微服务中的方法,只需要调用下面对应的接口既可以了*/
@FeignClient(value="microservicecloud-provider")
public interface ConsumerService {

	/*调用接口中的get方法,即可以向microservicecloud-provider微服务发送/get/{id}请求*/
	@RequestMapping(value="/get/{id}", method=RequestMethod.GET)
	public User get(@PathVariable("id") int id);
	
	@RequestMapping(value="/add", method=RequestMethod.POST)
	public boolean add(User user);
	
	@RequestMapping(value="/getUser/list", method=RequestMethod.GET)
	public List<User> getAll();
}

修改microservice-consumer-feign中的controller层为

package com.lzj.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.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.lzj.springcloud.entity.User;
import com.lzj.springcloud.service.ConsumerService;

@RestController
public class UserConsumerController {
//	private static String REST_URL_PREFIX = "http://localhost:8002";
	/*直接根据微服务名调用,而不再是根据地址和端口了,运用了eureka的发现功能*/
//	private static String REST_URL_PREFIX = "http://microservicecloud-provider";
//	@Autowired
//	private RestTemplate restTemplate;
	
	@Autowired
	private ConsumerService service;
	
	@RequestMapping(value="/consumer/add")
	public boolean addUser(User user){
		Boolean flag = service.add(user);
		return flag;
	}
	
	@RequestMapping(value="/consumer/get/{id}")
	public User get(@PathVariable("id") int id){
		User user = service.get(id);
		return user;
	}
	
	@SuppressWarnings({ "unchecked", "rawtypes" })
	@RequestMapping(value="/consumer/list")
	public List<User> getList(){
		List list = service.getAll();
		return list;
	}
}

修改启动类FeignConsumerApplication,在启动类上加启用Feign的注解:

package com.lzj.springcloud;
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(basePackages="com.lzj.springcloud.service")
public class FeignConsumerApplication {

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

}

测试,启动microservice-eurake1、microservice-eurake2、microservice-eurake3集群,然后启动microservice-provider、microservice-provider2、microservice-provider3集群,最后启动microservice-consumer-feign微服务。
从浏览器中向消费端发送请求http://localhost:7001/consumer/get/2,响应页面如下
这里写图片描述
Feign通过接口ConsumerService中的get方法调用Rest服务(之前是通过Ribbon+RestTemplate),并在eureka微服务中查找microservicecloud-provider服务,找到后,把请求http://localhost:7001/consumer/get/2发送到microservicecloud-provider微服务。

九、Hystrix断路器

如果一个请求需要调起多个服务时,其中一个服务不通或失败,当大量请求发生时,会导致请求延时和资源浪费。Hystrix是一个用于处理分布式系统的延迟和容错的开源库,在分布式系统里,许多依赖不可避免的会调用失败,比如超时、异常等,Hystrix能够保证在一个依赖出问题的情况下,不会导致整体服务失败,避免级联故障,以提高分布式系统的弹性。
当某个服务单元发生故障之后,通过断路器的故障监控,向调用方返回一个符合预期的、可处理的备选响应(FallBack),而不是长时间的等待或者抛出调用方无法处理的异常,这样就保证了服务调用方的线程不会被长时间、不必要地占用,从而避免了故障在分布式系统中的蔓延,乃至雪崩。
Hystrix可用于服务熔断、服务降级、服务限流等作用。

1、服务熔断
当某个服务出现异常时,熔断该服务,快速返回指定的错误信息,当服务正常时,恢复熔断。
复制microservice-provider工程为microservice-provider-hystrix;
修改microservice-provider-hystrix的启动类为HystrixProviderApplication;
pom文件中添加hystrix的依赖

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

application.yml配置如下:

server: 
  port: 8005

mybatis: 
  config-location: "classpath:mybatis/mybatis.cfg.xml"        # mybatis配置文件所在路径
  mapper-locations: 
    - "classpath:mybatis/mapper/**/*.xml"                     # mapper映射文件
  type-aliases-package: com.lzj.springcloud.entity            # 别名类所在包


spring: 
  application: 
    name: microservicecloud-provider                          #微服务的名字
  datasource: 
    driver-class-name: org.gjt.mm.mysql.Driver                # mysql驱动包      
    type: com.alibaba.druid.pool.DruidDataSource              # 当前数据源操作类型
    url: "jdbc:mysql://localhost:3306/lzj"                    # 数据库名称
    username: root
    password: lzjlzj
    dbcp2: 
      initial-size: 5                                         # 初始化连接数
      max-total: 5                                            # 最大连接数
      max-wait-millis: 200                                    # 等待连接获取的最大超时时间
      min-idle: 5                                             # 数据库连接池的最小维持连接数

eureka:
  client:
    service-url:
      #defaultZone: http://localhost:9001/eureka
      defaultZone: http://eureka9001.com:9001/eureka/,http://eureka9002.com:9002/eureka/,http://eureka9003.com:9003/eureka/
  instance:
    instance-id: microservicecloud-provider-hystrix           #自定义服务名称信息
    prefer-ip-address: true                                   #访问路径可以显示IP地址
    
info: 
  app.name: microservicecloud-provider
  company.name: www.lzj.com
  build.artifactId: $project.artifactId$
  build.version: $project.version$

只修改了server.port和eureka.instance.instance-id;
修改UserController内容为:

package com.lzj.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.lzj.springcloud.entity.User;
import com.lzj.springcloud.service.UserService;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;

@RestController
public class UserController {

	@Autowired
	private UserService service;
	
	@RequestMapping(value="/get/{id}", method=RequestMethod.GET)
	@HystrixCommand(fallbackMethod="hystrixGetUser") //一旦服务调用失败,就调用hystrixGetUser方法
	public User getUser(@PathVariable("id") int id){
		User user = service.getUser(id);
		if(user == null){
			throw new RuntimeException("不存在id=" + id + "对应的用户信息");
		}
		return user;
	}
	
	public User hystrixGetUser(@PathVariable("id") int id){
		User user = new User(id, "不存在该用户", 0);
		return user;
	}

}

在启动类HystrixProviderApplication上添加注解@EnableCircuitBreaker;
启动microservice-eurake1、microservice-eurake2、microservice-eurake3服务,然后启动microservice-provider-hystrix服务,然后启动microservice-consumer-feign服务。
测试:从浏览器中发送请求:http://localhost:7001/consumer/get/18,响应如下:
这里写图片描述

说明熔断起作用了,调用服务失败,返回了熔断指定的错误信息。

2、服务降级
在一个分布式系统中,当访问高峰期或资源有限时,需要关掉某个服务,若有请求访问该服务,不能因为系统服务关掉了,就一直中断在该调用服务处,这时就需要请求返回指定的错误信息。例如在分布式系统中有A、B两个服务,因为资源有限,需要关掉B服务,A服务在调用B服务时,没有调通,此时A返回指定的错误信息,注意不是在B服务端返回的,是A客户端返回的错误信息。看示例
复制microservice-consumer-feign服务为microservice-consumer-feign-hystrix;
microservice-consumer-feign-hystrix的pom文件中添加依赖:

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

microservice-consumer-feign-hystrix服务中新建实现FallbackFactory的ConsumerServiceFallbackFactory类,在类上添加@Component,并传入ConsumerService接口,当调用ConsumerService中对应的方法失败后,自动调用ConsumerServiceFallbackFactory 中对应实现的ConsumerService方法,并在对应方法中定制调用服务失败后显示的错误信息。

package com.lzj.springcloud.service;
import java.util.List;
import org.springframework.stereotype.Component;
import com.lzj.springcloud.entity.User;
import feign.hystrix.FallbackFactory;

@Component
public class ConsumerServiceFallbackFactory implements FallbackFactory<ConsumerService> {

	@Override
	public ConsumerService create(Throwable arg0) {
		// TODO Auto-generated method stub
		return new ConsumerService() {
			
			@Override
			public List<User> getAll() {
				// TODO Auto-generated method stub
				return null;
			}
			
			@Override
			public User get(int id) {
				User user = new User(id, "该用户不存在", 0);
				return user;
			}
			
			@Override
			public boolean add(User user) {
				// TODO Auto-generated method stub
				return false;
			}
		};
	}

}

在microservice-consumer-feign-hystrix服务中ConsumerService的接口中@FeignClient中添加fallbackFactory属性。运用spring的AOP切面,当调用ConsumerService中方法失败后,执行fallbackFactory属性指定的ConsumerServiceFallbackFactory类中的对应方法,ConsumerService修改后如下:

package com.lzj.springcloud.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.lzj.springcloud.entity.User;

/*以后调用microservicecloud-provider微服务中的方法,只需要调用下面对应的接口既可以了*/
@FeignClient(value="microservicecloud-provider", fallbackFactory=ConsumerServiceFallbackFactory.class)
public interface ConsumerService {

	/*调用接口中的get方法,即可以向microservicecloud-provider微服务发送/get/{id}请求*/
	@RequestMapping(value="/get/{id}", method=RequestMethod.GET)
	public User get(@PathVariable("id") int id);
	
	@RequestMapping(value="/add", method=RequestMethod.POST)
	public boolean add(User user);
	
	@RequestMapping(value="/getUser/list", method=RequestMethod.GET)
	public List<User> getAll();
}

修改microservice-consumer-feign-hystrix服务的application.yml文件为:

server: 
  port: 7001

feign: 
  hystrix: 
    enabled: true

eureka:
  client:
    register-with-eureka: false
    service-url: 
      defaultZone: http://eureka9003.com:9003/eureka/,http://eureka9002.com:9002/eureka/,http://eureka9001.com:9001/eureka/

测试:首先启动microservice-eurake1、microservice-eurake2、microservice-eurake3集群服务,然后启动microservice-provider提供者服务,最后启动microservice-consumer-feign-hystrix消费者服务。从浏览器中发送请求

http://localhost:7001/consumer/get/2
  • 1

响应正常如下:
这里写图片描述

下面模拟资源有限,关掉microservice-provider提供者服务,关掉提供者服务后,重新在浏览器中发送请求http://localhost:7001/consumer/get/2,响应如下:
这里写图片描述
响应过程如下:当发送请求后,调用microservice-consumer-feign-hystrix服务中的UserConsumerController的get方法,然后调用ConsumerService中的get方法,该方法向microservice-provider服务发送请求/get/{id},由于microservice-provider服务被关掉了,请求失败,转而调用ConsumerServiceFallbackFactory中实现的对应方法

			@Override
			public User get(int id) {
				User user = new User(id, "该用户不存在", 0);
				return user;
			}

因此,浏览器中得到的响应是指定的错误信息。

3、服务监控
hystrix除了应用于上述的服务熔断和降级,还可以应用于服务的实时监控。Hystrix会持续地记录所有通过Hystrix发起的请求的执行信息,并以统计报表和图形的形式展示给用户,包括每秒执行多少请求多少成功,多少失败等。Netflix通过hystrix-metrics-event-stream项目实现了对以上指标的监控。Spring Cloud也提供了Hystrix Dashboard的整合,对监控内容转化成可视化界面。示例如下
创建microservice-consumer-hystrix-dashbord微服务;
添加pom的依赖,如下:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <parent>
    <groupId>com.lzj.springcloud</groupId>
    <artifactId>microservice-parent</artifactId>
    <version>0.0.1-SNAPSHOT</version>
  </parent>
  <artifactId>microservice-consumer</artifactId>
  
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-hystrix</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-hystrix-dashboard</artifactId>
		</dependency>
		<!-- 修改后立即生效,热部署 -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>springloaded</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-devtools</artifactId>
		</dependency>
	</dependencies>
</project>

application.yml配置文件如下:

server: 
  port: 7002

创建启动类HystrixDashbordConsumerApplication:

package com.lzj.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;

@SpringBootApplication
@EnableHystrixDashboard
public class HystrixDashbordConsumerApplication {

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

}

然后启动微服务,响应页面如下,说明创建用于监控其它服务的微服务成功:
这里写图片描述

测试:
首先启动microservice-eurake1、microservice-eurake2、microservice-eurake3集群服务,然后启动microservice-provider-hystrix服务,最后启动刚创建监控服务的服务microservice-consumer-hystrix-dashbord。
本例要监听提供者microservice-provider-hystrix服务的访问情况,下面把监控提供者服务的链接填入豪猪监控客户端中,如下
这里写图片描述
填入完毕后,点击Monitor stream按钮,开启监控页面。
从浏览器中连续的发送请求http://localhost:8005/get/1(不停的刷新即可),监控页面如下:
这里写图片描述
图中的实心圆的颜色为绿色,表示健康,健康程度排序绿色>黄色>橙色>红色,绿色表示最健康,红色表示最不健康。实心圆的大小表示了监控的服务访问量的大小,访问量越大,实心圆越大,反之,越小。图中每个颜色数字表示如下(从别处盗图)
这里写图片描述

十、Zuul路由

Zuul路由包含了对请求的路由和过滤两个功能。
路由:路由功能负责将外部请求转发到具体的微服务实例上,是实现外部访问统一入口;
过滤:过滤器功能则负责对请求的处理过程进行干预,是实现请求校验、服务聚合等功能的基础。
Zuul和Eureka进行整合,将Zuul自身注册为Eureka服务治理下的应用,同时从Eureka中获得其他微服务的消息,也即以后的访问微服务都是通过Zuul跳转后获得。Zuul服务最终也会注册进Eureka。

1、路由配置
建立一个microservice-zull微服务;
pom文件如下:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <parent>
    <groupId>com.lzj.springcloud</groupId>
    <artifactId>microservice-parent</artifactId>
    <version>0.0.1-SNAPSHOT</version>
  </parent>
  <artifactId>microservice-zull</artifactId>
  
	<dependencies>
		<!-- zuul路由网关 -->
		<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</groupId>
			<artifactId>springloaded</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-devtools</artifactId>
		</dependency>
	</dependencies>
	

</project>

也要把Zuul微服务注册到Eureaka上面,application.yml文件配置如下:

server: 
  port: 6001

spring:
  application:
    name: microservice-zull
eureka:
  client:
    service-url: 
      defaultZone: http://eureka9003.com:9003/eureka/,http://eureka9002.com:9002/eureka/,http://eureka9001.com:9001/eureka/
  instance: 
    instance-id: microservice-zull6001                        #自定义服务名称信息
    prefer-ip-address: true                                   #访问路径可以显示IP地址
    
info: 
  app.name: microservice-zull
  company.name: www.lzj.com
  build.artifactId: $project.artifactId$
  build.version: $project.version$

在host文件中添加127.0.0.1的映射,127.0.0.1 zull6001.com用zull6001.com表示Zuul微服务的域名;
创建ZullApplication启动类,内容如下:

package com.lzj.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;

@SpringBootApplication
@EnableZuulProxy //启动Zuul
public class ZullApplication {

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

}

测试:
在浏览器中发送http://localhost:8002/get/2请求,直接请求microservice-provider微服务,响应如下:
这里写图片描述

在浏览器中发送http://zull6001.com:6001/microservicecloud-provider/get/2请求,通过Zuul路由访问microservicecloud-provider服务,其中zull6001.com:6001为microservice-zull微服务的域名和端口,microservicecloud-provider为要访问的微服务名(在application.yml中配置的),响应如下:
这里写图片描述
结果正常。
2、修改服务代理名称
上面通过路由访问服务的请求为http://zull6001.com:6001/microservicecloud-provider/get/2,其中microservicecloud-provider为调用的服务名,向调用方暴露了具体的服务名。如果不想暴露服务名,可以为服务指定一个代号别名,例如可以通过发送请求http://zull6001.com:6001/provider/get/2访问microservicecloud-provider服务,那么provider即为microservicecloud-provider的别名,在application.yml中配置如下:

server: 
  port: 6001

spring:
  application:
    name: microservice-zull
eureka:
  client:
    service-url: 
      defaultZone: http://eureka9003.com:9003/eureka/,http://eureka9002.com:9002/eureka/,http://eureka9001.com:9001/eureka/
  instance: 
    instance-id: microservice-zull6001                        #自定义服务名称信息
    prefer-ip-address: true                                   #访问路径可以显示IP地址
    
zuul:
  routes:
    mydept.serviceId: microservicecloud-provider
    mydept.path: /provider/**

info: 
  app.name: microservice-zull
  company.name: www.lzj.com
  build.artifactId: $project.artifactId$
  build.version: $project.version$

添加了关于zuul的配置,在浏览器中发送http://zull6001.com:6001/provider/get/2请求,响应如下:
这里写图片描述
响应正常,此时通过发送带调用的微服务名的请求也是可以访问的。

3、忽略带真实服务名的请求
如果想拒绝访问中带服务名的请求,例如http://zull6001.com:6001/microservicecloud-provider/get/2,使其不能再访问服务,只能通过指定的别名进行访问服务。
在application.yml中关于zuul的配置修改为如下(增加了ignored-services):

zuul:
  ignored-services: microservicecloud-provider
  routes:
    mydept.serviceId: microservicecloud-provider
    mydept.path: /provider/**

4、设置访问前缀
当设置完忽略真实服务名访问后,只能通过路由代理别名的形式进行访问,例如http://zull6001.com:6001/provider/get/2,如果要在每次访问的时候,在代理服务名前面加一个前缀,例如http://zull6001.com:6001/MyDemo/provider/get/2,MyDemo即为前缀。在application.yml中关于zuul的配置修改为如下(只是添加了prefix的配置):

zuul:
  prefix: /MyDemo
  ignored-services: microservicecloud-provider
  routes:
    mydept.serviceId: microservicecloud-provider
    mydept.path: /provider/**

浏览器中发送http://zull6001.com:6001/MyDemo/provider/get/2请求,响应如下:
这里写图片描述

十一、Config配置

一个分布式系统有可能包括非常多微服务,每个微服务都有独自的配置文件,当系统变更时,有可能需要修改很多服务的配置文件,导致运维繁琐,容易出问题,所以需要一套集中式的、动态的配置管理设施。spring cloud提供了Config来解决该问题。

1、建立Config服务端,与github通信
在github上建立一个respository,此地名为microservice-config,地址为:https://github.com/shuniversity/microservice-config.git;
把建立的respository 克隆到本地E:\demo\springcloud-config-repository

>git clone https://github.com/shuniversity/microservice-config.git
  • 1

在clone到本地的仓库E:\demo\springcloud-config-repository\microservice-config中新建一个application.yml文件,内容为:

spring:
  profiles:
    active:
      - dev
---
spring:
  profiles: dev                   #开发环境
  application:
    name: microservice-config-dev
    
---
spring:
  profiles: test                  #测试环境
  application:
    name: microservice-config-test

把新建的application.yml文件推送到github上的microservice-config仓库中:

    git add .
    git commit -m "init file" 
    git push origin master

新建microservice-config-server服务,pom文件配置为:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <parent>
    <groupId>com.lzj.springcloud</groupId>
    <artifactId>microservice-parent</artifactId>
    <version>0.0.1-SNAPSHOT</version>
  </parent>
  <artifactId>microservice-config-server</artifactId>
  
	<dependencies>
		<!-- 依赖microservice-com模块 -->
		<dependency>
    		<groupId>org.springframework.cloud</groupId>
    		<artifactId>spring-cloud-config-server</artifactId>
		</dependency>
		<!-- 修改后立即生效,热部署 -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>springloaded</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-devtools</artifactId>
		</dependency>
	</dependencies>
</project>
  •  

application.yml配置为:
server:
port: 4001

spring:
  application:
    name: microservice-config-server
  cloud:
    config:
      server:
        git:
          uri: https://github.com/shuniversity/microservice-config.git

建立启动类ConfigServerApp

package com.lzj.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.config.server.EnableConfigServer;

@SpringBootApplication
@EnableConfigServer
public class ConfigServerApp {

	public static void main(String[] args) {

		SpringApplication.run(ConfigServerApp.class, args);
	}

}

测试:
创建完仓库和Config的服务端工程后,下面开始测试,首先启动创建好的Config服务工程,然后再浏览器中发送请求http://localhost:4001/application-test.yml,响应如下:
在这里插入图片描述
在浏览器中发送http://localhost:4001/application-dev.yml请求,响应如下:
在这里插入图片描述
可见通过Config的服务工程可以获取github上的配置内容。

2、创建Config的客户端
上面一步已经成功了创建服务端,并已能够与github进行通信,下面建立Config的客户端,与上一步创建的服务端通信,通过服务端获取github上的配置信息。目前已经创建的cousumer、provider、eurake等微服务都可以改成Config的客户端,然后修改后的微服务不用在各自的微服务中application.yml中配置独有的信息,统一的把配置放在github上,由运维人员统一进行配置。

  • 下面把前面已经创建的microservice-consumer微服务复制成microservice-consumer-config-clent.yml,然后把microservice-consumer-config-clent.yml修改成Config客户端;

  • 在本地仓库添加一个配置文件microservice-consumer-config-clent.yml,用户放到github上;

    spring:
    profiles:
    active:
    - dev


    server:
    port: 7001

    spring:
    profiles: dev
    application:
    name: microservice-consumer-config-clent

    eureka:
    client:
    register-with-eureka: false
    service-url:
    defaultZone: http://eureka9003.com:9003/eureka/,http://eureka9002.com:9002/eureka/,http://eureka9001.com:9001/eureka/

  • 把microservice-consumer-config-clent.yml配置文件放到github上
    在这里插入图片描述

  • 修改microservice-consumer-config-client中的pom文件,config的客户端需要下面的依赖,一定不能少:
    具体关于SpringCloud介绍请参考其他文章,下面主要讲解SpringCloud搭建微服务的步骤。
    本次案例主要分为以下几个微服务

  •  提供者服务用于操作user表,进行增删改查;

  • 消费者服务用于请求提供者服务,进行增删改查;

  • 公共模块微服务,用于提供其他微服务共同需要的功能;

本次案例用的工具版本如下
eclipse : Mars
JDK : 1.8
SpringCloud : Dalston
SpringBoot : 1.5.9

本系统源码全部上传至github,地址为:
https://github.com/shuniversity/MySpringCloud

原文来自:https://blog.csdn.net/u010502101/article/details/81989756

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值