springCloud微服务基本使用

一、微服务简介

        微服务最重要的两点是服务发现和负债均衡,针对这两点springcloud分别提供了Eureka和Ribbon,其中Eureka是springCloud Netflix微服务套件的一部分,它基于Netflix Eureka做了二次封装,主要负责完成微服务架构中的服务治理功能。spring Cloud Ribbon是一个基于HTTP和TCP客户端负载均衡工具,它基于Netflix Ribbon实现。

        Eureka采用的是典型的C-S架构,在Eureka架构中有3个重要角色,

1:Eureka Server (注册中心服务端)

2:Service Provider(服务提供者客户端)

3:Service Consumer(服务消费者客户端)

对于eureka注册中心来说,服务消费者和服务提供者都是客户端

二、Eureka服务中心搭建

1.创建springboot工程添加如下依赖:


	<properties>
		<spring-cloud.version>Greenwich.SR3</spring-cloud.version>
	</properties>
	<dependencies>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
		</dependency>
	</dependencies>

	<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主要添加spring-cloud-starter-netflix-eureka-server依赖,这里springCloud的版本号为<spring-cloud.version>Greenwich.SR3</spring-cloud.version>,当锁定了springCloud的版本后就锁定了eureka的版本,所以这里不需要写eureka的版本号。而springboot和springCloud是有对应关系的,springCloud版本不可随便给,官网已经给出了对应的关系,如下图:

2.application.yml添加Eureka配置

如下

server:
  port: 8084
eureka:
  client:
    service-url:
      defaultZone: http://localhost:8084/eureka/
    register-with-eureka: false #单机版建议设置为false,设置false的目的是防止自己注册自己,集群版采用默认的true
    fetch-registry: false  #单机版建议设置为false,设置false的目的是防止自己发现自己,集群版采用默认的true

因为在默认设置下,eureka服务中心会将自己作为客户端来注册它自己,所以单机版需要将register-with-eureka设置为false,而在集群中由于需要相互注册,所以用true。

3.创建引导类添加@EnableEurekaServer

@SpringBootApplication
@EnableEurekaServer
public class Application {

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

}

@EnableEurekaServer注解是用来开启开启eureka服务

4.启动工程,浏览器输入ip端口号

看到如下界面

可以看到Instaances currently registered with Eureka为No instances available,即无微服务注册到进来。

三、搭建服务提供者

1.创建工程

pom.xml文件添加如下:

<properties>
	<java.version>1.8</java.version>
	<spring-cloud.version>Greenwich.SR3</spring-cloud.version>
</properties>

<dependencies>
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-web</artifactId>
	</dependency>
	<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-test</artifactId>
		<scope>test</scope>
	</dependency>
	</dependencies>

<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>

对于spring-cloud-starter-netflix-eureka-client依赖和eureka注册中心一样也要指定springCloud版本。

2.application.yml添加配置,指定注册中心地址

server:
  port: 9001 
eureka:
  client:
    serviceUrl:
     defaultZone: http://localhost:8084/eureka/  #eureka注册中心地址
spring:
  application:
    name: helloWorld-course-service #应用名

3.创建引导类添加@EnableEurekaClient注解

@SpringBootApplication
@EnableEurekaClient
public class Application {

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

}

@EnableEurekaClient注解用于注册服务

4.启动工程

工程启动后,进入注册中心页面,结果如下:

可以看到应用名就是配置文件里的应用名,status中up(1),如果是集群就是不是1了。

4.编写对外接口

@RestController("/helloWorld")
public class HelloWorldController {
	private  final Logger logger = LoggerFactory.getLogger(this.getClass());
	
	@RequestMapping("/getId/{id}")
    public String getId(@PathVariable("id") Integer id){
		logger.info("id = " + id);
        return "helloWorld";
    }
}

四、搭建服务消费者

对于服务消费者的搭建和服务提供者一样需要制定注册中心地址

1.编写controller调用远程服务

微服务之间的远程调用有3中方式,分别是:RestTemplate、DiscoveryClient、Feign

1.RestTemplate方式调用

对于RestTemplate方式有两种

第一种:

@RestController
@RequestMapping("/consumer")
public class HelloWorldController {

	private RestTemplate restTemplate = new RestTemplate();
	
	@RequestMapping("/getHelloWorld")
    public String getHelloWorld(){
 
        String result = restTemplate.getForObject("http://localhost:9001/helloWorld/getId/100", String.class);
        return result;
    }
}

RestTemplate是Spring提供的用于模拟浏览器发送http请求的客户端,RestTemplate提供了多种便捷访问远程Http服务的方法,能够大大提高客户端的编写效率。调用RestTemplate的默认构造函数,RestTemplate对象在底层通过使用java.net包下的实现创建HTTP 请求,可以通过使用ClientHttpRequestFactory指定不同的HTTP请求方式。

这种方式简单粗暴,这种方式有弊端,最直接的弊端是,如果服务提供者是一个集群,我们永远只能调用集群中的某一个。没法实现负载均衡。而且这种方式根本没利用Eureka,也就是不使用Eureka也可以使用这种方式。

第二种:

使用@LoadBalanced注解

第一步在restTemplate模板对象上加@LoadBalanced注解

@Configuration
public class ApplicationConfig {

	@Bean
    @LoadBalanced  //使用负载均衡器Ribbon
    public RestTemplate getRestTemplate(){
        return new RestTemplate();
    }
}

第二步:使用应用名调用

@RestController
@RequestMapping("/consumer")
public class HelloWorldController {

	@Autowired
	private RestTemplate restTemplate;
	
	@RequestMapping("/getHelloWorld")
    public String getHelloWorld(){
        String result = restTemplate.getForObject("http://HELLOWORLD-PROVIDER/helloWorld/getId/200", String.class);
        return result;
    }
}

这种方式不是以ip+port来调用,而是应用名,这种方式发送的请求都会被ribbon拦截,ribbon从eureka注册中心获取服务列表,然后采用均衡策略进行访问。

2.使用Feign方式调用

Feign 是一种声明式、模板化的 HTTP 客户端,在 Spring Cloud 中使用 Feign,可以做到使用 HTTP请求远程服务时能与调用本地方法一样的编码体验,开发者完全感知不到这是远程方法,更感知不到这是个 HTTP 请求。

第一步:

添加spring-cloud-starter-openfeign依赖:

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

feign无需指定version,因为springclould中已经指定了版本

第二步:

添加@EnableFeignClients注解启动feign

@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
public class Application {
	public static void main(String[] args) {
		SpringApplication.run(Application.class, args);
	}
}

第三步:

定义远程服务接口

@FeignClient("HELLOWORLD-PROVIDER")
public interface IService {

	@RequestMapping("/helloWorld/getId/{id}")
	public String getHelloWorld(@PathVariable("id") Integer id);
}

第四步:

实现当前controller层:

@RestController
@RequestMapping("/consumer")
public class HelloWorldController {
	
	@Autowired
	private IService service;
	
	@RequestMapping("/getHelloWorld")
    public String getHelloWorld(){
		String result = service.getHelloWorld(500);
        return result;
    }
}

启动注册中心,提供者,消费者,浏览器输入http://localhost:9002/consumer/getHelloWorld结果如下

helloWorld500

对于@FeignClient注册,应用名不区分大小写

五、使用nacos作为注册中心

1.依赖版本配置

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.12.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example.demo</groupId>
    <artifactId>springcloud-demo</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>pom</packaging>
    <modules>
        <module>nacos-provider</module>
        <module>nacos-consumer</module>
    </modules>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <spring-cloud.version>Hoxton.SR12</spring-cloud.version>
        <lombok.version>1.18.16</lombok.version>
    </properties>

    <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>
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>2.2.9.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>${lombok.version}</version>
            </dependency>
            <dependency>
                <groupId>io.springfox</groupId>
                <artifactId>springfox-swagger2</artifactId>
                <version>2.9.2</version>
            </dependency>
            <dependency>
                <groupId>io.springfox</groupId>
                <artifactId>springfox-swagger-ui</artifactId>
                <version>2.9.2</version>
            </dependency>
        </dependencies>
    </dependencyManagement>

</project>

2.yml配置

spring:
  application:
    name: nacos-consumer-demo
  cloud:
    nacos:
      config:
        server-addr: 127.0.0.1:8848
        username: nacos
        password: p5dS2kvnG$AcVMpZ
        namespace: 64742345-a882-4954-8604-9616242cd6f9
        group: DEFAULT_GROUP
        prefix: nacos-consumer
        file-extension: yml
      discovery:
        server-addr: 127.0.0.1:8848
        username: nacos
        password: p5dS2kvnG$AcVMpZ
        namespace: 64742345-a882-4954-8604-9616242cd6f9
        group: DEFAULT_GROUP

spring.cloud.nacos.discovery为注册中心配置,用于服务发现和注册

spring.cloud.nacos.config为nacos配置中心,nacos配置中心的使用可以参考官方平台什么是 Nacos

3.注解配置

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

@EnableDiscoveryClient,才能进行服务注册

@EnableFeignClients,开启feign调用

六、服务熔断

当消费者调用生产者接口出现报错或超时,可能需要进行对应的处理机制

1.启用服务熔断

以feign调用为例,开启服务熔断配置如下

feign:
  hystrix:
    #为false时  @FeignClient属性fallback不生效,true为开启服务降级
    enabled: true

开启后如果未实现服务熔断降级处理逻辑会报错

2.实现服务熔断

@FeignClient(value = "nacos-provider-demo", path = "/nacos-provider",
//        fallback = RemoteProviderServiceFallbackImpl.class,
        fallbackFactory = RemoteProviderServiceFallbackFactoryImpl.class)
public interface IRemoteProviderService {

    /**
     * test
     * @param arg
     * @return
     */
    @GetMapping("/provider/test/{arg}")
    String test(@PathVariable(value = "arg")String arg);

    /**
     * test
     * @param arg
     * @return
     */
    @GetMapping("/provider/test/error/{arg}")
    String testError(@PathVariable(value = "arg")String arg);

    /**
     * testTimeout
     * @param arg
     * @return
     */
    @GetMapping("/provider/test/timeout/{arg}")
    String testTimeout(@PathVariable(value = "arg") Long arg);
}

@FeignClient(value = "nacos-provider-demo", path = "/nacos-provider",
//        fallback = RemoteProviderServiceFallbackImpl.class,
        fallbackFactory = RemoteProviderServiceFallbackFactoryImpl.class)

注解有两个属性,fallback或fallbackFactory(两者选其一),配置上熔断处理的类即可

fallback中的类为IRemoteProviderService 的实现类如下:

@Slf4j
@Component
public class RemoteProviderServiceFallbackImpl implements IRemoteProviderService {
    @Override
    public String test(String arg) {
        log.error("调用服务失败,入参{}", arg);
        return "test调用失败";
    }

    @Override
    public String testError(String arg) {
        log.error("调用服务失败,入参{}", arg);
        return "testError调用失败";
    }

    @Override
    public String testTimeout(Long arg) {
        log.error("调用服务失败,入参{}", arg);
        return "testTimeout调用失败";
    }
}

fallbackFactory编写需要实现FallbackFactory类,如下:

@Component
@Slf4j
public class RemoteProviderServiceFallbackFactoryImpl implements FallbackFactory<IRemoteProviderService> {
    @Override
    public IRemoteProviderService create(Throwable throwable) {
        return new IRemoteProviderService() {
            @Override
            public String test(String arg) {
                log.error("调用服务失败" + throwable.getMessage(), throwable);
                return "test调用失败";
            }

            @Override
            public String testError(String arg) {
                log.error("调用服务失败:" + throwable.getMessage(), throwable);
                return "testError调用失败";
            }

            @Override
            public String testTimeout(Long arg) {
                log.error("调用服务失败" + throwable.getMessage(), throwable);
                return "testTimeout调用失败";
            }
        };
    }
}

七、服务超时时间配置

超时时间有三个地方配置,feign,hystrix,ribbon,配置不对将不生效或不能达到相应的效果,可参考SpringCloud的各种超时时间配置效果_shutdown hook installed for: nfloadbalancer-pingti-CSDN博客

feign:
  hystrix:
    #为false时  @FeignClient属性fallback不生效,true为开启服务降级
    enabled: true
  client:
    config:
      #设置feign日志级别,default为全局,若需要指定某个微服务则写微服务,需要此配置生效需要配置服务日志级别为debug
      default:
        logger-level: FULL
        connect-timeout: 1000
        read-timeout: 3000
#熔断器 Hystrix 的超时时间必须不小于 Feign 设置的超时时间,否则 Feign 设置的超时时间设置的超时时间无效
hystrix:
  command:
    default:
      execution:
        timeout:
          #是否开启超时熔断
          enabled: true
        isolation:
          thread:
            #服务降级超时时间,开启服务降级生效,
            timeoutInMilliseconds: 10000

ribbon:
  # 指的是建立连接后从服务器读取到可用资源所用的时间
  ReadTimeout: 1000
  # 指的是建立连接所用的时间,适用于网络状态正常的情况下,两端连接所用的时间
  ConnectTimeout: 1000
  • 如果hystrix.command.default.execution.timeout.enabled为true,则会有两个执行方法超时的配置,一个就是ribbon的ReadTimeout,一个就是熔断器hystrix的timeoutInMilliseconds, 此时谁的值小谁生效
  • 如果hystrix.command.default.execution.timeout.enabled为false,则熔断器不进行超时熔断,而是根据ribbon的ReadTimeout抛出的异常而熔断,也就是取决于ribbon
  • ribbon的ConnectTimeout,配置的是请求服务的超时时间,除非服务找不到,或者网络原因,这个时间才会生效
  • ribbon还有MaxAutoRetries对当前实例的重试次数,MaxAutoRetriesNextServer对切换实例的重试次数, 如果ribbon的ReadTimeout超时,或者ConnectTimeout连接超时,会进行重试操作
  • 由于ribbon的重试机制,通常熔断的超时时间需要配置的比ReadTimeout长,ReadTimeout比ConnectTimeout长,否则还未重试,就熔断了
  • 为了确保重试机制的正常运作,理论上(以实际情况为准)建议hystrix的超时时间为:(1 + MaxAutoRetries + MaxAutoRetriesNextServer) * ReadTimeout

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值