服务注册中心:eureka 由zookeeper,Consul,Nacos(重点)
服务调用:ribbon loadBalancer可能后面会替代ribbon
服务调用2:feign 由openFeign替代
服务降级:Hystrix 国内经常使用。国外已经在使用resilience4j 国内用alibaba sentienl替代
网关:zuul gateway替代
配置中心:config 由Nacos替代
消息总线:bus 由Nacos替代
所以Nacos是重点之中的重点
1,创建一个总的父工程。springcloud,project
设置编码utf-8 然后设置注解生效
选择java8
文件type过滤
如果一个项目的pom.xml文件里有< dependenciesmanager>那么一定是父工程 父项目中指定了版本号。子项目中就可以不用指定版本号了。。。
创建子工程时。直接创建maven项目
1,创建payment provider项目 粘贴父项目的pom.xml
application.yml
数据库db2019 payment的创建
创建payment提供者。写好增删改查。。。。
开启自动热部署功能 1,
2,这个放父工程,finalName可写可不写
3,
4,
5,重启idea完成热部署
###热部署只能在开发环境下使用
order只能有controller。客户端这块
拷贝provider的entity
6,restTemplete的。。。服务调用 创建一个config类
controller实现客户端调用provider 写操作
读操作
如果插入数据库只有主键没有内容。处理如下
在消费者这边和生产者这边加上@requestbody注解
上一节。。服务调用者和服务提供者。。。entity重复了。我们要将它们重构
创建一个公共类 cloud-api-commons pom.xml
把entity粘贴过来
然后clean,install
然后再改造80和8001 删除掉entity
然后分别引入坐标
如果最后出现包扫描有问题
请在两个启动类上加注解 @mapperscan(“com.atguigu.springcloud.dao”) @ComponentScan(“com.atguigu.springcloud.*”)
1,pom.xml
<!--eureka server--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency>2,application.yml
server: port: 7001 eureka: instance: hostname: eureka7001.com #eureka服务端的实例名称 client: #false表示不向注册中心注册自己 register-with-eureka: false #false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务 fetch-registry: false service-url: #设置与Eureka Server交互的地址查询服务和注册服务都需要依赖这个地址 #defaultZone: http://eureka7002.com:7002/eureka/ #互相注册 defaultZone: http://eureka7001.com:7001/eureka/ #单机模式 #server: #关闭自我保护机制,保证不可用服务被及时剔除 #enable-self-preservation: false #eviction-interval-timer-in-ms: 20003,主启动
package com.atguigu.springcloud; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer; @SpringBootApplication @EnableEurekaServer public class EurekaMain7001 { public static void main(String[] args) { SpringApplication.run(EurekaMain7001.class,args); } }8001和80整合eureka
创建微服务提供者8001和微服务消费者80 pom
yml 8001
server: port: 8001 spring: application: name: cloud-provider-service datasource: type: com.alibaba.druid.pool.DruidDataSource #当前数据源操作类型 driver-class-name: org.gjt.mm.mysql.Driver #mysql驱动包 url: jdbc:mysql://localhost:3306/db2019?useUnicode=true&characterEncoding=utf-8&useSSL=false username: root password: ljs eureka: client: #表示是否将自己注册进EurekaServer默认为true register-with-eureka: true #是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡 fetch-registry: true service-url: defaultZone: http://localhost:7001/eureka #单机版 #defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka #集群版 instance: instance-id: payment8001 prefer-ip-address: true #访问路径可以显示IP地址 #Eureka客户端向服务端发送心跳的时间间隔,单位为秒(默认是30秒) #lease-renewal-interval-in-seconds: 1 #Eurekaf服务端在收到最后一次心跳后等待时间上限,单位为秒(默认是90秒),超时将剔除服务 #lease-expiration-duration-in-seconds: 2 mybatis: mapper-locations: classpath:mapper/*.xml type-aliases-package: com.atguigu.springcloud.entities #所有Entity别名类所在包dao
package com.atguigu.springcloud.dao; import com.atguigu.springcloud.entities.Payment; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; @Mapper public interface PaymentDao { int create(Payment payment); Payment getPaymentById(@Param("id") Long id); }dao.xml
<?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="com.atguigu.springcloud.dao.PaymentDao"> <insert id="create" parameterType="Payment" useGeneratedKeys="true" keyProperty="id"> insert into payment(serial) values(#{serial}) </insert> <resultMap id="BaseResultMap" type="com.atguigu.springcloud.entities.Payment"> <id column="id" property="id" jdbcType="BIGINT"/> <result column="serial" property="serial" jdbcType="VARCHAR"/> </resultMap> <select id="getPaymentById" parameterType="Long" resultMap="BaseResultMap"> select * from payment where id=#{id}; </select> </mapper>service
package com.atguigu.springcloud.service; import com.atguigu.springcloud.entities.Payment; import org.apache.ibatis.annotations.Param; public interface PaymentService { public int create(Payment payment); public Payment getPaymentById(@Param("id") Long id); }serviceImpl
package com.atguigu.springcloud.service; import com.atguigu.springcloud.dao.PaymentDao; import com.atguigu.springcloud.entities.Payment; import org.springframework.stereotype.Service; import javax.annotation.Resource; @Service public class PaymentServiceImpl implements PaymentService{ @Resource private PaymentDao paymentDao; public int create(Payment payment){ return paymentDao.create(payment); } public Payment getPaymentById(Long id){ return paymentDao.getPaymentById(id); } }controller
package com.atguigu.springcloud.controller; import com.atguigu.springcloud.entities.CommonResult; import com.atguigu.springcloud.entities.Payment; import com.atguigu.springcloud.service.PaymentService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.discovery.DiscoveryClient; import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; import javax.xml.soap.SAAJResult; import java.util.List; import java.util.concurrent.TimeUnit; @RestController @Slf4j public class PaymentController { @Resource private PaymentService paymentService; @Value("${server.port}") private String serverPort; @Resource private DiscoveryClient discoveryClient; @GetMapping(value = "/payment/discovery") public Object discovery(){ List<String> services = discoveryClient.getServices(); //得到所有的微服务 for (String element : services) { log.info("*****element:"+element); } List<ServiceInstance> instances = discoveryClient.getInstances("CLOUD-PROVIDER-SERVICE"); //得到一个具体微服务的所有实例 for (ServiceInstance instance : instances){ log.info(instance.getServiceId()+"\t"+instance.getHost()+"\t"+instance.getPort()+"\t"+instance.getUri()); } return this.discoveryClient; } //只传给前端CommonResult,不需要前端了解其他的组件 @PostMapping(value = "/payment/create") public CommonResult create(@RequestBody Payment payment){ int result = paymentService.create(payment); log.info("*****插入结果:"+result); if(result > 0){ return new CommonResult(200,"插入数据成功,serverPort:"+serverPort,result); }else{ return new CommonResult(444,"插入数据失败",null); } } @GetMapping(value = "/payment/get/{id}") public CommonResult<Payment> getPaymentById(@PathVariable("id") Long id){ Payment payment = paymentService.getPaymentById(id); log.info("*****插入结果:"+payment); if(payment != null){ return new CommonResult(200,"查询成功,serverPort:"+serverPort,payment); }else{ return new CommonResult(444,"没有对应记录,查询ID:"+id,null); } } @GetMapping(value = "/payment/lb") public String getPaymentLB(){ return serverPort; } @GetMapping(value = "/payment/feign/timeout") public String paymentFeignTimeout(){ //暂停几秒钟线程 try{ TimeUnit.SECONDS.sleep(3); }catch (InterruptedException e){ e.printStackTrace(); } return serverPort; } @GetMapping("/payment/zipkin") public String paymentZipkin(){ return "hi, i'am paymentzipkin server fall back,welcome to atguigu, O(∩_∩)O哈哈~"; } }80
server: port: 80 spring: application: name: cloud-order-server eureka: client: #表示是否将自己注册进EurekaServer默认为true register-with-eureka: true fetch-registry: true service-url: defaultZone: http://localhost:7001/eureka #defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka #集群版服务名指定
启动类加注解
@SpringBootApplication @EnableEurekaClientmySelfRule
package com.atguigu.myrule; import com.netflix.loadbalancer.IRule; import com.netflix.loadbalancer.RandomRule; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * 自定义负载均衡规则类 * @author wsk * @date 2020/3/13 14:56 */ @Configuration public class MySelfRule { @Bean public IRule myRule(){ return new RandomRule(); } }config
package com.atguigu.springcloud.config; import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.client.RestTemplate; @Configuration public class ApplicationContextConfig { @Bean //@LoadBalanced //使用@LoadBalanced注解赋予RestTemplate负载均衡的能力 public RestTemplate getRestTemplate(){ return new RestTemplate(); } }lb
package com.atguigu.springcloud.lb; import org.springframework.cloud.client.ServiceInstance; import java.util.List; /** * 模拟 ILoadBalancer 的接口,选择哪一个负载算法和机器 * @author wsk * @date 2020/3/13 17:11 */ public interface LoadBalancer { //获取eureka上的活着的机器总数 ServiceInstance instances(List<ServiceInstance> serviceInstanceList); } package com.atguigu.springcloud.lb; import org.springframework.cloud.client.ServiceInstance; import org.springframework.stereotype.Component; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; /** * Ribbon 手写轮询算法 * @author wsk * @date 2020/3/13 17:16 */ @Component public class MyLB implements LoadBalancer { //原子类 private AtomicInteger atomicInteger = new AtomicInteger(0); public final int getAndIncrement(){ int current; int next; do{ current = this.atomicInteger.get(); //超过最大值,为0,重新计数 2147483647 Integer.MAX_VALUE,不直接写方法是因为方法运行时每次都会创建一个栈区,而写死就不会,涉及Jvm调优 next = current >= 2147483647 ? 0 : current + 1; //自旋锁 }while (!this.atomicInteger.compareAndSet(current,next)); System.out.println("*****第几次访问,次数next:"+next); return next; } @Override public ServiceInstance instances(List<ServiceInstance> serviceInstance) { int index = getAndIncrement() % serviceInstance.size(); return serviceInstance.get(index); } }controller
package com.atguigu.springcloud.controller; import com.atguigu.springcloud.entities.CommonResult; import com.atguigu.springcloud.entities.Payment; import com.atguigu.springcloud.lb.LoadBalancer; import lombok.extern.slf4j.Slf4j; import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.discovery.DiscoveryClient; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; import javax.annotation.Resource; import java.net.URI; import java.util.List; @RestController @Slf4j public class OrderController { //private static final String PAYMENT_URL="http://localhost:8001"; //通过在eureka上注册过的微服务名称调用 private static final String PAYMENT_URL = "http://CLOUD-PROVIDER-SERVICE"; @Resource private RestTemplate restTemplate; /** * 自定义负载均衡规则 */ @Resource private LoadBalancer loadBalancer; @Resource private DiscoveryClient discoveryClient; @GetMapping("/consumer/payment/create") public CommonResult<Payment> create(Payment payment){ return restTemplate.postForObject(PAYMENT_URL+"/payment/create",payment,CommonResult.class); } @GetMapping("/consumer/payment/get/{id}") public CommonResult<Payment> getPayment(@PathVariable("id") Long id){ return restTemplate.getForObject(PAYMENT_URL+"/payment/get/"+id,CommonResult.class); } @GetMapping("/consumer/payment/getForEntity/{id}") public CommonResult<Payment> getPayment2(@PathVariable("id") Long id){ ResponseEntity<CommonResult> entity = restTemplate.getForEntity(PAYMENT_URL+"/payment/get/"+id,CommonResult.class); if(entity.getStatusCode().is2xxSuccessful()){ return entity.getBody(); }else{ return new CommonResult(444,"操作失败"); } } /** * 路由规则:轮询 * @param * @return */ @GetMapping(value = "/consumer/payment/lb") public String getPaymentLB(){ List<ServiceInstance> instances = discoveryClient.getInstances("CLOUD-PROVIDER-SERVICE"); if(instances == null || instances.size() <= 0){ return null; } ServiceInstance serviceInstance = loadBalancer.instances(instances); URI uri = serviceInstance.getUri(); return restTemplate.getForObject(uri+"/payment/lb",String.class); } //============> zipkin + sleuth @GetMapping("/consumer/payment/zipkin") public String paymentZipkin(){ String result = restTemplate.getForObject("http://localhost:8001" + "/payment/zipkin", String.class); return result; } }eureka 集群
做集群修改window10配置文件
会出现相互注册的情况