1.order-user系统的整合
1.1订单支付
通过访问订单支付功能,订单系统发起代码内部的HTTP请求到用户系统调用user/update/point
1.2RestTemplate (个人理解相当于前端的Ajax用在了后端中,相当于后端的Ajax)
springmvc的对象,支持REST风格,支持http协议的并发访问(http协议封装成了对象调用的方法)
getForObject get请求
postForObject post 请求
putForObject
deleteForObject
Get
getForObject:返回值是响应体
getForEntity:返回值是响应对象
getForEntity().getBody()==getForObject();
重载方法三个
(url,responseType,Map<String,Object>);
(url,responseType,Object…);
(url,responseType);
Post
postForObject:返回值是响应体
postForEntity:返回值是响应对象
postForEntity().getBody()==postForObject();和get的区别就是post可以将请求体中封装一些内容
(url,request,responseType,param);
request:Object;类型请求体中的数据可以是代码中的对象,user对象,order(一json字符串格式传递给后端的服务器,接收请求体中的数据)
总结:
restTemplate封装的http协议的对象可以实现不同的请求方式,不同的传参格式,发起向服务器的请求,接收响应或者响应体数据
2.分布式的order_user系统的问题
没有管理的结构的引入,每个系统之间都要通过nginx这种技术的静态文件维持访问调用的关系
独立的系统越多,静态文件配置的内容就越多形成了强耦合—>频繁的大量修改静态配置文件(因为可能会遇到宕机等问题,必须修改配置文件)
3.微服务架构
3.1什么是微服务
3.2什么是微服务架构
3.3什么是微服务springcloud
4.springcloud
基于springboot的自动配置实现的微服务框架,可以在引入依赖之后,利用自动配置的机制,实现springcloud不同组件的自动配置
springboot整合纵向拆分order-user
1.订单功能
1.1接口文件
请求地址 /order/pay
请求参数 String orderId
返回数据 1/0成功和失败
1.2代码编写
Ordercontroller
OrderService()(实现支付逻辑,实现调用user的update接口)
OrderMapper
OrderMapper.xml
Order/User
1.3RestTemplate
封装了底层Http协议,可以从代码中发起http请求
测试案例
通过RestTemplate访问一个网站 (京东)
nginx配置修改
源码:
package cn.tedu.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import cn.tedu.service.OrderService;
@RestController
public class OrderController {
@Autowired
private OrderService orderService;
//订单支付
@RequestMapping("order/pay")
public Integer orderPay(String orderId){
try {
orderService.orderPay(orderId);
return 1;
} catch (Exception e) {
//给自己展示异常
e.printStackTrace();
return 0;
}
}
}
package cn.tedu.domain;
public class Order {
private String orderId;
private Integer orderMoney;
private Integer userId;
public Order(String orderId, Integer orderMoney, Integer userId) {
super();
this.orderId = orderId;
this.orderMoney = orderMoney;
this.userId = userId;
}
public Order() {
super();
}
public String getOrderId() {
return orderId;
}
public void setOrderId(String orderId) {
this.orderId = orderId;
}
public Integer getOrderMoney() {
return orderMoney;
}
public void setOrderMoney(Integer orderMoney) {
this.orderMoney = orderMoney;
}
public Integer getUserId() {
return userId;
}
public void setUserId(Integer userId) {
this.userId = userId;
}
@Override
public String toString() {
return "Order [orderId=" + orderId + ", orderMoney=" + orderMoney + ", userId=" + userId + "]";
}
}
package cn.tedu.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import cn.tedu.domain.Order;
import cn.tedu.mapper.OrderMapper;
@Service
public class OrderService {
@Autowired
private OrderMapper orderMapper;
public void orderPay(String orderId) {
//查询order对象,获取useId和orderMoney的数据
Order order=orderMapper.selectOrderById(orderId);
//支付逻辑中可以实现打折,活动,确定支付的真正金额
System.out.print("用户:"+order.getUserId()+
"支付金额:"+order.getOrderMoney());
//TODO想办法 将参数userid orderMoney封装到一个htt请求中,
//发起请求访问www.ssm.com/user/update/point(交给nginx)
//RestTemplate
RestTemplate client=new RestTemplate();
String url="http://www.ssm.com/user/update/point?userId="
+order.getUserId()+"&orderMoney="+order.getOrderMoney();
//发起代码的请求到接口的url地址
Integer success = client.getForObject(url, Integer.class);
if(success==1){
System.out.println("积分成功");
}
if(success==0){
System.out.println("积分失败");
}
}
}
package cn.tedu.mapper;
import cn.tedu.domain.Order;
public interface OrderMapper {
Order selectOrderById(String orderId);
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.tedu.mapper.OrderMapper">
<!-- selectUserById -->
<!-- <select id="selectUserById" parameterType="int" resultType="User">
select * from t_user where user_id=#{userId};
</select> -->
<!--selectOrderById -->
<select id="selectOrderById" parameterType="String" resultType="Order">
select * from t_order where order_id=#{orderId};
</select>
<!-- <update id="updateUserPointsById" parameterType="User" >
update t_user set points=points+#{points} where user_id=#{userId};
</update> -->
</mapper>
#8090
server.port=8091
#server.contextPath=/1906
#1906.class-name=big1906
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql:///easymall?useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
mybatis.typeAliasesPackage=cn.tedu.domain
mybatis.mapperLocations=classpath:mapper/*.xml
mybatis.configuration.mapUnderscoreToCamelCase=true
mybatis.configuration.cacheEnabled=false
#mapperscaner
<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>
<groupId>cn.tedu</groupId>
<artifactId>springboot-demo1</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>springboot-demo1</name>
<url>http://maven.apache.org</url>
<!-- 当前工程是一个子工程 继承父工程 -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.9.RELEASE</version>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!--web应用的简化依赖starter-web -->
<!--log jackson embed tomcat spring context beans springboot -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--jdbc协议 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!-- mysql -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.0.8</version>
</dependency>
<!--springboot mybaits -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.1</version>
</dependency>
<!--druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.14</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
测试代码resttemplate
package cn.tedu.restTemplate.test;
import java.util.HashMap;
import java.util.Map;
import org.junit.Test;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate;
import cn.tedu.domain.User;
//RestTemplate测试案例
public class RestTest {
@Test
public void test(){
RestTemplate client=new RestTemplate();
//根据封装的方法调用api
//url:访问的地址
//responseType:响应体的接收类型
String responseBody=client.getForObject("https://www.jd.com/", String.class);
System.out.println(responseBody);
//api有哪些,如何携带参数
//访问/user/query/point
User user=client.getForObject("http://localhost:8090/user/query/point?userId=1",
//String.class);//json
User.class);//user对象
System.out.println(user);
}
@Test
public void test2(){
RestTemplate client=new RestTemplate();
//根据封装的方法调用api
//url:访问的地址
//responseType:响应体的接收类型
ResponseEntity<String> response = client.getForEntity("https://www.jd.com/", String.class);
response.getBody();//==getForObject
response.getHeaders();//头信息
//client.getForObject(url, responseType, uriVariables)
//client.getForObject(url, responseType, uriVariables)
//System.out.println(response);
Map<String, Object> param=new HashMap<String, Object>();
param.put("userId", 1);
param.put("lev", 0);
//map 类型的api
String user=client.getForObject("http://localhost:8090/user/query/point?userId={userId}&lev={lev}",
String.class,param);//user对象
System.out.println(user);
//object类型的api
String user2=client.getForObject("http://localhost:8090/user/query/point?userId={1}&lev={2}&age={3}&name={4}",
String.class,1,0,"haha","kaka");//user对象
//将1,0看成一个整体 底层封装成数组Object[]={1,0}
System.out.println(user2);
}
@Test
public void test03(){
RestTemplate client=new RestTemplate();
// client.postForObject(url, request, responseType)
// client.postForEntity(url, request, responseType)
// client.postForObject(url, request, responseType,param)
// client.postForEntity(url, request, responseType,Object)
}
}
最终结构
微服务架构的由来
庞大的微服务架构,如果一个服务宕机,会引起nginx的维护,修改nginx中的配置,麻烦,需要有一个管理,引出springcloud
微服务的引入(详细)
1.纵向拆分的独立系统的集群的问题
1.1没有引入管理的功能
系统的管理,授权监听,熔断等逻辑都没有引入
1.2静态配置的负载均衡的强耦合
在庞大的纵向拆分的集群中,nginx的静态文件维护负载均衡逻辑,容易出现强耦合
例如:集群中某先节点出现宕机/迁移
可以引入微服务的概念,从架构角度去解决问题
2.微服务框架
2.1微服务
从一个单体项目,从一个功能比较集中的项目中纵向拆分出来独立运行的功能,每个功能的系统—微服务
微:纵向拆分
服务:功能被调用
2.2微服务框架
一个框架技术能够管理大量的拆分的独立的系统,具有监控熔断等功能,这种框架技术—微服务框架
2.3springcloud
轻量级的微服务框架,可以基于springboot的自动配置(减少了自定义的大量代码的编写)实现了多个微服务框架的组件的功能
eureka:服务的治理
ribbon:客户端负载均衡的调用
feign:ribbon是同一种客户端,封装了ribbon
hystrix:熔断器
zuul:网关,微服务唯一对外提供的接口
config:微服务种配置文件的管理者
微服务框架springcloud
微服务框架组件
1.Eureka服务治理组件(管理者)
springcloud中的核心组件,可以实现对整个微服务集群所有节点进行服务的发现,服务的抓取,和服务的监听功能
1.1服务治理的概念中的三个角色
服务注册中心:所有的服务的集中管理角色
服务的提供者:服务提供者会将自己的信息打包(ip,port,服务名称等等),注册,在注册中被注册中心管理和维护(注册)
服务的调用者:可以通过对注册中心的访问,获取服务提供者的信息,从而进行负载均衡的调用(抓取发现)
1.2入门案例服务注册中心
准备一个springboot+springcloud开发环境的工程
pom:继承springboot-parent
dependencyManagement:导入springcloud的所有的依赖
继承springboot
<!--继承springboot -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.9.RELEASE</version>
</parent>
导入依赖springcloud
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Edgware.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
导入eureka注册中心的依赖
<!-- 导入eureka -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka-server</artifactId>
</dependency>
pom
<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>
<groupId>cn.tedu</groupId>
<artifactId>springcloud-eureka-server</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>springcloud-eureka-server</name>
<url>http://maven.apache.org</url>
<!--继承springboot -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.9.RELEASE</version>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!-- 导入eureka -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka-server</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
<!--导入springcloud -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Edgware.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>
注意:有时候移除junit版本号很有可能会报大错
application.properties
server.port=8888
#eureka相关配置
##关闭当前配置中心的服务抓取和注册(给注册提供者使用,自己也可以作为注册提供者,在高可用时使用,自己注册自己抓取,这里关闭)
#如果自己用自己注册,需要提供服务名称
spring.application.name=eurekaserver
eureka.client.registerWithEureka=false
eureka.client.fetchRegistry=false
#注册中心的地址,但凡是服务器注册者都需要配置这个地址
#注册者会访问这个地址的接口,携带自己节点的信息注册
eureka.client.serviceUrl.defaultZone=http://localhost:8888/eureka
启动类:
添加一个eureka的注解,就能实现启动加载eureka的自动配置逻辑实现一个web应用中包含一个eurekaserver的进程
package cn.tedu;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
//启动类
//springboot
@SpringBootApplication
//rureka注册中心进程启动时需要springboot加载扫描的注解
@EnableEurekaServer
public class StarterEurekaServer {
public static void main(String[] args) {
SpringApplication.run(StarterEurekaServer.class, args);
}
}
web页面
互联网框架和大数据的关系
将分析结果传给表象层,(新功能:预测:物流高发预测)
注册中心的作用:
管理注册者的服务信息:
内部接收注册者的请求,注册者携带本机节点的详细参数(ip,port,服务名称)发送给注册中心,eureka接收请求,在内存中存储一个双层map对象 保存所有的内容
服务的监听超时:
多个注册者同时注册一个服务,相当于一个具体的服务,被一个集群管理接收请求,每60秒钟判断是否有服务提供者的续约,超时达到90秒,一旦满足条件,将会从内存中超时的实例剔除
1.3入门案例的服务提供者
创建一个工程springboot+springcloud可以被调用一个资源访问 /hello,作为一个服务的提供者,必须生成一个服务的名称,service-hi,注册在注册中心。
搭建一个系统springboot+springcloud
(做一个pom拥有springboot springcloud的依赖,然后springcloud的组件去继承这个pom(注意这个父类的pom文件的packing要改为pom类型))
<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>
<groupId>cn.tedu</groupId>
<artifactId>springcloud-parent-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>pom</packaging>
<!--继承springboot -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.9.RELEASE</version>
</parent>
<name>springcloud-parent-demo</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
<!--导入springcloud -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Edgware.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>
pom 继承springboot-parent
导入 springcloud dependencies
引入一个依赖 spring-cloud-starter-eureka eureka客户端依赖
<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>
<groupId>cn.tedu</groupId>
<artifactId>springcloud-eureka-client1</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>springcloud-eureka-client1</name>
<url>http://maven.apache.org</url>
<!--继承springcloud-parent -->
<parent>
<groupId>cn.tedu</groupId>
<artifactId>springcloud-parent-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<!-- <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.9.RELEASE</version> -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!-- eureka客户端依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
starter-eureka-server 服务器端依赖,包含了上述的依赖(spring-cloud-starter-eureka)
(更改后的服务端pom文件)
<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>
<groupId>cn.tedu</groupId>
<artifactId>springcloud-eureka-server</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>springcloud-eureka-server</name>
<url>http://maven.apache.org</url>
<!--继承springcloud-parent -->
<parent>
<groupId>cn.tedu</groupId>
<artifactId>springcloud-parent-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<!-- <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.9.RELEASE</version> -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!-- 导入eureka -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka-server</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
<!--导入springcloud -->
<!-- <dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Edgware.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement> -->
</project>
application.properties
端口 9001
服务名称:service-hi
注册/发现 开启(默认开启)
注册地址 8888/eureka
server.port=9001
spring.application.name=service-hi
#eureka.client.registerWithEureka=true
#eureka.client.fetchRegistry=true
eureka.client.serviceUrl.defaultZone=http://localhost:8888/eureka
启动类
添加一个eureka客户端的注解(注册和发现的功能)
调用功能
/hello?name=wang 返回hello *** name
package cn.tedu;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
//客户端的启动类
@SpringBootApplication
//eureka客户端注解
@EnableEurekaClient
public class StarterEurekaClient1 {
public static void main(String[] args) {
SpringApplication.run(StarterEurekaClient1.class, args);
}
}
高可用的集群注册同一个服务
eureka-client1 eureka-client2
eureka-client2修改3处
server.port=9002
controller 返回值 I am from 9002
启动类StarterEurekaClient2
注册者实现的逻辑
注册:启动后,当前eureka client一旦赋予注册能力
registerWithEureka=true 将会访问注册中心接口
8888/eureka 携带详情信息,注册在注册中心,注册中心以服务名称为key值记录一个当前服务的所有节点信息的map对象
续约:eureka client一旦在注册中心提供注册的信息,将会每三十秒发起一次心跳(heartbeat)请求,告知注册中心,当前实例是存活的。
服务的剔除:
30:客户端发起心跳
60:注册中心,每隔60秒检测一次心跳最后的时间
90:注册中心,检测发现超过90秒没有发起心跳检测,将会把该实例剔除
注册中心的保护机制
庞大的集群中如果按照正常的逻辑,管理一个微服务集群(一百个服务,每个20节点,2000注册者),超时续约的个数如果在15%以内,eureka注册中心,判断是正常的超时,将会按照剔除逻辑对实例进行内存数据的清除,如果超过15%认为是不正常的超时,将会开启保护机制,一旦开启,所有的服务信息(map对象)不会做任何的剔除,形成对微服务的保护
如果需要观察剔除的现象可以将保护机制关闭,注册中心的配置文件中配置保护机制关闭为false
2.ribbon组件
配合服务治理组件eureka的客户端的发现功能,从注册中心抓取最新的服务注册信息,从而可以在代码内部发起向该服务的访问,使用注册信息中实例的详细信息访问不同的节点实现负载均衡—可以调用服务,支持负载均衡访问的springcloud客户端组件
2.1入门案例ribbon负载均衡调用9001/9002
创建一个具备开发springboot-sprinigcloud功能的maven工程
pom文件继承自定义的parent-demo工程
pom添加依赖
rureka-client(抓取服务)
ribbon的组件相关的依赖
application.properties
9004
服务名称 service-ribbon
抓取服务 /true(默认是开启)
注册url地址:8888/eureka
启动类:
eureka client相关的注解
创建一个springboot框架管理的内存对象
@Bean
@loadBalanced
RestTemplate
2.2实现一个负载均衡的调用后端服务的service-hi
编写一个ribbon的对外访问的url接口
locahost:9004/hi?name=wang
通过ribbon直接实现负载均衡访问9001/9002
HelloController
HelloService
2.3实现动态添加扩容服务提供者
单独访问ribbon工程,可以使用springcloud的ribbon组件做服务调用服务的功能的测试,对于被调用的服务service-hi,只要启动9003作为扩容的节点,添加到eureka注册中心,注册中心维护的注册信息,将会发生变动
ribbon工程整合了eureka client 可以实现服务的抓取,每30秒重新更新抓取一次服务注册信息的map数据
代码整理
先创建一个springcloud项目作为父类,书写pom里边的依赖,方便继承使用
(具体在springcloud工作空间里边的springcloud-parent-demo项目)
pom文件配置
<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>
<groupId>cn.tedu</groupId>
<artifactId>springcloud-parent-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>pom</packaging>
<!--继承springboot -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.9.RELEASE</version>
</parent>
<name>springcloud-parent-demo</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
<!--导入springcloud -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Edgware.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>
eureka注册中心(服务名称,端口,注册中心接口)
启动类
package cn.tedu;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
//启动类
//springboot
@SpringBootApplication
//rureka注册中心进程启动时需要springboot加载扫描的注解
@EnableEurekaServer
public class StarterEurekaServer {
public static void main(String[] args) {
SpringApplication.run(StarterEurekaServer.class, args);
}
}
配置类
server.port=8888
#eureka相关配置
##关闭当前配置中心的服务抓取和注册(给注册提供者使用,自己也可以作为注册提供者,在高可用时使用,自己注册自己抓取,这里关闭)
#如果自己用自己注册,需要提供服务名称
spring.application.name=eurekaserver
eureka.client.registerWithEureka=true
eureka.client.fetchRegistry=false
#注册中心的地址,但凡是服务器注册者都需要配置这个地址
#注册者会访问这个地址的接口,携带自己节点的信息注册
eureka.client.serviceUrl.defaultZone=http://localhost:8888/eureka
#关闭保护机制(可以剔除,观察剔除现象)
eureka.server.enable-self-preservation=false
pom
<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>
<groupId>cn.tedu</groupId>
<artifactId>springcloud-eureka-server</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>springcloud-eureka-server</name>
<url>http://maven.apache.org</url>
<!--继承springcloud-parent -->
<parent>
<groupId>cn.tedu</groupId>
<artifactId>springcloud-parent-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<!-- <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.9.RELEASE</version> -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!-- 导入eureka -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka-server</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
<!--导入springcloud -->
<!-- <dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Edgware.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement> -->
</project>
eureka注册者(好几个client1,client2…服务名称可能相同,注册中心接口(固定的),修改端口,修改启动类的类名,方便区别)
启动类
package cn.tedu;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
//客户端的启动类
@SpringBootApplication
//eureka客户端注解
@EnableEurekaClient
public class StarterEurekaClient1 {
public static void main(String[] args) {
SpringApplication.run(StarterEurekaClient1.class, args);
}
}
配置
server.port=9001
#eureka相关配置
##关闭当前配置中心的服务抓取和注册(给注册提供者使用,自己也可以作为注册提供者,在高可用时使用,自己注册自己抓取,这里关闭)
#如果自己用自己注册,需要提供服务名称
spring.application.name=service-hi
#eureka.client.registerWithEureka=true
#eureka.client.fetchRegistry=true
#注册中心的地址,但凡是服务器注册者都需要配置这个地址
#注册者会访问这个地址的接口,携带自己节点的信息注册
eureka.client.serviceUrl.defaultZone=http://localhost:8888/eureka
pom
<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>
<groupId>cn.tedu</groupId>
<artifactId>springcloud-eureka-client1</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>springcloud-eureka-client1</name>
<url>http://maven.apache.org</url>
<!--继承springcloud-parent -->
<parent>
<groupId>cn.tedu</groupId>
<artifactId>springcloud-parent-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<!-- <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.9.RELEASE</version> -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!-- eureka客户端依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
控制层
package cn.tedu.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
@RequestMapping("hello")
public String sayHi(String name){
return "hello,"+name+",i am from 9001";
}
}
ribbon服务调用者(也算是一个注册者client)
启动类(除了启动类,还可以配置负载均衡,不配置为默认轮询,还有随机和权重)
package cn.tedu;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.RandomRule;
//ribbon的启动类
@SpringBootApplication
@EnableEurekaClient
public class StarterRibbon {
public static void main(String[] args) {
SpringApplication.run(StarterRibbon.class, args);
}
@Bean
//负载均衡
@LoadBalanced
public RestTemplate initRestTemplate(){
return new RestTemplate();
}
//修改自定义的随机负载均衡逻辑
@Bean
public IRule initRule(){
return new RandomRule();
}
}
控制层
package cn.tedu.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import cn.tedu.service.HelloService;
//访问localhost:9004/hi?name=王老师
@RestController
public class HelloController {
@Autowired
private HelloService helloService;
//客户端调用hi接口
@RequestMapping("hi")
public String sayHi(String name){
return "RIBBON:"+helloService.sayHi(name);
}
}
服务层(拦截服务名称,解析,url,可以被搜索执行)
package cn.tedu.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
@Service
public class HelloService {
@Autowired
private RestTemplate client;//会因为使用的创建过程
//@Bean+@LoadBalanced注解,ribbon会对restTemplate
//发送的任何请求做拦截工作,将域名寻找抓取的服务名称做实例
//节点,将节点的ip:port替换
public String sayHi(String name){
//http://service-hi/hello?name=wang 会拦截替换
String sayHi=client.getForObject("http://service-hi/hello?name="+name, String.class);
return sayHi;
}
}
配置
server.port=9004
#eureka相关配置
##关闭当前配置中心的服务抓取和注册(给注册提供者使用,自己也可以作为注册提供者,在高可用时使用,自己注册自己抓取,这里关闭)
#如果自己用自己注册,需要提供服务名称
spring.application.name=server-ribbon
#eureka.client.registerWithEureka=true
#eureka.client.fetchRegistry=true
#注册中心的地址,但凡是服务器注册者都需要配置这个地址
#注册者会访问这个地址的接口,携带自己节点的信息注册
eureka.client.serviceUrl.defaultZone=http://localhost:8888/eureka
pom
<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>
<groupId>cn.tedu</groupId>
<artifactId>springcloud-ribbon-client</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>springcloud-ribbon-client</name>
<url>http://maven.apache.org</url>
<!--继承springcloud-parent -->
<parent>
<groupId>cn.tedu</groupId>
<artifactId>springcloud-parent-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<!-- <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.9.RELEASE</version> -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!-- eureka客户端依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<!--ribbon依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
2.5ribbon实现负载均衡访问服务的原理
ribbon具备注册和抓取的能力,启动后会将eureka注册中心的所有map数据抓取到本地存储,每隔30秒更新一次抓取内容
restTemplate的创建过程是通过LoadBalances,ribbon会对当前这个对象的所有请求做拦截处理,LoadBalancerInterceptor
restTemplate发送请求http://service-hi/hello?name=wang
ribbon通过拦截解析了服务名称service-hi
通过抓取的map对象找到service-hi value(三个节点的详细信息)
通过负载均衡对象IRule从3个节点选取一个ip:port拼接到域名所在位置最终发送请求
注意:springcloud中只要是服务调用服务的过程都是通过ribbon+restTemplate实现(zuul feign)
3.order-user系统整合到springcloud(需要再加入一个组件)
最前端的客户端浏览器,如何调用suer order 的功能?zuul
4.zuul网关集群的调用结构
完成eureka注册中心的高可用
总结:
访问网址:
http://localhost:9004/hi?name=王老师(通过端口访问ribbon)