上一篇文章我们搭建完服务注册中心,这篇文章就来向服务注册中心注册服务以及消费服务吧
在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