Spring Cloud Eureka是Spring Cloud Netflix 微服务套件中的一部分,它基于Netflix Eureka做了二次封装,主要负责完成微服务架构中的服务治理功能。以下是它的核心内容:
- 构建服务注册中心
- 服务注册与发现
- Eureka 的基础架构
- Eureka 的服务治理机制
- Eureka 的配置
服务治理
服务治理实现了各个微服务实例的自动化注册与发现。简单理解,比如说你想买衣服,你就得去商场,商场里面有很多的商店可以供你选择,然而这些商店的衣服又不是凭空来的,而是由不同品牌的厂家送来的,你是通过商场来找到了你想要品牌的衣服。类比到我们的服务治理,我们的商场就相当于服务注册中心,不同品牌的厂家相当于服务提供者,而我们顾客就相当于服务调用者。
服务注册:
位于不同进程上的服务提供者在进程启动后将自己的服务注册到注册中心,注册中心就会将它们列成一个服务清单,并以心跳的方式监测其是否可用,若不可用则移除。
服务发现:
服务调用者向服务注册中心咨询服务,获取上面说的服务清单,查看是否有自己要调用的服务,若有的话,看在哪些节点上都有这些服务,可以采用轮询或者其他的方法进行访问(后面会讲到的负载均衡)。
Eureka 服务注册与发现
Eureka 包含两个组件:Eureka Server和Eureka Client。
Eureka 服务端 Eureka Server,也就是我们上面提到的服务注册中心,它支持高可用配置。简单理解就是可以有多个服务注册中心,再形象点就是可以有多个商场,作用就是为了商场A今天要是不开门的话,我可以去商场B去买衣服。
Eureka 客户端 Eureka Client主要处理服务的注册与发现。形象点理解就是,不同品牌的厂商要不断的给商场送货,并且得按时送货,不然商场就认为你们家倒闭了,将会在它的厂家供应商清单里面将你移除掉。官方的话是这样说的:在应用程序运行时,Eureka 客户端向注册中心注册自身提供的服务并周期性地发送心跳来更新它的服务租约。可以自己理解,我的理解大概就是这样,可以好记点。下面来个简单的栗子,实战体会一下Eureka !
搭建服务注册中心
- 首先,创建一个空的父工程,命名为springcloud,然后创建一个Module, 命名为springcloud-eureka-7001,"-7001"是端口号,为了清楚的看到是哪个端口,引入pom依赖:
<parent>
<artifactId>springcloud</artifactId>
<groupId>org.example</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>springcloud-eureka-7001</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka-server</artifactId>
<version>1.4.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<version>2.2.6.RELEASE</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.5</version>
</dependency>
</dependencies>
- 给启动类上添加@EnableEurekaServer注解,使它成为一个服务注册中心:
@SpringBootApplication
@EnableEurekaServer
public class EurekaServer_7001 {
public static void main(String[] args) {
SpringApplication.run(EurekaServer_7001.class,args);
}
}
- 在resources下面建个application.yml配置文件
server:
port: 7001
eureka:
instance:
hostname: eureka7001.com
#hostname: localhost
client:
register-with-eureka: false
fetch-registry: false
service-url:
#defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
来来来,解释一波:
- 端口号:7001;
- hostname : eureka7001.com 是主机名,这里就是localhost,其实是给localhost起了很多个名字,模拟使用不同的主机;
- register-with-eureka: false 是不让自己注册自己;
- fetch-registry: false:因为注册中心的职责是维护服务实例,所以不需要检索服务,因此设置为false;
- service-url: 注册中心的地址。
完成上述工作后,访问http://eureka7001.com:7001/,如下图所示:
创建一个实例
创建Module,命名为springcloud-api,自己建个包,然后在包里创建pojo包,在pojo里建实体类Dept,建立这个实例的目的是为了后面让服务提供者可以提供一些具体的服务。
- 引入pom依赖
<parent>
<artifactId>springcloud</artifactId>
<groupId>org.example</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>springcloud-api</artifactId>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
<version>1.4.7.RELEASE</version>
</dependency>
</dependencies>
- 建Dept类
@Data
@Accessors(chain = true)
@NoArgsConstructor
public class Dept implements Serializable {
private long deptno;
private String dname;
private String db_source;
public Dept(String dname){
this.dname = dname;
}
}
这里引入了lombok依赖,因此省略了setter、getter等方法,读者可根据这个Dept类建立数据库。
注册服务提供者。
创建Module,命名为springcloud-provider-dept-8001。
- 导入pom依赖
<parent>
<artifactId>springcloud</artifactId>
<groupId>org.example</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>springcloud-provider-dept-8001</artifactId>
<dependencies>
<dependency>
<groupId>org.example</groupId>
<artifactId>springcloud-api</artifactId>
<version>1.0-SNAPSHOT</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-web</artifactId>
<version>2.2.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
<version>2.2.6.RELEASE</version>
</dependency>
#热部署
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<version>2.2.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
<version>1.4.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
<version>2.2.6.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.google.code.gson/gson -->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.5</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
这里有些依赖不用引用,是因为后面还做了其他功能,所以引了,我是直接拷过来的。
先附一张图,方面学习下面的内容
- mapper包下面创建DeptMapper接口
@Mapper
@Repository
public interface DeptMapper {
//添加部门
public boolean addDept(Dept dept);
//根据Id查询部门
public Dept queryById(long id);
//查询所有部门
public List<Dept> queryAll();
}
- service包下创建DeptService接口以及DeptServiceImpl实现类
public interface DeptService {
public boolean addDept(Dept dept);
public Dept queryById(long id);
public List<Dept> queryAll();
}
@Service
public class DeptServiceImpl implements DeptService {
//service掉dao层,这里即mapper层
@Autowired
DeptMapper deptMapper;
@Override
public boolean addDept(Dept dept) {
return deptMapper.addDept(dept);
}
@Override
public Dept queryById(long id) {
return deptMapper.queryById(id);
}
@Override
public List<Dept> queryAll() {
return deptMapper.queryAll();
}
}
- resources包下创建DeptMapper.xml文件
<mapper namespace="com.lei.springcloud.mapper.DeptMapper">
<insert id="addDept" parameterType="dept">
insert into db01.dept (dname, db_source)
values(#{dname},DATABASE())
</insert>
<select id="queryById" resultType="dept" parameterType="long">
select *from db01.dept where deptno = #{deptno}
</select>
<select id="queryAll" resultType="dept">
select *from db01.dept
</select>
</mapper>
- resources包下创建application.yml配置文件
server:
port: 8001
mybatis:
type-aliases-package: com.lei.springcloud.pojo
mapper-locations: classpath:mybatis/mapper/*.xml
config-location: classpath:mybatis/mybatis-config.xml
spring:
application:
name: springcloud-provider-dept
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/db01?useUnicode=ture&characterEncoding=utf-8
username:
password:
eureka:
client:
service-url:
defaultZone: http://localhost:7001/eureka
instance:
instance-id: springcloud-provide-dept8001
info:
app.name: leiger.springcloud
company.name: blog.leiger.com
- controller包下创建DeptController
@RestController
public class DeptController {
//注入service
@Autowired
private DeptService deptService;
@PostMapping("/dept/add")
public boolean addDept(Dept dept){
return deptService.addDept(dept);
}
@GetMapping("/dept/get/{id}")
public Dept get(@PathVariable("id")long id){
return deptService.queryById(id);
}
@GetMapping("/dept/list")
public List<Dept> queryAll(){
return deptService.queryAll();
}
}
- 主启动类上添加@EnableDiscoveryClient注解,激活Eureka中的DiscoveryClient实现。
@SpringBootApplication
@EnableDiscoveryClient
public class DeptProvider_8001 {
public static void main(String[] args) {
SpringApplication.run(DeptProvider_8001.class,args);
}
}
完成上面代码之后,可访问http://eureka7001.com:7001/,如下图所示:
通过访问http://localhost:8001/dept/get/1,直接向该服务发起请求,得到:
至此,服务提供者已经将自身服务注册到注册中心!喝杯咖啡压压惊,接着再战!商场有了,厂家也把衣服送到了,接下来就是要逛商场买衣服了,下面就是服务消费者登场。
服务发现与消费
还是一样,先附张图
创建Module,命名为springcloud-consumer-dept-80,
- 导入依赖
<parent>
<artifactId>springcloud</artifactId>
<groupId>org.example</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>springcloud-consumer-dept-80</artifactId>
<dependencies>
<dependency>
<groupId>org.example</groupId>
<artifactId>springcloud-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.2.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<version>2.2.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-commons</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
<version>1.4.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
<version>1.4.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.5</version>
</dependency>
</dependencies>
- config包下创建ConfigBean
@Configuration
public class ConfigBean {
@Bean
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
- controller包下创建DeptConsumerController
@RestController
public class DeptConsumerController {
@Autowired
private RestTemplate restTemplate;
private static final String REST_URL_PREFIX = "http://localhost:8001/";
@RequestMapping("/consumer/dept/add")
public boolean add(Dept dept){
return restTemplate.postForObject(REST_URL_PREFIX+"dept/add",dept,boolean.class);
}
@RequestMapping("/consumer/dept/get/{id}")
public Dept getById(@PathVariable("id")long id){
return restTemplate.getForObject(REST_URL_PREFIX+"dept/get/"+id,Dept.class);
}
@RequestMapping("/consumer/dept/list")
public List<Dept> getAll(){
return restTemplate.getForObject(REST_URL_PREFIX+"dept/list",List.class);
}
}
- resources下创建application.yml
server:
port: 80
eureka:
client:
register-with-eureka: false
fetch-registry: true
service-url:
defaultZone: http://eureka7001.com:7001/eureka/
- 主启动类添加@EnableEurekaClient注解,让注册中心发现并扫描该服务。
@SpringBootApplication
@EnableEurekaClient
public class DeptConsumer_80 {
public static void main(String[] args) {
SpringApplication.run(DeptConsumer_80.class,args);
}
}
完成上面代码之后可访问http://localhost/consumer/dept/get/1,如下图所示:
至此,一个基本的框架已经搭好了,服务注册中心,服务提供者,服务消费者,这只是个基础,其他的组件功能以及这篇文章中的一些细节问题,以及源码的分析将会在后续的文章中跟大家一一分享,敬请期待,文章有什么错误的地方请指正,谢谢大家!