SpringCloud服务注册发现(服务治理)之Eureka学习笔记步步截图(附带学习时写的源码项目)
SpringCloud
整个spring cloud学习时写的项目源码:git@gitee.com:HumorChen/spring-cloud-parent.git
初识Spring Cloud
什么是微服务
- "微服务”一词源于Martin Fowler的名为Microservices的博文,可以在他的官方博客上找到
http://martinfowler.com/articles/microservices.html - 微服务是系统架构上的一种设计风格,它的主旨是将一个原本独立的系统拆分成多个小型服务,这些小型服务都在各自独立的进程中运行,服务之间一般通过HTTP的RESTfuLAPI进行通信协作。
- 被拆分成的每一个小型服务都围绕着系统中的某一项或些耦合度较高的业务功能进行构建,并且每个服务都维护着白身的数据存储、业务开发自动化测试案例以及独立部署机制。
SpringCloud简介
-
spring cloud 是一系列框架的有序集合
-
spring cloud 并没有重复制造轮子,它只是将目前各家公司开发的比较成熟的、经得起实际考验的框架组合起来
-
通过Spring Boot 风格进行再封装屏蔽掉了复杂的配置和实现原理,最终给开发者留出了一套简单易懂、易部署和易维护的分布式系统开发工具包
-
它利用Spring Boot的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务注册发现、配置中心、消息总线、负载均衡、断路器、数据监控等,都可以用Spring Boot的开发风格做到一键启动和部署。
-
Spring Cloud版本命名方式采用了伦敦地铁站的名称,同时根据字母表的顺序来对应版本时间顺序,比如:最早的Release版本: Angel,第二个Release版本: Brixton,然后是Camden、Dalston、Edgware,Finchley,Greenwich,Hoxton
-
spring cloud版本和springboot版本对应关系
Spring Cloud 与Dubbo对比
- Spring Cloud 与 Dubbo都是实现微服务有效的工具。
- Dubbo只是实现了服务治理,而Spring Cloud子项目分别覆盖了微服务架构下的众多部件。
- Dubbo使用RPC通讯协议,Spring Cloud使用RESTful完成通信,Dubbo效率略高于Spring Cloud。
小结
- 微服务就是将项目的各个模块拆分为可独立运行、部署、测试的架构设计风格。
- Spring公司将其他公司中微服务架构常用的组件整合起来,并使用SpringBoot简化其开发、配置。称为Spring Cloud
- Spring Cloud 与Dubbo都是实现微服务有效的工具。Dubbo性能更好,而Spring Cloud功能更全面。
服务治理
Eureka
- Eureka是 Netflix公司开源的一个服务注册与发现的组件。
- Eureka和其他Netflix公司的服务组件(例负载均衡、熔断器、网关等)一起,被Spring Cloud社区整合为Spring-Cloud-Netflix模块。
- Eureka包含两个组件: Eureka Server (注册中心)和Eureka Client(服务提供者、服务消费者)。
搭建步骤
- 搭建Provider和Consumer服务。
- 使用RestTemplate完成远程调用。
- 搭建Eureka Server 服务。
- 改造Provider和Consumer称为Eureka Client。
- Consumer服务通过从Eureka Server中抓取Provider地址完成远程调用
搭建父项目
-
IDEA创建一个空的项目(Empty Project)SpringCloudDemo
-
点开Project,选择自己的SDK。我这里用的是1.8,Project Language Level也改成8,点击OK
-
配置Maven
点击File->Setting->Maven,改好之后点击OK
我这里是自己安装的maven3.1,用的自己的配置(主要是设置了Mirros中心为阿里云)
<?xml version="1.0" encoding="UTF-8"?>
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd">
<pluginGroups>
</pluginGroups>
<proxies>
</proxies>
<servers>
</servers>
<mirrors>
<mirror>
<id>alimaven</id>
<mirrorOf>central</mirrorOf>
<name>aliyun maven</name>
<url>http://maven.aliyun.com/nexus/content/repositories/central/</url>
</mirror>
<mirror>
<id>alimaven</id>
<name>aliyun maven</name>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
<mirrorOf>central</mirrorOf>
</mirror>
<!-- 中央仓库在中国的镜像 -->
<mirror>
<id>maven.net.cn</id>
<name>oneof the central mirrors in china</name>
<url>http://maven.net.cn/content/groups/public/</url>
<mirrorOf>central</mirrorOf>
</mirror>
</mirrors>
<profiles>
</profiles>
</settings>
-
创建父工程 spring-cloud-parent
File->new->Module->Maven->next
创建后删除spring cloud parent下的src目录,保留pom.xml
搭建Provider和Consumer项目
-
右击spring cloud parent项目,选择new module新建一个模块eureka-provider
-
同样的方法创建消费方项目 eureka-consumer
-
给spring-cloud-parent父项目引入springboot2.1
先复制parent标签加入进去保存,再加入属性标签进去保存。而且开启maven auto import(你改动之后右下角会提示你)
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.0.RELEASE</version> <relativePath/> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties>
-
给provider和consumer项目的依赖引入springboot-web
修改项目的pom.xml,加入下面的依赖保存
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies>
-
给provider创建启动类 com.fpa.provider.ProviderApp
package com.fpa.provider; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class ProviderApp { public static void main(String[] args) { SpringApplication.run(ProviderApp.class,args); } }
-
创建商品类并生成构造方法、getter&setter(以后可以用lombok)@Data,这里不多引入
package com.fpa.provider.bean; public class Goods { private int id; private String title; private double price; public Goods() { } public Goods(int id, String title, double price) { this.id = id; this.title = title; this.price = price; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public double getPrice() { return price; } public void setPrice(double price) { this.price = price; } }
-
创建GoodsDao类
package com.fpa.provider.dao; import com.fpa.provider.bean.Goods; import org.springframework.stereotype.Repository; @Repository public class GoodsDao { public Goods findOne(int id){ return new Goods(1,"华为手机",5999); } }
-
创建service层
package com.fpa.provider.service; import com.fpa.provider.bean.Goods; public interface GoodsService { public Goods findOne(int id); }
-
创建service实现类serviceImpl.GoodsServiceImpl
package com.fpa.provider.service.serviceImpl; import com.fpa.provider.bean.Goods; import com.fpa.provider.dao.GoodsDao; import com.fpa.provider.service.GoodsService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service public class GoodsServiceImpl implements GoodsService { @Autowired private GoodsDao goodsDao; @Override public Goods findOne(int id) { return goodsDao.findOne(id); } }
-
创建controller.GoodsController
package com.fpa.provider.controller; import com.fpa.provider.bean.Goods; import com.fpa.provider.service.GoodsService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/goods") public class GoodsController { @Autowired private GoodsService goodsService; @GetMapping("/{id}") public Goods findOne(@PathVariable("id") int id){ return goodsService.findOne(id); } }
-
创建provider项目的配置文件
右击provider项目下的resources文件夹,创建三个文件
- application.yml
spring: profiles: active: dev
- application-dev.yml 用来开发环境使用的配置文件
server: port: 8000
- application-prod.yml 用来生产环境使用的配置文件
server: port: 8000
-
启动provider并使用postman访问下这个接口看是否正常
接口正常
-
给consumer项目也创建启动类和一个接口,并复制bean实体类过去。
package com.fpa.consumer; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class ConsumerApp { public static void main(String[] args) { SpringApplication.run(ConsumerApp.class,args); } }
```java
package com.fpa.consumer.controller;
import com.fpa.consumer.bean.Goods;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/goods")
public class OrderController {
@GetMapping("/findGoodsById/{id}")
public Goods findOne(@PathVariable("id") int id){
//需要远程调用provider项目提供的接口查询商品,这里先直接返回,等下改代码
return new Goods(2,"消费者",500);
}
}
```
-
启动consumer项目并测试接口是否正常可用
启动方法相同,不再赘述。测试发现接口正常。
使用RestTemplate远程调用
-
在consumer项目中配置RestTemplate Bean
package com.fpa.consumer.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.client.RestTemplate; /** * 配置RestTemplate Bean */ @Configuration public class RestTemplateConfig { @Bean public RestTemplate restTemplate(){ return new RestTemplate(); } }
-
修改consumer项目中的接口使用restTemplate进行远程restful调用,重启项目调用测试
package com.fpa.consumer.controller; import com.fpa.consumer.bean.Goods; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; 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; @RestController @RequestMapping("/goods") public class OrderController { @Autowired private RestTemplate restTemplate; @GetMapping("/findGoodsById/{id}") public Goods findOne(@PathVariable("id") int id){ /** * 1 配置 RestTemplate 的bean * 2 在需要的地方注入 restTemplate * 3 在接口内使用restTemplate进行远程调用 * restTemplate有提供一些方法,例如get post put delete等常用的,自己点开看 */ String url = "http://127.0.0.1:8000/goods/1"; Goods goods = this.restTemplate.getForObject(url,Goods.class); return goods; } }
搭建Eureka服务 eureka-server
-
创建eureka-server模块,和之前一样的创建。不演示了
-
给parent父项目pom.xml引入spring cloud
版本对应信息
https://start.spring.io/actuator/info
<!-- 配置-->
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<!-- spring cloud版本-->
<spring-cloud.version>Greenwich.RELEASE</spring-cloud.version>
</properties>
<!-- 引入spring cloud-->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org-springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
-
给eureka-server项目引入eureka-server依赖
<!-- eureka-server --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> <version>2.1.0.RELEASE</version> </dependency>
-
创建EurekaApp并加入@EnableEurekaServer注解
package com.fpa.eureka; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer; @SpringBootApplication @EnableEurekaServer public class EurekaApp { public static void main(String[] args) { SpringApplication.run(EurekaApp.class,args); } }
-
eureka配置文件
直接去复制provider项目下的三个配置文件,然后改掉dev和prod这两个,下面的放进去
server: port: 8761 #eureka配置 # eureka一共有4部分配置 #1. dashboard : eureka的web控制台配置 #2. server : eureka的服务端配置 #3. client : eureka的客户端配置 #4. instance : eureka的实例配置 spring: application: # 这个应用的名字 name: eureka-server-app eureka: instance: hostname: localhost # 主机名 #appname: eureka-server #这个app的名字(注册自己的话就会显示这个名字作为实例的app名,如果写了spring应用名也可以,两个都写优先这个) client: service-url: defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka # eureka服务端地址,将来客户端使用该地址和eureka进行通信 # 为了进eureka的时候能看到注册上的服务,我还是注销掉了这两个,让他把自己注册上去 # register-with-eureka: false # 是否将自己的路径 注册到eureka上。eureka server 不需要的,eureka provider client 需要 # fetch-registry: false # 是否需要从eureka中抓取路径。eureka server 不需要的,eureka consumer client 需要
-
进入 http://localhost:8761查看eureka控制台
provider使用eureka注册服务
-
加入 eureka-client依赖
<!-- eureka-client --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency>
-
给启动类加上@EnableEurekaClient注解
package com.fpa.provider; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; @SpringBootApplication @EnableEurekaClient public class ProviderApp { public static void main(String[] args) { SpringApplication.run(ProviderApp.class,args); } }
-
给provider加上应用名(直接改application.yml)
spring:
profiles:
active: dev
application:
name: provider-app
-
给provider修改配置
修改dev和prod
eureka:
instance:
hostname: localhost #自己的主机地址,非本地环路测试填自己IP
client:
service-url:
defaultZone: http://localhost:8761/eureka #eureka服务器地址
- 启动provider即可看到已经注册上了
consumer使用eureka获得服务提供者地址并调用
加入 eureka-client依赖
<!-- eureka-client -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
给启动类加上@EnableEurekaClient和@EnableDiscoveryClient注解(新版本可以不写,本版本就是新版本,但我们还是写下)
package com.fpa.consumer;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication
@EnableEurekaClient
@EnableDiscoveryClient
public class ConsumerApp {
public static void main(String[] args) {
SpringApplication.run(ConsumerApp.class,args);
}
}
- 给consumer加上应用名(直接改application.yml)
spring:
profiles:
active: dev
application:
name: consumer-app
-
去改造接口OrderController
注入discoveryClient,使用该bean根据服务ID获取节点,拼成url,使用restTemplate访问
package com.fpa.consumer.controller; import com.fpa.consumer.bean.Goods; 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.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; import java.net.URI; import java.util.List; @RestController @RequestMapping("/goods") public class OrderController { @Autowired private RestTemplate restTemplate; @Autowired private DiscoveryClient discoveryClient; @GetMapping("/findGoodsById/{id}") public Goods findOne(@PathVariable("id") int id){ /** * 1 配置 RestTemplate 的bean * 2 在需要的地方注入 restTemplate * 3 在接口内使用restTemplate进行远程调用 * restTemplate有提供一些方法,例如get post put delete等常用的,自己点开看 */ // String url = "http://127.0.0.1:8000/goods/1"; //serviceId就是那个服务的应用名,不区分大小写 List<ServiceInstance> instanceList = discoveryClient.getInstances("provider-app"); if (instanceList != null && instanceList.size() > 0){ ServiceInstance serviceInstance = instanceList.get(0); // String host = serviceInstance.getHost(); // int port = serviceInstance.getPort(); URI uri = serviceInstance.getUri(); String url = uri + "/goods/1"; Goods goods = this.restTemplate.getForObject(url,Goods.class); return goods; } return null; } }
-
启动consumer并测试
默认会把自己也注册上去,可以通过配置取消注册自己,前面提到了。
配置讲解
驼峰法和短杠法是都可以的,一般用短杠法
eureka:
instance:
hostname: localhost #主机名
ip-address: 127.0.0.1
prefer-ip-address: true #是否将自己的ip注册到eureka中,默认false 注册主机名ip-address:#设置当前实例p
#instance-id: #修改instance-id 显示
lease-renewal-interval-in-seconds : 30 #每一次eureka client向 eureka server发送心跳的时间间隔
lease-expiration-duration-in-seconds : 90 #如果90秒内eureka server没有收到eureka client的心跳包,则剔除该服务
server:
enable-self-preservation: false #是否开启自我保护,也就是当超过时间没有收到心跳的时候先不要移除,因为可能是网络波动造成的 开发的时候关闭,生产环境为了高可用打开
eviction-interval-timer-in-ms: 60000 #清理间隔 定期扫描看是不是挂了的,就剔除 默认是60*1000也就是1分钟
client:
service-url:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka # eureka服务端地址,将来客户端使用该地址和eureka进行通信
# register-with-eureka: false # 是否将自己的路径 注册到eureka上。eureka server 不需要的,eureka provider client 需要
# fetch-registry: false # 是否需要从eureka中抓取路径。eureka server 不需要的,eureka consumer client 需要
Eureka高可用
之前的eureka服务只启动了一个,如果因为什么愿意挂了,那么没办法提供服务注册、发现功能了,因为需要做成高可用。启动若干个eureka-server
演示启动两个eureka-server
-
创建两个项目,分别为eureka-server1和eureka-server2
创建项目步骤和前面的一样,然后把pom.xml里的依赖、resources里的配置文件、启动类从之前的eureka-server项目复制过来,启动类一个叫Eureka1App一个叫Eureka2App,不演示了
-
重点:配置集群
第一个从8761端口启动,第二个从8762端口启动,互相配置server-url为对方的,然后应用名用同一个就可以了
8761的
application.yml
spring:
profiles:
active: dev
application:
name: eureka-server-app
application-dev.yml
server:
port: 8761
eureka:
instance:
hostname: localhost #主机名
ip-address: 127.0.0.1
server:
# enable-self-preservation: false #是否开启自我保护,也就是当超过时间没有收到心跳的时候先不要移除,因为可能是网络波动造成的 开发的时候关闭,生产环境为了高可用打开
eviction-interval-timer-in-ms: 60000 #清理间隔 定期扫描看是不是挂了的,就剔除 默认是60*1000也就是1分钟
client:
service-url:
defaultZone: http://localhost:8762/eureka # eureka服务端地址,将来客户端使用该地址和eureka进行通信
8762的
application.yml
spring:
profiles:
active: dev
application:
name: eureka-server-app
application-dev.yml
server:
port: 8762
eureka:
instance:
hostname: localhost #主机名
ip-address: 127.0.0.1
server:
# enable-self-preservation: false #是否开启自我保护,也就是当超过时间没有收到心跳的时候先不要移除,因为可能是网络波动造成的 开发的时候关闭,生产环境为了高可用打开
eviction-interval-timer-in-ms: 60000 #清理间隔 定期扫描看是不是挂了的,就剔除 默认是60*1000也就是1分钟
client:
service-url:
defaultZone: http://localhost:8761/eureka # eureka服务端地址,将来客户端使用该地址和eureka进行通信
两个都启动
- 修改服务提供方、消费方eureka配置,把这两个节点都配上去,并重启provider和consumer,测试接口正常
测试下接口是不是正常,接口正常
总结
个人觉得这个eureka还不如去用nacos
本文来自博客园,作者:HumorChen99,转载请注明原文链接:https://www.cnblogs.com/HumorChen/p/18039572