Spring Cloud之服务注册与消费

上一篇文章我们搭建完服务注册中心,这篇文章就来向服务注册中心注册服务以及消费服务吧
在这里插入图片描述
在maven项目cloud下面创建两个module,一个提供服务的spring boot项目,一个消费服务的spring boot项目

创建服务提供方

创建spring boot项目,引入web、eureka discovery client
在这里插入图片描述

基本配置

在application.properties文件中配置服务提供方的信息

spring.application.name=provider
server.port=3000
eureka.instance.hostname=localhost
#之前我们已经注册了两个eureka注册中心,这里我们可以写一个或者两个都行,服务注册中心会自动的同步数据
eureka.client.service-url.defaultZone=http://localhost:1111/eureka

创建服务提供注册接口

package com.zhouym.provider;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * 〈〉
 *
 * @author zhouym
 * @create 2019/8/31
 * @since 1.0.0
 */
@RestController
public class ProviderController {

    @Value("${server.port}") //@Value注入application.properties文件中的端口信息
    Integer port;

    @GetMapping("/provider")
    public String provide(){
        return "provider:"+port;
    }
}

运行服务提供方的启动类,查看1111端口的后台服务注册中心,看看服务是否注册成功
在这里插入图片描述
很明显服务已经被注册到注册中心上面去了,下面我们新建一个服务消费方

注册服务消费方

在maven项目cloud下创建spring boot项目,还是引入web、以及eureka discover client依赖即可
在这里插入图片描述
同样的还是需要在application.properties配置服务消费方的信息

spring.application.name=consumer
server.port=4000
eureka.client.service-url.defaultZone=http://localhost:1112/eureka

创建服务消费方接口

package com.zhouym.consumer;

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 java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.List;

/**
 * 〈〉
 *
 * @author zhouym
 * @create 2019/8/31
 * @since 1.0.0
 */
@RestController
public class ConsumerController {

    @Autowired
    DiscoveryClient discoveryClient;

    @GetMapping("/consumer")
    public void consumer() throws IOException {
        List<ServiceInstance> serviceInstances = discoveryClient.getInstances("provider");
        ServiceInstance serviceInstance = serviceInstances.get(0);
        String host = serviceInstance.getHost();
        int port = serviceInstance.getPort();
        URL url = new URL("http://" + host + ":" + port + "/provider");
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        conn.connect();
        if (conn.getResponseCode() == 200) {
            BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream()));
            String s = br.readLine();
            System.out.println(s);
            br.close();
        }

    }
}

运行服务消费方,页面访问
在这里插入图片描述
来看看控制台是否有消息
在这里插入图片描述
来看看服务注册中心的记录
在这里插入图片描述

手动实现provider的负载均衡

我们在provider项目执行打包操作
在这里插入图片描述
在控制台执行如下命令,表示启动两个provider实例,实现负载均衡

E:\IDEA_WorkSpace\cloud\provider\target>java -jar provider-0.0.1-SNAPSHOT.jar --server.port=3002
E:\IDEA_WorkSpace\cloud\provider\target>java -jar provider-0.0.1-SNAPSHOT.jar --server.port=3001

在consumer方对controller接口作相应调整

package com.zhouym.consumer;

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 java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.List;

/**
 * 〈〉
 *
 * @author zhouym
 * @create 2019/8/31
 * @since 1.0.0
 */
@RestController
public class ConsumerController {

    @Autowired
    DiscoveryClient discoveryClient;
    int count = 0; //定义临时变量
    @GetMapping("/consumer")
    public void consumer() throws IOException {
        List<ServiceInstance> serviceInstances = discoveryClient.getInstances("provider"); //由于服务提供方不止一个,所以这里我们需要对serviceInstances 进行遍历
        ServiceInstance serviceInstance = serviceInstances.get(count++ % serviceInstances.size()); //通过count自增1对list集合取模,实现随机获取到一个服务提供方实例
        String host = serviceInstance.getHost();
        int port = serviceInstance.getPort();
        URL url = new URL("http://" + host + ":" + port + "/provider");
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        conn.connect();
        if (conn.getResponseCode() == 200) {
            BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream()));
            String s = br.readLine();
            System.out.println(s);
            br.close();
        }

    }
}

页面访问进行多次访问http://localhost:4000/consumer。看看控制台的输出结果
在这里插入图片描述
上面我们在controller接口类中,创建一系列的url连接,觉得很麻烦,在spring中提供了一个RestTemplate,它可以帮我们发送一个GET或者POST请求,我们在consumer项目中的启动类创建一个RestTemplate的Bean实例

package com.zhouym.consumer;

import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.RandomRule;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

@SpringBootApplication
public class ConsumerApplication {

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

    @Bean("restTemplateOne")
    RestTemplate restTemplateOne() {
        return new RestTemplate();
    }


在controller接口中使用,注入RestTemplate就能用

package com.zhouym.consumer;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
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.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.List;

/**
 * 〈〉
 *
 * @author zhouym
 * @create 2019/8/31
 * @since 1.0.0
 */
@RestController
public class ConsumerController {

    @Autowired
    DiscoveryClient discoveryClient;

    @Autowired
    @Qualifier("restTemplateOne")
    RestTemplate restTemplateOne;
    int count = 0;
    @GetMapping("/consumer")
    public void consumer() throws IOException {
        List<ServiceInstance> serviceInstances = discoveryClient.getInstances("provider");
        ServiceInstance serviceInstance = serviceInstances.get(count++ % serviceInstances.size());
        String host = serviceInstance.getHost();
        int port = serviceInstance.getPort();
        URL url = new URL("http://" + host + ":" + port + "/provider");
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        conn.connect();
        if (conn.getResponseCode() == 200) {
            BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream()));
            String s = br.readLine();
            System.out.println(s);
            br.close();
        }

    }

    @GetMapping("/consumer1")
    public void consumer1() throws IOException {
        List<ServiceInstance> serviceInstances = discoveryClient.getInstances("provider");
        ServiceInstance serviceInstance = serviceInstances.get(count++ % serviceInstances.size());
        String host = serviceInstance.getHost();
        int port = serviceInstance.getPort();
        String url = "http://" + host + ":" + port + "/provider";
        String message = restTemplateOne.getForObject(url, String.class);//第一个参数是请求地址,第二个参数服务端响应的数据类型

        System.out.println(message);


    }


}

我们在页面访问http://localhost:4000/consumer1,在控制台中就能获取数据

我们在启动类添加一个注解@LoadBalanced,可以实现负载均衡

package com.zhouym.consumer;

import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.RandomRule;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

@SpringBootApplication
public class ConsumerApplication {

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

    @Bean
    @LoadBalanced//可以自动实现负载均衡的注解
   // RestTemplate继承了InterceptingHttpAccessor,见名之意,继承了一个Http的拦截器对象,就是要拦截所有的http请求,对请求做进一步处理
    RestTemplate restTemplate() {
        return new RestTemplate();
    }


    //配置随机的负载均衡策略
    @Bean
    IRule iRule(){
        return new RandomRule();
    }
}

在controller接口类中,

package com.zhouym.consumer;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
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.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.List;

/**
 * 〈〉
 *
 * @author zhouym
 * @create 2019/8/31
 * @since 1.0.0
 */
@RestController
public class ConsumerController {

@Autowired
    RestTemplate restTemplate;
    @GetMapping("/consumer2")
    public void consumer2() throws IOException {
        for(int i = 0; i < 10; i++){
            String result = restTemplate.getForObject("http://provider/provider", String.class);
            System.out.println(result);
        }

    }
}

来看看@LoadBalanced这个注解的源码

/*
 * Copyright 2012-2019 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.cloud.client.loadbalancer;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import org.springframework.beans.factory.annotation.Qualifier;

/**
 * Annotation to mark a RestTemplate bean to be configured to use a LoadBalancerClient.
 * 大概意思就是使用LoadBalancerClient可以配置RestTemplate
 * @author Spencer Gibb
 */
@Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Qualifier
public @interface LoadBalanced {

}


再来看看LoadBalancerClient,它是一个接口,它的实现类只有一个
LoadBalancerClient的核心方法
在这里插入图片描述
在这里插入图片描述

@Override
	public URI reconstructURI(ServiceInstance instance, URI original) {
		Assert.notNull(instance, "instance can not be null");
		String serviceId = instance.getServiceId();
		RibbonLoadBalancerContext context = this.clientFactory
				.getLoadBalancerContext(serviceId);

		URI uri;
		Server server;
		if (instance instanceof RibbonServer) {
			RibbonServer ribbonServer = (RibbonServer) instance;
			server = ribbonServer.getServer();
			uri = updateToSecureConnectionIfNeeded(original, ribbonServer);
		}
		else {
			server = new Server(instance.getScheme(), instance.getHost(),
					instance.getPort());
			IClientConfig clientConfig = clientFactory.getClientConfig(serviceId);
			ServerIntrospector serverIntrospector = serverIntrospector(serviceId);
			uri = updateToSecureConnectionIfNeeded(original, clientConfig,
					serverIntrospector, server);
		}
		return context.reconstructURIWithServer(server, uri);
	}

方法第一个参数就是服务实例,就是provider
在这里插入图片描述
方法第二个参数是请求的URL
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值