目录
前言
服务治理
在传统的RPC远程调用框架中, 管理每个服务与服务之间依赖关系比较复杂,管理比较复杂,所以需要使用服务治理,管理服务与服务之间依赖关系,可以实现服务调用、负载均衡、容错等,实现服务发现与注册。
服务注册与发现
首先在分布式微服务系统中,有一个注册中心,使用CS的设计架构,当服务端的微服务启动时,会把自己当前服务的相关信息(服务名,服务所在主机地址等)注册到注册中心。而消费端的微服务会根据所需的服务端的微服务别名到注册中心获取实际的服务通讯地址,调用远程服务(RPC:dubbo+zk或eureka)(DNS协议:k8s + coredns)。
注册中心
配置总工程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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>top.hcode</groupId>
<artifactId>springcloud</artifactId>
<version>1.0-SNAPSHOT</version>
<!--打包方式 pom-->
<packaging>pom</packaging>
<properties>
<project.bulid.sourceEncoding>UTF-8</project.bulid.sourceEncoding>
<maven.compiler.sourse>1.8</maven.compiler.sourse>
<maven.compiler.target>1.8</maven.compiler.target>
<junit.version>4.12</junit.version>
<lombok.version>1.16.10</lombok.version>
<log4j.version>1.2.17</log4j.version>
<mysql.version>5.1.47</mysql.version>
<mybatis.spring.boot.version>2.1.1</mybatis.spring.boot.version>
</properties>
<dependencyManagement>
<dependencies>
<!--Springcloud的依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!--spring cloud 阿里巴巴-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.2.0.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!--Springboot依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.2.5.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!--数据库-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.20</version>
</dependency>
<!--mybatis-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>${mybatis.spring.boot.version}</version>
</dependency>
<!--Springboot:启动器-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency>
<!--log4j日志-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>${log4j.version}</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.2.3</version>
</dependency>
<!--junit单元测试-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
</project>
Eureka
Eureka包含两个组件: Eureka Server和Eureka Client
- Eureka Server提供服务注册服务
各个微服务节点通过配置启动后,会在EurekaServer中进行注册, 这样EurekaServer中的服务注册表中将 会存储所有可用服务节点的信息,服务节点的信息可以在界面中直观看到。
- EurekaClient通过注册中心进行访问
Java客户端,用于简化Eureka Server的交互,客户端同时也具备一个内置的、 使用轮询(round-robin)负载算法的负载均衡器。在应用启动后,将会向Eureka Server发送心跳(默认周期为30秒)。如果Eureka Server在多个心跳周期内没有接收到某个节点的心跳,EurekaServer将 会从服务注册表中把这个服务节点移除(默认90秒)
Eureka Server的搭建
每个子项目第一步:创建maven子模块
- 配置该子模块的pom.xml依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
- 配置yml(单机)
注:eureka7001.com,eureka7002.com是本地配置127.0.0.1映射为这些域名,主要是为了区别,在服务器上可根据服务器更改
server:
port: 7001
eureka:
instance:
hostname: eureka7001.com #eureka服务端的实例名称
client:
register-with-eureka: false #是否向eureka注册中心注册自己
fetch-registry: false #false表示自己为注册中心
service-url: #监控页面
# 单机: defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
#集群(关联其它注册中心) defaultZone: http://eureka7002.com:7002/,.....
defaultZone: http://eureka7002.com:7002/eureka/
server:
enable-self-preservation: true # eureka的自我保护模式开启
eviction-interval-timer-in-ms: 30000 # 自我保护几秒
配置yml(集群):其它不变,修改注册空间添加其它注册中心地址
defaultZone: http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/,.....
- 在主启动类上添加开启Eureka注册中心的注解
@SpringBootApplication
@EnableEurekaServer //服务器启动类,可以接受别人的注册
public class EurekaServer_7001 {
public static void main(String[] args) {
SpringApplication.run(EurekaServer_7001.class,args);
}
}
Eureka Client的搭建
首先根据CS架构应该分为微服务提供者(provider)和微服务消费者(consumer),同时两者都得注册到Eureka注册中心,并且微服务提供者(服务端)可以进行集群扩建,配置均是端口不同即可(当然,在不同服务器就没这个限制,本地是为了端口不被占用)。
微服务提供端(服务端)
首先:同样也是得先创建子模块maven项目(Springboot也行,偷懒就不用手动创建启动类~)
注:不做实际业务的演示,只说明配置,集群的话相同配置,修改端口号即可(本地伪集群)
- 配置该模块的pom.xml依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</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-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
- 配置applaction.yml(直接集群版,单机版也就是注册空间只有一个的区别)
server:
port: 8001
spring:
application:
name: springcloud-provider-test #服务名
#eureka的配置,注册到注册中心
eureka:
client:
service-url:
defaultZone: http://eureka7002.com:7002/eureka/, http://eureka7001.com:7001/eureka/
# 集群必须为true
fetch-registry: true
instance:
instance-id: springcloud-provider-test-8001 #修改eureka上的监控的默认信息
prefer-ip-address: true #显示eureka界面的服务ip
lease-renewal-interval-in-seconds: 30 # 向eureka注册中心发送心跳的时间间隔
lease-expiration-duration-in-seconds: 90 # eureka注册中心收到最后一次心跳等待的时间上限,超过就清除该服务
- 配置主启动类的注解
//启动类
@SpringBootApplication
@EnableEurekaClient //注册到注册中心
@EnableDiscoveryClient //服务发现
public class TestProvider_8001 {
public static void main(String[] args) {
SpringApplication.run(TestProvider_8001.class, args);
}
}
微服务消费端(客户端)
- 在pom.xml导入依赖
<!--eureka client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</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-actuator</artifactId>
</dependency>
- 配置application.yml(集群)
server:
port: 80
eureka:
client:
register-with-eureka: false #不注册自己
service-url:
#到哪些注册中心发现服务
defaultZone: http://eureka7002.com:7002/eureka/,http://eureka7001.com:7001/eureka/
- 启动类
@SpringBootApplication
@EnableEurekaClient
public class DeptConsumer_80 {
public static void main(String[] args) {
SpringApplication.run(DeptConsumer_80.class,args);
}
}
服务调用
一般客户访问都是走消费端(consumer)的请求,然后消费端(consumer)要到注册中心根据客户请求URL路径所对应的controller层所指定的微服务,根据别名请求调用服务端(provider)的微服务执行相应请求(这里默认的负载均衡策略为轮询),返回结果到消费端,消费端再将结果封装返回到前端客户访问的页面。
消费端调用方式有两种:Ribbon和feign。
(一) Ribbon方式:
- 不用再导入依赖,spring-cloud-starter-netflix-eureka-client中已经包含了ribbon,当然要自己导入也行。
在consumer的maven工程的pom.xml加入
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
<dependency/>
- 加个配置类,将RestTemplate注入到spring容器中
@Configuration
public class ConfigBean {
@Bean
@LoadBalanced //设置实现负载均衡,Ribbon
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
//修改负载均衡的策略,这次选择已有的随机访问,下面不写的话,默认为轮询
//AvailabilityFilteringRule : 先过滤掉出现故障的服务器,对剩下的服务进行轮询
//RoundRobinRule 轮询 默认设置
//RandomRule 随机
//WeightedResponseTimeRule 权重
//RetryRule:先按照轮询获取,若获取服务失败,则在指定时间内进行重试
@Bean
public IRule myRule(){
return new RandomRule();
}
}
- 在controller层进行请求服务端的操作
服务名对应provider端所配置的服务名
@RestController
public class ConsumerController {
@Autowired
private RestTemplate restTemplate;
//Ribbon,地址应该是个变量,通过服务名来访问
private static final String REST_URL_PREFIX = "http://SPRINGCLOUD-PROVIDER-TEST";
@RequestMapping("/consumer/test")
public String test() {
return restTemplate.getForObject(REST_URL_PREFIX+"/test" String.class);
}
}
- 相对应的,在服务端的controller里面得有个相对应的路径请求方法
@RestController
public class ProviderController {
@RequestMapping("/test")
public String test() {
return "(●'◡'●),调用服务成功哦~";
}
}
(二)feign方式(接口):
- 在consumer端导入依赖
Netflix的feign已经不更新了,所以这次用的组件时springcloud的openfeign
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>2.2.2.RELEASE</version>
</dependency>
- 配置yml
ribbon: # openfeign默认一秒超时,需要重新设置超时时间
ReadTimeout: 5000
ConnectTimeout: 5000
- 在启动类上开启feign的注解
@EnableEurekaClient
@SpringBootApplication
@EnableFeignClients
public class DeptConsumer_80 {
public static void main(String[] args) {
SpringApplication.run(DeptConsumer_80.class,args);
}
}
- 在service层创建一个接口,新增注解@FeignClient,方法(参数)需同服务端controller层的路径请求方法一样。
@FeignClient(value = "springcloud-provider-test") //value填的就是provider端的服务别名
@Component //交给spring容器托管
public interface TestService {
@RequestMapping("/test")
public String test();
}
- 然后呢,在controller层测试(还是consumer端的哦)
@RestController
public class DeptConsumerController {
@Autowired
private TestService testService ;
@RequestMapping("/consumer/test")
public String test(){
return testService.test();
}
}
Zookeeper
先到官网下载安装zookeeper
- 下载zookeeper :https://zookeeper.apache.org/releases.html 解压zookeeper
- 运行/bin/zkServer.cmd ,初次运行会报错,没有zoo.cfg配置文件,直接复制zoo_sample.cfg即可;当然,在Linux上运行zookeeper最好。
微服务提供者(provider)
- 在pom.xml导入依赖,主要是偷懒,如果不导入整合包,一个个导入也行。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
<exclusions>
<exclusion>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--添加zk 3.4,9版本-->
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.9</version>
</dependency>
- 配置application.yml
server:
port: 8003
spring:
application:
name: cloud-provider-test
cloud:
zookeeper:
connect-string: localhost:2181
- 启动类上添上注解
@SpringBootApplication
@EnableDiscoveryClient
public class TestProvider8004 {
public static void main(String[] args) {
SpringApplication.run(TestProvider8004.class,args);
}
}
微服务消费者(consumer)
- 在pom.xml导入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
<exclusions>
<exclusion>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--添加zk 3.4,9版本-->
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.9</version>
</dependency>
- 配置yml
server:
port: 80
spring:
application: # 当前微服务的别名
name: cloud-consumer-text
cloud:
zookeeper:
connect-string: localhost:2181
- 主启动类
@SpringBootApplication
@EnableDiscoveryClient
public class TestConsumer80 {
public static void main(String[] args) {
SpringApplication.run(TestConsumer80.class,args);
}
}
- 增加配置类(通信)
@Configuration
public class ApplicationContextConfig {
@LoadBalanced //负载均衡ribbon
@Bean
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
服务调用
- HTTP:跟eureka一样,可以使用ribbon或者封装过的feign接口方式调用。(略)
- RPC:使用Dubbo框架。(感觉项目实现的步骤跟feign差不多~,都是接口调用)
- 在消费者端和提供者端的pom.xml中都导入依赖
<!-- Dubbo Spring Boot Starter -->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>2.7.3</version>
</dependency>
- 服务端的application.yml修改相关配置
server:
port: 8008
dubbo:
application: #当前微服务名字
name: provider-server
registry: #注册中心地址
address: zookeeper://127.0.0.1:2181
scan: #扫描指定包下服务
base-packages: top.hcode.service
service的实现类中添加相关注解,而TestService可以专门写个api的maven模块,这样利用导入,这里就直接写在提供者里面了。
import org.apache.dubbo.config.annotation.Service;
import org.springframework.stereotype.Component;
@Service //将服务发布出去,注意应该是dubbo包下的
@Component //放在容器中
public class TestServiceImpl implements TestService{
@Override
public String test() {
return "(●'◡'●)";
}
}
项目启动类添加注解@EnableDubboConfiguration
@SpringBootApplication
@EnableDubboConfiguration
public class DubboTest8008 {
public static void main(String[] args) {
SpringApplication.run(DubboTest8008.class, args);
}
}
这样的话,dubbo就会扫描指定的包下带有@component注解的服务,将它发布在指定的注册中心中。
- 消费者端的application.yml同样也要添加dubbo的配置
<!-- Dubbo Spring Boot Starter -->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>2.7.3</version>
</dependency>
配置application.yml
server:
port: 80
dubbo:
application: #当前应用名字
name: consumer-server
registry: #注册中心地址
address: zookeeper://127.0.0.1:2181
直接将服务端的TestService接口拿过来,路径必须保证正确,即和服务提供者相同,然后在添加消费端(如果已经将服务接口都写在另一个maven里面就可以直接在pom导入即可),这里就需要自己写。
@Service //注入到容器中
public class TestController {
@Reference //远程引用指定的服务,他会按照全类名进行匹配,看谁给注册中心注册了这个全类名
private TestService testService;
@GetMapping("/test")
public void test(){ //直接调用~
testService.test();
}
}
项目启动类添加注解@EnableDubboConfiguration
@SpringBootApplication
@EnableDubboConfiguration
public class DubboTest80 {
public static void main(String[] args) {
SpringApplication.run(DubboTest80.class, args);
}
}
nacos
Nacos就是注册中心+配置中心的组合 ,拥有注册中心的服务注册与发现,springcloud config的远程配置功能,还有springcloud bus消息总线的动态更新服务配置的功能,而且做到了不用像bus一样需要每次都发请求提醒微服务配置已经更新,真正实现了动态更新配置。
下载后,解压安装包,windows直接运行bin目录下的startup.cmd,Linux就运行startup.sh,docker就简单多了。启动后,运行成功后直接访问http://localhost:8848/nacos,账号密码都是nacos,登录即可监控服务。
服务提供者(provider)
- 导入pom依赖
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</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-actuator</artifactId>
</dependency>
- 配置yml
server:
port: 8010
spring:
application:
name: nacos-test-provider
cloud:
nacos:
discovery:
server-addr: localhost:8848 #配置Nacos地址
# 暴露监控
management:
endpoints:
web:
exposure:
include: '*'
- 主启动类添加注解
@EnableDiscoveryClient
@SpringBootApplication
public class TestNacos8010{
public static void main(String[] args) {
SpringApplication.run(TestNacos8010.class,args);
}
}
- controller和service就不写了,跟之前一样的。
服务消费者(consumer)
- 导入pom依赖
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</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-actuator</artifactId>
</dependency>
- 配置yml
server:
port: 80
spring:
application:
name: nacos-test-consumer
cloud:
nacos:
discovery:
server-addr: localhost:8848
# 消费者将要去访问的微服务名称(注册成功进入nacos的微服务提供者)
service-url:
nacos-user-service: http://nacos-test-provider
- 主启动类添加注解
@EnableDiscoveryClient
@SpringBootApplication
public class TestNacos80
{
public static void main(String[] args)
{
SpringApplication.run(TestNacos80.class,args);
}
}
- 添加服务调用的template的配置类
@Configuration
public class ConfigBean {
@Bean
@LoadBalanced //设置实现负载均衡,Ribbon
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
}
服务调用
肯定不是因我懒 不写了,反正都差不多,也就feign和ribbon那种方式。注册成功后,可以登录nacos查看到服务的~,至于自动化配置下次再说。