首先在provider中提供一个接口,然后创建一个新的consumer项目,消费这个端口
在provider中提供一个hello接口。如下
@RestController
public class HelloController {
@GetMapping("/hello")
public String hello(){
return "hello jiangbeicheng!";
}
}
接下来创建一个consumer项目,consumer项目中,去消费provider提供的接口。comsumer 要能获取到provider这个接口的地址,也就需要去Eureka Server 中查询。如果直接在consumer中写死provider地址,意味着这两个服务之间的耦合度太高了,我们要降低耦合度,首先我们看一个写死的调用。
创建一个consumer项目,添加web和eureka client依赖:
创建完成后,我们首先也在application.properties中配置一下注册信息:
spring.application.name=consumer
server.port=1115
eureka.client.service-url.defaultZone=http://localhost:1111/eureka
配置完成后,我们现在想在consumer 中调用provider提供的服务,我们可以直接将调用写死,就是说,整个调用过程不会涉及到eureka server.
@GetMapping("/hello1")
public String hello1(){
HttpURLConnection con=null;
URL url= null;
try {
url = new URL("http://localhost:1113/hello");
con=(HttpURLConnection) url.openConnection();
if(con.getResponseCode()==200){
BufferedReader br = new BufferedReader(new InputStreamReader(con.getInputStream()));
String s=br.readLine();
br.close();
return s;
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return "error";
}
这是一段利用了HttpUrlConnect来发起的请求,请求中provider的地址写死了,意味着provider和consumer高度绑定在一起,这个不符合微服务的思想。要改造他,我们可以借助Eureka Client提供的工具,利用这个工具。我们可以更具服务从Eureka Server上查询到一个服务的详细信息。改造后的代码如下:
@Autowired
DiscoveryClient discoveryClient;
//包不要导入出错了(import org.springframework.cloud.client.discovery.DiscoveryClient;)
@GetMapping("/hello2")
public String hello2(){
List<ServiceInstance> list =discoveryClient.getInstances("provider");
ServiceInstance instance=list.get(0);
String host=instance.getHost();
int port = instance.getPort();
StringBuffer sb= new StringBuffer();
sb.append("http://")
.append(host)
.append(":")
.append(port)
.append("/hello");
HttpURLConnection con=null;
try {
URL url = new URL(sb.toString());
con=(HttpURLConnection) url.openConnection();
if(con.getResponseCode()==200){
BufferedReader br = new BufferedReader(new InputStreamReader(con.getInputStream()));
String s=br.readLine();
br.close();
return s;
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return "error";
}
注意,DiscoverClient查询到的服务列表是一个集合,因为服务在部署的时候,可能是集群化部署,首先修改provider中的hello接口
@RestController
public class HelloController {
@Value("${server.port}")
Integer port;
@GetMapping("/hello")
public String hello(){
return "hello jiangbeicheng!";
}
}
因为我一会启动多个provider实例,多个provider实例的端口号不同,为了区分调用时到底调用的是哪一provider提供的服务,在这个接口返回端口,修改完成后对provider进行打包,打包成功后,在命令行启动两个个provider实例
java-jar provider-0.0.1-SNAPSHOT.jar --server.port=1113
java-jar provider-0.0.1-SNAPSHOT.jar --server.port=1116
启动成功后,检查Eureka Server 是否成功注册进来
注册成功后,在consumer中在去调用provider DiscoveryClient集合中,获取到的不是一个实例而是两个实例。
从集合中获取数据时候通过一个小小举动就可以实现线性负载均衡
int count =0;
@GetMapping("/hello3")
public String hello3(){
List<ServiceInstance> list =discoveryClient.getInstances("provider");
ServiceInstance instance=list.get((count++)%list.size());
String host=instance.getHost();
int port = instance.getPort();
StringBuffer sb= new StringBuffer();
sb.append("http://")
.append(host)
.append(":")
.append(port)
.append("/hello");
HttpURLConnection con=null;
try {
URL url = new URL("http://localhost:1113/hello");
con=(HttpURLConnection) url.openConnection();
if(con.getResponseCode()==200){
BufferedReader br = new BufferedReader(new InputStreamReader(con.getInputStream()));
String s=br.readLine();
br.close();
return s;
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return "error";
}
升级改造:
http调用
负载均衡
Http调用,我们使用Spring提供的RestTemplate来实现。
package org.jiangbeicheng.comsumer;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
@SpringBootApplication
public class ComsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ComsumerApplication.class, args);
}
@Bean
RestTemplate restTemplateOne(){
return new RestTemplate();
}
}
然后,在Http调用时候,不在使用HttpUrlConnection 而是直接使用RestTemplate
@Autowired
RestTemplate restTemplate;
@Autowired
DiscoveryClient discoveryClient;
//包不要导入出错了(import org.springframework.cloud.client.discovery.DiscoveryClient;)
@GetMapping("/hello2")
public String hello2(){
List<ServiceInstance> list =discoveryClient.getInstances("provider");
ServiceInstance instance=list.get(0);
String host=instance.getHost();
int port = instance.getPort();
StringBuffer sb= new StringBuffer();
sb.append("http://")
.append(host)
.append(":")
.append(port)
.append("/hello");
/*HttpURLConnection con=null;
try {
URL url = new URL("http://localhost:1113/hello");
con=(HttpURLConnection) url.openConnection();
if(con.getResponseCode()==200){
BufferedReader br = new BufferedReader(new InputStreamReader(con.getInputStream()));
String s=br.readLine();
br.close();
return s;
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return "error";*/
String s = restTemplate.getForObject(sb.toString(), String.class);
return s;
}
用一行代码就实现了Http的调用
接下来使用Rubbon来快速实现负载均衡
首先,我们需要给RestTemplate实例添加一个@LoadBalancer注解,开启负载均衡:
package org.jiangbeicheng.comsumer;
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 ComsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ComsumerApplication.class, args);
}
@Bean
RestTemplate restTemplateOne(){
return new RestTemplate();
}
@Bean
@LoadBalanced
RestTemplate restTemplate(){
return new RestTemplate();
}
}
此时候RestTemplate 就自动具备了负载均衡的功能。
@Autowired
@Qualifier("restTemplate")
RestTemplate restTemplate;
@GetMapping("/hello3")
public String hello3(){
return restTemplate.getForObject("http://provider/hello",String.class);
}