笔记整理来源 B站UP主狂神说:https://www.bilibili.com/video/BV1jJ411S7xr
文档来源:https://www.kuangstudy.com/bbs/1374942542566551554
* 熟练使用SpringBoot 微服务快速开发框架
* 了解过Dubbo + Zookeeper 分布式基础
* 电脑配置内存不低于8G(我自己的是16G)
Spring Cloud 五大组件
* 服务注册与发现——Netflix Eureka
* 负载均衡:
* 客户端负载均衡——Netflix Ribbon
* 服务端负载均衡:——Feign(其也是依赖于Ribbon,只是将调用方式RestTemplete 更改成Service 接口)
* 断路器——Netflix Hystrix
* 服务网关——Netflix Zuul
* 分布式配置——Spring Cloud Config
1.1 什么是微服务?
1.2 微服务之间是如何独立通讯的?
1.3 SpringCloud 和 Dubbo有那些区别?
1.4 SpringBoot 和 SpringCloud,请谈谈你对他们的理解
1.5 什么是服务熔断?什么是服务降级?
1.6 微服务的优缺点分别是什么?说下你在项目开发中遇到的坑
1.7 你所知道的微服务技术栈有哪些?列举一二
1.8 Eureka和Zookeeper都可以提供服务注册与发现的功能,请说说两者的区别
什么是微服务?
微服务(Microservice Architecture) 是近几年流行的一种架构思想,关于它的概念很难一言以蔽之。
究竟什么是微服务呢?我们在此引用ThoughtWorks 公司的首席科学家 Martin Fowler 于2014年提出的一段话:
原文:https://martinfowler.com/articles/microservices.html
汉化:https://www.cnblogs.com/liuning8023/p/4493156.html
再来从技术维度角度理解下:
微服务
强调的是服务的大小,它关注的是某一个点,是具体解决某一个问题/提供落地对应服务的一个服务应用,狭义的看,可以看作是IDEA中的一个个微服务工程,或者Moudel。IDEA 工具里面使用Maven开发的一个个独立的小Moudel,它具体是使用SpringBoot开发的一个小模块,专业的事情交给专业的模块来做,一个模块就做着一件事情。强调的是一个个的个体,每个个体完成一个具体的任务或者功能。
微服务架构
一种新的架构形式,Martin Fowler 于2014年提出。
微服务架构是一种架构模式,它体长将单一应用程序划分成一组小的服务,服务之间相互协调,互相配合,为用户提供最终价值。每个服务运行在其独立的进程中,服务与服务之间采用轻量级的通信机制 (如HTTP)互相协作,每个服务都围绕着具体的业务进行构建,并且能够被独立的部署到生产环境中,另外,应尽量避免统一的,集中式的服务管理机制,对具体的一个服务而言,应根据业务上下文,选择合适的语言、工具(如Maven)**对其进行构建。
优点
缺点
| **微服务技术条目** | 落地技术 |
| -------------------- | ------------------------------------------------ |
| 服务开发 | SpringBoot、Spring、SpringMVC等 |
| 服务配置与管理 | Netfix公司的Archaius、阿里的Diamond等 |
| 服务注册与发现 | Eureka、Consul、Zookeeper等 |
| 服务调用 | Rest、PRC、gRPC |
| 服务熔断器 | Hystrix、Envoy等 |
| 负载均衡 | Ribbon、Nginx等 |
| 服务接口调用(客户端调用服务的简化工具) | Fegin等 |
| 消息队列 | Kafka、RabbitMQ、ActiveMQ等 |
| 服务配置中心管理 | SpringCloudConfig、Chef等 |
| 服务路由(API网关) | Zuul等 |
| 服务监控 | Zabbix、Nagios、Metrics、Specatator等 |
| 全链路追踪 | Zipkin、Brave、Dapper等 |
| 数据流操作开发包 | SpringCloud Stream(封装与Redis,Rabbit,Kafka等发送接收消息) |
| 时间消息总栈 | SpringCloud Bus |
| 服务部署 | Docker、OpenStack、Kubernetes等 |
| 功能点/服务框架 | Netflix/SpringCloud | Motan | gRPC | Thri t | Dubbo/DubboX |
| —————— | ———————————————————————————— | —————————————————- | ————————— | ———— | —————————— |
| 功能定位 | 完整的微服务框架 | RPC框架,但整合了ZK或Consul,实现集群环境的基本服务注册发现 | RPC框架 | RPC框架 | 服务框架 |
| 支持Rest | 是,Ribbon支持多种可拔插的序列号选择 | 否 | 否 | 否 | 否 |
| 支持RPC | 否 | 是(Hession2) | 是 | 是 | 是 |
| 支持多语言 | 是(Rest形式) | 否 | 是 | 是 | 否 |
| 负载均衡 | 是(服务端zuul+客户端Ribbon),zuul-服务,动态路由,云端负载均衡Eureka(针对中间层服务器) | 是(客户端) | 否 | 否 | 是(客户端) |
| 配置服务 | Netfix Archaius,Spring Cloud Config Server 集中配置 | 是(Zookeeper提供) | 否 | 否 | 否 |
| 服务调用链监控 | 是(zuul),zuul提供边缘服务,API网关 | 否 | 否 | 否 | 否 |
| 高可用/容错 | 是(服务端Hystrix+客户端Ribbon) | 是(客户端) | 否 | 否 | 是(客户端) |
| 典型应用案例 | Netflix | Sina | Google | Facebook | |
| 社区活跃程度 | 高 | 一般 | 高 | 一般 | 2017年后重新开始维护,之前中断了5年 |
| 学习难度 | 中等 | 低 | 高 | 高 | 低 |
| 文档丰富程度 | 高 | 一般 | 一般 | 一般 | 高 |
| 其他 | Spring Cloud Bus为我们的应用程序带来了更多管理端点 | 支持降级 | Netflix内部在开发集成gRPC | IDL定义 | 实践的公司比较多 |
| | Dubbo | SpringCloud |
| ------ | ------------- | ---------------------------- |
| 服务注册中心 | Zookeeper | Spring Cloud Netfilx Eureka |
| 服务调用方式 | RPC | REST API |
| 服务监控 | Dubbo-monitor | Spring Boot Admin |
| 断路器 | 不完善 | Spring Cloud Netfilx Hystrix |
| 服务网关 | 无 | Spring Cloud Netfilx Zuul |
| 分布式配置 | 无 | Spring Cloud Config |
| 服务跟踪 | 无 | Spring Cloud Sleuth |
| 消息总栈 | 无 | Spring Cloud Bus |
| 数据流 | 无 | Spring Cloud Stream |
| 批量任务 | 无 | Spring Cloud Task |
最大区别:Spring Cloud 抛弃了Dubbo的RPC通信,采用的是基于HTTP的REST方式
严格来说,这两种方式各有优劣。虽然从一定程度上来说,后者牺牲了服务调用的性能,但也避免了上面提到的原生RPC带来的问题。而且REST相比RPC更为灵活,服务提供方和调用方的依赖只依靠一纸契约,不存在代码级别的强依赖,这个优点在当下强调快速演化的微服务环境下,显得更加合适。
社区支持与更新力度的区别
总结:二者解决的问题域不一样:Dubbo的定位是一款RPC框架,而SpringCloud的目标是微服务架构下的一站式解决方案。
Distributed/versioned configuration 分布式/版本控制配置
Service registration and discovery 服务注册与发现
Routing 路由
Service-to-service calls 服务到服务的调用
Load balancing 负载均衡配置
Circuit Breakers 断路器
Distributed messaging 分布式消息管理
…
官网:http://projects.spring.io/spring-cloud/
版本号有点特别:
SpringCloud没有采用数字编号的方式命名版本号,而是采用了伦敦地铁站的名称,同时根据字母表的顺序来对应版本时间顺序,比如最早的Realse版本:Angel,第二个Realse版本:Brixton,然后是Camden、Dalston、Edgware,目前最新的是Hoxton SR4 CURRENT GA通用稳定版。
自学参考书:
SpringCloud Netflix 中文文档:https://springcloud.cc/spring-cloud-netflix.html
SpringCloud 中文API文档(官方文档翻译版):https://springcloud.cc/spring-cloud-dalston.html
SpringCloud中国社区:http://springcloud.cn/
SpringCloud中文网:https://springcloud.cc
SpringCloud项目:https://spring.io/projects/spring-cloud
一个简单的Maven模块结构是这样的:
-- app-parent: 一个父项目(app-parent)聚合了很多子项目(app-util\app-dao\app-web...)
|-- pom.xml
|
|-- app-core
||---- pom.xml
|
|-- app-web
||---- pom.xml
......
一个父工程带着多个Moudule子模块
MicroServiceCloud父工程(Project)下初次带着3个子模块(Module)
| SpringBoot | SpringCloud | 关系 |
| ---------- | ----------------- | ---------------------------------- |
| 1.2.x | Angel版本(天使) | 兼容SpringBoot1.2x |
| 1.3.x | Brixton版本(布里克斯顿) | 兼容SpringBoot1.3x,也兼容SpringBoot1.4x |
| 1.4.x | Camden版本(卡姆登) | 兼容SpringBoot1.4x,也兼容SpringBoot1.5x |
| 1.5.x | Dalston版本(多尔斯顿) | 兼容SpringBoot1.5x,不兼容SpringBoot2.0x |
| 1.5.x | Edgware版本(埃奇韦尔) | 兼容SpringBoot1.5x,不兼容SpringBoot2.0x |
| 2.0.x | Finchley版本(芬奇利) | 兼容SpringBoot2.0x,不兼容SpringBoot1.5x |
| 2.1.x | Greenwich版本(格林威治) | |
实际开发版本关系:使用后两个
| spring-boot-starter-parent | | spring-cloud-dependencles | |
|:--------------------------:| --------:|:-------------------------:|:--------:|
| **版本号** | **发布日期** | **版本号** | **发布日期** |
| 1.5.2.RELEASE | 2017-03 | Dalston.RC1 | 2017-x |
| 1.5.9.RELEASE | 2017-11 | Edgware.RELEASE | 2017-11 |
| 1.5.16.RELEASE | 2018-04 | Edgware.SR5 | 2018-10 |
| 1.5.20.RELEASE | 2018-09 | Edgware.SR5 | 2018-10 |
| 2.0.2.RELEASE | 2018-05 | Fomchiey.BULD-SNAPSHOT | 2018-x |
| 2.0.6.RELEASE | 2018-10 | Fomchiey-SR2 | 2018-10 |
| 2.1.4.RELEASE | 2019-04 | Greenwich.SR1 | 2019-03 |
右键项目->new module
新建model
项目路径
pom配置
https://spring.io/projects/spring-cloud
<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.0modelVersion>
<groupId>com.xxxgroupId>
<artifactId>springcloud_parentartifactId>
<version>1.0-SNAPSHOTversion>
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.2.5.RELEASEversion>
parent>
<properties>
<spring.cloud-version>Hoxton.SR6spring.cloud-version>
properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-dependenciesartifactId>
<version>${spring.cloud-version}version>
<type>pomtype>
<scope>importscope>
dependency>
dependencies>
dependencyManagement>
project>
<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.0modelVersion>
<groupId>com.xxxgroupId>
<artifactId>1_springCloudartifactId>
<version>1.0-SNAPSHOTversion>
<properties>
<maven.compiler.source>8maven.compiler.source>
<maven.compiler.target>8maven.compiler.target>
<project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
<junit.version>4.12 junit.version>
<log4j.version>1.2.17log4j.version>
<lombok.version>1.16.18lombok.version>
properties>
<packaging>pompackaging>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-alibaba-dependenciesartifactId>
<version>0.2.0.RELEASEversion>
<type>pomtype>
<scope>importscope>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-dependenciesartifactId>
<version>Greenwich.SR1version>
<type>pomtype>
<scope>importscope>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-dependenciesartifactId>
<version>2.6.3version>
<type>pomtype>
<scope>importscope>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>8.0.28version>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>druidartifactId>
<version>1.2.8version>
dependency>
<dependency>
<groupId>org.mybatis.spring.bootgroupId>
<artifactId>mybatis-spring-boot-starterartifactId>
<version>2.2.2version>
dependency>
<dependency>
<groupId>ch.qos.logbackgroupId>
<artifactId>logback-coreartifactId>
<version>1.2.11version>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>${junit.version}version>
dependency>
<dependency>
<groupId>log4jgroupId>
<artifactId>log4jartifactId>
<version>${log4j.version}version>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<version>${lombok.version}version>
dependency>
dependencies>
dependencyManagement>
project>
<dependencies>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
dependency>
dependencies>
-- 创建数据库
drop database if exists db01;
create database db01 DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;
-- 创建表
CREATE TABLE `dept` (
`deptno` bigint(20) NOT NULL AUTO_INCREMENT,
`dname` varchar(60) DEFAULT NULL,
`db_source` varchar(60) DEFAULT NULL,
PRIMARY KEY (`deptno`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='部门表'
-- 添加数据
INSERT INTO dept(dname,db_source) VALUES ('开发部',DATABASE());
INSERT INTO dept(dname,db_source) VALUES ('项目部',DATABASE());
INSERT INTO dept(dname,db_source) VALUES ('研发部',DATABASE());
INSERT INTO dept(dname,db_source) VALUES ('运维部',DATABASE());
INSERT INTO dept(dname,db_source) VALUES ('市场部',DATABASE());
INSERT INTO dept(dname,db_source) VALUES ('人事部',DATABASE());
package com.xxx.springcloud.pojo;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import java.io.Serializable;
@Data
@NoArgsConstructor
@Accessors(chain = true)//开启链式写法 普通写法:new Dept().setid(1); 链式写法:new Dept().setid(1).setname("test")....
/*实体类实现序列化*/
public class Dept implements Serializable { //数据库的表和当前实体类映射,类表关系映射
private Long deptno;//主键
private String dname;
//存在于哪个数据库的字段 ~ 微服务,一个服务对应一个数据库,同一个信息可能存在于不同的数据库
private String db_source;
public Dept(String dname) {
this.dname = dname;
}
}
<artifactId>springcloud-provider-dept-8001artifactId>
<properties>
<maven.compiler.source>8maven.compiler.source>
<maven.compiler.target>8maven.compiler.target>
properties>
<dependencies>
<dependency>
<groupId>com.xxxgroupId>
<artifactId>springcloud-apiartifactId>
<version>1.0-SNAPSHOTversion>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>druidartifactId>
dependency>
<dependency>
<groupId>ch.qos.logbackgroupId>
<artifactId>logback-coreartifactId>
dependency>
<dependency>
<groupId>org.mybatis.spring.bootgroupId>
<artifactId>mybatis-spring-boot-starterartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-jettyartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-devtoolsartifactId>
dependency>
dependencies>
src/main/resources/application.yml
server:
port: 8001
#mybatis配置
mybatis:
type-aliases-package: com.lemon.springcloud.pojo
mapper-locations: classpath:mybatis/mapper/*.xml
config-location: classpath:mybatis/mybatis-config.xml
#spring的配置
spring:
application:
name: springcloud-provider-dept
#数据源的配置
datasource:
type: com.alibaba.druid.pool.DruidDataSource #数据源为druid
driver-class-name: com.mysql.jdbc.Driver #数据库驱动
url: jdbc:mysql://localhost:3306/db01?useUnicode=true&characterEncoding=utf-8&servetTimeZone=Asia/Shanghai
username: root
password: root
src/main/resources/mybatis/mybatis-config.xml
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>
src/main/java/com/xxx/springcloud/dao/DeptDao.java
package com.xxx.springcloud.dao;
import com.xxx.springcloud.pojo.Dept;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;
import java.util.List;
@Mapper
public interface DeptDao {
//添加一个部门
public boolean addDept(Dept dept);
//根据id查出一个部门
public Dept queryById(Long id);
//查询所有部门信息
public List<Dept> queryAll();
}
src/main/resources/mybatis/mapper/DeptMapper.xml
DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.xxx.springcloud.dao.DeptDao">
<insert id="addDept" parameterType="com.xxx.springcloud.pojo.Dept">
insert into dept (dname, db_source)
values (#{dname}, DATABASE())
insert>
<select id="queryById" resultType="com.xxx.springcloud.pojo.Dept">
select deptno, dname, db_source
from dept
where deptno = #{deptno};
select>
<select id="queryAll" resultType="com.xxx.springcloud.pojo.Dept">
select deptno, dname, db_source
from dept;
select>
mapper>
src/main/java/com/xxx/springcloud/service/DeptService.java
package com.xxx.springcloud.service;
import com.xxx.springcloud.pojo.Dept;
import java.util.List;
public interface DeptService {
//添加一个部门
public boolean addDept(Dept dept);
//根据id查出一个部门
public Dept queryById(Long id);
//查询所有部门信息
public List<Dept> queryAll();
}
com/xxx/springcloud/service/DeptServiceImpl.java
package com.xxx.springcloud.service;
import com.xxx.springcloud.dao.DeptDao;
import com.xxx.springcloud.pojo.Dept;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class DeptServiceImpl implements DeptService {
@Autowired
public DeptDao deptDao;
@Override
public boolean addDept(Dept dept) {
return deptDao.addDept(dept);
}
@Override
public Dept queryById(Long id) {
return deptDao.queryById(id);
}
@Override
public List<Dept> queryAll() {
return deptDao.queryAll();
}
}
src/main/java/com/xxx/springcloud/controller/DeptController.java
package com.xxx.springcloud.controller;
import com.xxx.springcloud.pojo.Dept;
import com.xxx.springcloud.service.DeptService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
public class DeptController{
@Autowired
private DeptService deptService;
@PostMapping("/dept/add")
public boolean addDept(Dept dept) {
return deptService.addDept(dept);
}
@RequestMapping("/dept/get/{id}")
public Dept queryById(@PathVariable("id") Long id) {
return deptService.queryById(id);
}
@RequestMapping("/dept/list")
public List<Dept> queryAll() {
return deptService.queryAll();
}
}
src/main/java/com/xxx/springcloud/DeptProvider_8001.java
package com.xxx.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class DeptProvider_8001 {
public static void main(String[] args) {
SpringApplication.run(DeptProvider_8001.class);
}
}
<artifactId>springcloud-consumer-dept-80artifactId>
<properties>
<maven.compiler.source>8maven.compiler.source>
<maven.compiler.target>8maven.compiler.target>
properties>
<dependencies>
<dependency>
<groupId>com.xxxgroupId>
<artifactId>springcloud-apiartifactId>
<version>1.0-SNAPSHOTversion>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
dependencies>
src/main/java/com/xxx/springcloud/config/ConfigBean.java
package com.xxx.springcloud.config;
import org.springframework.beans.factory.annotation.Configurable;
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();
}
}
src/main/java/com/xxx/springcloud/controller/DeptConsumerController.java
package com.xxx.springcloud.controller;
import com.xxx.springcloud.pojo.Dept;
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 java.util.List;
@RestController
public class DeptConsumerController {
/**
* 理解:消费者,不应该有service层~
* RestTemplate .... 供我们直接调用就可以了! 注册到Spring中
* (地址:url, 实体:Map ,Class responseType)
*
* 提供多种便捷访问远程http服务的方法,简单的Restful服务模板~
*/
@Autowired
private RestTemplate restTemplate;
/**
* 服务提供方地址前缀
*
* Ribbon:我们这里的地址,应该是一个变量,通过服务名来访问
*/
private static final String REST_URL_PREFIX = "http://localhost:8001";
//private static final String REST_URL_PREFIX = "http://SPRINGCLOUD-PROVIDER-DEPT";
/**
* 消费方添加部门信息
* @param dept
* @return
*/
@RequestMapping("/consumer/dept/add")
public boolean add(Dept dept) {
// postForObject(服务提供方地址(接口),参数实体,返回类型.class)
return Boolean.TRUE.equals(restTemplate.postForObject(REST_URL_PREFIX + "/dept/add", dept, Boolean.class));
}
/**
* 消费方根据id查询部门信息
* @param id
* @return
*/
@RequestMapping("/consumer/dept/get/{id}")
public Dept get(@PathVariable("id") Long id) {
// getForObject(服务提供方地址(接口),返回类型.class)
return restTemplate.getForObject(REST_URL_PREFIX + "/dept/get/" + id, Dept.class);
}
/**
* 消费方查询部门信息列表
* @return
*/
@RequestMapping("/consumer/dept/list")
public List<Dept> list() {
return restTemplate.getForObject(REST_URL_PREFIX + "/dept/list", List.class);
}
}
src/main/java/com/xxx/springcloud/DeptConsumer_80.java
package com.xxx.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class DeptConsumer_80 {
public static void main(String[] args) {
SpringApplication.run(DeptConsumer_80.class, args);
}
}
三大角色
* Eureka Server:提供服务的注册与发现
* Service Provider:服务生产方,将自身服务注册到Eureka中,从而使服务消费方能狗找到
* Service Consumer:服务消费方,从Eureka中获取注册服务列表,从而找到消费服务
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-serverartifactId>
dependency>
dependencies>
src/main/resources/application.yml
# 默认端口号:8761
server:
port: 8761
# Eureka配置
eureka:
instance:
# Eureka服务端的实例名字
hostname: 127.0.0.1
client:
# 表示是否向 Eureka 注册中心注册自己(这个模块本身是服务器,所以不需要)
# 让当前应用仅仅是服务注册中心
register-with-eureka: false
# fetch-registry如果为false,则表示自己为注册中心,客户端的化为 ture
# 关闭eureka client的立即注册
fetch-registry: false
# Eureka监控页面~
service-url:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
# spring 配置
spring:
application:
# 服务名不能出现下划线,默认服务名不区分大小写,推荐大写
name: EUREKASERVER
src/main/java/com/xxx/EurekaServerApplication.java
package com.xxx;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@SpringBootApplication
//开启服务注册中心
@EnableEurekaServer
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class, args);
}
}
基于业务开发出来的一个个微服务
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
dependency>
dependencies>
src/main/resources/application.yml
# 端口
server:
port: 8989
# 服务名称
spring:
application:
name: EUREKACLIENT
# 指定服务注册中心地址 Eureka配置:配置服务注册中心地址
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
src/main/java/com/xxx/EurekaClintApplication.java
package com.xxx;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication
//开启Eureka客户端
@EnableEurekaClient
public class EurekaClintApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaClintApplication.class, args);
}
}
出现一下这句话,说明进入自我保护机制
详细内容可以参考下这篇博客内容:https://blog.csdn.net/wudiyong22/article/details/80827594
服务端配置测试
客户端配置测试
加快统计时间,用最短的时间清除自我保护
4. 停止服务,配置本地hosts
路径:C:\Windows\System32\drivers\etc
5. 配置hosts
注意:最好通过软件修改,如果手动打开文件修改可能不生效或者保存失败
127.0.0.1 eureka8761.com
127.0.0.1 eureka8762.com
127.0.0.1 eureka8763.com
springcloud_01eureka_server\src\main\resources\application.yml
server:
port: 8761
#Eureka配置
eureka:
instance:
hostname: eureka8761.com #Eureka服务端的实例名字
client:
register-with-eureka: false #表示是否向 Eureka 注册中心注册自己(这个模块本身是服务器,所以不需要)
fetch-registry: false #fetch-registry如果为false,则表示自己为注册中心
service-url: #监控页面~
#重写Eureka的默认端口以及访问路径 --->http://localhost:8761/eureka/
# 单机: defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
# 集群(关联):8761关联8762、8763
defaultZone: http://eureka8762.com:8762/eureka/,http://eureka8763.com:8763/eureka/
两个节点,当前运行时8761
7. 配置服务端8762集群
路径:springcloud_01eureka_server\src\main\resources\application.yml
server:
port: 8762
#Eureka配置
eureka:
instance:
hostname: eureka8762.com #Eureka服务端的实例名字
client:
register-with-eureka: false #表示是否向 Eureka 注册中心注册自己(这个模块本身是服务器,所以不需要)
fetch-registry: false #fetch-registry如果为false,则表示自己为注册中心
service-url: #监控页面~
#重写Eureka的默认端口以及访问路径 --->http://localhost:8761/eureka/
# 单机: defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
# 集群(关联):8762关联8761、8763
defaultZone: http://eureka8761.com:8761/eureka/,http://eureka8763.com:8763/eureka/
springcloud_01eureka_server\src\main\resources\application.yml
server:
port: 8763
#Eureka配置
eureka:
instance:
hostname: eureka8763.com #Eureka服务端的实例名字
client:
register-with-eureka: false #表示是否向 Eureka 注册中心注册自己(这个模块本身是服务器,所以不需要)
fetch-registry: false #fetch-registry如果为false,则表示自己为注册中心
service-url: #监控页面~
#重写Eureka的默认端口以及访问路径 --->http://localhost:8761/eureka/
# 单机: defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
# 集群(关联):8763关联8762、8761
defaultZone: http://eureka8761.com:8761/eureka/,http://eureka8762.com:8762/eureka/
springcloud_02eureka_client\src\main\resources\application.yml
# 端口
server:
port: 8989
# Eureka配置:配置服务注册中心地址
# Eureka配置:配置服务注册中心地址
eureka:
client:
service-url:
# 注册中心地址8761-8763
defaultZone: http://eureka8761.com:8761/eureka/,http://eureka8762.com:8762/eureka/,http://eureka8763.com:8763/eureka/
访问:http://eureka8762.com:8762/
访问:http://eureka8763.com:8763/
文档:https://www.consul.io/docs/architecture
下载完成后生成如下文件
指定数据中心名称启动:consul agent -dev -datacenter=aaa
访问:http://localhost:8500/
自动跳转到 http://localhost:8500/ui/aaa/services
配置环境变量
查看当前exe的路径:E:\Java\springCloud\other\consul_1.13.2_windows_amd64
右键win->应用和功能(F)->搜索环境变量,最后编辑path,全部点击确定
添加完成后,任意位置都可以打开consul 了
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-consul-discoveryartifactId>
dependency>
dependencies>
springcloud_03consulclient\src\main\resources\application.yml
# 端口
server:
port: 8082
# 指定名称
spring:
application:
name: CONSULCLIENT
cloud:
consul:
host: localhost
port: 8500
discovery:
service-name: ${spring.application.name} # 注册服务名称
# consul server 服务注册地址
src/main/java/com/xxx/ConsulClientApplication.java
package com.xxx;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication
// 开启consul ,jk
@EnableDiscoveryClient
public class ConsulClientApplication {
public static void main(String[] args) {
SpringApplication.run(ConsulClientApplication.class, args);
}
}
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-actuatorartifactId>
dependency>
users Model 和 orders model 同步进行创建配置,最后进行通信,因为是user调用order,所以要先有order,user在调用。
名称:springcloud_04users
路由查看
pom.xml 依赖引入
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-consul-discoveryartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-actuatorartifactId>
dependency>
dependencies>
application.yml配置
# 端口
server:
port: 8888
# 指定名称
spring:
application:
name: USER
cloud:
consul:
host: localhost
port: 8500
discovery:
service-name: ${spring.application.name} # 注册服务名
UsersApplication启动类创建
package com.xxx;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication
// 开启consul ,jk
@EnableDiscoveryClient
public class UsersApplication {
public static void main(String[] args) {
SpringApplication.run(UsersApplication.class, args);
}
}
启动运行,先开启的两个服务,再查看的服务管理,所有两个都有
创建controller:src/main/java/com/xxx/controller/UserController.java
package com.xxx.controller;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
@RestController
public class UserController {
private static final Logger log = LoggerFactory.getLogger(UserController.class);
/**
* 调用demo
*/
@GetMapping("user")
public String invokeDemo() {
System.out.println("user demo");
//调用order 订单服务,http://localhost:9999/order 接收返回值
RestTemplate restTemplate = new RestTemplate();
String orderResult = restTemplate.getForObject("http://localhost:9999/order", String.class);
log.info("调用顶层服务成功:{}", orderResult);
return "调用order服务成功,结果为:" + orderResult;
}
}
启动程序并访问:http://localhost:8888/user
名称:springcloud_05orders
路由查看
pom.xml 依赖引入
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-consul-discoveryartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-actuatorartifactId>
dependency>
dependencies>
application.yml配置
# 端口
server:
port: 9999
# 指定名称
spring:
application:
name: ORDER
cloud:
consul:
host: localhost
port: 8500
discovery:
service-name: ${spring.application.name} # 注册服务名称
OrdersApplication启动类创建
package com.xxx;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication
// 开启consul ,jk
@EnableDiscoveryClient
public class OrdersApplication {
public static void main(String[] args) {
SpringApplication.run(OrdersApplication.class, args);
}
}
启动运行,先开启的两个服务,再查看的服务管理,所有两个都有
创建controller:src/main/java/com/xxx/controller/OrderController.java
package com.xxx.controller;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class OrderController {
private static final Logger log = LoggerFactory.getLogger(OrderController.class);
@Value("${server.port}")
private int port;
/**
* 调用demo
*/
@GetMapping("order")
public String demo() {
log.info("order demo.....");
return "order demo ok ,当前提供服务端口为:" + port;
}
}
启动程序并访问:http://localhost:9999/order
Ribbon:只能负载均衡不能发送请求
RestTemplate:发送请求
服务注册与发现客户端对象,需要自己写策略
路径: springcloud_04users\src\main\java\com\xxx\controller\UserController.java
package com.xxx.controller;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import java.util.List;
@RestController
public class UserController {
private static final Logger log = LoggerFactory.getLogger(UserController.class);
@Autowired //服务注册与发现客户端对象
private DiscoveryClient discoveryClient;
/**
* 调用demo
*/
@GetMapping("user")
public String invokeDemo() {
//1. 调用order 订单服务,http://localhost:9999/order 接收返回值
//RestTemplate restTemplate = new RestTemplate();
//String orderResult = restTemplate.getForObject("http://localhost:9999/order", String.class);
//2. 调用ribbon组件+restTemplate 实现负载均衡的调用 1. DiscoveryClient 2. LoadBalanceClient 3.LoadBalance
List<ServiceInstance> serviceInstances = discoveryClient.getInstances("ORDER");
serviceInstances.forEach(serviceInstance -> {
log.info("服务机:{} 服务端口:{} 服务地址:{}", serviceInstance.getHost(), serviceInstance.getPort(), serviceInstance.getUri());
});
/* log.info("调用顶层服务成功:{}", orderResult);
return "调用order服务成功,结果为:" + orderResult;*/
return "user ok";
}
}
运行:http://localhost:8888/user
,发现返回两个接口的数据
负载均衡客户端对象,轮询策略
路径: springcloud_04users\src\main\java\com\xxx\controller\UserController.java
package com.xxx.controller;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import java.util.List;
@RestController
public class UserController {
private static final Logger log = LoggerFactory.getLogger(UserController.class);
@Autowired //服务注册与发现客户端对象,需要自己写策略
private DiscoveryClient discoveryClient;
@Autowired //负载均衡客户端对象,轮询策略
private LoadBalancerClient loadBalancerClient;
/**
* 调用demo
*/
@GetMapping("user")
public String invokeDemo() {
//1. 调用order 订单服务,http://localhost:9999/order 接收返回值
//RestTemplate restTemplate = new RestTemplate();
//String orderResult = restTemplate.getForObject("http://localhost:9999/order", String.class);
//2. 调用ribbon组件+restTemplate 实现负载均衡的调用 1. DiscoveryClient 2. LoadBalancerClient 3.LoadBalance
/* List serviceInstances = discoveryClient.getInstances("ORDER");
serviceInstances.forEach(serviceInstance -> {
log.info("服务机:{} 服务端口:{} 服务地址:{}", serviceInstance.getHost(), serviceInstance.getPort(), serviceInstance.getUri());
});
String forObject = new RestTemplate().getForObject(serviceInstances.get(0).getUri() + "/order", String.class);
*/
// 3. 使用LoadBalancerClient 进行服务调用 轮询策略
ServiceInstance serviceInstance = loadBalancerClient.choose("ORDER");
log.info("服务机:{} 服务端口:{} 服务地址:{}", serviceInstance.getHost(), serviceInstance.getPort(), serviceInstance.getUri());
String forObject = new RestTemplate().getForObject(serviceInstance.getUri() + "/order", String.class);
/* log.info("调用顶层服务成功:{}", orderResult);
return "调用order服务成功,结果为:" + orderResult;*/
return "user ok" + forObject;
}
}
访问:http://localhost:8888/user
,查看服务端信息
服务机:localhost 服务端口:9990 服务地址:http://localhost:9990
再次访问:http://localhost:8888/user
,查看服务端信息
服务机:localhost 服务端口:9999 服务地址:http://localhost:9999
springcloud_04users\src\main\java\com\xxx\config\BeansConfig.java
package com.xxx.config;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
@Configuration //代表这是一个springboot 配置类 spring.xml
public class BeansConfig {
//在工厂中创建restTemplate
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
springcloud_04users\src\main\java\com\xxx\controller\UserController.java
package com.xxx.controller;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import java.util.List;
@RestController
public class UserController {
private static final Logger log = LoggerFactory.getLogger(UserController.class);
@Autowired //服务注册与发现客户端对象,需要自己写策略
private DiscoveryClient discoveryClient;
@Autowired //负载均衡客户端对象,轮询策略
private LoadBalancerClient loadBalancerClient;
@Autowired
private RestTemplate restTemplate;
/**
* 调用demo
*/
@GetMapping("user")
public String invokeDemo() {
//1. 调用order 订单服务,http://localhost:9999/order 接收返回值
//RestTemplate restTemplate = new RestTemplate();
//String orderResult = restTemplate.getForObject("http://localhost:9999/order", String.class);
//2. 调用ribbon组件+restTemplate 实现负载均衡的调用 1. DiscoveryClient 2. LoadBalancerClient 3.@LoadBalance
/* List serviceInstances = discoveryClient.getInstances("ORDER");
serviceInstances.forEach(serviceInstance -> {
log.info("服务机:{} 服务端口:{} 服务地址:{}", serviceInstance.getHost(), serviceInstance.getPort(), serviceInstance.getUri());
});
String forObject = new RestTemplate().getForObject(serviceInstances.get(0).getUri() + "/order", String.class);
*/
// 3. 使用LoadBalancerClient 进行服务调用 轮询策略
/* ServiceInstance serviceInstance = loadBalancerClient.choose("ORDER");
log.info("服务机:{} 服务端口:{} 服务地址:{}", serviceInstance.getHost(), serviceInstance.getPort(), serviceInstance.getUri());
String forObject = restTemplate.getForObject(serviceInstance.getUri() + "/order", String.class);*/
// 4. 使用 @LoadBalanced注解调用
String forObject = restTemplate.getForObject("http://ORDER/order", String.class);
/* log.info("调用顶层服务成功:{}", orderResult);
return "调用order服务成功,结果为:" + orderResult;*/
return "user ok" + forObject;
}
}
网页:https://spring.io/projects/spring-cloud-openfeign
consul agent -dev
路径:http://localhost:8500/ui/dc1/services
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-consul-discoveryartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-actuatorartifactId>
dependency>
dependencies>
application配置:src/main/resources/application.yml
# 端口
# 端口
server:
port: 8787
spring:
application:
name: CATEGORY
cloud:
consul:
host: localhost
port: 8500
创建启动文件:src/main/java/com/xxx/CategoryApplication.java
package com.xxx;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@EnableDiscoveryClient
public class CategoryApplication {
public static void main(String[] args) {
SpringApplication.run(CategoryApplication.class, args);
}
}
启动服务查看consul:http://localhost:8500/ui/dc1/services
创建控制类:src/main/java/com/xxx/controller/CategoryController.java
package com.xxx.controller;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class CategoryController {
private static final Logger log = LoggerFactory.getLogger(CategoryController.class);
@Value("${server.port}")
private int port;
@GetMapping("/category")
public String category() {
log.info("进入类别服务....");
return "category ok,类别服务提供的端口为:" + port;
}
}
访问:http://localhost:8787/category
pom.xml 添加openfeign
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-openfeignartifactId>
dependency>
启动类开启openFeign
package com.xxx;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
@EnableDiscoveryClient//开启服务注册
@EnableFeignClients//开启openFeign的调用
public class CategoryApplication {
public static void main(String[] args) {
SpringApplication.run(CategoryApplication.class, args);
}
}
创建配置:src/main/java/com/xxx/feignclient/ProductClient.java
package com.xxx.feignclient;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
//调用商品服务接口
@FeignClient(value = "PRODUCT")//value:用来书写调用服务的id
public interface ProductClient {
//调用商品服务
@GetMapping("/product")
public String product();
}
修改控制器:src/main/java/com/xxx/controller/CategoryController.java
package com.xxx.controller;
import com.xxx.feignclient.ProductClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class CategoryController {
private static final Logger log = LoggerFactory.getLogger(CategoryController.class);
@Autowired
private ProductClient productClient;
@GetMapping("/category")
public String category() {
log.info("进入类别服务....");
String product = productClient.product();
return "category ok !!!" + product;
}
}
启动,访问路径:http://localhost:8787/category
测试:product添加了一个list方法,这边修改ProductClient
package com.xxx.feignclient;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
//调用商品服务接口
@FeignClient(value = "PRODUCT")//value:用来书写调用服务的id
public interface ProductClient {
//调用商品服务
@GetMapping("/product")
public String product();
@GetMapping("/list")
public String list();
}
测试:修改controller
package com.xxx.controller;
import com.xxx.feignclient.ProductClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class CategoryController {
private static final Logger log = LoggerFactory.getLogger(CategoryController.class);
@Autowired
private ProductClient productClient;
@GetMapping("/category")
public String category() {
log.info("进入类别服务....");
String product = productClient.product();
String list = productClient.list();
log.info("结果1:{}",product);
log.info("结果2:{}",list);
return "category ok !!! ===>product:" + product+" ===>list:"+list;
}
}
访问路径:http://localhost:8787/category
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-consul-discoveryartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-actuatorartifactId>
dependency>
dependencies>
application配置:src/main/resources/application.yml
# 端口
# 端口
server:
port: 8788
spring:
application:
name: PRODUCT
cloud:
consul:
host: localhost
port: 8500
创建启动文件:src/main/java/com/xxx/ProductApplication.java
package com.xxx;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@EnableDiscoveryClient
public class ProductApplication {
public static void main(String[] args) {
SpringApplication.run(ProductApplication.class, args);
}
}
启动服务查看consul:http://localhost:8500/ui/dc1/services
创建控制类:src/main/java/com/xxx/controller/ProductController.java
package com.xxx.controller;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ProductController {
private static final Logger log = LoggerFactory.getLogger(ProductController.class);
@Value("${server.port}")
private int port;
@GetMapping("/product")
public String product() {
log.info("进入商品服务....");
return "product ok,商品服务提供的端口为:" + port;
}
}
访问:http://localhost:8788/product
pom.xml 添加openfeign
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-openfeignartifactId>
dependency>
启动类开启openFeign
package com.xxx;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
@EnableDiscoveryClient//开启服务注册
@EnableFeignClients//开启openFeign的调用
public class ProductApplication {
public static void main(String[] args) {
SpringApplication.run(ProductApplication.class, args);
}
}
controller 添加方法:src/main/java/com/xxx/controller/ProductController.java
package com.xxx.controller;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ProductController {
private static final Logger log = LoggerFactory.getLogger(ProductController.class);
@Value("${server.port}")
private int port;
@GetMapping("/product")
public String product() {
log.info("进入商品服务....");
return "product ok,商品服务提供的端口为:" + port;
}
@GetMapping("/list")
public String list() {
log.info("进入商品列表服务....");
return "list ok,商品列表服务提供的端口为:" + port;
}
}
querySting:test?name=zs&age=11
ProductController :springcloud_07product\src\main\java\com\xxx\controller\ProductController.java
package com.xxx.controller;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ProductController {
private static final Logger log = LoggerFactory.getLogger(ProductController.class);
@Value("${server.port}")
private int port;
// 零散类型 querySting test?name=zs&age=11
@GetMapping("/test")
public String test(@RequestParam("name") String name, @RequestParam("age") Integer age) {
log.info("name:{} age:{}", name, age);
return "test ok, 当前服务端口为:" + port;
}
}
ProductClient:springcloud_06category\src\main\java\com\xxx\feignclient\ProductClient.java
package com.xxx.feignclient;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
//调用商品服务接口
@FeignClient(value = "PRODUCT")//value:用来书写调用服务的id
public interface ProductClient {
//调用商品服务
@GetMapping("/test")
public String test(@RequestParam("name") String name, @RequestParam("age") Integer age);
}
CategoryController:springcloud_06category\src\main\java\com\xxx\controller\CategoryController.java
package com.xxx.controller;
import com.xxx.feignclient.ProductClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class CategoryController {
private static final Logger log = LoggerFactory.getLogger(CategoryController.class);
@Autowired
private ProductClient productClient;
@GetMapping("/category")
public String category() {
log.info("进入类别服务....");
String zs = productClient.test("zs", 11);
return "category ok !!! " + zs;
}
}
访问:http://localhost:8787/category
查看ProductController运行界面,已经接收到
路由传参:test1/1/zs
ProductController :springcloud_07product\src\main\java\com\xxx\controller\ProductController.java
package com.xxx.controller;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ProductController {
private static final Logger log = LoggerFactory.getLogger(ProductController.class);
@Value("${server.port}")
private int port;
// 零散类型 路径传参 test1/zs/11
@GetMapping("/test1/{id}/{name}")
public String test1(@PathVariable("id") Integer id, @PathVariable("name") String name) {
log.info("id:{} name:{}", id, name);
return "test1 ok, 当前服务端口为:" + port;
}
}
ProductClient:springcloud_06category\src\main\java\com\xxx\feignclient\ProductClient.java
package com.xxx.feignclient;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestParam;
//调用商品服务接口
@FeignClient(value = "PRODUCT")//value:用来书写调用服务的id
public interface ProductClient {
//调用商品服务
// 零散类型 路径传参 test1/zs/11
@GetMapping("/test1/{id}/{name}")
public String test1(@PathVariable("id") Integer id, @PathVariable("name") String name);
@GetMapping("/product")
}
CategoryController:springcloud_06category\src\main\java\com\xxx\controller\CategoryController.java
package com.xxx.controller;
import com.xxx.feignclient.ProductClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class CategoryController {
private static final Logger log = LoggerFactory.getLogger(CategoryController.class);
@Autowired
private ProductClient productClient;
@GetMapping("/category")
public String category() {
log.info("进入类别服务....");
String zs = productClient.test1(1, "zs");
return "category ok !!! " + zs;
}
}
访问:http://localhost:8787/category
查看ProductController运行界面,已经接收到
form 表单 ---------------> 参数加 @RequestPart ,文件上传的时候可以用
application/json--------> 参数加 @RequestBody
Product实体类:springcloud_07product\src\main\java\com\xxx\pojo\Product.java
package com.xxx.pojo;
import java.util.Date;
public class Product {
private Integer id;
private String name;
private Double price;
private Date bir;
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;
}
public Double getPrice() {
return price;
}
public void setPrice(Double price) {
this.price = price;
}
public Date getBir() {
return bir;
}
public void setBir(Date bir) {
this.bir = bir;
}
public Product() {
}
public Product(Integer id, String name, Double price, Date bir) {
this.id = id;
this.name = name;
this.price = price;
this.bir = bir;
}
@Override
public String toString() {
return "Product{" +
"id=" + id +
", name='" + name + '\'' +
", price=" + price +
", bir=" + bir +
'}';
}
}
Category实体类:springcloud_06category\src\main\java\com\xxx\pojo\Product.java
package com.xxx.pojo;
import java.util.Date;
public class Product {
private Integer id;
private String name;
private Double price;
private Date bir;
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;
}
public Double getPrice() {
return price;
}
public void setPrice(Double price) {
this.price = price;
}
public Date getBir() {
return bir;
}
public void setBir(Date bir) {
this.bir = bir;
}
public Product() {
}
public Product(Integer id, String name, Double price, Date bir) {
this.id = id;
this.name = name;
this.price = price;
this.bir = bir;
}
@Override
public String toString() {
return "Product{" +
"id=" + id +
", name='" + name + '\'' +
", price=" + price +
", bir=" + bir +
'}';
}
}
ProductController :springcloud_07product\src\main\java\com\xxx\controller\ProductController.java
package com.xxx.controller;
import com.xxx.pojo.Product;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;
@RestController
public class ProductController {
private static final Logger log = LoggerFactory.getLogger(ProductController.class);
@Value("${server.port}")
private int port;
// 对象类型
@PostMapping("/test3")
public String test3(@RequestBody Product product) {
log.info("product:{} ", product);
return "test3 ok, 当前服务端口为:" + port;
}
}
ProductClient:springcloud_06category\src\main\java\com\xxx\feignclient\ProductClient.java
package com.xxx.feignclient;
import com.xxx.pojo.Product;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.*;
//调用商品服务接口
@FeignClient(value = "PRODUCT")//value:用来书写调用服务的id
public interface ProductClient {
//调用商品服务
// 对象类型
@PostMapping("/test3")
public String test3(@RequestBody Product product);
}
CategoryController:springcloud_06category\src\main\java\com\xxx\controller\CategoryController.java
package com.xxx.controller;
import com.xxx.feignclient.ProductClient;
import com.xxx.pojo.Product;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Date;
@RestController
public class CategoryController {
private static final Logger log = LoggerFactory.getLogger(CategoryController.class);
@Autowired
private ProductClient productClient;
@GetMapping("/category")
public String category() {
log.info("进入类别服务....");
String zs = productClient.test3(new Product(1, "zs", 23.3, new Date()));
return "category ok !!! " + zs;
}
}
访问:http://localhost:8787/category
查看ProductController运行界面,已经接收到
querySting方式:test?name=zs&age=11
ProductController :springcloud_07product\src\main\java\com\xxx\controller\ProductController.java
package com.xxx.controller;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ProductController {
private static final Logger log = LoggerFactory.getLogger(ProductController.class);
@Value("${server.port}")
private int port;
// 数组类型 queryString test4?name=zs&age=11
@GetMapping("/test4")
public String test4(@RequestParam("ids") String[] ids) {
for (String id : ids) {
log.info("id:{} ", id);
}
//转为list集合
List<String> strings = Arrays.asList(ids);
return "test4 ok, 当前服务端口为:" + port;
}
}
ProductClient:springcloud_06category\src\main\java\com\xxx\feignclient\ProductClient.java
package com.xxx.feignclient;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
//调用商品服务接口
@FeignClient(value = "PRODUCT")//value:用来书写调用服务的id
public interface ProductClient {
// 数组类型 queryString test4?name=zs&age=11
@GetMapping("/test4")
public String test4(@RequestParam("ids") String[] ids);
}
CategoryController:springcloud_06category\src\main\java\com\xxx\controller\CategoryController.java
package com.xxx.controller;
import com.xxx.feignclient.ProductClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class CategoryController {
private static final Logger log = LoggerFactory.getLogger(CategoryController.class);
@Autowired
private ProductClient productClient;
@GetMapping("/category")
public String category() {
log.info("进入类别服务....");
String result = productClient.test4(new String[]{"11", "22", "33", "44"});
return "category ok !!! " + result;
}
}
访问:http://localhost:8787/category
查看ProductController运行界面,已经接收到
oo(oriented 面向 object 对象):面向对象
vo(value 值 object 对象):用来传递数据对象称之为值对象
dto(data 数据 transfer 传输 object 对象):数据传输对象
传递:以数组传递 。
接收:以 值对象vo 接收。
Product创建vo对象:springcloud_07product\src\main\java\com\xxx\vos\CollectionVO.java
package com.xxx.vos;
import java.util.List;
//定义用来接收集合类型参数对象
public class CollectionVO {
private List<String> ids;
public List<String> getIds() {
return ids;
}
public void setIds(List<String> ids) {
this.ids = ids;
}
}
ProductController :springcloud_07product\src\main\java\com\xxx\controller\ProductController.java
package com.xxx.controller;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ProductController {
private static final Logger log = LoggerFactory.getLogger(ProductController.class);
@Value("${server.port}")
private int port;
// 集合列表
// oo(oriented 面向 object 对象):面向对象 vo(value 值 object 对象):用来传递数据对象称之为值对象
// dto(data 数据 transfer 传输 object 对象):数据传输对象
@PostMapping("/test5")
public String test5(CollectionVO collectionVO) {
collectionVO.getIds().forEach(id -> log.info("id:{}", id));
return "test5 ok, 当前服务端口为:" + port;
}
}
ProductClient:springcloud_06category\src\main\java\com\xxx\feignclient\ProductClient.java
package com.xxx.feignclient;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
//调用商品服务接口
@FeignClient(value = "PRODUCT")//value:用来书写调用服务的id
public interface ProductClient {
// 集合列表
@PostMapping("/test5")
public String test5(@RequestParam("ids") String[] ids);
}
CategoryController:springcloud_06category\src\main\java\com\xxx\controller\CategoryController.java
package com.xxx.controller;
import com.xxx.feignclient.ProductClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class CategoryController {
private static final Logger log = LoggerFactory.getLogger(CategoryController.class);
@Autowired
private ProductClient productClient;
@GetMapping("/category")
public String category() {
log.info("进入类别服务....");
String result = productClient.test5(new String[]{"11", "22", "33", "44"});
return "category ok !!! " + result;
}
}
访问:http://localhost:8787/category
查看ProductController运行界面,已经接收到
ProductController :springcloud_07product\src\main\java\com\xxx\controller\ProductController.java
package com.xxx.controller;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ProductController {
private static final Logger log = LoggerFactory.getLogger(ProductController.class);
@Value("${server.port}")
private int port;
// 定义一个接口接收id类型参数,返回一个基于id查询的对象
@GetMapping("/product/{id}")
public Product product(@PathVariable("id") Integer id) {
log.info("id:{} ", id);
return new Product(1, "十四", 23.5, new Date());
}
}
ProductClient:springcloud_06category\src\main\java\com\xxx\feignclient\ProductClient.java
package com.xxx.feignclient;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
//调用商品服务接口
@FeignClient(value = "PRODUCT")//value:用来书写调用服务的id
public interface ProductClient {
//根据id查询商品
@GetMapping("/product/{id}")
public Product product(@PathVariable("id") Integer id);
}
CategoryController:springcloud_06category\src\main\java\com\xxx\controller\CategoryController.java
package com.xxx.controller;
import com.xxx.feignclient.ProductClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class CategoryController {
private static final Logger log = LoggerFactory.getLogger(CategoryController.class);
@Autowired
private ProductClient productClient;
@GetMapping("/category")
public Product category() {
log.info("进入类别服务....");
Product result = productClient.product(1);
return result;
}
}
访问:http://localhost:8787/category
ProductController :springcloud_07product\src\main\java\com\xxx\controller\ProductController.java
package com.xxx.controller;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ProductController {
private static final Logger log = LoggerFactory.getLogger(ProductController.class);
@Value("${server.port}")
private int port;
// 定义一个接口接收类型id类型参数,返回一个基于id查询的list对象
@GetMapping("/products")
public List<Product> finProductsByCategoryId(@RequestParam("categoryId") Integer categoryId) {
log.info("categoryId:{} ", categoryId);
List<Product> productList = new ArrayList<>();
productList.add(new Product(1, "十四", 24.5, new Date()));
productList.add(new Product(2, "十五", 25.5, new Date()));
productList.add(new Product(3, "十六", 26.5, new Date()));
return productList;
//return new Product(1, "十四", 23.5, new Date());
}
}
ProductClient:springcloud_06category\src\main\java\com\xxx\feignclient\ProductClient.java
package com.xxx.feignclient;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
//调用商品服务接口
@FeignClient(value = "PRODUCT")//value:用来书写调用服务的id
public interface ProductClient {
// 定义一个接口接收类型id类型参数,返回一个基于id查询的list对象
@GetMapping("/products")
public List<Product> finProductsByCategoryId(@RequestParam("categoryId") Integer categoryId) ;
}
CategoryController:springcloud_06category\src\main\java\com\xxx\controller\CategoryController.java
package com.xxx.controller;
import com.xxx.feignclient.ProductClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class CategoryController {
private static final Logger log = LoggerFactory.getLogger(CategoryController.class);
@Autowired
private ProductClient productClient;
@GetMapping("/category")
public List<Product> category() {
log.info("进入类别服务....");
List<Product> products = productClient.finProductsByCategoryId(1);
return products;
}
}
访问:http://localhost:8787/category
ProductController :springcloud_07product\src\main\java\com\xxx\controller\ProductController.java
package com.xxx.controller;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ProductController {
private static final Logger log = LoggerFactory.getLogger(ProductController.class);
@Value("${server.port}")
private int port;
// 定义一个接口接收类型id类型参数,返回一个基于id查询的list对象
@GetMapping("/productsPage")
public Map<String, Object> finProductsByCategoryIdAndPage(@RequestParam("page") Integer page, @RequestParam("pageNum") Integer pageNum, @RequestParam("categoryId") Integer categoryId) {
log.info("当前页:{} 每页数量:{} categoryId:{} ", page, pageNum, categoryId);
Map<String, Object> map = new HashMap<>();
List<Product> productList = new ArrayList<>();
productList.add(new Product(1, "十四", 24.5, new Date()));
productList.add(new Product(2, "十五", 25.5, new Date()));
productList.add(new Product(3, "十六", 26.5, new Date()));
map.put("row", productList);
map.put("total", 50);
return map;
}
}
ProductClient:springcloud_06category\src\main\java\com\xxx\feignclient\ProductClient.java
package com.xxx.feignclient;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
//调用商品服务接口
@FeignClient(value = "PRODUCT")//value:用来书写调用服务的id
public interface ProductClient {
// 定义一个接口接收类型id类型参数,返回一个基于id查询的list对象,并返回分页信息
@GetMapping("/productsPage")
public Map<String, Object> finProductsByCategoryIdAndPage(@RequestParam("page") Integer page, @RequestParam("pageNum") Integer pageNum, @RequestParam("categoryId") Integer categoryId);
}
CategoryController:springcloud_06category\src\main\java\com\xxx\controller\CategoryController.java
package com.xxx.controller;
import com.xxx.feignclient.ProductClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class CategoryController {
private static final Logger log = LoggerFactory.getLogger(CategoryController.class);
@Autowired
private ProductClient productClient;
@GetMapping("/category")
public Map<String, Object> category() {
log.info("进入类别服务....");
Map<String, Object> map = productClient.finProductsByCategoryIdAndPage(1, 10, 1);
int total = (int) map.get("total");
List<Product> row = (List<Product>) map.get("row");
System.out.println(total);
System.out.println(row);
return map;
}
}
访问:http://localhost:8787/category
CategoryController 查看打印的数据
openfeign默认调用时间为1秒,超时报错,如下图。可以通过修改超时时间,处理.
路径:springcloud_06category\src\main\resources\application.yml
# 端口
server:
port: 8787
spring:
application:
name: CATEGORY
cloud:
consul:
host: localhost
port: 8500
# 配置类别调用商品服务openfeign 默认 超时时间 默认1s 现在改为5秒。PRODUCT:指定的服务名称
feign:
client:
config:
PRODUCT:
connect-timeout: 5000
read-timeout: 5000